diff --git a/docs/conf.py b/docs/conf.py index 84d5b9b7e6..28d2c4ac30 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -27,7 +27,12 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'nbsphinx'] +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.napoleon', + 'nbsphinx', + 'sphinx.ext.intersphinx', +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -249,4 +254,11 @@ # Ignore tile URLs linkcheck_ignore = [ r"https://free.*", -] \ No newline at end of file +] + +intersphinx_mapping = { + "xyzservices": ( + "https://xyzservices.readthedocs.io/en/latest/", + "https://xyzservices.readthedocs.io/en/latest/objects.inv", + ), +} diff --git a/folium/folium.py b/folium/folium.py index 6be351b7b2..3043579127 100644 --- a/folium/folium.py +++ b/folium/folium.py @@ -81,7 +81,8 @@ class Map(JSCSSMixin, MacroElement): - "Mapbox" (Must pass API key) - "CartoDB" (positron and dark_matter) - You can pass a custom tileset to Folium by passing a Leaflet-style + You can pass a custom tileset to Folium by passing a + :class:`xyzservices.TileProvider` or a Leaflet-style URL to the tiles parameter: ``http://{s}.yourtiles.com/{z}/{x}/{y}.png``. You can find a list of free tile providers here: @@ -97,8 +98,9 @@ class Map(JSCSSMixin, MacroElement): Width of the map. height: pixel int or percentage string (default: '100%') Height of the map. - tiles: str or TileLayer, default 'OpenStreetMap' + tiles: str or TileLayer or :class:`xyzservices.TileProvider`, default 'OpenStreetMap' Map tileset to use. Can choose from a list of built-in tiles, + pass a :class:`xyzservices.TileProvider`, pass a custom URL, pass a TileLayer object, or pass `None` to create a map without tiles. For more advanced tile layer options, use the `TileLayer` class. diff --git a/folium/raster_layers.py b/folium/raster_layers.py index 95dfd838a5..dc1ba0c106 100644 --- a/folium/raster_layers.py +++ b/folium/raster_layers.py @@ -20,13 +20,14 @@ class TileLayer(Layer): Parameters ---------- - tiles: str, default 'OpenStreetMap' + tiles: str or :class:`xyzservices.TileProvider`, default 'OpenStreetMap' Map tileset to use. Can choose from this list of built-in tiles: - "OpenStreetMap" - "Stamen Terrain", "Stamen Toner", "Stamen Watercolor" - "CartoDB positron", "CartoDB dark_matter" - You can pass a custom tileset to Folium by passing a Leaflet-style + You can pass a custom tileset to Folium by passing a + :class:`xyzservices.TileProvider` or a Leaflet-style URL to the tiles parameter: ``http://{s}.yourtiles.com/{z}/{x}/{y}.png``. You can find a list of free tile providers here: @@ -80,6 +81,14 @@ def __init__(self, tiles='OpenStreetMap', min_zoom=0, max_zoom=18, control=True, show=True, no_wrap=False, subdomains='abc', tms=False, opacity=1, **kwargs): + # check for xyzservices.TileProvider without importing it + if isinstance(tiles, dict): + attr = attr if attr else tiles.html_attribution + min_zoom = tiles.get("min_zoom", min_zoom) + max_zoom = tiles.get("max_zoom", max_zoom) + subdomains = tiles.get("subdomains", subdomains) + tiles = tiles.build_url(fill_subdomain=False, scale_factor="{r}") + self.tile_name = (name if name is not None else ''.join(tiles.lower().strip().split())) super(TileLayer, self).__init__(name=self.tile_name, overlay=overlay, diff --git a/requirements-dev.txt b/requirements-dev.txt index ed51c5ebcf..c7928bcf7d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -33,3 +33,4 @@ twine>=3.2.0 vega_datasets vincent wheel +xyzservices diff --git a/tests/test_raster_layers.py b/tests/test_raster_layers.py index 7741a50332..7654dc3ba4 100644 --- a/tests/test_raster_layers.py +++ b/tests/test_raster_layers.py @@ -8,6 +8,8 @@ from jinja2 import Template +import xyzservices + def test_tile_layer(): m = folium.Map([48., 5.], tiles='stamentoner', zoom_start=6) @@ -106,3 +108,20 @@ def test_image_overlay(): bounds = m.get_bounds() assert bounds == [[0, -180], [90, 180]], bounds + + +def test_xyzservices(): + m = folium.Map([48., 5.], tiles=xyzservices.providers.Stamen.Toner, zoom_start=6) + + folium.raster_layers.TileLayer( + tiles=xyzservices.providers.Stamen.Terrain, + ).add_to(m) + folium.LayerControl().add_to(m) + + out = m._parent.render() + assert xyzservices.providers.Stamen.Toner.build_url( + fill_subdomain=False, scale_factor="{r}" + ) in out + assert xyzservices.providers.Stamen.Terrain.build_url( + fill_subdomain=False, scale_factor="{r}" + ) in out