Skip to content

Bounds #305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 30, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion folium/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import base64

from .six import urlopen, text_type, binary_type
from .utilities import _camelify, _parse_size
from .utilities import _camelify, _parse_size, none_min, none_max


ENV = Environment(loader=PackageLoader('folium', 'templates'))
Expand All @@ -40,6 +40,32 @@ def __init__(self, template=None, template_name=None):
def get_name(self):
return _camelify(self._name) + '_' + self._id

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
return [[None,None],[None,None]]

def get_bounds(self):
"""Computes the bounds of the object and all it's children
in the form [[lat_min, lon_min], [lat_max, lon_max]].
"""
bounds = self._get_self_bounds()

for child in self._children.values():
child_bounds = child.get_bounds()
bounds = [
[
none_min(bounds[0][0], child_bounds[0][0]),
none_min(bounds[0][1], child_bounds[0][1]),
],
[
none_max(bounds[1][0], child_bounds[1][0]),
none_max(bounds[1][1], child_bounds[1][1]),
],
]
return bounds

def add_children(self, child, name=None, index=None):
"""Add a children."""
if name is None:
Expand Down
104 changes: 101 additions & 3 deletions folium/features.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

from .utilities import (color_brewer, _parse_size, legend_scaler,
_locations_mirror, _locations_tolist, image_to_url,
text_type, binary_type)
text_type, binary_type,
none_min, none_max, iter_points,
)

from .element import Element, Figure, JavascriptLink, CssLink, MacroElement
from .map import Layer, Icon, Marker, Popup
Expand Down Expand Up @@ -126,7 +128,6 @@ def render(self, **kwargs):
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/leaflet-dvf/0.2/leaflet-dvf.markers.min.js"), # noqa
name='dvf_js')


class Vega(Element):
def __init__(self, data, width=None, height=None,
left="0%", top="0%", position='relative'):
Expand Down Expand Up @@ -277,6 +278,35 @@ def style_data(self):
self.style_function(feature))
return json.dumps(self.data)

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
if not self.embed:
raise ValueError('Cannot compute bounds of non-embedded GeoJSON.')

if 'features' not in self.data.keys():
# Catch case when GeoJSON is just a single Feature or a geometry.
if not (isinstance(self.data, dict) and 'geometry' in self.data.keys()):
# Catch case when GeoJSON is just a geometry.
self.data = {'type' : 'Feature', 'geometry' : self.data}
self.data = {'type' : 'FeatureCollection', 'features' : [self.data]}

bounds = [[None,None],[None,None]]
for feature in self.data['features']:
for point in iter_points(feature.get('geometry',{}).get('coordinates',{})):
bounds = [
[
none_min(bounds[0][0], point[1]),
none_min(bounds[0][1], point[0]),
],
[
none_max(bounds[1][0], point[1]),
none_max(bounds[1][1], point[0]),
],
]
return bounds

class TopoJson(MacroElement):
def __init__(self, data, object_path):
"""
Expand All @@ -286,10 +316,13 @@ def __init__(self, data, object_path):
super(TopoJson, self).__init__()
self._name = 'TopoJson'
if 'read' in dir(data):
self.embed = True
self.data = data.read()
elif type(data) is dict:
self.embed = True
self.data = json.dumps(data)
else:
self.embed = False
self.data = data

self.object_path = object_path
Expand All @@ -315,6 +348,38 @@ def render(self, **kwargs):
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.9/topojson.min.js"), # noqa
name='topojson')

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
if not self.embed:
raise ValueError('Cannot compute bounds of non-embedded TopoJSON.')

data = json.loads(self.data)

xmin,xmax,ymin,ymax = None, None, None, None

for arc in data['arcs']:
x,y = 0,0
for dx, dy in arc:
x += dx
y += dy
xmin = none_min(x, xmin)
xmax = none_max(x, xmax)
ymin = none_min(y, ymin)
ymax = none_max(y, ymax)
return [
[
data['transform']['translate'][0] + data['transform']['scale'][0] * xmin,
data['transform']['translate'][1] + data['transform']['scale'][1] * ymin,
],
[
data['transform']['translate'][0] + data['transform']['scale'][0] * xmax,
data['transform']['translate'][1] + data['transform']['scale'][1] * ymax,
]

]

class ColorScale(MacroElement):
def __init__(self, color_domain, color_code, caption=""):
"""
Expand Down Expand Up @@ -344,7 +409,6 @@ def render(self, **kwargs):
JavascriptLink("https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"), # noqa
name='d3')


class MarkerCluster(Layer):
"""Adds a MarkerCluster layer on the map."""
def __init__(self, overlay=True, control=True):
Expand Down Expand Up @@ -569,6 +633,23 @@ def __init__(self, locations, color=None, weight=None,
{% endmacro %}
""") # noqa

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
bounds = [[None, None], [None, None]]
for point in iter_points(self.data):
bounds = [
[
none_min(bounds[0][0], point[0]),
none_min(bounds[0][1], point[1]),
],
[
none_max(bounds[1][0], point[0]),
none_max(bounds[1][1], point[1]),
],
]
return bounds

class MultiPolyLine(MacroElement):
def __init__(self, locations, color=None, weight=None,
Expand Down Expand Up @@ -616,6 +697,23 @@ def __init__(self, locations, color=None, weight=None,
{{this._parent.get_name()}}.addLayer({{this.get_name()}});
{% endmacro %}
""") # noqa
def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
bounds = [[None, None], [None, None]]
for point in iter_points(self.data):
bounds = [
[
none_min(bounds[0][0], point[0]),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see what you are trying to do here (and I do not have a better suggestion right now), but it looks like we could do something simpler...

none_min(bounds[0][1], point[1]),
],
[
none_max(bounds[1][0], point[0]),
none_max(bounds[1][1], point[1]),
],
]
return bounds

class CustomIcon(Icon):
def __init__(self, icon_image, icon_size=None, icon_anchor=None,
Expand Down
11 changes: 5 additions & 6 deletions folium/map.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

from .element import Element, Figure, MacroElement, Html


class Map(MacroElement):
def __init__(self, location=None, width='100%', height='100%',
left="0%", top="0%", position='relative',
Expand Down Expand Up @@ -243,7 +242,6 @@ def __init__(self, tiles='OpenStreetMap', name=None,
{% endmacro %}
""")


class FeatureGroup(Layer):
def __init__(self, name=None, overlay=True, control=True):
"""
Expand Down Expand Up @@ -273,7 +271,6 @@ def __init__(self, name=None, overlay=True, control=True):
{% endmacro %}
""")


class LayerControl(MacroElement):
"""Adds a layer control to the map."""
def __init__(self):
Expand Down Expand Up @@ -374,7 +371,6 @@ def __init__(self, color='blue', icon_color='white', icon='info-sign',
{% endmacro %}
""")


class Marker(MacroElement):
def __init__(self, location, popup=None, icon=None):
"""Create a simple stock Leaflet marker on the map, with optional
Expand Down Expand Up @@ -421,6 +417,11 @@ def __init__(self, location, popup=None, icon=None):
{% endmacro %}
""")

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
return [[self.location[0], self.location[1]], [self.location[0], self.location[1]]]

class Popup(Element):
def __init__(self, html=None, max_width=300):
Expand Down Expand Up @@ -469,7 +470,6 @@ def render(self, **kwargs):
self._template.render(this=self, kwargs=kwargs)),
name=self.get_name())


class FitBounds(MacroElement):
def __init__(self, bounds, padding_top_left=None,
padding_bottom_right=None, padding=None, max_zoom=None):
Expand All @@ -491,7 +491,6 @@ def __init__(self, bounds, padding_top_left=None,
the same value.
max_zoom: int, default None
Maximum zoom to be used.

"""
super(FitBounds, self).__init__()
self._name = 'FitBounds'
Expand Down
17 changes: 8 additions & 9 deletions folium/plugins/boat_marker.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
import json
from jinja2 import Template

from folium.element import JavascriptLink, MacroElement, Figure
from folium.element import JavascriptLink, Figure
from folium.map import Marker


class BoatMarker(MacroElement):
class BoatMarker(Marker):
"""Adds a BoatMarker layer on the map."""
def __init__(self, position=None, heading=0,
wind_heading=None, wind_speed=0, **kwargs):
def __init__(self, location, popup=None, icon=None,
heading=0, wind_heading=None, wind_speed=0, **kwargs):
"""Creates a BoatMarker plugin to append into a map with
Map.add_plugin.

Parameters
----------
position: tuple of length 2, default None
location: tuple of length 2, default None
The latitude and longitude of the marker.
If None, then the middle of the map is used.

Expand All @@ -36,9 +36,8 @@ def __init__(self, position=None, heading=0,
wind_speed: int, default 0
Speed of the wind in knots.
"""
super(BoatMarker, self).__init__()
super(BoatMarker, self).__init__(location, popup=popup, icon=icon)
self._name = 'BoatMarker'
self.position = None if position is None else tuple(position)
self.heading = heading
self.wind_heading = wind_heading
self.wind_speed = wind_speed
Expand All @@ -47,7 +46,7 @@ def __init__(self, position=None, heading=0,
self._template = Template(u"""
{% macro script(this, kwargs) %}
var {{this.get_name()}} = L.boatMarker(
[{{this.position[0]}},{{this.position[1]}}],
[{{this.location[0]}},{{this.location[1]}}],
{{this.kwargs}}).addTo({{this._parent.get_name()}});
{{this.get_name()}}.setHeadingWind({{this.heading}}, {{this.wind_speed}}, {{this.wind_heading}});
{% endmacro %}
Expand Down
20 changes: 19 additions & 1 deletion folium/plugins/heat_map.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

from folium.element import JavascriptLink, Figure
from folium.map import TileLayer

from folium.utilities import none_min, none_max

class HeatMap(TileLayer):
def __init__(self, data, name=None, min_opacity=0.5, max_zoom=18,
Expand Down Expand Up @@ -79,3 +79,21 @@ def render(self, **kwargs):
figure.header.add_children(
JavascriptLink("https://leaflet.github.io/Leaflet.heat/dist/leaflet-heat.js"), # noqa
name='leaflet-heat.js')

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
bounds = [[None,None],[None,None]]
for point in self.data:
bounds = [
[
none_min(bounds[0][0], point[1]),
none_min(bounds[0][1], point[0]),
],
[
none_max(bounds[1][0], point[1]),
none_max(bounds[1][1], point[0]),
],
]
return bounds
6 changes: 6 additions & 0 deletions folium/plugins/image_overlay.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,9 @@ def __init__(self, image, bounds, opacity=1., attr=None,
).addTo({{this._parent.get_name()}});
{% endmacro %}
""")

def _get_self_bounds(self):
"""Computes the bounds of the object itself (not including it's children)
in the form [[lat_min, lon_min], [lat_max, lon_max]]
"""
return self.bounds
Loading