我有一个200X200的网格/数组(即40,000个值)的10mX10m“像素”,其值表示该点的平均海拔高度。
在整个网格中有很多连通区域,其高程值为0米,因为它们代表实际的海洋。
问题:是否有快速算法来获取大致的土地面积?据我所知,我可以乘以200 ^ 2 * 10 ^ 2来获得该区域的粗略近似值,但是有些值会有很大差异。
我想我知道一种相当昂贵的方式,即通过对顶点位于高程的所有三角形求和。但是有更快/更简单的方法吗?
答案 0 :(得分:4)
NumPy和SciPy是解决此类问题的工具。这是一个合成的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平方米是土地。