通过高程网格找到地面近似区域的算法?

时间:2016-05-10 03:32:52

标签: python arrays algorithm

我有一个200X200的网格/数组(即40,000个值)的10mX10m“像素”,其值表示该点的平均海拔高度。

在整个网格中有很多连通区域,其高程值为0米,因为它们代表实际的海洋。

问题:是否有快速算法来获取大致的土地面积?据我所知,我可以乘以200 ^ 2 * 10 ^ 2来获得该区域的粗略近似值,但是有些值会有很大差异。

我想我知道一种相当昂贵的方式,即通过对顶点位于高程的所有三角形求和。但是有更快/更简单的方法吗?

2 个答案:

答案 0 :(得分:4)

NumPySciPy是解决此类问题的工具。这是一个合成的200×200景观,在10米的网格上有点,高度可达海拔40米:

>>> import matplotlib.pyplot as plt
>>> import mpl_toolkits.mplot3d.axes3d as axes3d
>>> _, axes = plt.subplots(subplot_kw=dict(projection='3d'))
>>> axes.plot_surface(x, y, z, cmap=plt.get_cmap('winter'))
>>> plt.show()

我们可以在Matplotlib中查看此内容:

>>> (z > 0).sum() * 100
1396500

现在,陆地上的点数(即高度大于0)很容易计算,您可以将其乘以网格方块的大小(在您的问题中为100平方米)来估算土地面积:

>>> points = np.vstack((x, y, z)).reshape(3, -1).T
>>> points
array([[  0.000000e+00,   0.000000e+00,   0.000000e+00],
       [  1.000000e+01,   0.000000e+00,   1.142702e+00],
       [  2.000000e+01,   0.000000e+00,   2.284471e+00],
       ..., 
       [  1.970000e+03,   1.990000e+03,   3.957136e+01],
       [  1.980000e+03,   1.990000e+03,   3.944581e+01],
       [  1.990000e+03,   1.990000e+03,   3.930390e+01]])

但是从这个问题来看,我知道你想要一个更准确的估计,一个考虑到土地坡度的估计。一种方法是制作覆盖土地的三角形网格,并将三角形区域相加。

首先,将坐标数组转换为点数组(point cloud):

>>> from scipy.spatial import Delaunay
>>> tri = Delaunay(points[:,:2])
>>> len(tri.simplices)
79202
>>> tri.simplices
array([[39698, 39899, 39898],
       [39899, 39698, 39699],
       [39899, 39700, 39900],
       ..., 
       [19236, 19235, 19035],
       [19437, 19236, 19237],
       [19436, 19236, 19437]], dtype=int32)

其次,使用scipy.spatial.Delaunay在二维中进行三角测量,获得表面网格:

points

三角测量中每个三角形的值是三角形中三个点的>>> land = (points[tri.simplices][...,2] > 0).any(axis=1) >>> triangles = tri.simplices[land] >>> len(triangles) 27858 数组中的索引。

第三,选择其中有一些土地的三角形:

>>> v0, v1, v2 = (points[triangles[:,i]] for i in range(3))
>>> areas = np.linalg.norm(np.cross(v1 - v0, v2 - v0), axis=1) / 2
>>> areas
array([ 50.325028,  50.324343,  50.32315 , ...,  50.308673,  50.313157, 50.313649])

第四,计算这些三角形的面积:

>>> areas.sum()
1397829.2847141961

最后,添加它们:

{{1}}

这与原始估计没有太大差别,因为斜坡很浅,所以可以预期。

答案 1 :(得分:1)

首先是一些对测试有用的附加内容:

# a function to create a random map as simulated input for testing:
def get_map(x_size, y_size, h_min, h_max):
    import random
    return [[random.randint(h_min, h_max) for x in range(x_size)] for y in range(y_size)]

# a function to nicely print the map for debug and visualization
def print_map(hmap):
    print(*hmap, sep="\n")

然后我们写下实际的土地面积计算器:

# calculate approximate land area where the height is greater than zero
# map is a list of lists, tile_size is in m², min_level is the sea level
def calc_land_area(hmap, tile_size=100, min_level=0):
    land_tiles = sum(len([tile for tile in row if tile>min_level]) for row in hmap)
    return tile_size * land_tiles

现在进行测试:

hmap = get_map(5, 5, 0, 3)
print_map(hmap)
print("land area:", calc_land_area(hmap), "m²")

这可能导致例如在这个随机的例子中输出:

[2, 0, 3, 0, 2]
[3, 0, 0, 0, 2]
[1, 0, 0, 1, 2]
[3, 3, 3, 2, 1]
[3, 1, 1, 3, 0]
land area: 1700 m²

你可以在25块瓷砖的地图上看到8块海瓦,所以800平方米是海,1700平方米是土地。

See this code running on ideone.com