aperta.osm_helpers¶
Helpers for downloading and categorizing OpenStreetMap data via osmnx.
Two concerns, separated:
Categorized POI downloads — accessibility analyses typically classify OSM features into user-defined categories (groceries, schools, …) that may bundle multiple OSM tag:value pairs, optionally with per-pair weights (a convenience shop might count as 0.5 of a supermarket for the groceries category). fetch_pois automates the full pipeline: build the OSM tag query from the category map, download via osmnx, tag each feature with per-category count + weighted-count columns, drop features matching no category.
Network downloads — fetch_network wraps osmnx.graph_from_polygon + osmnx.project_graph so the standard “fetch in EPSG:4326, project to a metric CRS” pattern is one line.
Categorization without download (when POIs come from another source — cached, a different provider, etc.) is also exposed as categorize_pois.
osmnx is an optional dependency (aperta[osm] or aperta[examples]); calls to fetch_* import it lazily and raise a helpful error if missing.
Example category map:
POI_CATEGORIES = {
'groceries': [
('shop:supermarket', 1.0),
('shop:convenience', 0.5),
('shop:bakery', 0.5),
],
'education_school': [('amenity:school', 1.0)],
'transit_rail': [('railway:station', 3), ('railway:halt', 1)],
}
pois = osm_helpers.fetch_pois(
polygon=dest_polygon, polygon_crs='EPSG:2056',
category_map=POI_CATEGORIES, target_crs='EPSG:2056',
)
# pois now has columns: geometry, name, amenity, shop, railway, ...
# plus per-category columns: groceries, groceries_weight,
# education_school, education_school_weight, transit_rail, transit_rail_weight.
- aperta.osm_helpers.osm_tag_query_for_categories(category_map)[source]¶
Build the osmnx tags= argument from a category map.
Unions every key:value pair across all categories and groups by key. The result is the minimal query that returns every feature any category could match.
- Parameters:
category_map (dict[str, list[tuple[str, float]]]) – {user_category -> [(tag_pair, weight), …]} where tag_pair is a ‘key:value’ string.
- Returns:
{osm_tag_key -> [values, …]} with values sorted (deterministic for caching / hashing). Pass directly as the tags argument to osmnx.features_from_polygon / osmnx.features_from_place.
- Return type:
- aperta.osm_helpers.categorize_pois(pois, category_map, *, weight_suffix='_weight', drop_unmatched=True)[source]¶
Add per-category count + weighted-count columns to a POI GeoDataFrame.
For each (category, [(tag:value, weight), …]) entry, two new columns are appended:
`{category}` (int): number of listed (tag:value) pairs this row matches. Usually 0 or 1; can be ≥ 2 if multiple of the listed pairs match for one feature (a feature with both amenity=school and school=primary for a schools category that lists both).
`{category}{weight_suffix}` (float): sum of weights across all matching pairs. Equal to the count if every weight is 1.
Features matching no category at all are dropped if drop_unmatched=True (the typical case — saves carrying around OSM features that aren’t of interest).
- Parameters:
pois (GeoDataFrame) – GeoDataFrame containing the OSM tag columns referenced by category_map (e.g. amenity, shop, leisure). Tags missing from the DataFrame are silently treated as never-matching, so partial input works.
category_map (dict[str, list[tuple[str, float]]]) – {category -> [(tag:value, weight), …]}.
weight_suffix (str) – suffix for the per-category weight column. Default ‘_weight’. Set to e.g. ‘_w’ for shorter columns.
drop_unmatched (bool) – drop rows matching no listed (tag:value) pair. Default True.
- Returns:
A copy of pois with two new columns per category. Original columns + index preserved.
- Return type:
- aperta.osm_helpers.fetch_pois(polygon, polygon_crs, category_map, *, target_crs=None, use_centroid=True, weight_suffix='_weight', drop_unmatched=True)[source]¶
Fetch OSM POIs within polygon and tag them with category columns.
- End-to-end pipeline:
Reproject polygon to EPSG:4326 for the osmnx query.
Build the OSM tag query via osm_tag_query_for_categories.
Call osmnx.features_from_polygon.
Drop non-Point/Polygon geometries (lines, etc.).
(Optional) reduce polygon footprints to point centroids.
(Optional) reproject to target_crs.
Categorize via categorize_pois.
- Parameters:
polygon (BaseGeometry) – shapely polygon (or multipolygon) describing the fetch area.
polygon_crs (str) – CRS of polygon (e.g. ‘EPSG:2056’).
category_map (dict[str, list[tuple[str, float]]]) – {category -> [(tag:value, weight), …]}.
target_crs (str | None) – optional CRS to project the result to. None keeps the EPSG:4326 of the OSM source.
use_centroid (bool) – reduce polygon footprints to point centroids. Centroid is computed in target_crs if given (geometrically meaningful) else in EPSG:4326 (warns).
weight_suffix (str) – as in categorize_pois.
drop_unmatched (bool) – as in categorize_pois.
- Returns:
GeoDataFrame indexed by OSM ID with the original OSM tag columns plus per-category count + weight columns.
- Return type:
- aperta.osm_helpers.fetch_network(polygon, polygon_crs, network_type, *, target_crs=None, simplify=True)[source]¶
Fetch an OSM network within polygon and project to target_crs.
One-line wrapper around osmnx.graph_from_polygon + project_graph. Removes the standard reproject-polygon-to-4326, fetch, reproject-graph boilerplate.
- Parameters:
polygon (BaseGeometry) – shapely polygon describing the fetch area.
polygon_crs (str) – CRS of polygon.
network_type (str) – passed through to osmnx.graph_from_polygon — ‘walk’, ‘bike’, ‘drive’, ‘all’, ‘all_public’, etc.
target_crs (str | None) – optional CRS to project the resulting graph to. None keeps EPSG:4326.
simplify (bool) – passed through. True (default) consolidates degree-2 nodes — usually what you want for routing.
- Returns:
networkx.MultiDiGraph. Nodes carry x / y attributes in target_crs (or EPSG:4326 if no target).
- Return type: