12

I have this code:

import pandas as pd
import numpy as np
from geopandas import GeoDataFrame
import geopandas
from shapely.geometry import LineString, Point
import matplotlib.pyplot as plt
import contextily

''' Do Something'''

df = start_stop_df.drop('track', axis=1)
crs = {'init': 'epsg:4326'}
gdf = GeoDataFrame(df, crs=crs, geometry=geometry)

ax = gdf.plot()
contextily.add_basemap(ax)
ax.set_axis_off()
plt.show()

Basically, this generates a background map that is in Singapore. However, when I run it, I get the following error: HTTPError: Tile URL resulted in a 404 error. Double-check your tile url:http://tile.stamen.com/terrain/29/268436843/268435436.png However, it still produces this image: Code output

How can I change the Tile URL? I would still like to have the map of Singapore as the base layer.

EDIT:
Also tried including this argument to add_basemap:
url ='https://www.openstreetmap.org/#map=12/1.3332/103.7987'
Which produced this error:
OSError: cannot identify image file <_io.BytesIO object at 0x000001CC3CC4BC50>

3 Answers3

13

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()
Marjan Moderc
  • 2,747
  • 23
  • 44
  • I'm having the same problem and followed your example but it doesn't seem to solve the issue. I converted the gdf via `.to_crs(epsg=3857)` from epsg=4326 but I still get `HTTPError: Tile URL resulted in a 404 error. Double-check your tile url: http://tile.stamen.com/terrain-background/23/4194302/4194300.png` – JHuw Oct 15 '19 at 21:47
  • I am not sure that you can get zoom levels as high as 23... Usually it only goes up to 20. (http://maps.stamen.com/toner/#20/37.80802/-122.32054). Maybe you should zoom out your map a bit. – Marjan Moderc Oct 16 '19 at 09:26
  • I am having problem with the coordinate system, still didn't manage to fix it. (so I am seeing just the Ocean) Even so, to simple avoid having errors due to zoom level as explained above I am coding: contextily.add_basemap(ax, zoom=18) – ChrCury78 Jan 24 '20 at 23:16
  • I also had a `HTTPError: Tile URL resulted in a 404 error. Double-check your tile url: https://stamen-tiles-a.a.ssl.fastly.net/terrain/14/8383/5485.png` but found that it is an issue with some of the Stamen map tile providers (https://github.com/stamen/maps.stamen.com/issues/107). As soon as I **changed to another map provider**, for example `ctx.add_basemap(ax, alpha=0.8, source=ctx.providers.OpenStreetMap.Mapnik)`, **the issue seems to be solved**. – Sander Vanden Hautte May 31 '21 at 07:37
4

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.

Subrat Prasad
  • 133
  • 1
  • 9
0

im too new to add a comment but I wanted to point out to those saying in the comments that they get a 404 error. Check you capitaliations, etc. Stamen's urls are specifc on this. For instance there is not an all caps call. It is only capitalize the first letter. For example:

ctx.add_basemap(ax=ax,url=ctx.providers.Stamen.Toner, zoom=10)

SirRacha
  • 33
  • 5