Utils¶
Helper modules used throughout the package.
Geometry (geo_polars)¶
haversine_distance_m ¶
haversine_distance_m(lat1: str | Expr, lon1: str | Expr, lat2: str | Expr, lon2: str | Expr) -> pl.Expr
Builds a polars expression computing great-circle distance in meters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lat1
|
str | Expr
|
Column name or expression for the first point's latitude (degrees). |
required |
lon1
|
str | Expr
|
Column name or expression for the first point's longitude (degrees). |
required |
lat2
|
str | Expr
|
Column name or expression for the second point's latitude (degrees). |
required |
lon2
|
str | Expr
|
Column name or expression for the second point's longitude (degrees). |
required |
Returns:
| Type | Description |
|---|---|
Expr
|
pl.Expr: Distance in meters between the two points, evaluated |
Expr
|
row-wise via the standard haversine formula. |
Source code in pyGTFSHandler/utils/geo_polars.py
degrees_per_meter ¶
Returns (degrees-latitude-per-meter, degrees-longitude-per-meter) at a given latitude, for sizing a lon/lat grid cell to a real-world distance.
Used to bucket points into grid cells approximately cell_size_m wide,
so nearby-candidate-pair search only needs to compare each point against
the ~9 cells around it instead of an all-pairs cross join. It is
intentionally an approximation (a flat local reference frame around the
given latitude) -- fine for the modest cell sizes clustering uses (tens
to low hundreds of meters); actual distances between candidate pairs are
still verified with the exact haversine_distance_m above.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
latitude_deg
|
float
|
Latitude (degrees) to approximate the local scale at. |
required |
Returns:
| Type | Description |
|---|---|
float
|
tuple[float, float]: (degrees latitude per meter, degrees longitude |
float
|
per meter). |
Source code in pyGTFSHandler/utils/geo_polars.py
mean_angle ¶
Compute the mean angle in degrees of a column of angles.
Source code in pyGTFSHandler/utils/geo_polars.py
max_separation_angle ¶
Compute the maximum separation angle in a list column of angles (degrees). Returns a Series of maximum separation angles.
Source code in pyGTFSHandler/utils/geo_polars.py
connected_components_from_edges ¶
Labels each of n_nodes node indices [0, n_nodes) with its connected
component id, given an undirected edge list.
This is the one step that isn't a polars primitive -- graph connectivity
-- so it's delegated to a single vectorized scipy.sparse.csgraph call
over the whole edge list at once (not per-pair, not per-cluster), rather
than pulling in a full clustering library (e.g. scikit-learn) for what is
fundamentally just "which points are transitively within the distance
threshold of one another."
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_nodes
|
int
|
Total number of nodes (points) being clustered. |
required |
edge_index_pairs
|
list[tuple[int, int]]
|
List of |
required |
Returns:
| Type | Description |
|---|---|
list[int]
|
list[int]: Component label for each node index |
Source code in pyGTFSHandler/utils/geo_polars.py
grid_cell_columns ¶
grid_cell_columns(lat_col: str, lon_col: str, cell_size_m: float, reference_latitude_deg: float) -> list[pl.Expr]
Returns [cell_lat, cell_lon] integer-bucket expressions for lat_col/lon_col.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lat_col
|
str
|
Name of the latitude column. |
required |
lon_col
|
str
|
Name of the longitude column. |
required |
cell_size_m
|
float
|
Desired grid cell size, in meters. |
required |
reference_latitude_deg
|
float
|
Latitude used to approximate the local
meters-per-degree scale (see |
required |
Returns:
| Type | Description |
|---|---|
list[Expr]
|
list[pl.Expr]: Two expressions aliased |
Source code in pyGTFSHandler/utils/geo_polars.py
filter_by_id_column ¶
filter_by_id_column(lf: Optional[LazyFrame], column: str, ids: list | None = []) -> Optional[pl.LazyFrame]
Semi-joins lf down to rows whose column value is in ids.
IDs are suffixed with "_<original_value>_file_<n>" only when more than
one GTFS directory was loaded together (see io.read_csv_list), to keep
ids that collide across the loaded feeds distinct; a single-feed load
keeps plain ids. To support both a plain id and an already-suffixed one
(e.g. re-using an id obtained from a previous multi-feed query's
output), this matches on the id with any trailing _file_<n> suffix
stripped from both sides.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lf
|
Optional[LazyFrame]
|
LazyFrame to filter, or None (returned unchanged). |
required |
column
|
str
|
Name of the id column to filter on. |
required |
ids
|
list | None
|
List of ids to keep (plain or already |
[]
|
Returns:
| Type | Description |
|---|---|
Optional[LazyFrame]
|
Optional[pl.LazyFrame]: The filtered LazyFrame, or None if |
Source code in pyGTFSHandler/utils/geo_polars.py
Time (time_parsing)¶
time_to_seconds ¶
Convert datetime/time object to seconds since midnight.
normalize_time_expr ¶
Normalizes a GTFS-style time column into a clean "HH:MM:SS" string.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
col
|
str
|
Name of the column to normalize (e.g. |
required |
Returns:
| Type | Description |
|---|---|
Expr
|
pl.Expr: |
Expr
|
used as a sentinel by callers to distinguish "malformed" from "was |
Expr
|
genuinely blank") for anything that doesn't match |
Expr
|
otherwise the zero-padded |
Expr
|
to 24, so GTFS's |
Expr
|
through this function unchanged. |
Source code in pyGTFSHandler/utils/time_parsing.py
time_displacement ¶
Source code in pyGTFSHandler/utils/time_parsing.py
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 | |
Dates (date_parsing)¶
datetime_to_days_since_epoch ¶
Convert datetime/date to number of days since 1970-01-01.
get_holidays ¶
Fetch public holidays for a country and optional subdivision.
Source code in pyGTFSHandler/utils/date_parsing.py
GTFS checking & normalization (gtfs_checker)¶
parse_date ¶
Parse a date from various formats and return an int in YYYYMMDD format.
Source code in pyGTFSHandler/utils/gtfs_checker.py
parse_time ¶
Parse a time string and return HH:MM:SS. Rules: - With colons (:): parse flexibly (e.g., "7:1" -> "07:01:00"). - Without colons: only 4 or 6 digits are allowed: 4 digits (HHMM) -> HH:MM:00 6 digits (HHMMSS) -> HH:MM:SS Supports hours up to 47.
Source code in pyGTFSHandler/utils/gtfs_checker.py
get_df_schema_dict ¶
Returns the expected Polars dtype schema and mandatory columns for a GTFS file.
Used throughout models/*.py to pass schema_overrides to
utils.io.read_csv_list/read_csv_lazy and to know which columns
check_files must validate as present/non-null.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
A GTFS filename or path (only the basename, e.g.
|
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Tuple[Dict[str, Any], List[str]]: |
List[str]
|
where |
Tuple[Dict[str, Any], List[str]]
|
|
Source code in pyGTFSHandler/utils/gtfs_checker.py
normalize_string ¶
Normalize a string:
- Converts accented characters to ASCII.
- Strips leading/trailing whitespace in all cases.
strict=True: - Lowercase - Replace spaces (and multiple consecutive spaces) with a single underscore - Keep only a-z, 0-9, and underscores - Discard all other symbols
strict=False: - Preserve case and spaces - Keep URL-safe characters: a-zA-Z0-9-_.~:/?#[]@!$&'()*+,;= - Collapse multiple spaces into a single space
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
s
|
str
|
Input string |
required |
strict
|
bool
|
Whether to apply strict rules |
True
|
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
Normalized string |
Source code in pyGTFSHandler/utils/gtfs_checker.py
normalize_df ¶
Normalize both column names and string values in a Polars LazyFrame or DataFrame.
Column names: - Lowercased, stripped of spaces and special characters.
String columns (pl.Utf8): - Strip leading/trailing whitespace. - Remove diacritical marks (accents) from characters. - Optionally preserve uppercase (used here to keep valid URL casing). - Retain all valid URL characters. - Remove invalid characters.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
lf
|
LazyFrame | DataFrame
|
Input LazyFrame or DataFrame. |
required |
Returns:
| Type | Description |
|---|---|
LazyFrame | DataFrame
|
pl.LazyFrame | pl.DataFrame: The normalized LazyFrame or DataFrame. |
Source code in pyGTFSHandler/utils/gtfs_checker.py
normalize_route_type ¶
Normalizes a route type (int code or name string) to its integer GTFS code.
Accepts either the standard/extended numeric route_type code
(returned as-is) or a human-friendly name (e.g. "bus", "subway"),
which is mapped to the corresponding standard code. Used by Feed
and Routes.load to normalize the caller-supplied route_types
filter before comparing it against routes.txt.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
route_type
|
Union[int, str]
|
An |
required |
Returns:
| Type | Description |
|---|---|
Optional[int]
|
int | None: The normalized integer route type code, or |
Optional[int]
|
|
Optional[int]
|
known name. |
Source code in pyGTFSHandler/utils/gtfs_checker.py
extended_to_standard_route_type ¶
Convert any GTFS route_type or Extended GTFS (Transmodel) route_type into a standard GTFS route_type (0–7).
Returns:
| Type | Description |
|---|---|
int | None
|
int in {0..7} or None if not mappable (either because the code is |
int | None
|
not a recognized GTFS/extended route_type at all, or because it's a |
int | None
|
recognized extended category -- e.g. Air/Taxi/Misc -- with no |
int | None
|
closer standard-GTFS analog; use |
int | None
|
to tell these two cases apart). |
Source code in pyGTFSHandler/utils/gtfs_checker.py
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | |
route_type_to_str ¶
Converts a standard GTFS route_type integer code to its lowercase name.
Inverse of the numeric side of normalize_route_type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
route_type
|
int
|
A standard GTFS route type code ( |
required |
Returns:
| Name | Type | Description |
|---|---|---|
str |
str
|
The lowercase route type name, e.g. |
Raises:
| Type | Description |
|---|---|
Exception
|
If |
Source code in pyGTFSHandler/utils/gtfs_checker.py
Geocoding¶
get_city_geometry ¶
Retrieve a place's boundary geometry from OpenStreetMap/Nominatim.
Uses geopy's Nominatim geocoder with GeoJSON polygon output, so it
needs no extra dependency beyond the geocoding optional extra
(pip install pyGTFSHandler[geocoding]) already used by the other
functions in this module.
Source code in pyGTFSHandler/utils/geocoding.py
get_geographic_suggestions_from_string ¶
get_geographic_suggestions_from_string(query: str, user_agent: str = 'MobilityDatabaseClient', max_results: int = 25) -> Dict[str, List[str]]
Suggest country codes, subdivisions, and municipalities from a query string.
Source code in pyGTFSHandler/utils/geocoding.py
get_geographic_suggestions_from_aoi ¶
get_geographic_suggestions_from_aoi(aoi: Union[Polygon, MultiPolygon, GeoDataFrame, GeoSeries], num_points: int = 1, user_agent: str = 'MobilityDatabaseClient') -> Dict[str, List[str]]
Reverse-geocode AOI geometry to suggest country, subdivision, and municipality.
Source code in pyGTFSHandler/utils/geocoding.py
get_country_region ¶
Return ISO country code and subdivision code for a lat/lon location.
Source code in pyGTFSHandler/utils/geocoding.py
Hashing / caching¶
hash_path ¶
Compute MD5 hash of a file, folder, or zip archive.
Source code in pyGTFSHandler/utils/hashing.py
compare_paths ¶
Colors¶
route_id_to_color ¶
Hashes route_id to a deterministic, varied hex color (no leading
#), by picking a hue around the color wheel and a fixed
saturation/lightness so results stay readable as marker/badge fills.
Source code in pyGTFSHandler/utils/colors.py
contrasting_text_color ¶
Returns "000000" or "ffffff", whichever contrasts better against
hex_color as a background, using WCAG-ish relative luminance.