From 6e7b77d7080bbc6ebe61f238a1dbb475263d838a Mon Sep 17 00:00:00 2001 From: nikita Date: Wed, 27 Mar 2024 16:09:49 +0300 Subject: [PATCH 1/4] added support for implicit maxspeed values --- osmnx/routing.py | 13 ++-- osmnx/settings.py | 153 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_osmnx.py | 1 + 3 files changed, 162 insertions(+), 5 deletions(-) diff --git a/osmnx/routing.py b/osmnx/routing.py index 493ecbb61..e5e111345 100644 --- a/osmnx/routing.py +++ b/osmnx/routing.py @@ -19,6 +19,7 @@ import pandas as pd from . import convert +from . import settings from . import utils if TYPE_CHECKING: @@ -493,9 +494,11 @@ def _clean_maxspeed( Clean a maxspeed string and convert mph to kph if necessary. If present, splits maxspeed on "|" (which denotes that the value contains - different speeds per lane) then aggregates the resulting values. Invalid - inputs return None. See https://wiki.openstreetmap.org/wiki/Key:maxspeed - for details on values and formats. + different speeds per lane) then aggregates the resulting values. If given + string is not a valid numeric string, tries to look up its value in + implicit maxspeed values mapping. Invalid inputs return None. See + https://wiki.openstreetmap.org/wiki/Key:maxspeed for details on values and + formats. Parameters ---------- @@ -531,8 +534,8 @@ def _clean_maxspeed( return float(agg(clean_values)) except (ValueError, AttributeError): - # if invalid input, return None - return None + # if input is not a valid numeric string, look it up in implicit speed mapping + return settings.implicit_maxspeed_values.get(maxspeed) def _collapse_multiple_maxspeed_values( diff --git a/osmnx/settings.py b/osmnx/settings.py index 248ed6d07..37856fc1f 100644 --- a/osmnx/settings.py +++ b/osmnx/settings.py @@ -120,6 +120,10 @@ retrieved from OSM. Default is `["access", "area", "bridge", "est_width", "highway", "junction", "landuse", "lanes", "maxspeed", "name", "oneway", "ref", "service", "tunnel", "width"]`. +implicit_maxspeed_values: dict + Implicit maxspeed values mapping that is used by `add_edge_speeds` + function. Default values are obtained from wiki page + https://wiki.openstreetmap.org/wiki/Key:maxspeed. """ from __future__ import annotations @@ -180,3 +184,152 @@ "tunnel", "width", ] +implicit_maxspeed_values = { + "AR:rural": 110.0, + "AR:urban": 40.0, + "AR:urban:primary": 60.0, + "AR:urban:secondary": 60.0, + "AT:bicycle_road": 30.0, + "AT:motorway": 130.0, + "AT:rural": 100.0, + "AT:trunk": 100.0, + "AT:urban": 50.0, + "BE-BRU:rural": 70.0, + "BE-BRU:urban": 30.0, + "BE-VLG:rural": 70.0, + "BE-VLG:urban": 50.0, + "BE-WAL:rural": 90.0, + "BE-WAL:urban": 50.0, + "BE:cyclestreet": 30.0, + "BE:living_street": 20.0, + "BE:motorway": 120.0, + "BE:trunk": 120.0, + "BE:zone30": 30.0, + "BG:living_street": 20.0, + "BG:motorway": 140.0, + "BG:rural": 90.0, + "BG:trunk": 120.0, + "BG:urban": 50.0, + "BY:living_street": 20.0, + "BY:motorway": 110.0, + "BY:rural": 90.0, + "BY:urban": 60.0, + "CA-AB:rural": 90.0, + "CA-AB:urban": 65.0, + "CA-BC:rural": 80.0, + "CA-BC:urban": 50.0, + "CA-MB:rural": 90.0, + "CA-MB:urban": 50.0, + "CA-ON:rural": 80.0, + "CA-ON:urban": 50.0, + "CA-QC:motorway": 100.0, + "CA-QC:rural": 75.0, + "CA-QC:urban": 50.0, + "CA-SK:nsl": 80.0, + "CH:motorway": 120.0, + "CH:rural": 80.0, + "CH:trunk": 100.0, + "CH:urban": 50.0, + "CZ:living_street": 20.0, + "CZ:motorway": 130.0, + "CZ:pedestrian_zone": 20.0, + "CZ:rural": 90.0, + "CZ:trunk": 110.0, + "CZ:urban": 50.0, + "CZ:urban_motorway": 80.0, + "CZ:urban_trunk": 80.0, + "DE:bicycle_road": 30.0, + "DE:living_street": 15.0, + "DE:motorway": 120.0, + "DE:rural": 80.0, + "DE:urban": 50.0, + "DK:motorway": 130.0, + "DK:rural": 80.0, + "DK:urban": 50.0, + "EE:rural": 90.0, + "EE:urban": 50.0, + "ES:living_street": 20.0, + "ES:motorway": 120.0, + "ES:rural": 90.0, + "ES:trunk": 90.0, + "ES:urban": 50.0, + "ES:zone30": 30.0, + "FI:motorway": 120.0, + "FI:rural": 80.0, + "FI:trunk": 100.0, + "FI:urban": 50.0, + "FR:motorway": 120.0, + "FR:rural": 80.0, + "FR:urban": 50.0, + "FR:zone30": 30.0, + "GB:nsl_restricted": 48.28, + "GR:motorway": 130.0, + "GR:rural": 90.0, + "GR:trunk": 110.0, + "GR:urban": 50.0, + "HU:living_street": 20.0, + "HU:motorway": 130.0, + "HU:rural": 90.0, + "HU:trunk": 110.0, + "HU:urban": 50.0, + "IT:motorway": 130.0, + "IT:rural": 90.0, + "IT:trunk": 110.0, + "IT:urban": 50.0, + "JP:express": 100.0, + "JP:nsl": 60.0, + "LT:rural": 90.0, + "LT:urban": 50.0, + "NO:rural": 80.0, + "NO:urban": 50.0, + "PH:express": 100.0, + "PH:rural": 80.0, + "PH:urban": 30.0, + "PT:motorway": 120.0, + "PT:rural": 90.0, + "PT:trunk": 100.0, + "PT:urban": 50.0, + "RO:motorway": 130.0, + "RO:rural": 90.0, + "RO:trunk": 100.0, + "RO:urban": 50.0, + "RS:living_street": 10.0, + "RS:motorway": 130.0, + "RS:rural": 80.0, + "RS:trunk": 100.0, + "RS:urban": 50.0, + "RU:living_street": 20.0, + "RU:motorway": 110.0, + "RU:rural": 90.0, + "RU:urban": 60.0, + "SE:rural": 70.0, + "SE:urban": 50.0, + "SI:motorway": 130.0, + "SI:rural": 90.0, + "SI:trunk": 110.0, + "SI:urban": 50.0, + "SK:living_street": 20.0, + "SK:motorway": 130.0, + "SK:motorway_urban": 90.0, + "SK:rural": 90.0, + "SK:trunk": 90.0, + "SK:urban": 50.0, + "TR:living_street": 20.0, + "TR:motorway": 130.0, + "TR:rural": 90.0, + "TR:trunk": 110.0, + "TR:urban": 50.0, + "TR:zone30": 30.0, + "UA:living_street": 20.0, + "UA:motorway": 130.0, + "UA:rural": 90.0, + "UA:trunk": 110.0, + "UA:urban": 50.0, + "UK:motorway": 112.65, + "UK:nsl_dual": 112.65, + "UK:nsl_single": 96.56, + "UZ:living_street": 30.0, + "UZ:motorway": 110.0, + "UZ:rural": 100.0, + "UZ:urban": 70.0, +} diff --git a/tests/test_osmnx.py b/tests/test_osmnx.py index 976405d3e..68bf7cf0d 100644 --- a/tests/test_osmnx.py +++ b/tests/test_osmnx.py @@ -291,6 +291,7 @@ def test_routing() -> None: assert ox.routing._clean_maxspeed("60|100 mph") == pytest.approx(128.7472) assert ox.routing._clean_maxspeed("signal") is None assert ox.routing._clean_maxspeed("100;70") is None + assert ox.routing._clean_maxspeed("FR:urban") == 50.0 # test collapsing multiple mph values to single kph value assert ox.routing._collapse_multiple_maxspeed_values(["25 mph", "30 mph"], np.mean) == 44.25685 From ae91015f79d77a82f333e3815e10ac82341e1277 Mon Sep 17 00:00:00 2001 From: nikita Date: Sun, 31 Mar 2024 14:19:10 +0300 Subject: [PATCH 2/4] moved speed mapping to routing module --- osmnx/routing.py | 155 +++++++++++++++++++++++++++++++++++++++++++++- osmnx/settings.py | 153 --------------------------------------------- 2 files changed, 153 insertions(+), 155 deletions(-) diff --git a/osmnx/routing.py b/osmnx/routing.py index e5e111345..200d35f8c 100644 --- a/osmnx/routing.py +++ b/osmnx/routing.py @@ -19,12 +19,163 @@ import pandas as pd from . import convert -from . import settings from . import utils if TYPE_CHECKING: import geopandas as gpd +# Dict that is used by `add_edge_speeds` to convert implicit values +# to numbers, based on https://wiki.openstreetmap.org/wiki/Key:maxspeed. +_IMPLICIT_MAXSPEED_VALUES: dict[str, float] = { + "AR:rural": 110.0, + "AR:urban": 40.0, + "AR:urban:primary": 60.0, + "AR:urban:secondary": 60.0, + "AT:bicycle_road": 30.0, + "AT:motorway": 130.0, + "AT:rural": 100.0, + "AT:trunk": 100.0, + "AT:urban": 50.0, + "BE-BRU:rural": 70.0, + "BE-BRU:urban": 30.0, + "BE-VLG:rural": 70.0, + "BE-VLG:urban": 50.0, + "BE-WAL:rural": 90.0, + "BE-WAL:urban": 50.0, + "BE:cyclestreet": 30.0, + "BE:living_street": 20.0, + "BE:motorway": 120.0, + "BE:trunk": 120.0, + "BE:zone30": 30.0, + "BG:living_street": 20.0, + "BG:motorway": 140.0, + "BG:rural": 90.0, + "BG:trunk": 120.0, + "BG:urban": 50.0, + "BY:living_street": 20.0, + "BY:motorway": 110.0, + "BY:rural": 90.0, + "BY:urban": 60.0, + "CA-AB:rural": 90.0, + "CA-AB:urban": 65.0, + "CA-BC:rural": 80.0, + "CA-BC:urban": 50.0, + "CA-MB:rural": 90.0, + "CA-MB:urban": 50.0, + "CA-ON:rural": 80.0, + "CA-ON:urban": 50.0, + "CA-QC:motorway": 100.0, + "CA-QC:rural": 75.0, + "CA-QC:urban": 50.0, + "CA-SK:nsl": 80.0, + "CH:motorway": 120.0, + "CH:rural": 80.0, + "CH:trunk": 100.0, + "CH:urban": 50.0, + "CZ:living_street": 20.0, + "CZ:motorway": 130.0, + "CZ:pedestrian_zone": 20.0, + "CZ:rural": 90.0, + "CZ:trunk": 110.0, + "CZ:urban": 50.0, + "CZ:urban_motorway": 80.0, + "CZ:urban_trunk": 80.0, + "DE:bicycle_road": 30.0, + "DE:living_street": 15.0, + "DE:motorway": 120.0, + "DE:rural": 80.0, + "DE:urban": 50.0, + "DK:motorway": 130.0, + "DK:rural": 80.0, + "DK:urban": 50.0, + "EE:rural": 90.0, + "EE:urban": 50.0, + "ES:living_street": 20.0, + "ES:motorway": 120.0, + "ES:rural": 90.0, + "ES:trunk": 90.0, + "ES:urban": 50.0, + "ES:zone30": 30.0, + "FI:motorway": 120.0, + "FI:rural": 80.0, + "FI:trunk": 100.0, + "FI:urban": 50.0, + "FR:motorway": 120.0, + "FR:rural": 80.0, + "FR:urban": 50.0, + "FR:zone30": 30.0, + "GB:nsl_restricted": 48.28, + "GR:motorway": 130.0, + "GR:rural": 90.0, + "GR:trunk": 110.0, + "GR:urban": 50.0, + "HU:living_street": 20.0, + "HU:motorway": 130.0, + "HU:rural": 90.0, + "HU:trunk": 110.0, + "HU:urban": 50.0, + "IT:motorway": 130.0, + "IT:rural": 90.0, + "IT:trunk": 110.0, + "IT:urban": 50.0, + "JP:express": 100.0, + "JP:nsl": 60.0, + "LT:rural": 90.0, + "LT:urban": 50.0, + "NO:rural": 80.0, + "NO:urban": 50.0, + "PH:express": 100.0, + "PH:rural": 80.0, + "PH:urban": 30.0, + "PT:motorway": 120.0, + "PT:rural": 90.0, + "PT:trunk": 100.0, + "PT:urban": 50.0, + "RO:motorway": 130.0, + "RO:rural": 90.0, + "RO:trunk": 100.0, + "RO:urban": 50.0, + "RS:living_street": 10.0, + "RS:motorway": 130.0, + "RS:rural": 80.0, + "RS:trunk": 100.0, + "RS:urban": 50.0, + "RU:living_street": 20.0, + "RU:motorway": 110.0, + "RU:rural": 90.0, + "RU:urban": 60.0, + "SE:rural": 70.0, + "SE:urban": 50.0, + "SI:motorway": 130.0, + "SI:rural": 90.0, + "SI:trunk": 110.0, + "SI:urban": 50.0, + "SK:living_street": 20.0, + "SK:motorway": 130.0, + "SK:motorway_urban": 90.0, + "SK:rural": 90.0, + "SK:trunk": 90.0, + "SK:urban": 50.0, + "TR:living_street": 20.0, + "TR:motorway": 130.0, + "TR:rural": 90.0, + "TR:trunk": 110.0, + "TR:urban": 50.0, + "TR:zone30": 30.0, + "UA:living_street": 20.0, + "UA:motorway": 130.0, + "UA:rural": 90.0, + "UA:trunk": 110.0, + "UA:urban": 50.0, + "UK:motorway": 112.65, + "UK:nsl_dual": 112.65, + "UK:nsl_single": 96.56, + "UZ:living_street": 30.0, + "UZ:motorway": 110.0, + "UZ:rural": 100.0, + "UZ:urban": 70.0, +} + def route_to_gdf( G: nx.MultiDiGraph, @@ -535,7 +686,7 @@ def _clean_maxspeed( except (ValueError, AttributeError): # if input is not a valid numeric string, look it up in implicit speed mapping - return settings.implicit_maxspeed_values.get(maxspeed) + return _IMPLICIT_MAXSPEED_VALUES.get(maxspeed) def _collapse_multiple_maxspeed_values( diff --git a/osmnx/settings.py b/osmnx/settings.py index 37856fc1f..248ed6d07 100644 --- a/osmnx/settings.py +++ b/osmnx/settings.py @@ -120,10 +120,6 @@ retrieved from OSM. Default is `["access", "area", "bridge", "est_width", "highway", "junction", "landuse", "lanes", "maxspeed", "name", "oneway", "ref", "service", "tunnel", "width"]`. -implicit_maxspeed_values: dict - Implicit maxspeed values mapping that is used by `add_edge_speeds` - function. Default values are obtained from wiki page - https://wiki.openstreetmap.org/wiki/Key:maxspeed. """ from __future__ import annotations @@ -184,152 +180,3 @@ "tunnel", "width", ] -implicit_maxspeed_values = { - "AR:rural": 110.0, - "AR:urban": 40.0, - "AR:urban:primary": 60.0, - "AR:urban:secondary": 60.0, - "AT:bicycle_road": 30.0, - "AT:motorway": 130.0, - "AT:rural": 100.0, - "AT:trunk": 100.0, - "AT:urban": 50.0, - "BE-BRU:rural": 70.0, - "BE-BRU:urban": 30.0, - "BE-VLG:rural": 70.0, - "BE-VLG:urban": 50.0, - "BE-WAL:rural": 90.0, - "BE-WAL:urban": 50.0, - "BE:cyclestreet": 30.0, - "BE:living_street": 20.0, - "BE:motorway": 120.0, - "BE:trunk": 120.0, - "BE:zone30": 30.0, - "BG:living_street": 20.0, - "BG:motorway": 140.0, - "BG:rural": 90.0, - "BG:trunk": 120.0, - "BG:urban": 50.0, - "BY:living_street": 20.0, - "BY:motorway": 110.0, - "BY:rural": 90.0, - "BY:urban": 60.0, - "CA-AB:rural": 90.0, - "CA-AB:urban": 65.0, - "CA-BC:rural": 80.0, - "CA-BC:urban": 50.0, - "CA-MB:rural": 90.0, - "CA-MB:urban": 50.0, - "CA-ON:rural": 80.0, - "CA-ON:urban": 50.0, - "CA-QC:motorway": 100.0, - "CA-QC:rural": 75.0, - "CA-QC:urban": 50.0, - "CA-SK:nsl": 80.0, - "CH:motorway": 120.0, - "CH:rural": 80.0, - "CH:trunk": 100.0, - "CH:urban": 50.0, - "CZ:living_street": 20.0, - "CZ:motorway": 130.0, - "CZ:pedestrian_zone": 20.0, - "CZ:rural": 90.0, - "CZ:trunk": 110.0, - "CZ:urban": 50.0, - "CZ:urban_motorway": 80.0, - "CZ:urban_trunk": 80.0, - "DE:bicycle_road": 30.0, - "DE:living_street": 15.0, - "DE:motorway": 120.0, - "DE:rural": 80.0, - "DE:urban": 50.0, - "DK:motorway": 130.0, - "DK:rural": 80.0, - "DK:urban": 50.0, - "EE:rural": 90.0, - "EE:urban": 50.0, - "ES:living_street": 20.0, - "ES:motorway": 120.0, - "ES:rural": 90.0, - "ES:trunk": 90.0, - "ES:urban": 50.0, - "ES:zone30": 30.0, - "FI:motorway": 120.0, - "FI:rural": 80.0, - "FI:trunk": 100.0, - "FI:urban": 50.0, - "FR:motorway": 120.0, - "FR:rural": 80.0, - "FR:urban": 50.0, - "FR:zone30": 30.0, - "GB:nsl_restricted": 48.28, - "GR:motorway": 130.0, - "GR:rural": 90.0, - "GR:trunk": 110.0, - "GR:urban": 50.0, - "HU:living_street": 20.0, - "HU:motorway": 130.0, - "HU:rural": 90.0, - "HU:trunk": 110.0, - "HU:urban": 50.0, - "IT:motorway": 130.0, - "IT:rural": 90.0, - "IT:trunk": 110.0, - "IT:urban": 50.0, - "JP:express": 100.0, - "JP:nsl": 60.0, - "LT:rural": 90.0, - "LT:urban": 50.0, - "NO:rural": 80.0, - "NO:urban": 50.0, - "PH:express": 100.0, - "PH:rural": 80.0, - "PH:urban": 30.0, - "PT:motorway": 120.0, - "PT:rural": 90.0, - "PT:trunk": 100.0, - "PT:urban": 50.0, - "RO:motorway": 130.0, - "RO:rural": 90.0, - "RO:trunk": 100.0, - "RO:urban": 50.0, - "RS:living_street": 10.0, - "RS:motorway": 130.0, - "RS:rural": 80.0, - "RS:trunk": 100.0, - "RS:urban": 50.0, - "RU:living_street": 20.0, - "RU:motorway": 110.0, - "RU:rural": 90.0, - "RU:urban": 60.0, - "SE:rural": 70.0, - "SE:urban": 50.0, - "SI:motorway": 130.0, - "SI:rural": 90.0, - "SI:trunk": 110.0, - "SI:urban": 50.0, - "SK:living_street": 20.0, - "SK:motorway": 130.0, - "SK:motorway_urban": 90.0, - "SK:rural": 90.0, - "SK:trunk": 90.0, - "SK:urban": 50.0, - "TR:living_street": 20.0, - "TR:motorway": 130.0, - "TR:rural": 90.0, - "TR:trunk": 110.0, - "TR:urban": 50.0, - "TR:zone30": 30.0, - "UA:living_street": 20.0, - "UA:motorway": 130.0, - "UA:rural": 90.0, - "UA:trunk": 110.0, - "UA:urban": 50.0, - "UK:motorway": 112.65, - "UK:nsl_dual": 112.65, - "UK:nsl_single": 96.56, - "UZ:living_street": 30.0, - "UZ:motorway": 110.0, - "UZ:rural": 100.0, - "UZ:urban": 70.0, -} From 377fc67c8d7f161be817313fd822d4b51c8daa67 Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Sun, 31 Mar 2024 18:09:16 -0700 Subject: [PATCH 3/4] Update routing.py --- osmnx/routing.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osmnx/routing.py b/osmnx/routing.py index 200d35f8c..2263a4261 100644 --- a/osmnx/routing.py +++ b/osmnx/routing.py @@ -25,8 +25,8 @@ import geopandas as gpd # Dict that is used by `add_edge_speeds` to convert implicit values -# to numbers, based on https://wiki.openstreetmap.org/wiki/Key:maxspeed. -_IMPLICIT_MAXSPEED_VALUES: dict[str, float] = { +# to numbers, based on https://wiki.openstreetmap.org/wiki/Key:maxspeed +_IMPLICIT_MAXSPEEDS: dict[str, float] = { "AR:rural": 110.0, "AR:urban": 40.0, "AR:urban:primary": 60.0, @@ -685,8 +685,8 @@ def _clean_maxspeed( return float(agg(clean_values)) except (ValueError, AttributeError): - # if input is not a valid numeric string, look it up in implicit speed mapping - return _IMPLICIT_MAXSPEED_VALUES.get(maxspeed) + # if not valid numeric string, try looking it up as implicit value + return _IMPLICIT_MAXSPEEDS.get(maxspeed) def _collapse_multiple_maxspeed_values( From cd2a742017dd9cae3e9cb3546abc2ae29f5453ce Mon Sep 17 00:00:00 2001 From: Geoff Boeing Date: Sun, 31 Mar 2024 18:10:27 -0700 Subject: [PATCH 4/4] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 18c012a2b..3ddc92968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Read the v2 [migration guide](https://github.com/gboeing/osmnx/issues/1123) - make dist function parameters required rather than optional throughout package (#1134) - make which_result function parameter consistently able to accept a list throughout package (#1113) - make utils_geo.bbox_from_point function return a tuple of floats for consistency with rest of package (#1113) +- handle implicit maxspeed values in add_edge_speeds function (#1153) - change add_node_elevations_google default batch_size to 512 to match Google's limit (#1115) - allow analysis of MultiDiGraph directional edge bearings and orientation (#1139) - fix bug in \_downloader.\_save_to_cache function usage (#1107)