ahul-fell-awen
ahul-fell-awen

Reputation: 69

Python: Plotting a Basemap in the x-y-plane of a 3d plot

I am trying to get the code contained in

https://basemaptutorial.readthedocs.io/en/latest/basemap3d.html

to work. However, I get an error already in the first snippet:

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

map = Basemap()

fig = plt.figure()
ax = Axes3D(fig)

'''
ax.azim = 270
ax.elev = 90
ax.dist = 5
'''

ax.add_collection3d(map.drawcoastlines(linewidth=0.25))
ax.add_collection3d(map.drawcountries(linewidth=0.35))

plt.show()

The error reads:

Traceback (most recent call last):
  File ".../main.py", line 16, in <module>
    ax.add_collection3d(map.drawcoastlines(linewidth=0.25))
  File ".../venv/lib/python3.12/site-packages/mpl_toolkits/mplot3d/axes3d.py", line 2314, in add_collection3d
    collection = super().add_collection(col)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../venv/lib/python3.12/site-packages/matplotlib/axes/_base.py", line 2260, in add_collection
    self._set_artist_props(collection)
  File ".../venv/lib/python3.12/site-packages/matplotlib/axes/_base.py", line 1177, in _set_artist_props
    a.axes = self
    ^^^^^^
  File ".../venv/lib/python3.12/site-packages/matplotlib/artist.py", line 302, in axes
    raise ValueError("Can not reset the axes.  You are probably "
ValueError: Can not reset the axes.  You are probably trying to re-use an artist in more than one Axes which is not supported

Process finished with exit code 1

I cannot find a solution to this yet. My final goal is to achieve something similar to what user guidocioni shows here: https://community.plotly.com/t/adding-a-world-map-to-a-3d-volume-graph-python/33592/2

Upvotes: 1

Views: 43

Answers (1)

RuthC
RuthC

Reputation: 3896

drawcoastlines creates a Matplotlib LineCollection and adds that to a 2D axes before returning it. Modern Matplotlib does not let you add the same artist to two axes. We can instead create a LineCollection directly from the coastsegs attribute and add that to the 3D axes. drawcountries is similar, except that it calls a private method to make sure the cntrysegs attribute exists. So as a workaround I add first add the countries to a 2D plot, which is then discarded.

import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import LineCollection

map = Basemap()

# Draw and discard a dummy 2D plot so that country data is added as the
# cntrysegs attribute.
dummy_fig, dummy_ax = plt.subplots()
map.drawcountries(ax=dummy_ax)
plt.close(dummy_fig)

# Create the figure and 3D axes we are actually going to use.
fig = plt.figure()
ax = fig.add_subplot(projection='3d')

# Create and add boundary LineCollections.
coastlines = LineCollection(map.coastsegs, linewidth=0.25, color='black')
countries = LineCollection(map.cntrysegs, linewidth=0.35, color='black')
ax.add_collection3d(coastlines)
ax.add_collection3d(countries)

# Set global x and y limits.
ax.set_xlim(-180, 180)
ax.set_ylim(-90, 90)

plt.show()

enter image description here

Upvotes: 0

Related Questions