Projection Module — Projected ("Globe") Map Frames & Presets#
The cleopatra.basemap.projection module has two layers:
-
apply_projection_frame— the low-level, stateless renderer. It turns a plain matplotlibAxesinto a static projected ("globe") frame: it sets the projected limits and equal aspect, draws the projection boundary (the globe's circle, Robinson's rounded rectangle, ...), optionally clips the existing data layers to that boundary, and draws the graticule polylines. This function is pure matplotlib with no PROJ/CRS dependency — it only receives already-computed geometry (boundary vertices, graticule polylines, projected limits) as plain arrays and renders it. -
Orthographic globe presets — higher-level helpers that do the reprojection for the common orthographic ("globe") case, and therefore require
pyproj(thecleopatra[tiles]extra):apply_projection_style(the one-call entry point, driven byPROJECTION_STYLES),orthographic_grid/orthographic_grid_edges(reproject a lon/lat grid, masking the far hemisphere),orthographic_points(reproject scattered lon/lat points), andorthographic_boundary/orthographic_graticule(the globe outline and meridian/parallel polylines).apply_projection_style(style="globe")reprojects(lon, lat, data)and draws the boundary + graticule viaapply_projection_frame; pair it withcolors.apply_data_styleto compose the full CAMS "haze" globe in a couple of lines (see the Haze-style presets example notebook).
If you already have projected geometry from an upstream engine, use apply_projection_frame
directly and skip the pyproj-backed presets.
Usage#
import matplotlib
matplotlib.use("Agg") # any backend; Agg shown for headless rendering
import numpy as np
import matplotlib.pyplot as plt
from cleopatra.basemap.projection import apply_projection_frame
# Geometry comes in as plain arrays (here a unit-circle globe outline and a
# meridian); upstream produces the real projected boundary/graticule.
theta = np.linspace(0, 2 * np.pi, 200)
boundary = np.column_stack([np.cos(theta), np.sin(theta)])
meridian = np.column_stack([np.zeros(50), np.linspace(-1, 1, 50)])
fig, ax = plt.subplots()
# plot your (already reprojected) data first so it can be clipped ...
ax.imshow(np.zeros((8, 8)), extent=(-1, 1, -1, 1))
# ... then frame the axes as a globe and clip the data to the boundary
patch = apply_projection_frame(
ax,
boundary_xy=boundary,
xlim=(-1, 1),
ylim=(-1, 1),
graticule_lines=[meridian],
)
fig.savefig("globe.png")
Note
apply_projection_frame performs no reprojection — pass already-densified,
already-projected geometry as (N, 2) arrays. It is a one-shot helper: each call
appends a fresh boundary patch and graticule lines, so apply it once per axes (create a
new axes to re-frame). With clip_artists=True (default) every existing
ax.images/ax.collections/ax.lines artist — and the graticule — is clipped to the
boundary; pass clip_artists=False to leave the data layers unclipped. Style the
boundary and graticule via boundary_kw / graticule_kw, which override
DEFAULT_BOUNDARY_KW / DEFAULT_GRATICULE_KW.
Module Documentation#
cleopatra.basemap.projection
#
Static projected ('globe') map frame for matplotlib axes.
Provides apply_projection_frame -- a single stateless helper that turns
a plain matplotlib.axes.Axes into a static projected map frame: it sets
the projected limits and equal aspect, draws the projection boundary
(the globe's circle, Robinson's rounded rectangle, ...), optionally
clips the existing data layers to that boundary, and draws the
graticule polylines.
The module is pure matplotlib with no PROJ/CRS dependency. It only receives already-computed geometry -- boundary vertices, graticule polylines, and projected limits -- as plain arrays. Whatever produces the projection (reprojecting data and deriving the boundary/graticule) lives upstream; cleopatra only renders the result. This keeps the engine split clean: the upstream owns CRS/PROJ, cleopatra owns matplotlib.
Examples:
Frame a plain axes as an orthographic globe and clip an image to the boundary circle:
>>> import matplotlib
>>> matplotlib.use("Agg")
>>> import numpy as np
>>> import matplotlib.pyplot as plt
>>> theta = np.linspace(0, 2 * np.pi, 200)
>>> boundary = np.column_stack([np.cos(theta), np.sin(theta)])
>>> fig, ax = plt.subplots()
>>> _ = ax.imshow(np.random.rand(8, 8), extent=(-1, 1, -1, 1))
>>> patch = apply_projection_frame(
... ax, boundary_xy=boundary, xlim=(-1, 1), ylim=(-1, 1)
... )
>>> ax.get_aspect()
1.0
apply_projection_frame(ax, *, boundary_xy, xlim, ylim, graticule_lines=None, clip_artists=True, boundary_kw=None, graticule_kw=None)
#
Turn a plain axes into a static projected ('globe') frame.
Sets equal aspect and the projected x/y limits, draws the projection
boundary as a matplotlib.patches.PathPatch, draws the graticule
polylines, optionally clips the existing data layers to the boundary,
and turns the axis decorations off. The boundary geometry, graticule
polylines, and limits are supplied as plain arrays -- this function
performs no reprojection and has no PROJ/CRS dependency.
This is a one-shot helper: each call appends a fresh boundary patch and a fresh set of graticule lines, so calling it twice on the same axes stacks duplicate artists. Apply it once per axes (create a new axes to re-frame).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Any
|
Matplotlib |
required |
boundary_xy
|
Any
|
|
required |
xlim
|
Sequence[float]
|
Projected-coordinate x-limits |
required |
ylim
|
Sequence[float]
|
Projected-coordinate y-limits |
required |
graticule_lines
|
Sequence[Any] | None
|
Optional list of |
None
|
clip_artists
|
bool
|
If |
True
|
boundary_kw
|
dict[str, Any] | None
|
Style overrides for the boundary patch, merged over
|
None
|
graticule_kw
|
dict[str, Any] | None
|
Style overrides for the graticule lines, merged
over |
None
|
Returns:
| Type | Description |
|---|---|
PathPatch
|
matplotlib.patches.PathPatch: The boundary patch added to the |
PathPatch
|
axes (also used as the clip path). |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
Examples:
- Frame a plain axes as a globe and read back the result: the
returned patch is registered on the axes and the aspect is equal:
>>> import matplotlib >>> matplotlib.use("Agg") >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_frame >>> theta = np.linspace(0, 2 * np.pi, 200) >>> boundary = np.column_stack([np.cos(theta), np.sin(theta)]) >>> fig, ax = plt.subplots() >>> patch = apply_projection_frame( ... ax, boundary_xy=boundary, xlim=(-1, 1), ylim=(-1, 1) ... ) >>> ax.get_aspect() 1.0 >>> patch in ax.patches True - Clip a data image and draw one graticule line (plain lists are
accepted): the image gains a clip path and one line is added:
>>> import matplotlib >>> matplotlib.use("Agg") >>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_frame >>> boundary = [[1, 0], [0, 1], [-1, 0], [0, -1]] >>> meridian = [[0, -1], [0, 1]] >>> fig, ax = plt.subplots() >>> im = ax.imshow(np.zeros((4, 4)), extent=(-1, 1, -1, 1)) >>> patch = apply_projection_frame( ... ax, ... boundary_xy=boundary, ... xlim=(-1, 1), ... ylim=(-1, 1), ... graticule_lines=[meridian], ... ) >>> im.get_clip_path() is not None True >>> len(ax.lines) 1 - Passing a non-Axes object raises
TypeError:
Source code in src/cleopatra/basemap/projection.py
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 | |
apply_projection_style(ax, lon, lat, data, style='globe', *, draw_frame=True, **overrides)
#
Reproject (lon, lat, data) and frame ax per a PROJECTION_STYLES preset.
For "globe", reprojects via orthographic_grid and draws the globe
boundary + graticule on ax via apply_projection_frame. For "flat",
returns (lon, lat, data) unchanged and does not touch ax at all --
the data is meant to be plotted directly in lon/lat coordinates.
This is the projection half of composing the haze look; the colour half
is cleopatra.styling.colors.apply_data_style, called with the (x, y, data)
this function returns as its x/y/layer arguments. Both styles return
cell-edge coordinates (one larger per axis than data) for use with
shading="flat": the orthographic projection's extreme distortion near
its centre makes matplotlib's automatic centre-to-edge inference
(shading="auto"/"nearest") unreliable -- most cells can render as
degenerate slivers -- so edges are computed explicitly and reliably
instead. The same two-line pattern composes both layers regardless of
which style was chosen:
x, y, masked = apply_projection_style(ax, lon, lat, data, style=chosen)
apply_data_style(ax, {"dust": masked}, x=x, y=y, shading="flat")
Neither function requires the other: use "globe" with a single plain
colormap instead of apply_data_style, or "flat" with the "haze"
data style and no globe at all.
This function takes a single data array, not a layers dict like
apply_data_style -- drawing several layers on the same grid/axes
means calling it once per layer. apply_projection_frame (which draws
the boundary/graticule) is one-shot per axes: a second unguarded call
stacks a duplicate boundary patch and graticule. Pass draw_frame=False
on every call after the first to reproject/mask that layer's data
without redrawing the chrome:
x, y, om = apply_projection_style(ax, lon, lat, organic_matter, style="globe")
_, _, du = apply_projection_style(ax, lon, lat, dust, style="globe", draw_frame=False)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes
|
Axes to draw the boundary/graticule on. Ignored for |
required |
lon
|
Any
|
1D vector of cell-centre longitudes, degrees. Both styles
require 1D |
required |
lat
|
Any
|
1D vector of cell-centre latitudes, degrees, paired with |
required |
data
|
Any
|
2D array of values, shape |
required |
style
|
str
|
A name from |
'globe'
|
draw_frame
|
bool
|
If |
True
|
**overrides
|
Any
|
Override any of the style's parameters -- for
|
{}
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
ndarray
|
|
ndarray
|
shaped |
|
ndarray
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
ValueError
|
If |
ImportError
|
If |
Examples:
"globe"reprojects the data, draws a boundary patch onax, and returns edge coordinates one larger per axis thandata:>>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_style >>> fig, ax = plt.subplots() >>> lon = np.array([0.0, 90.0]) >>> lat = np.array([90.0, -90.0]) >>> data = np.array([[1.0, 2.0], [3.0, 4.0]]) >>> x, y, masked = apply_projection_style(ax, lon, lat, data, style="globe") >>> len(ax.patches) # the boundary circle 1 >>> x.shape # one larger per axis than data's (2, 2) (3, 3) >>> np.all(np.isnan(masked[1])) # far hemisphere still masked np.True_ >>> plt.close(fig)"flat"draws nothing but still returns matching edge coordinates, so the sameshading="flat"call works for either style:>>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_style >>> fig, ax = plt.subplots() >>> lon = np.array([0.0, 90.0]) >>> lat = np.array([90.0, -90.0]) >>> data = np.array([[1.0, 2.0], [3.0, 4.0]]) >>> x, y, out = apply_projection_style(ax, lon, lat, data, style="flat") >>> len(ax.patches) 0 >>> x.shape (3, 3) >>> np.array_equal(out, data) True >>> plt.close(fig)- A second layer on the same globe with
draw_frame=Falsereuses the already-drawn chrome instead of stacking a duplicate boundary:>>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_style >>> fig, ax = plt.subplots() >>> lon = np.array([0.0, 90.0]) >>> lat = np.array([90.0, -90.0]) >>> first = np.array([[1.0, 2.0], [3.0, 4.0]]) >>> second = np.array([[5.0, 6.0], [7.0, 8.0]]) >>> _ = apply_projection_style(ax, lon, lat, first, style="globe") >>> _ = apply_projection_style(ax, lon, lat, second, style="globe", draw_frame=False) >>> len(ax.patches) # still one boundary, not two 1 >>> plt.close(fig) - An unknown style raises
KeyError:>>> import numpy as np >>> import matplotlib.pyplot as plt >>> from cleopatra.basemap.projection import apply_projection_style >>> fig, ax = plt.subplots() >>> apply_projection_style( ... ax, np.array([0.0]), np.array([0.0]), np.array([[1.0]]), ... style="not-a-style", ... ) Traceback (most recent call last): ... KeyError: "Unknown projection style 'not-a-style'; available: ['flat', 'globe']" >>> plt.close(fig)
See Also
orthographic_grid: The "globe" cell-centre reprojection primitive.
orthographic_grid_edges: The "globe" cell-edge reprojection primitive.
apply_projection_frame: Draws the boundary/graticule this composes.
cleopatra.styling.colors.apply_data_style: The companion data-style axis.
Source code in src/cleopatra/basemap/projection.py
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 | |
apply_projection_style_mesh(ax, node_x, node_y, triangles, *, style='globe', draw_frame=True, **overrides)
#
Reproject an unstructured-mesh triangulation onto a projection preset.
The mesh counterpart to apply_projection_style: reproject the node lon/lat
to the projected plane, build a matplotlib.tri.Triangulation on the
projected coordinates with the same connectivity, mask any triangle that has
a node on the far hemisphere, and -- for "globe" -- draw the boundary +
graticule frame on ax. Returns the (masked) triangulation, ready for
tripcolor / tricontourf.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
ax
|
Axes
|
Axes to frame (used by the |
required |
node_x
|
Any
|
1-D node longitudes (degrees). |
required |
node_y
|
Any
|
1-D node latitudes (degrees). |
required |
triangles
|
Any
|
|
required |
style
|
str
|
A |
'globe'
|
draw_frame
|
bool
|
Draw the globe boundary + graticule (globe only). |
True
|
**overrides
|
Any
|
Override the style's |
{}
|
Returns:
| Type | Description |
|---|---|
Triangulation
|
matplotlib.tri.Triangulation: The reprojected, far-hemisphere-masked
triangulation. |
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
ImportError
|
If the globe path is used without |
Source code in src/cleopatra/basemap/projection.py
orthographic_boundary(n=200, radius=ORTHOGRAPHIC_RADIUS_M)
#
Return the orthographic globe's boundary circle.
The orthographic projection's visible-hemisphere boundary is always a
circle of the projection's radius centred at the origin, independent of
which point the globe is centred on -- no pyproj call is needed. Pass
the result as apply_projection_frame's boundary_xy.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n
|
int
|
Number of vertices around the circle. |
200
|
radius
|
float
|
Circle radius in projected-CRS units (metres). Defaults to
|
ORTHOGRAPHIC_RADIUS_M
|
Returns:
| Type | Description |
|---|---|
ndarray
|
np.ndarray: |
Examples:
- The boundary is a circle of the given radius, centred at the origin:
See Also
orthographic_grid: The projected data this boundary frames. apply_projection_frame: Renders the boundary onto an axes.
Source code in src/cleopatra/basemap/projection.py
orthographic_graticule(center_lat=90.0, center_lon=0.0, step=30.0, densify=200)
#
Build graticule (meridian/parallel) polylines for an orthographic view.
Generates meridian (constant-longitude) and parallel (constant-latitude)
lines spaced step degrees apart, reprojects them with the same centre
as orthographic_grid, and splits each at the visible-hemisphere edge so
only the visible portion of each line is returned. Pass the result as
apply_projection_frame's graticule_lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
center_lat
|
float
|
Latitude the globe is centred on. Must match the value
passed to |
90.0
|
center_lon
|
float
|
Longitude the globe is centred on. Must match
|
0.0
|
step
|
float
|
Degree spacing between meridians/parallels. Must be positive. |
30.0
|
densify
|
int
|
Number of points each line is sampled at before reprojecting -- higher values give smoother curves near the visible-hemisphere edge. |
200
|
Returns:
| Type | Description |
|---|---|
list[ndarray]
|
list[np.ndarray]: One |
list[ndarray]
|
crossing the hemisphere edge are split into separate segments). |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyproj (the |
ValueError
|
If |
Examples:
- A 90-degree step gives a small set of meridians/parallels, each a
(densify, 2)or shorter (edge-clipped) segment: - A non-positive step raises
ValueError:
See Also
orthographic_grid: The projected data this graticule frames. orthographic_boundary: The matching globe outline.
Source code in src/cleopatra/basemap/projection.py
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 | |
orthographic_grid(lon, lat, data, center_lat=90.0, center_lon=0.0)
#
Reproject a lon/lat grid onto an orthographic ('globe') view.
Projects every grid point via pyproj onto a sphere viewed from directly
above (center_lat, center_lon), and masks out the far (non-visible)
hemisphere -- the orthographic projection formula is defined everywhere
but is only meaningful for the visible half, so points beyond it are set
to NaN in the returned data rather than silently folded onto the visible
disk. Pair the result with alpha_scaled_mesh (cleopatra.styling.colors),
which -- unlike alpha_scaled_image -- can render this kind of
curvilinear (non-rectangular) grid.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lon
|
Any
|
Longitudes in degrees, either a 1D vector (paired with a 1D
|
required |
lat
|
Any
|
Latitudes in degrees, same convention as |
required |
data
|
Any
|
2D array of values to reproject alongside the grid. |
required |
center_lat
|
float
|
Latitude the globe is centred on (the "camera" points at
this point). Defaults to |
90.0
|
center_lon
|
float
|
Longitude the globe is centred on. Defaults to |
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
ndarray
|
|
ndarray
|
same shape as |
|
ndarray
|
set to NaN. |
|
tuple[ndarray, ndarray, ndarray]
|
diverges at the antipodal point, so any non-visible or non-finite |
|
tuple[ndarray, ndarray, ndarray]
|
coordinate is replaced with a |
|
tuple[ndarray, ndarray, ndarray]
|
cells carry no data and are never rendered), keeping the grid a |
|
tuple[ndarray, ndarray, ndarray]
|
valid |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyproj (the |
ValueError
|
If |
Examples:
- Reproject a tiny 2x2 grid centred on the North Pole; the row at
latitude -90 (South Pole) is masked out as not visible:
>>> import numpy as np >>> from cleopatra.basemap.projection import orthographic_grid >>> lon = np.array([0.0, 90.0]) >>> lat = np.array([90.0, -90.0]) >>> data = np.array([[1.0, 2.0], [3.0, 4.0]]) >>> x, y, masked = orthographic_grid(lon, lat, data) >>> masked[0] # latitude 90: visible, values kept array([1., 2.]) >>> np.all(np.isnan(masked[1])) # latitude -90: not visible np.True_
See Also
orthographic_boundary: The matching globe outline for this centre. orthographic_graticule: Matching meridian/parallel lines. cleopatra.styling.colors.alpha_scaled_mesh: Renders the returned grid.
Source code in src/cleopatra/basemap/projection.py
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 | |
orthographic_grid_edges(lon, lat, center_lat=90.0, center_lon=0.0)
#
Reproject 1D lon/lat cell-centre vectors to orthographic cell-edge coordinates.
Companion to orthographic_grid: that function reprojects data at cell
centres, matching pcolormesh's shading="auto"/"nearest"
convention -- but the orthographic projection's extreme local distortion
(longitude lines compress drastically near the projection centre) makes
matplotlib's automatic centre-to-edge inference unreliable there: most
cells can render as degenerate slivers, silently dropping most of the
grid. This function instead reprojects the cell edges, for use with
shading="flat" (matplotlib draws exactly the given quads, inferring
nothing) -- the reliable choice for this projection.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lon
|
Any
|
1D vector of cell-centre longitudes, degrees. |
required |
lat
|
Any
|
1D vector of cell-centre latitudes, degrees. |
required |
center_lat
|
float
|
Latitude the globe is centred on. Must match the value
passed to |
90.0
|
center_lon
|
float
|
Longitude the globe is centred on. Must match
|
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
ndarray
|
|
ndarray
|
-- one more per axis than a |
|
tuple[ndarray, ndarray]
|
for |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyproj (the |
Warning
The orthographic projection is only finite on the visible (near)
hemisphere -- an edge point beyond it has no real coordinate, so it
is placed at a finite placeholder (not a meaningful position). A data
cell whose centre is visible can still have a corner past the
horizon; drawing it anyway pulls that corner toward the placeholder,
producing a wrongly-shaped quad. Before drawing, drop (NaN) any
cell for which not all four corners are visible -- apply_projection_style
does this automatically; call it instead of this function directly
unless you are prepared to replicate that check.
Examples:
- Edge arrays are one larger per axis than the centre vectors:
See Also
orthographic_grid: The matching cell-centre reprojection for data.
cleopatra.styling.colors.alpha_scaled_mesh: Renders with shading="flat".
Source code in src/cleopatra/basemap/projection.py
orthographic_points(lon, lat, center_lat=90.0, center_lon=0.0)
#
Reproject scattered lon/lat points onto an orthographic ('globe') view.
The point counterpart to orthographic_grid: use this for discrete
locations -- e.g. city markers for cleopatra.basemap.geo.add_point_labels --
rather than a raster grid. A globe's axes are scaled in projected
metres (ORTHOGRAPHIC_RADIUS_M), not degrees, so plotting raw lon/lat
values directly on a globe axes collapses every point toward the origin;
reproject with this function first.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lon
|
Any
|
Longitudes in degrees, scalar or 1D array. |
required |
lat
|
Any
|
Latitudes in degrees, scalar or 1D array, paired with |
required |
center_lat
|
float
|
Latitude the globe is centred on. Must match the value
passed to |
90.0
|
center_lon
|
float
|
Longitude the globe is centred on. Must match
|
0.0
|
Returns:
| Name | Type | Description |
|---|---|---|
tuple |
ndarray
|
|
ndarray
|
least 1D). A point on the far (non-visible) hemisphere is |
|
tuple[ndarray, ndarray]
|
both |
Raises:
| Type | Description |
|---|---|
ImportError
|
If pyproj (the |
Examples:
- Reproject two cities visible from a North-Pole-centred view; a
point on the far side comes back
NaN:>>> import numpy as np >>> from cleopatra.basemap.projection import orthographic_points >>> lon = np.array([-21.9, 0.0]) >>> lat = np.array([64.1, -80.0]) >>> x, y = orthographic_points(lon, lat, center_lat=90.0, center_lon=0.0) >>> np.isnan(x[0]) # Reykjavik: visible np.False_ >>> np.isnan(x[1]) # near the South Pole: not visible np.True_ - A single scalar point round-trips as a 1-element array:
See Also
orthographic_grid: The raster-grid counterpart. cleopatra.basemap.geo.add_point_labels: Renders the reprojected points.
Source code in src/cleopatra/basemap/projection.py
projection_draws_frame(style)
#
Whether a projection style draws a frozen boundary/graticule frame.
"globe" draws the frame and freezes the axes limits/aspect and hides the
axis; "flat" (identity) and no projection do not touch the axes. Glyphs use
this to decide whether the current render installs a frame (so a later flat
render must undo it), treating "flat" and None alike as flat views.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
style
|
Any
|
A projection style name, or a falsy value for no projection. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
bool |
bool
|
|