diff --git a/routingpy/isochrone.py b/routingpy/isochrone.py index f335f75..6d66555 100644 --- a/routingpy/isochrone.py +++ b/routingpy/isochrone.py @@ -64,9 +64,14 @@ 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], ...]], [other_polygon]] 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 exterior and interior rings of the resulting polygons. """ return self._geometry diff --git a/routingpy/routers/graphhopper.py b/routingpy/routers/graphhopper.py index 02e7e28..bf983b4 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"]] # if MultiPolygon + ) 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 5e16be7..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 1737162..1b9e549 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"]] # if MultiPolygon + ) isochrones.append( Isochrone( - geometry=isochrone["geometry"]["coordinates"][0], + geometry=geometry, interval=isochrone["properties"]["value"], center=isochrone["properties"]["center"], ) diff --git a/routingpy/routers/valhalla.py b/routingpy/routers/valhalla.py index 4701fb4..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/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..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", }, @@ -74,7 +76,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 +145,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", }, @@ -155,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", }, 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): 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