This is a key feature for almost any location-based application: given a current location, it will return the nearest feature (or list of nearest features, ordered by distance).
The naive approach to this problem would be to query the table ordering by ST_Distance. Let's find the five earthquakes closest to San Juan:
SELECT * FROM data_import.earthquakes_subset_with_geom
ORDER BY ST_Distance(geom::geography, ST_SetSRID(ST_MakePoint(-66.11,18.46),4326)::geography)
LIMIT 5;
id | time | depth | mag | magtype | place | geom
------------+--------------------------+-------+-----+---------+---------------------------------------------------+----------------------------------------------------
pr16281009 | 2016-10-08 01:08:46.4+02 | 5 | 2.5 | Md | 28km SE of El Negro, Puerto Rico | 0101000020E610000062A1D634EF6850C0E561A1D634DF3140
pr16281009 | 2016-10-08 01:08:46.4+02 | 5 | 2.5 | Md | 28km SE of El Negro, Puerto Rico | 0101000020E610000062A1D634EF6850C0E561A1D634DF3140
pr16282000 | 2016-10-08 04:40:43.2+02 | 6 | 2.6 | Md | 14km SSE of Tallaboa, Puerto Rico | 0101000020E610000044696FF085A950C0FF21FDF675E03140
pr16282000 | 2016-10-08 04:40:43.2+02 | 6 | 2.6 | Md | 14km SSE of Tallaboa, Puerto Rico | 0101000020E610000044696FF085A950C0FF21FDF675E03140
pr16282001 | 2016-10-08 11:27:19.6+02 | 27 | 2.9 | Md | 28km NNW of Charlotte Amalie, U.S. Virgin Islands | 0101000020E6100000DAACFA5C6D4150C0857CD0B359953240
This is fast for a table with 50 rows, but what if we'd like to reverse geocode (find the nearest address) given a pair of coordinates and an os_address_base_gml table?
SELECT * FROM data_import.os_address_base_gml
ORDER BY ST_Distance(wkb_geometry::geography, ST_SetSRID(ST_MakePoint(-3.5504,50.7220),4258)::geography)
LIMIT 1;
This will require a lot of geodesic calculations, as the ST_Distance function cannot use a spatial index.
To make things better, a <-> operator can be used to make indexed K-nearest feature searches. First, let's change the data type of the os_address_base_gml geometry column to geography, as the index won't work when using a type cast:
ALTER TABLE data_import.os_address_base_gml ALTER COLUMN wkb_geometry SET DATA TYPE geography;
Then, the query for reverse geocoding will look like this:
SELECT * FROM data_import.os_address_base_gml
ORDER BY wkb_geometry <-> ST_SetSRID(ST_MakePoint(-3.5504,50.7220),4258)::geography
LIMIT 1;
Using the right data type, a spatial index, and a <-> operator can make this kind of query an order of magnitude faster.
Beware, though--this operator compares the distances between the centers of bounding boxes. For points, this is not a problem, as the BBOX center of the point is equal to that point. But for more complicated shapes the results might be suboptimal. To address this, one can use the <-> operator as a prefilter, and then do a precise computation with ST_Distance:
WITH prefilter AS (
SELECT *, ST_Distance(way, ST_SetSRID('POINT(-100 6705148)'::geometry,900913)) AS dist FROM planet_osm_polygon
ORDER BY way <-> ST_SetSRID('POINT(-100 6705148)'::geometry,900913) LIMIT 10
)
SELECT * FROM prefilter ORDER BY dist LIMIT 1;