carto_flow.symbol_cartogram.layouts¶
Layout subpackage: all layout classes and registry.
Modules:
-
base–Layout ABC, registry, and shared helpers.
-
centroid–Centroid-based layout: CentroidLayout and CentroidLayoutOptions.
-
data_prep–Data preparation for symbol cartogram layouts.
-
flow–Flow-density layout: FlowDensityLayout and FlowDensityLayoutOptions.
-
grid–Grid-based layout: GridBasedLayout and GridBasedLayoutOptions.
-
layout_result–Layout result classes for symbol cartograms.
-
mosaic–Mosaic (tilegram) layout: MosaicLayout and MosaicLayoutOptions.
-
packing–Two-stage circle packing layout: CirclePackingLayout and CirclePackingLayoutOptions.
Classes:
-
AlgorithmMetrics–Final scalar summaries common to all layouts.
-
CentroidLayout–Layout that places symbols at geometry centroids.
-
CentroidLayoutOptions–Options for centroid-based placement.
-
CentroidMetrics–Algorithm-specific metrics for CentroidLayout.
-
CirclePackingAdvancedOptions–Low-level algorithm tuning for CirclePackingLayout.
-
CirclePackingLayout–Layout using two-stage circle packing simulation.
-
CirclePackingLayoutOptions–Options for CirclePackingLayout (two-stage with contact reaction).
-
FlowDensityHistory–Simulation diagnostics for FlowDensityLayout.
-
FlowDensityLayout–Layout using Gaussian density-field flow advection.
-
FlowDensityLayoutOptions–Options for FlowDensityLayout (Gaussian density-field advection).
-
FlowDensityMetrics–Final scalar metrics for FlowDensityLayout.
-
GridBasedLayout–Layout from grid-based assignment.
-
GridBasedLayoutOptions–Options for grid-based placement.
-
GridMetrics–Algorithm-specific metrics for GridBasedLayout.
-
HungarianOptions–Cost-function parameters for mosaic Hungarian assignment.
-
Layout–Abstract base for layout algorithms.
-
LayoutData–Preprocessed data for layout algorithms.
-
LayoutResult–Immutable output from layout algorithm.
-
MosaicLayout–Layout using mosaic (tilegram) assignment.
-
MosaicLayoutOptions–Options for mosaic (tilegram) layout.
-
MosaicMetrics–Algorithm-specific metrics for MosaicLayout.
-
PackingHistory–Per-iteration diagnostics for CirclePackingLayout.
-
PackingMetrics–Final scalar metrics for CirclePackingLayout.
-
SimulationHistory–Per-iteration arrays common to all iterative layouts.
-
Transform–Transformation applied to a symbol.
Functions:
-
check_group_by_support–Raise if
group_bywas requested for a layout that ignores it. -
compute_symbol_sizes–Compute symbol sizes from data values.
-
get_layout–Instantiate a registered layout by name.
-
group_by_layouts–Return the registered layout names whose placement honors
group_by. -
prepare_layout_data–Prepare data for layout algorithms.
-
register_layout–Register a layout class by name.
AlgorithmMetrics
dataclass
¶
AlgorithmMetrics(
converged: bool | None = None,
iterations: int | None = None,
final_overlaps: int | None = None,
algorithm: Any = None,
)
Final scalar summaries common to all layouts.
Attributes:
-
converged(bool or None) –Whether the algorithm met its convergence criterion.
-
iterations(int or None) –Number of iterations executed.
-
final_overlaps(int or None) –Number of overlapping nearest-neighbor pairs at termination.
-
algorithm(Any) –Algorithm-specific final scalars (PackingMetrics, FlowDensityMetrics, CentroidMetrics, GridMetrics, etc.).
CentroidLayout
¶
Bases: Layout
Layout that places symbols at geometry centroids.
Places symbols at the centroids of the original geometries, with optional local overlap removal.
Returns LayoutResult with CircleSymbol as canonical symbol.
Parameters:
-
options(CentroidLayoutOptions, default:None) –Full options object. Defaults to CentroidLayoutOptions().
-
**kwargs–Individual option overrides applied on top of options.
Examples:
>>> layout = CentroidLayout() # Default: remove overlap
>>> layout = CentroidLayout(remove_overlap=False) # Just centroids
>>> layout = CentroidLayout(spacing=0.1) # Larger gaps
CentroidLayoutOptions
dataclass
¶
CentroidLayoutOptions(
spacing: float = 0.05,
remove_overlap: bool = True,
max_iterations: int = 20,
overlap_tolerance: float = 0.0001,
origin_attraction: float = 0.2,
group_attraction: float = 0.15,
attraction_cycles: int = 100,
)
Options for centroid-based placement.
Places symbols at geometry centroids with optional overlap removal.
Parameters:
-
spacing(float, default:0.05) –Gap between symbols as fraction of average symbol size. Default: 0.05
-
remove_overlap(bool, default:True) –Whether to run overlap removal after placing at centroids. Default: True
-
max_iterations(int, default:20) –Maximum iterations per overlap resolution pass. Default: 20
-
overlap_tolerance(float, default:0.0001) –Tolerance for overlap resolution convergence. Default: 1e-4
-
origin_attraction(float, default:0.2) –After overlap resolution, pull each symbol this fraction toward its pre-resolution position, then re-resolve overlaps. Repeated
attraction_cyclestimes. Keeps symbols from drifting far from their starting positions. 0 = off. Default: 0.2 -
group_attraction(float, default:0.15) –After overlap resolution, pull each symbol this fraction toward its group centroid, then re-resolve overlaps. Repeated
attraction_cyclestimes. Active whengroup_byortile_countis set. 0 = off. Default: 0.15 -
attraction_cycles(int, default:100) –Number of attract → re-resolve iterations to run after the primary overlap resolution. Each cycle applies one attraction step then one separation step, so attraction and separation have equal weight. More cycles pull symbols closer to their origins or group centroids. Default: 100
Methods:
-
validate–Validate options.
CentroidMetrics
dataclass
¶
Algorithm-specific metrics for CentroidLayout.
CirclePackingAdvancedOptions
dataclass
¶
CirclePackingAdvancedOptions(
local_step_fraction: float = 0.5,
expansion_max_iterations: int = 500,
topology_gate_distance: float = 2.5,
contact_tolerance: float = 0.02,
contact_iterations: int = 3,
contact_elasticity: float = 0.0,
size_sensitivity: float = 0.0,
overlap_projection_iters: int = 5,
step_smoothing_window: int = 20,
convergence_window: int = 50,
adaptive_ema: bool = True,
)
Low-level algorithm tuning for CirclePackingLayout.
These parameters rarely need changing. The defaults are calibrated for typical cartogram inputs. Override only when you have a specific reason.
Parameters:
-
local_step_fraction(float, default:0.5) –Fraction of overlap to correct per Gauss-Seidel step in Stage 1 (0-1]. Lower values (e.g. 0.2) reduce intermingling in dense configurations at the cost of more passes. Default: 0.5
-
expansion_max_iterations(int, default:500) –Maximum Gauss-Seidel passes for Stage 1 overlap resolution. Exits early when max overlap falls below
overlap_tolerance. Default: 500 -
topology_gate_distance(float, default:2.5) –Topology forces only act when circles are within
topology_gate_distance * (r_i + r_j)of each other. Default: 2.5 -
contact_tolerance(float, default:0.02) –Contact detection threshold as fraction of radii sum. Default: 0.02
-
contact_iterations(int, default:3) –Contact reaction passes per Stage 2 packing step. Default: 3
-
contact_elasticity(float, default:0.0) –Net compression vs bounce behavior at contact points (-1 to 1). 0 = neutral; < 0 = compression; > 0 = bounce. Only has effect when
contact_transfer_ratio(core option) is between 0 and 1. Default: 0.0 -
size_sensitivity(float, default:0.0) –How step size scales with circle radius in Stage 2 (-1 to 1). 0 = all circles use avg_radius; 1 = proportional to radius; -1 = inverse. Default: 0.0
-
overlap_projection_iters(int, default:5) –Jacobi overlap projection passes per Stage 2 packing step. Default: 5
-
step_smoothing_window(int, default:20) –EMA effective window size for step smoothing. Default: 20
-
convergence_window(int, default:50) –EMA effective window size for displacement convergence tracking. Default: 50
-
adaptive_ema(bool, default:True) –Whether EMA uses adaptive warmup (alpha starts at 1/k). Default: True
Methods:
-
validate–Validate advanced options.
CirclePackingLayout
¶
Bases: Layout
Layout using two-stage circle packing simulation.
Two-stage approach: 1. Overlap Resolution: Global expansion + Gauss-Seidel separation 2. Circle Packing: Force-based refinement with contact reaction
Parameters:
-
options(CirclePackingLayoutOptions, default:None) –Full options object. Defaults to CirclePackingLayoutOptions().
-
**kwargs–Individual option overrides (core options only; use
advanced=CirclePackingAdvancedOptions(...)for advanced tuning).
Methods:
-
centroid–Stage 1 only: place at geographic centroids then separate overlaps.
-
dorling–Compact circles pulled toward the global centroid — classic Dorling style.
-
dorling_grouped–Grouped Dorling — circles pack toward group centroids.
-
geographic–Geography-preserving — symbols stay close to original positions.
-
geographic_grouped–Grouped geography-preserving — symbols stay near geographic positions.
centroid
classmethod
¶
Stage 1 only: place at geographic centroids then separate overlaps.
No Stage 2 compaction (max_iterations=0). expansion=0 (default)
uses Gauss-Seidel only (more topology-preserving); expansion=1 adds
a single global expansion step (faster for large overlaps).
dorling
classmethod
¶
dorling(
*,
spacing: float = 0.05,
compactness: float = 0.8,
topology_weight: float = 0.0,
neighbor_weight: float = 0.0,
) -> CirclePackingLayout
Compact circles pulled toward the global centroid — classic Dorling style.
No origin attraction. Enable topology_weight / neighbor_weight
to add spatial structure while keeping the compact character.
dorling_grouped
classmethod
¶
dorling_grouped(
*,
spacing: float = 0.05,
compactness: float = 0.1,
group_weight: float = 0.5,
topology_weight: float = 0.5,
neighbor_weight: float = 0.5,
) -> CirclePackingLayout
Grouped Dorling — circles pack toward group centroids.
Intended with collapse_group=1.0 so symbols start coincident within
each group. group_weight provides the main cohesion force;
compactness adds mild global pull. group_weight is inert when no
groups are defined.
geographic
classmethod
¶
geographic(
*,
spacing: float = 0.05,
origin_weight: float = 0.5,
topology_weight: float = 0.5,
neighbor_weight: float = 0.5,
) -> CirclePackingLayout
Geography-preserving — symbols stay close to original positions.
No global compaction. Reduce topology_weight / neighbor_weight
for a looser layout that still tracks geography.
geographic_grouped
classmethod
¶
geographic_grouped(
*,
spacing: float = 0.05,
origin_weight: float = 0.5,
group_weight: float = 0.3,
topology_weight: float = 0.5,
neighbor_weight: float = 0.5,
) -> CirclePackingLayout
Grouped geography-preserving — symbols stay near geographic positions.
Origin tracks the group starting centroid (geographic or collapsed)
when used with collapse_group. group_weight adds soft intra-group
cohesion on top of origin attraction.
CirclePackingLayoutOptions
dataclass
¶
CirclePackingLayoutOptions(
max_iterations: int = 500,
expansion: float = 1.0,
convergence_tolerance: float = 0.025,
spacing: float = 0.05,
compactness: float = 0.1,
topology_weight: float = 1.0,
neighbor_weight: float = 1.0,
origin_weight: float = 0.1,
group_weight: float = 0.0,
force_mode: ForceMode = ForceMode.DIRECTION,
max_step: float = 0.3,
contact_transfer_ratio: float = 0.5,
overlap_tolerance: float = 0.0001,
advanced: CirclePackingAdvancedOptions = CirclePackingAdvancedOptions(),
)
Options for CirclePackingLayout (two-stage with contact reaction).
Two-stage approach:
- Stage 1 (Feasibility): Single global expansion + Gauss-Seidel overlap resolution to reach a non-overlapping configuration.
- Stage 2 (Refinement): Force-based packing with contact reaction, topology and neighbor forces, and optional origin/group attraction.
Low-level algorithm tuning is available via the advanced field.
Parameters:
-
max_iterations(int, default:500) –Maximum Stage 2 packing iterations. 0 = Stage 1 only (no compaction). Default: 500
-
expansion(float, default:1.0) –Stage 1 expansion fraction (0-1). 0 = GS-only (most topology- preserving); 1 = full exact expansion (fastest, default); intermediate = partial expansion with GS handling residuals. Default: 1.0
-
convergence_tolerance(float, default:0.025) –Convergence threshold for mean relative displacement. Default: 0.025
-
spacing(float, default:0.05) –Minimum gap between symbols as fraction of average radius. Default: 0.05
-
compactness(float, default:0.1) –Global centroid attraction strength (0-1). Pulls all circles toward the weighted centroid of the layout. Default: 0.1
-
topology_weight(float, default:1.0) –Topology preservation strength (0-1). Pulls each adjacent pair toward their original relative angle. Default: 1.0
-
overlap_tolerance(float, default:0.0001) –Stage 1 convergence: max overlap as fraction of average radius. Default: 1e-4
-
neighbor_weight(float, default:1.0) –Neighbor tangency force strength. Pulls separated adjacent circles together. Default: 1.0
-
origin_weight(float, default:0.1) –Origin attraction strength. Pulls each circle toward its starting position (geographic centroid, or group centroid when
collapse_groupis used). Default: 0.1 -
group_weight(float, default:0.0) –Group centroid attraction strength during Stage 2. Active when
group_byortile_countis set. Usecollapse_groupincreate_layoutto prevent intermingling before Stage 2. Default: 0.0 -
force_mode(ForceMode, default:DIRECTION) –How attraction force magnitude scales with distance. Applies to compactness, origin, and group forces. Default: ForceMode.DIRECTION
- DIRECTION: constant magnitude with drop-off near target
- LINEAR: proportional to distance (spring-like)
- NORMALIZED: proportional to distance / radius
-
max_step(float, default:0.3) –Maximum step size per iteration as fraction of average radius. Default: 0.3
-
contact_transfer_ratio(float, default:0.5) –Balance between dissipating (0) and transferring (1) compressive forces at contact points. Default: 0.5
-
advanced(CirclePackingAdvancedOptions, default:CirclePackingAdvancedOptions()) –Low-level algorithm tuning. See :class:
CirclePackingAdvancedOptions.
Methods:
-
validate–Validate options.
FlowDensityHistory
dataclass
¶
FlowDensityHistory(
errors: NDArray[floating],
max_errors: NDArray[floating],
density_snapshots: FlowDensityFieldSnapshots
| None = None,
)
Simulation diagnostics for FlowDensityLayout.
Attributes:
-
errors, max_errors(NDArray) –Per-iteration mean and max relative NN spacing errors.
-
density_snapshots(FlowDensityFieldSnapshots | None) –Density and velocity field snapshots, one per recompute interval. Only populated when
save_density_fields=True.
FlowDensityLayout
¶
Bases: Layout
Layout using Gaussian density-field flow advection.
Positions circles by building a divergence field from per-pair Gaussian blobs placed at predicted contact points and advecting centroids through the resulting velocity field. Unlike the other force-based layouts, the field covers the full domain so there is no background sink pulling circles into empty space.
Returns LayoutResult with CircleSymbol as canonical symbol.
Parameters:
-
options(FlowDensityLayoutOptions, default:None) –Full options object. Defaults to FlowDensityLayoutOptions().
-
**kwargs–Individual option overrides applied on top of options.
Examples:
FlowDensityLayoutOptions
dataclass
¶
FlowDensityLayoutOptions(
spacing: float = 0.05,
sigma_perp_factor: float = 1.0,
smooth: float = 0.0,
damp: bool = True,
use_gabriel: bool = True,
max_iterations: int = 500,
recompute_every: int = 5,
dt_factor: float = 0.3,
convergence_tolerance: float = 0.05,
grid_size: int = 256,
save_density_fields: bool = False,
force_balance: float | str = 1.0,
cross_group_pull_scale: float = 1.0,
)
Options for FlowDensityLayout (Gaussian density-field advection).
Positions circles by constructing a divergence field from per-pair Gaussian blobs placed at predicted contact points, then advecting centroids through the resulting velocity field.
Parameters:
-
spacing(float, default:0.05) –Minimum gap between circle boundaries as a fraction of the mean circle radius. Default: 0.05
-
sigma_perp_factor(float, default:1.0) –Controls the perpendicular width of each contact-point Gaussian: sigma_perp = factor * max(claim_i, claim_j), where claim_i = d * (r_i + spacing/2) / (r_i + r_j + spacing) is the distance from centroid i to its claim point. At 1.0 the blob is nearly isotropic; values < 1 (typically 0.25-0.5) focus pressure along the pair axis and reduce cross-talk between nearby pairs. Default: 1.0
-
smooth(float, default:0.0) –Gaussian filter sigma for the density field in real-world coordinate units (same units as centroid positions). Converted internally to grid cells via grid.dx / grid.dy after the grid is constructed. 0 = no smoothing. Default: 0.0
-
damp(bool, default:True) –Apply exponential dampening to amplitude when circles are far from their target distance, preventing extreme distortion. Default: True
-
use_gabriel(bool, default:True) –Use the Gabriel graph (subset of Delaunay) for adjacency pairs. False = full Delaunay triangulation. Default: True
-
max_iterations(int, default:500) –Maximum number of advection steps. Default: 500
-
recompute_every(int, default:5) –Rebuild density and velocity fields every N steps. Default: 5
-
dt_factor(float, default:0.3) –Timestep = factor * min(dx, dy) / max_velocity. Default: 0.3
-
convergence_tolerance(float, default:0.05) –Stop when mean relative NN spacing error < tolerance. Default: 0.05 This is a mean over all nearest-neighbor pairs, not a per-pair bound, so individual pairs can still have spacing error above the tolerance (including residual overlap) when the layout reports convergence.
-
grid_size(int, default:256) –Grid resolution (square). Larger values improve accuracy at the cost of runtime. Default: 256
-
save_density_fields(bool, default:False) –Record the density field (rho) and velocity fields (vx, vy) at every density recompute step. Results are accessible via
layout_result.history.algorithm.density_snapshots(aFlowDensityFieldSnapshotsinstance). Default: False -
force_balance(float or {'count', 'rms', 'repulse'}, default:1.0) –Relative weighting of push (overlapping) vs pull (too-far) pair contributions to the density field. Only the ratio push/pull affects the velocity field shape; the adaptive timestep absorbs any global scale.
float > 0: push_scale = force_balance, pull_scale = 1.0. Values1 amplify push relative to pull, helping resolve remaining overlaps.
"count": push_scale = 1/N_push, pull_scale = 1/N_pull — each pair contributes equally regardless of push/pull count imbalance."rms": scales so RMS(push field) = RMS(pull field) — equalises field energy per type."repulse": pull_scale = 0 — only overlapping pairs contribute, so circles are separated without being packed together. Default: 1.0 -
cross_group_pull_scale(float, default:1.0) –Pull-force multiplier for cross-group pairs (pairs where the two circles belong to different groups, as determined by
group_byortile_count). 0.0 = repulse-only across groups (circles from different groups separate but do not attract); 1.0 = same as within-group (no distinction). Only has effect when the layout is run withgroup_byortile_count. Default: 1.0Setting this to 0.0 removes cross-group pull, but does not isolate groups from one another: the density field that drives advection is built from all pairs together and covers the full domain, so groups still interact through it (via push forces and the shared field geometry) even with no cross-group pull.
Methods:
-
validate–Validate options.
FlowDensityMetrics
dataclass
¶
FlowDensityMetrics(
final_error: float,
final_max_error: float,
final_signed_errors: NDArray[floating],
)
Final scalar metrics for FlowDensityLayout.
GridBasedLayout
¶
Bases: Layout
Layout from grid-based assignment.
Returns LayoutResult with appropriate Symbol based on tiling type.
Parameters:
-
options(GridBasedLayoutOptions, default:None) –Full options object (positional only). Defaults to GridBasedLayoutOptions().
-
**kwargs–Individual option overrides applied on top of options. Raises TypeError for unrecognized names.
Examples:
>>> layout = GridBasedLayout(tiling="hexagon")
>>> layout = GridBasedLayout(my_opts, neighbor_weight=0.5)
Notes
The tile lattice is calibrated from the largest symbol, so
size_normalization rescales the whole grid — lattice and symbols
together — without changing how symbols are arranged. The layout
therefore defaults to "max", which keeps the grid at the scale of
the input geometries.
GridBasedLayoutOptions
dataclass
¶
GridBasedLayoutOptions(
tiling: object = "hexagon",
grid_size: int
| tuple[int, int]
| Literal["auto"] = "auto",
origin_weight: float = 0.5,
neighbor_weight: float = 0.5,
topology_weight: float = 0.5,
compactness: float = 0.1,
spacing: float = 0.05,
fill_holes: bool = True,
min_hole_fraction: float = 0.5,
fix_islands: bool = True,
verbose: bool = False,
symbol_shape: SymbolShape | None = None,
symbol_orientation: SymbolOrientation = SymbolOrientation.UPRIGHT,
rotation: float = 0.0,
)
Options for grid-based placement.
The assignment cost function combines four terms, harmonized with the
TopologyPreservingSimulator force terminology:
- origin_weight: Preference for assigning regions near their original centroid positions (analogous to origin attraction force).
- neighbor_weight: Keep adjacent regions close on the grid (analogous to neighbor tangency force).
- topology_weight: Preserve the relative direction between neighbors (analogous to angular topology force).
- compactness: Prefer central/compact placement on the grid (analogous to global centroid attraction).
Parameters:
-
tiling(Tiling or str, default:'hexagon') –Tiling to use. Can be a
Tilinginstance for full control, or a string shorthand:"square","hexagon","triangle","quadrilateral". Default is"hexagon". -
grid_size(int, tuple, or "auto", default:'auto') –Number of grid cells. "auto" creates ~2x regions cells.
-
origin_weight(float, default:0.5) –Weight for assigning regions near their original centroids. Default: 1.0.
-
neighbor_weight(float, default:0.5) –Weight for keeping adjacent regions close on the grid. Default: 0.3.
-
topology_weight(float, default:0.5) –Weight for preserving relative neighbor orientations. Default: 0.0.
-
compactness(float, default:0.1) –Weight for compact/central placement. Default: 0.0.
-
spacing(float, default:0.05) –Gap between symbols as fraction of cell size (0-1). Used when computing the maximum symbol size that fits within grid cells.
-
fill_holes(bool, default:True) –If True, post-process the assignment to fill internal holes — unoccupied tiles surrounded by occupied tiles that don't correspond to geographic gaps in the original geometries. Islands and genuine geographic gaps (e.g. internal lakes) are preserved. Default: False.
-
min_hole_fraction(float, default:0.5) –Minimum area of a geographic interior ring, as a fraction of one tile's area, for it to count as a genuine geographic gap. Rings smaller than this are treated as boundary artifacts and ignored. Default: 0.5.
-
fix_islands(bool, default:True) –If True, post-process the assignment to reassign regions that are disconnected from the main assignment cluster to tiles adjacent to the cluster. True geographic islands (regions not adjacent to any other region) are preserved. Default: False.
-
verbose(bool, default:False) –If True, print diagnostic information during fill_holes and fix_islands processing. Default: False.
-
symbol_shape(SymbolShape or None, default:None) –Target symbol shape. When set, the grid algorithm computes grid-appropriate sizes to prevent overlap. None skips adjustment.
-
symbol_orientation(SymbolOrientation, default:UPRIGHT) –How symbols are oriented relative to their tile.
UPRIGHTkeeps symbols axis-aligned;WITH_TILErotates/flips with the tile. -
rotation(float, default:0.0) –Rotation angle for the entire tiling grid in degrees (counter-clockwise). Default: 0.0 (no rotation).
Methods:
-
validate–Validate options.
GridMetrics
dataclass
¶
Algorithm-specific metrics for GridBasedLayout.
HungarianOptions
dataclass
¶
HungarianOptions(
distance_weight: float = 1.0,
outside_penalty: float = 1.0,
interior_bonus: float = 2.0,
max_connectivity_iters: int = 5,
penalize_disconnected: bool = True,
neighbor_weight: float = 0.3,
neighbor_bfs: bool = False,
swap_repair_passes: int = 10,
ring_swapback_max_hops: int = 16,
)
Cost-function parameters for mosaic Hungarian assignment.
All cost terms are normalized to [0, 1] so the parameters are comparable across datasets.
Parameters:
-
distance_weight(float, default:1.0) –Weight on the normalized centroid-distance term. The cost function is not scale-free in distance_weight and outside_penalty: interior_bonus and the neighbor term are absolute, so scaling both by a common factor changes the result. Their magnitudes matter, not only their ratio. Moving distance_weight off 1.0 splits regions on finely divided and multi-component inputs more often than not. Default 1.0.
-
outside_penalty(float, default:1.0) –Weight on the outside-fraction penalty. Tiles whose centroid lies outside the study union incur cost
outside_penalty * outside_frac. Every value gives a different placement, and the direction of the effect reverses between inputs, so no single setting is better everywhere. Default 1.0. -
interior_bonus(float, default:2.0) –Connectivity bonus — subtracted from cost for tiles that are well-surrounded by other valid tiles (interior tiles), pushing unselected surplus toward the periphery. Raising it keeps a region's tiles in a tighter block, which helps most when a region owns several tiles or when
group_byrequires a group to stay together; at one tile per region withmorph=Falsea lower value places symbols closer to their neighbors and truer to the original directions between them. Values much above the default start to break groups apart on finely divided inputs. -
max_connectivity_iters(int, default:5) –Maximum number of iterative re-solve passes for connectivity repair (intra-region disconnections + inter-region gap bridging). The loop also stops as soon as a pass fails to improve on the incumbent, which on every input measured happens well inside the cap -- four passes at most. Default 5.
-
penalize_disconnected(bool, default:True) –Raise the cost of a tile that sits apart from the rest of its region's block, so the next pass has to place it elsewhere. The raise is a multiple of the static cost maximum and any multiple above one puts the tile out of contention outright, which is why this is a switch and not a magnitude. Default True.
-
neighbor_weight(float, default:0.3) –Weight on the neighbor cost term. After each solve, penalizes placing geometry g's tiles far from the pool centroids of g's geographic neighbors. 0 disables. Default 0.3.
-
neighbor_bfs(bool, default:False) –If True, measure neighbor proximity using BFS hop distance on the tile adjacency graph instead of Euclidean distance to the neighbor's tile pool centroid. More accurate for non-convex or tightly-packed regions (e.g. New England). Default False.
-
swap_repair_passes(int, default:10) –Maximum passes of the chain-swap contiguity repair that runs on the final assignment, after the extra-ring swap-back. It permutes which geometry owns each occupied tile along short chains, so tile counts and the occupied tile set are preserved exactly, and the result is kept only when it strictly reduces the number of split regions or groups without increasing either. 0 disables it. Default 10.
-
ring_swapback_max_hops(int, default:16) –How far the extra-ring swap-back may search for an unassigned core tile to pull a stranded ring tile into. Ownership is shifted along the path, so tile counts are preserved and the move is kept only when it increases neither the split-region nor the split-group count. 0 disables the swap-back; 1 restricts it to directly adjacent tiles. A larger reach closes more holes but can cost compactness, so the default is a deliberate balance.
Layout
¶
Bases: ABC
Abstract base for layout algorithms.
Layouts compute positions and transforms, returning immutable LayoutResult.
Subclasses implement :meth:_compute; the public :meth:compute validates
the request first.
Attributes:
-
supports_group_by(bool) –Whether the layout lets the
group_bygrouping affect placement. Class-level flag; layouts that only pass the group labels through to the result for styling leave it False, andgroup_bythen raises. -
default_size_normalization({'max', 'total'}) –Which
size_normalizationthe pipeline applies when the caller does not pass one. Class-level flag; layouts that set their own symbol scale from a tile lattice override it.
Methods:
-
compute–Validate the request, then run the layout algorithm.
compute
¶
compute(
data: LayoutData,
show_progress: bool = True,
save_history: bool = False,
) -> LayoutResult
Validate the request, then run the layout algorithm.
Parameters:
-
data(LayoutData) –Preprocessed data from prepare_layout_data().
-
show_progress(bool, default:True) –Display progress feedback during placement.
-
save_history(bool, default:False) –Record position snapshots per iteration.
Returns:
-
LayoutResult–Immutable result with canonical symbol and transforms.
Raises:
-
ValueError–If the data carries a
group_bygrouping and the layout does not support it.
Warns:
-
UserWarning–If the layout supports
group_bybut the option that acts on the grouping is still at its inert default.
LayoutData
dataclass
¶
LayoutData(
positions: NDArray[floating],
sizes: NDArray[floating],
adjacency: NDArray[floating],
bounds: tuple[float, float, float, float],
mean_area: float,
source_gdf: GeoDataFrame,
valid_mask: NDArray[bool_] | None = None,
geometry_positions: NDArray[floating] | None = None,
source_indices: NDArray[intp] | None = None,
group_ids: NDArray[intp] | None = None,
group_ids_G: NDArray[intp] | None = None,
counts_G: NDArray[int32] | None = None,
sizes_G: NDArray[floating] | None = None,
components: list[list[int]] | None = None,
component_labels: NDArray[intp] | None = None,
)
Preprocessed data for layout algorithms.
Created by prepare_layout_data(). Advanced users can inspect/modify before passing to Layout.compute().
Attributes:
-
positions(NDArray[floating]) –Initial positions (centroids), shape (N, 2). When tile_count is set, N = Σcounts (expanded); otherwise N = G.
-
sizes(NDArray[floating]) –Area-equivalent symbol sizes (circle radii), shape (N,). Symbol area = pi x size^2. Use area_factor to convert to native half-extent for rendering.
-
adjacency(NDArray[floating]) –Adjacency matrix, shape (N, N).
-
bounds(tuple[float, float, float, float]) –Geographic bounds (xmin, ymin, xmax, ymax).
-
mean_area(float) –Mean area of input geometries (unit cell area).
-
source_gdf(GeoDataFrame) –Original input geometries.
-
valid_mask(NDArray[bool_] | None) –Boolean mask indicating which rows had valid (non-null) values. None if all values were valid.
-
geometry_positions(NDArray[floating]) –Original centroid positions at geometry level, shape (G, 2). Always G-level (before tile_count expansion). When tile_count is absent, equals positions.
-
source_indices(NDArray[intp] | None) –Mapping from item k (0..N-1) to geometry row in valid source_gdf. None when tile_count is absent (implicit identity mapping).
-
group_ids(NDArray[intp] | None) –Integer group label per item at N-level, shape (N,). None when neither group_by nor tile_count is set. Already expanded via source_indices when tile_count is used, where each geometry is its own group (group k = geometry k), so this is a per-symbol source label rather than a user grouping.
-
group_ids_G(NDArray[intp] | None) –User grouping at G-level (one entry per geometry), shape (G,). Set only when
group_bywas used (tile_countandgroup_bycannot be combined). None means each geometry is its own group: withtile_count, a geometry and its tiles form one block and MosaicLayout still enforces per-geometry contiguity. Layouts that group geometries rather than symbols (e.g. mosaic) must use this, notgroup_ids.
LayoutResult
dataclass
¶
LayoutResult(
canonical_symbol: Symbol,
transforms: list[Transform],
base_size: float,
positions: NDArray[floating],
sizes: NDArray[floating],
adjacency: NDArray[floating],
bounds: tuple[float, float, float, float],
crs: str | None = None,
layout_type: str = "",
metrics: Any = None,
history: Any = None,
valid_mask: NDArray[bool_] | None = None,
source_indices: NDArray[intp] | None = None,
group_ids: NDArray[intp] | None = None,
)
Immutable output from layout algorithm.
Contains canonical symbol and per-geometry transforms, plus the serializable preprocessing data (positions, sizes, adjacency, bounds, crs). The original GeoDataFrame is NOT stored here (too large for serialization).
Attributes:
-
canonical_symbol(Symbol) –The base symbol shape for this layout.
-
transforms(list[Transform]) –One transform per input geometry.
-
base_size(float) –Reference size for scaling.
-
positions(NDArray[floating]) –Original centroid positions, shape (n, 2).
-
sizes(NDArray[floating]) –Symbol sizes, shape (n,).
-
adjacency(NDArray[floating]) –Adjacency matrix from original geometries, shape (n, n).
-
bounds(tuple[float, float, float, float]) –Geographic bounds (xmin, ymin, xmax, ymax).
-
crs(str | None) –CRS information as WKT string from source GeoDataFrame.
-
layout_type(str) –Registry key of the layout that produced the result (
"packing","flow_density","centroid","grid","mosaic"). It records which layout ran, and selects the result class to rebuild when a serialized result is read back with :meth:from_dict. -
metrics(AlgorithmMetrics | None) –Final scalar summaries (converged, iterations, overlaps, algorithm-specific). Populated by all layout types.
-
history(SimulationHistory | None) –Per-iteration diagnostics and optional position snapshots. Only populated by iterative layouts (packing, flow density, centroid).
Methods:
-
from_serialized–Reconstruct from serialized data.
-
serialize–Serialize for persistence.
-
style–Apply styling to create symbol cartogram.
from_serialized
classmethod
¶
Reconstruct from serialized data.
Parameters:
-
data(dict) –Serialized layout result from serialize().
Returns:
-
LayoutResult–Reconstructed layout result.
Examples:
serialize
¶
Serialize for persistence.
Returns:
-
dict–Serialized representation of the layout result.
Notes
All data is serialized including positions, sizes, adjacency, bounds, crs. The original GeoDataFrame is NOT stored - it must be provided separately when attribute merging is needed via SymbolCartogram.to_geodataframe().
Examples:
style
¶
Apply styling to create symbol cartogram.
Parameters:
-
styling(Styling or None, default:None) –Pre-configured Styling object.
-
**kwargs–Convenience kwargs for simple cases (symbol, scale, etc.) Creates a temporary Styling object internally.
Returns:
-
SymbolCartogram–Rendered cartogram with styled symbols.
MosaicLayout
¶
Bases: Layout
Layout using mosaic (tilegram) assignment.
Assigns each geometry an exact integer number of tiles from a regular tiling that covers the study area. Optionally pre-morphs geometries using a flow cartogram so their areas are proportional to tile counts.
Parameters:
-
options(MosaicLayoutOptions, default:None) –Full options object. Defaults to MosaicLayoutOptions().
-
**kwargs–Individual option overrides.
Examples:
>>> layout = MosaicLayout() # 1 tile per geometry
>>> layout = MosaicLayout(count_column="n_tiles") # N tiles per geometry
>>> layout = MosaicLayout(tiling="square", morph=False)
Notes
Symbol scale comes from the tile lattice: each symbol fills its tile in
proportion to the largest symbol. size_normalization scales every
symbol by the same factor and so has no effect on the result.
MosaicLayoutOptions
dataclass
¶
MosaicLayoutOptions(
tiling: object = "hexagon",
morph: bool = True,
morph_options: object = None,
hungarian_options: object = None,
tile_size: float | None = None,
spacing: float = 0.05,
extra_tile_rings: int = 1,
min_overlap_frac: float = 0.1,
min_one_tile_per_region: bool = False,
)
Options for mosaic (tilegram) layout.
Parameters:
-
tiling(str or Tiling, default:'hexagon') –Tile shape. Default
"hexagon". -
morph(bool, default:True) –Run flow cartogram to pre-morph geometries proportionally to tile counts before assignment. Default True.
-
morph_options(MorphOptions or None, default:None) –Options for flow morphing. None → MorphOptions(n_iter=100).
-
hungarian_options(HungarianOptions or None, default:None) –Assignment cost-function parameters. None → HungarianOptions() defaults.
-
tile_size(float or None, default:None) –Explicit tile size; skips calibration if provided.
-
spacing(float, default:0.05) –Gap between drawn symbols as a fraction of tile size (0-1). Default 0.05, matching GridBasedLayout. 0 makes symbols touch flat-edge to flat-edge.
spacingdoes not change the assignment: it divides each symbol'sTransform.scaleby1 + spacingand leaves the tile size andtiles_gdfuntouched. -
extra_tile_rings(int, default:1) –Number of rings of adjacent tiles to add to each component's pool beyond those produced by calibration. Extra tiles are outside the study union (high
outside_fraccost) so they act as reserve: selected only when interior tiles are exhausted by other cost pressures (e.g. highneighbor_weight). The effect is not monotone: 0 leaves no reserve and regions go short of tiles, while 2 or more splits regions and raises runtime sharply, since each ring enlarges every component's pool. Default 1. -
min_overlap_frac(float, default:0.1) –Minimum fraction of a tile's area that must intersect the study union for the tile to be counted as a core tile during calibration. Values below 0.5 capture tiles whose centroid falls outside the geometry (e.g. narrow peninsulas like Florida). The effect is not monotone in either direction from the default, and the values that best match the map's silhouette also split regions. At 1.0 the layout degenerates. Default 0.1.
-
min_one_tile_per_region(bool, default:False) –Place a symbol for regions that sit on a land mass too small to win a tile of its own. A region only gets tiles from the pool of its geographic component, and a component whose every tile falls below
min_overlap_fracgets an empty pool and no symbols at all -- which is what happens to small island states at coarse tile sizes. With this set, such a component is given the unclaimed lattice cells that overlap it most, enough of them to cover its requested count, so its regions are drawn. The cells come from outside every component's tiles, so no other region loses one. Off by default: a region below one tile's worth of area is drawn at a full tile either way, which overstates it against every other region on the map, and whether that is the right trade is the caller's decision. Regions left without a symbol are reported in a warning whether or not this is set. Default False.
Methods:
-
validate–Validate options.
MosaicMetrics
dataclass
¶
MosaicMetrics(
tiling: str = "",
tile_size: float = 0.0,
n_components: int = 0,
regions_correct: int = 0,
regions_total: int = 0,
n_noncontiguous_regions: int = 0,
n_split_groups: int = 0,
repair_passes: int = 0,
n_unassigned_core_tiles: int = 0,
n_enclosed_unassigned_tiles: int = 0,
)
Algorithm-specific metrics for MosaicLayout.
Attributes:
-
tiling, tile_size, n_components–Tiling used, its calibrated tile size, and the number of disconnected geographic components the study area was split into.
-
regions_correct, regions_total–How many geometries received exactly their requested tile count.
-
n_noncontiguous_regions(int) –Number of geometries whose assigned tiles do not form a single connected block in the tile adjacency graph.
-
n_split_groups(int) –With
group_by, the number of groups whose tiles form more than one block. 0 without a grouping. Groups are counted after splitting at geographic component boundaries, since a group spread over separate land masses cannot be one block. -
repair_passes(int) –Linear-assignment solves actually run by the connectivity-repair loop (the maximum over components, so 1 means "no repair pass was needed").
-
n_unassigned_core_tiles(int) –Core tiles (inside the study union) that received no symbol. Because calibration matches the core tile count to the tile budget, this also counts the tiles the solver had to take from the extra rings outside the core, which show up as protruding tails. Unlike an enclosed-hole count it also catches gaps in concave boundary pockets.
-
n_enclosed_unassigned_tiles(int) –Lattice cells that received no symbol yet have every lattice neighbor assigned. Deliberately independent of core status: a cell ringed by assigned tiles reads as a hole whatever its overlap fraction, and a core-only count misses the ones that fall below
min_overlap_frac.
PackingHistory
dataclass
¶
PackingHistory(
drift: NDArray[floating],
jitter: NDArray[floating],
drift_rate: NDArray[floating],
drift_rate_neg_frac: NDArray[floating],
)
Per-iteration diagnostics for CirclePackingLayout.
PackingMetrics
dataclass
¶
Final scalar metrics for CirclePackingLayout.
SimulationHistory
dataclass
¶
SimulationHistory(
positions: list[NDArray[floating]] | None = None,
overlaps: NDArray[intp] | None = None,
algorithm: Any = None,
)
Per-iteration arrays common to all iterative layouts.
Attributes:
-
positions(list[ndarray] | None) –Position snapshots, each of shape
(n, 2). Only populated whensave_history=True. -
overlaps(ndarray | None) –Per-iteration overlap count. Shape:
(n_iters,). -
algorithm(Any) –Algorithm-specific per-iteration arrays (PackingHistory or FlowDensityHistory depending on the layout used).
Methods:
-
__len__–Number of recorded iterations.
Transform
dataclass
¶
Transform(
position: tuple[float, float] = (0.0, 0.0),
rotation: float = 0.0,
scale: float = 1.0,
reflection: bool = False,
)
Transformation applied to a symbol.
Note: This is an internal class that stores rotation in radians. User-facing APIs (Styling, TransformedSymbol) use degrees for convenience.
Attributes:
-
position(tuple[float, float]) –Center position (x, y) for the symbol.
-
rotation(float) –Rotation angle in radians (counter-clockwise). Default: 0.0 User-facing APIs accept degrees and convert internally.
-
scale(float) –Scale multiplier. Default: 1.0
-
reflection(bool) –Whether to reflect the symbol about the vertical axis. Default: False
Methods:
-
compose–Compose this transform with another.
check_group_by_support
¶
Raise if group_by was requested for a layout that ignores it.
Parameters:
-
layout(Layout) –Layout that will run.
-
has_group_by(bool) –Whether the user supplied a
group_bygrouping.
Raises:
-
ValueError–If
has_group_byis True and the layout does not support it.
compute_symbol_sizes
¶
compute_symbol_sizes(
values: ArrayLike,
scale: Literal["sqrt", "linear", "log"] = "sqrt",
target_max_size: float | None = None,
size_max_value: float | None = None,
size_clip: bool = True,
) -> NDArray[np.floating]
Compute symbol sizes from data values.
Uses abs(values) for size computation. Sign can be used for styling.
The returned sizes are area-equivalent radii, meaning the symbol area equals pi x size^2. This ensures symbols of different shapes with the same size have the same visual area.
Parameters:
-
values(array - like) –Data values to scale.
-
scale(str, default:'sqrt') –"sqrt": Area ∝ value (perceptually accurate, default) "linear": Size ∝ value "log": Logarithmic scaling via log(1 + x)
-
target_max_size(float, default:None) –Maximum symbol size (area-equivalent radius). If None, normalized to 1.0.
-
size_max_value(float, default:None) –Reference max value for consistent scaling across cartograms. When set, sizes are scaled relative to this value instead of the data maximum.
-
size_clip(bool, default:True) –Whether to clip values exceeding size_max_value. Default True.
Returns:
-
ndarray–Symbol sizes (area-equivalent radii), always non-negative.
Examples:
get_layout
¶
Instantiate a registered layout by name.
Parameters:
-
name(str) –Registered layout name.
Returns:
-
Layout–New layout instance.
Raises:
-
ValueError–If name is not registered.
group_by_layouts
¶
Return the registered layout names whose placement honors group_by.
Returns:
-
list[str]–Registered names, one per supporting layout class (aliases dropped).
prepare_layout_data
¶
prepare_layout_data(
gdf: GeoDataFrame,
size: str | None = None,
*,
tile_count: str | None = None,
group_by: str | None = None,
size_scale: Literal["sqrt", "linear", "log"] = "sqrt",
size_max_value: float | None = None,
size_clip: bool = True,
adjacency_mode: AdjacencyMode
| Literal[
"binary", "weighted", "area_weighted"
] = "binary",
distance_tolerance: float | None = None,
size_normalization: Literal["max", "total"] = "total",
tile_size_expansion: Literal[
"shared", "copied"
] = "copied",
collapse_group: float = 0.0,
pre_scale: bool = False,
) -> LayoutData
Prepare data for layout algorithms.
This function:
- Computes unit cell area as mean geometry area
- Extracts centroids as initial positions
- Computes symbol sizes (proportional or uniform)
- Computes adjacency matrix
- Optionally expands rows for tile_count (N = Σcounts)
- Optionally encodes group_by as integer labels
The unit cell is a circle with area equal to the mean geometry area. Symbol sizes are area-equivalent radii relative to this unit cell.
Parameters:
-
gdf(GeoDataFrame) –Input GeoDataFrame with polygon geometries.
-
size(str, default:None) –Column for proportional sizing. If None, uses uniform sizing (all symbols same size as unit cell).
-
tile_count(str, default:None) –Column with integer tile counts per geometry (N = Σcounts). Cannot be combined with group_by.
-
group_by(str, default:None) –Column whose values define connectivity groups. Cannot be combined with tile_count.
-
size_scale(str, default:'sqrt') –Scaling method: "sqrt" (area ∝ value, default), "linear", or "log".
-
size_max_value(float, default:None) –Reference max value for consistent scaling across cartograms.
-
size_clip(bool, default:True) –Whether to clip values exceeding size_max_value.
-
adjacency_mode(str, default:'binary') –"binary" (adjacent or not), "weighted" (shared border fraction), or "area_weighted" (neighbor area fraction).
-
distance_tolerance(float, default:None) –Buffer for adjacency detection.
-
size_normalization(('max', 'total'), default:"max") –How to normalize symbol sizes after tile expansion, relative to original geometry areas:
"total"(default): all N-level sizes are scaled by a single global factor so thatΣ(π x size²) = Σ(geometry_area). With"copied"expansion, this accounts for tile counts; with"shared"the result equals what G-level normalization would give."max": the largest N-level symbol has area equal to the mean geometry area (π x unit_cell_radius²). With the defaultsqrtscale the symbols then cover a fractionmean(value) / max(value)of the total geometry area, so the covered fraction follows the skew of the sizing column.
-
tile_size_expansion(str, default:'copied') –How to expand symbol sizes when
tile_countis set:"copied"(default): each tile has the same radius as the original geometry symbol (r_g). Combined withsize_normalization="total", tiles are all equal whensize=None."shared": each tile's radius is scaled by1/√K_gso that the K_g tiles of geometry g together cover the same area as one un-expanded symbol.
-
collapse_group(float, default:0.0) –Pre-collapse starting positions toward each group's area-weighted centroid (0-1). At 1.0 all items in a group start coincident at the group centroid, matching
tile_countbehavior. Area-weighting avoids bias toward regions with many small polygons (e.g. NY congressional districts clustered around NYC — a simple mean would place the NY centroid too far south). At 0.0 (default) geographic centroids are used unchanged.geometry_positionsis always left unchanged so thatorigin_weightin force-based layouts can attract symbols back toward geography. Only has effect whengroup_byortile_countis set. Default: 0.0. -
pre_scale(bool, default:False) –Uniformly scale each geographically connected component so its area matches its share of the data (
tile_countif given, elsesize) before any layout step. No effect for single-component inputs. This rescales geometry positions and areas, which affects layouts that read component geometry (e.g. mosaic). Symbol sizing undersize_normalization="max"and themean_areareported on the returnedLayoutDataare recomputed from the rescaled geometries, but are unaffected in practice: each component's rescaled area matches its share of the data by construction, so the total area — and therefore the mean — is unchanged. A component whose data values are all zero is collapsed to a zero-area geometry (returned as aPolygonwithis_valid == False) rather than left at its original size. Values are taken by absolute value, so a component's negative and positive values do not cancel out to zero. Default: False.
Returns:
-
LayoutData–Preprocessed data ready for Layout.compute().
Examples: