5
votes

I would like to add a categorical/numerical legend to a folium map like addLenged() from R does.

Examples:

Numerical legend

numeric legend

I also would like to add a categorical legend like this:

Categorical legend

enter image description here

For now I only have this code, I am stuck trying to achieve what addLegend from R does.

Function

def add_categorical_legend(folium_map, title, colors, labels):
    if len(colors) != len(labels):
        raise ValueError("colors and labels must have the same length.")

    color_by_label = dict(zip(labels, colors))
    
    legend_categories = ""     
    for label, color in color_by_label.items():
        legend_categories += f"<li><span style='background:{color}'></span>{label}</li>"
        
    legend_html = f"""
    <div id='maplegend' class='maplegend'>
      <div class='legend-title'>{title}</div>
      <div class='legend-scale'>
        <ul class='legend-labels'>
        {legend_categories}
        </ul>
      </div>
    </div>
    """
    script = f"""
        <script type="text/javascript">
        var oneTimeExecution = (function() {{
                    var executed = false;
                    return function() {{
                        if (!executed) {{
                             var checkExist = setInterval(function() {{
                                       if ((document.getElementsByClassName('leaflet-top leaflet-right').length) || (!executed)) {{
                                          document.getElementsByClassName('leaflet-top leaflet-right')[0].style.display = "flex"
                                          document.getElementsByClassName('leaflet-top leaflet-right')[0].style.flexDirection = "column"
                                          document.getElementsByClassName('leaflet-top leaflet-right')[0].innerHTML += `{legend_html}`;
                                          clearInterval(checkExist);
                                          executed = true;
                                       }}
                                    }}, 100);
                        }}
                    }};
                }})();
        oneTimeExecution()
        </script>
      """
   

    css = """

    <style type='text/css'>
      .maplegend {
        z-index:9999;
        float:right;
        background-color: rgba(255, 255, 255, 1);
        border-radius: 5px;
        border: 2px solid #bbb;
        padding: 10px;
        font-size:12px;
        positon: relative;
      }
      .maplegend .legend-title {
        text-align: left;
        margin-bottom: 5px;
        font-weight: bold;
        font-size: 90%;
        }
      .maplegend .legend-scale ul {
        margin: 0;
        margin-bottom: 5px;
        padding: 0;
        float: left;
        list-style: none;
        }
      .maplegend .legend-scale ul li {
        font-size: 80%;
        list-style: none;
        margin-left: 0;
        line-height: 18px;
        margin-bottom: 2px;
        }
      .maplegend ul.legend-labels li span {
        display: block;
        float: left;
        height: 16px;
        width: 30px;
        margin-right: 5px;
        margin-left: 0;
        border: 0px solid #ccc;
        }
      .maplegend .legend-source {
        font-size: 80%;
        color: #777;
        clear: both;
        }
      .maplegend a {
        color: #777;
        }
    </style>
    """

    folium_map.get_root().header.add_child(folium.Element(script + css))

    return folium_map

Reproducible example

import folium
m = folium.Map()

m = add_categorical_legend(m, 'My title',
                             colors = ['#000','#03cafc'],
                           labels = ['Heat', 'Cold'])
m = add_categorical_legend(m, 'My title 2',
                             colors = ['#F23','#777'],
                           labels = ['Heat 2', 'Cold 2'])
m.save("map.html")
m

Result

enter image description here

Problems

  • However the legend I have made conflicts with folium LayerControls and they stop working which for me is a very big problem because legends are for describing what I am using in LayerControls. I don't know what is the reason.
  • Also this only works for categorical data.

Important

  • I would like that legends could be added to top | bot & right | left positions like addLegend from R does. Giving absolute position is not an option.

Any help is appreciated

NOTE: I don't want to use branca color map because I would like to have a legend like a have shown you in the images

1
I have the same problem at the moment. Have you perhaps figured out how to solve it in the meantime?Max Muller
@MaxMuller no because I stopped working with Python for doing these type of complex visualizations with maps. I would suggest using a front end framework like React and use the original Leaflet library. And If your project is really simple you could also try with Plotly for Python (Has good but simple support for map visualizations)Ángel

1 Answers

2
votes

I'm not experienced with folium, but I think the reference legend provided by the official will help you solve your problem: I applied the example on this page to the Choropleth example. You're more experienced with the web code, so customize it. I checked and did not find any of the features provided by folium that can be configured for deployment.

import pandas as pd
import folium
import branca

legend_html = '''
{% macro html(this, kwargs) %}
<div style="
    position: fixed; 
    bottom: 50px;
    left: 50px;
    width: 250px;
    height: 80px;
    z-index:9999;
    font-size:14px;
    ">
    <p><a style="color:#000000;font-size:150%;margin-left:20px;">&diams;</a>&emsp;Heat</p>
    <p><a style="color:#03cafc;font-size:150%;margin-left:20px;">&diams;</a>&emsp;Cold</p>
</div>
<div style="
    position: fixed; 
    bottom: 50px;
    left: 50px;
    width: 150px;
    height: 80px; 
    z-index:9998;
    font-size:14px;
    background-color: #ffffff;

    opacity: 0.7;
    ">
</div>
{% endmacro %}
'''
legend = branca.element.MacroElement()
legend._template = branca.element.Template(legend_html)

url = 'https://raw.githubusercontent.com/python-visualization/folium/master/examples/data'
state_geo = f'{url}/us-states.json'
state_unemployment = f'{url}/US_Unemployment_Oct2012.csv'
state_data = pd.read_csv(state_unemployment)

m = folium.Map(location=[48, -102], zoom_start=4)

folium.Choropleth(
    geo_data=state_geo,
    name='choropleth',
    data=state_data,
    columns=['State', 'Unemployment'],
    key_on='feature.id',
    fill_color='YlGn',
    fill_opacity=0.7,
    line_opacity=0.2,
    legend_name='Unemployment Rate (%)'
).add_to(m)

folium.LayerControl().add_to(m)
m.get_root().add_child(legend)
m

enter image description here