11from jinja2 import Template
2-
3- from folium .elements import JSCSSMixin
4- from folium .map import Layer
2+ from folium .elements import JSCSSMixin , MacroElement
53from folium .utilities import parse_options
64
7-
8- class OverlappingMarkerSpiderfier (JSCSSMixin , Layer ):
9- """A plugin that handles overlapping markers by spreading them into a spider-like pattern.
10-
11- This plugin uses the OverlappingMarkerSpiderfier-Leaflet library to manage markers
12- that are close to each other or overlap. When clicked, the overlapping markers
13- spread out in a spiral pattern, making them easier to select individually.
14-
5+ class OverlappingMarkerSpiderfier (JSCSSMixin , MacroElement ):
6+ """
7+ A plugin that handles overlapping markers on a map by spreading them out in a spiral or circle pattern when clicked.
8+
9+ This plugin is useful when you have multiple markers in close proximity that would otherwise be difficult to interact with.
10+ When a user clicks on a cluster of overlapping markers, they spread out in a 'spider' pattern, making each marker
11+ individually accessible.
12+
13+ Markers must be added to the map **before** calling `oms.add_to(map)`.
14+ The plugin identifies and manages all markers already present on the map.
15+
1516 Parameters
1617 ----------
17- markers : list, optional
18- List of markers to be managed by the spiderfier
19- name : string, optional
20- Name of the layer control
21- overlay : bool, default True
22- Whether the layer will be included in LayerControl
23- control : bool, default True
24- Whether the layer will be included in LayerControl
25- show : bool, default True
26- Whether the layer will be shown on opening
2718 options : dict, optional
28- Additional options to be passed to the OverlappingMarkerSpiderfier instance
29- See https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet for available options
30-
19+ The options to configure the spiderfier behavior:
20+ - keepSpiderfied : bool, default True
21+ If true, markers stay spiderfied after clicking
22+ - nearbyDistance : int, default 20
23+ Pixels away from a marker that is considered overlapping
24+ - legWeight : float, default 1.5
25+ Weight of the spider legs
26+ - circleSpiralSwitchover : int, optional
27+ Number of markers at which to switch from circle to spiral pattern
28+
3129 Example
3230 -------
33- >>> markers = [marker1, marker2, marker3] # Create some markers
34- >>> spiderfier = OverlappingMarkerSpiderfier(
35- ... markers=markers, keepSpiderfied=True, nearbyDistance=20
36- ... )
37- >>> spiderfier.add_to(m) # Add to your map
31+ >>> oms = OverlappingMarkerSpiderfier(options={
32+ ... "keepSpiderfied": True,
33+ ... "nearbyDistance": 30,
34+ ... "legWeight": 2.0
35+ ... })
36+ >>> oms.add_to(map)
3837 """
39-
4038 _template = Template (
4139 """
4240 {% macro script(this, kwargs) %}
43- var {{ this.get_name() }} = (function () {
44- var layerGroup = L.layerGroup();
45-
41+ (function () {
4642 try {
4743 var oms = new OverlappingMarkerSpiderfier(
4844 {{ this._parent.get_name() }},
4945 {{ this.options|tojson }}
5046 );
5147
52- var popup = L.popup( {
53- offset: L.point(0, -30)
48+ oms.addListener('spiderfy', function() {
49+ {{ this._parent.get_name() }}.closePopup();
5450 });
5551
56- oms.addListener('click', function(marker) {
57- var content;
58- if (marker.options && marker.options.options && marker.options.options.desc) {
59- content = marker.options.options.desc;
60- } else if (marker._popup && marker._popup._content) {
61- content = marker._popup._content;
62- } else {
63- content = "";
64- }
65-
66- if (content) {
67- popup.setContent(content);
68- popup.setLatLng(marker.getLatLng());
69- {{ this._parent.get_name() }}.openPopup(popup);
52+ {{ this._parent.get_name() }}.eachLayer(function(layer) {
53+ if (
54+ layer instanceof L.Marker
55+ ) {
56+ oms.addMarker(layer);
7057 }
7158 });
7259
73- oms.addListener('spiderfy', function(markers) {
74- {{ this._parent.get_name() }}.closePopup();
75- });
76-
77- {% for marker in this.markers %}
78- var {{ marker.get_name() }} = L.marker(
79- {{ marker.location|tojson }},
80- {{ marker.options|tojson }}
81- );
82-
83- {% if marker.popup %}
84- {{ marker.get_name() }}.bindPopup({{ marker.popup.get_content()|tojson }});
85- {% endif %}
86-
87- oms.addMarker({{ marker.get_name() }});
88- layerGroup.addLayer({{ marker.get_name() }});
89- {% endfor %}
9060 } catch (error) {
91- console.error('Error in OverlappingMarkerSpiderfier initialization :', error);
61+ console.error('Error initializing OverlappingMarkerSpiderfier:', error);
9262 }
93-
94- return layerGroup;
9563 })();
9664 {% endmacro %}
97-
9865 """
9966 )
10067
@@ -105,27 +72,14 @@ class OverlappingMarkerSpiderfier(JSCSSMixin, Layer):
10572 )
10673 ]
10774
108- def __init__ (
109- self ,
110- markers = None ,
111- name = None ,
112- overlay = True ,
113- control = True ,
114- show = True ,
115- options = None ,
116- ** kwargs ,
117- ):
118- super ().__init__ (name = name , overlay = overlay , control = control , show = show )
75+ def __init__ (self , options = None , ** kwargs ):
76+ super ().__init__ ()
11977 self ._name = "OverlappingMarkerSpiderfier"
120-
121- self .markers = markers or []
122-
12378 default_options = {
12479 "keepSpiderfied" : True ,
12580 "nearbyDistance" : 20 ,
12681 "legWeight" : 1.5 ,
12782 }
12883 if options :
12984 default_options .update (options )
130-
13185 self .options = parse_options (** default_options , ** kwargs )
0 commit comments