组 lat/lon ,如果它们出现在边界框内

Posted

技术标签:

【中文标题】组 lat/lon ,如果它们出现在边界框内【英文标题】:Group lat/lon , if they occur witin a bounding box 【发布时间】:2021-12-26 18:16:45 【问题描述】:

我有一个如下的数据框:

我创建了一个 10 公里乘 10 公里的边界框。

def create_space(lat, lon, s=10):
    n = (180/math.pi)*(500/6378137)*10
    return lat - n, lon - n, lat + n, lon + n

现在,我想看看我的数据框中是否有两行或多行(纬度和经度)在边界框内。如果任何纬度和经度落在边界框内,我想添加该事件。 例如,如果 index[9] 落在 index[0] 的边界框上,则出现次数为 6495+23,并且 index[9] 将被删除。

我知道了:

我试过了

step =0.1
to_bin = lambda x: np.floor(x / step) * step
df["latbin"] = df.lat.map(to_bin)
df["lonbin"] = df.lon.map(to_bin)
#groups = df.groupby(("latbin", "lonbin"))

但它并没有解决我的问题,我不知道如何进一步。

【问题讨论】:

这能回答你的问题吗? Is a lat lon within a bounding box? 是的,它部分是。但我想检查边界框内是否有 lon 和 lat,如果是,则添加事件并合并行。 嗨 Nicky - 欢迎来到 ***! Please do not upload images of code/errors when asking a question. - 图像不能被索引/搜索,不能被屏幕阅读器读取,容易损坏等。相反,在显示输入/输出时使用格式化的代码块。谢谢! 另外,你能描述一下为什么它没有解决你的问题吗?如果导致错误,请提供traceback。 groupby 方法看起来很合适,因此它可以帮助我们更多地了解问题所在。 【参考方案1】:

我的解决方案是创建一个geopandas.geoseries.GeoSeries,它代表您在地图上的边界框。然后在 Python 中有现有的工具来测试一个点是否在它里面。

但是由于我没有你的数据,这里我只是用最简单的例子来向你展示我的代码是如何工作的。

# import packages
import geopandas as gpd
from shapely.ops import cascaded_union
from shapely import geometry

# create a function to build the "geopandas.geoseries.GeoSeries" for your bounding box
def create_boundingbox(p1,p2,p3,p4): 
    '''provide four corner points as (lon,lat), 
       the order is bottom-left(p1), bottom-right(p2), top-right(p3),top-left(p4) on a map'''
    p1 = geometry.Point(p1)
    p2 = geometry.Point(p2)
    p3 = geometry.Point(p3)
    p4 = geometry.Point(p4)
    pointList = [p1, p2, p3, p4, p1]
    boundingbox = geometry.Polygon([[p.x, p.y] for p in pointList])
    boundingbox = gpd.GeoSeries(cascaded_union(boundingbox))
    return boundingbox

# use some simple points as an example

# construct your box
p1 = (0,0)
p2 = (1,0)
p3 = (1,1)
p4 = (0,1)

box1 = create_boundingbox(p1,p2,p3,p4)

# now test if these points are inside or not
p5 = geometry.Point(0.5,0.5)
p6 = geometry.Point(15,15)

print(box1.contains(p5)) # this is True
print(box1.contains(p6)) # this is False

【讨论】:

【参考方案2】:

请注意,当使用 lon-lat 时,您是在暗示某种地球,并且一组点描述了球体表面上两个可能的球形多边形。在这里,我假设您想要较小的。如果您以逆时针方向对点进行排序,则可以使用此处描述的方法使用线性代数。

这会考虑到您绕极点或逆子午线的情况,并查看连接点的大圆(因此连接 (000, 45) 和 (100, 45) 的线不是 遵循 45 度平行线。因此,请仔细考虑您在正方形中的含义,因为正方形(我们通常认为它们的方式)不能很好地覆盖在球体上。

根据您的申请,Jeremy 的回答可能就足够了,但在某些情况下可能不够。

我在下面建议的方法是基于对I asked here 的问题的回答,它解释了我在这里实施的数学。

首先,您需要将点转换为矢量(可以使用单位球体)

import numpy as np
def deg2rad(theta):
    return(theta * np.pi / 180)
def get_xyz(p):
    lon = deg2rad(p[0])
    lat = deg2rad(p[1])
        
    return(np.array([np.cos(lat) * np.cos(lon),
                     np.cos(lat) * np.sin(lon),
                     np.sin(lat)]))

因此,为您的四个点创建四个向量来描述笛卡尔坐标中的位置。例如,

p1 = [ 170, -10]
p2 = [-170, -10]
p3 = [-170, 10]
p4 = [ 170, 10]
v1 = get_xyz(p1)
v2 = get_xyz(p2)
v3 = get_xyz(p3)
v4 = get_xyz(p4)

多边形的每一边是great circle 的一部分,垂直于平面的向量是n1 = np.cross(v1, v2),等等:

n1 = np.cross(v1, v2)
n2 = np.cross(v2, v3)
n3 = np.cross(v3, v4)
n4 = np.cross(v4, v1)

如果某个点 v5 在 v1、v2、v3 和 v4 所描述的多边形内,则 v5 与每个 n 向量的点积将大于 1。(如果它在一个边缘边数等于 1。)

p5 = [180, 0]
v5 = get_xyz(p5)

in_poly = (np.dot(v5, n1)) > 0 and \
    (np.dot(v5, n2)) > 0 and \
    (np.dot(v5, n3)) > 0 and \
    (np.dot(v5, n4)) > 0

print(in_poly) # True

p5 = [180, 20]
v5 = get_xyz(p5) 

in_poly = (np.dot(v5, n1)) > 0 and \
    (np.dot(v5, n2)) > 0 and \
    (np.dot(v5, n3)) > 0 and \
    (np.dot(v5, n4)) > 0

print(in_poly) # False

编辑:

今天早上我意识到我没有解释如何对数据进行分类。我在这里把它放在字典里,但你可以在数据框中创建一个列来做类似的事情。另外,并不是说我的网格框需要是凸的(没有 pacman 形状),并且网格点的角需要按逆时针顺序提供。

def in_poly(poly, point):
   v_vec = [get_xyz(p) for p in poly]
   n_vec = [np.cross(v_vec[i], v_vec[(i+1)%len(v_vec)]) for i in range(len(v_vec))]
   v_p = get_xyz(point)
   dot_prod = [np.dot(n, v_p) for n in n_vec]
   if all(d > 0 for d in dot_prod):
      return True
   else:
      return False

p1 = [ 170, -10]
p2 = [-170, -10]
p3 = [-170,  10]
p4 = [ 170,  10]
p5 = [ 150, -10]
p6 = [ 150,  10]
P = 1: [p1, p2, p3, p4],
     2: [p1, p4, p6, p5]
InBox = 1: 0, 2: 0

Npts = 100
lonlim = [150, 190]
latlim = [-20, 20]
points = np.stack([np.random.randint(lonlim[0], lonlim[1], Npts),
                   np.random.randint(latlim[0], latlim[1], Npts)])


for i in range(Npts):
   for key in InBox.keys():
      if in_poly(P[key], points[:,i]):
         InBox[key] += 1

print(InBox) 
#1: 25, 2: 22

【讨论】:

以上是关于组 lat/lon ,如果它们出现在边界框内的主要内容,如果未能解决你的问题,请参考以下文章

当我将其粘贴到 vim 时,怎么会出现语法错误?

应用小部件布局:图像上方的文本行,在边界框内居中

如何从 2D numpy (lat,lon) 数组中删除扇区/切片?

如何在地图上找到围绕对角线的边界矩形? (地理位置)

R - 数据帧中的条件更新坐标列

来自 Lat/Lon 的邮政编码(批量查询)[重复]