I have the following query to try and get a total of places within the range of a provided point, comparing against my table of places. All places have a geo
column, defined as geo point default null
.
select
count(*)
from
places
where
ST_DWithin(
ST_GeomFromText(concat('POINT', geo::text), 4326),
ST_GeomFromText('POINT(37.64903402157866, -83.84765625000001)', 4326),
66 * 1609.34
);
However, that returns:
parse error - invalid geometry
I tried casting to ::geometry, using other functions, but nothing really works. Here’s how my data looks like:
select geo, geo::text, geo::geometry from places where geo is not null limit 10
returns:
{"x":-84.3871,"y":33.7485}
(-84.3871,33.7485)
01010000000612143FC61855C02B8716D9CEDF4040
Am I doing something wrong? Should I alter the table and store another column type? How do I not lose the existing data?
2
Answers
About everything is wrong here.
Type: you have postgres native
point
type, which is not usable by Postgis. You most likely want thegeometry(point)
Conversion: Luckily, you can simply cast from point to geometry:
Coordinates order: In Postgis, coordinates are usually expressed as longitude first, then latitude. In you example, the column is in long-lat while the hard coded geometry is in lat-long.
Coordinate system: your data seems to be not projected. You could use a column specifying this, so cast using
point_column::geometry(point,4326)
.Distances in 4326: DO NOT COMPUTE DISTANCES IN 4326. They are meaningless. A degree of longitude doesn’t have the same ground length, in meters, as a degree of latitude.
Instead, you must use a suitable local projection that preserve distances or use the
geography
type instead, but be aware of the implication, especially over long distances.Your lon/lat order is flipped between what you’re looking for and what you have in the table. You’ll need to make sure which order is correct and flip back the target point or the data in the table. PostGIS operates in x+y, lon+lat, while Google Maps is an example where you can find y+x, lat+lon. Always check what you’re dealing with.
66 * 1609.34
seems like conversion from miles to meters, but since you’re using typegeometry
in 4326,STDWithin()
will assume a degree to be your unit of measurement:Which means your target range exceeds the circumference of the globe, around 295 times.
Postgres’ built-in
point
is not the same type asgeometry(Point)
from PostGIS extension. You’ll need to cast or convert between them.STDWithin()
can measure in meters, but only if you give itgeography
types (or switch the CRS/SRID to meter-based), so that’s another cast. It can also increase the precision at the cost of some performance, if you tell it to assume a spheroid, as it by default assumes the globe is a regular sphere.Demo at db<>fiddle:
"3: target coords"}