GenPlanner¶
GenPlanner is the main orchestration class of the library.
It prepares input territory geometry, applies preprocessing (roads, exclusions,
existing zones), and executes the zoning pipeline.
The planner operates in a projected local CRS (UTM), but all outputs are returned in the original CRS of the input territory.
See also:
Conceptual pipeline¶
High-level workflow:
Prepare territory geometry.
Apply exclusions (optional).
Split by roads (optional).
Integrate existing territorial zones (optional).
Balance target ratios.
Build relation matrix.
Run territorial optimization.
(Optional) Split zones into blocks.
The heavy geometric split is performed by the internal
split_polygon engine.
Minimal example¶
import geopandas as gpd
from genplanner import GenPlanner
from genplanner.zones import basic_func_zone
territory = gpd.read_file("territory.geojson")
gp = GenPlanner(features_gdf=territory)
zones_gdf, roads_gdf = gp.features2terr_zones(
funczone=basic_func_zone
)
Generate blocks instead of zone-level polygons:
blocks_gdf, roads_gdf = gp.features2terr_zones2blocks(
funczone=basic_func_zone
)
Relation matrix¶
The relation_matrix argument controls adjacency rules between zones.
See Zone Relations for details.
Supported values:
"default"— built from predefined forbidden neighborhood rules"empty"— no adjacency constraintsZoneRelationMatrixinstanceNone— treated as"default"
Example:
from genplanner import ZoneRelationMatrix
from genplanner.zones import default_terr_zones
res = default_terr_zones.residential_terr
ind = default_terr_zones.industrial_terr
mat = ZoneRelationMatrix.from_pairs(
zones=[res, ind],
forbidden=[(res, ind)],
)
zones, roads = gp.features2terr_zones(
relation_matrix=mat
)
Fixed zone anchor points¶
Both zoning methods accept an optional argument:
terr_zones_fix_points: GeoDataFrame
It allows you to constrain spatial allocation by specifying anchor points for particular territorial zones.
Each row represents a fixed spatial hint for a zone.
Requirements:
Geometry must be
PointColumn
fixed_zonemust be presentfixed_zonevalues must beTerritoryZoneobjects present infunczone.zones_ratio
All fixed points must lie inside the working territory.
Example:
import geopandas as gpd
from shapely.geometry import Point
from genplanner.zones import default_terr_zones
fix_points = gpd.GeoDataFrame(
{
"fixed_zone": [
default_terr_zones.residential_terr,
default_terr_zones.business_terr,
],
"geometry": [
Point(30.1, 59.9),
Point(30.2, 59.91),
],
},
crs="EPSG:4326",
)
zones, roads = gp.features2terr_zones(
terr_zones_fix_points=fix_points
)
Existing zones integration¶
If existing_terr_zones is provided:
Territory fragments sufficiently covered by existing zones are merged.
Static fix points are derived automatically.
Target ratios are rebalanced.
Final output always includes existing zones.
See internal validation logic for details.
Full example with roads, exclusions, existing zones and fixed anchors¶
import geopandas as gpd
from shapely.geometry import Point
from genplanner import GenPlanner, default_terr_zones
roads = gpd.read_file("roads.geojson")
territory = gpd.read_file("territory.geojson")
exclude = gpd.read_file("exclude_features.geojson")
existing_zones = gpd.read_file("existing_zones.geojson")
terr_zone_map = {
"industrial": default_terr_zones.industrial_terr,
"special": default_terr_zones.special_terr,
}
existing_zones["territory_zone"] = (
existing_zones["territory_zone"]
.map(terr_zone_map)
)
# Optional: add anchor points for certain zones
fix_points = gpd.GeoDataFrame(
{
"fixed_zone": [
default_terr_zones.industrial_terr,
],
"geometry": [
Point(30.15, 59.92),
],
},
crs=territory.crs,
)
gp = GenPlanner(
features_gdf=territory,
roads_gdf=roads,
exclude_gdf=exclude,
existing_terr_zones=existing_zones,
parallel=True,
)
new_zones, new_roads = gp.features2terr_zones2blocks(
terr_zones_fix_points=fix_points
)
Visualize:
m = new_zones.explore(column="territory_zone", tiles="cartodb positron")
new_roads.explore(m=m, column="road_lvl")
Parallel execution¶
If parallel=True (default), the planner uses
ProcessPoolExecutor for downstream tasks.
Worker count defaults to
cpu_count - 1.Falls back to single-thread mode if only one CPU is available.
Logging¶
If rust_write_logs=True:
A directory named after
run_nameis created.Optimizer logs are written per split attempt.
Useful for debugging zoning instability.
Returned objects¶
Both zoning methods return:
(zones_or_blocks_gdf, roads_gdf)
All geometries are returned in the original input CRS.
roads_widthcolumn is always present.road_lvlcolumn describes road type.