简体   繁体   English

Postgis SQL为最近邻居

[英]Postgis SQL for nearest neighbors

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 neighbors within a radius of 1000 meters? 例如,哪些是半径1000米内最近的邻居?

I did the following : 我做了以下事情:

I created my table with the data: 我用数据创建了我的表:

id | name | latitude | longitude

After that, I executed the following query : 之后,我执行了以下查询:

SELECT AddGeometryColumn ( 'public' , ' green ', ' geom ' , 4326 , ' POINT' , 2 );

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

First question, Is the SRID of Brazil 4326? 第一个问题,巴西的SRID是4326吗? What would be 4269 ? 什么是4269?

Second question, by doing the following SQL 第二个问题,通过执行以下SQL

SELECT id, name
FROM season
WHERE ST_DWithin (
                 geom ,
                 ST_GeomFromText ('POINT(-49.2653819 -25.4244287 )', 4326),
                 1000
                 );

This returns nothing. 这什么都不返回。 From what I understand, this SQL would further point the radius of the maximum distance, right? 据我所知,这个SQL会进一步指向最大距离的半径,对吧?

It appears if you put 1000 results for 100000000, all my entries appear . 如果您将1000个结果显示为100000000,则会显示所有条目。

So, I wonder what is wrong here? 所以,我想知道这里有什么问题?

First, If you are using latitude, longitude, you need to use 4326. 首先,如果您使用纬度,经度,则需要使用4326。

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

Then you create an index on the geom field 然后在geom字段上创建索引

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

Then you get the kNN neightbors: 然后你会得到kNN的近处:

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 ). 此查询将利用gist索引的kNN功能( http://workshops.boundlessgeo.com/postgis-intro/knn.html )。

Still the distance returned will be in degrees, not meters (projection 4326 uses degrees). 返回的距离仍然是度数,而不是米(投影4326使用度数)。

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. 计算ST_distance时使用地理类型。 There distance is always in meters: 距离总是以米为单位:

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

All this functionality will probably need a recent Postgis version (2.0+). 所有这些功能可能都需要最新的Postgis版本(2.0+)。 I am not sure though. 我不确定。

Check this for reference https://gis.stackexchange.com/questions/91765/improve-speed-of-postgis-nearest-neighbor-query/ 请查看此参考https://gis.stackexchange.com/questions/91765/improve-speed-of-postgis-nearest-neighbor-query/

If you want to find information in Postgis on spatial reference systems look in the table spatial_ref_sys. 如果要在空间参考系统上查找Postgis中的信息,请查看表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. 这表明它是使用GRS80椭圆的北美基准,而不是巴西,这与4326使用的WGS84不同,后者是GPS使用的标准。 This information is what is used by the Proj4 projections library, which is included as part of a Postgis build on Postgres. 这些信息是Proj4投影库使用的,它是作为Postgis Postgis构建的一部分包含在Postgres中的。

I searched for Brasil in spatial_ref_sys, 我在spatial_ref_sys中搜索了Brasil,

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. 我确实在网上找到了对29101的引用,它使用的是南美数据,其中包含巴西Polyconic投影和以米为单位的单位。

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) 要点(5476247.04359163 7178517.77380949)

and would allow you to use ST_Distance or ST_DWithin in meters. 并允许您以米为单位使用ST_Distance或ST_DWithin。 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. 我只放了ST_AsText以便你可以看到输出,你显然不会在使用ST_Transform的更新中实际使用它。

Note the use of ST_Makepoint instead of concatenating coordinates -- the same thing, but easier to read, imho. 注意使用ST_Makepoint而不是连接坐标 - 同样的事情,但更容易阅读,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. 要回答你的第二个问题,因为你有效地工作在4326,lat / lon,范围从-180到180和-90到90,如果你在ST_DWithin中使用1000作为距离,你将获得返回的每条记录,因为1000不是米,而是度,所以你必须使用类似上面的系统。

If you want to take distance in lat/lon, have a look at the haversine formula which will work fine for short distances. 如果你想在纬度/经度上取一段距离,可以看一下在短距离内可以正常使用的半正式公式

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM