【问题标题】:How do I query all rows within a 5-mile radius of my coordinates?如何查询坐标 5 英里半径内的所有行?
【发布时间】:2015-12-11 02:39:19
【问题描述】:

这是我的 PostgreSQL CSV 格式示例。

row,latitude,longitude
1,42.082513,-72.621498
2,42.058588,-72.633386
3,42.061118,-72.631541
4,42.06035,-72.634145

我在世界各地还有数千行类似这些跨越坐标的行。

我只想在表格中查询某个半径内的坐标。我如何使用 PostGIS 和 PostgreSQL 做到这一点?

【问题讨论】:

  • 您希望使用 GiST 或 GIN 索引进行 K-最近邻 (KNN) 搜索。
  • @CraigRinger 我从来没有做过任何涉及这些的事情。我在哪里可以了解更多信息?
  • 您将数据保存在 CSV 格式的文件中,或者保存在包含 CSV 格式文件中此类数据的表格中?您正在寻找“一定半径内的坐标”。从特定位置还是您想要任何位置的解决方案(例如带参数的函数)?

标签: sql postgresql indexing postgis


【解决方案1】:

您想要“坐标半径 5 英里内的所有行”,所以这完全是 K-nearest-neighbour (KNN) problem。相关,但你的情况更简单。 “找到离我的坐标最近的 10 行”将是一个 KNN 问题。

将您的坐标转换为geography 值:

ST_SetSRID(ST_MakePoint(longitude, latitude),4326)::geography

您也可以使用更简单的geometry 类型。考虑:
4.2.2. When to use Geography Data type over Geometry data type

然后我们有一个像这样的表:

CREATE TABLE tbl (
  tbl_id serial PRIMARY KEY
, geog geography NOT NULL
);

您只需要ST_DWithin() - 和一个空间索引来加快速度:

CREATE INDEX tbl_geog_gist ON tbl USING gist(geog);

查询:

SELECT *, ST_Distance(c.x, geog) AS distance  -- distance is optional
FROM   tbl t, (SELECT ST_GeographyFromText('SRID=4326;POINT(-72.63 42.06)')) AS c(x)
WHERE  ST_DWithin(c.x, geog, 8045)  -- distance in meter
ORDER  BY distance; -- order is optional, you did not ask for that

或者您可以使用原始列并创建功能索引... dba.SE 上这个密切相关的答案中的这个和其他细节:

【讨论】:

    【解决方案2】:

    您应该首先从您的 CSV 格式文件创建一个表,使用 COPY command(如果该文件可被 PostgreSQL 服务器访问)或 \copy command in psql 如果文件不是服务器本地的。如果您有任何问题,请参阅 SO 上的其他 Q+A 示例。

    将数据放入表中后,您应该将 longitudelatitude 列转换为 PostGIS geography 类型,方法是向您的 geography(POINT, 4326) 类型的表中添加一列,然后填充该列(此处使用适当的值调用 gps):

    UPDATE my_table SET gps = ST_SetSRID(ST_MakePoint(longitude, latitude), 4326);
    

    在该列上添加索引以实现高效搜索:

    CREATE INDEX my_table_gps ON my_table USING gist(gps);
    

    您现在可以找到距给定位置 5 英里范围内的行,例如(-72.657, 42.0657),如下:

    SELECT *
    FROM my_table
    WHERE ST_DWithin(gps, ST_SetSRID(ST_MakePoint(-72.657, 42.0657), 4326), 5 * 1609);
    

    请注意,geography 列上的 ST_DWithin() 将以米为单位,因此您必须将半径以英里为单位乘以 1,609 米为一英里。

    【讨论】:

      【解决方案3】:

      我综合了 Erwin 和 Patrick 的答案。

      -- 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);
      

      【讨论】:

        猜你喜欢
        • 2011-05-10
        • 2015-06-15
        • 2019-11-29
        • 1970-01-01
        • 1970-01-01
        • 2012-02-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多