Frames & projections#
Every skyplothelper plot starts with a frame: a matplotlib axes wired to a
world coordinate system so that sky positions land where the projection says
they should. This page covers building frames — the master builder, the
convenience wrappers, the 32 available projections and how to choose among
them — plus the utilities for offset (tangent-plane) coordinates and
synthetic WCS headers. The conventions that apply to every builder
(direction=, frame=, lon_units=, centering) are covered once in
Core concepts & conventions and not repeated here.
import skyplothelper as sph
import matplotlib.pyplot as plt
The master builder: make_wcs_frame#
make_wcs_frame() builds any frame the package supports —
all-sky ovals, globe disks, bounded fields — and returns a ready-to-draw
WCSAxes. A minimal call is just a subplot position and a projection:
ax = sph.make_wcs_frame(111, "AIT", center=180)
Its arguments fall into three groups:
What sky goes on the canvas — projection= (FITS code or alias),
center= / center_lon= / center_lat=, frame= ('ICRS',
'galactic', …), direction=, and for the projections that need them,
lonpole=/latpole= and the conic pv2_1=/pv2_2= parameters (sensible
defaults are supplied, so conics work out of the box).
How much of it — for bounded fields, set the field of view with
fov_deg= (or, FITS-style, cdelt= + npix=). All-sky projections ignore
these and show the whole sphere. shape= overrides the frame outline
(elliptical / circular / rectangular) when you want something other than
the projection’s natural boundary.
How it’s dressed — grid=, gridcolor=, gridalpha=,
lon_spacing=/lat_spacing= ('auto' picks round values),
lon_units=, tick_style=, tick_rotation=, and auto_fontsize=. These
are creation-time conveniences; everything is re-tunable afterward (see
Ticks, grids & labels).
Two practically useful extras: fig= targets an existing figure (for
multi-panel layouts), and return_hdr=True also returns the synthesized
FITS header, handy when downstream code (reprojection, HEALPix rasterizing)
needs the WCS itself.
lon_west=True labels longitude the western way (e.g. 71°W instead of
289°) — a label-only convention flip for planetary maps. The data
stays east-longitude internally and the map is not mirrored (that is
direction=’s job, see Core concepts & conventions); only the tick labels change.
lon_west_to_east() / lon_east_to_west()
convert values between the two conventions. It is available on
make_wcs_frame, make_globe_frame(), and
make_planet_frame().
# Galactic-frame Mollweide with a coarser grid, on an existing figure
fig = plt.figure(figsize=(9, 5))
ax = sph.make_wcs_frame(111, "MOL", frame="galactic", center=0,
lon_spacing=30, lat_spacing=15, fig=fig)
Convenience builders#
For the everyday cases, one call makes the figure and the frame together:
Builder |
Makes |
Notes |
|---|---|---|
full-sky elliptical frame |
returns |
|
tangent-plane field on a target |
|
|
orthographic celestial globe |
||
Earth/planet globe (default |
||
z–RA wedge (not a WCS frame) |
see Cone frames |
|
cartopy GeoAxes |
terrestrial maps with cartopy’s feature stack |
|
grid of frames across projections |
quick visual comparison |
fig, ax = sph.allsky_figure(projection="AIT", center=180)
# 12-arcmin TAN field on the Crab Nebula, offset coordinates
fig, ax = sph.offset_figure(center=(83.63, 22.01), fov_deg=0.2)
The projections#
Projection gallery — code in the Feature Gallery.
list_projections() prints the full registry — 27 FITS projections
(everything astropy/wcslib supports) plus five classic compromise
projections implemented as custom matplotlib frames (Robinson,
Kavrayskiy VII, Eckert IV, Winkel Tripel, McBryde–Thomas). Each entry lists
its aliases, natural frame shape, and whether it can show the full sky.
The ones you’ll probably encounter the most:
Code |
Name |
Character |
Typical use |
|---|---|---|---|
|
Hammer–Aitoff |
equal-area, elliptical |
the default all-sky map |
|
Mollweide |
equal-area, elliptical |
all-sky, straight parallels |
|
Plate Carrée |
equirectangular |
simple lon/lat grids, Earth maps |
|
Sanson–Flamsteed |
equal-area, sinusoidal |
all-sky with straight parallels |
|
Gnomonic |
tangent plane |
fields, FITS images, interferometry |
|
Slant orthographic |
globe view |
hemispheres, tilted globes |
|
Stereographic |
conformal disk |
wide fields with low shape distortion |
|
Lambert azimuthal |
equal-area disk |
polar caps, hemisphere statistics |
|
Mercator |
conformal, cylindrical |
low-latitude strips |
|
conics |
between cylindrical and azimuthal |
mid-latitude regions |
Quick guidance on choosing: use an equal-area projection (AIT, MOL, SFL,
CEA, ZEA) whenever the density of things matters — source counts, survey
coverage, HEALPix maps — so a deg² covers the same canvas area everywhere.
Use a conformal projection (TAN, STG, MER) when local shapes matter —
imaging fields, morphology. The compromise projections (Robinson,
Winkel Tripel, …) trade a little of both for looks, which is exactly what
you want in outreach figures. When in doubt:
projection_gallery() renders your choice of projections
side by side.
sph.projection_gallery(projections=["AIT", "MOL", "SFL", "robinson"], center=180)
All 27 FITS projections plus the five compromise frames render as proper
all-sky maps — with a complete graticule and a drawn boundary outline — and
the interrupted / “oddball” ones (HEALPix HPX/XPH, Bonne BON, polyconic
PCO, the quad-cubes, and the conics) clip data to their visible region so
nothing bleeds past the frame.
Centering, aspect & projection constraints#
center= and the conventions behind it are covered in Core concepts & conventions; a few
projection-specific constraints are worth knowing when you go beyond the
common set (all are spelled out in the make_wcs_frame()
docstring):
Oblique aspect. Most all-sky projections honor
center=(lon, lat)to tilt the map off the equator. The exceptions are the pole-tiled HEALPix/quad-cube projections (HPX,XPH,TSC,CSC,QSC), which stay equatorial —center_latis ignored for them.Conics (
COD/COE/COO/COP) are all-sky by default, centered on their standard parallel (set viapv2_1, default 45°);center_latdoesn’t apply.COO/COPclip the divergent far pole. Passfov_deg=for a zoomed regional view instead.Quad-cubes read cleanest at a face-aligned
center_lonof 0/90/180/270.Bonne / polyconic (
BON,PCO) accept an obliquecenter_latmathematically, but the boundary outline overflows under a latitude shift — prefer longitude shifts for clean frames.
Two finite-resolution caveats for raster overlays on interrupted projections:
pcolormesh/HEALPix data can’t completely fill the extreme corners of
HPX/XPH/cube frames (thin edge gaps that shrink as nside grows), and
PCO’s overlapping lobes double-value data beyond |lon| ~ 90°. Use
clip_to_projection_boundary() to clip a custom data
artist to the visible region (the built-in plotters already do this).
Tick placement on odd frames#
make_wcs_frame’s tick_style= controls where coordinate ticks land:
'auto' (default) routes the interrupted projections to legible in-frame
central crosshair labels; 'boundary' places ticks on the projection’s true
edge (the HEALPix diamond, the conic wedge, the Bonne cardioid, …) rather than
the canvas rectangle; 'in_frame' and 'native' force the other two modes.
Offset coordinates & tangent-plane fields#
Zoomed-in fields usually want relative coordinates — arcsec or arcmin from
a reference position — rather than absolute RA/Dec.
offset_figure() builds this directly. For finer
control, the underpinnings are public:
WCS_to_offsetWCS()— convert an absolute WCS into an offset WCS about a reference point.offset_coord_WCS()— the offset-coordinate WCS for a given center and scale.apply_boundary_labels()— label a frame’s boundary with offset-style tick labels, orientedperpendicular(default),parallel, orhorizontalto the edge.
The offset tick styles themselves (arcsec offsets, VLBI hybrid absolute+offset labeling) live with the rest of the tick machinery — see Ticks, grids & labels.
Drawing data on a frame#
A WCSAxes plots in pixel coordinates by default, so raw ax.plot(ra, dec)
lands in the wrong place. skyplothelper mirrors the everyday matplotlib
plotting methods as module-level functions that take sky coordinates
instead — a SkyCoord or plain (lon, lat) in
degrees — project them through the frame, honor a frame= conversion, and
split lines at the antimeridian so nothing streaks across an all-sky map:
import numpy as np
import skyplothelper as sph
ax = sph.make_wcs_frame(111, "AIT", center=0)
lon = np.linspace(-170, 170, 60)
lat = 30 * np.sin(np.radians(lon))
sph.plot(ax, lon, lat, frame="galactic", color="C1") # a great-circle-ish track
sph.scatter(ax, [45, 120], [10, -20], s=40) # points
sph.text(ax, 0, 0, "GC", frame="galactic", ha="center")
The full set — plot(), scatter(),
errorbar(), step(),
fill()/fill_between(),
text()/annotate(),
contour()/contourf()/tricontourf(),
pcolormesh(), hist2d() — each forwards
its **kwargs straight to the matplotlib method of the same name. For a custom
artist not in the list, draw it yourself against
world_transform(), the (lon, lat) → display transform:
ax.plot(lon, lat, transform=sph.world_transform(ax))
The higher-level catalog and vector plotters (Vectors & sky kinematics) are built on top of these; reach for the passthroughs when you want a plain matplotlib call to simply understand sky coordinates.
Synthetic headers & frame utilities#
Sometimes you need a WCS without having data yet — for layout planning, testing, or rasterizing onto a target grid:
dummy_allsky_hdr(),dummy_ortho_hdr(),dummy_offset_hdr(),dummy_standard_hdr()— ready-made FITS headers for each frame family.get_frame_class()— the matplotlib frame class (elliptical, circular, …) registered for a projection.clip_to_frame()— clip all data artists on a frame to its curved boundary (handy after adding artists outside the helpers);clip_to_projection_boundary()clips a single artist to the projection’s visible region (the per-artist variant the interrupted-projection plotters use).describe_wcs()— print a readable summary of any WCS or FITS header.
The projection primitive#
Underneath every overlay sits one function, project(),
mapping sky (lon, lat) to canvas (x, y) for a given projection/center —
the primitive shared by the matplotlib and plotly backends (Core concepts & conventions).
You rarely call it directly, but it’s public for custom work, alongside the
lower-level project_to_canvas() and the HEALPix-specific
healpix_to_canvas() (HEALPix).
Pitfalls#
A field that’s all sky, or an all-sky map that’s a postage stamp — on the elliptical all-sky projections (AIT, MOL, …)
fov_deg=/cdelt=/npix=don’t crop; they always show the full sphere. The conics are the exception: they’re all-sky by default but switch to a zoomed regional view when you passfov_deg=.Conic projections erroring in other tools — conics genuinely require PV parameters; skyplothelper supplies a usable default and exposes
pv2_1=/pv2_2=when you need a specific standard parallel.Mirrored or “backwards” maps — longitude direction, not the projection. See Core concepts & conventions.
Mixing offset and absolute thinking — an
offset_figureframe labels positions relative to the center; overlay helpers still take absolute sky coordinates and project correctly. Don’t pre-subtract the center yourself.
The full builder/utility listing is in the API reference.
See also: Images & FITS (reproject imagery onto a frame),
Styling & themes (the style= tick/label presets), HEALPix
(HEALPix rasters and the HPX pole-lock).
Tutorial: A tour of projections walks through the projection gallery, choosing among frame types, and the longitude conventions with worked figures.