Skip to content

carto_flow.proportional_cartogram.splitting

Geometry splitting utilities.

This module provides functions for splitting geometries into parts with specified area fractions. Supports both sequential and treemap splitting strategies.

Functions:

  • split

    Split a geometry into multiple parts with specified area fractions.

split

split(
    geom: BaseGeometry,
    fractions: float | Sequence[float],
    direction: Literal[
        "vertical", "horizontal"
    ] = "vertical",
    alternate: bool = True,
    strategy: Literal[
        "sequential", "treemap"
    ] = "sequential",
    tol: float = 0.01,
    treemap_reference: Sequence[float] | None = None,
) -> list[BaseGeometry]

Split a geometry into multiple parts with specified area fractions.

This function divides a geometry into N parts. Each fraction specifies the relative area of one resulting part. Two splitting strategies are available:

  • sequential: Carves off parts one-by-one from edges, optionally alternating direction. Creates strip-like or staircase patterns.
  • treemap: Recursively partitions using balanced binary splits with automatic direction alternation. Creates grid-like patterns with compact regions.

Parameters:

  • geom (BaseGeometry) –

    Input geometry to split. Should be a Polygon or MultiPolygon.

  • fractions (float or Sequence[float]) –

    Target area fractions for the resulting parts.

    • Single float: Split into 2 parts with areas (fraction, 1-fraction). Must be in range (0, 1).
    • Sequence of floats: Split into N parts where N = len(fractions). Each value represents the target area fraction for that part. Values should be non-negative and sum to approximately 1.0. Zero fractions produce empty geometries in the corresponding position. Example: [0.3, 0.2, 0.5] creates 3 parts with 30%, 20%, 50% of total area.
  • direction (('vertical', 'horizontal'), default: 'vertical' ) –

    Initial direction of the splitting line:

    • 'vertical': Split along vertical lines (constant x-coordinate)
    • 'horizontal': Split along horizontal lines (constant y-coordinate)
  • alternate (bool, default: True ) –

    For sequential strategy only. Whether to alternate between vertical and horizontal directions for each subsequent split. If False, all splits use the same direction. Ignored for treemap strategy (which always alternates).

  • strategy (('sequential', 'treemap'), default: 'sequential' ) –

    Splitting strategy for N-way splits:

    • 'sequential': Parts are carved off one-by-one from the remaining geometry. Creates asymmetric strip/staircase patterns. Good when order matters or for ranked data.
    • 'treemap': Recursive binary partitioning that balances total weight on each side at every split. Creates grid-like patterns with compact, similarly-shaped regions. Good for categorical data where each part should have similar visual prominence.
  • tol (float, default: 0.01 ) –

    Absolute tolerance for area matching in each split operation.

  • treemap_reference (Sequence[float], default: None ) –

    Only used when strategy='treemap'. When provided, the tree grouping structure (which fraction indices go left vs right at each recursive level) is derived from these reference fractions rather than from fractions. The actual split ratios are still computed from fractions, so part areas exactly match the data. Must be the same length as fractions.

    Use this to achieve a consistent spatial layout when splitting multiple geometries that have different fractions. Pass the same treemap_reference to each call and part[i] will always occupy the same relative spatial position regardless of the actual values.

Returns:

  • list[BaseGeometry]

    List of geometry parts. For single float input, returns 2 parts. For sequence input with N values, returns N parts. Parts are ordered corresponding to input fractions.

Raises:

  • ValueError

    If fractions are not valid (out of range, don't sum to ~1, etc.)

  • TypeError

    If geom is not a valid Shapely geometry

Examples:

Binary split (single fraction):

>>> from shapely.geometry import Polygon
>>> from carto_flow.proportional_cartogram import split
>>>
>>> rect = Polygon([(0, 0), (10, 0), (10, 5), (0, 5)])
>>> parts = split(rect, 0.3, direction='vertical')
>>> len(parts)  # 2 parts
2
>>> print(f"Areas: {[round(p.area, 1) for p in parts]}")  # [15.0, 35.0]

Sequential strategy (default):

>>> # Split into 4 strips, alternating direction
>>> parts = split(rect, [0.25, 0.25, 0.25, 0.25], strategy='sequential')
>>> len(parts)  # 4 parts
4

Treemap strategy for grid-like layout:

>>> # Split into 4 compact regions (2x2 grid pattern)
>>> parts = split(rect, [0.25, 0.25, 0.25, 0.25], strategy='treemap')
>>> len(parts)  # 4 parts
4

Sequential without alternation (vertical strips):

>>> parts = split(rect, [0.25, 0.25, 0.25, 0.25], strategy='sequential', alternate=False)

Consistent layout across geometries with different fractions:

>>> ref = [0.3, 0.3, 0.4]
>>> g1 = Polygon([(0, 0), (1, 0), (1, 1), (0, 1)])
>>> g2 = Polygon([(0, 0), (3, 0), (3, 1), (0, 1)])
>>> # part[0] occupies the same relative spatial position in both geometries
>>> p1 = split(g1, [0.5, 0.2, 0.3], strategy='treemap', treemap_reference=ref)
>>> p2 = split(g2, [0.1, 0.6, 0.3], strategy='treemap', treemap_reference=ref)