我应该使用哪种数据结构进行地理编码?

时间:2012-04-12 12:56:33

标签: python openstreetmap geocoding large-data-volumes large-data

我正在尝试创建一个Python脚本,它将地址作为输入,并在多次匹配的情况下吐出纬度和经度,或纬度和经度,就像Nominatim一样。

因此,可能的输入和输出可能是: -

  1. 在:美国纽约 =>出:纽约(纬度:x1 lon:y1)
  2. 在:纽约 =>出:纽约(纬度:x1 lon:y1)
  3. 在:美国纽约珍珠街 =>出:珍珠街(纬度:x2离子:y2)
  4. 在:美国珍珠街 =>出:珍珠街(纬度:x2离子:y2),珍珠街(纬度:x3离子:y3)
  5. 在: Pearl Street =>出:珍珠街(纬度:x2离子:y2),珍珠街(纬度:x3离子:y3)
  6. In: 103 Alkazam,纽约,美国 =>出:纽约(纬度:x1 lon:y1)
  7. 在上面的6中,纽约被归还,因为找不到地址为103 Alkazam, New York, USA的地方,但它至少可以找到New York, USA

    最初,我想到构建一个树,表示兄弟姐妹按字母顺序排序的层次结构关系。它可能是这样的: -

                                         GLOBAL
                                           |
                       ---------------------------------------------
                       |            | ...
                      USA
                 ---------------
                 |        | ...
             CALIFORNIA  NEW YORK 
                |         |
         -----------    -------------
         |        |..   |          |....
     PEARL STREET      PEARL STREET
    

    但问题是用户可以提供不完整的地址,如2,4和5。

    因此,我接下来想到使用搜索树并在每个节点中存储完全限定的地址。但这也是非常糟糕的: -

    • 这将在每个节点中存储高度冗余的数据。因为这将是一个非常大的数据,所以空间保护很重要。
    • 它无法利用用户缩小搜索空间的事实。

    我有一个附加要求。我需要检测拼写错误。我想这必须作为一个单独的问题来处理,并且可以将每个节点视为通用字符串。

    更新1

    一点阐述。输入将是一个列表,其中较低索引上的项是较高索引中项的父项;他们当然可能是也可能不是直接的父母或孩子。因此,对于查询1,输入将是["USA", "NEW YORK"]。因此,USA, New York没有返回结果是完全正常的。

    如果建筑物有地址并且我们的数据非常详细,用户应该能够找到建筑物。

    更新2(省略案例)

    如果用户查询Pearl Street, USA,那么我们的算法应该能够找到该地址,因为它知道Pearl Street已将New York作为父级,USA是其父级。

    更新3(剩余案例)

    假设用户查询101 C, Alley A, Pearl Street, New York。另外,假设我们的数据确实知道101 C但不知道Alley A。根据它101 CPearl Street的直接孩子。即使在这种情况下,它也应该能够找到地址。

3 个答案:

答案 0 :(得分:2)

感谢所有人的回答,他们的答案很有帮助,但没有解决我需要的一切。我终于找到了一种处理我所有案件的方法。该方法是我在问题中建议的修改版本。

基本方法

这里我将引用一个名为'node'的东西,它是一个类对象,它将包含地理信息,如地点实体的纬度,经度,也可能是维度,以及它的完整地址。

如果该实体的地址是'101 C,Pearl Street,New York,USA',那么这意味着我们的数据结构将至少有四个节点 - '101 C','Pearl Street','New York '和'美国'。每个节点都有一个name和一个address部分。对于'101 C',name将是'101 C',地址将是'Pearl Street,New York,USA'。

基本思想是拥有这些节点的搜索树,其中节点name将用作搜索的关键字。我们可能会得到多个匹配项,因此稍后我们需要根据节点address与查询的匹配项的匹配程度对结果进行排名。

                                    EARTH
                                      |
                ---------------------------------------------
                |                                           |
               USA                                        INDIA
                |                                           |
        ---------------------------                     WEST BENGAL
        |                         |                         |
     NEW YORK                 CALIFORNIA                 KOLKATA
        |                         |                         |
   ---------------            PEARL STREET              BARA BAZAR
   |             |                                          |
PEARL STREET   TIME SQUARE                                 101 C
   |             |
  101 C         101 C

假设我们有如上所述的地理数据。因此,搜索“101 C,纽约”不仅会返回“纽约”中的“101 C”节点,还会返回“印度”节目中的“101 C”节点。这是因为算法将仅使用name,即“101 C”来搜索节点。稍后,我们可以通过测量节点address与查询地址的差异来评估结果的质量。我们没有使用完全匹配,因为允许用户提供不完整的地址,如本例所示。

评分搜索结果

要对结果的质量进行评分,我们可以使用Longest Common Subsequence。在这个算法中,“遗漏”和“剩余”案件得到妥善处理。

我最好让代码进行交谈。下面是为此目的而定制的Python实现。

def _lcs_diff_cent(s1, s2):
    """
    Calculates Longest Common Subsequence Count Difference in percentage between two strings or lists.

    LCS reference: http://en.wikipedia.org/wiki/Longest_common_subsequence_problem.
    Returns an integer from 0-100. 0 means that `s1` and `s2` have 0% difference, i.e. they are same.
    """
    m = len(s1)
    n = len(s2)

    if s1 == s2:
        return 0
    if m == 0: # When user given query is empty then that is like '*'' (match all)
        return 0
    if n == 0:
        return 100

    matrix = [[0] * (n + 1)] * (m + 1)
    for i in range(1, m+1):
        for j in range(1, n+1):
            if s1[i-1] == s2[j-1]:
                matrix[i][j] = matrix[i-1][j-1] + 1
            else:
                matrix[i][j] = max(matrix[i][j-1], matrix[i-1][j])

    return int( ( 1 - float(matrix[m][n]) / m ) * 100 )

优化方法

我秉承上述(基本)方法,因为它强制裁员,并且如果用户在他的查询中提供了“USA”,那么我们无需查看“印度”中的节点,这无法削减杠杆率。 / p>

这种优化的方法在很大程度上解决了上述问题。解决方案是没有一个大的搜索树。我们可以将搜索空间划分为“USA”和“INDIA”。稍后我们可以进一步对这些搜索空间进行状态重新分区。这就是我所说的'切片'。

在下图中,SearchSlice表示“切片”,SearchPool表示搜索树。

                            SearchSlice()
                                  |
            ---------------------------------------------
            |                                           |
        SearchSlice(USA)                           SearchSlice(INDIA)
            |                                           |
    ---------------------------                  SearchPool(WEST BENGAL)
    |                         |                   |
 SearchPool(NEW YORK)     SearchPool(CALIFORNIA)  |- KOLKATA
    |                         |                   |- BARA BAZAR, KOLKATA
    |- PEARL STREET           |- PEARL STREET     |- 101 C, BARA BAZAR, KOLKATA
    |- TIME SQUARE
    |- 101 C, PEARL STREET
    |- 101 C, TIME SQUARE

上面几点要注意的几点......

  • 每个切片只有一个深度。嗯,这在上面并不明显。
  • 切片级别的名称不会出现在其子级的地址中。例如,SearchSlice(USA)在“USA”中维护一段状态。因此,“纽约”下的节点在address中不包含“纽约”或“美国”这个名称。其他地区也是如此。层次结构关系隐式定义完整地址。
  • '101 C'address也包括其父级name,因为它们没有被切片。

缩放可能性

如果存在存储桶(池),则存在隐式扩展可能性。我们(比如说)将“美国”的地理数据分成两组。两者都可以在不同的系统上。因此,如果'NEW YORk'池在系统A上,但'CALIFORNIA'池在系统B上,那是完全没问题的,因为它们不共享任何数据,当然除了父母。

这是警告。我们需要复制永远是切片的父母。由于切片的数量有限,因此层次结构不会太深,因此复制它们不应过于冗余。

工作代码

请参阅我的GitHub了解working demo Python code

答案 1 :(得分:1)

如何使用键值存储映射和全文搜索。

  • 位置字符串的键
  • location_level和lat& lon数据的值。
  • 搜索:
    • 将用户输入字符串拆分为单个位置字(不仅仅是逗号)
    • 搜索地图中的每个单词
    • 返回最小位置级别的lat& lon

python.dict,memcached,mongodb ....将满足你的需要。

  • 如果您的位置字数太多,将location_level拆分为新地图,则会加快两次搜索
  • 忘记位置级别,将traet作为全文搜索
  • 庞大的数据?散列密钥到短字符串或数字

需要考虑的一些问题:

  • 如何将数据存储在数据库中
  • 如何从数据中初始化搜索树(如果有的话)
  • 如何在运行时扩展/编辑搜索树
  • 输入/存储容错
  • 存储空间>速度?或速度>存储

因此,更多可用的测试用例用于用户输入

101 C, Time Square, New York, US
101 C, Pearl street, New York, US

101 C, Time Square, SomeCity, Mars
101 C
101 C, US
101 C, New York, US

101 C, New York, Time Square, US

North Door, 101 C, Time Square, New York, US
South Door, 101 C, Time Square, New York, US
情况

  • 快速掌握大量数据;
  • 全容错;
  • 通过存储和运行时轻松调整

最佳解决方案 :(也是最复杂的)

  • 平面键值映射存储
  • 全文搜索
    • 或带有B树搜索的哈希键

您的计划/网站可能会像谷歌一样快速运行。

答案 2 :(得分:0)

如果您尝试为此问题创建数据结构,我认为您将拥有数据冗余。相反,你可以使用树/图和&尝试实现一种搜索算法,该算法根据节点值搜索用户输入的单词。 模糊匹配可以帮助您生成最可能的结果,并且您可以根据其相似性 - 引用的置信度建议/向用户显示其中的前几个。

这也可以处理拼写错误等。