laurel.pipelines.describe_locations package

Submodules

laurel.pipelines.describe_locations.nodes module

Kedro pipeline nodes for the describe_locations pipeline (Model Module 3 — Augment TAZs).

Constructs the spatial foundation of the model: H3 resolution-8 hexagonal Traffic Analysis Zones (TAZs) covering the continental U.S., each labelled with a freight-activity class that determines which charger types are deployed there and how many. This pipeline implements Model Module 3 (Augment TAZs) from the paper.

Pipeline overview

Substation territory construction:

  1. format_substation_boundaries_pg_and_e — Sums transformer bank ratings to substation level and adds provenance metadata for PG&E ICA data.

  2. format_substation_profiles — Collapses hour-month baseload profiles to a characteristic 24-hour day per substation.

  3. describe_substation_usage — Joins profiles and capacities to compute available headroom at each substation hour.

  4. format_substations_contin — Formats HIFLD point-based substation data (continental U.S.) for merging with polygon data.

  5. fill_out_substations — Fills in substation territories not covered by ICA polygon data using Voronoi tessellation of HIFLD point locations.

  6. build_remainder_polys — Constructs Voronoi-based territory polygons for point substations outside the ICA coverage area (called by fill_out_substations).

Spatial layer formatting:

  1. format_states — Renames columns in the U.S. state polygons layer.

  2. format_urban — Renames columns in the urban-area polygons layer.

  3. format_highways — Dissolves and buffers highway polylines to create highway-corridor polygons.

  4. build_land_use_areas — Constructs three mutually exclusive land-use area polygons: urban, rural-highway, and rural-non-highway.

  5. clip_to_extent — Clips any spatial layer to the extent of a reference layer.

  6. hexify_polygons — Converts polygon layers to H3 hex grids by assigning each hex that overlaps the polygon the polygon’s attributes.

Establishment data preparation:

  1. concat_columns — Joins per-hexagon feature tables by shared hex index.

  2. fill_missingness — Drops rows with missing required values and fills optional columns with configured defaults.

  3. prepare_shared_locations — Formats shared truck-stop locations (e.g., Jason’s Law) with a standardised location-type label.

  4. format_estabs — Merges raw Data Axle establishment core, geo, and relationship tables; assigns H3 hex IDs.

  5. reassign_hqs — Reassigns NAICS codes for corporate-headquarters establishments to a HQ-specific code to prevent them from being misidentified as large freight facilities.

  6. prepare_stop_locations_public — Formats publicly available truck-stop locations from Jason’s Law for use as establishment records.

  7. get_osm_estabs_truck_stops — Extracts fuel stations matching a truck- stop name pattern from an OSM PBF file.

  8. get_osm_estabs_warehouses — Extracts warehouse/distribution-centre candidates from an OSM PBF file by name pattern.

  9. concat_extra_estabs — Concatenates supplementary establishment sources.

  10. format_extra_estabs — Reprojects extra establishments, assigns H3 hex IDs, and deduplicates.

  11. collapse_naics_classes — Maps 8-digit NAICS codes to the smaller set of leaf classes used for clustering.

  12. pivot_hex_estabs — Pivots the establishment table to a per-hexagon employment matrix (one column per NAICS leaf class).

NLCD land use raster extraction (read_land_use tag):

  1. partition_hex_corresp — Converts the pandas hex_base_corresp feather to a partitioned Dask parquet file for parallel raster extraction.

  2. read_land_use — Extracts NLCD 2023 land cover fractions for every H3 hexagon using exactextract; writes hex_land_use in long format.

Freight-activity-class assignment:

  1. pivot_hex_land_use — Pivots the land-use coverage table to a per-hexagon land-use-group fraction matrix.

  2. group_hexes — Assigns each hexagon a freight-activity class using a combination of development threshold, freight-intensity rules, special- establishment flags, and K-Means clustering.

  3. apply_groups — Merges freight-activity-class labels onto the hexagon feature table.

Key design decisions

  • Voronoi gap-filling: HIFLD substation data provides points for most of the U.S., but ICA data provides polygons for PG&E territory only. Voronoi tessellation fills the remaining territory while avoiding overlap with known ICA polygons. Disconnected Voronoi shards (islands separated from their substation point) are merged into the nearest connected territory to prevent orphaned hexagons.

  • Headquarters NAICS reassignment: Data Axle assigns some large corporate headquarters the NAICS of the parent company’s primary activity. Without reassignment, a trucking-company HQ would be indistinguishable from a freight terminal. The heuristic (employment ratio × business-status code × establishment count) is conservative to avoid over-correction.

  • K-Means on sparse employment matrices: The clustering uses log1p- transformed sparse COO matrices as input so that large employment counts in a single NAICS class do not dominate the distance metric. Only hexagons with non-zero freight-intensive NAICS employment (excluding special categories like truck stops) are clustered; others receive rule-based labels.

  • Neighbor embedding: Each hexagon’s feature vector is augmented with the summed employment of its six H3 ring-1 neighbors to capture the local land-use context, which improves cluster coherence at the cluster-boundary hexagons.

  • Land use raster extraction runtime: Extracting NLCD fractions for all H3 resolution 8 hexagons in the Continental U.S. via exactextract is I/O-intensive and may take several hours on a single machine. The step is isolated under the read_land_use tag so it can be run once and its output (hex_land_use) reused for subsequent runs. The raster path is stored as a parameter rather than a catalog dataset because exactextract reads the GeoTIFF directly from disk and there is no standard Kedro dataset type that loads it in the required format.

References

Passow, F., & Rajagopal, R. (2026). Identifying indicators to inform proactive substation upgrades for charging electric heavy-duty trucks. Applied Energy.

Uber Technologies. H3: Hierarchical Hexagonal Geospatial Indexing System. https://h3geo.org/

Data Axle USA. Business listings database. U.S. DOT FHWA. Jason’s Law Truck Parking Survey. Homeland Infrastructure Foundation-Level Data (HIFLD). Electric substations. USGS National Land Cover Database (NLCD).

laurel.pipelines.describe_locations.nodes.apply_groups(hexes, groups, params)[source]

Merge freight-activity-class labels onto a hexagon feature table.

Parameters:
  • hexes (DataFrame) – Per-hexagon feature DataFrame indexed by hex ID.

  • groups (DataFrame) – Freight-activity-class labels indexed by hex ID (output of group_hexes).

  • params (dict) –

    Pipeline parameters dict with keys:

    • hex_col (str): name of the shared hexagon index.

Return type:

DataFrame

Returns:

The hexes DataFrame with the freight-activity-class column from groups merged in on the shared hex index.

Raises:

AssertionError – If either hexes or groups does not have params["hex_col"] as its index name.

laurel.pipelines.describe_locations.nodes.build_land_use_areas(govt, highways, urban, params)[source]

Construct three mutually exclusive land-use area polygons: urban, highway, and rural.

The three areas are:

  • urban: the union of all Census urban-area polygons.

  • highway: the highway-corridor polygon union, minus the urban areas.

  • rural: the full state extent minus both urban and highway areas.

Parameters:
  • govt (GeoDataFrame) – State boundary GeoDataFrame used to define the full analysis extent.

  • highways (GeoDataFrame) – Highway-corridor polygons (output of format_highways).

  • urban (GeoDataFrame) – Urban-area polygons (output of format_urban).

  • params (dict) –

    Pipeline parameters dict with keys:

    • crs (str | CRS): common CRS for all overlay operations.

    • highway_buffer_miles (float): additional buffer applied to the already-buffered highway polygons (set to 0 if not needed).

    • land_use_col (str): name of the land-use category column in the output.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame with three rows (urban, highway, rural) and a land_use_col column, in params["crs"].

laurel.pipelines.describe_locations.nodes.build_remainder_polys(points, poly_mask, buff_dist, id_col)[source]

Construct Voronoi-based territory polygons for point substations outside the ICA coverage area.

This function fills in the substation territory map for all point substations that are not already covered by a polygon from poly_mask. The algorithm proceeds in seven stages:

  1. Filter: Retain only point substations whose location does not intersect the union of poly_mask polygons.

  2. Voronoi: Compute Voronoi polygons for the remaining point locations.

  3. Difference: Subtract the poly_mask union from each Voronoi polygon so that known ICA territories take precedence.

  4. Explode: Split multi-part polygons into individual shards and flag shards that do not contain their originating substation point as “disconnected” (these are Voronoi islands).

  5. Compact donors: Buffer disconnected shards by buff_dist and dissolve nearby ones together to reduce the number of donor groups.

  6. Find acceptors: For each compacted donor group, find the nearest connected (“acceptor”) territory by spatial join and centroid proximity.

  7. Dissolve: Merge each donor shard into its chosen acceptor territory.

Parameters:
  • points (GeoDataFrame) – GeoDataFrame of point-substation locations; must be in the same CRS as poly_mask.

  • poly_mask (GeoSeries) – GeoSeries of existing territory polygons to subtract from the Voronoi result.

  • buff_dist (float) – Buffer distance (in the projected CRS units, typically metres) used to compact nearby disconnected shards.

  • id_col (str) – Column name in points containing the unique substation ID.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame with one row per substation territory, matching the column schema of points.

Raises:

ValueError – If points.crs does not match poly_mask.crs.

laurel.pipelines.describe_locations.nodes.clip_to_extent(gdf, extent, params)[source]

Clip a geometry layer to the extent of a reference layer via polygon intersection.

Reprojects both layers to params["crs"], performs an overlay intersection, drops empty or null geometries, and dissolves on all non-geometry attribute columns to merge adjacent fragments that share the same attributes.

Parameters:
  • gdf (GeoDataFrame) – Input GeoDataFrame to clip.

  • extent (GeoDataFrame) – Reference GeoDataFrame whose union defines the clip boundary.

  • params (dict) –

    Pipeline parameters dict with keys:

    • crs (str | CRS): CRS for the overlay operation.

Return type:

DataFrame

Returns:

A pd.DataFrame (GeoDataFrame) clipped to the extent and dissolved on all attribute columns.

laurel.pipelines.describe_locations.nodes.collapse_naics_classes(estabs, naics_leaves, params)[source]

Map 8-digit NAICS codes to the smaller set of leaf classes used for clustering.

Each raw NAICS code is mapped to the most specific leaf in naics_leaves that is a prefix of the raw code. Codes with no matching leaf are assigned params["fill_leaf"]. This reduces the dimensionality of the employment embedding while preserving the distinctions most relevant to freight activity.

Parameters:
  • estabs (GeoDataFrame) – Establishment GeoDataFrame with an 8-digit NAICS column.

  • naics_leaves (DataFrame) – DataFrame of allowed leaf NAICS codes.

  • params (dict) –

    Pipeline parameters dict with keys:

    • naics_cols (dict): sub-keys raw (input column) and out (output column) and leaf (column in naics_leaves containing the allowed codes).

    • fill_leaf (int): leaf code to use when no match is found.

Return type:

GeoDataFrame

Returns:

The establishment GeoDataFrame with params["naics_cols"]["out"] added as an integer column.

laurel.pipelines.describe_locations.nodes.concat_columns(*args)[source]

Outer-join multiple per-hexagon DataFrames on their shared hex index.

Parameters:

*args (list[DataFrame]) – Two or more pd.DataFrame objects sharing the same hex-ID index.

Return type:

DataFrame

Returns:

A single pd.DataFrame with all columns from all inputs, joined on the shared index with join="outer" (hexagons present in any input are retained).

laurel.pipelines.describe_locations.nodes.concat_extra_estabs(*args)[source]

Concatenate supplementary establishment GeoDataFrames from multiple sources.

Parameters:

*args (list[GeoDataFrame]) – Two or more gpd.GeoDataFrame objects sharing the same column schema.

Return type:

GeoDataFrame

Returns:

A single gpd.GeoDataFrame with all rows concatenated and a fresh integer index.

laurel.pipelines.describe_locations.nodes.describe_substation_usage(profs, subs, params)[source]

Join baseload profiles and rated capacities to compute hourly available headroom.

Merges the characteristic-day profile onto the substation capacity table and computes cap_avail_mw = rating_mw - baseload_mw for each hour.

Parameters:
  • profs (DataFrame) – Characteristic-day baseload profiles (output of format_substation_profiles).

  • subs (GeoDataFrame) – Substation GeoDataFrame with rated capacity.

  • params (dict) –

    Pipeline parameters dict with keys:

    • drop_substation_cols (list[str]): columns to drop from subs before merging.

    • columns (dict): sub-keys substation_id, hour, rating_mw, baseload_mw, cap_avail_mw.

Return type:

DataFrame

Returns:

A pd.DataFrame indexed by substation_id, sorted by hour, with columns for baseload, capacity, and available headroom (all MW).

laurel.pipelines.describe_locations.nodes.fill_missingness(df, params)[source]

Drop rows with required missing values and fill optional missing values.

Applies two sequential operations: first drops all rows with NaN in drop_na_cols (required columns); then fills NaN in fill_na_vals columns with configured constants. Handles CategoricalDtype columns correctly by adding the fill value to the category list before filling.

Parameters:
  • df (DataFrame) – Input DataFrame, typically the concatenated hexagon feature table.

  • params (dict) –

    Pipeline parameters dict with keys:

    • drop_na_cols (list[str] | None): columns whose NaN rows should be dropped entirely.

    • fill_na_vals (dict[str, any] | None): mapping from column name to fill value for optional NaN imputation.

Return type:

DataFrame

Returns:

A pd.DataFrame with required NaN rows removed and optional NaN values filled.

laurel.pipelines.describe_locations.nodes.fill_out_substations(poly_subs, point_subs, params)[source]

Build complete substation territory coverage by supplementing ICA polygons with Voronoi polygons.

Calls build_remainder_polys to create Voronoi-based territory polygons for HIFLD point substations that fall outside the PG&E ICA polygon coverage area, then concatenates both polygon sets into a single unified GeoDataFrame.

Parameters:
  • poly_subs (GeoDataFrame) – ICA polygon substations (output of format_substation_boundaries_pg_and_e).

  • point_subs (GeoDataFrame) – HIFLD point substations (output of format_substations_contin).

  • params (dict) –

    Pipeline parameters dict with keys:

    • columns (dict): sub-keys substation_id and source.

    • proj_crs (str | CRS): projected CRS for distance operations.

    • buff_dist_meters (float): buffer distance used to compact disconnected Voronoi shards.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame covering all substation territories, with a composite {substation_id}_{source} column indexing each territory.

laurel.pipelines.describe_locations.nodes.format_estabs(estabs_core, estabs_geo, estabs_rels, params)[source]

Merge Data Axle establishment core, geo, and relationship tables into a single GeoDataFrame.

The three raw Data Axle tables are joined on the establishment ID: estabs_core contains NAICS codes and employment; estabs_geo contains latitude/longitude; estabs_rels contains parent-company relationships. Establishments missing geographic coordinates are dropped. H3 resolution-8 hex IDs are computed from the coordinates.

Parameters:
  • estabs_core (DataFrame) – Dask DataFrame of establishment business attributes.

  • estabs_geo (DataFrame) – Dask DataFrame of establishment geographic coordinates.

  • estabs_rels (DataFrame) – Dask DataFrame of parent-company relationship records.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names, applied to all three tables.

    • default_naics (int): fallback NAICS code for establishments with a missing code.

    • calculated_keep_cols (list[str]): computed columns to retain (e.g., "hex_id", "geometry").

Return type:

GeoDataFrame

Returns:

A Dask GeoDataFrame with one row per establishment, point geometry, H3 hex-ID column, and the columns specified by col_renamer plus calculated_keep_cols.

laurel.pipelines.describe_locations.nodes.format_extra_estabs(estabs, params)[source]

Reproject, compute H3 hex IDs, and deduplicate supplementary establishment records.

Converts polygon geometries to centroids in a projected CRS, reprojects to the H3 geographic CRS, computes H3 resolution-8 hex IDs, and drops duplicate (hex, NAICS, name) combinations.

Parameters:
  • estabs (GeoDataFrame) – Concatenated supplementary establishment GeoDataFrame (output of concat_extra_estabs).

  • params (dict) –

    Pipeline parameters dict with keys:

    • proj_crs (str | CRS): projected CRS for centroid computation.

    • hex_col (str): output H3 hex-ID column name.

    • naics_col (str): NAICS column name (used for deduplication).

    • name_col (str): name column name (used for deduplication).

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame of establishments with uint64 H3 hex IDs and duplicates removed.

laurel.pipelines.describe_locations.nodes.format_highways(highways, params)[source]

Dissolve and buffer highway polylines into corridor polygons.

Dissolves all highway segments sharing the same dissolve_cols values into a single geometry, reprojects to a projected CRS for accurate buffering, applies a mile-radius buffer, then reprojects back to the original CRS.

Parameters:
  • highways (GeoDataFrame) – Raw highway polyline GeoDataFrame.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

    • dissolve_cols (list[str]): columns to dissolve on.

    • highway_buffer_miles (float): buffer radius in miles.

    • buff_crs (str | CRS): projected CRS for buffering.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame of highway-corridor polygons in the original CRS.

laurel.pipelines.describe_locations.nodes.format_states(states, params)[source]

Rename columns in the U.S. state polygons layer to internal names.

Parameters:
  • states (GeoDataFrame) – Raw state-boundary GeoDataFrame.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

Return type:

GeoDataFrame

Returns:

The GeoDataFrame with columns renamed.

laurel.pipelines.describe_locations.nodes.format_substation_boundaries_pg_and_e(infra, params)[source]

Aggregate transformer bank ratings to substation level for PG&E ICA data.

The PG&E ICA dataset provides one polygon per transformer bank; this function dissolves them to one polygon per substation, summing the MW ratings, and adds source and state metadata columns.

Parameters:
  • infra (GeoDataFrame) – Raw PG&E ICA GeoDataFrame with one row per transformer bank.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names (e.g., renaming the capacity and ID columns).

    • add_state_col (dict): name and value for a static state-name column to add.

    • add_source_col (dict): name and value for a static data-source column to add.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame indexed by substation_id with dissolved territory polygons, summed ratings, and metadata columns.

laurel.pipelines.describe_locations.nodes.format_substation_profiles(profs, params)[source]

Collapse hourly-by-month PG&E baseload profiles to a characteristic 24-hour day.

The raw PG&E ICA data encodes load as a month_hour string (e.g., "1_14" for January hour 14). This function splits the combined column, aggregates to the maximum baseload for each (substation, hour) combination across all months, and derives the substation’s peak baseload across all hours.

Parameters:
  • profs (DataFrame) – Raw PG&E ICA baseload profile DataFrame with a combined month_hour string column.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

    • columns (dict): sub-keys month_hour, month, hour, substation_id, baseload (kW column name).

Return type:

DataFrame

Returns:

A pd.DataFrame indexed by substation_id with columns hour, max_base_by_hour_mw (max baseload for that hour, in MW), and max_base_mw (peak baseload across all hours, in MW).

laurel.pipelines.describe_locations.nodes.format_substations_contin(subs, params)[source]

Standardise the HIFLD continental substation GeoDataFrame for territory construction.

Parameters:
  • subs (GeoDataFrame) – Raw HIFLD substation point GeoDataFrame.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

    • add_source_col (dict): name and value for a static data-source column.

    • keep_cols (list[str]): columns to retain.

Return type:

GeoDataFrame

Returns:

A standardised gpd.GeoDataFrame with integer substation_id, a source label, and only the required columns retained.

laurel.pipelines.describe_locations.nodes.format_urban(urban, params)[source]

Rename columns in the urban-areas polygons layer to internal names.

Parameters:
  • urban (GeoDataFrame) – Raw Census urban-area GeoDataFrame.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

Return type:

GeoDataFrame

Returns:

The GeoDataFrame with columns renamed.

laurel.pipelines.describe_locations.nodes.get_osm_estabs_truck_stops(osm_path, params)[source]

Extract truck-stop fuel stations from an OSM PBF file by name pattern.

Filters OSM nodes/ways that have a name tag, are tagged as amenity=fuel, and whose name matches params["tag_regex"] (e.g., "(?i)truck stop|travel center").

Parameters:
  • osm_path (str) – Path to the OSM PBF input file, supplied by the osm_north_america catalog entry. Read directly by osmium rather than loaded through the catalog, because the file is far too large to hold in memory and osmium streams it.

  • params (dict) –

    Pipeline parameters dict with keys:

    • naics_code (int): NAICS code to assign to all extracted records.

    • tag_regex (str): regular-expression pattern matched against the OSM name tag.

    • naics_col (str): output column name for the NAICS code.

    • temp_path (str): path for the filtered intermediate PBF written during OSM parsing. Distinct per node so two OSM nodes cannot overwrite each other’s intermediate.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame of matching OSM establishments with a naics_col column added.

laurel.pipelines.describe_locations.nodes.get_osm_estabs_warehouses(osm_path, params)[source]

Extract warehouse and distribution-centre candidates from an OSM PBF file by name pattern.

Filters OSM nodes/ways that have a name tag and whose name matches params["tag_regex"] (e.g., "(?i)warehouse|distribution"). Records tagged as amenity=social_facility are excluded as false positives.

Parameters:
  • osm_path (str) – Path to the OSM PBF input file, supplied by the osm_north_america catalog entry. Read directly by osmium rather than loaded through the catalog, because the file is far too large to hold in memory and osmium streams it.

  • params (dict) –

    Pipeline parameters dict with keys:

    • naics_code (int): NAICS code to assign to all extracted records.

    • tag_regex (str): regular-expression pattern matched against the OSM name tag.

    • naics_col (str): output column name for the NAICS code.

    • temp_path (str): path for the filtered intermediate PBF written during OSM parsing. Distinct per node so two OSM nodes cannot overwrite each other’s intermediate.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame of matching OSM establishments (excluding social facilities) with a naics_col column added.

laurel.pipelines.describe_locations.nodes.group_hexes(land_use, estabs, params)[source]

Assign each hexagon a freight-activity class using development thresholds, rules, and K-Means.

The assignment algorithm proceeds in five stages:

  1. Development threshold: Only hexagons with at least params["development"]["frac_thresh"] developed land cover are considered “developed” and are eligible for freight-class assignment. All others receive the label "undeveloped".

  2. Neighbor embedding: Each developed hexagon’s employment vector is augmented with the summed employment of its ring-1 H3 neighbors (include_center=False, distance=1), capturing the surrounding land-use context.

  3. Special establishment detection: Hexagons containing (or adjacent to) any establishment with a NAICS code listed in params["special_naics"] are labelled with that special class (e.g., "truck_stop"). Special labels take precedence over cluster-assigned labels.

  4. K-Means clustering: The remaining developed hexagons with at least one freight-intensive NAICS establishment are clustered using K-Means on the log1p-transformed sparse employment matrix. Hexagons with no freight-intensive establishments receive the label "some_estabs" or "no_estabs".

  5. Output: All labels (including "undeveloped") are consolidated into a single categorical column.

Parameters:
  • land_use (DataFrame) – Per-hexagon land-use-group fraction matrix (output of pivot_hex_land_use).

  • estabs (DataFrame) – Per-hexagon employment embedding matrix (output of pivot_hex_estabs).

  • params (dict) –

    Pipeline parameters dict with keys:

    • development (dict): col (developed land-cover column) and frac_thresh (minimum fraction threshold).

    • naics_prefix (str): prefix identifying NAICS columns.

    • default_naics (int): NAICS code used as the “no freight industry” category (excluded from freight-intensity tests).

    • special_naics (dict[str, int]): mapping from label to NAICS code for special establishment categories.

    • loc_group_col (str): output freight-activity-class column name.

    • clusterer_kwargs (dict): keyword arguments forwarded to sklearn.cluster.KMeans.

Return type:

DataFrame

Returns:

A pd.DataFrame indexed by hexagon ID with a single categorical column params["loc_group_col"] containing the freight-activity class for each hexagon.

laurel.pipelines.describe_locations.nodes.hexify_polygons(gdf, params)[source]

Convert polygon geometries to an H3 hexagon index by assigning hex IDs to overlapping hexes.

For each polygon in gdf, all H3 resolution-8 hexagons that overlap the polygon are identified and assigned the polygon’s attribute values. When n_partitions > 1, the operation is parallelised via Dask GeoDataFrame; otherwise it runs in-process.

Parameters:
  • gdf (GeoDataFrame) – GeoDataFrame of polygons, each with attribute columns to carry forward to the hex index.

  • params (dict) –

    Pipeline parameters dict with keys:

    • hex_col (str): name of the H3 hex-ID output column.

    • n_partitions (int): number of Dask partitions (1 = no Dask).

Return type:

DataFrame

Returns:

A pd.DataFrame indexed by H3 hex ID (uint64), with one row per unique hexagon and the polygon attribute columns attached. Duplicate hexagons (where a hexagon intersects multiple polygons) are dropped, keeping the first occurrence.

laurel.pipelines.describe_locations.nodes.partition_hex_corresp(hex_base, params)[source]

Split the hexagon base correspondence table into Dask parquet partitions.

Converts the pandas feather hex_base_corresp to a Dask DataFrame so that the downstream read_land_use() node can extract raster values in parallel, one partition per worker. The index is sorted before partitioning so that each partition covers a contiguous range of uint64 hex IDs.

Parameters:
  • hex_base (DataFrame) – Pandas DataFrame of hexagon base correspondence data, indexed by uint64 hex ID.

  • params (dict) –

    Pipeline parameters dict with key:

    • n_partitions (int): number of Dask partitions to create. Controls parallelism during raster extraction.

Return type:

DataFrame

Returns:

Dask DataFrame with the same schema as hex_base, split into n_partitions partitions and saved as hex_base_corresp_dask.

laurel.pipelines.describe_locations.nodes.pivot_hex_estabs(estabs, params)[source]

Build a per-hexagon employment embedding matrix from the establishment dataset.

For each hexagon, computes the total employment in each NAICS leaf class across all establishments in that hexagon. Establishments reporting zero employees are filled with the median employment for that NAICS class, then offset by params["zero_emp_buff"] to avoid log-zero issues downstream.

Column names for NAICS codes are prefixed with params["naics_prefix"] and sorted lexically to ensure stable column ordering across pipeline runs.

Parameters:
  • estabs (GeoDataFrame) – Establishment GeoDataFrame with leaf NAICS codes and employment (output of collapse_naics_classes).

  • params (dict) –

    Pipeline parameters dict with keys:

    • columns (dict): sub-keys hex_id, naics, n_employees, geom.

    • naics_prefix (str): string prefix for NAICS column names (e.g., "naics_").

    • zero_emp_buff (float): small constant added to all employee counts after median-fill to prevent exact zeros.

    • keep_metadata_cols (list[str]): non-NAICS attribute columns to retain in the output (e.g., substation territory ID).

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame indexed by hexagon ID with one column per NAICS leaf class (integer employment totals) plus metadata columns.

laurel.pipelines.describe_locations.nodes.pivot_hex_land_use(land_use, params)[source]

Pivot the per-hexagon NLCD land-use coverage table from long to wide format.

Maps fine-grained NLCD category codes to broader land-use groups using params["code_group_corresp"], then aggregates fractional coverage by (hexagon, group) and unstacks to produce one column per land-use group.

The hexagon index is cast to uint64 to match the dtype used elsewhere in the pipeline.

Parameters:
  • land_use (DataFrame) – Dask DataFrame of NLCD coverage fractions in long format, indexed by hexagon ID string.

  • params (dict) –

    Pipeline parameters dict with keys:

    • input_cols (dict): sub-keys categories (NLCD code column), fractions (coverage fraction column), hex (hexagon index name after renaming).

    • code_group_corresp (dict[str, str]): mapping from NLCD code to land-use group label.

Return type:

DataFrame

Returns:

A pd.DataFrame indexed by hexagon ID (uint64) with one column per land-use group, filled with 0.0 for groups with no coverage.

laurel.pipelines.describe_locations.nodes.prepare_shared_locations(shared, params)[source]

Format shared truck-stop charger locations with a standardised location-type label.

Parameters:
  • shared (GeoDataFrame) – Raw truck-stop GeoDataFrame (e.g., Jason’s Law locations already assigned H3 hex IDs).

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

    • loc_col (str): output column name for the location type.

    • shared_location_type (str): categorical value assigned to all rows (e.g., "truck_stop").

Return type:

DataFrame

Returns:

A pd.DataFrame with a categorical loc_col column.

laurel.pipelines.describe_locations.nodes.prepare_stop_locations_public(parks, params)[source]

Format Jason’s Law truck-stop locations as establishment records.

Assigns the truck-stop NAICS code and renames columns so that these public data records can be concatenated with the Data Axle establishment dataset.

Parameters:
  • parks (GeoDataFrame) – Jason’s Law truck-stop GeoDataFrame.

  • params (dict) –

    Pipeline parameters dict with keys:

    • col_renamer (dict[str, str]): mapping from raw to internal column names.

    • columns (dict): sub-key naics for the NAICS column name.

    • naics_code (int): NAICS code to assign to all records.

    • keep_cols (list[str]): columns to retain.

Return type:

GeoDataFrame

Returns:

A gpd.GeoDataFrame ready to be concatenated with format_estabs output.

laurel.pipelines.describe_locations.nodes.read_land_use(hex_base_corresp, params)[source]

Extract NLCD 2023 land cover fractions for every H3 hexagon using exactextract.

Reads the NLCD land cover raster directly via exactextract, computing for each hexagon polygon the fraction of its area covered by each NLCD category code. The result is in long format: one row per (hexagon × category) pair.

The algorithm proceeds in three steps:

  1. Attach polygon geometries to each partition of hex_base_corresp using add_geometries(), producing a Dask GeoDataFrame.

  2. Reproject each partition to the raster’s native CRS (read once from the file header) so that fractional areas are computed in the correct coordinate space.

  3. Call _extract_land_use_part() via map_partitions, passing the raster path directly so exactextract handles file I/O per partition.

Note

This node is I/O-intensive and may take several hours to complete on a single machine. Run it once with kedro run --pipeline=describe_locations --tags=read_land_use and rely on the cached hex_land_use parquet output for subsequent runs.

The index of the output is a string representation of the uint64 hex ID ({hex_col}_str). The downstream pivot_hex_land_use() node casts it back to uint64 and renames it to {hex_col}.

Parameters:
  • hex_base_corresp (DataFrame) – Dask DataFrame of hexagon correspondence data, indexed by uint64 hex ID. Loaded from hex_base_corresp_dask in the catalog.

  • params (dict) –

    Pipeline parameters dict with keys:

    • raster_path (str): path to the NLCD 2023 GeoTIFF, relative to the Kedro project root. Passed directly to exactextract rather than loaded through the Kedro catalog because there is no standard dataset type that reads a GeoTIFF into the format exactextract requires.

    • hex_col (str): name of the hex ID column / index; used to derive the string column {hex_col}_str that becomes the output index.

Returns:

  • unique (int32): NLCD land cover category code.

  • frac (float64): fraction of hexagon area covered by that category.

Return type:

Dask DataFrame indexed by {hex_col}_str (object dtype) with columns

laurel.pipelines.describe_locations.nodes.reassign_hqs(estabs, params)[source]

Reassign corporate-HQ establishments to a HQ-specific NAICS code to prevent misclassification.

A corporate headquarters may be coded with its parent company’s primary NAICS (e.g., a trucking-company HQ coded as a freight terminal), which would cause the clustering step to treat the HQ building as a large freight facility. This function identifies likely HQ records using a combination of employment ratio, business-status code, number of sibling establishments, and NAICS window, then overrides their NAICS code with params["hq_naics"].

The heuristic is intentionally conservative (large employee ratio, specific HQ business codes, many sibling establishments) to avoid reassigning genuine large freight terminals.

Parameters:
  • estabs (GeoDataFrame) – Establishment GeoDataFrame with NAICS, employment, business- status, and parent-ID columns.

  • params (dict) –

    Pipeline parameters dict with keys:

    • columns (dict): sub-keys parent_id, naics, n_employees, buss_status, estab_id.

    • emp_ratio_min (float): minimum (establishment_employees / median_sibling_employees) ratio for HQ classification.

    • hq_bus_codes (list): business-status code values indicating an HQ.

    • n_estabs_big (int): minimum number of sibling establishments required.

    • naics_window (dict): lower and upper NAICS code bounds for the window within which HQ reassignment is applied.

    • hq_naics (int): the NAICS code to assign to identified HQ establishments.

Return type:

GeoDataFrame

Returns:

The establishment GeoDataFrame with HQ NAICS codes overridden.

laurel.pipelines.describe_locations.pipeline module

Kedro pipeline definition for the describe_locations pipeline.

Wires the nodes from laurel.pipelines.describe_locations.nodes into a single Pipeline object. For full documentation of each node’s inputs, outputs, and algorithm, see laurel.pipelines.describe_locations.nodes.

Sub-pipelines / tags

  • california_substations — aggregates PG&E ICA transformer banks to substation level and builds characteristic baseload profiles.

  • continental_substations — formats HIFLD point substations and fills territory gaps with Voronoi polygons.

  • format_states / format_highways / format_urban_areas — standardise and buffer the state, highway, and urban-area vector layers.

  • polys_to_hexes — hexifies states, highways, urban areas, and substation territories to H3 resolution-8 grids (one namespaced sub- pipeline per layer).

  • extra_estabs — extracts supplementary establishment records from Jason’s Law data and OpenStreetMap (truck stops, warehouses).

  • read_land_use — partitions the hexagon base table for Dask and extracts NLCD 2023 land cover fractions for every H3 hexagon from the raw GeoTIFF using exactextract. This step is I/O-intensive and may take several hours; run it once and rely on the cached output for subsequent runs.

  • establishments — merges Data Axle tables, reassigns HQ NAICS codes, collapses to leaf classes, pivots to a per-hexagon employment matrix, and assigns freight-activity classes via K-Means clustering.

To visualise the node graph interactively, run:

kedro viz run

then open http://localhost:4141 in a browser and select describe_locations from the pipeline dropdown.

laurel.pipelines.describe_locations.pipeline.create_pipeline(**kwargs)[source]
Return type:

Pipeline

Module contents

This is a boilerplate pipeline ‘describe_locations’ generated using Kedro 0.19.3

laurel.pipelines.describe_locations.create_pipeline(**kwargs)[source]
Return type:

Pipeline