La mappa di base mpl3d è un hack abbastanza curato, ma non è stato progettato per funzionare nel modo descritto. Di conseguenza, al momento non è possibile utilizzare la stessa tecnica per molto altro che semplici litorali. Ad esempio, i continenti pieni semplicemente non funzionano AFAICT.
Detto questo, un analogo hack è disponibile quando si utilizza il cartopy. Dal momento che possiamo accedere genericamente a informazioni sullo shapefile, questa soluzione dovrebbe funzionare per qualsiasi shapefile di polilinea come le linee costiere.
Il primo passo è procurarsi shapefile, e le rispettive geometrie:
feature = cartopy.feature.NaturalEarthFeature('physical', 'coastline', '110m')
geoms = feature.geometries()
Successivamente, possiamo convertire questi per la proiezione desiderata:
target_projection = ccrs.PlateCarree()
geoms = [target_projection.project_geometry(geom, feature.crs)
for geom in geoms]
Trattandosi di geometrie tornite , quindi vogliamo convertirli in percorsi matplotlib con:
from cartopy.mpl.patch import geos_to_path
import itertools
paths = list(itertools.chain.from_iterable(geos_to_path(geom)
for geom in geoms))
Con pa Dovremmo essere in grado di creare semplicemente un PathCollection in matplotlib e aggiungerlo agli assi, ma purtroppo Axes3D non sembra affrontare le istanze di PathCollection, quindi dobbiamo aggirarlo creando un LineCollection (come fa la mappa di base) . Purtroppo LineCollections non prendono percorsi, ma segmenti, che siamo in grado di calcolare con:
segments = []
for path in paths:
vertices = [vertex for vertex, _ in path.iter_segments()]
vertices = np.asarray(vertices)
segments.append(vertices)
Tirando questo tutti insieme, si finisce con un risultato simile alla trama mappa di base che il codice produce:
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()
![mplt3d with cartopy](https://i.stack.imgur.com/MODYB.png)
in cima a questo, mpl3d sembra gestire PolyCollection bene, che sarebbe il percorso che avrebbe indagato per geometrie pieni, come ad esempio il contorno terreno (in contrapposizione alla linea di costa, che è strettamente un contorno).
Il passo importante è quello di convertire i percorsi di poligoni, e utilizzare questi in un oggetto PolyCollection:
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)
il codice completo per questo caso sarebbe un aspetto simile:
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()
a cedere :
![mpl3d land outline](https://i.stack.imgur.com/CC3Zt.png)