3D CartoPy类似Matplotlib,底图 [英] 3D CartoPy similar to Matplotlib-Basemap

查看:2817
本文介绍了3D CartoPy类似Matplotlib,底图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是新来的Python一个有关Cartopy能够在3D绘图中使用的问题。下面是使用的例子 matplotlibBasemap

I'm new to Python with a question about Cartopy being able to be used in a 3D plot. Below is an example using 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()

这将创建在一个3D轴的地图,让您可以绘制在表面上的物体。但随着Cartopy返回 matplotlib.axes.GeoAxesSubplot 。尚不清楚如何利用这一点,并添加到3D图形/轴如上 matplotlib-底图

This creates a map within a 3D axis so that you can plot objects over the surface. But with Cartopy returns a matplotlib.axes.GeoAxesSubplot. Not clear how to take this and add to a 3D figure/axis as above with matplotlib-basemap.

所以,有人可以就如何做一个类似的3D绘图与Cartopy任何指针?

So, can someone give any pointers on how to do a similar 3D plot with Cartopy?

推荐答案

底图mpl3d是pretty的整洁劈,但它并没有被设计在描述的方式发挥作用。这样一来,您目前不能使用相同的技术很多其他的不是简单的海岸线。例如,填补大陆的不愉快AFAICT。

The basemap mpl3d is a pretty neat hack, but it hasn't been designed to function in the described way. As a result, you can't currently use the same technique for much other than simple coastlines. For example, filled continents just don't work AFAICT.

这表示,采用cartopy当类似的黑客可用。既然我们可以笼统访问shape文件的信息,该解决方案应该可以在任何多行shape文件的工作,如海岸线。

That said, a similar hack is available when using cartopy. Since we can access shapefile information generically, this solution should work for any poly-line shapefile such as coastlines.

第一步是弄个shape文件的,和各自的几何形状:

The first step is to get hold of the shapefile, and the respective geometries:

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

接下来,我们可以将这些到所需的投射:

Next, we can convert these to the desired projection:

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

由于这些是身材匀称的几何形状,我们则希望将它们转换为matplotlib与路径:

Since these are shapely geometries, we then want to convert them to matplotlib paths with:

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要解决这个(作为底图一样)。可悲的是LineCollections不走的路径,但段,我们可以计算:

With paths, we should be able to just create a PathCollection in matplotlib, and add it to the axes, but sadly, Axes3D doesn't seem to cope with PathCollection instances, so we need to workaround this by constructing a LineCollection (as basemap does). Sadly LineCollections don't take paths, but segments, which we can compute with:

segments = []
for path in paths:
    vertices = [vertex for vertex, _ in path.iter_segments()]
    vertices = np.asarray(vertices)
    segments.append(vertices)

这拉在一起,我们最终得到了类似的结果,你的code生产的底图图:

Pulling this all together, we end up with a similar result to the basemap plot which your code produces:

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似乎处理PolyCollection好,这将是路线我会调查的充满几何形状,如土地的轮廓(相对于海岸线,这是严格意义上的大纲)。

On top of this, mpl3d seems to handle PolyCollection well, which would be the route I would investigate for filled geometries, such as the land outline (as opposed to the coastline, which is strictly an outline).

的重要步骤是在路径转换为多边形,并使用这些在一个PolyCollection对象

The important step is to convert the paths to polygons, and use these in a PolyCollection object:

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)

完整的code对于这种情况看起来是这样的:

The complete code for this case would look something like:

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()

要产生:

这篇关于3D CartoPy类似Matplotlib,底图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆