pythonleafletcolormapfoliumchoropleth

Nonlinear colormap in a folium map


I'm drawing colored regions on a folium map using log scale shading. Using custom colormap, the region colors seem to be ok. But the colormap, when added to the map legend, still looks linear:

Still linear in the Leaflet map legend

Is there any way to draw it as equal-width colored rectangles with text values underneath?

import numpy as np, branca, folium
m = folium.Map()
colormap = branca.colormap.LinearColormap(colors=['blue', 'green', 'yellow', 'orange', 'red'],
                                          index = np.round(np.exp(np.linspace(0, 9, 5))),
                                          vmin = 0, vmax = np.exp(9),
           ).to_step(n=6, index=np.round(np.exp(np.linspace(0, 9, 6))))
colormap.add_to(m)

Solution

  • There is no Folium-native way to do this, and branca legends seem to be resistant to custom scaling. A workaround I have found is to create the legend separately (I use matplotlib), then overlay it on the Folium map as a Custom Icon. Looks like this:

    enter image description here

    import matplotlib.pyplot as plt
    import matplotlib.lines as mlines
    import numpy as np
    import folium
    from folium.features import CustomIcon
    import matplotlib.colors as mcolors
    
    def generate_legend(num_colors):
        # dynamically generate a color map
        cmap = plt.get_cmap('viridis', num_colors)
        colors = [cmap(i) for i in range(cmap.N)]
        
        # generate labels based on how many colors there are
        log_labels = np.logspace(0, num_colors, num=num_colors, base=10).astype(int)
        
        fig, ax = plt.subplots(figsize=(6, 0.6))
        font_size = 12
        line_width = 30 
        
        # create colors
        for i, color in enumerate(colors):
            plt.plot([i, i+1], [1, 1], color=color, solid_capstyle='butt', linewidth=line_width)
            plt.text(i+0.5, 0.5, f'{log_labels[i]}', ha='center', va='center', fontsize=font_size, transform=ax.transData)
    
        ax.set_xlim(0, num_colors)
        ax.set_ylim(0, 2)
        ax.axis('off')
        plt.subplots_adjust(left=0, right=1, top=1, bottom=0, wspace=0, hspace=0)
        plt.savefig('dynamic_legend.png', dpi=300, transparent=True, bbox_inches='tight', pad_inches=0)
        plt.close()
    
        # add legend to map
        m = folium.Map(location=[20, 0], zoom_start=2)
        legend_img = 'dynamic_legend.png'
        icon = CustomIcon(legend_img, icon_size=(600, 120))  # Adjust icon size as needed
    
        marker = folium.Marker(location=[-25, 0], icon=icon, popup='Legend')  # Adjust location as needed
        m.add_child(marker)
    
        m.save('map_with_dynamic_legend.html')
    
    num_colors = 5  
    generate_legend(num_colors)