Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cartopy - round polar projections for just the polar regions?

I am trying to draw a pair of round polar projections centered on the north and south (celestial) poles as an adjunct to a more conventional star map. These should only cover about 20 degrees of latitude each.

I can get cartopy to draw reasonable-looking polar projections of the entire celestial sphere centered on either pole, but if I try to restrict the range of latitude, an azimuthal-equidistant plot collapses to a vertical line(!) and a stereographic projection becomes square, like this:

enter image description here

  • Top left: azeq projection of the whole celestial sphere centered on the north pole.
  • Top right: stereo projection of the whole celestial sphere centered on the south pole.
  • Bottom left: azeq projection from 70°N to the north pole.
  • Bottom right: stereo projection from 70°S to the south pole.

What I want is round plots like on top, but cut off at 70°N and 70°S respectively.

Code I have now:

from cartopy import crs
from math import pi as PI
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

CEL_SPHERE = crs.Globe(
    ellipse=None,
    semimajor_axis=180/PI,
    semiminor_axis=180/PI,
)
PC_GALACTIC = crs.PlateCarree(globe=CEL_SPHERE)

def render_map(path, width, height):
    fig = plt.figure(layout="constrained", figsize=(width, height))
    try:
        gs = GridSpec(2, 2, figure=fig)

        axN1 = fig.add_subplot(
            gs[0, 0],
            projection=crs.AzimuthalEquidistant(
                central_latitude=90,
                globe=CEL_SPHERE,
            )
        )
        axN1.gridlines(draw_labels=True)

        axS2 = fig.add_subplot(
            gs[0, 1],
            projection=crs.SouthPolarStereo(globe=CEL_SPHERE)
        )
        axS2.gridlines(draw_labels=True)

        axN2 = fig.add_subplot(
            gs[1, 0],
            projection=crs.AzimuthalEquidistant(
                central_latitude=90,
                globe=CEL_SPHERE,
            )
        )
        axN2.set_extent((-180, 180, 70, 90), crs=PC_GALACTIC)
        axN2.gridlines(draw_labels=True)

        axS2 = fig.add_subplot(
            gs[1, 1],
            projection=crs.SouthPolarStereo(globe=CEL_SPHERE)
        )
        axS2.set_extent((-180, 180, -90, -70), crs=PC_GALACTIC)
        axS2.gridlines(draw_labels=True)

        fig.savefig(path)
    finally:
        plt.close(fig)

if __name__ == "__main__":
    render_map("map_test.pdf", 12, 12)

like image 885
zwol Avatar asked Nov 15 '25 15:11

zwol


1 Answers

Cartopy has a demo that addresses this issue here: https://scitools.org.uk/cartopy/docs/v0.15/examples/always_circular_stereo.html?highlight=set_extent

Basically, make a clip path around the border of your map. The clip path is defined underneath your call to generate the figure, and there are two set_boundary calls for the maps with the limited extents.

The output (the automatic gridlines are a little funky but you can always make your own):

enter image description here

Here's your modified code:

from cartopy import crs
from math import pi as PI
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
import numpy as np
import matplotlib.path as mpath

CEL_SPHERE = crs.Globe(
    ellipse=None,
    semimajor_axis=180/PI,
    semiminor_axis=180/PI,
)
PC_GALACTIC = crs.PlateCarree(globe=CEL_SPHERE)

def render_map(path, width, height):
    fig = plt.figure(layout="constrained", figsize=(width, height))
    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)
    try:
        gs = GridSpec(2, 2, figure=fig)

        axN1 = fig.add_subplot(
            gs[0, 0],
            projection=crs.AzimuthalEquidistant(
                central_latitude=90,
                globe=CEL_SPHERE,
            )
        )
        axN1.gridlines(draw_labels=True)

        axS2 = fig.add_subplot(
            gs[0, 1],
            projection=crs.SouthPolarStereo(globe=CEL_SPHERE)
        )
        axS2.gridlines(draw_labels=True)

        axN2 = fig.add_subplot(
            gs[1, 0],
            projection=crs.AzimuthalEquidistant(
                central_latitude=90,
                globe=CEL_SPHERE,
            )
        )
        axN2.set_extent((-180, 180, 70, 90), crs=PC_GALACTIC)
        axN2.gridlines(draw_labels=True)
        axN2.set_boundary(circle, transform=axN2.transAxes)

        axS2 = fig.add_subplot(
            gs[1, 1],
            projection=crs.SouthPolarStereo(globe=CEL_SPHERE)
        )
        axS2.set_extent((-180, 180, -90, -70), crs=PC_GALACTIC)
        axS2.gridlines(draw_labels=True)
        axS2.set_boundary(circle, transform=axS2.transAxes)

        fig.savefig(path)
    finally:
        plt.close(fig)

if __name__ == "__main__":
    render_map("map_test.pdf", 12, 12)
like image 87
E. V. Hadzen Avatar answered Nov 17 '25 04:11

E. V. Hadzen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!