Skip to content

carto_flow.flow_cartogram.anisotropy

Velocity modulation system for cartogram algorithms.

This module provides a comprehensive system for velocity field modulation, supporting boundary decay, smoothing, and anisotropy transformations. Distance and sigma parameters are specified in world/data-coordinate units (same CRS as the input geometries) and converted to pixel space automatically at call time, so modulators work consistently across grid resolutions.

Classes:

  • VelocityModulator

    Base class for velocity field modulators.

  • BoundaryDecay

    Distance-based multiplicative falloff applied to all velocity components near geometry boundaries.

  • BoundaryNormalDecay

    Damps only the boundary-normal velocity component while preserving tangential flow near geometry boundaries.

  • DirectionalTensor

    Anisotropy aligned with a direction field (uniform angle, callable, or raster). Convenience constructors: radial, tangential, from_seeds.

  • LocalizedTensor

    Anisotropy from seed points with Gaussian spatial influence; blends toward identity where seeds don't reach.

  • Smooth

    Gaussian smoothing of velocity field.

  • Multiplicative

    Multiplicative velocity modulation.

  • Tensor

    Low-level 2x2 tensor-based velocity modulation.

  • Pipeline

    Sequence of modulators applied in order.

Functions:

Examples:

>>> from carto_flow.flow_cartogram.anisotropy import DirectionalTensor, BoundaryDecay, BoundaryNormalDecay, Smooth
>>> import numpy as np
>>>
>>> # Uniform 30° tilt
>>> DirectionalTensor(theta=np.pi / 6, Dpar=2.0, Dperp=0.5)
>>>
>>> # Radially outward from a fixed centre
>>> DirectionalTensor.radial(center=(500_000, 200_000), Dpar=2.0)
>>>
>>> # Tangential (counter-clockwise vortex)
>>> DirectionalTensor.tangential(center=(500_000, 200_000), Dpar=2.0)
>>>
>>> # Direction field from control points, blended with boundary decay
>>> seeds = [(1e5, 2e5, 0), (4e5, 5e5, np.pi / 2)]
>>> mod = DirectionalTensor.from_seeds(seeds, Dpar=3.0) + BoundaryDecay(decay_length=5)

BoundaryDecay

BoundaryDecay(
    decay_length: float = 3.0,
    damping_floor: float = 0.0,
    smooth: float | None = None,
    outside_decay: float | None = None,
)

Bases: VelocityModulator

Distance-based multiplicative velocity falloff near geometry boundaries.

Multiplies the entire velocity field by a smooth spatial mask that transitions from damping_floor at the outer boundary to 1 deep in the interior over decay_length world-coordinate units. Outside cells decay from damping_floor to 0 over outside_decay units.

This is the simpler of the two boundary-decay modulators: it treats all velocity components identically. For a geometrically aware variant that preserves tangential flow, use :class:BoundaryNormalDecay.

Parameters:

  • decay_length (float, default: 3.0 ) –

    Width of the inside transition zone in world/data-coordinate units. Controls how quickly the velocity recovers from damping_floor to 1 moving inward from the boundary.

  • damping_floor (float, default: 0.0 ) –

    Velocity factor applied exactly at the outer boundary; in [0, 1). A small positive value (e.g. 0.1–0.3) prevents complete freezing of boundary vertices and avoids jagged edges.

  • smooth (float or None, default: None ) –

    Optional Gaussian smoothing sigma applied to the signed distance field before computing the falloff, in world-coordinate units. Smoothing blurs the boundary mask, creating a softer transition.

  • outside_decay (float or None, default: None ) –

    Width of the outside decay zone in world-coordinate units. If None, uses the same value as decay_length. Smaller values suppress outside-cell drift more aggressively while leaving the interior unaffected.

Examples:

>>> BoundaryDecay(decay_length=5, damping_floor=0.1)
>>> BoundaryDecay(decay_length=10) + Smooth(sigma=2)

Methods:

  • __call__

    Apply distance-based multiplicative falloff to velocity field.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply distance-based multiplicative falloff to velocity field.

BoundaryNormalDecay

BoundaryNormalDecay(
    decay_length: float = 3.0,
    damping_floor: float = 0.0,
    smooth: float | None = None,
    renormalize: bool = False,
)

Bases: VelocityModulator

Damp the boundary-normal velocity component while preserving tangential flow.

Near the outer boundary the velocity field is decomposed into a component normal to the boundary and a tangential component. The normal component is exponentially damped over decay_length world-coordinate units; the tangential component is left unchanged. This suppresses the outward drift responsible for boundary distortion without affecting flow parallel to the boundary.

Parameters:

  • decay_length (float, default: 3.0 ) –

    Length scale over which the normal velocity component is damped, in world/data-coordinate units.

  • damping_floor (float, default: 0.0 ) –

    Minimum damping factor at the boundary; in [0, 1]. At distance d from the boundary, the normal factor is floor + (1 - floor) * (1 - exp(-d / decay_length)).

  • smooth (float or None, default: None ) –

    If not None, applies Gaussian smoothing to the signed distance field before computing boundary normals, in world-coordinate units. Useful to reduce noise in the normal direction for complex geometries.

  • renormalize (bool, default: False ) –

    If True, the output velocity is rescaled to preserve the original magnitude at each grid cell. This keeps the speed of flow constant while only rotating the direction.

Examples:

>>> BoundaryNormalDecay(decay_length=5)
>>> BoundaryNormalDecay(decay_length=3, damping_floor=0.1, smooth=1.0)

Methods:

  • __call__

    Damp the normal velocity component near geometry boundaries.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Damp the normal velocity component near geometry boundaries.

DirectionalTensor

DirectionalTensor(
    theta, Dpar: float = 2.0, Dperp: float = 1.0
)

Bases: VelocityModulator

Anisotropic velocity modulation aligned with a direction field.

Amplifies velocity along a preferred direction (Dpar) and optionally suppresses the perpendicular component (Dperp). Works correctly at any grid resolution: the rotation tensor is built lazily on the first call at each grid shape and cached for all subsequent iterations at that resolution.

Parameters:

  • theta (float | callable | ndarray) –

    Preferred flow direction in radians (0 = positive x-axis, π/2 = positive y-axis).

    • float: uniform angle applied everywhere.
    • callable (grid) -> (ny, nx) array: evaluated once per grid shape. The callable receives the current :class:Grid object, giving access to grid.X, grid.Y, grid.shape, etc.
    • ndarray: pre-computed angle field; automatically interpolated to the current grid shape with bilinear interpolation if shapes differ.
  • Dpar (float, default: 2.0 ) –

    Amplification factor along the preferred direction.

  • Dperp (float, default: 1.0 ) –

    Amplification factor perpendicular to the preferred direction.

Examples:

Uniform 45° tilt with double amplification along that axis:

>>> DirectionalTensor(theta=np.pi / 4, Dpar=2.0, Dperp=0.5)

Flow aligned radially outward from the domain centre:

>>> DirectionalTensor(
...     theta=lambda g: np.arctan2(g.Y - g.Y.mean(), g.X - g.X.mean()),
...     Dpar=2.0,
... )

Direction field from an external raster (e.g. slope aspect) — auto-resized:

>>> DirectionalTensor(theta=aspect_array, Dpar=3.0, Dperp=0.5)

Methods:

  • __call__

    Apply directional tensor modulation, using a cached tensor when possible.

  • from_seeds

    Build a direction field from a sparse set of (x, y, θ) control points.

  • radial

    Flow aligned radially outward from (or inward toward) a point.

  • tangential

    Flow aligned tangentially around a point (counter-clockwise by default).

__call__

__call__(
    vx: ndarray, vy: ndarray, grid, mask: ndarray
) -> tuple

Apply directional tensor modulation, using a cached tensor when possible.

from_seeds classmethod

from_seeds(
    seeds,
    Dpar: float = 2.0,
    Dperp: float = 1.0,
    power: float = 2.0,
) -> DirectionalTensor

Build a direction field from a sparse set of (x, y, θ) control points.

The direction at each grid cell is computed as the inverse-distance-weighted (IDW) average of the seed angles. Angle averaging is circular: each seed contributes (cos θ, sin θ) proportional to its weight, and the resultant angle is recovered with arctan2.

Parameters:

  • seeds (array-like of shape (n, 3)) –

    Each row is (x, y, theta) where x, y are data coordinates and theta is the preferred flow direction in radians.

  • Dpar (float, default: 2.0 ) –

    Amplification along the preferred direction.

  • Dperp (float, default: 1.0 ) –

    Amplification perpendicular to the preferred direction.

  • power (float, default: 2.0 ) –

    IDW distance exponent. Higher values give more localised influence (1 = linear falloff, 2 = classic IDW, ≥3 = near-Voronoi).

Examples:

>>> seeds = [
...     (100_000, 200_000, 0),          # flow east at this point
...     (400_000, 500_000, np.pi / 2),  # flow north at this point
...     (700_000, 150_000, np.pi / 4),  # flow north-east at this point
... ]
>>> DirectionalTensor.from_seeds(seeds, Dpar=3.0, Dperp=0.5)

radial classmethod

radial(
    center=None,
    Dpar: float = 2.0,
    Dperp: float = 1.0,
    inward: bool = False,
) -> DirectionalTensor

Flow aligned radially outward from (or inward toward) a point.

Parameters:

  • center ((float, float) or None, default: None ) –

    (x, y) of the origin in data coordinates. If None, the centroid of the grid bounding box is used (evaluated lazily).

  • Dpar (float, default: 2.0 ) –

    Amplification along the radial direction.

  • Dperp (float, default: 1.0 ) –

    Amplification tangential to the radial direction.

  • inward (bool, default: False ) –

    If True, preferred direction points toward center instead of away from it.

Examples:

>>> DirectionalTensor.radial(center=(500_000, 200_000), Dpar=3.0, Dperp=0.5)
>>> DirectionalTensor.radial(inward=True)          # toward domain centroid

tangential classmethod

tangential(
    center=None,
    Dpar: float = 2.0,
    Dperp: float = 1.0,
    clockwise: bool = False,
) -> DirectionalTensor

Flow aligned tangentially around a point (counter-clockwise by default).

Parameters:

  • center ((float, float) or None, default: None ) –

    (x, y) of the rotation origin in data coordinates. If None, the centroid of the grid bounding box is used.

  • Dpar (float, default: 2.0 ) –

    Amplification along the tangential direction.

  • Dperp (float, default: 1.0 ) –

    Amplification along the radial direction.

  • clockwise (bool, default: False ) –

    If True, the preferred rotation direction is clockwise.

Examples:

>>> DirectionalTensor.tangential(center=(500_000, 200_000), Dpar=2.0)

LocalizedTensor

LocalizedTensor(
    seeds,
    default_Dpar: float = 2.0,
    default_Dperp: float = 1.0,
)

Bases: VelocityModulator

Velocity modulation from localized seed points with Gaussian influence.

Each seed defines a preferred flow direction and amplification at a specific location. Influence decays as a Gaussian whose shape is controlled by sigma. Where seeds do not reach (total Gaussian weight < 1) the field blends toward the identity — isotropic, velocity unchanged.

Parameters:

  • seeds (list of dict or list of tuple) –

    Each element describes one seed. Dict keys:

    • x, y: location in data coordinates (required)
    • theta: preferred flow direction in radians, 0 = +x axis. Required unless sigma is a (2, 2) covariance matrix, in which case it is derived from the matrix's major eigenvector.
    • sigma: influence zone — three forms accepted:

    • float — isotropic circle of radius σ

    • array-like (σ_par, σ_perp) — ellipse aligned with theta; σ_par along the flow direction, σ_perp across it. theta is required.
    • (2, 2) array Σ — full covariance matrix; theta may be omitted (derived from the major eigenvector of Σ) or specified independently

    • Dpar, Dperp: optional per-seed amplification overrides

    Tuple form (x, y, theta, sigma) or (x, y, theta, sigma, Dpar, Dperp) is also accepted; the tuple form always requires theta and sigma must be a scalar.

  • default_Dpar (float, default: 2.0 ) –

    Amplification along preferred direction for seeds that omit Dpar.

  • default_Dperp (float, default: 1.0 ) –

    Amplification perpendicular to preferred direction for seeds that omit Dperp.

Examples:

Isotropic influence zone:

>>> LocalizedTensor([dict(x=500_000, y=200_000, theta=np.pi/4, sigma=80_000)])

Elliptical zone aligned with flow (3× wider along flow than across):

>>> LocalizedTensor([
...     dict(x=500_000, y=200_000, theta=np.pi/4,
...          sigma=(120_000, 40_000), Dpar=3.0, Dperp=0.5),
... ])

Full covariance; theta derived from the major eigenvector:

>>> theta = np.pi / 4
>>> c, s = np.cos(theta), np.sin(theta)
>>> Sigma = (120_000**2 * np.outer([c, s], [c, s])
...        + 40_000**2 * np.outer([-s, c], [-s, c]))
>>> LocalizedTensor([dict(x=500_000, y=200_000, sigma=Sigma, Dpar=3.0)])

Methods:

  • __call__

    Apply localised tensor modulation, using cached arrays when possible.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid, mask: ndarray
) -> tuple

Apply localised tensor modulation, using cached arrays when possible.

Multiplicative

Multiplicative(fx, fy)

Bases: VelocityModulator

Element-wise scaling of velocity components.

Multiplies vx by fx and vy by fy independently. Each factor can be a scalar, a pre-computed (ny, nx) array, or a callable (grid) -> (ny, nx) array evaluated lazily at call time.

Parameters:

  • fx (float or ndarray or callable) –

    Scaling factor for the x-component of velocity.

  • fy (float or ndarray or callable) –

    Scaling factor for the y-component of velocity.

Examples:

Suppress horizontal flow everywhere:

>>> Multiplicative(fx=0.0, fy=1.0)

Spatially varying x-suppression based on grid position:

>>> Multiplicative(fx=lambda g: np.clip(g.X / g.X.max(), 0, 1), fy=1.0)

Methods:

  • __call__

    Apply multiplicative modulation to velocity field.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply multiplicative modulation to velocity field.

Pipeline

Pipeline(modulators: list)

Bases: VelocityModulator

Sequence of velocity modulators applied left-to-right.

Constructed automatically when two modulators are combined with +. Further + calls append to the same pipeline rather than nesting pipelines::

pipe = BoundaryDecay() + Smooth(sigma=2)

Methods:

  • __add__

    Chain another modulator after this pipeline.

  • __call__

    Apply all modulators in sequence.

__add__

__add__(other: VelocityModulator) -> Pipeline

Chain another modulator after this pipeline.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply all modulators in sequence.

Smooth

Smooth(sigma: float = 3.0)

Bases: VelocityModulator

Gaussian smoothing of the velocity field.

Convolves both velocity components with an isotropic Gaussian kernel. Smoothing reduces high-frequency oscillations and can improve convergence, at the cost of some sharpness in the final cartogram.

Parameters:

  • sigma (float, default: 3.0 ) –

    Standard deviation of the Gaussian kernel in world/data-coordinate units. Converted to pixels automatically at call time.

Examples:

>>> Smooth(sigma=2)
>>> BoundaryDecay(decay_length=5) + Smooth(sigma=1)

Methods:

  • __call__

    Apply Gaussian smoothing to velocity field.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply Gaussian smoothing to velocity field.

Tensor

Tensor(Axx, Axy, Ayx, Ayy)

Bases: VelocityModulator

Low-level 2×2 matrix transform of the velocity field.

Applies [vx', vy'] = A @ [vx, vy] pointwise, where A has components Axx, Axy, Ayx, Ayy. Each component can be a scalar, a pre-computed (ny, nx) array, or a callable (grid) -> (ny, nx) array.

This is the most general velocity modulator; higher-level classes such as :class:DirectionalTensor and :class:LocalizedTensor construct their tensors automatically from geometric parameters.

Parameters:

  • Axx (float or ndarray or callable) –

    Components of the 2×2 transform matrix. The identity is Axx=Ayy=1, Axy=Ayx=0.

  • Axy (float or ndarray or callable) –

    Components of the 2×2 transform matrix. The identity is Axx=Ayy=1, Axy=Ayx=0.

  • Ayx (float or ndarray or callable) –

    Components of the 2×2 transform matrix. The identity is Axx=Ayy=1, Axy=Ayx=0.

  • Ayy (float or ndarray or callable) –

    Components of the 2×2 transform matrix. The identity is Axx=Ayy=1, Axy=Ayx=0.

Examples:

Uniform 45° rotation:

>>> import numpy as np
>>> c, s = np.cos(np.pi / 4), np.sin(np.pi / 4)
>>> Tensor(Axx=c, Axy=-s, Ayx=s, Ayy=c)

Methods:

  • __call__

    Apply tensor modulation to velocity field.

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply tensor modulation to velocity field.

VelocityModulator

Abstract base class for velocity field modulators.

Subclasses implement __call__(vx, vy, grid, mask) and return (vx_new, vy_new). Modulators can be chained with + into a :class:Pipeline that applies them left-to-right::

mod = BoundaryDecay(decay_length=5) + Smooth(sigma=2)

Methods:

  • __add__

    Chain modulators in sequence (self followed by other).

  • __call__

    Apply modulation to velocity field.

__add__

__add__(other: VelocityModulator) -> Pipeline

Chain modulators in sequence (self followed by other).

__call__

__call__(
    vx: ndarray, vy: ndarray, grid: Grid, mask: ndarray
) -> tuple[np.ndarray, np.ndarray]

Apply modulation to velocity field.

apply_anisotropy_tensor

apply_anisotropy_tensor(vx, vy, Axx, Axy, Ayx, Ayy)

Apply anisotropy tensor to velocity field components.

Axx etc are (ny,nx) arrays. Compute: vx' = Axxvx + Axyvy vy' = Ayxvx + Ayyvy

Parameters:

  • vx (ndarray) –

    Velocity field components with shape (ny, nx)

  • vy (ndarray) –

    Velocity field components with shape (ny, nx)

  • Axx (ndarray) –

    Anisotropy tensor components with shape (ny, nx)

  • Axy (ndarray) –

    Anisotropy tensor components with shape (ny, nx)

  • Ayx (ndarray) –

    Anisotropy tensor components with shape (ny, nx)

  • Ayy (ndarray) –

    Anisotropy tensor components with shape (ny, nx)

Returns:

  • vx_new, vy_new : np.ndarray

    Transformed velocity field components

build_axis_aligned_tensor

build_axis_aligned_tensor(
    nx, ny, Dx=1.0, Dy=1.0, theta_field=None
)

Build axis-aligned anisotropy field (scalar Dx, Dy) optionally rotated by theta(x,y).

Returns Axx, Axy, Ayx, Ayy arrays. If theta_field is None, axis-aligned (no rotation). theta_field shape (ny,nx) in radians if provided.

Parameters:

  • nx (int) –

    Grid dimensions (x, y)

  • ny (int) –

    Grid dimensions (x, y)

  • Dx (float, default: 1.0 ) –

    Anisotropy scaling factors in x and y directions

  • Dy (float, default: 1.0 ) –

    Anisotropy scaling factors in x and y directions

  • theta_field (ndarray, default: None ) –

    Rotation field with shape (ny, nx) in radians

Returns:

  • Axx, Axy, Ayx, Ayy : np.ndarray

    Anisotropy tensor components

preview_modulator

preview_modulator(
    modulator: VelocityModulator | None = None,
    gdf=None,
    grid_size: int = 64,
    skip: int = 4,
    input_angle: float = 0.0,
    margin: float = 0.1,
    show_geometry: bool = True,
    ax=None,
    cmap="viridis",
    show_colorbar: bool = True,
    arrows_kwargs: dict | None = None,
    colorbar_kwargs: dict | None = None,
    values=None,
    column: str | None = None,
    Dx: float = 1.0,
    Dy: float = 1.0,
    show_vectors: bool | str | tuple = "output",
    input_arrows_kwargs: dict | None = None,
    diff_cmap: str = "Reds",
    diff_arrows_kwargs: dict | None = None,
    diff_colorbar_kwargs: dict | None = None,
    heatmap: str | None = None,
    heatmap_type: str = "magnitude",
    heatmap_cmap: str | None = None,
    heatmap_kwargs: dict | None = None,
    heatmap_colorbar_kwargs: dict | None = None,
    heatmap_alpha_from_magnitude: bool = False,
    arrow_scale: float = 1.0,
)

Preview a velocity modulator on a uniform probe field.

Applies the modulator to a spatially uniform input field and plots the result as a quiver diagram. Arrow colour encodes the local amplification factor (output magnitude ÷ input magnitude = 1.0 for identity). For :class:LocalizedTensor, an optional background heatmap shows the total Gaussian seed weight (how much each location is dominated by seeds vs. the isotropic background).

The geometry mask passed to the modulator is rasterized from gdf, so modulators that use boundary proximity (e.g. :class:BoundaryDecay) work correctly in the preview.

Parameters:

  • modulator (VelocityModulator, default: None ) –

    The modulator to preview.

  • gdf (GeoDataFrame, default: None ) –

    Geometries used to derive the spatial extent and the geometry mask passed to the modulator.

  • grid_size (int, default: 64 ) –

    Number of grid cells along the longer axis. Kept small for a fast, clutter-free preview.

  • skip (int, default: 4 ) –

    Plot every skip-th arrow to reduce clutter.

  • input_angle (float, default: 0.0 ) –

    Direction of the uniform probe field in radians (0 = +x = east). Change this to see how the modulator responds to a different incoming flow direction.

  • margin (float, default: 0.05 ) –

    Fractional margin added around the bounds before building the grid, matching the convention used by the real algorithm.

  • show_geometry (bool, default: True ) –

    When True, draw the geometry outlines as a light background layer.

  • show_colorbar (bool, default: True ) –

    When False, the colorbar for the arrows is omitted. Useful when embedding the preview in a figure that provides its own colorbar.

  • ax (Axes, default: None ) –

    Axes to draw on. A new figure is created if None.

  • values (array - like, default: None ) –

    Per-geometry values (same length as gdf) used to compute a realistic density field and velocity field via the FFT Poisson solver — identical to what :func:morph_geometries computes internally. When provided, the probe field is the normalised velocity field derived from those values rather than a uniform field. Useful for modulators that depend on the spatial structure of the field.

  • column (str, default: None ) –

    Column name in gdf to use as values. Equivalent to passing values=gdf[column].to_numpy(). Takes precedence over values if both are given.

  • Dx (float, default: 1.0 ) –

    Anisotropic diffusion factor in x passed to :class:VelocityComputerFFTW when computing the density-based velocity field.

  • Dy (float, default: 1.0 ) –

    Anisotropic diffusion factor in y passed to :class:VelocityComputerFFTW.

  • show_vectors (bool or str or tuple, default: 'output' ) –

    Controls which velocity quiver layers are drawn:

    • 'output' — only the modulated output field (default).
    • True — input + output (comparison view).
    • False or None — no quivers (useful when only a heatmap is wanted).
    • 'input' or 'diff' — only that single layer.
    • tuple, e.g. ('output', 'diff') — any combination.
  • input_arrows_kwargs (dict, default: None ) –

    Extra keyword arguments for the input quiver ax.quiver call.

  • diff_cmap (str or Colormap, default: 'Reds' ) –

    Colormap for the difference quiver.

  • diff_arrows_kwargs (dict, default: None ) –

    Extra keyword arguments for the difference quiver ax.quiver call.

  • arrow_scale (float, default: 1.0 ) –

    Length of the longest output arrow as a fraction of the skipped-cell diagonal (sqrt((dx*skip)² + (dy*skip)²)). All quivers share this scale so arrows are directly comparable. Values < 1 add a gap between adjacent arrows; values > 1 allow overlap.

  • cmap (str or Colormap, default: 'viridis' ) –

    Colormap for the quiver arrows, encoding amplification factor.

  • arrows_kwargs (dict, default: None ) –

    Extra keyword arguments merged into the ax.quiver call for the modulated-velocity arrows. Override any of the defaults (cmap, pivot, zorder, …).

  • colorbar_kwargs (dict, default: None ) –

    Extra keyword arguments merged into the plt.colorbar call (e.g. label, shrink, pad).

  • heatmap ((weight, input, output, diff), default: 'weight' ) –

    Optional background heatmap overlay:

    • 'weight' — Gaussian seed-weight field for :class:LocalizedTensor (replaces the old show_weight parameter).
    • 'input', 'output', 'diff' — scalar reduction of the corresponding velocity field controlled by heatmap_type.
  • heatmap_type ((magnitude, angle, magnitude_diff, angle_diff), default: 'magnitude' ) –

    How to reduce the 2-D vector field to a scalar for the heatmap:

    • 'magnitude'‖field‖; valid for all heatmap modes.
    • 'angle' — direction of the field vector in [−π, π]; valid for all heatmap modes.
    • 'magnitude_diff'‖v_out‖ − ‖v_in‖; signed speed change. Positive where the modulator amplified speed, negative where it suppressed it. Uses a divergent colormap. Only valid with heatmap='diff'.
    • 'angle_diff'angle(v_out) − angle(v_in) wrapped to [−π, π]; how much the modulator rotated the flow direction. Uses a cyclic colormap ('twilight_shifted') so that −π and +π (both a 180° flip) have the same colour. Only valid with heatmap='diff'.
  • heatmap_alpha_from_magnitude (bool, default: False ) –

    When True and heatmap_type='angle', the imshow alpha channel is set to the normalised velocity magnitude so regions with near-zero velocity become transparent. Has no effect for heatmap='weight'.

Returns:

  • ModulatorPreviewResult

    Named container with all produced artists:

    • ax — the axes
    • arrows — the :class:~matplotlib.quiver.Quiver artist
    • colorbar — :class:~matplotlib.colorbar.Colorbar for arrows
    • geometry_collections — list of collections from gdf.plot(); empty when show_geometry is False
    • weight_image — :class:~matplotlib.image.AxesImage for the seed-weight heatmap, or None
    • input_arrows — :class:~matplotlib.quiver.Quiver for the pre-modulation arrows, or None
    • diff_arrows — :class:~matplotlib.quiver.Quiver for the difference field, or None

Examples:

>>> from carto_flow.flow_cartogram import preview_modulator, DirectionalTensor
>>> mod = DirectionalTensor.radial(center=(500_000, 200_000), Dpar=3.0)
>>> result = preview_modulator(mod, gdf)
>>> result.arrows.set_alpha(0.8)
>>> result.colorbar.set_label("strength")
>>> # Density-based velocity:
>>> result = preview_modulator(mod, gdf, column="population", show_vectors=True)