Search code examples
pythonpandasmatplotlibgeopandas

Make Legend Marker Size the Same as in Map


I have a geopandas data frame full of points that I want to represent as a map. The points vary in size depending upon the value in another column, call it count. I want the legend to display various values of count and points whose size is the corresponding size in the map.

I have created proxy artists as described in the mathplotlib documentation. Here is a reproducible dataset.

import matplotlib.pyplot as plt
import matplotlib.lines as mlines
import geopandas as gpd
from shapely.geometry import Point

data = {'count': [1, 10, 20, 50, 100],
        'geometry': [Point(1, 2), Point(3, 4), Point(5, 6), Point(7, 8), Point(9, 10)]}
gdf = gpd.GeoDataFrame(data, geometry='geometry')
legend1 = mlines.Line2D([], [], color="red", lw=0, marker=".", markersize=1, label="1")
legend10 = mlines.Line2D([], [], color="red", lw=0, marker=".", markersize=10, label="10")
legend100 = mlines.Line2D([], [], color="red", lw=0, marker=".", markersize=100, label="100")

ax = gdf.plot(marker = ".", color='red', markersize=data['count'], edgecolors='none')
ax.legend(handles=[legend1, legend10, legend100], markerscale = 1)

plt.show_plot()

This is the resulting image.

In addition, I have tried to follow the advice posted here about how to add a legend for marker size. The responder set the marker size in the legend as the square root. The resulting legend is closer to what I want but still slightly off at smaller marker sizes.

Any advice is greatly appreciated!


Solution

  • geopandas.GeoDataFrame.plot uses matplotlib's scatter to do the plotting. This means that the marker size is given in points^2.

    Line2D, however, sets the markersize in points (not squared). Hence why using the sqrt for the proxy artists is suggested elsewhere.

    As well as using the sqrt of the marker size for the proxy artists, to ensure the markers are exactly the same size, I would also recommend setting markeredgewidth=0 for the Line2D instances, and linewidths=0 for the gdf.plot.

    import matplotlib.pyplot as plt
    import matplotlib.lines as mlines
    import geopandas as gpd
    import numpy as np
    from shapely.geometry import Point
    
    data = {'count': [1, 10, 20, 50, 100],
            'geometry': [Point(1, 2), Point(3, 4), Point(5, 6),
                         Point(7, 8), Point(9, 10)]}
    
    gdf = gpd.GeoDataFrame(data, geometry='geometry')
    
    legend1 = mlines.Line2D([], [], color="red", lw=0, marker=".", 
                            markeredgewidth=0, markersize=np.sqrt(1), label="1")
    legend10 = mlines.Line2D([], [], color="red", lw=0, marker=".",
                             markeredgewidth=0, markersize=np.sqrt(10), label="10")
    legend100 = mlines.Line2D([], [], color="red", lw=0, marker=".",
                              markeredgewidth=0, markersize=np.sqrt(100), label="100")
    
    ax = gdf.plot(marker = ".", color='red', markersize=data['count'], 
                  edgecolors='none', linewidths=0,)
    
    ax.legend(handles=[legend1, legend10, legend100], markerscale = 1)
    
    plt.savefig('geopandasmarkers.png', dpi=300)
    
    

    enter image description here