processing.add_zone_ids
processing.add_zone_ids.add_zone_ids
Add zone IDs to every table with a location, one column per configured zone system.
prepare_zones
prepare_zones(
shp: gpd.GeoDataFrame, zone_id_field: str
) -> gpd.GeoDataFrame
Zone polygons indexed by zone ID, as a string so nulls survive pandas.
prepare_snap_zones
prepare_snap_zones(
shp: gpd.GeoDataFrame, zone_id_field: str, projected_crs: str
) -> gpd.GeoDataFrame
Zone polygons for the nearest-zone fallback: projected and repaired.
make_valid() repairs self-intersecting rings that cause GEOSException in
shapely's query_nearest. Reprojecting and repairing a large zone system
takes seconds, so callers zoning several tables against the same shapefile
should build this once and pass it to add_zone_to_dataframe.
zone_points
zone_points(
df: pl.DataFrame, df_index: str, lon_col: str, lat_col: str
) -> gpd.GeoDataFrame
WGS-84 points for df's coordinates, indexed by df_index.
add_zone_to_dataframe
add_zone_to_dataframe(
df: pl.DataFrame,
shp: gpd.GeoDataFrame,
df_index: str,
lon_col: str,
lat_col: str,
zone_col_name: str,
zone_id_field: str,
projected_crs: str | None = None,
max_snap_distance: float | None = None,
zones: gpd.GeoDataFrame | None = None,
snap_zones: gpd.GeoDataFrame | None = None,
points: gpd.GeoDataFrame | None = None,
) -> pl.DataFrame
Add a zone ID column to a Polars DataFrame via point-in-polygon spatial join.
Each row's lon/lat coordinates are matched against the provided zone shapefile. If a point falls within exactly one zone polygon it receives that zone's ID.
Nearest-neighbour fallback
If projected_crs is provided, any unmatched point is snapped to the
nearest zone polygon (by boundary distance) instead of being left null.
Supply a metric CRS such as "EPSG:26910" (UTM Zone 10N for the Bay
Area); operating in degrees produces incorrect distances and triggers a
GeoPandas warning. When projected_crs is None no snapping is
performed and unmatched points are left null with a WARNING logged.
Use max_snap_distance (in projected_crs units, i.e. metres for
UTM) to distinguish GPS-drift edge cases from genuinely out-of-region
points: a point is snapped only when the distance to the nearest zone
boundary is ≤ max_snap_distance; points farther away are left null.
max_snap_distance=None snaps all unmatched points regardless of
distance.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
df
|
pl.DataFrame
|
Input Polars DataFrame. |
required |
shp
|
gpd.GeoDataFrame
|
Zone boundary GeoDataFrame (any CRS; will be reprojected as needed). |
required |
df_index
|
str
|
Name of the unique-ID column in |
required |
lon_col
|
str
|
Name of the longitude column in |
required |
lat_col
|
str
|
Name of the latitude column in |
required |
zone_col_name
|
str
|
Name for the output zone-ID column added to |
required |
zone_id_field
|
str
|
Field name in |
required |
projected_crs
|
str | None
|
EPSG string for the metric CRS used during the
nearest-neighbour fallback (e.g. |
None
|
max_snap_distance
|
float | None
|
Maximum distance (in |
None
|
zones
|
gpd.GeoDataFrame | None
|
|
None
|
snap_zones
|
gpd.GeoDataFrame | None
|
|
None
|
points
|
gpd.GeoDataFrame | None
|
|
None
|
Returns:
| Type | Description |
|---|---|
pl.DataFrame
|
|
pl.DataFrame
|
is |
add_zone_ids
add_zone_ids(
zone_geographies: list[dict],
households: pl.DataFrame | None = None,
persons: pl.DataFrame | None = None,
unlinked_trips: pl.DataFrame | None = None,
linked_trips: pl.DataFrame | None = None,
tours: pl.DataFrame | None = None,
joint_trips: pl.DataFrame | None = None,
habitual_locations: pl.DataFrame | None = None,
projected_crs: str | None = None,
max_snap_distance: float | None = None,
canonical_data: CanonicalData | None = None,
) -> dict
Add zone IDs for multiple geographic levels based on locations.
Automatically applies each zone geography to standard locations:
- households: home_lon/lat → home_{zone_name}
- persons: work_lon/lat → work_{zone_name}, school_lon/lat → school_{zone_name}
- unlinked_trips, linked_trips, tours: o_lon/lat → o_{zone_name}, d_lon/lat → d_{zone_name}
- habitual_locations: lon/lat → {zone_name}, so a workplace found from trips can stand in as a model's usual workplace
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
households
|
pl.DataFrame | None
|
Households dataframe |
None
|
persons
|
pl.DataFrame | None
|
Persons dataframe |
None
|
unlinked_trips
|
pl.DataFrame | None
|
Unlinked trips dataframe |
None
|
linked_trips
|
pl.DataFrame | None
|
Linked trips dataframe |
None
|
tours
|
pl.DataFrame | None
|
Tours dataframe |
None
|
joint_trips
|
pl.DataFrame | None
|
Joint trips dataframe |
None
|
habitual_locations
|
pl.DataFrame | None
|
Habitual locations dataframe |
None
|
canonical_data
|
CanonicalData | None
|
Canonical data container. When present, each generated
|
None
|
zone_geographies
|
list[dict]
|
List of dicts, each containing: - shapefile: Path to shapefile with zone boundaries (str) - zone_id_field: Field name in shapefile for zone ID - zone_name: Short name for zone type (e.g., 'taz', 'maz', 'county') |
required |
projected_crs
|
str | None
|
Optional EPSG string (e.g. 'EPSG:26910') for the metric CRS to use when snapping unmatched points to the nearest zone. If None, no nearest-zone fallback is performed. |
None
|
max_snap_distance
|
float | None
|
Maximum snap distance in |
None
|
Returns:
| Type | Description |
|---|---|
dict
|
Dictionary with updated dataframes |