【问题标题】:Spatial index/query (finding k nearest points)空间索引/查询(查找 k 个最近点)
【发布时间】:2011-08-26 22:16:02
【问题描述】:

我有 +10k 点(纬度、经度),我正在构建一个应用程序,向您显示离用户位置最近的 k 个点。

我认为这是一个非常普遍的问题,我不想重新发明轮子。我正在学习四叉树。这似乎是解决这个空间问题的好方法。

我正在使用这些工具:

  • Python 2.5
  • MySQL
  • MongoDb

构建四叉树并不难:http://donar.umiacs.umd.edu/quadtree/points/pointquad.html 但是一旦我创建了树并将其保存到数据库(MySQL 或 MongoDb),我如何运行查询?

我需要运行如下查询:

  1. 查找用户位置 10 公里范围内的所有点。
  2. 找到最接近的 6 个(或至少 6 个)点 用户的位置。

做这件事的标准和常用方法是什么?

编辑 1:

我已将 +10k 点加载到 MongoDB(地理空间索引)中,乍一看它运行良好。反正我找到了PostGis:

PostGIS 是 PostgreSQL 对象关系数据库系统的扩展,它允许将 GIS(地理信息系统)对象存储在数据库中。

所以我想我会试试 PostGis。

我还找到了SimpleGeo。您可以将点/地点存储在云端,然后通过 API 进行查询:https://simplegeo.com/docs/tutorials/python#how-do-radial-nearby-query

【问题讨论】:

    标签: python geospatial spatial spatial-index spatial-query


    【解决方案1】:

    您可能想查看维基百科中的kdtree 条目。当您也有两个以上的维度时(与四叉树不同),这将很有用。我建议使用 kd-tree,因为该条目具有用于创建和查询树的 python 代码。

    【讨论】:

      【解决方案2】:

      MongoDB 有 support for spatial indexes built-in,因此您只需使用正确的格式加载点,创建空间索引,然后运行查询。

      举个简单的例子,我在 mongo shell 中加载了所有 50 个状态的中心点:

      > db.places.ensureIndex({loc: "2d"})
      > db.places.save({name: "AK", loc: {long: -152.2683, lat: 61.3850}})
      > db.places.save({name: "AL", loc: {long: -86.8073, lat: 32.7990}})
      > db.places.save({name: "AR", loc: {long: -92.3809, lat: 34.9513}})
      > db.places.save({name: "AS", loc: {long: -170.7197, lat: 14.2417}})
      > ...
      

      接下来,查询 离给定位置最近的 6 个点

      > db.places.find({loc: { $near: {long: -90, lat: 50}}}).limit(6)
      {"name" : "WI", "loc" : { "long" : -89.6385, "lat" : 44.2563 } }
      {"name" : "MN", "loc" : { "long" : -93.9196, "lat" : 45.7326 } }
      {"name" : "MI", "loc" : { "long" : -84.5603, "lat" : 43.3504 } }
      {"name" : "IA", "loc" : { "long" : -93.214, "lat" : 42.0046 } }
      {"name" : "IL", "loc" : { "long" : -89.0022, "lat" : 40.3363 } }
      {"name" : "ND", "loc" : { "long" : -99.793, "lat" : 47.5362 } }
      

      接下来,查询给定位置 10 公里内的所有点。由于我在计算最近的州,我将使用 888 公里(大约是纬度 8 度):

      > db.places.find({loc: { $near: {long: -90, lat: 50}, $maxDistance: 8}})
      {"name" : "WI", "loc" : { "long" : -89.6385, "lat" : 44.2563 } }
      {"name" : "MN", "loc" : { "long" : -93.9196, "lat" : 45.7326 } }
      

      由于one degree of latitude is approximately 111.12km,您将使用$maxDistance: 0.08999 来代表您的应用程序的10 公里。

      更新 默认情况下,MongoDB 假定“理想的平坦地球模型”,但这会导致不准确,因为经度线会聚在两极。 MongoDB versions 1.7+ support spherical distance calculations,提供更高的精度。

      这是一个使用球面距离运行上述查询的示例。 maxDistance 的单位是弧度,所以我们需要除以地球的平均半径:

      > db.runCommand({geoNear: "places", near: [-90, 50], spherical: true, 
                       maxDistance: 800/6378});
      (summarizing results as they're too verbose to include)
      "MN"  dis: 0.087..
      "WI"  dis: 0.100..
      "ND"  dis: 0.120..
      

      【讨论】:

      • +1。使用带有空间扩展的数据库要容易得多。还有spatial extensions in MySQL,另见here
      • $maxDistance的单位是什么??这个答案有一种巨大的球面几何气味......它似乎没有考虑到赤道的 经度 度约为 111 公里,减少到 ZERO 两极公里。
      • 我的示例只是为了证明存在 Mongo 空间扩展。我提供的链接显示了如何使用平面或球面距离功能(取决于所使用的软件版本)。
      • 您的示例展示了如何使用蹩脚的平坦地球模型,没有任何警告。就像他们蹩脚的文档一样,首先提到的是平面地球模型。还是-1。
      【解决方案3】:

      如果您想使用 MongoDB,请仔细阅读their docs。默认模型是平地假设经度与纬度具有相同的长度

      我引用:“”“当前的实现假设一个平坦地球的理想模型,这意味着纬度 (y) 和经度 (x) 的弧度在任何地方都表示相同的距离。这仅在它们所在的赤道处是正确的两者都大约等于 69 英里或 111 公里。但是,在 { x : -74 , y : 40.74 } 的 10gen 办事处,经度的一个弧度约为 52 英里或 83 公里(纬度不变)。这意味着 1 英里向北似乎比向东 1 英里的地方更近。"""

      您需要他们的“新球形模型”。请注意:您需要按顺序使用 (longtitude, latitude) -- 再次仔细阅读他们的文档。

      【讨论】:

      • 我知道这一点:顺序(lng,lat),球面($nearSphere,$centerSphere,$box 保持不变)和距离使用弧度。
      猜你喜欢
      • 2018-02-11
      • 1970-01-01
      • 1970-01-01
      • 2011-02-01
      • 2015-03-27
      • 1970-01-01
      • 2017-10-16
      • 2018-06-05
      • 2016-07-19
      相关资源
      最近更新 更多