Reputation: 3663
Here's a sample of my PostgreSQL in CSV format.
row,latitude,longitude
1,42.082513,-72.621498
2,42.058588,-72.633386
3,42.061118,-72.631541
4,42.06035,-72.634145
I have thousands more rows like these spanning coordinates across the world.
I want to query the table only for coordinates within a certain radius. How do I do this with PostGIS and PostgreSQL?
Upvotes: 5
Views: 6559
Reputation: 658342
You want "all rows within a 5-mile radius of a coordinate", so this is not exactly a K-nearest-neighbour (KNN) problem. Related, but your case is much simpler. "Find the 10 rows closest to my coordinates" would be a KNN problem.
Convert your coordinates to geography
values:
ST_SetSRID(ST_MakePoint(longitude, latitude),4326)::geography
Alternatively you could use the simpler geometry
type. Consider the manual:
Then we have a table like:
CREATE TABLE tbl (
tbl_id serial PRIMARY KEY
, geog geography NOT NULL
-- more?
);
Use ST_DWithin()
and a spatial index to make the query fast:
CREATE INDEX tbl_geog_gist ON tbl USING gist(geog);
Query:
SELECT *, ST_Distance(c.x, geog) AS distance -- distance is optional
FROM tbl t
, ST_GeographyFromText('SRID=4326;POINT(-72.63 42.06)') c(x) -- you are here
WHERE ST_DWithin(c.x, geog, 8047) -- distance in meter
ORDER BY distance; -- optional
5 miles are =~ 8047 meter.
Or use your original columns and create an expression index ... See:
Upvotes: 13
Reputation: 3663
I did a combo of Erwin's and Patrick's answers.
-- Add geography column
ALTER TABLE googleplaces ADD COLUMN gps geography;
UPDATE googleplaces SET gps = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
CREATE INDEX googleplaces_gps ON googleplaces USING gist(gps);
SELECT *
FROM my_table
WHERE ST_DWithin(gps, ST_SetSRID(ST_MakePoint(-72.657, 42.0657), 4326), 5 * 1609);
Upvotes: 3
Reputation: 32326
You should first create a table from your CSV formatted file, using the COPY
command (if the file is accessible to the PostgreSQL server) or the \copy
command in psql
if the file is not local to the server. See other Q+A on SO for examples if you have any trouble.
Once you have your data in a table, you should convert your longitude
and latitude
columns to the PostGIS geography
type by adding a column to your table of type geography(POINT, 4326)
and then populating that column (here called gps
) with the appropriate values:
UPDATE my_table SET gps = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
Add an index on that column to allow for efficient searches:
CREATE INDEX my_table_gps ON my_table USING gist(gps);
You can now find the rows within 5 miles from a given location, e.g. (-72.657, 42.0657)
, as follows:
SELECT *
FROM my_table
WHERE ST_DWithin(gps, ST_SetSRID(ST_MakePoint(-72.657, 42.0657), 4326), 5 * 1609);
Note that ST_DWithin()
on a geography
column will work in meters, so you have to multiply your radius in miles with the 1,609 meters in a mile.
Upvotes: 4