Skip to content

Commit 2d457b0

Browse files
authored
Merge pull request #728 from ocefpaf/improve_iter_coords
Improve `iter_points`
2 parents dae9e07 + bc9df45 commit 2d457b0

File tree

2 files changed

+42
-44
lines changed

2 files changed

+42
-44
lines changed

folium/features.py

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
from branca.utilities import (_locations_tolist, _parse_size, image_to_url, iter_points, none_max, none_min) # noqa
1818

1919
from folium.map import FeatureGroup, Icon, Layer, Marker
20-
from folium.utilities import _parse_wms
20+
from folium.utilities import _parse_wms, get_bounds
2121
from folium.vector_layers import PolyLine
2222

2323
from jinja2 import Template
@@ -510,37 +510,13 @@ def style_data(self):
510510
feature.setdefault('properties', {}).setdefault('highlight', {}).update(self.highlight_function(feature)) # noqa
511511
return json.dumps(self.data, sort_keys=True)
512512

513-
def get_bounds(self):
513+
def _get_self_bounds(self):
514514
"""
515515
Computes the bounds of the object itself (not including it's children)
516-
in the form [[lat_min, lon_min], [lat_max, lon_max]]
516+
in the form [[lat_min, lon_min], [lat_max, lon_max]].
517517
518518
"""
519-
if not self.embed:
520-
raise ValueError('Cannot compute bounds of non-embedded GeoJSON.')
521-
522-
if 'features' not in self.data.keys():
523-
# Catch case when GeoJSON is just a single Feature or a geometry.
524-
if not (isinstance(self.data, dict) and 'geometry' in self.data.keys()): # noqa
525-
# Catch case when GeoJSON is just a geometry.
526-
self.data = {'type': 'Feature', 'geometry': self.data}
527-
self.data = {'type': 'FeatureCollection', 'features': [self.data]}
528-
529-
bounds = [[None, None], [None, None]]
530-
for feature in self.data['features']:
531-
for point in iter_points(feature.get('geometry', {}).get('coordinates', {})): # noqa
532-
bounds = [
533-
[
534-
none_min(bounds[0][0], point[1]),
535-
none_min(bounds[0][1], point[0]),
536-
],
537-
[
538-
none_max(bounds[1][0], point[1]),
539-
none_max(bounds[1][1], point[0]),
540-
],
541-
]
542-
return bounds
543-
519+
return get_bounds(self.data, lonlat=True)
544520

545521
class TopoJson(Layer):
546522
"""

folium/utilities.py

Lines changed: 38 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -338,33 +338,53 @@ def none_max(x, y):
338338
return max(x, y)
339339

340340

341-
def iter_points(x):
342-
"""Iterates over a list representing a feature, and returns a list of points,
343-
whatever the shape of the array (Point, MultiPolyline, etc).
341+
def iter_coords(obj):
344342
"""
345-
if isinstance(x, (list, tuple)):
346-
if len(x):
347-
if isinstance(x[0], (list, tuple)):
348-
out = []
349-
for y in x:
350-
out += iter_points(y)
351-
return out
352-
else:
353-
return [x]
343+
Returns all the coordinate tuples from a geometry or feature.
344+
345+
"""
346+
if isinstance(obj, (tuple, list)):
347+
coords = obj
348+
elif 'features' in obj:
349+
coords = [geom['geometry']['coordinates'] for geom in obj['features']]
350+
elif 'geometry' in obj:
351+
coords = obj['geometry']['coordinates']
352+
else:
353+
coords = obj.get('coordinates', obj)
354+
for coord in coords:
355+
if isinstance(coord, (float, int)):
356+
yield tuple(coords)
357+
break
354358
else:
355-
return []
359+
for f in iter_coords(coord):
360+
yield f
361+
362+
363+
def _locations_mirror(x):
364+
"""
365+
Mirrors the points in a list-of-list-of-...-of-list-of-points.
366+
For example:
367+
>>> _locations_mirror([[[1, 2], [3, 4]], [5, 6], [7, 8]])
368+
[[[2, 1], [4, 3]], [6, 5], [8, 7]]
369+
370+
"""
371+
if hasattr(x, '__iter__'):
372+
if hasattr(x[0], '__iter__'):
373+
return list(map(_locations_mirror, x))
374+
else:
375+
return list(x[::-1])
356376
else:
357-
raise ValueError('List/tuple type expected. Got {!r}.'.format(x))
377+
return x
358378

359379

360-
def get_bounds(locations):
380+
def get_bounds(locations, lonlat=False):
361381
"""
362382
Computes the bounds of the object in the form
363383
[[lat_min, lon_min], [lat_max, lon_max]]
364384
365385
"""
366386
bounds = [[None, None], [None, None]]
367-
for point in iter_points(locations):
387+
for point in iter_coords(locations):
368388
bounds = [
369389
[
370390
none_min(bounds[0][0], point[0]),
@@ -375,4 +395,6 @@ def get_bounds(locations):
375395
none_max(bounds[1][1], point[1]),
376396
],
377397
]
398+
if lonlat:
399+
bounds = _locations_mirror(bounds)
378400
return bounds

0 commit comments

Comments
 (0)