matplotlib Basemap Fundamental Lune

Question:

I’m trying to recreate this projection using matplotlib Fundamental Lune Plot. The reference material associated with this specific projection is here, Carl Tape Moment Tensors

The geophysics behind the plot isn’t crucial, but essentially its a projection between longitudes of -30 and 30 degrees and latitudes -90 to 90. I’ve thought that Basemap might be a good way of creating the projection, but I cannot seem to figure out how to only show this fundamental lune section. Here is what I’ve been playing around with,but it still shows the entire globe:

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

m = Basemap(
        resolution='l',     # coastline resolution, can be 'l' (low), 'h'
        projection='hammer', # Hammer projection
        lat_ts=0,          # latitude of true scale
        lon_0=0,          # longitude of the plotting domain center
        lat_0=0)           # latitude of the plotting domain center

# draw parallels and meridians.
m.drawparallels(np.arange(-90.,90.,10.))
m.drawmeridians(np.arange(-30.,31.,10.))
ax = plt.gca()
plt.show()

Can anybody offer some guidance or suggestions?

Asked By: nanoPhD

||

Answers:

In Basemap, I believe the Hammer projection is "global", meaning that it doesn’t take extent inputs so it makes sense the entire globe would always show. As of 2022 Basemap is heavily deprecated / not supported.

I was able to make the plot you want using Cartopy instead. The following code produces image below and on the left, with some demo data:

import matplotlib.pyplot as plt
import numpy as np
import cartopy.crs as ccrs
import matplotlib.path as mpath

# The Sinusoidal projection was added to Cartopy in version 0.14
fig = plt.figure(figsize=(3, 5))
ax = fig.add_subplot(111, projection=ccrs.Sinusoidal())

# Here I define a matplotlib Path object to use as the boundary
outlinex = np.concatenate([[-30],np.tile(-30,180), np.tile(30,180),[-30]])
outliney = np.concatenate([[-90],np.arange(-90,90),np.arange(89,-91,-1),[-90]])
outlinecodes = np.array([mpath.Path.MOVETO]+[mpath.Path.LINETO]*360+[mpath.Path.MOVETO])
outlinepath = mpath.Path(np.column_stack([outlinex[::-1], outliney[::-1]]), outlinecodes[::-1])

# For good measure, plot some data
ax.plot(np.arange(-10,25), np.linspace(80,45,35), transform=ccrs.Geodetic())
ax.plot(np.tile(25,91),np.arange(45,-46,-1), transform=ccrs.Geodetic())

# Plot gridlines and set the boundary
ax.gridlines(xlocs=np.arange(-30,31,10), ylocs=np.arange(-90,91,45))
ax.set_boundary(outlinepath, transform=ccrs.Geodetic())

# The plotting will have automatically set the extents, so set them to what we want
ax.set_extent((-30,30,-90,90))

plt.show()

Note, that if you omit the set_boundary elements and just use the set_extent, you’ll get the image on the right, rather than the image on the left.

lune plot with correct boundary lune plot with squared boundary

Answered By: Ajean