grid line labeling (labels around the map's border) for a South Polar Stereo projection

110 Views Asked by At

I'm using the South Polar Stereographic projection to create a map of Antarctica. I've been able to create the gridlines using Cartopy, but the labels for the gridlines are not circular around the map, which is what I want. Is there a way to make the gridline labels follow the circular pattern of the South Polar Stereographic projection? Here's the code I'm currently using:

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as  mpath

fig = plt.figure(figsize=(12,8))
ax = plt.axes(projection=ccrs.SouthPolarStereo())
ax.set_extent([-180, 180, -90, -30], ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, color='darkgrey')
ax.add_feature(cfeature.OCEAN, color='lightblue')
ax.add_feature(cfeature.COASTLINE, linewidth=1.25)

# Draw meridian lines with labels around circular boundary
ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=True, linewidth=1, \
                xlocs=range(-180,171,10), ylocs=[], \
                color='gray', alpha=0.5, linestyle='--', zorder=10)
# Draw concentric circles (but hide labels) for the parallels of the latitude
ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False, linewidth=1, \
                xlocs=[], ylocs=None, \
                color='gray', alpha=0.5, linestyle='--', zorder=10)

# Add circular boundary
theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpath.Path(verts * radius + center)

ax.set_boundary(circle, transform=ax.transAxes)


plt.tight_layout()
plt.show()

The output looks like

enter image description here

What I am expecting:

enter image description here

1

There are 1 best solutions below

0
swatchai On BEST ANSWER

To my knowledge, there is no shortcut for this kind of plot. Special instructions are needed in many places in the code. You can see comments in the code that I added in various places.

Complete code

import cartopy.crs as ccrs
import cartopy.feature as cfeature
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as  mpath #for circular boundary

nonproj = ccrs.PlateCarree()  #for plain lat/long degree system

fig = plt.figure(figsize=(7,7))
ax = plt.axes(projection=ccrs.SouthPolarStereo())
ax.set_extent([-180, 180, -90, -30], ccrs.PlateCarree())
ax.add_feature(cfeature.LAND, color='darkgrey')
ax.add_feature(cfeature.OCEAN, color='lightblue')
ax.add_feature(cfeature.COASTLINE, linewidth=1.25)

# Make the gridlines circular
gls1 = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False, linewidth=1, \
                xlocs=range(-180,171,10), ylocs=[], \
                color='gray', alpha=0.5, linestyle='--', zorder=10)
gls2 = ax.gridlines(crs=ccrs.PlateCarree(), draw_labels=False, linewidth=1, \
                xlocs=[], ylocs=None, \
                color='gray', alpha=0.5, linestyle='--', zorder=10)

# Add circular boundary
theta = np.linspace(0, 2*np.pi, 100)
center, radius = [0.5, 0.5], 0.5
verts = np.vstack([np.sin(theta), np.cos(theta)]).T
circle = mpath.Path(verts * radius + center)

def plot_text(p1, p2, ax, ang_d, txt):
    """
    Plot text string at a location defined by 2 points (p1,p2)
    The string is rotated by an angle 'ang_d' 
    usage:
    plot_text([90, -31], [90, -50], ax, 45, "45-deg_text")
    By swatchai
    """
    # Locations to plot text
    l1 = np.array((p1[0], p1[1]))
    l2 = np.array((p2[0], p2[1]))

    # Plot text
    th1 = ax.text(l1[0], l1[1], txt, fontsize=10, \
                  transform=nonproj, \
                  ha="center", \
                  rotation=ang_d, rotation_mode='anchor')


# Plot text labels outside circular boundary
for lon in range(-180,180,10):
    lat = -33  # determined by inspection

    a1, a2 = -29.5, -39  #text anchor for general use ...
    #... need adjustments in some cases

    if lon>=90 and lon<=170:
        plot_text([lon, a1+2.35], [lon, a2], ax, -lon-180, str(lon)+"°E")  
        # Special rotation+shift
    elif lon<-90 and lon>=-170:
        # Need a1+2 to move texts in line with others
        plot_text([lon, a1+2.5], [lon, a2], ax, -lon+180, str(-lon)+"°W")  
        # Special rotation+shift
    elif lon > 0:
        plot_text([lon, a1], [lon, a2], ax, -lon, str(lon)+"°E")
    elif lon==0:
        plot_text([lon, a1], [lon, a2], ax, lon, str(lon)+"°")
    elif lon==-180:
        plot_text([lon, a1+2.2], [lon, a2], ax, lon+180, str(-lon)+"°")
    else:
        plot_text([lon, a1], [lon, a2], ax, -lon, str(-lon)+"°W")
        pass

ax.set_boundary(circle, transform=ax.transAxes)

plt.tight_layout()
plt.show()

Output:

spolar