Skip to content

Commit a7a6ab6

Browse files
jtbakerConengmo
andauthored
GeoJson Marker (#957)
* Initial rework of pointToLayer Add handling for other icons Overload render method to add runtime validation On location field Use name attribute in error reporting * Update mouse event handling * Add notebook example and subway dataset * code style * add Marker example, run output * Add docstring Co-authored-by: Frank <[email protected]>
1 parent b3f2156 commit a7a6ab6

File tree

8 files changed

+13701
-10
lines changed

8 files changed

+13701
-10
lines changed

examples/GeoJSONMarker.ipynb

+373
Large diffs are not rendered by default.

examples/data/subwaystations.geojson

+6,627
Large diffs are not rendered by default.

folium/features.py

+44-4
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
parse_options,
2929
camelize
3030
)
31-
from folium.vector_layers import PolyLine, path_options
31+
from folium.vector_layers import Circle, CircleMarker, PolyLine, path_options
3232

3333
from jinja2 import Template
3434

@@ -346,6 +346,12 @@ class GeoJson(Layer):
346346
tooltip: GeoJsonTooltip, Tooltip or str, default None
347347
Display a text when hovering over the object. Can utilize the data,
348348
see folium.GeoJsonTooltip for info on how to do that.
349+
popup: GeoJsonPopup, optional
350+
Show a different popup for each feature by passing a GeoJsonPopup object.
351+
marker: Circle, CircleMarker or Marker, optional
352+
If your data contains Point geometry, you can format the markers by passing a Cirle,
353+
CircleMarker or Marker object with your wanted options. The `style_function` and
354+
`highlight_function` will also target the marker object you passed.
349355
embed: bool, default True
350356
Whether to embed the data in the html file or not. Note that disabling
351357
embedding is only supported if you provide a file link or URL.
@@ -396,21 +402,48 @@ class GeoJson(Layer):
396402
}
397403
}
398404
{%- endif %}
405+
406+
{%- if this.marker %}
407+
function {{ this.get_name() }}_pointToLayer(feature, latlng) {
408+
var opts = {{ this.marker.options | tojson | safe }};
409+
{% if this.marker._name == 'Marker' and this.marker.icon %}
410+
const iconOptions = {{ this.marker.icon.options | tojson | safe }}
411+
const iconRootAlias = L{%- if this.marker.icon._name == "Icon" %}.AwesomeMarkers{%- endif %}
412+
opts.icon = new iconRootAlias.{{ this.marker.icon._name }}(iconOptions)
413+
{% endif %}
414+
{%- if this.style_function %}
415+
let style = {{ this.get_name()}}_styler(feature)
416+
Object.assign({%- if this.marker.icon -%}opts.icon.options{%- else -%} opts {%- endif -%}, style)
417+
{% endif %}
418+
return new L.{{this.marker._name}}(latlng, opts)
419+
}
420+
{%- endif %}
421+
399422
function {{this.get_name()}}_onEachFeature(feature, layer) {
400423
layer.on({
401424
{%- if this.highlight %}
402425
mouseout: function(e) {
403-
{{ this.get_name() }}.resetStyle(e.target);
426+
if(typeof e.target.setStyle === "function"){
427+
{{ this.get_name() }}.resetStyle(e.target);
428+
}
404429
},
405430
mouseover: function(e) {
406-
e.target.setStyle({{ this.get_name() }}_highlighter(e.target.feature));
431+
if(typeof e.target.setStyle === "function"){
432+
const highlightStyle = {{ this.get_name() }}_highlighter(e.target.feature)
433+
e.target.setStyle(highlightStyle);
434+
}
407435
},
408436
{%- endif %}
409437
{%- if this.zoom_on_click %}
410438
click: function(e) {
411439
if (typeof e.target.getBounds === 'function') {
412440
{{ this.parent_map.get_name() }}.fitBounds(e.target.getBounds());
413441
}
442+
else if (typeof e.target.getLatLng === 'function'){
443+
let zoom = {{ this.parent_map.get_name() }}.getZoom()
444+
zoom = zoom > 12 ? zoom : zoom + 1
445+
{{ this.parent_map.get_name() }}.flyTo(e.target.getLatLng(), zoom)
446+
}
414447
}
415448
{%- endif %}
416449
});
@@ -423,6 +456,9 @@ class GeoJson(Layer):
423456
{% if this.style %}
424457
style: {{ this.get_name() }}_styler,
425458
{%- endif %}
459+
{%- if this.marker %}
460+
pointToLayer: {{ this.get_name() }}_pointToLayer
461+
{%- endif %}
426462
});
427463
428464
function {{ this.get_name() }}_add (data) {
@@ -443,7 +479,7 @@ class GeoJson(Layer):
443479
def __init__(self, data, style_function=None, highlight_function=None, # noqa
444480
name=None, overlay=True, control=True, show=True,
445481
smooth_factor=None, tooltip=None, embed=True, popup=None,
446-
zoom_on_click=False):
482+
zoom_on_click=False, marker=None):
447483
super(GeoJson, self).__init__(name=name, overlay=overlay,
448484
control=control, show=show)
449485
self._name = 'GeoJson'
@@ -455,6 +491,10 @@ def __init__(self, data, style_function=None, highlight_function=None, # noqa
455491
self.style = style_function is not None
456492
self.highlight = highlight_function is not None
457493
self.zoom_on_click = zoom_on_click
494+
if marker:
495+
if not isinstance(marker, (Circle, CircleMarker, Marker)):
496+
raise TypeError("Only Marker, Circle, and CircleMarker are supported as GeoJson marker types.")
497+
self.marker = marker
458498

459499
self.data = self.process_data(data)
460500

folium/map.py

+7-2
Original file line numberDiff line numberDiff line change
@@ -270,18 +270,19 @@ class Marker(MacroElement):
270270
{% endmacro %}
271271
""")
272272

273-
def __init__(self, location, popup=None, tooltip=None, icon=None,
273+
def __init__(self, location=None, popup=None, tooltip=None, icon=None,
274274
draggable=False, **kwargs):
275275
super(Marker, self).__init__()
276276
self._name = 'Marker'
277-
self.location = validate_location(location)
277+
self.location = validate_location(location) if location else None
278278
self.options = parse_options(
279279
draggable=draggable or None,
280280
autoPan=draggable or None,
281281
**kwargs
282282
)
283283
if icon is not None:
284284
self.add_child(icon)
285+
self.icon = icon
285286
if popup is not None:
286287
self.add_child(popup if isinstance(popup, Popup)
287288
else Popup(str(popup)))
@@ -296,6 +297,10 @@ def _get_self_bounds(self):
296297
"""
297298
return [self.location, self.location]
298299

300+
def render(self):
301+
if self.location is None:
302+
raise ValueError("{} location must be assigned when added directly to map.".format(self._name))
303+
super(Marker, self).render()
299304

300305
class Popup(Element):
301306
"""Create a Popup instance that can be linked to a Layer.

folium/vector_layers.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ class Circle(Marker):
263263
{% endmacro %}
264264
""")
265265

266-
def __init__(self, location, radius, popup=None, tooltip=None, **kwargs):
266+
def __init__(self, location=None, radius=50, popup=None, tooltip=None, **kwargs):
267267
super(Circle, self).__init__(location, popup=popup, tooltip=tooltip)
268268
self._name = 'circle'
269269
self.options = path_options(line=False, radius=radius, **kwargs)
@@ -300,7 +300,7 @@ class CircleMarker(Marker):
300300
{% endmacro %}
301301
""")
302302

303-
def __init__(self, location, radius=10, popup=None, tooltip=None, **kwargs):
303+
def __init__(self, location=None, radius=10, popup=None, tooltip=None, **kwargs):
304304
super(CircleMarker, self).__init__(location, popup=popup,
305305
tooltip=tooltip)
306306
self._name = 'CircleMarker'

0 commit comments

Comments
 (0)