Changelog¶
All notable changes to carto-flow are documented here. The format follows Keep a Changelog and carto-flow uses semantic versioning.
Releases before 2.0.0 have no changelog entry; see the git history for those.
2.0.0 - 2026-09-24¶
Step-by-step edits for every break below are in the migration guide, including what a caller sees if they change nothing.
Breaking changes¶
symbol_cartogramreorganized into alayouts/subpackage. The layout classes, their option dataclasses,LayoutData,LayoutResult,TransformandSimulationHistorynow live incarto_flow.symbol_cartogram.layouts;symbol_cartogram.optionskeeps only the enums. The moduleslayout.py,layout_result.py,data_prep.pyandplacement.pyare gone. Every name is still re-exported fromcarto_flow.symbol_cartogram, so package-level imports are unaffected; submodule imports are not. (#19)create_symbol_cartogramandcreate_layoutrenamedvalue_columntosize. The parameter is positional in both, so only keyword callers are affected. (#19)- The
preset_*functions were replaced by named cartogram functions.preset_dorling,preset_topology_preserving,preset_demers,preset_tile_map,preset_fastandpreset_qualityreturned kwargs dicts;dorling_cartogram,geographic_cartogram,demers_cartogram,tile_map_cartogram,centroid_cartogram,dorling_grouped_cartogramandgeographic_grouped_cartogramtake a GeoDataFrame and return aSymbolCartogram. (#19) SymbolCartogram.metricsrenamedplacement_metrics, andSymbolCartogram.simulation_historyremoved in favor ofSymbolCartogram.layout_result.history. Algorithm-specific per-iteration arrays (drift,jitter,drift_rate,velocity) moved offSimulationHistoryonto itsalgorithmsubobject (PackingHistoryorFlowDensityHistory), and the scalar summaries ontoAlgorithmMetrics. (#19)- The
physicslayout was removed and the default layout changed from"physics"to"packing".CirclePhysicsLayout,CirclePhysicsLayoutOptions,PhysicsHistoryandPhysicsMetricsare gone.CirclePackingLayoutsupersedes them: it honorssource_indicesandgroup_by, which physics did not. (#54) LayoutResult.layout_typerecords the layout's registry key. Packing and flow-density results reportedlayout_type="physics"; they now report"packing"and"flow_density". (#54)group_byraises for layouts that ignore the grouping. The grid layout passedgroup_bythrough for labeling only and placed symbols as if it were absent; it now raisesValueError. The circle packing and flow-density layouts warn when the option that acts on the grouping is at its inert default (group_weight=0.0andcross_group_pull_scale=1.0). (#39)size_normalizationdefaults per layout instead of to"max". The layouts that place symbols by radius — circle packing, centroid, flow density — default to"total", so total symbol area equals total geometry area. The grid layout keeps"max", since its lattice is calibrated from the largest symbol; the mosaic layout is unaffected, because its symbol scale comes from the tile lattice. An unrecognized value raisesValueErrorinstead of silently behaving as"max". (#42)- Bundled datasets are GeoParquet rather than GeoJSON.
world.geojson,us_states.geojsonandcities.geojsonwere replaced by.parquetfiles andpyarrowbecame a required dependency.load_world,load_us_statesandload_sample_citiesare unchanged; code that opened the packaged files directly is not. (#17)
MosaicLayout is new in this release, so its option changes below break only
code written against an unreleased main:
HungarianOptions.gap_bridge_multandHungarianOptions.disconnected_score_weightwere removed; both were measurably inert. (#43)HungarianOptions.max_connectivity_itersdefault 15 -> 5, anddisconnected_penalty_mult(float) was replaced bypenalize_disconnected(bool). (#48)MosaicLayoutOptions.spacingdefault 0.0 -> 0.05, matching the grid layout. Rendering only. (#56)
Added¶
MosaicLayout, a contiguous tilegram layout for the symbol cartogram: each region receives a connected block of lattice tiles, assigned by a Hungarian matching over a morphed target field and repaired by chain swaps. Accepts bothtile_countandgroup_by. (#21)FlowDensityLayout, which advects symbols along the gradient of a smoothed density field derived from their target sizes, withcross_group_pull_scaleto separate groups. (#19)voronoi_cartogram, a new submodule producing area-equalized Voronoi cartograms by Lloyd relaxation, with raster and geometric backends, geodesic labeling, contiguity analysis and repair, visualization and animation. (#15)- A bundled ACS 2020 census snapshot.
load_us_censusreads it for its default resolution, so the common path needs no Census API key, no network access and no optional dependency. Requests outside the snapshot still fall back tocensusdis. Congressional districts were added as a resolution, alongsideload_us_state_populationfor a yearly population series. (#13, #16, #17) MosaicLayoutOptions.min_one_tile_per_region, which places a region left without any tile on the nearest free cell. A warning names the regions that would otherwise disappear. (#47)- Grouped symbol cartograms:
group_by,tile_count,collapse_group,tile_size_expansionandpre_scaleoncreate_symbol_cartogramandcreate_layout, per-group styling throughStyling.set_group_symbol,Styling.group_transformandStyling.set_group_params, and thedorling_grouped_cartogram/geographic_grouped_cartogramentry points. (#19) - Flow cartogram performance options:
MorphOptions.parallel_fftandparallel_densityfor threaded FFT and density rasterization, a bounding-box filter, andMorphOptions.benchmarkwith aBenchmarkobject recording per-stage timings. (#7, #11, #14) - Shared geometry utilities in
carto_flow.geo_utils:explode_geodataframe,compute_connected_components,components_from_adjacency,prescale_connected_components,densify_coverage,repair_contiguity,repair_adjacency,repair_compactnessandrepair_group_assignment. (#18)
Changed¶
SymbolCartogram.to_geodataframe(level="group", ...)read the group index as a row index into the source GeoDataFrame, so group attributes were taken from an unrelated region whenever a group's first member was not at that row. It now reads the group's first source row. Output that relied on the old behavior was wrong. (#57)- The bundled
world.parquetwas repaired: invalid rings were fixed and boundaries densified so the dataset survives reprojection. World cartograms differ slightly from 1.x. (#45) MosaicLayoutOptions.interior_bonusdefault 0.5 -> 2.0, which reduces split regions. (#40)- Mosaic component tile pools are disjoint, so a tile can no longer be claimed by two geographic components. (#46)
Fixed¶
- Flow cartogram: a zero sizing value made
mean_log_errorandmax_log_errorinfinite for the whole morph, defeating the convergence check and emitting divide-by-zero warnings. The convergence loop now floors the area ratio at one grid cell area. (#35) prescale_connected_componentsused signed values, so a component whose values summed to zero was skipped as carrying no data; it now takes absolute values, collapses a zero-target component to exactly zero area, and rejects an all-zero dataset. (#38)- Symbol cartogram:
plot_adjacencydrew edges from raw tile positions for grouped results instead of aggregating tiles back to geometry level; packing convergence metrics went non-finite for zero-size symbols; atile_countof zero crashed the mosaic layout; an input with no adjacent pairs, or a single symbol, crashed or never converged under the packing layout. (#19, #38, #41, #54) - Mosaic connectivity repair: repair was scored by disconnected tiles rather than split regions, stranded ring tiles were never pulled back into empty core tiles, enclosed holes survived when their core status differed, and the swap-back reach was too short to close longer splits. (#29, #30, #31, #32, #34)
MosaicLayoutread the per-symbol group id array as if it were per-geometry, corrupting connectivity repair and per-region tile counts. (#23)- Voronoi cartogram: degenerate cells broke adjacency and line clipping and made
the area equalizer run away, raster cell extraction lost whole cells, the
coverage_simplifytolerance was interpreted in the wrong units, andsimplify_coverageproduced sliver interior rings. (#24, #25, #27, #28) carto_flow.flow_cartogram.__all__listedHarmonic, a name the package never defined, which madefrom carto_flow.flow_cartogram import *fail. (#14)
Performance¶
- The flow cartogram morph is substantially faster than 1.1.2. Measured on contiguous US states with identical geometry and options, serial in both versions: 3.5x at a 128-cell grid, 5.3x at the default 256, 10.0x at 512 and 11.6x at 1024. Iteration counts are identical at every grid size, so the convergence path is unchanged — the work per iteration got cheaper. The gain is in the density rasterization and FFT solve rather than in vertex advection, so it grows with grid size and shrinks as vertex count rises. (#11)
repair_contiguityskips path enumeration for pairs it cannot connect, and mosaic calibration skips adjacency computation and vectorizes lattice adjacency. (#36, #44)