Search code examples
pythonfolium

Show path in folium map by clicking or hovering marker


I made a folium map with lot of markers, each of them has a tooltip and a popup filled up with html formatted text. For every position defined by the markers I have additional geo datapoints which I want to display as a line / path / route / AntPath .. whatever. My Problem: The additional line should only appear when you click the marker (--> opens also the popup) or when hovering the marker (--> opens the tooltip).

I have no clue if it even is possible and hope to find some inspiration here

enter image description here

Here is an example which can be used in a jupyter. When you have installed pandas and folium it should work. I added some AntPath, but they never disappear as they are in the map. If you add them to the markercluster, the ants are not moving, if I add them to the popup everything is broken.


# imports
import pandas as pd
import folium
from folium.plugins import HeatMap, AntPath

# functions
def segmrk(latlng, geopath, pop='some text in the popup', tool='tooltiptext<br>in html'):
    # define marker
    style = ['bicycle', 'blue', '#FFFFFF']
    # popup
    iframe = folium.IFrame(pop,  # html style text .. next step: change font!!
                           width=200,
                           height=200
                           )
    fpop = folium.Popup(iframe)
    #AntPath(geopath).add_to(fpop)
    
    # marker
    mrk = folium.Marker(location=latlng,
                        popup=fpop,
                        tooltip=tool,
                        icon=folium.Icon(icon=style[0], prefix='fa',
                                         color=style[1],
                                         icon_color=style[2]
                                         ),
                        )
    return mrk

# sample data
df = pd.DataFrame()
df['geo'] = [[52.5172, 12.1024],[52.5172, 12.2024],[52.5172, 12.3024]]
df['geo_path'] = [[[52.5172, 12.1024],[52.6172, 12.1024],[52.7172, 12.1024],[52.7172, 12.1024]],
                  [[52.5172, 12.2024],[52.6172, 12.2024],[52.7172, 12.2024],[52.7172, 12.2024]],
                  [[52.5172, 12.3024],[52.6172, 12.3024],[52.7172, 12.3024],[52.7172, 12.3024]],
                 ]

# define map

geo_start = [52.5172, 12.2024]
dmap = folium.Map(location=geo_start,
                  zoom_start=10,
                  tiles='OpenStreetMap'
                  )

mapstyle_2 = folium.raster_layers.TileLayer(tiles='CartoDB dark_matter',
                                            name='dark',
                                            overlay=False,
                                            control=True,
                                            show=True,
                                            )
mapstyle_2.add_to(dmap)

# add full screen button
folium.plugins.Fullscreen().add_to(dmap)


# add layercontrol

# markergroups in layercontrol
mc = folium.plugins.MarkerCluster(name='Segment Markers',
                                  overlay=True,
                                  control=True,
                                  show=True,
                                  disableClusteringAtZoom=10
                                  )
mc.add_to(dmap)
mcsub1 = folium.plugins.FeatureGroupSubGroup(mc, name='- markers subcluster',
                                             show=True,
                                             control=False)  # checkmark actually not shown
mcsub1.add_to(dmap)

# the layercontrol itself
lc = folium.map.LayerControl(collapsed=False)
lc.add_to(dmap)

# add geo markers
for _, data in df.iterrows():
    mrk = segmrk(data['geo'], data['geo_path'])
    mrk.add_to(mcsub1)
    # this AntPath should be shown when popup appears OR when hovering marker
    AntPath(data['geo_path']).add_to(dmap)
    
# show map
dmap

Solution

  • If you don't have too many markers, you can try this.

    # imports
    import pandas as pd
    import folium
    from folium.plugins import HeatMap
    from folium.map import Marker, Template
    
    # sample data
    df = pd.DataFrame()
    df['geo'] = [[52.5172, 12.1024],[52.5172, 12.2024],[52.5172, 12.3024]]
    df['geo_path'] = [[[52.5172, 12.1024],[52.6172, 12.1024],[52.7172, 12.1024],[52.7172, 12.1024]],
                      [[52.5172, 12.2024],[52.6172, 12.2024],[52.7172, 12.2024],[52.7172, 12.2024]],
                      [[52.5172, 12.3024],[52.6172, 12.3024],[52.7172, 12.3024],[52.7172, 12.3024]],
                     ]
    
    # define map
    
    geo_start = [52.5172, 12.2024]
    dmap = folium.Map(location=geo_start,
                      zoom_start=10,
                      tiles='OpenStreetMap'
                      )
    
    mapstyle_2 = folium.raster_layers.TileLayer(tiles='CartoDB dark_matter',
                                                name='dark',
                                                overlay=False,
                                                control=True,
                                                show=True,
                                                )
    mapstyle_2.add_to(dmap)
    
    # add full screen button
    folium.plugins.Fullscreen().add_to(dmap)
    
    # Modify Marker template to include the onClick event
    click_template = """{% macro script(this, kwargs) %}
        var {{ this.get_name() }} = L.marker(
            {{ this.location|tojson }},
            {{ this.options|tojson }}
        ).addTo({{ this._parent.get_name() }}).on('click', onClick);
    {% endmacro %}"""
    
    # Change template to custom template
    Marker._template = Template(click_template)
    
    
    
    # add layercontrol
    
    # markergroups in layercontrol
    mc = folium.plugins.MarkerCluster(name='Segment Markers',
                                      overlay=True,
                                      control=True,
                                      show=True,
                                      disableClusteringAtZoom=10
                                      )
    mc.add_to(dmap)
    mcsub1 = folium.plugins.FeatureGroupSubGroup(mc, name='- markers subcluster',
                                                 show=True,
                                                 control=False)  # checkmark actually not shown
    mcsub1.add_to(dmap)
    
    
    # the layercontrol itself
    lc = folium.map.LayerControl(collapsed=False)
    lc.add_to(dmap)
    
    # Create the onClick listener function as a branca element and add to the map html
    map_id = dmap.get_name()
    click_js = f"""function onClick(e) {{                
                        
                                     
                     var coords = e.target.options.pathCoords;
                     //var coords = JSON.stringify(coords);
                     //alert(coords);
                     var ant_path = L.polyline.antPath(coords, {{
                    "delay": 400,
                    "dashArray": [
                        10,
                        20
                    ],
                    "weight": 5,
                    "color": "#0000FF",
                    "pulseColor": "#FFFFFF",
                    "paused": false,
                    "reverse": false,
                    "hardwareAccelerated": true
                    }}); 
                    
                    {map_id}.eachLayer(function(layer){{
                       if (layer instanceof L.Polyline)
                          {{ {map_id}.removeLayer(layer) }}
                          }});
                         
                    ant_path.addTo({map_id});
                     }}"""
                     
    e = folium.Element(click_js)
    html = dmap.get_root()
    html.script.add_child(e)
    
    
    # add geo markers
    for index, data in df.iterrows():
        mrk = folium.Marker(data['geo'], pathCoords=data['geo_path'])
        mrk.add_to(mcsub1)
        
    # Add leaflet antpath plugin cdn link
    link = folium.JavascriptLink("https://cdn.jsdelivr.net/npm/leaflet-ant-path@1.3.0/dist/leaflet-ant-path.js")
    dmap.get_root().html.add_child(link)  
    # show map
    
    dmap
    ​