Search code examples
pythongisgeopandasgeography

How to plot geographic data with customized legend?


Having the geographic points with values, I would like to encode the values with colormap and customize the legend position and colormap range.

Using geopandas, I have written the following function:

def plot_continuous(df, column_values, title):
    fig = plt.figure()
    ax = fig.add_axes([0, 0, 1, 1])
    ax.axis('off')
    df.plot(ax=ax, column=column_values, cmap='OrRd', legend=True);
    ax.title.set_text(title)

The colorbar by default is vertical, but I would like to make it horizontal.

In order to have a horizontal colorbar, I have written the following function:

def plot_continuous(df, column_values, title, legend_title=None):
    fig = plt.figure()
    ax = fig.add_axes([0, 0, 1, 1])
    x = np.array(df.geometry.apply(lambda x: x.x))
    y = np.array(df.geometry.apply(lambda x: x.y))
    vals = np.array(df[column_values])
    sc = ax.scatter(x, y, c=vals, cmap='OrRd')
    cbar = plt.colorbar(sc, orientation="horizontal")
    if legend_title is not None:
        cbar.ax.set_xlabel(legend_title)
    ax.title.set_text(title)

The image width and height in the latter case, however, is not proportional so the output looks distorted.

Does anyone know how to customize the geographic plot and keep the width-height ratio undistorted?


Solution

  • This gets far simpler if you use geopandas customisation of plot()

    This is documented: https://geopandas.org/en/stable/docs/user_guide/mapping.html

    Below I show MWE using your function and then using geopandas. Later has scaled data correctly.

    MWE of your code

    import geopandas as gpd
    import matplotlib.pyplot as plt
    import numpy as np
    
    def plot_continuous(df, column_values, title, legend_title=None):
        fig = plt.figure()
        ax = fig.add_axes([0, 0, 1, 1])
        x = np.array(df.geometry.apply(lambda x: x.x))
        y = np.array(df.geometry.apply(lambda x: x.y))
        vals = np.array(df[column_values])
        sc = ax.scatter(x, y, c=vals, cmap='OrRd')
        cbar = plt.colorbar(sc, orientation="horizontal")
        if legend_title is not None:
            cbar.ax.set_xlabel(legend_title)
        ax.title.set_text(title)
        
    cities = gpd.read_file(gpd.datasets.get_path("naturalearth_cities"))
    cities["color"] = np.random.randint(1,10, len(cities))
    
    plot_continuous(cities, "color", "my title", "Color")
    

    enter image description here

    use geopandas

    ax = cities.plot(
        column="color",
        cmap="OrRd",
        legend=True,
        legend_kwds={"label": "Color", "orientation": "horizontal"},
    )
    
    ax.set_title("my title")
    

    enter image description here