Find records by given latitude and longitude which intersects and circle within 2 mile radius using PostGIS? - sql

I have a Postgres table with some data created by using a shapefile. I need to find all records which intersect within 2 miles radius from a given latitude and longitude. I used some queries including the following one.
SELECT * FROM us_census_zcta WHERE ST_INTERSECTS(geom,
CIRCLE(POINT(40.730610, -73.935242), 2));
But none of them worked. What I am doing wrong here? How can I get the results I need?
The SRID is 4269 (NAD 83).
EDIT: After Mike Organek pointed me out that I have switched the lat-long in the above query. And then I tried a few things and the following query gave me 1 record.
SELECT * FROM us_census_zcta WHERE ST_INTERSECTS(geom::geometry,
ST_SETSRID(ST_POINT(-73.935242, 40.730610), 4269)::geometry);
But how can I use Circle and find records which intersect within 2 miles radius from that given lat-long?

What you're looking for is ST_DWithin, which will check if records intersect within a given buffer. In order to use it with miles you better cast the geometry column to geography, as it then computes distances in metres:
For geography: units are in meters and distance measurement defaults to use_spheroid=true. For faster evaluation use use_spheroid=false to measure on the sphere.
SELECT * FROM us_census_zcta
WHERE
ST_DWithin(
geom::geography,
ST_SetSRID(ST_MakePoint(-73.935242, 40.730610), 4269)::geography,
3218.688); -- ~2 miles
Keep in mind that this cast might affect query performance if the indexes aren't set properly.
See also: Getting all Buildings in range of 5 miles from specified coordinates

Related

Problem with ST_AREA regarding coordinate system

I want to select all polygons with area more than 350 square kilometers from my initial polygons and tried ST_Area. But my polygons are in WKID: 25832 (UTM 32N) and I read that ST_Area always calculates in WGS 84 (EPSG 4326)....
I tried it like this but did not work.
DROP TABLE IF EXISTS projekt."Final_Selection";
CREATE TABLE projekt."Final_Selection" AS
SELECT (st_area/1000000), geom
FROM projekt."New_Pot" WHERE st_area>350 ;
and like this:
DROP TABLE IF EXISTS projekt."Final_Selection";
CREATE TABLE projekt."Final_Selection" AS
SELECT (st_area(ST_Transform(geom,25832)))/10000000, geom
FROM projekt."New_Pot" WHERE st_area>350;
Does anyone have an advice for me? Thx in advance!
If you want to select geometries by their size in sqm use ST_Area with geography instead of geometry:
For geography types by default area is determined on a spheroid with units in square meters.
SELECT * FROM projekt."New_Pot"
WHERE ST_Area(ST_Transform(geom,4326)::geography) >= 350000
Note: keep in mind that using ST_Area in the WHERE clause might slow down your query significantly, as the area is being calculated in query time. If it isn't a one time query, consider creating an index with the area pre-calculated, e.g.
CREATE INDEX idx_geom ON projekt."New_Pot" (ST_Area(ST_Transform(geom,4326)::geography));
See also: Calculate the percentage of polygon inside the buffer in postgresql
In case you want to query polygons within a given radius: the easiest way would be to convert your geometry column to geography and from there use ST_DWithin, which will calculate the distance in metres when used with geography parameters:
SELECT * FROM projekt."New_Pot"
WHERE
ST_DWithin(
ST_Transform(geom,4326)::geography, -- your geometry converted to geography
ST_MakePoint(1,52)::geography, -- a given point
350000) -- a 350km buffer
Related post: Getting all Buildings in range of 5 miles from specified coordinates

SQL (Presto): How to pull locations within X mile radius of lat/lon pont

I have a bunch of stores with their own Latitudes and Longitudes. I'm trying to pull data that is within a 2 mile radius of each point. Eg. How many stores are within 2 miles of each store. What is the best way to go about this?
I know rounding the lat/longs to the tenth (18.4, -66.2) can essentially give me 5 mile radius, but how do I get more granular. I'm not sure how granular rounding to the 100th (18.4, -66.21) gets me in terms of miles, but seems too small of a radius.
Date is stored as:
Store Name (string)
Latitude (double)
Longitude (double)
What you want is spatial join:
https://prestodb.io/blog/2020/05/07/local-spatial-joins
Just join a table with itself, on condition that distance between two points is below 2 miles, and aggregate. Something like this:
SELECT
a.store_name,
(COUNT(*) - 1) AS neighbors -- subtract 1 for self
FROM stores a JOIN stores b
ON ST_Distance(ST_Point(a.longitude, a.latitude),
ST_Point(b.longitude, b.latitude)) < 2 * 1609
GROUP BY a.store_name
Make sure you have a relatively fresh Presto installation, I think Presto got it optimized around end of 2018, and it would run as plain cross join before that - which would be too slow.

How can I return all the rows in a PostgreSQL/PostGIS table within a radius of Xkm provided by a longitude and latitude value?

I'm trying to have a go at learning about PostgreSQL and in particular, it's PostGIS extension and the benefits with regards to geographic spatial features it provides. I've loaded a PostgreSQL DB with a table that contains 30,000 records of latitude, longitude and a price value (for houses) and I want to start querying the DB to return all the rows that would be in a radius of Xkm of a particular latitude and longitude.
I've hit a brick wall as to how I might run this type of query as I've found the documentation to be quite limited online and I've found no similar attempts at this method of querying online.
Some methods I've tried:
SELECT *
FROM house_prices
WHERE ST_DWithin( ST_MakePoint(53.3348279,-6.269547099999954)) <= radius_mi *
1609.34;
This prompts the following error:
ERROR: function st_dwithin(geometry) does not exist
Another attempt:
SELECT * FROM house_prices ST_DWithin( 53.3348279, -6.269547099999954, 5); <-- A latitude value, longitude value and 5 miles radius
This prompts the following error:
ERROR: syntax error at or near "53.3348279"
Could anyone point me in the right direction/ know of some documentation I could look at?
** Edit **
Structure and set up of database and table in pgAdmin4
The first query has an invalid number of parameters. The function ST_DWithin expects at least two geometries and the srid distance,
and optionally a Boolean parameter indicating the usage of a spheroid (see documentation).
The second query is missing a WHERE clause and has the same problem as the first query.
Example from documentation:
SELECT s.gid, s.school_name
FROM schools s
LEFT JOIN hospitals h ON ST_DWithin(s.the_geom, h.the_geom, 3000)
WHERE h.gid IS NULL;
Perhaps something like this would be what you want to achieve:
SELECT *
FROM house_prices h
WHERE ST_DWithin(ST_MakePoint(53.3348,-6.2695),h.geom,h.radius_mi * 1609.34)
Also pay attention to the order of the coordinates pair (x,y or y,x), otherwise you might easily land on the sea with these coordinates ;-)
EDIT: Taking into account that there is no geometry on the table, so the points are stored in two different columns, longitude and latitude:
SELECT *
FROM house_prices
WHERE ST_DWithin(ST_MakePoint(longitude,latitude),ST_MakePoint(53.3348,-6.2695),1609.34)

Get Distance in Meters instead of degrees in Spatialite

I have the following query:
select distance(GeomFromText('POINT(8 49)',4326),GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326))
this gives me 0.97 degrees. But I need it in meters and do not know which SRID to transform to.
Can somebody give me an example how to get the result in meters for spatialite?
The positions are all in Europe.
Just multiply the value in degrees by 111195 - this value is (Earth mean radius)*PI/180 - that is 'mean length of one great circle degree in meters on Earth's surface'.
The result obtained using this method is within 1% of the geodesic distance for the WGS84 ellipsoid.
EDIT
OK, my answer above still stands for the question: "how to convert arcs in degrees into lengths in meters", however, it's not the question you asked (should have asked).
I haven't used Spatialite professionally, so I assumed that your sample query indeed returns the 'length in degrees'. That's not true.
Unfortunately, it appears that Spatialite fails to calculate the distance in 'geographic sense'. Despite your geometries are defined with SRID 4326, it treats them as if they were on a plane.
Here's a simple proof:
select Distance(GeomFromText('POINT(0 0)',4326),GeomFromText('POINT(3 4)',4326));
returns 5.0.
It's a shame ...
Lets have a look at your original query:
select Distance(
GeomFromText('POINT(8 49)',4326),
GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326)
)
An equivalent query in MS SQL Server:
SELECT (geography::STGeomFromText('POINT(8 49)', 4326)).STDistance(geography::STGeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)', 4326));
gets you the correct result immediately: 105006.59673084648, in meters, and without any extra brouhaha.
So what are your options with Spatialite?
Indeed, as you said in comments, one option is to project your geometries, and calculate on those. Using SRID 3035 for Europe makes sense, too (if your locations are mostly in Germany, I'd consider SRID 25832).
select Distance(
Transform(GeomFromText('POINT(8 49)',4326),25832),
Transform(GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326),25832)
)
returns 104969.401605453.
As to your other sample (in comments):
select distance(
Transform(GeomFromText('POINT(8.328957 49.920900)',4326),3035),
Transform(GeomFromText('POINT(8.339665 49.918000)',4326),3035)
)
There's a simpler way to do it (if you have two POINTs, not a POINT and a LINESTRING): create a LINESTRING with your POINTs and use GeodesicLength function, like this:
select GeodesicLength(GeomFromText('LINESTRING(8.328957 49.920900, 8.339665 49.918000)',4326))
It returns 833.910006698673, as expected.
In SpatiaLite's functions reference guide, you can see there are two version of the Distance() function. One takes only two arguments and return the distance in CRS units, the other takes 3 arguments and return the distance in meters.
To get the distance in meters, simply pass a third argument to Distance:
sqlite> select Distance(MakePoint(0, 0), MakePoint(3, 4));
5.0
sqlite> select Distance(MakePoint(0, 0), MakePoint(3, 4), 1);
554058.923752633

Optimizing Sqlite query for INDEX

I have a table of 320000 rows which contains lat/lon coordinate points. When a user selects a location my program gets the coordinates from the selected location and executes a query which brings all the points from the table that are near. This is done by calculating the distance between the selected point and each coordinate point from my table row. This is the query I use:
select street from locations
where ( ( (lat - (-34.594804)) *(lat - (-34.594804)) ) + ((lon - (-58.377676 ))*(lon - (-58.377676 ))) <= ((0.00124)*(0.00124)))
group by street;
As you can see the WHERE clause is a simple Pythagoras formula to calculate the distance between two points.
Now my problem is that I can not get an INDEX to be usable. I've tried with
CREATE INDEX indx ON location(lat,lon)
also with
CREATE INDEX indx ON location(street,lat,lon)
with no luck. I've notice that when there is math operation with lat or lon, the index is not being called . Is there any way I can optimize this query for using an INDEX so as to gain speed results?
Thanks in advance!
The problem is that the sql engine needs to evaluate all the records to do the comparison (WHERE ..... <= ...) and filter the points so the indexes don’t speed up the query.
One approach to solve the problem is compute a Minimum and Maximum latitude and longitude to restrict the number of record.
Here is a good link to follow: Finding Points Within a Distance of a Latitude/Longitude
Did you try adjusting the page size? A table like this might gain from having a different (i.e. the largest?) available page size.
PRAGMA page_size = 32768;
Or any power of 2 between 512 and 32768. If you change the page_size, don't forget to vacuum the database (assuming you are using SQLite 3.5.8. Otherwise, you can't change it and will need to start a fresh new database).
Also, running the operation on floats might not be as fast as running it on integers (big maybe), so that you might gain speed if you record all your coordinates times 1 000 000.
Finally, euclydian distance will not yield very accurate proximity results. The further you get from the equator, the more the circle around your point will flatten to ressemble an ellipse. There are fast approximations which are not as calculation intense as a Great Circle Distance Calculation (avoid at all cost!)
You should search in a square instead of a circle. Then you will be able to optimize.
Surely you have a primary key in locations? Probably called id?
Why not just select the id along with the street?
select id, street from locations
where ( ( (lat - (-34.594804)) *(lat - (-34.594804)) ) + ((lon - (-58.377676 ))*(lon - (-58.377676 ))) <= ((0.00124)*(0.00124)))
group by street;