Azimlib0.3.0 · documentation

Transforms and composition in 0.3 development

This is an implemented subset of familiar plotting contracts, using Azimlib's own geometry, projection, scene and renderer. Stable PyPI 0.2.0 is unchanged.

Coordinate spaces and inverses

ax.transData maps longitude/latitude degrees to display; transProjection maps projected X/Y in the projection's units; transAxes maps fractions of the actual equal-aspect map box. fig.transFigure uses figure fractions. Public display coordinates are DPI pixels, origin bottom-left, Y up. The internal scene uses logical 100-DPI pixels and Y down; conversion is explicit. IdentityTransform means display coordinates. PhysicalTransform(fig, unit) accepts inches, points or mm; fig.dpi_scale_trans is inches to display.

from azimlib.transforms import Affine2D, offset_copy, blended_transform_factory

t = Affine2D().scale(2).translate(5, 3)
assert t.inverted().transform_point(t.transform_point((1, 2))) == (1, 2)
ax.text(-50, -20, 'Offset', transform=offset_copy(ax.transData, fig, x=6, y=4, units='points'))
ax.text(-50, .05, 'Mixed', transform=blended_transform_factory(ax.transData, ax.transAxes))

a + b applies a first, b second. Transform objects compose by reference; axes limits, figure size and DPI are read live. Affine inversion rejects a singular matrix. Geographic inverse/forward may return None outside a domain. Mixed inversion is supported only for known separable transforms; a rotated or curved geographic blend raises NotImplementedError instead of inventing an inverse. Cross-figure Artist transforms are rejected before attachment/edit. This is not a full cache/invalidation graph for arbitrary user transforms.

Existing geometry/scatter/text/path artists accept set_transform(t); text, line and annotation entry points also accept a Transform. An annotation uses it for its target and for a data-space text position; offset points remain relative to the transformed target. Existing offset-pixels Y-down semantics are retained. Independent xycoords/textcoords Transform objects and general Figure-text transforms are not yet part of the annotation/text contract.

Gaps, paths and editable collections

Plot inputs preserve NaN gaps in get_data and editing; masked NumPy scalars become gaps without making NumPy a core dependency. Infinity is rejected. This does not relax strict finite geographic Geometry/GeoJSON validation. Path has immutable vertices/codes, MOVETO/LINETO/CURVE3/CURVE4/CLOSEPOLY/STOP and compound rings. Public flattening defaults to 24 steps for compatibility; PathPatch/LineCollection rendering now uses bounded adaptive subdivision with a .2 logical-pixel tolerance. See curves and transform limits.

PathPatch draws reusable Path geometry under a transform. Symbol.patch() creates a new patch per use. LineCollection batches independent paths, cycles per-segment colors/widths/dashes, and edits segments/styles/array in one transaction with one notification. Its scalar array must match the segment count; its norm/cmap drive colors and colorbars. A failed edit leaves data intact. Collection attachment currently does not auto-fit path/segment bounds; set the map extent explicitly. Curved transformed paths are flattened before their transform, rather than using geographic great-circle densification.

Plot, scatter, fill/patch, collections and unmapped quiver have independent style-cycle cursors. Explicit properties do not consume a row when every cycle property is already supplied; color-mapped vectors use their mappable.

HandlerBase.create_artists receives the familiar legend/handle/dimensions/ fontsize/transform signature. Handlers return fresh Azimlib PathPatch artists; HandlerSymbol supplies a reusable unit-box Symbol. Instance mappings precede MRO class mappings. Arbitrary foreign artists, images and composite handler families are outside this initial protocol.

Numeric scales, dates and graticules

scale supplies LinearScale, LogScale, SymLogScale and inverses for numeric transforms. dates supplies UTC days since 1970, date2num/num2date, DateFormatter, DayLocator, MonthLocator and AutoDateLocator. Date/log locators and formatters can be installed on scalar colorbar axes; LogNorm remains a color normalization. MapAxes longitude/latitude stay linear degrees and reject log/symlog setters. No implicit date-as-latitude or unit converter registry. LogFormatter variants produce plain-text powers, not a MathText engine.

Curved or rotated graticules sample lines over the supported projection domain, intersect their segments with the frame and place border ticks at those hits. Orthographic horizon endpoints are assigned to a border label side. Sampling is bounded (361 points for intersections), not exact analytic intersection of every possible projection boundary. Scientific offset artists remain linked to the formatter and use the same scene as export/layout; CRS values do not change.

Subfigures, independent grids and layout

fig = azl.figure(figsize=(12, 5), layout='constrained')
left, right = fig.subfigures(1, 2)
a, b = left.subplots(), right.subplots()
left.suptitle('Region A')
right.suptitle('Region B')

SubFigure owns its axes/text/grids, shares the root canvas and export, and can contain subfigures. add_axes/text positions are local fractions; returned GridSpecs use root fractions, including explicit updates. MapAxes.figure is the root Figure, while its Artist parent is the SubFigure. Independent roots must have disjoint explicit regions. The regional solver preserves manual axes, uses existing weighted/nested-track measurement and rolls all roots back on failure. Overlapping root regions warn and retain the previous positions.

layout='compressed' first solves constrained layout, then compacts measured fixed-aspect boxes in complete, unspanned grids. Nested or spanning grids keep their constrained solution. This does not reproduce all reference solver optimizations. Free Figure/SubFigure text and explicit cax reserve edge strips when in_layout is true; interior objects stay manual. It is not a universal collision solver. Manual positions remain unchanged. Simple/nested solvers retain the existing bounded 16/32 measurement limits and failure rollback.

See layout, rotation and recommended light view, runnable atlas and 27 integration tests.

Custom HandlerSymbol fits the finite path bounds uniformly and centres it in the handle box, preserving aspect ratio across handle width/height and DPI. Empty symbols and nonpositive handle sizes fail explicitly.