Skip to content

Commit 84459ba

Browse files
authored
use xyzservices instead of templates (#1827)
* use xyzservices instead of templates * fix minimap test * more robust check for openstreetmap * lint * rm ENV
1 parent 937693f commit 84459ba

File tree

17 files changed

+65
-80
lines changed

17 files changed

+65
-80
lines changed

folium/folium.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,15 +82,14 @@ class Map(JSCSSMixin, MacroElement):
8282
"""Create a Map with Folium and Leaflet.js
8383
8484
Generate a base map of given width and height with either default
85-
tilesets or a custom tileset URL. The following tilesets are built-in
86-
to Folium. Pass any of the following to the "tiles" keyword:
85+
tilesets or a custom tileset URL. Folium has built-in all tilesets
86+
available in the ``xyzservices`` package. For example, you can pass
87+
any of the following to the "tiles" keyword:
8788
8889
- "OpenStreetMap"
89-
- "Mapbox Bright" (Limited levels of zoom for free tiles)
90-
- "Mapbox Control Room" (Limited levels of zoom for free tiles)
91-
- "Cloudmade" (Must pass API key)
92-
- "Mapbox" (Must pass API key)
93-
- "CartoDB" (positron and dark_matter)
90+
- "CartoDB Positron"
91+
- "CartoBD Voyager"
92+
- "NASAGIBS Blue Marble"
9493
9594
You can pass a custom tileset to Folium by passing a
9695
:class:`xyzservices.TileProvider` or a Leaflet-style

folium/raster_layers.py

Lines changed: 34 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
Wraps leaflet TileLayer, WmsTileLayer (TileLayer.WMS), ImageOverlay, and VideoOverlay
33
44
"""
5-
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
5+
from typing import Any, Callable, Optional, Union
66

7+
import xyzservices
78
from branca.element import Element, Figure
8-
from jinja2 import Environment, PackageLoader, Template
9+
from jinja2 import Template
910

1011
from folium.map import Layer
1112
from folium.utilities import (
@@ -16,12 +17,6 @@
1617
parse_options,
1718
)
1819

19-
if TYPE_CHECKING:
20-
import xyzservices
21-
22-
23-
ENV = Environment(loader=PackageLoader("folium", "templates"))
24-
2520

2621
class TileLayer(Layer):
2722
"""
@@ -30,9 +25,14 @@ class TileLayer(Layer):
3025
Parameters
3126
----------
3227
tiles: str or :class:`xyzservices.TileProvider`, default 'OpenStreetMap'
33-
Map tileset to use. Can choose from this list of built-in tiles:
28+
Map tileset to use. Folium has built-in all tilesets
29+
available in the ``xyzservices`` package. For example, you can pass
30+
any of the following to the "tiles" keyword:
31+
3432
- "OpenStreetMap"
35-
- "CartoDB positron", "CartoDB dark_matter"
33+
- "CartoDB Positron"
34+
- "CartoBD Voyager"
35+
- "NASAGIBS Blue Marble"
3636
3737
You can pass a custom tileset to Folium by passing a
3838
:class:`xyzservices.TileProvider` or a Leaflet-style
@@ -90,7 +90,7 @@ class TileLayer(Layer):
9090

9191
def __init__(
9292
self,
93-
tiles: Union[str, "xyzservices.TileProvider"] = "OpenStreetMap",
93+
tiles: Union[str, xyzservices.TileProvider] = "OpenStreetMap",
9494
min_zoom: int = 0,
9595
max_zoom: int = 18,
9696
max_native_zoom: Optional[int] = None,
@@ -104,14 +104,26 @@ def __init__(
104104
subdomains: str = "abc",
105105
tms: bool = False,
106106
opacity: float = 1,
107-
**kwargs
107+
**kwargs,
108108
):
109-
# check for xyzservices.TileProvider without importing it
110-
if isinstance(tiles, dict):
109+
if isinstance(tiles, str):
110+
if tiles.lower() == "openstreetmap":
111+
tiles = "OpenStreetMap Mapnik"
112+
if name is None:
113+
name = "openstreetmap"
114+
try:
115+
tiles = xyzservices.providers.query_name(tiles)
116+
except ValueError:
117+
# no match, likely a custom URL
118+
pass
119+
120+
if isinstance(tiles, xyzservices.TileProvider):
111121
attr = attr if attr else tiles.html_attribution # type: ignore
112122
min_zoom = tiles.get("min_zoom", min_zoom)
113123
max_zoom = tiles.get("max_zoom", max_zoom)
114124
subdomains = tiles.get("subdomains", subdomains)
125+
if name is None:
126+
name = tiles.name.replace(".", "").lower()
115127
tiles = tiles.build_url(fill_subdomain=False, scale_factor="{r}") # type: ignore
116128

117129
self.tile_name = (
@@ -122,27 +134,9 @@ def __init__(
122134
)
123135
self._name = "TileLayer"
124136

125-
tiles_flat = "".join(tiles.lower().strip().split())
126-
if tiles_flat in {"cloudmade", "mapbox", "mapboxbright", "mapboxcontrolroom"}:
127-
# added in May 2020 after v0.11.0, remove in a future release
128-
raise ValueError(
129-
"Built-in templates for Mapbox and Cloudmade have been removed. "
130-
"You can still use these providers by passing a URL to the `tiles` "
131-
"argument. See the documentation of the `TileLayer` class."
132-
)
133-
templates = list(
134-
ENV.list_templates(filter_func=lambda x: x.startswith("tiles/"))
135-
)
136-
tile_template = "tiles/" + tiles_flat + "/tiles.txt"
137-
attr_template = "tiles/" + tiles_flat + "/attr.txt"
138-
139-
if tile_template in templates and attr_template in templates:
140-
self.tiles = ENV.get_template(tile_template).render()
141-
attr = ENV.get_template(attr_template).render()
142-
else:
143-
self.tiles = tiles
144-
if not attr:
145-
raise ValueError("Custom tiles must have an attribution.")
137+
self.tiles = tiles
138+
if not attr:
139+
raise ValueError("Custom tiles must have an attribution.")
146140

147141
self.options = parse_options(
148142
min_zoom=min_zoom,
@@ -154,7 +148,7 @@ def __init__(
154148
detect_retina=detect_retina,
155149
tms=tms,
156150
opacity=opacity,
157-
**kwargs
151+
**kwargs,
158152
)
159153

160154

@@ -219,7 +213,7 @@ def __init__(
219213
overlay: bool = True,
220214
control: bool = True,
221215
show: bool = True,
222-
**kwargs
216+
**kwargs,
223217
):
224218
super().__init__(name=name, overlay=overlay, control=control, show=show)
225219
self.url = url
@@ -231,7 +225,7 @@ def __init__(
231225
transparent=transparent,
232226
version=version,
233227
attribution=attr,
234-
**kwargs
228+
**kwargs,
235229
)
236230
if cql_filter:
237231
# special parameter that shouldn't be camelized
@@ -309,7 +303,7 @@ def __init__(
309303
overlay: bool = True,
310304
control: bool = True,
311305
show: bool = True,
312-
**kwargs
306+
**kwargs,
313307
):
314308
super().__init__(name=name, overlay=overlay, control=control, show=show)
315309
self._name = "ImageOverlay"
@@ -406,7 +400,7 @@ def __init__(
406400
overlay: bool = True,
407401
control: bool = True,
408402
show: bool = True,
409-
**kwargs: TypeJsonValue
403+
**kwargs: TypeJsonValue,
410404
):
411405
super().__init__(name=name, overlay=overlay, control=control, show=show)
412406
self._name = "VideoOverlay"

folium/templates/tiles/cartodbdark_matter/attr.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbdark_matter/tiles.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositron/attr.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositron/tiles.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositronnolabels/attr.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositronnolabels/tiles.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositrononlylabels/attr.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/cartodbpositrononlylabels/tiles.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/openstreetmap/attr.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

folium/templates/tiles/openstreetmap/tiles.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.

requirements-dev.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,3 @@ types-requests
3636
vega_datasets
3737
vincent
3838
wheel
39-
xyzservices

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ branca>=0.6.0
22
jinja2>=2.9
33
numpy
44
requests
5+
xyzservices

tests/plugins/test_minimap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,4 @@ def test_minimap():
2525

2626
out = normalize(m._parent.render())
2727
# verify that tiles are being used
28-
assert r"https://{s}.tile.openstreetmap.org" in out
28+
assert r"https://tile.openstreetmap.org" in out

tests/test_folium.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
import numpy as np
1414
import pandas as pd
1515
import pytest
16+
import xyzservices.providers as xyz
1617
from jinja2 import Environment, PackageLoader
1718
from jinja2.utils import htmlsafe_json_dumps
1819

1920
import folium
2021
from folium import TileLayer
2122
from folium.features import Choropleth, GeoJson
22-
from folium.raster_layers import ENV
2323

2424
rootpath = os.path.abspath(os.path.dirname(__file__))
2525

@@ -106,24 +106,24 @@ def test_init(self):
106106
},
107107
}
108108

109-
def test_builtin_tile(self):
109+
@pytest.mark.parametrize(
110+
"tiles,provider",
111+
[
112+
("OpenStreetMap", xyz.OpenStreetMap.Mapnik),
113+
("CartoDB positron", xyz.CartoDB.Positron),
114+
("CartoDB dark_matter", xyz.CartoDB.DarkMatter),
115+
],
116+
)
117+
def test_builtin_tile(self, tiles, provider):
110118
"""Test custom maptiles."""
111119

112-
default_tiles = [
113-
"OpenStreetMap",
114-
"CartoDB positron",
115-
"CartoDB dark_matter",
116-
]
117-
for tiles in default_tiles:
118-
m = folium.Map(location=[45.5236, -122.6750], tiles=tiles)
119-
tiles = "".join(tiles.lower().strip().split())
120-
url = "tiles/{}/tiles.txt".format
121-
attr = "tiles/{}/attr.txt".format
122-
url = ENV.get_template(url(tiles)).render()
123-
attr = ENV.get_template(attr(tiles)).render()
124-
125-
assert m._children[tiles].tiles == url
126-
assert htmlsafe_json_dumps(attr) in m._parent.render()
120+
m = folium.Map(location=[45.5236, -122.6750], tiles=tiles)
121+
tiles = "".join(tiles.lower().strip().split())
122+
url = provider.build_url(fill_subdomain=False, scale_factor="{r}")
123+
attr = provider.html_attribution
124+
125+
assert m._children[tiles.replace("_", "")].tiles == url
126+
assert htmlsafe_json_dumps(attr) in m._parent.render()
127127

128128
bounds = m.get_bounds()
129129
assert bounds == [[None, None], [None, None]], bounds

tests/test_raster_layers.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
------------------
44
55
"""
6+
import pytest
67
import xyzservices
78
from jinja2 import Template
89

@@ -113,10 +114,11 @@ def test_image_overlay():
113114
assert bounds == [[0, -180], [90, 180]], bounds
114115

115116

116-
def test_xyzservices():
117-
m = folium.Map(
118-
[48.0, 5.0], tiles=xyzservices.providers.CartoDB.DarkMatter, zoom_start=6
119-
)
117+
@pytest.mark.parametrize(
118+
"tiles", ["CartoDB DarkMatter", xyzservices.providers.CartoDB.DarkMatter]
119+
)
120+
def test_xyzservices(tiles):
121+
m = folium.Map([48.0, 5.0], tiles=tiles, zoom_start=6)
120122

121123
folium.raster_layers.TileLayer(
122124
tiles=xyzservices.providers.CartoDB.Positron,

0 commit comments

Comments
 (0)