我正在尝试使用matplotlib路径和形状多边形函数来计算特定轮廓线所覆盖的区域,这样做的问题是我的多边形是不规则的并且在其中有孔,并且形状上的Polygpon函数没有'知道哪些顶点是外部的,哪些顶点是内部的,因此哪些顶点应该作为多边形的一部分,哪些顶点应该被认为是间隙的一部分。有办法解决这个问题吗?
import numpy as np
import numpy.ma as ma
from shapely.geometry import Polygon
from mpl_toolkits.basemap import Basemap
m = Basemap(projection='cyl',llcrnrlat=-30,urcrnrlat=30,llcrnrlon=30%360,\
urcrnrlon=-130%360,lat_ts=20,resolution='c');
lons, lats = np.meshgrid(lon_sst, lat_sst);
x, y = m(lons, lats);
levels=[28.5,np.amax(data)]
cs = m.contourf(x,y,data,levels)
cn = cs.collections[0].get_paths()
vs=[]
for i in range(len(cn)):
vs.append(cn[i].vertices)
area_of_individual_polygons = []
for i in range(len(vs)):
area_of_individual_polygons.append(Polygon(vs[i]).area)
total_area = np.sum(area_of_individual_polygons)
答案 0 :(得分:0)
您的代码有两个问题
但是,让我们从一开始就走吧。 Axes.contourf()
会返回QuadContourSet
(您存储在cs
。这包含matplotlib
绘制填充轮廓所需的所有数据。因为您只有两个级别,{{ 1}}只包含一个项目cs.contours
。使用PathCollection
,您可以获得描述多边形的路径列表。但是,如果仔细观察第一张图片,则会显示更大的绿色形状(让它称之为陆地),其中有一些较小的白洞(让我们称之为湖泊,也可能是山谷)和一些较小的绿色斑块土地质量(让他们称之为岛屿)。问题是土地和湖泊都是所有的路径,而岛屿是分开的路径。
通过首先在一个方向上定义轮廓(例如顺时针方向)然后定义相反方向的湖泊轮廓(然后是逆时针方向)来定义大陆的轮廓。为了让matplotlib知道如何处理这个问题,使用代码将路径分解为多个部分。这里使用了三个代码:cs.collections[0].get_paths()
(= 1),Path.MOVETO
(= 2)和Path.LINETO
(= 79)。您可以使用Path.CLOSEPOLY
获取这些代码的列表(参见下文)。每个细分以path.codes
开头,以Path.MOVETO
结尾,其间有很多Path.CLOSEPOLY
个(至少在这种情况下,没有弯曲的路径)。每个代码都有一个相应的顶点(用PATH.LINETO
得到的,只有与path.vertices
对应的顶点被忽略(通常设置为(0,0)),所以你必须删除它们才能进行最终计算在这些代码的帮助下,每条路径被分成若干段。
在告诉你所有这些之后,你需要做的是不仅从每个路径中检索顶点,还要检索相应的代码。然后,您需要将代码和顶点拆分成段(使用这很简单的代码),然后分别计算每个段的面积。最重要的是,要正确计算阴影区域的面积,您必须对相应的第一个路径段的面积求和,并减去其他段的面积。以下是如何执行此操作的示例:
Path.CLOSEPOLY
这是与上述代码对应的图像。请注意,我概述了黑色中添加的区域以及红色中减去的区域。另请注意,仍有一些极端情况,这会出错(例如湖中的岛屿)。
这里仍然是from matplotlib import pyplot as plt
import numpy as np
import numpy.ma as ma
from shapely.geometry import Polygon
from mpl_toolkits.basemap import Basemap
from matplotlib.path import Path
from matplotlib.patches import PathPatch
##setup
lonmin, lonmax = 30%360,-130%360
latmin, latmax = -30, 30
lon_sst = np.linspace(lonmin, lonmax, 50)
lat_sst = np.linspace(latmin, latmax, 50)
lons, lats = np.meshgrid(lon_sst, lat_sst);
##generating some example data
lonmid = 0.5*(lonmin+lonmax)
latmid = 0.5*(latmin+latmax)
data = (
10*np.cos(np.deg2rad(lons-lonmid))**2*np.cos(3*np.deg2rad(lats-latmid))**2
-10*np.exp(-0.01*((lons-(lonmin+2*lonmid)/3)**2+2*(lats-latmid)**2))
-10*np.exp(-0.01*((lons-(lonmax+2*lonmid)/3)**2+(lats-latmid)**2))
+10*np.exp(-0.1*((lons-(lonmid+2*lonmax)/3)**2+2*(lats-(latmid+2*latmax)/3)**2))
)
##opening figure and axes
fig,ax = plt.subplots()
##do the Basemap stuff
m = Basemap(
ax=ax,projection='cyl',
llcrnrlat=-30, urcrnrlat=30,
llcrnrlon=30%360, urcrnrlon=-130%360,
lat_ts=20,resolution='c'
)
x, y = m(lons, lats);
levels=[2,np.amax(data)]
cs = ax.contourf(x,y,data,levels)
##organizing paths and computing individual areas
paths = cs.collections[0].get_paths()
help(paths[0])
area_of_individual_polygons = []
for p in paths:
sign = 1 ##<-- assures that area of first(outer) polygon will be summed
verts = p.vertices
codes = p.codes
idx = np.where(codes==Path.MOVETO)[0]
vert_segs = np.split(verts,idx)[1:]
code_segs = np.split(codes,idx)[1:]
for code, vert in zip(code_segs,vert_segs):
##visualising (not necessary for the calculation)
new_path = Path(vert,code)
patch = PathPatch(
new_path,
edgecolor = 'black' if sign == 1 else 'red',
facecolor = 'none',
lw =1
)
ax.add_patch(patch)
##computing the area of the polygon
area_of_individual_polygons.append(sign*Polygon(vert[:-1]).area)
sign = -1 ##<-- assures that the other (inner) polygons will be subtracted
##computing total area
total_area = np.sum(area_of_individual_polygons)
formstring = ''.join(['{:+.2}' for a in area_of_individual_polygons])+'={:.2}'
print(formstring.format(*area_of_individual_polygons,total_area))
plt.show()
命令的输出,它只是代码中计算的可视化:
print
希望这可以解决它。