Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added tile_sources module #165

Merged
merged 3 commits into from
May 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 29 additions & 60 deletions examples/user_guide/Working_with_Bokeh.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@
"\n",
"from cartopy import crs as ccrs\n",
"\n",
"from bokeh.tile_providers import STAMEN_TONER, STAMEN_TONER_LABELS\n",
"\n",
"hv.extension('bokeh')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In most GeoViews examples, we have selected the matplotlib plotting backend, because it has general support for projecting data to different geographic projections using cartopy. The Bokeh backend offers much more advanced tools to interactively explore data, but is currently restricted to displaying data in web Mercator coordinates. Luckily, cartopy makes it possible to project points, geometries and even images from arbitrary coordinate systems into web Mercator so that they can be rendered by Bokeh. So as long as you choose the web Mercator format for your output (or don't specify the output format), you should be able to use Bokeh for any of the GeoViews examples from other notebooks. Bokeh also provides a general interface to render web-based map tile sources, making it simple to overlay your plots onto map tiles."
"In most GeoViews examples, we have selected the matplotlib plotting backend, because it has general support for projecting data to different geographic projections using cartopy. The Bokeh backend offers much more advanced tools to interactively explore data, but is currently restricted to displaying data in web Mercator coordinates, something GeoViews will handle automatically as long as you declare the appropriate coordinate reference system on your data.\n",
"\n",
"One of the most useful features Bokeh provides, is support for renderering your data on top of web-based map tile sources."
]
},
{
Expand All @@ -39,7 +39,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"When using the matplotlib backend, the ``gv.WMTS`` element accepts tile source URLs valid for cartopy. When using the Bokeh backend, you will need to first wrap the URL into a ``WMTSTileSource`` object, because Bokeh's tile support uses a different URL format. Here we provide a list of common tile sources for use with Bokeh. Additional open tile sources you could use can be found at [openstreetmap.org](http://wiki.openstreetmap.org/wiki/Tile_servers)."
"GeoViews provides a number of tile sources by default, provided by CartoDB, Stamen, OpenStreetMap, ESRI and Wikipedia. These can be imported from the ``geoviews.tile_sources`` module."
]
},
{
Expand All @@ -48,36 +48,25 @@
"metadata": {},
"outputs": [],
"source": [
"tiles = {'OpenMap': 'http://c.tile.openstreetmap.org/{Z}/{X}/{Y}.png',\n",
" 'ESRI': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg',\n",
" 'Wikipedia': 'https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}@2x.png',\n",
" 'Stamen Toner': STAMEN_TONER}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we can lay these Elements out in an NdLayout by wrapping each ``WMTSTileSource`` in a ``WMTS`` Element and specifying both the extent and the coordinate reference system of these extents. Note that the extents are only required when displaying the tile source on its own; when it is overlaid with some data the data determines the extent automatically."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"%%opts WMTS [width=450 height=250 xaxis=None yaxis=None]\n",
"hv.NdLayout({name: gv.WMTS(wmts)\n",
" for name, wmts in tiles.items()}, kdims=['Source']).cols(2)"
"import geoviews.tile_sources as gts\n",
"\n",
"hv.Layout([ts.relabel(name) for name, ts in gts.tile_sources.items()]).options(\n",
" 'WMTS', xaxis=None, yaxis=None, width=250, height=250\n",
").cols(4)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The tile sources that are defined as part of GeoViews are simply instances of the ``gv.WMTS`` and ``gv.Tiles`` elements, which accept tile source URLs of three formats:\n",
"\n",
"1. Web mapping tile sources: ``{X}``, ``{Y}`` defining the location and a ``{Z}`` parameter defining the zoom level \n",
"2. Bounding box tile source: ``{XMIN}``, ``{XMAX}``, ``{YMIN}``, and ``{YMAX}`` parameters defining the bounds\n",
"3. Quad-key tile source: a single ``{Q}`` parameter\n",
"\n",
"Additional, freely available tile sources can be found at [wiki.openstreetmap.org](http://wiki.openstreetmap.org/wiki/Tile_servers).\n",
"\n",
"A tile source may also be drawn at a different ``level`` allowing us to overlay a regular tile source with a set of labels. Valid options for the 'level' option include 'image', 'underlay', 'glyph', 'annotation' and 'overlay':"
]
},
Expand All @@ -88,15 +77,7 @@
"outputs": [],
"source": [
"%%opts WMTS [width=600 height=570]\n",
"gv.WMTS(tiles['ESRI'], extents=(0, -90, 360, 90), crs=ccrs.PlateCarree()) *\\\n",
"gv.WMTS(STAMEN_TONER_LABELS).opts(style=dict(level='annotation'))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You may also supply a tuple of tile sources pairing a Bokeh WMTSTileSource object with a simple cartopy string URL, allowing the `gv.WMTS` element to be used for both matplotlib and Bokeh rendering without having to declare separate objects."
"gts.ESRI * gts.StamenLabels.options(level='annotation')"
]
},
{
Expand Down Expand Up @@ -134,18 +115,13 @@
},
"outputs": [],
"source": [
"%%opts Overlay [width=600 height=350] \n",
"%%opts Points (size=0.005 cmap='viridis') [tools=['hover'] size_index=2 color_index=2]\n",
"(gv.WMTS(tiles['Wikipedia']) *\\\n",
"population.to(gv.Points, kdims=['Longitude', 'Latitude'],\n",
" vdims=['Population', 'City', 'Country'], crs=ccrs.PlateCarree()))"
"points = population.to(gv.Points, ['Longitude', 'Latitude'], ['Population', 'City', 'Country'])\n",
"(gts.Wikipedia * points.options(width=600, height=350, tools=['hover'], size_index=2, color_index=2, size=0.005, cmap='viridis'))"
]
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"metadata": {},
"source": [
"And because this is a fully interactive Bokeh plot, you can now hover over each datapoint to see all of the values associated with it (name, location, etc.), and you can zoom and pan using the tools provided. Each time, the map tiles should seamlessly update to provide additional detail appropriate for that zoom level.\n",
"\n",
Expand Down Expand Up @@ -185,8 +161,10 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Polygons [tools=['hover'] width=450 height=600 color_index='leaveVoteshare' colorbar=True toolbar='above' xaxis=None yaxis=None]\n",
"gv.Polygons(gdf, vdims=['name', 'leaveVoteshare'])"
"gv.Polygons(gdf, vdims=['name', 'leaveVoteshare']).options(\n",
" tools=['hover'], width=450, height=600, color_index='leaveVoteshare',\n",
" colorbar=True, toolbar='above', xaxis=None, yaxis=None\n",
")"
]
},
{
Expand All @@ -198,9 +176,7 @@
},
{
"cell_type": "markdown",
"metadata": {
"collapsed": true
},
"metadata": {},
"source": [
"The Bokeh backend also provides basic support for working with images. In this example we will load a very simple Iris Cube and display it overlaid with the coastlines feature from Cartopy. Note that the Bokeh backend does not project the image directly into the web Mercator projection, instead relying on regridding, i.e. resampling the data using a new grid. This means the actual display may be subtly different from the more powerful image support for the matplotlib backend, which will project each of the pixels into the chosen display coordinate system without regridding."
]
Expand All @@ -211,20 +187,13 @@
"metadata": {},
"outputs": [],
"source": [
"%%opts Overlay [width=600 height=500] Image (cmap='viridis') Feature (line_color='black')\n",
"%%opts Overlay [width=600 height=500] Image [tools=['hover']] (cmap='viridis') Feature (line_color='black')\n",
"dataset = xr.open_dataset('../data/pre-industrial.nc')\n",
"air_temperature = gv.Dataset(dataset, kdims=['longitude', 'latitude'],\n",
" group='Pre-industrial air temperature', vdims=['air_temperature'],\n",
" crs=ccrs.PlateCarree())\n",
"air_temperature.to.image() * gf.coastline()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Support for other projections is eventually planned for Bokeh, but meanwhile you should usually be able to use either backend interchangeably as long as you use web Mercator coordinates for display, and the additional interactivity provided by Bokeh is often very useful!"
]
}
],
"metadata": {
Expand All @@ -244,13 +213,13 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.6.3"
"version": "3.6.4"
},
"widgets": {
"state": {},
"version": "1.1.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
"nbformat_minor": 2
}
2 changes: 1 addition & 1 deletion geoviews/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from . import operation # noqa (API import)
from . import plotting # noqa (API import)
from . import feature # noqa (API import)

from . import tile_sources # noqa (API import)

__version__ = str(param.version.Version(fpath=__file__, archive_commit="$Format:%h$",
reponame="geoviews"))
Expand Down
11 changes: 9 additions & 2 deletions geoviews/plotting/bokeh/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
HexTiles)
from ...operation import (project_image, project_shape, project_points,
project_path, project_graph, project_quadmesh)
from ...tile_sources import _ATTRIBUTIONS
from ...util import geom_to_array
from .plot import GeoPlot, OverlayPlot, DEFAULT_PROJ
from . import callbacks # noqa
Expand Down Expand Up @@ -50,8 +51,14 @@ def get_data(self, element, ranges, style):
elif all(kw in element.data for kw in ('{X}', '{Y}', '{Z}')):
tile_source = WMTSTileSource
else:
raise ValueError('Tile source URL format not recognized.')
return {}, {'tile_source': tile_source(url=element.data)}, style
raise ValueError('Tile source URL format not recognized. '
'Must contain {X}/{Y}/{Z}, {XMIN}/{XMAX}/{YMIN}/{YMAX} '
'or {Q} template strings.')
params = {'url': element.data}
for key, attribution in _ATTRIBUTIONS.items():
if all(k in element.data for k in key):
params['attribution'] = attribution
return {}, {'tile_source': tile_source(**params)}, style

def _update_glyph(self, renderer, properties, mapping, glyph):
allowed_properties = glyph.properties()
Expand Down
49 changes: 49 additions & 0 deletions geoviews/tile_sources.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from .element import WMTS

# Mapping between patterns to match specified as tuples and tuples containing attributions
_ATTRIBUTIONS = {
('openstreetmap',) : (
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
),
('cartodb') : (
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors,'
'&copy; <a href="https://cartodb.com/attributions">CartoDB</a>'
),
('cartocdn') : (
'&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors,'
'&copy; <a href="https://cartodb.com/attributions">CartoDB</a>'
),
('stamen', 'terrain'): (
'Map tiles by <a href="https://stamen.com">Stamen Design</a>, '
'under <a href="https://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. '
'Data by <a href="https://openstreetmap.org">OpenStreetMap</a>, '
'under <a href="https://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.'
),
('stamen', 'toner'): (
'Map tiles by <a href="https://stamen.com">Stamen Design</a>, '
'under <a href="https://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. '
'Data by <a href="https://openstreetmap.org">OpenStreetMap</a>, '
'under <a href="https://www.openstreetmap.org/copyright">ODbL</a>.'
)
}

# CartoDB basemaps
CartoDark = WMTS('https://cartodb-basemaps-4.global.ssl.fastly.net/dark_all/{Z}/{X}/{Y}.png')
CartoEco = WMTS('http://3.api.cartocdn.com/base-eco/{Z}/{X}/{Y}.png')
CartoLight = WMTS('https://cartodb-basemaps-4.global.ssl.fastly.net/light_all/{Z}/{X}/{Y}.png')
CartoMidnight = WMTS('http://3.api.cartocdn.com/base-midnight/{Z}/{X}/{Y}.png')

# Stamen basemaps
StamenTerrain = WMTS('http://tile.stamen.com/terrain/{Z}/{X}/{Y}.png')
StamenTerrainRetina = WMTS('http://tile.stamen.com/terrain/{Z}/{X}/{Y}@2x.png')
StamenWatercolor = WMTS('http://c.tile.stamen.com/watercolor/{Z}/{X}/{Y}.jpg')
StamenToner = WMTS('http://tile.stamen.com/toner/{Z}/{X}/{Y}.png')
StamenTonerBackground = WMTS('http://tile.stamen.com/toner-background/{Z}/{X}/{Y}.png')
StamenLabels = WMTS('http://tile.stamen.com/toner-labels/{Z}/{X}/{Y}.png')

# Miscellaneous
OSM = WMTS('http://c.tile.openstreetmap.org/{Z}/{X}/{Y}.png')
ESRI = WMTS('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg')
Wikipedia = WMTS('https://maps.wikimedia.org/osm-intl/{Z}/{X}/{Y}@2x.png')

tile_sources = {k: v for k, v in locals().items() if isinstance(v, WMTS)}