给出纬度/经度的最快方法(纬度,城市,州)

时间:2009-08-11 14:01:36

标签: algorithm geolocation

我需要一个免费的(开源)解决方案,给定lat / lng可以返回壁橱城市/州或邮编。 mysql不是一个选项,如果可能的话,小型轻量级数据库将是最好的。

更新:没有网络服务,即使是最小的插件也会每天带来5000万次展示,因此添加服务请求会占用响应时间。我不希望在请求上添加超过200毫秒。

我在csv中有数据库,lat / lon / zip / city / state它只是如何存储,更重要的是如何以最快的速度检索它。

10 个答案:

答案 0 :(得分:10)

这是一个非常有趣的问题,答案很复杂。

你提到一个有纬度/经度的城市数据库,但城市不是单点,这可以在人口密集的地区产生很大的不同,城市A的大部分地区可能更接近城市B的“中心”而不是城市中心A.在一个被较小郊区包围的大城市。大城市的外围部分可能更靠近郊区的中心,而不是大城市的中心。捕捉到最近的市中心意味着地图是城市中心点的Voronoi图。这样的地图看起来不像是城市地区的实际地图。

如果你想知道一个给定纬度/经度的城市和州,你需要查询一个合适的地图,并在多边形测试中指出它是什么。这听起来计算成本很高,但它实际上是如果您使用适当的空间索引并且在编码时要小心,那就不错了。我运行一个销售此访问和其他地理查询的API访问的网站,我们的底层引擎(用Java编写)可以返回美国的包含或最近的城市,平均查询时间为3e-4秒(超过3,000个查询)每秒)。

即使我们正在销售它,我也很乐意解释它是如何工作的,因为从我们这里购买它比自己制造它更便宜,即使有说明也是如此。所以这里是:

  • 找到您想要的地图。对于美国地区,美国人口普查在http://www.census.gov/geo/www/tiger/tgrshp2010/tgrshp2010.html提供极其准确的地图。我没有找到与美国人口普查地图一样好的全球地图,但它们可能存在。
  • 查找或编写ESRI shapefile格式的解析器。我没有这方面的特定链接,因为它高度依赖于语言,但网上有许多免费和商业解析器。只需搜索“shapefile解析器”以及您的编程语言。
  • 将地图加载到内存中。数字地图由一系列由纬度/经度对列表示的多边形组成,通常以逆时针方向排序。大多数地图允许切口(例如,南非的莱索托),其仅被列为多边形,其中纬度/经度对以顺时针方向列出。出于性能和内存消耗的原因,您将需要使用原始浮点数组(避免双精度,因为它浪费内存,并尽可能使用本机数组,以避免装箱)。
  • 接下来,您将需要代码来回答给定多边形中是否包含给定查询点。以下是对多边形点问题的精彩讨论:How can I determine whether a 2D Point is within a Polygon?
  • 根据我的经验,在另一个答案(检查每个实体)中建议的暴力技术在国家或世界地图上不能很好地运作。相反,我强烈建议快速空间索引返回给定纬度/经度的候选多边形列表。这里有很多选择。很多人会建议基于树的索引,但我倾向于选择网格索引,因为它们更快,现代服务器往往有大量内存。我写了唯一一个与我合作的索引。我知道它们存在于GIS库中,但我发现大多数GIS代码过于复杂,缓慢且难以使用。因此,给定一个查询lat / lon,您可以从空间索引中获取候选多边形列表,并使用多边形点函数来查找哪些候选包含查询点。
  • 处理任何多边形都不包含查询点的情况也很重要。在这种情况下,您可能希望找到最接近指定最大距离的最近这样的多边形。为此,您需要确保空间索引可以返回附近多边形的列表,而不仅仅是包含多边形的候选列表。您还需要使用代码来计算查询点和纬度/经度线段之间的距离(这很难,因为纬度/经度不是欧几里德空间)。我没有找到任何关于如何在网上做这个的好讨论,所以我设计了自己的方法。它的工作原理是在查询点周围创建一个线性化空间(在新空间中变为(0,0)),其中相对经度被重新缩放,使得经修改的经度的程度为与纬度相同的距离(包括将相对经度乘以纬度的余弦)。在此线性化空间中,您可以使用标准方法找到线段上的最近点(请参阅Shortest distance between a point and a line segment),然后将该点转换回lat / lon并使用Haversine公式计算两点之间的距离(请参阅Calculate distance between two latitude-longitude points? (Haversine formula))。

就是这样。我打造和关闭了这个系统大约半年。我的估计是,其中至少有三个月的严格编码,这是熟悉该主题的人(因此,请注意,如果您正在做出购买或构建决策)。

答案 1 :(得分:9)

暴力:将所有数据预加载到阵列中。计算当前点与数组中每个点之间的距离(有一种方法可以使用线性代数而不是触发函数进行此计算,但我不记得它是什么,以便找到最近的点。

请在向下投票之前阅读:有很多方法可以加快像这样的暴力搜索,但我发现他们通常不值得这么麻烦。我不仅在使用此方法之前从纬度/经度找到最近的拉链,我已经在Windows Mobile应用程序中使用它(处理能力不是很强大)并且仍然实现了亚秒级搜索时间。只要你避免使用trig函数,这不是一个昂贵的过程。

更新:您可以通过将您的zip数据分配到子区域(象限,例如西北,东南等)并使用每个数据点保存区域ID来加快搜索时间。然后,在搜索中,首先确定当前位置所在的区域,并仅与这些数据点进行比较。

为了避免边界错误(例如当您的当前位置靠近其区域的边缘但实际上最接近相邻区域中的zip时),您的区域应该在某种程度上重叠。这意味着您的一些邮政记录将被复制,因此您的整体数据集将会更大。

答案 2 :(得分:3)

使用kd-tree加速最近邻搜索。无论您的平台是什么,都应该有很多免费的实现。

答案 3 :(得分:1)

它不是开源的,但也许您可以使用Google Maps API:

Reverse Geocoding

答案 4 :(得分:1)

你应该看看geonames。他们有一个返回XML和/或JSON的API。   另外,你可以使用他们的数据库。

答案 5 :(得分:0)

另一个线程通过MaxMind推荐mod_geoip。 它在Apache级别运行,甚至在它进入PHP / .NET / Java之前。 Maxmind geolocation apis: Apache vs PHP

答案 6 :(得分:0)

如果您同时拥有拉链的长度和纬度以及当前位置,则可以计算半径并找到该圆圈内的点。如果您设定每个邮政编码范围的假定边界,则可以加快搜索速度。

如果您可以使用SQL 2008(标准版或快速版),则可以使用Spatial data类型。

答案 7 :(得分:0)

Yahoo! Placemaker是一个免费的网络服务,可以做到这一点。它可以查找地名(“纽约市”,“白金汉宫”),但也可以使用Geo microformat来查找纬度和经度。

要使用该服务,请提交POST请求,然后返回XML:

一个小命令行示例(我隐藏了我的Yahoo!应用程序ID;您需要注册自己的代码):

$ curl -X POST -ddocumentContent='<div class="geo">GEO: <span class="latitude">37.386013</span>, <span class="longitude">-122.082932</span></div>' -ddocumentType='text/html' -dappid='your_yahoo_app_id' http://wherein.yahooapis.com/v1/document

这将返回一个非常详细的XML文档,其中一部分是:

<type>Town</type>
<name><![CDATA[Los Altos, CA, US]]></name>

它还包含以下数据:

<type>Zip</type>
<name><![CDATA[94024, Los Altos, CA, US]]></name>

我没有非常使用Placemaker,但我使用了他们的Geocoding API并且它非常快。将其与本地memcached结合使用,用户不知道数据不是本地数据。

答案 8 :(得分:0)

查看geonames.org数据库中的源数据。

对于轻型数据库,sqlite是一个不错的选择。

geonames也可以进行网络服务,但如果您想在没有网络电话的情况下自己完成(听起来就好像这样),那么您将需要一个本地数据库。然后,您只需要进行正确的三角形计算,以计算出一对纬度/经度点之间的大圆距离(谷歌那个),然后按距离对结果进行排序。如果要在执行计算之前限制搜索半径,也可以使用边界框或半径。

如果您的本地数据库可以基于SQL(sqllite3是),则所有这些都会增加一个SQL查询,该查询会添加一堆三角形计算来计算“距离”列,也可能是一个类似的“where”子句来限制在半径或边界框内搜索。计算了查询中的距离列后,可以轻松按距离排序并添加您喜欢的任何其他条件。如果您了解ruby / rails并希望看到如何完成此操作的一个很好的示例,请查看GeoKit rails插件源代码。

答案 9 :(得分:0)

您预计离您最近的城市有多远? 50英里? 200英里? 500英里?如果两个城市几乎是等距的,那么如果你的算法选择了更接近的算法,这是否重要?您可以使用此信息来加快搜索速度。

如果您可以合理地假设距离差异很小(约250英里左右可能足够接近被认为是'小'),并且您的距离计算可能有点“模糊”,那么您可以优化'蛮力'通过限制你的搜索空间到源头的+/- 5纬度(每个纬度大约70英里,所以这给你大约350英里到北方和南方),并且+/- 5长(假设你不是在极地寻找城市,从赤道约350英里到加拿大北部约100英里。将这些范围调整为您认为适合您的问题空间的范围。

虽然触发功能将帮助您精确指示距离,但对于较小的距离,例如这些毕达哥拉斯通常足够接近“最佳猜测”答案,x = 69.1 *(sourcelat - citylat)和y = 53.0 * (sourcelong - citylong)。