Postgresql postgis ST_DWithin always return true - postgresql

I need to calculate if one point is not more distant than a given radius from another point. I used the function ST_DWithin, in google maps I get lanLot using "what is here" section of two points. First: (43.2137617, 76.8598076) and second (43.220109 76.865100). The distance between them is 1.25km. My query
SELECT ST_DWithin (
ST_GeomFromText('POINT(76.8598076 43.2137617)',3857),
ST_GeomFromText('POINT(76.865100 43.220109)',3857),
100
);
And it always returns true. I think that I put radius 100 meters and used SRID 3875 to use meters. What is wrong?

The coordinates you are using are not in CRS 3857 but are rather unprojected lat-long, i.e. CRS 4326, so you are looking for points within 100 degrees of each others.
You would need to create the point in 4326, project it in 3857 using ST_Transform and then make the distance computation in meters.
SELECT ST_DWithin (
ST_Transform(ST_GeomFromText('POINT(76.8598076 43.2137617)',4326),3857),
ST_Transform(ST_GeomFromText('POINT(76.865100 43.220109)',4326),3857),
100
);
CRS 3857 is a projection that does not preserve distances that well as you move away from the equator. You may want to use ST_Distance_Sphere instead. Comparing the two methods, the first one gives 1134m between the points and the second one 825m... quite a difference!
SELECT ST_Distance_Sphere (
ST_GeomFromText('POINT(76.8598076 43.2137617)',4326),
ST_GeomFromText('POINT(76.865100 43.220109)',4326))
<= 100;

Related

Can someone tell me why this keeps returning degrees instead of meters?

Can someone tell me why this keeps returning degrees instead of meters? I’m transforming the geometry SRID to 32613, which measures in meters. Thanks
SELECT storm_date, hail_size_inches,
ST_Distance(
ST_Transform(geom32613, 32613),
ST_SetSRID(
ST_MakePoint(-104.89907, 39.66643),
32613)
) distance
FROM hail.hail_swaths
WHERE storm_date >= '2021/06/01'
You are using lat-long coordinates (4326) as if they were in 32613.
ST_SetSRID(ST_MakePoint(-104.89907, 39.66643), 32613) --> replace with ST_Transform(ST_SetSRID(ST_MakePoint(-104.89907, 39.66643), 4326),32613);
Also double check what values are stored in the column geom32613. If they are indeed in 32613, there is no need to reproject them
Example for 1 degree, near the central meridian for this projection:
SELECT ST_Distance(ST_Transform('SRID=4326;POINT(-105 40)',32613),
jgtest(> ST_Transform('SRID=4326;POINT(-106 40)',32613));
st_distance
------------------
85361.8049211818
Welcome to SO.
Your problem might be somewhere else. ST_Distance with two geometries using the SRS 32613 returns the distance in metres:
SELECT ST_Distance('SRID=32613;POINT(508654.55672303465 4390740.143711988)',
'SRID=32613;POINT(508654.55672303480 4390740.143711988)');
st_distance
----------------------
1.74622982740402e-10
(1 row)
It also works using ST_Transform
SELECT ST_Distance(
ST_Transform('SRID=4326;POINT(-104.89910 39.66643)',32613),
ST_Transform('SRID=4326;POINT(-104.89907 39.66643)',32613));
st_distance
------------------
2.57321026907276
(1 row)
Demo: db<>fiddle
Are you perhaps mixing the order of the coordinate pairs? Remember, it is longitude, latitude, not the other way around. If the geometries are correct, please post a WKT literal from both geometries, so that we can reproduce your environment. Another option would be to use geography instead of geometry, which would automatically return the result in metres, but you would need to transform the geometries encoded in 32613 in a lon/lat coordinate system to make the cast work, such as 4326.
EDIT: Read carefully the answer of #JGH - he might have found the real issue. You're probably using the coordinates with a wrong SRS!

How I calculate the total distance of points in postgres in a table?

long,lat,time
10,11,0
11,12,1
12,13,2
I have a simple table with longitudine, latitude and time and i want know a function that calculate the total distance of points in postgres.
If you want to calculate the distance you can use the extention earthdistance and do something like :
SELECT earth_distance(lat, long)
FROM your_table;
The earthdistance module provides two different approaches to calculating great circle distances on the surface of the Earth. The one described first depends on the cube package (which must be installed before earthdistance can be installed). The second one is based on the built-in point datatype, using longitude and latitude for the coordinates.
In this module, the Earth is assumed to be perfectly spherical. (If that's too inaccurate for you, you might want to look at the PostGIS project.)

postgresql postgis If point inside circle

I'm using postgresql as db , i have the table named car_wash with field "point geometry"(use postgis) so in application I'm getting lon lat from user using GOOGLE API, next step I need to create circle around user and check if car_wash inside this circle I use
select *
from car_wash cw
where
ST_DWithin (
cw.lon_lat,
ST_GeomFromText('POINT(54.21 22.54)')
)=false
AND
not cw.was_deleted
Is it corect way? IF you need my srid is 0 according to this query
Select Find_SRID('public', 'car_wash', 'lon_lat')
First - i assume that lat_long is georaphy type column. If it is geometry type column you will have to modify my examples to some other EPSG (propably 3857 metric EPSG for whole world). It is very important because st_dwithin check in meters for geopraphy type , and in map units for geometry (and for EPSG 4326 unit is degree not meter)
Insert your data like this
insert into car_wash values (1,'aaa',st_setsrid(st_makepoint(54.51, 22.54),4326))
I explain why use st_setsrid, st_makepoint, and what the hell is 4326.
- 4326 is EPSG 4326 - it is most known coordinate reference system (where you have lat and long in degrees).
st_makepoint - will create geography point from your lat and long coordinates. It will looks like bytes, but dont worry, if you will need lat and long for some reasons you can get them with st_x() and st_y() or st_astext() functions. Best thing of have geoms or geogs (in this case) is that you can use gist index. Very powerful tool that speed up your geo queries.
st_setsrid - st_makepoint will create point but with srid=0. You have to tell POSTGIS in what EPSG it should read your data. For example if you tell him to read it with 4326 it will be in correct places on google world map, but if you say for example 3857 it will be in completly diffrent place, as 3857 is metric system not degree so it will be around 50 and 50 meters from left down corner (or maybe left up, dont remember)
Create index on geog
create index on car_wash using gist (geog);
We have table, we have data in it and index on it. Now we want to check if your point is close to any of your car washes.
select *
from car_wash cw
where ST_DWithin (cw.geog,ST_GeogFromtext('SRID=4326;POINT(54.21 22.54)'),1000)
AND cw.was_deleted=false
In ST_DWithin third parameters is distance in meters (georpahy) or map units (geometry). So in this case it will show you all car washes that are up to 1000 meters from your user location and are not deleted.
While using ST_DWithin function, your third parameter must be distance.
You can also define srid in ST_GeomFromText
there are two simple example so you can see difference:
select ST_DWithin(
st_geomfromtext('POINT(54.51 22.54)',4326),
st_geomfromtext('POINT(54.21 22.54)',4326),0.5
)
result is true
select ST_DWithin(
st_geomfromtext('POINT(54.51 22.54)',4326),
st_geomfromtext('POINT(54.21 22.54)',4326),0.1
)
result is false
Source:
https://postgis.net/docs/ST_DWithin.html
https://postgis.net/docs/ST_GeomFromText.html
SRID of the ST_GeomFromText('POINT(54.21 22.54)') must be same as the SRID of cw.lon_lat. Suppose SRID of cw.lon_lat is 4326 you can set the other attribute srib by using ST_GeomFromText('POINT(54.21 22.54)',4326).
Secondly, ST_DWithin needs buffer distance as 3rd parameter. So suppose if you want to check if point is within 100 meter buffer it should be like
ST_DWithin (
cw.lon_lat,
ST_GeomFromText('POINT(54.21 22.54)', 3857), 100
)
Buffer value is according to the srid unit. in case of 3857 its meter. You need to convert cw.lon_lat and POINT(54.21 22.54) to the same SRID in order to make this work, using st_setSRID e.g.

Using STDistance with Spatial index on SQL Server 2012 is slower then using COS, SIN & ACOS Calculations and gives oval shaped results

I have a table in a SQL Server 2012 database with 3.000.000 records. Those records represent a point on a map. Al those records have x, y coordinates and geography point as fields (x, y, geo).
I need to calculate all points within a distance of 10.000 meter from a certain point.
Query no. 1 I use :
DECLARE #point geography
DECLARE #rad float
SET #point = geography::STGeomFromText('POINT(51.2207099068778 4.39961050577564)', 4326);
SET #rad = 10000
SELECT count(1)
FROM t_mailbox WITH (INDEX(SIndx_t_mailbox_geo_MHHM_512))
WHERE
#point.STDistance(geo) <= #rad
Result : It takes 4 seconds to find 273.346 points. Drawing those points on a map results in an oval shape on the map.
For sure this is wrong because not all points are included in the result.
Query no. 2 I use :
declare #radius int = 10000
DECLARE #x float = 51.2207099068778
DECLARE #y float = 4.39961050577564
SELECT count(1)
FROM t_mailbox
WHERE
ACOS(COS(RADIANS(90-#x))*COS(RADIANS(90-x)) +SIN(RADIANS(90-#x)) *SIN(RADIANS(90-x))*COS(RADIANS(#y-y)))*6371000 <= #radius
Result : It takes 2 seconds to find 564.547 points. Drawing those points on a map results in a perfect shaped circle.
Questions :
Why is using SPATIAL INDEX and STDistance slower then the more complicated query with SIN, COS and ACOS?
Why is results in a wrong oval shaped set of points?
What am I doing wrong?
Geography data is drawn on the surface of a sphere. This means it looks different than geometry (flat) data.
Imagine taking a globe, and drawing a point on it. Then take a compass and draw a circle around that point. Now peel the skin off the globe. Notice it does not lie flat, to make it flat you have to stretch it. Now the way most people do that, is the stretch the top and bottom (north/south poles) and stretch it until it is the same length as the equator. This makes the circle you drew an oval which is bigger horizontally than vertically.
Now the formula you used is for points within a radius on flat plane. This means that you assume the distance between two lines of longitude is the same no matter what latitude you are (5 feet away from the north pole, the distance between 90 degrees and 91 degrees longitude is much smaller than at the equator).
On a mercator projection map, this formula will make a map that is a perfect circle, however on a globe, it is not. Hopefully this makes sense.
As for you speed issue: A: Apples to oranges, you are doing different calculations. and B: Without knowing how you have your index set up, it is very difficult to analyze, but geography indexing is pretty bad regardless, it works much better on very large geographies like countries.
Whilst hcaelxxam answers the "why" perfectly, you may find better performance by moving away from STDistance(). Whilst not always the case, I have generally found it better to use STIntersects() or STWithin() for distances - how you do this is pretty easy!
Try changing your query to the following. I'd be interested in the results:
DECLARE #point geography;
DECLARE #rad float = 10000;
SET #point = geography::STGeomFromText('POINT(51.2207099068778 4.39961050577564)', 4326).STBuffer(#rad); -- We're creating the "oval" here
SELECT count(1)
FROM t_mailbox WITH (INDEX(SIndx_t_mailbox_geo_MHHM_512))
WHERE
#point.STIntersects(geo) = 1
You may also like to try with and without the index hint. Sometimes, forcing it can generate an inefficient query plan.

Get metric distance between two points via a PostgreSQL/PostGIS request

I have a question about the use of postgreSQL/postGIS.
I would like to display markers on a map (stored in a database) which are some distance away from the user (coordinates given to the request).
The type of the field of the markers is POINT (I store lat/long).
The user position is detetermined by the Google Map API.
Here is the actual request :
SELECT * FROM geo_points WHERE ST_distance(ST_SetSRID(geo_points.coords::geometry,4326),ST_GeomFromEWKT('SRID=4326;POINT(45.0653944 4.859764599999996)')) > 65
I know (after some research on internet) that the function ST_distance gives me the distance in degree between markers and the user position and that I test the distance in km.
I think I have to use the function ST_tranform to transform the points in metric coordinates.
So my questions are :
- what is the SRID for France
- how can I make this dynamically for the entire world according to the user position ?
I also kow that the function ST_within exists and that could do this. But I anticipate the fact that later, I could need the distance.
Any help would be greatly appreciated
ps: there are maybe solutions in other post, but all the answers I have found during my researches were not really meeting my needs.
Firstly, pay attention to the axis order of coordinates used by PostGIS, it should be long/lat. Currently you are searching in Somalia. Swapping to the coordinates, you would be searching in France.
You can use a geodesic calculation with the geography type, or use geodesic functions like ST_Distance_Spheroid. With the geography type, you may want to use ST_DWithin for higher performance.
Here are geo_points 65 m away or less from the point of interest in France (not Somalia):
SELECT * FROM geo_points
WHERE ST_Distance_Spheroid(
ST_Transform(geo_points.coords::geometry, 4326),
ST_SetSRID(ST_MakePoint(4.859764599999996, 45.0653944), 4326),
'SPHEROID["WGS 84",6378137,298.257223563]') < 65.0;
However, it will be very slow, since it needs to find the distance to every geo_points, so only do this if you don't care about performance and have less than a few thousand points.
If you change and transform geo_points.coords to store lon/lat (WGS84) as a geography type:
SELECT * FROM geo_points
WHERE ST_DWithin(
geo_points::geography,
ST_SetSRID(ST_MakePoint(4.859764599999996, 45.0653944), 4326)::geography,
65.0);