Skip to content

processing.add_zone_ids

Module for adding zone IDs based on geographic locations.

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 df used for joining back.

required
lon_col str

Name of the longitude column in df (WGS-84 assumed).

required
lat_col str

Name of the latitude column in df (WGS-84 assumed).

required
zone_col_name str

Name for the output zone-ID column added to df.

required
zone_id_field str

Field name in shp that holds the zone ID values.

required
projected_crs str | None

EPSG string for the metric CRS used during the nearest-neighbour fallback (e.g. "EPSG:26910"). If None, no fallback is performed and unmatched points remain null.

None
max_snap_distance float | None

Maximum distance (in projected_crs units, i.e. metres for UTM) within which an unmatched point is snapped to the nearest zone polygon. Points farther than this are left null (assumed to be genuinely out-of-region rather than near a zone edge). None means no distance limit — all unmatched points are snapped. Only used when projected_crs is provided.

None
zones gpd.GeoDataFrame | None

shp as built by prepare_zones for the same zone_id_field. Built here when not given; passing it lets repeated calls reuse its spatial index.

None
snap_zones gpd.GeoDataFrame | None

shp as built by prepare_snap_zones for the same zone_id_field and projected_crs. Built here when not given.

None
points gpd.GeoDataFrame | None

df's coordinates as built by zone_points. Built here when not given; passing it lets several zone systems reuse them.

None

Returns:

Type Description
pl.DataFrame

df with a new zone_col_name column appended. The column dtype

pl.DataFrame

is Int64 when all non-null values are numeric, otherwise Utf8.

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 {prefix}_{zone_name} column is registered on it, so the writer can tell a column the pipeline created from vendor passthrough.

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 projected_crs units (metres for UTM). Unmatched points farther than this from all zone boundaries are left null (assumed out-of-region). None means no distance limit. Only used when projected_crs is set.

None

Returns:

Type Description
dict

Dictionary with updated dataframes