使用matplotlib路径计算轮廓区域

时间:2018-02-06 03:17:14

标签: python-3.x matplotlib-basemap vertices contourf shapely.geometry

我正在尝试使用matplotlib路径和形状多边形函数来计算特定轮廓线所覆盖的区域,这样做的问题是我的多边形是不规则的并且在其中有孔,并且形状上的Polygpon函数没有'知道哪些顶点是外部的,哪些顶点是内部的,因此哪些顶点应该作为多边形的一部分,哪些顶点应该被认为是间隙的一部分。有办法解决这个问题吗?

Contour obtained using matplotlib pyplot function

eShapely polygon plot of vertices obtained from the contour with internal areas wrongly considered as gaps

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)

1 个答案:

答案 0 :(得分:0)

您的代码有两个问题

  1. 您处理的顶点不是您认为的顶点
  2. 您的等高线图显示了''你需要从你的总区域删除而不是添加以使绿色阴影的区域正确
  3. 但是,让我们从一开始就走吧。 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

    这是与上述代码对应的图像。请注意,我概述了黑色中添加的区域以及红色中减去的区域。另请注意,仍有一些极端情况,这会出错(例如湖中的岛屿)。

    result of above code

    这里仍然是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

    希望这可以解决它。