Skip to content

carto_flow.flow_cartogram.density

Density field computation utilities for cartographic applications.

This module provides functions for computing density fields from geospatial data, which are essential for flow-based cartography algorithms. The main functionality includes rasterizing polygon geometries onto regular grids to create density distributions that drive cartogram deformation processes.

Functions:

Examples:

>>> from carto_flow.flow_cartogram.density import compute_density_field
>>> from carto_flow.flow_cartogram.grid import Grid
>>> grid = Grid.from_bounds((0, 0, 100, 80), size=100)
>>> density = compute_density_field(gdf, "population", grid)

Classes:

  • DensityBorderExtension

    Extend interior density values outward, blending toward the target density.

  • DensityModulator

    Abstract base class for density field modulators.

  • DensityPipeline

    Sequence of density modulators applied left-to-right.

  • Smooth

    Gaussian smoothing of the density field, preserving the global mean.

DensityBorderExtension

DensityBorderExtension(
    extension_width: float = 10.0,
    transition_width: float = 10.0,
    smooth: float | None = None,
)

Bases: DensityModulator

Extend interior density values outward, blending toward the target density.

By default, outside cells are assigned target_density (the mean equalized density). This can cause a sharp density step at the outer boundary, which may distort boundary geometries during morphing.

DensityBorderExtension replaces outside-cell densities with a smooth outward extrapolation of the nearest interior values. Over extension_width units outside the boundary the interior density is copied as-is; beyond that it transitions toward target_density over transition_width units via linear blending.

Parameters:

  • extension_width (float, default: 10.0 ) –

    Distance in world/data-coordinate units over which interior densities are propagated outward without blending.

  • transition_width (float, default: 10.0 ) –

    Distance in world/data-coordinate units over which the density blends from the extrapolated interior value to target_density. Set to 0 for a hard cutoff at extension_width.

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

    Optional Gaussian smoothing sigma in world-coordinate units applied to the final density field to remove small artefacts.

Examples:

>>> DensityBorderExtension(extension_width=5, transition_width=20)
>>> DensityBorderExtension(extension_width=10) + DensitySmooth(sigma=2)

Methods:

  • __call__

    Extend density values from the interior outward and blend to target.

__call__

__call__(
    density: ndarray,
    grid: Grid,
    mask: ndarray,
    target_density: float,
) -> np.ndarray

Extend density values from the interior outward and blend to target.

DensityModulator

Abstract base class for density field modulators.

Subclasses implement __call__(density, grid, mask, target_density) and return a modified density array. Modulators can be chained with + into a :class:DensityPipeline that applies them left-to-right::

mod = DensityBorderExtension(extension_width=10) + DensitySmooth(sigma=2)

Methods:

  • __add__

    Chain modulators in sequence (self followed by other).

  • __call__

    Apply modulation to density field.

__add__

__add__(other: DensityModulator) -> DensityPipeline

Chain modulators in sequence (self followed by other).

__call__

__call__(
    density: ndarray,
    grid: Grid,
    mask: ndarray,
    target_density: float,
) -> np.ndarray

Apply modulation to density field.

DensityPipeline

DensityPipeline(modulators: list)

Bases: DensityModulator

Sequence of density modulators applied left-to-right.

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

pipe = DensityBorderExtension() + DensitySmooth(sigma=2)

Methods:

  • __add__

    Chain another modulator after this pipeline.

  • __call__

    Apply all modulators in sequence.

__add__

__add__(other: DensityModulator) -> DensityPipeline

Chain another modulator after this pipeline.

__call__

__call__(
    density: ndarray,
    grid: Grid,
    mask: ndarray,
    target_density: float,
) -> np.ndarray

Apply all modulators in sequence.

Smooth

Smooth(sigma: float = 1.0)

Bases: DensityModulator

Gaussian smoothing of the density field, preserving the global mean.

Convolves the density field with an isotropic Gaussian kernel, then rescales the result so the spatial mean is unchanged. This softens sharp density boundaries between geometries without shifting the overall target density. Exported from the package as DensitySmooth to avoid name collision with the velocity :class:~anisotropy.Smooth.

Parameters:

  • sigma (float, default: 1.0 ) –

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

Examples:

>>> DensitySmooth(sigma=3)

compute_density_field

compute_density_field(
    gdf: Any,
    column: str,
    grid: Grid,
    mean_density: float | None = None,
    smooth: float | None = None,
) -> np.ndarray

Rasterize polygon geometries to a density grid.

This function computes a density field by rasterizing polygon geometries onto a regular grid. Each polygon's density contribution is calculated as its attribute value divided by its area.

Parameters:

  • gdf (Any) –

    GeoDataFrame-like object containing polygon geometries. Must have: - 'geometry' column with polygon geometries - Column specified by 'column' parameter with values to use for densities

  • column (str) –

    Name of the column in gdf containing the variable of interest (e.g., 'population').

  • grid (Grid) –

    Grid information object containing X, Y meshgrid arrays and grid properties.

  • mean_density (float, default: None ) –

    Background density value for areas outside polygons. If None, computed as total column sum divided by total geometry area sum.

  • smooth (float, default: None ) –

    Standard deviation for Gaussian smoothing. If provided, applies gaussian_filter with this sigma value and preserves global mean.

Returns:

  • rho ( ndarray ) –

    2D density array with same shape as grid.X and grid.Y. Contains density values (attribute per unit area) at each grid cell center.

Notes

The function: - Assigns density = polygon_value / polygon_area to cells inside each polygon - Fills exterior cells with mean_density (computed or provided) - Optionally applies Gaussian smoothing while preserving the global mean

Examples:

>>> import geopandas as gpd
>>> from carto_flow.density import compute_density_field
>>> from carto_flow.grid import Grid
>>>
>>> # Create sample data
>>> gdf = gpd.GeoDataFrame({
...     'geometry': [polygon1, polygon2],
...     'population': [1000, 2000]
... })
>>>
>>> # Set up grid
>>> bounds = (0, 0, 10, 10)
>>> grid = Grid.from_bounds(bounds, size=50)
>>>
>>> # Compute density field
>>> density = compute_density_field(gdf, 'population', grid)
>>> print(f"Density grid shape: {density.shape}")

compute_density_field_from_geometries

compute_density_field_from_geometries(
    geometries,
    column_values,
    grid,
    mean_density=None,
    smooth=None,
    return_geometry_mask=False,
    return_outside_mask=False,
)

Compute density field directly from geometries (no dataframe dependency).

Parameters:

  • geometries (list of shapely geometries) –

    The geometries to compute density for.

  • column_values (array - like) –

    Values associated with each geometry.

  • grid (Grid) –

    Grid object with X, Y coordinate arrays.

  • mean_density (float, default: None ) –

    Background density for cells outside geometries.

  • smooth (float, default: None ) –

    Gaussian smoothing sigma applied to the entire density field (both inside and outside). Preserves the global mean.

  • return_geometry_mask (bool, default: False ) –

    If True, return tuple (rho, geometry_mask) where geometry_mask is an integer array indicating which geometry each cell belongs to: - -1: Outside all geometries - k: Inside geometry k (0 <= k < number of geometries)

  • return_outside_mask (bool, default: False ) –

    (Deprecated) If True, return tuple (rho, outside_mask) where outside_mask is a boolean array indicating cells outside all geometries.

Returns:

  • rho ( ndarray ) –

    Density field array.

  • geometry_mask ( np.ndarray (only if return_geometry_mask=True) ) –

    Integer array where each element indicates which geometry the cell belongs to.

  • outside_mask ( np.ndarray (only if return_outside_mask=True) ) –

    Boolean array where True indicates cells outside all geometries.

preview_modulator

preview_modulator(
    modulator: DensityModulator,
    gdf,
    grid_size: int = 64,
    column: str | None = None,
    values=None,
    margin: float = 0.1,
    show_geometry: bool = True,
    show: str = "output",
    ax=None,
    cmap: str = "RdBu_r",
    show_colorbar: bool = True,
    colorbar_kwargs: dict | None = None,
    image_kwargs: dict | None = None,
    area_scale: float = 1.0,
)

Preview a density modulator applied to a computed density field.

Rasterizes the input density from gdf (or uniform values if column and values are both omitted), applies the modulator, and displays the result as a heatmap.

Parameters:

  • modulator (DensityModulator) –

    The modulator to preview.

  • gdf (GeoDataFrame) –

    Geometries used to derive the spatial extent, the geometry mask, and (when column is given) the density values.

  • grid_size (int, default: 64 ) –

    Number of grid cells along the longer axis.

  • column (str, default: None ) –

    Column in gdf to use as per-geometry values. Takes precedence over values.

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

    Per-geometry values (same length as gdf). Ignored when column is given. If both are None, uniform values (all 1.0) are used so the input density equals 1 / geometry_area everywhere.

  • margin (float, default: 0.1 ) –

    Fractional margin added around the bounds before building the grid.

  • show_geometry (bool, default: True ) –

    Draw geometry outlines as a light background layer.

  • show (str, default: 'output' ) –

    Which field to display. One of:

    • 'output' — post-modulation density field (default).
    • 'input' — pre-modulation density field.
    • 'ratio' — log₁₀ of the element-wise ratio rho_out / rho_in, displayed with a symmetric linear norm (equal multiplicative changes occupy equal visual space); tick labels show actual ratio values.
  • ax (Axes, default: None ) –

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

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

    Colormap used for all show modes. A diverging colormap is recommended: for 'input' and 'output' the centre is anchored at target_density (white = equilibrium, blue = below, red = above); for 'ratio' the centre is anchored at 1 (no change).

  • show_colorbar (bool, default: True ) –

    When False, the colorbar is omitted.

  • colorbar_kwargs (dict, default: None ) –

    Extra keyword arguments for plt.colorbar.

  • image_kwargs (dict, default: None ) –

    Extra keyword arguments merged into the ax.imshow call.

  • area_scale (float, default: 1.0 ) –

    Multiplier applied to areas when computing density, matching MorphOptions.area_scale. For example, 1e-6 converts m² grid coordinates to km² density units.

Returns:

  • DensityModulatorPreviewResult

    Named container with the produced artists:

    • ax — the axes
    • image — the :class:~matplotlib.image.AxesImage heatmap
    • colorbar — :class:~matplotlib.colorbar.Colorbar, or None
    • geometry_collections — list of collections from gdf.plot()

Examples:

>>> from carto_flow.flow_cartogram import preview_density_modulator, DensityBorderExtension
>>> mod = DensityBorderExtension(extension_width=50_000, transition_width=30_000)
>>> result = preview_density_modulator(mod, gdf, column="population")
>>> # Inspect where the modulator changed the density (ratio centred at 1):
>>> result = preview_density_modulator(mod, gdf, column="population", show="ratio")