postgresql postgis If point inside circle - postgresql

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.

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 to change buffer size from degrees to distance by meters in PostGIS?

SELECT ST_Buffer(geom, 400)
FROM my_table;
This query creates a buffer in 400 degrees. How can I change it to have 400m buffers around points?
I have tried to use ST_Transform and ST_SetSRID but I got errors. Most likely because I structure my query wrong.
Thank you for your help.
ST_buffer uses the projection unit. You can cast the geometry to a geography to use meters, or rely on a suitable local projection whose unit is in meters.
SELECT ST_Buffer(geom::geography, 400)
FROM my_table;
If it is not yet set, you may have to set the original CRS first
SELECT ST_Buffer(st_setSRID(geom,4326)::geography, 400)
FROM my_table;

Geopoints: From Single Coordinate to Bounds on map

I try to figure out how to come from a single given coordinate (lat/lon) to the nearest bounds which enclose this coordinate on a map e.g. streets or sea.
Here two examples to give you a better understanding of what I mean:
What i tried already or thought about:
Setting up a Nominatim server and search for the given coordinate via the reverse-function to get the bbox and/or the geojson polygon of this coordinate. -> this only works when the given coordinate is within a POI or for example directly on a street.
Writing an algorithm to walk in all 4 or 8 directions (n/e/s/w) and 'stop' when the map layer/surface changes (change = stop for this direction and mark a bounding-point)
Building up an image-recognition system using TensorFlow to detect the different colors and 'draw' the polygon. Worked with TensorFlow a couple of times but this seems to be the most tricky solution to implement (but at my current understanding the most precise one)
Does someone of you have any other ideas to get a solution for this problem? Would appreciate any kind of approaches
Cheers!
If I got your question right, you might wanna first select all polygons in which the given point is inside of using ST_Contains, and then compute the distance to this point using ST_Distance. If you ORDER BY distance and LIMIT to 1 result you'll get the nearest polygon, e.g.
Data Sample
CREATE TABLE t (gid int, geom geometry);
INSERT INTO t VALUES
(1,'POLYGON((-4.47 54.26,-4.44 54.28,-4.41 54.24,-4.46 54.23,-4.47 54.26))'),
(2,'POLYGON((-4.48 54.25,-4.40 54.25,-4.41 54.23,-4.48 54.23,-4.48 54.25))'),
(3,'POLYGON((-4.53 54.23,-4.44 54.29,-4.38 54.22,-4.53 54.23))');
Query
SELECT gid,ST_AsText(geom) FROM t
WHERE ST_Contains(geom,ST_MakePoint(-4.45, 54.25))
ORDER BY ST_Distance(geom,ST_MakePoint(-4.45, 54.25))
LIMIT 1;
gid | st_astext
-----+------------------------------------------------------------------------
1 | POLYGON((-4.47 54.26,-4.44 54.28,-4.41 54.24,-4.46 54.23,-4.47 54.26))
(1 Zeile)

Postgresql postgis ST_DWithin always return true

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;

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);