First make sure that your GeoDataframe is in Web Mercator projection (epsg=3857). Once your Geodataframe is correctly georeferenced, you can achieve this by Geopandas reprojection:
df = df.to_crs(epsg=3857)
Once you have this done, you easily choose any of the supported map styles. A full list can be found in contextily.sources module, at the time of writing:
### Tile provider sources ###
ST_TONER = 'http://tile.stamen.com/toner/tileZ/tileX/tileY.png'
ST_TONER_HYBRID = 'http://tile.stamen.com/toner-hybrid/tileZ/tileX/tileY.png'
ST_TONER_LABELS = 'http://tile.stamen.com/toner-labels/tileZ/tileX/tileY.png'
ST_TONER_LINES = 'http://tile.stamen.com/toner-lines/tileZ/tileX/tileY.png'
ST_TONER_BACKGROUND = 'http://tile.stamen.com/toner-background/tileZ/tileX/tileY.png'
ST_TONER_LITE = 'http://tile.stamen.com/toner-lite/tileZ/tileX/tileY.png'
ST_TERRAIN = 'http://tile.stamen.com/terrain/tileZ/tileX/tileY.png'
ST_TERRAIN_LABELS = 'http://tile.stamen.com/terrain-labels/tileZ/tileX/tileY.png'
ST_TERRAIN_LINES = 'http://tile.stamen.com/terrain-lines/tileZ/tileX/tileY.png'
ST_TERRAIN_BACKGROUND = 'http://tile.stamen.com/terrain-background/tileZ/tileX/tileY.png'
ST_WATERCOLOR = 'http://tile.stamen.com/watercolor/tileZ/tileX/tileY.png'
# OpenStreetMap as an alternative
OSM_A = 'http://a.tile.openstreetmap.org/tileZ/tileX/tileY.png'
OSM_B = 'http://b.tile.openstreetmap.org/tileZ/tileX/tileY.png'
OSM_C = 'http://c.tile.openstreetmap.org/tileZ/tileX/tileY.png'
Keep in mind that you should not be adding actual x,y,z tile numbers in your tile URL (like you did in your "EDIT" example). ctx will take care of all this.
You can find a working copy-pastable example and further info at GeoPandas docs.
import contextily as ctx
# Dataframe you want to plot
gdf = GeoDataFrame(df, crs= {"init": "epsg:4326"}) # Create a georeferenced dataframe
gdf = gdf.to_crs(epsg=3857) # reproject it in Web mercator
ax = gdf.plot()
# choose any of the supported maps from ctx.sources
ctx.add_basemap(ax, url=ctx.sources.ST_TERRAIN)
ax.set_axis_off()
plt.show()
Contextily's default crs is epsg:3857. However, your data-frame is in different CRS. Use the following,refer the manual here:
ctx.add_basemap(ax, crs='epsg:4326', source=ctx.providers.Stamen.TonerLite)
Please, refer to this link for using different sources such as Stamen.Toner, Stamen.Terrain etc. (Stamen.Terrain is used as default).
Also, you can cast your data frame to EPSG:3857 by using df.to_crs(). In this case, you should skip crs argument inside ctx.add_basemap() function.
I'm not sure what's going on here. I have reproduced your code, which generates a .tif file I can open fine on QGIS, no black. I can also plot the file directly with rasterio:
import rasterio
from rasterio.plotting import show as rioshow
rioshow(rasterio.open("C:\\Temp\\topo.tif"))
And that plots the portion of the map (this is all on the latest 1.0.0 version and rasterio 1.1.5, which actually handles all the I/O).
More generally, contextily only currently works with tiles in Web Mercator (EPSG:3857). There is an issue here discussion potential extensions but these are at the idea level currently:
https://github.com/geopandas/contextily/issues/119
Note however that you can use the square trick you have above, together with ctx.add_basemap to retrieve the map, but this would be for display only, we currently have no functionality to query with a different CRS and write to disk in a GeoTif format. That would be a cool feature to have and should not be too hard to do (basically hook up the CRS option in add_basemap to the bounds2X methods), but it does not exist currently (PRs welcome!).
An alternative is to convert your 27700 coords into Web Mercator, use img2raster to write the Tiff in Web Mercator and then reproject either with QGIS, GDAL, or rio transform
A further update, although I can't be certain I have a hunch the problem related to the GDAL install. Specifically the Windows Environment Variables: GDAL_DATA, GDAL_DRIVER_PATH, GDAL_VERSION.
Since I had multiple python environments, python 3.7x32, 3.7x64, 3.8x32 I therefore have multiple GDAL Env variables. Perhaps when running a 3.7x64 script, the first GDAL variable it found in the registry was the 3.7x32 version pointing to the wrong GDAL resources- creating the error.
Trying to get contextily working involved installing GDAL windows msi installers on www.gisinternals.com. Before using those I had a lot of visual studio errors CL.exe compiling errors when installing gdal via pip install. But after installing loads of versions from there I created more problems for myself.
SOLUTION:
- I uninstalled all python environments
- deleted all GDAL registry entries
- deleted all python IDLE context menu registry entries
- Installed GDAL 3.1.1 from a wheel from the unofficial windows binaries
- Installed rasterio 1.15 from wheel
- Fiona from wheel
- pip install geopandas (as I need to combine with vector data too)
- pip install contextily
and then contextily worked on 3.7x64!
My GDAL Env Variables look like this:

My Path Env Variables look like this:

And PYTHONPATH:

