Python 2.7 类似于Matplotlib Basemap的3D CartoPy

Python 2.7 类似于Matplotlib Basemap的3D CartoPy,python-2.7,matplotlib,3d,matplotlib-basemap,cartopy,Python 2.7,Matplotlib,3d,Matplotlib Basemap,Cartopy,我是Python新手,有一个关于Cartopy能否用于3D绘图的问题。下面是使用matplotlibBasemap的示例 import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from mpl_toolkits.basemap import Basemap m = Basemap(projection='merc', llcrnrlat=52.0,urcrnrlat=58.0,

我是Python新手,有一个关于Cartopy能否用于3D绘图的问题。下面是使用
matplotlibBasemap
的示例

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap

m = Basemap(projection='merc',
            llcrnrlat=52.0,urcrnrlat=58.0,
            llcrnrlon=19.0,urcrnrlon=40.0,
            rsphere=6371200.,resolution='h',area_thresh=10)

fig = plt.figure()
ax = Axes3D(fig)
ax.add_collection3d(m.drawcoastlines(linewidth=0.25))
ax.add_collection3d(m.drawcountries(linewidth=0.35))
ax.add_collection3d(m.drawrivers(color='blue'))

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Height')

fig.show()
这将在三维轴内创建贴图,以便可以在曲面上打印对象。但是使用Cartopy返回一个
matplotlib.axes.GeoAxesSubplot
。不清楚如何使用
matplotlib basemap
将其添加到如上所述的三维地物/轴

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap

m = Basemap(projection='merc',
            llcrnrlat=52.0,urcrnrlat=58.0,
            llcrnrlon=19.0,urcrnrlon=40.0,
            rsphere=6371200.,resolution='h',area_thresh=10)

fig = plt.figure()
ax = Axes3D(fig)
ax.add_collection3d(m.drawcoastlines(linewidth=0.25))
ax.add_collection3d(m.drawcountries(linewidth=0.35))
ax.add_collection3d(m.drawrivers(color='blue'))

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Height')

fig.show()

那么,有没有人能为如何使用Cartopy进行类似的3D绘图提供一些指导呢?

basemap mpl3d是一种非常巧妙的技术,但它的设计并没有以所描述的方式发挥作用。因此,除了简单的海岸线之外,您目前无法在其他地方使用相同的技术。例如,填充的大陆根本不起作用

也就是说,在使用cartopy时也有类似的攻击。由于我们可以一般地访问形状文件信息,因此此解决方案适用于任何多边形线形状文件,如海岸线

第一步是获取shapefile和相应的几何图形:

feature = cartopy.feature.NaturalEarthFeature('physical', 'coastline', '110m')
geoms = feature.geometries()
接下来,我们可以将这些转换为所需的投影:

target_projection = ccrs.PlateCarree()
geoms = [target_projection.project_geometry(geom, feature.crs)
         for geom in geoms]
由于这些是形状优美的几何图形,因此我们希望将它们转换为matplotlib路径,使用:

from cartopy.mpl.patch import geos_to_path
import itertools

paths = list(itertools.chain.from_iterable(geos_to_path(geom)
                                             for geom in geoms))
对于路径,我们应该能够在matplotlib中创建一个PathCollection,并将其添加到轴中,但遗憾的是,Axes3D似乎无法处理PathCollection实例,因此我们需要通过构造一个LineCollection来解决这个问题(就像basemap那样)。遗憾的是,LineCollections不采用路径,而是采用分段,我们可以使用这些分段进行计算:

segments = []
for path in paths:
    vertices = [vertex for vertex, _ in path.iter_segments()]
    vertices = np.asarray(vertices)
    segments.append(vertices)
综上所述,我们最终得到了与代码生成的basemap图类似的结果:

import itertools

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
import numpy as np

import cartopy.feature
from cartopy.mpl.patch import geos_to_path
import cartopy.crs as ccrs


fig = plt.figure()
ax = Axes3D(fig, xlim=[-180, 180], ylim=[-90, 90])
ax.set_zlim(bottom=0)


target_projection = ccrs.PlateCarree()

feature = cartopy.feature.NaturalEarthFeature('physical', 'coastline', '110m')
geoms = feature.geometries()

geoms = [target_projection.project_geometry(geom, feature.crs)
         for geom in geoms]

paths = list(itertools.chain.from_iterable(geos_to_path(geom) for geom in geoms))

# At this point, we start working around mpl3d's slightly broken interfaces.
# So we produce a LineCollection rather than a PathCollection.
segments = []
for path in paths:
    vertices = [vertex for vertex, _ in path.iter_segments()]
    vertices = np.asarray(vertices)
    segments.append(vertices)

lc = LineCollection(segments, color='black')

ax.add_collection3d(lc)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Height')

plt.show()

最重要的是,mpl3d似乎能很好地处理多聚集问题,这将是我研究填充几何体的路线,例如土地轮廓(与海岸线相反,海岸线严格来说是轮廓线)

重要的步骤是将路径转换为多边形,并在多边形集合对象中使用这些路径:

concat = lambda iterable: list(itertools.chain.from_iterable(iterable))

polys = concat(path.to_polygons() for path in paths)
lc = PolyCollection(polys, edgecolor='black',
                    facecolor='green', closed=False)
本例的完整代码如下所示:

import itertools

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection, PolyCollection
import numpy as np

import cartopy.feature
from cartopy.mpl.patch import geos_to_path
import cartopy.crs as ccrs


fig = plt.figure()
ax = Axes3D(fig, xlim=[-180, 180], ylim=[-90, 90])
ax.set_zlim(bottom=0)


concat = lambda iterable: list(itertools.chain.from_iterable(iterable))

target_projection = ccrs.PlateCarree()

feature = cartopy.feature.NaturalEarthFeature('physical', 'land', '110m')
geoms = feature.geometries()

geoms = [target_projection.project_geometry(geom, feature.crs)
         for geom in geoms]

paths = concat(geos_to_path(geom) for geom in geoms)

polys = concat(path.to_polygons() for path in paths)

lc = PolyCollection(polys, edgecolor='black',
                    facecolor='green', closed=False)

ax.add_collection3d(lc)

ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Height')

plt.show()
产生:


佩尔森,回答得很好,真是太好了。是否可以只绘制地球的一部分而不是整个轮廓,并使用不同的cartopy投影?是否可以在此基础上绘制热图或散点图?忽略不计:已回答