From 2acef229580a14682a6e5a75872d40c4af0a365b Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Fri, 25 Jun 2021 14:35:59 +0200 Subject: [PATCH 1/7] changed Isochrone geometry to MultiPolygon --- routingpy/routers/graphhopper.py | 2 +- routingpy/routers/mapbox_osrm.py | 2 +- routingpy/routers/openrouteservice.py | 2 +- routingpy/routers/valhalla.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/routingpy/routers/graphhopper.py b/routingpy/routers/graphhopper.py index 02e7e28..62542b1 100644 --- a/routingpy/routers/graphhopper.py +++ b/routingpy/routers/graphhopper.py @@ -509,7 +509,7 @@ def _parse_isochrone_json(response, type, max_range, buckets, center): isochrones.append( Isochrone( geometry=[ - l[:2] for l in polygon["geometry"]["coordinates"][0] # noqa: E741 + [l[:2] for l in polygon["geometry"]["coordinates"][0]] # noqa: E741 ], # takes in elevation for some reason interval=int(max_range * ((polygon["properties"]["bucket"] + 1) / buckets)), center=center, diff --git a/routingpy/routers/mapbox_osrm.py b/routingpy/routers/mapbox_osrm.py index 5e16be7..5618d1b 100644 --- a/routingpy/routers/mapbox_osrm.py +++ b/routingpy/routers/mapbox_osrm.py @@ -430,7 +430,7 @@ def _parse_isochrone_json(response, intervals, locations): return Isochrones( [ Isochrone( - geometry=isochrone["geometry"]["coordinates"], + geometry=[isochrone["geometry"]["coordinates"]], interval=intervals[idx], center=locations, ) diff --git a/routingpy/routers/openrouteservice.py b/routingpy/routers/openrouteservice.py index 1737162..2881e0b 100644 --- a/routingpy/routers/openrouteservice.py +++ b/routingpy/routers/openrouteservice.py @@ -475,7 +475,7 @@ def _parse_isochrone_json(response): for idx, isochrone in enumerate(response["features"]): isochrones.append( Isochrone( - geometry=isochrone["geometry"]["coordinates"][0], + geometry=isochrone["geometry"]["coordinates"], interval=isochrone["properties"]["value"], center=isochrone["properties"]["center"], ) diff --git a/routingpy/routers/valhalla.py b/routingpy/routers/valhalla.py index 4701fb4..840619b 100644 --- a/routingpy/routers/valhalla.py +++ b/routingpy/routers/valhalla.py @@ -426,7 +426,7 @@ def _parse_isochrone_json(response, intervals, locations): if feature["geometry"]["type"] in ("LineString", "Polygon"): isochrones.append( Isochrone( - geometry=feature["geometry"]["coordinates"], + geometry=[feature["geometry"]["coordinates"]], interval=intervals[idx], center=locations, ) From e02f9846e463871bf6de5bba79f8405a3c42ad81 Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Mon, 28 Jun 2021 09:20:33 +0200 Subject: [PATCH 2/7] added note warning of possible orientation issues with isochrone polygons --- routingpy/isochrone.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/routingpy/isochrone.py b/routingpy/isochrone.py index f335f75..4b9404c 100644 --- a/routingpy/isochrone.py +++ b/routingpy/isochrone.py @@ -64,9 +64,13 @@ def __init__(self, geometry=None, interval=None, center=None): @property def geometry(self): """ - The geometry of the isochrone as [[lon1, lat1], [lon2, lat2], ...] list. + The geometry of the isochrone as [[[lon1, lat1], [lon2, lat2], ...]] list. :rtype: list or None + + .. note:: + Since it's not known whether providers' responses adhere to OGC standards, caution is advised with regard + to possible orientation issues of Polygons. """ return self._geometry From cb0ecb4dda6cf2e5e53f6ede5ae8db7998253561 Mon Sep 17 00:00:00 2001 From: Christian <58629404+chrstnbwnkl@users.noreply.github.com> Date: Mon, 28 Jun 2021 10:09:07 +0200 Subject: [PATCH 3/7] Update routingpy/isochrone.py Co-authored-by: Nils --- routingpy/isochrone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routingpy/isochrone.py b/routingpy/isochrone.py index 4b9404c..1825bd0 100644 --- a/routingpy/isochrone.py +++ b/routingpy/isochrone.py @@ -70,7 +70,7 @@ def geometry(self): .. note:: Since it's not known whether providers' responses adhere to OGC standards, caution is advised with regard - to possible orientation issues of Polygons. + to possible orientation issues of exterior and interior rings of the resulting polygons. """ return self._geometry From a6f2396ae2704f9e10df49f21f1254e8651b60b9 Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Mon, 28 Jun 2021 15:27:07 +0200 Subject: [PATCH 4/7] added extra wrap level for Isochrone geometry --- routingpy/isochrone.py | 3 ++- routingpy/routers/graphhopper.py | 9 ++++++--- routingpy/routers/heremaps.py | 2 +- routingpy/routers/mapbox_osrm.py | 2 +- routingpy/routers/openrouteservice.py | 7 ++++++- routingpy/routers/valhalla.py | 2 +- tests/utils.py | 3 +++ 7 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 tests/utils.py diff --git a/routingpy/isochrone.py b/routingpy/isochrone.py index 4b9404c..f1060f4 100644 --- a/routingpy/isochrone.py +++ b/routingpy/isochrone.py @@ -64,7 +64,8 @@ def __init__(self, geometry=None, interval=None, center=None): @property def geometry(self): """ - The geometry of the isochrone as [[[lon1, lat1], [lon2, lat2], ...]] list. + The geometry of the isochrone as + [[[[lon_ex1, lat_ex1], [lon_ex2, lat_ex2], ...], [lon_in1, lat_in1], [lon_in2, lat_in2], ...]] list. :rtype: list or None diff --git a/routingpy/routers/graphhopper.py b/routingpy/routers/graphhopper.py index 62542b1..23fbeb6 100644 --- a/routingpy/routers/graphhopper.py +++ b/routingpy/routers/graphhopper.py @@ -506,11 +506,14 @@ def _parse_isochrone_json(response, type, max_range, buckets, center): isochrones = [] accessor = "polygons" if type == "json" else "features" for index, polygon in enumerate(response[accessor]): + geometry = ( + [[polygon["geometry"]["coordinates"]]] + if polygon["geometry"]["type"] == "polygon" + else [polygon["geometry"]["coordinates"]] + ) isochrones.append( Isochrone( - geometry=[ - [l[:2] for l in polygon["geometry"]["coordinates"][0]] # noqa: E741 - ], # takes in elevation for some reason + geometry=geometry, # noqa: E741 interval=int(max_range * ((polygon["properties"]["bucket"] + 1) / buckets)), center=center, ) diff --git a/routingpy/routers/heremaps.py b/routingpy/routers/heremaps.py index 907dfb0..ccdd072 100644 --- a/routingpy/routers/heremaps.py +++ b/routingpy/routers/heremaps.py @@ -1093,7 +1093,7 @@ def _parse_isochrone_json(response, intervals): geometries.append( Isochrone( - geometry=range_polygons, + geometry=[range_polygons], interval=intervals[idx], center=list(response["response"]["start"]["mappedPosition"].values()), ) diff --git a/routingpy/routers/mapbox_osrm.py b/routingpy/routers/mapbox_osrm.py index 5618d1b..6eec63b 100644 --- a/routingpy/routers/mapbox_osrm.py +++ b/routingpy/routers/mapbox_osrm.py @@ -430,7 +430,7 @@ def _parse_isochrone_json(response, intervals, locations): return Isochrones( [ Isochrone( - geometry=[isochrone["geometry"]["coordinates"]], + geometry=[[isochrone["geometry"]["coordinates"]]], interval=intervals[idx], center=locations, ) diff --git a/routingpy/routers/openrouteservice.py b/routingpy/routers/openrouteservice.py index 2881e0b..82f7ce0 100644 --- a/routingpy/routers/openrouteservice.py +++ b/routingpy/routers/openrouteservice.py @@ -473,9 +473,14 @@ def _parse_isochrone_json(response): isochrones = [] for idx, isochrone in enumerate(response["features"]): + geometry = ( + [[isochrone["geometry"]["coordinates"]]] + if isochrone["geometry"]["type"] == "Polygon" + else [isochrone["geometry"]["coordinates"]] + ) isochrones.append( Isochrone( - geometry=isochrone["geometry"]["coordinates"], + geometry=geometry, interval=isochrone["properties"]["value"], center=isochrone["properties"]["center"], ) diff --git a/routingpy/routers/valhalla.py b/routingpy/routers/valhalla.py index 840619b..ce8c26b 100644 --- a/routingpy/routers/valhalla.py +++ b/routingpy/routers/valhalla.py @@ -426,7 +426,7 @@ def _parse_isochrone_json(response, intervals, locations): if feature["geometry"]["type"] in ("LineString", "Polygon"): isochrones.append( Isochrone( - geometry=[feature["geometry"]["coordinates"]], + geometry=[[feature["geometry"]["coordinates"]]], interval=intervals[idx], center=locations, ) diff --git a/tests/utils.py b/tests/utils.py new file mode 100644 index 0000000..9ea156e --- /dev/null +++ b/tests/utils.py @@ -0,0 +1,3 @@ +def get_max_depth(list_): + """Returns the maximum depth of a nested list.""" + return isinstance(list_, list) and max(map(get_max_depth, list_)) + 1 From efd1fb7f66cf575ba966c8c9b7a652e8d1d90dd6 Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Mon, 28 Jun 2021 16:14:06 +0200 Subject: [PATCH 5/7] adapted tests for isochrone geometries --- routingpy/routers/graphhopper.py | 2 +- routingpy/routers/openrouteservice.py | 4 ++-- tests/test_graphhopper.py | 3 +++ tests/test_helper.py | 8 ++++++-- tests/test_heremaps.py | 3 +++ tests/test_mapbox_osrm.py | 12 +++++++----- tests/test_openrouteservice.py | 2 ++ tests/test_valhalla.py | 2 ++ 8 files changed, 26 insertions(+), 10 deletions(-) diff --git a/routingpy/routers/graphhopper.py b/routingpy/routers/graphhopper.py index 23fbeb6..bf983b4 100644 --- a/routingpy/routers/graphhopper.py +++ b/routingpy/routers/graphhopper.py @@ -509,7 +509,7 @@ def _parse_isochrone_json(response, type, max_range, buckets, center): geometry = ( [[polygon["geometry"]["coordinates"]]] if polygon["geometry"]["type"] == "polygon" - else [polygon["geometry"]["coordinates"]] + else [polygon["geometry"]["coordinates"]] # if MultiPolygon ) isochrones.append( Isochrone( diff --git a/routingpy/routers/openrouteservice.py b/routingpy/routers/openrouteservice.py index 82f7ce0..1b9e549 100644 --- a/routingpy/routers/openrouteservice.py +++ b/routingpy/routers/openrouteservice.py @@ -474,9 +474,9 @@ def _parse_isochrone_json(response): isochrones = [] for idx, isochrone in enumerate(response["features"]): geometry = ( - [[isochrone["geometry"]["coordinates"]]] + [isochrone["geometry"]["coordinates"]] if isochrone["geometry"]["type"] == "Polygon" - else [isochrone["geometry"]["coordinates"]] + else [isochrone["geometry"]["coordinates"]] # if MultiPolygon ) isochrones.append( Isochrone( diff --git a/tests/test_graphhopper.py b/tests/test_graphhopper.py index 45637b1..20c6ad3 100644 --- a/tests/test_graphhopper.py +++ b/tests/test_graphhopper.py @@ -21,8 +21,10 @@ from routingpy.isochrone import Isochrones, Isochrone from routingpy.matrix import Matrix + from tests.test_helper import * import tests as _test +from tests.utils import get_max_depth import responses from copy import deepcopy @@ -128,6 +130,7 @@ def test_full_isochrones(self): self.assertIsInstance(iso.geometry, list) self.assertIsInstance(iso.interval, int) self.assertIsInstance(iso.center, list) + self.assertEqual(get_max_depth(iso.geometry), 4) @responses.activate def test_full_matrix(self): diff --git a/tests/test_helper.py b/tests/test_helper.py index 88b72e7..5fd0ffa 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -74,7 +74,9 @@ }, "geometry": { "coordinates": [ - [[8.684544, 49.423295], [8.684665, 49.423101], [8.684706, 49.423036]] + [8.684544, 49.423295], + [8.684665, 49.423101], + [8.684706, 49.423036], ], "type": "Polygon", }, @@ -141,7 +143,9 @@ }, "geometry": { "coordinates": [ - [[8.684544, 49.423295], [8.684665, 49.423101], [8.684706, 49.423036]] + [8.684544, 49.423295], + [8.684665, 49.423101], + [8.684706, 49.423036], ], "type": "Polygon", }, diff --git a/tests/test_heremaps.py b/tests/test_heremaps.py index 8687949..7fbe4a5 100644 --- a/tests/test_heremaps.py +++ b/tests/test_heremaps.py @@ -20,6 +20,7 @@ from routingpy.direction import Direction, Directions from routingpy.isochrone import Isochrones, Isochrone from routingpy.matrix import Matrix +from tests.utils import get_max_depth from tests.test_helper import * import tests as _test @@ -171,6 +172,7 @@ def test_full_isochrones_response_object(self): self.assertIsInstance(iso.geometry, list) self.assertIsInstance(iso.center, list) self.assertIsInstance(iso.interval, int) + self.assertEqual(get_max_depth(iso.geometry), 4) @responses.activate def test_full_matrix(self): @@ -356,6 +358,7 @@ def test_full_isochrones_response_object(self): self.assertIsInstance(iso.geometry, list) self.assertIsInstance(iso.center, list) self.assertIsInstance(iso.interval, int) + self.assertEqual(get_max_depth(iso.geometry), 4) @responses.activate def test_full_matrix(self): diff --git a/tests/test_mapbox_osrm.py b/tests/test_mapbox_osrm.py index efd81a1..c1a503a 100644 --- a/tests/test_mapbox_osrm.py +++ b/tests/test_mapbox_osrm.py @@ -23,6 +23,7 @@ from routingpy.matrix import Matrix from tests.test_helper import * import tests as _test +from tests.utils import get_max_depth import responses from copy import deepcopy @@ -134,11 +135,12 @@ def test_full_isochrones(self): ) self.assertIsInstance(iso, Isochrones) self.assertEqual(2, len(iso)) - for ischrone in iso: - self.assertIsInstance(ischrone, Isochrone) - self.assertIsInstance(ischrone.geometry, list) - self.assertIsInstance(ischrone.interval, int) - self.assertIsInstance(ischrone.center, list) + for isochrone in iso: + self.assertIsInstance(isochrone, Isochrone) + self.assertIsInstance(isochrone.geometry, list) + self.assertIsInstance(isochrone.interval, int) + self.assertIsInstance(isochrone.center, list) + self.assertEqual(get_max_depth(isochrone.geometry), 4) @responses.activate def test_full_matrix(self): diff --git a/tests/test_openrouteservice.py b/tests/test_openrouteservice.py index ceb1013..a0bd182 100644 --- a/tests/test_openrouteservice.py +++ b/tests/test_openrouteservice.py @@ -24,6 +24,7 @@ from tests.test_helper import * import tests as _test +from tests.utils import get_max_depth import responses import json @@ -122,6 +123,7 @@ def test_full_isochrones(self): self.assertIsInstance(iso.geometry, list) self.assertIsInstance(iso.center, list) self.assertIsInstance(iso.interval, int) + self.assertEqual(get_max_depth(iso.geometry), 4) @responses.activate def test_full_matrix(self): diff --git a/tests/test_valhalla.py b/tests/test_valhalla.py index c8360af..91867ff 100644 --- a/tests/test_valhalla.py +++ b/tests/test_valhalla.py @@ -22,6 +22,7 @@ from routingpy.matrix import Matrix from tests.test_helper import * import tests as _test +from tests.utils import get_max_depth import json import responses @@ -111,6 +112,7 @@ def test_full_isochrones(self): self.assertIsInstance(i.geometry, list) self.assertIsInstance(i.interval, int) self.assertIsInstance(i.center, list) + self.assertEqual(get_max_depth(i.geometry), 4) @responses.activate def test_isodistances(self): From 7258a2ebaaac487d962fe262f7d05382395f2f61 Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Mon, 28 Jun 2021 16:15:52 +0200 Subject: [PATCH 6/7] changed mock responses for MapBoxOSRM and Valhalla --- tests/test_helper.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_helper.py b/tests/test_helper.py index 5fd0ffa..11893cb 100644 --- a/tests/test_helper.py +++ b/tests/test_helper.py @@ -60,7 +60,9 @@ }, "geometry": { "coordinates": [ - [[8.684544, 49.423295], [8.684665, 49.423101], [8.684706, 49.423036]] + [8.684544, 49.423295], + [8.684665, 49.423101], + [8.684706, 49.423036], ], "type": "Polygon", }, @@ -159,7 +161,9 @@ }, "geometry": { "coordinates": [ - [[8.684544, 49.423295], [8.684665, 49.423101], [8.684706, 49.423036]] + [8.684544, 49.423295], + [8.684665, 49.423101], + [8.684706, 49.423036], ], "type": "Polygon", }, From 7bf935f49f01ac721d086d37643de7557610646c Mon Sep 17 00:00:00 2001 From: chrstnbwnkl Date: Mon, 28 Jun 2021 16:41:52 +0200 Subject: [PATCH 7/7] updated Isochrone.geometry docstring --- routingpy/isochrone.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routingpy/isochrone.py b/routingpy/isochrone.py index 8ce2c42..6d66555 100644 --- a/routingpy/isochrone.py +++ b/routingpy/isochrone.py @@ -65,7 +65,7 @@ def __init__(self, geometry=None, interval=None, center=None): def geometry(self): """ The geometry of the isochrone as - [[[[lon_ex1, lat_ex1], [lon_ex2, lat_ex2], ...], [lon_in1, lat_in1], [lon_in2, lat_in2], ...]] list. + [[[[lon_ex1, lat_ex1], [lon_ex2, lat_ex2], ...], [[lon_in1, lat_in1], [lon_in2, lat_in2], ...]], [other_polygon]] list. :rtype: list or None