0

I created a map using folium and python, which show the path of GPX files. The starting point of each gpx track is located by using a marker. Since there are several paths, they may be overlaid on each other. I'd like to select only one starting point (markers) and when the marker is clicked, the track appears.

Here is a picture of my issue in order to better understand the situation.

Overlaid on each other paths

How can I just show a basic straight line when clicking on the marker (made with folium.PolyLine like the gpx files) ?

I've found a partially solution here, but I am not familiar at all with the antpath and it's not my point.

I guess the following part of the thread above, especially the "coords" and "ant_path" variables, has to be adapted but I don't know how.

# 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

Any help would be appreciate ! Thanks in advance ;)

Pierre-Loic
  • 1,524
  • 1
  • 6
  • 12
K3liane
  • 1
  • 1

2 Answers2

0

Sorry for the late reply. Data are extracted directly from my garmin account, with this code. They're all listed in a csv table and exploitated as a DataFrame in python. The starting point of each activities is stored in the "df_loc" dataframe, which allow to create the folium map and add markers as :

map = folium.Map(location=[df_loc["Begin Latitude (°DD)"][0].mean(),df_loc["Begin Longitude (°DD)"][0].mean()], control_scale=True, zoom_start = 6)
folium.Marker(
    location = [row["Begin Latitude (°DD)"],row["Begin Longitude (°DD)"]],
    icon=folium.Icon(color=col,icon='circle',prefix='fa'),
    popup = popact + '<b> Date_</b>' + str(row['Date']) + '<b> Vitesse_(km/h)</b>' + ':' + str(round(row["Average Moving Speed (km/h)"],2)) + '<b> Distance_(km)</b>' + ':' + str(round(row["Distance (km)"],2)) + '<b> Dénivelé_(m)</b>' + ':' + str(round(row["Elevation Gain (m)"],2))
).add_to(map)

On the other hand, GPX are read with the following code and the path is added to the map thanks to a loop :

filename = "path/activity_" + str(row["Activity ID"]) + ".gpx"
gpx = gpxpy.parse(open(filename))
track = gpx.tracks[0]
segment = track.segments[0]
data_act= []
segment_length = segment.length_3d()
for point_idx, point in enumerate(segment.points):
    data_act.append([point.longitude, point.latitude,point.elevation,
             point.time, segment.get_speed(point_idx)])

    columns = ['Longitude', 'Latitude', 'Altitude', 'Time', 'Speed']
df_gpx = pd.DataFrame(data_act, columns=columns)
folium.PolyLine(df_gpx[['Latitude','Longitude']].values, color=col, weight=2.5, opacity=1).add_to(map)

Finally, the map is plotted and saved.

map.save('path')

I know that I have to adapted the referenced answer given in the question but I don't know how.

Thanks for the help

K3liane
  • 1
  • 1
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 26 '22 at 07:39
0

Based on what you provided and not having the actual data. This is the best I could try:

import gpxpy
import pandas as pd
import folium
from folium.map import Marker, Template

def get_path_coordinates(activity_id):
    filename = f"path/activity_{activity_id}.gpx"
    gpx = gpxpy.parse(open(filename))
    track = gpx.tracks[0]
    segment = track.segments[0]
    data_act= []
    segment_length = segment.length_3d()
    for point_idx, point in enumerate(segment.points):
        data_act.append([point.longitude, point.latitude,point.elevation,
                point.time, segment.get_speed(point_idx)])

        columns = ['Longitude', 'Latitude', 'Altitude', 'Time', 'Speed']
    df_gpx = pd.DataFrame(data_act, columns=columns)
    return list(map(list, zip(df_gpx.Longitude, df_gpx.Latitude)))


# 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)

#Map
df_loc = pd.read_csv('activities.csv')
map = folium.Map(
    location=[df_loc["Begin Latitude (°DD)"][0].mean(),df_loc["Begin Longitude (°DD)"][0].mean()],
    control_scale=True,
    zoom_start = 6)

# Add js to draw paths on marker click
map_id = map.get_name()
click_js = f"""function onClick(e) {{                                 
                 var coords = e.target.options.pathCoords;                 
                 var path = L.polyline(coords); 
                
                {map_id}.eachLayer(function(layer){{
                   if (layer instanceof L.Polyline)
                      {{ {map_id}.removeLayer(layer) }}
                      }});
                     
                path.addTo({map_id});
                 }}"""
                 
e = folium.Element(click_js)
html = map.get_root()
html.script.add_child(e)

for i, row in df_loc.iterrows():
    activity_id = row["Activity ID"]   
    path_coordinates = get_path_coordinates(activity_id)    
    folium.Marker(
        location = [row["Begin Latitude (°DD)"],row["Begin Longitude (°DD)"]],
        pathCoords=path_coordinates
    ).add_to(map)  
PeaceLeka
  • 1,138
  • 1
  • 9
  • 11
  • Hi @PeaceLeka ! I really want to thank you for this code. When I run it with my data, there is this following error : 'Map' is not callable. I tried to change the folium map's name (which is named map) to mymap, the error disappears but the onClick event doesn't work. Any clue ? – K3liane Dec 30 '22 at 11:47
  • Change this line `map_id = map.get_name()` to `map_id = mymap.get_name()` – PeaceLeka Dec 30 '22 at 12:03