我在处理二维地理数据。我有一长串的轮廓路径。现在我想确定在我的领域内的每一个点有多少个轮廓(也就是说,我想计算轮廓所代表的特征的空间频率分布)。在
为了说明我想做什么,这里有一个非常天真的实现:
import numpy as np
from shapely.geometry import Polygon, Point
def comp_frequency(paths,lonlat):
"""
- paths: list of contour paths, made up of (lon,lat) tuples
- lonlat: array containing the lon/lat coordinates; shape (nx,ny,2)
"""
frequency = np.zeros(lonlat.shape[:2])
contours = [Polygon(path) for path in paths]
# Very naive and accordingly slow implementation
for (i,j),v in np.ndenumerate(frequency):
pt = Point(lonlat[i,j,:])
for contour in contours:
if contour.contains(pt):
frequency[i,j] += 1
return frequency
lon = np.array([
[-1.10e+1,-7.82+0,-4.52+0,-1.18+0, 2.19e+0,5.59e+0,9.01+0,1.24+1,1.58+1,1.92+1,2.26+1],
[-1.20e+1,-8.65+0,-5.21+0,-1.71+0, 1.81e+0,5.38e+0,8.97+0,1.25+1,1.61+1,1.96+1,2.32+1],
[-1.30e+1,-9.53+0,-5.94+0,-2.29+0, 1.41e+0,5.15e+0,8.91+0,1.26+1,1.64+1,2.01+1,2.38+1],
[-1.41e+1,-1.04+1,-6.74+0,-2.91+0, 9.76e-1,4.90e+0,8.86+0,1.28+1,1.67+1,2.06+1,2.45+1],
[-1.53e+1,-1.15+1,-7.60+0,-3.58+0, 4.98e-1,4.63e+0,8.80+0,1.29+1,1.71+1,2.12+1,2.53+1],
[-1.66e+1,-1.26+1,-8.55+0,-4.33+0,-3.00e-2,4.33e+0,8.73+0,1.31+1,1.75+1,2.18+1,2.61+1],
[-1.81e+1,-1.39+1,-9.60+0,-5.16+0,-6.20e-1,3.99e+0,8.66+0,1.33+1,1.79+1,2.25+1,2.70+1],
[-1.97e+1,-1.53+1,-1.07+1,-6.10+0,-1.28e+0,3.61e+0,8.57+0,1.35+1,1.84+1,2.33+1,2.81+1],
[-2.14e+1,-1.69+1,-1.21+1,-7.16+0,-2.05e+0,3.17e+0,8.47+0,1.37+1,1.90+1,2.42+1,2.93+1],
[-2.35e+1,-1.87+1,-1.36+1,-8.40+0,-2.94e+0,2.66e+0,8.36+0,1.40+1,1.97+1,2.52+1,3.06+1],
[-2.58e+1,-2.08+1,-1.54+1,-9.86+0,-3.99e+0,2.05e+0,8.22+0,1.44+1,2.05+1,2.65+1,3.22+1]])
lat = np.array([
[ 29.6, 30.3, 30.9, 31.4, 31.7, 32.0, 32.1, 32.1, 31.9, 31.6, 31.2],
[ 32.4, 33.2, 33.8, 34.4, 34.7, 35.0, 35.1, 35.1, 34.9, 34.6, 34.2],
[ 35.3, 36.1, 36.8, 37.3, 37.7, 38.0, 38.1, 38.1, 37.9, 37.6, 37.1],
[ 38.2, 39.0, 39.7, 40.3, 40.7, 41.0, 41.1, 41.1, 40.9, 40.5, 40.1],
[ 41.0, 41.9, 42.6, 43.2, 43.7, 44.0, 44.1, 44.0, 43.9, 43.5, 43.0],
[ 43.9, 44.8, 45.6, 46.2, 46.7, 47.0, 47.1, 47.0, 46.8, 46.5, 45.9],
[ 46.7, 47.7, 48.5, 49.1, 49.6, 49.9, 50.1, 50.0, 49.8, 49.4, 48.9],
[ 49.5, 50.5, 51.4, 52.1, 52.6, 52.9, 53.1, 53.0, 52.8, 52.4, 51.8],
[ 52.3, 53.4, 54.3, 55.0, 55.6, 55.9, 56.1, 56.0, 55.8, 55.3, 54.7],
[ 55.0, 56.2, 57.1, 57.9, 58.5, 58.9, 59.1, 59.0, 58.8, 58.3, 57.6],
[ 57.7, 59.0, 60.0, 60.8, 61.5, 61.9, 62.1, 62.0, 61.7, 61.2, 60.5]])
lonlat = np.dstack((lon,lat))
paths = [
[(-1.71,34.4),(1.81,34.7),(5.15,38.0),(4.9,41.0),(4.63,44.0),(-0.03,46.7),(-4.33,46.2),(-9.6,48.5),(-8.55,45.6),(-3.58,43.2),(-2.91,40.3),(-2.29,37.3),(-1.71,34.4)],
[(0.976,40.7),(-4.33,46.2),(-0.62,49.6),(3.99,49.9),(4.33,47.0),(4.63,44.0),(0.976,40.7)],
[(2.9,55.8),(2.37,56.0),(8.47,56.1),(3.17,55.9),(-2.05,55.6),(-1.28,52.6),(-0.62,49.6),(4.33,47.0),(8.8,44.1),(2.29,44.0),(2.71,43.9),(3.18,46.5),(3.25,49.4),(3.33,52.4),(2.9,55.8)],
[(2.25,35.1),(2.26,38.1),(8.86,41.1),(5.15,38.0),(5.38,35.0),(9.01,32.1),(2.25,35.1)]]
frequency = comp_frequency(paths,lonlat)
当然,这是尽可能低效率地编写的,使用所有显式循环,因此需要花费很长时间。在
我如何才能有效地做到这一点?
编辑:根据要求添加了一些样本数据。请注意,我的实际域比原来的域大150**2(就分辨率而言),因为我通过切片原始数组创建了样本坐标:lon[::150]
。在
因此,同时我找到了一个好的解决方案,这要感谢一位同事,他在某个时候实现了类似的东西(基于this blog post)。在
使用shapely的旧的、非常慢的方法
首先,这里是我使用shapely的参考实现,它只是我在开头文章中第一个“天真”方法的一个稍微改进的版本。它很容易理解并且有效,但是非常慢。在
新的,非常快速的解决方案使用PIL
我的(几乎)最终解决方案不再使用shapely,而是使用PIL(Python图像库)中的图像处理技术。这个解决方案要快得多,多得多,虽然这种形式只适用于规则的矩形网格(见下面的注释)。在
^{pr2}$值得注意的是,这两种方法的结果并不完全相同。用PIL方法识别的特征比用shapely方法识别的特征稍大一些,但实际上其中一个并不比另一个好。
时间安排
以下是一些使用简化数据集创建的计时(尽管不是来自开场白的半人工示例数据):
最耗时的步骤是在轮廓点的不规则lon/lat网格上寻找索引。其中最消耗时间的部分是构造cKDTree,这就是为什么我把它移出
get_indices
。为了将其放在透视图中,pil-tree-inside
是在get_indices
中创建树的版本。pil-regular-grid
与regular_grid=True
一起使用,对于我的数据集,这会产生错误的结果,但是给出了一个ide,说明了它在常规网格上的运行速度。在总的来说,现在我已经设法消除了非规则网格的影响(
pil
vs.pil-regular-grid
),这是我在开始时所希望的!:)以下是计时的结果。。。。其中comp_frequency_lc是指 使用列表理解
^{pr2}$如果你的输入多边形实际上是轮廓,那么你最好直接使用你的输入网格,而不是计算轮廓并测试其中是否有一个点。在
等高线遵循网格数据的常量值。每个轮廓都是一个多边形,包含输入栅格中大于该值的区域。在
如果您需要知道一个给定的点在里面有多少个轮廓,那么在该点的位置对输入网格进行采样并操作返回的“z”值会更快。如果知道创建轮廓的值,可以直接从中提取它内部的轮廓数。在
例如:
相关问题 更多 >
编程相关推荐