按经纬度将城市划分为矩阵

Posted

技术标签:

【中文标题】按经纬度将城市划分为矩阵【英文标题】:Dividing City by Longitude and Latitude into Matrix 【发布时间】:2018-03-24 17:50:29 【问题描述】:

您如何根据经度和纬度坐标将旧金山等城市划分为大小相等的块?这样做的目的是,当我收到城市中某个位置的坐标时,我想将其自动分配到这些街区之一中。

【问题讨论】:

How to create a Grid is the viewing area of a map的可能重复 相关问题:Grid on top of Google maps produces gaps in squares How do I create a grid in google maps api v3 的可能副本 也考虑实现自己的 Geohash 或使用公开可用的库。 【参考方案1】:

这里是公开可用的 python 库模块 Geohash (https://github.com/vinsci/geohash) 的简单使用。

python hashmap(字典)用于将 Geohash 字符串映射到已添加到由 Geohash 表示的区域(“块”)中的 lat/lng 点列表。通过计算位置的 Geohash 并访问该条目的哈希图以检索该“块”的所有条目来执行查询。

    ### Add position to Geohash
    ###    Each hash mapping contains a list of points in the "block"
    ### 
    def addToGeohash(m, latitude, longitude):
        p = (latitude, longitude)
        ph = encode(p[0],p[1],5)
        if ph not in m:
            m[ph] = []
        m[ph].append(p)
        return

    ### Get positions in same "block"   or empty list if none found
    ###
    def getFromGeohash(m, latitude, longitude):
      p = (latitude, longitude)
      ph = encode(p[0],p[1],5)
      print ("query hash: " + ph)
      if ph in m:
        return m[ph]
      return []


    ### Test
    m = dict()

    # Add 2 points in general area (1st near vista del mar)
    addToGeohash(m, 37.779914,-122.509431)
    addToGeohash(m, 37.780546,-122.366189)

    # Query a point 769 meters from vista del mar point
    n = getFromGeohash(m, 37.779642,-122.502993)

    print ("Length of hashmap: "+str(len(m)))
    print ("Contents of map  : "+str(m))
    print ("Query result     : ")
    print (n)

默认精度为 12 个字符(示例中使用了 5 个),会影响字典映射效率或“块”大小。

请注意,使用基于 lat/lng 的方法是一种非线性方法,因此在广阔的区域或靠近两极的地方不会是“大小相等的块”。然而, 在 San Fransisco 区域内,如果具有足够的精度,这种非线性会显着降低。

输出

query hash: 9q8yu
Length of hashmap: 2
Contents of map  : '9q8yu': [(37.779914, -122.509431)], '9q8yz': [(37.780546, -122.366189)]
Query result     :
    [(37.779914, -122.509431)]

要了解各种精度的块大小,请使用 link 并输入例如 37.779914,-122.509431 和 5 的精度。试验精度。

以下是精度为 5-8 的近似框尺寸:

5 ≤ 4.89km × 4.89km 
6 ≤ 1.22km × 0.61km 
7 ≤ 153m × 153m 
8 ≤ 38.2m × 19.1m 

Geohash 的一个有趣功能主要(并且始终与 SanFran 区域一起使用)是您可以通过操纵最后一个字符轻松找到 8 个相邻的邻居。因此,您可以用最少的努力使用更高的精度(例如 8)并将其减小到 7 到 8 之间的大小。

【讨论】:

【参考方案2】:

天真的方法是在整个城市(您想要覆盖的区域)周围找到一个足够大的矩形,然后通过所需的块数可以推断出矩形边缘有多少部分,这应该是一个相当基本的数学 给定一个点,您可以以非常快速的方式将其分配给它的块(只需检查它的 lan 并长时间查看它在网格中的位置)

【讨论】:

【参考方案3】:

多年来,我编写了该算法的不同版本。花了很多时间思考这个问题。涉及两个问题:

    将二维坐标映射到一维值。 (从根本上说,为了达到最佳效果,您必须知道两个维度的界限) 将覆盖球体的平面大致切割成“正方形”。 (当您靠近两极时,正方形的大小会有所不同。此外,它们实际上从来都不是正方形,但地球是如此之大,这通常无关紧要)

这是我编写的一些符合我的目的的代码。需要考虑的几点:

这会创建您指定的固定大小的块。 python Geohash 库做了一件很酷的事情,可以截断散列以产生更大尺寸的散列。该算法不会这样做,但另一方面,您可以(大致)指定所需的块大小。 这实际上创建了二维坐标,我将其视为一维字符串。我这样做是因为它对我来说已经足够好了,而且我可以轻松地操纵字符串数据,以一种清晰且有意义的方式获取相邻的块。例如哈希:“-23,407”是中心点以西 23 个块和以北 407 个块。所以如果你想向东移动一个街区,你只需在 -23 上加 1:“-22,407”。 我在这里使用的中心点是华盛顿特区的中部。您可以使用中心点 0,0,或旧金山的中部或其他。但不要在两极附近使用中心点:-90,-180。因为当算法以公里为单位计算经度偏移时,它将计算(-90,您的经度)和(-90,-180)之间的距离。这些点在南极(我认为是南极?),距离将无限小,因为在两极,所有这些块都非常小。

这是散列点的主要算法:

# Define center point
CENTER_LAT = 38.893
CENTER_LNG = -77.084

def geohash(lat, lng, BLOCK_SIZE_KM=.05):
    # Get Latitude Offset
    lat_distance = haversine_km(
        (CENTER_LAT, CENTER_LNG),
        (lat,        CENTER_LNG)
    )
    if lat < CENTER_LAT:
        lat_distance = lat_distance*-1
    lat_offset = int(lat_distance/BLOCK_SIZE_KM)

    # Get Longitude offset
    lng_distance = haversine_km(
        (CENTER_LAT, CENTER_LNG),
        (CENTER_LAT, lng)
    )
    if lng < CENTER_LNG:
        lng_distance = lng_distance*-1
    lng_offset = int(lng_distance/BLOCK_SIZE_KM)

    block_str = '%s,%s' % (lat_offset, lng_offset)

    return block_str

我包含了这些帮助函数来计算两个坐标之间的距离:

def haversine_km(origin, destination):
    return haversine(origin, destination, 6371)

def haversine(origin, destination, radius):
    lat1, lon1 = origin
    lat2, lon2 = destination

    lat1 = float(lat1)
    lon1 = float(lon1)
    lat2 = float(lat2)
    lon2 = float(lon2)

    dlat = math.radians(lat2-lat1)
    dlon = math.radians(lon2-lon1)
    a = math.sin(dlat/2) * math.sin(dlat/2) + math.cos(math.radians(lat1)) \
        * math.cos(math.radians(lat2)) * math.sin(dlon/2) * math.sin(dlon/2)
    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
    d = radius * c

    return d

【讨论】:

以上是关于按经纬度将城市划分为矩阵的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 bing 将城市/州转换为地理位置(纬度/经度)?

使用带有geom_point的for循环将点添加到现有ggplot对象中

php 根据经纬度 获得 城市名

简单查询:各个国家最北端的城市? (纬度、城市、国家)

怎样根据GPS获得的经纬度来获取城市名?

通过经纬度获取城市名/地址(不需要三方包)