pythonpandasgeopandascontextily

Contextily add_basemap uses wrong extent and zoom level


I'm trying to add a contextily basemap to a Matplotlib figure containing a GeoPandas data frame. When I just plot the data frame using df.plot the map extent is calculated correctly.

However, when I try adding a contextily basemap the map extent (and zoom level) is calculated wrongly and the following warning is shown:

UserWarning: The inferred zoom level of 27 is not valid for the current tile provider (valid zooms: 0 - 20).

I'm trying to execute the following code:

df = gpd.read_file('linz/StatBez_Linz_EPSG_4326.gml')
df = df.to_crs(epsg=3857)

fig = plt.figure(figsize=(16,9))
ax = plt.subplot()
ctx.add_basemap(ax = ax, source=ctx.providers.Stamen.Toner, crs=df.crs.to_string())
df.plot(color='none',edgecolor='green', ax = ax)

The output of df.tail() can be seen here:

enter image description here

The gml file is from data.gv.at


Solution

  • The GML file of Linz is based on Gauss-Krüger system M31-5Mio (EPSG:31255). Here is runnable code that demonstrates all the steps to produce a plot of the GML with basemap requested from webmap tiles' provider of choice.

    import contextily as ctx
    import geopandas
    import matplotlib.pyplot as plt
    
    # Read GML
    linz_districts = geopandas.read_file('./data/StatBez_Linz.gml')
    
    # The coordinates are in the Gauss-Krüger system M31-5Mio. 
    # CRS is EPSG:31255
    # Set proper coordinate system to the geoDataFrame
    linz_31255 = linz_districts.set_crs(31255)
    
    # Convert CRS to Web-Mercator to match basemap layer
    linz_3857 = linz_31255.to_crs('epsg:3857')
    
    # plot Linz
    ax = linz_3857.plot(figsize=(9, 16), zorder=10, ec='gray', alpha=0.4)
    
    # plot basemap (it uses 'epsg:3857')
    src_basemap = ctx.providers.Stamen.Terrain
    ctx.add_basemap( ax, source=src_basemap, alpha=0.6, zorder=8 )
    
    # Also possible with
    #ctx.add_basemap( ax, source='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png' )
    
    # manipulate xticks, use format
    ax.set_xticklabels(['{:,.0f}'.format(x) for x in ax.get_xticks()]);
    ax.set_yticklabels(['{:,.0f}'.format(y) for y in ax.get_yticks()]);
    

    The output plot:

    linz-map