Postgis SQL for nearest neighbors

前端 未结 2 1454
野性不改
野性不改 2021-02-09 15:35

I\'m trying to calculate the nearest neighbors. For that, I need to pass a parameter to limit the maximum distance from the neighbors. For example, which are the nearest neighbo

相关标签:
2条回答
  • 2021-02-09 15:56

    First, If you are using latitude, longitude, you need to use 4326.

    UPDATE season SET geom = ST_PointFromText ('POINT(' || longitude || ' ' || latitude || ')' , 4326 ) ;
    

    Then you create an index on the geom field

    CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield] ); 
    

    Then you get the kNN neightbors:

    SELECT *,ST_Distance(geom,'SRID=4326;POINT(newLon newLat)'::geometry) 
    FROM yourDbTable
    ORDER BY
    yourDbTable.geom <->'SRID=4326;POINT(newLon newLat)'::geometry
    LIMIT 10;
    

    This query will take advantage of kNN functionality of the gist index (http://workshops.boundlessgeo.com/postgis-intro/knn.html).

    Still the distance returned will be in degrees, not meters (projection 4326 uses degrees).

    To fix this:

    SELECT *,ST_Distance(geography(geom),ST_GeographyFromText('POINT(newLon newLat)') 
    FROM yourDbTable
    ORDER BY
    yourDbTable.geom <->'SRID=4326;POINT(newLon newLat)'::geometry
    LIMIT 10;
    

    When you calculate the ST_distance use the geography type. There distance is always in meters:

    http://workshops.boundlessgeo.com/postgis-intro/geography.html

    All this functionality will probably need a recent Postgis version (2.0+). I am not sure though.

    Check this for reference https://gis.stackexchange.com/questions/91765/improve-speed-of-postgis-nearest-neighbor-query/

    0 讨论(0)
  • 2021-02-09 16:08

    If you want to find information in Postgis on spatial reference systems look in the table spatial_ref_sys. Based on that and to answer you first question,

    select * from spatial_ref_sys where srid=4629
    

    returns the following:

    4269 | EPSG      |      4269 | GEOGCS["NAD83",DATUM["North_American_Datum_1983"
    ,SPHEROID["GRS 1980",6378137,298.257222101,AUTHORITY["EPSG","7019"]],AUTHORITY["
    EPSG","6269"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.01
    745329251994328,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4269"]] | +proj=long
    lat +ellps=GRS80 +datum=NAD83 +no_defs
    

    This indicates that it is a North American Datum, and not Brasil, using the GRS80 ellipse, which is not the same as the WGS84 one used by 4326, which is the standard used by GPS. This information is what is used by the Proj4 projections library, which is included as part of a Postgis build on Postgres.

    I searched for Brasil in spatial_ref_sys,

    select * from spatial_ref_sys where auth_name ilike '%Bras%';
    

    and this returned no results.

    I did find a reference to 29101 on the web, which uses a South American datum, with a Brazilian Polyconic projection and units in metres.

    To convert you original example to this, you could use:

    select ST_Astext(ST_Transform(ST_SetSrid(ST_Makepoint(-49.265, -25.424),4326), 29101));
    

    which returns:

    POINT(5476247.04359163 7178517.77380949)

    and would allow you to use ST_Distance or ST_DWithin in meters. I only put the ST_AsText so you can see the output, you obviously wouldn't actually use that in an update using ST_Transform.

    Note the use of ST_Makepoint instead of concatenating coordinates -- the same thing, but easier to read, imho.

    To answer your second question, because you are effectively working in 4326, lat/lon, which ranges from -180 to 180 and -90 to 90, if you used 1000 as the distance in ST_DWithin, you will get every record returned, because the 1000 is not meters, but degrees, so you would have to use something like the system above.

    If you want to take distance in lat/lon, have a look at the haversine formula which will work fine for short distances.

    0 讨论(0)
提交回复
热议问题