3

I'm working on a project where I'm using Folium to create an interactive map in Python. I want to use a marker that can visualize directional data; however, Folium doesn't seem to offer any and I had to make a custom one (ie an arrow) from HTML. I've now run into an issue with controlling the z-order of a custom marker created using DivIcon(). Specifically, I have a custom arrow marker I want to appear below other markers on the map; however, no matter what I do, this custom arrow marker stays on top.

I've tried various methods to set the z-order, including:

  1. Adding layers to the map in the desired layer order.
  2. Using a .png with folium.Icon(); however, since I'm also changing the color of the arrows based on a value, this doesn't seem feasible.
  3. Adding a z-order property to the HTML of the DivIcon() marker.
  4. Using the keep_in_front() method on the layers I want on top.
  5. Setting the z_index parameter for the other layers I want on top.
  6. Using the z_index_offset parameter when creating the DivIcon() marker.
  7. I've thought about creating arrows as polygons; however, I need something that scales with zoom.

Unfortunately, none of these approaches seem to work as expected, and the custom DivIcon() marker still appears on top. I've also tried other methods that I might have forgotten to mention.

Here's a simplified version of my code for troubleshooting:

import folium

def create_arrow_marker(angle, color, z_index=100, opacity=1):
    return f'''
    <div style="z-index: {z_index};">
        <svg width="30" height="30" viewBox="0 0 30 30" xmlns="http://www.w3.org/2000/svg" style="transform: rotate({angle}deg);">
            <!-- Arrow shaft -->
            <line x1="15" y1="25" x2="15" y2="5" style="stroke:{color};stroke-width:4; opacity:{opacity}" />
            <!-- Arrowhead -->
            <polygon points="15,0 5,10 25,10" fill="{color}" style="opacity:{opacity}"/>
        </svg>
    </div>
    '''

# Create a Folium map
m = folium.Map(location=[51.5074, -0.1278], zoom_start=10)

# Example coordinates for the markers (same spot)
lat, lon = 51.5074, -0.1278

# Create the arrow marker HTML
angle = 45  # Angle of the arrow (you can change this as needed)
color = 'blue'  # Color of the arrow
z_index_arrow = 100  # Z-index for the arrow marker
arrow_marker_html = create_arrow_marker(angle, color, z_index_arrow)

# Add the custom arrow marker to the map
icon = folium.DivIcon(html=arrow_marker_html)
folium.Marker(location=[lat, lon], icon=icon).add_to(m)

# Create a normal circle marker
radius = 50  # Radius of the circle marker
color_circle = 'red'  # Color of the circle marker
z_index_circle = 200  # Z-index for the circle marker

# Add the circle marker to the map
folium.CircleMarker(location=[lat, lon], radius=radius, color=color_circle, fill=True, fill_color=color_circle, fill_opacity=1, z_index=z_index_circle).add_to(m)

# Display the map
m

Output: enter image description here

In the code example and the visual you can see the arrow is on top.

How do I control it's z-order?

Versions: Python 3.9.7 on Windows Folium 0.12.1.post1

1 Answer 1

6

Solution for your problem are Leaflet map panes (see https://leafletjs.com/reference.html#map-pane), which are DOM elements used to control the ordering of layers on the map. Each map pane has it's own z_index. For example, tile layers are rendered to pane with z_index 200, vector layer to pane with zIndex 400 and markers to pane with z_index 600 (pane for markers shadows has z_index 500).

You can create your own custom map pane with desired z_index (see https://python-visualization.github.io/folium/latest/reference.html#folium.map.CustomPane), which in your case would be 450 (to place it under markers) and then assign your arrow marker to this pane.

Relevant part of the code could then look something like this:

folium.map.CustomPane("arrow", z_index=450).add_to(m)
icon = folium.DivIcon(html=arrow_marker_html)
folium.Marker(location=[lat, lon], icon=icon, pane="arrow").add_to(m)
2
  • This answer worked for me; however, I needed the syntax z_index = 450
    – Austin
    Dec 2, 2023 at 0:05
  • My typo from being use to JS Leaflet. Corrected.
    – TomazicM
    Dec 2, 2023 at 5:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.