如何在Python Folium地图上添加分类图例?

7
我希望在folium地图中添加一个类别/数值图例,就像R中的addLenged()一样。
示例:

数值图例

numeric legend

我可以帮您翻译成中文。以下是翻译的结果:

我还想添加一个类别图例,就像这样:

类别图例

enter image description here

目前我只有这段代码,我卡在尝试实现 R 中的 addLegend 功能上。

函数

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

可重现的例子。
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

结果

enter image description here

问题

  • 然而,我做的图例与folium LayerControls存在冲突,导致它们停止工作。对我来说,这是一个非常大的问题,因为图例用于描述我在LayerControls中使用的内容。我不知道原因。
  • 此外,这仅适用于分类数据。

重要

  • 我希望图例可以添加到顶部|底部和右侧|左侧位置,就像R中的addLegend一样。给出绝对位置不是一个选项。

任何帮助都将不胜感激

注意:我不想使用branca颜色映射,因为我想要像图片中所示的图例。


我目前也遇到了同样的问题。你最近有没有想出如何解决它的方法? - Max Muller
1
@MaxMuller 不用,因为我已经停止使用Python来制作这种复杂的地图可视化。我建议使用像React这样的前端框架,并使用原始的Leaflet库。如果你的项目非常简单,你也可以尝试使用Plotly for Python(对地图可视化有很好但简单的支持)。 - Angel
@Angel - 我的地图也遇到了类似的问题。你所建议的使用React和Leaflet实现这个功能有多难?这将完全摆脱Python,转而使用JS,对吗? - Will
1个回答

3

我对 folium 不是很熟悉,但我认为官方提供的参考图例会帮助你解决问题:我将此页面上的示例应用于 Choropleth 示例。由于您更有经验的 web 代码,因此可以自定义它。 我检查了 folium 提供的所有功能,并没有发现可以用于部署配置的功能。

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


1
谢谢您的回复,但是正如我在问题中所说,我希望图例可以相对于底部|顶部和左侧|右侧的leaflet控件进行定位。另外,我不想使用branca来作为连续图例,就像我之前说的那样。 - Angel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接