diff options
-rw-r--r-- | fietsboek/config.py | 2 | ||||
-rw-r--r-- | fietsboek/jinja2.py | 4 | ||||
-rw-r--r-- | fietsboek/views/tileproxy.py | 282 |
3 files changed, 132 insertions, 156 deletions
diff --git a/fietsboek/config.py b/fietsboek/config.py index d95469c..9949198 100644 --- a/fietsboek/config.py +++ b/fietsboek/config.py @@ -127,7 +127,7 @@ class TileLayerConfig(BaseModel): layer_type: LayerType = Field(LayerType.BASE, alias="type") """Type of the layer.""" - zoom: int = 22 + zoom: typing.Optional[int] = 22 """Maximum zoom factor of the layer.""" attribution: str = "" diff --git a/fietsboek/jinja2.py b/fietsboek/jinja2.py index 6e5e7b6..07c9087 100644 --- a/fietsboek/jinja2.py +++ b/fietsboek/jinja2.py @@ -99,13 +99,13 @@ def global_embed_tile_layers(request): if request.config.disable_tile_proxy: def _url(source): - return source.url_template + return source.url else: def _url(source): return ( - request.route_url("tile-proxy", provider=source.key, x="{x}", y="{y}", z="{z}") + request.route_url("tile-proxy", provider=source.layer_id, x="{x}", y="{y}", z="{z}") .replace("%7Bx%7D", "{x}") .replace("%7By%7D", "{y}") .replace("%7Bz%7D", "{z}") diff --git a/fietsboek/views/tileproxy.py b/fietsboek/views/tileproxy.py index 61747d4..1ec609b 100644 --- a/fietsboek/views/tileproxy.py +++ b/fietsboek/views/tileproxy.py @@ -9,10 +9,11 @@ Additionally, this protects the users' IP, as only fietsboek can see it. import datetime import random import logging -from typing import NamedTuple, Optional +from typing import List from itertools import chain from pyramid.view import view_config +from pyramid.request import Request from pyramid.response import Response from pyramid.httpexceptions import HTTPBadRequest, HTTPGatewayTimeout @@ -20,26 +21,7 @@ import requests from requests.exceptions import ReadTimeout from .. import __VERSION__ -from ..config import LayerType, LayerAccess - - -class TileSource(NamedTuple): - """Represents a remote server that can provide tiles to us.""" - - key: str - """Key to indicate this source in URLs.""" - name: str - """Human-readable name of the source.""" - url_template: str - """URL with placeholders.""" - layer_type: LayerType - """Type of this layer.""" - zoom: Optional[int] - """Max zoom of this layer.""" - access: LayerAccess - """Access restrictions to use this layer.""" - attribution: str - """Attribution string.""" +from ..config import Config, LayerType, LayerAccess, TileLayerConfig LOGGER = logging.getLogger(__name__) @@ -54,14 +36,14 @@ _jb_copy = _href("https://www.j-berkemeier.de/GPXViewer", "GPXViewer") DEFAULT_TILE_LAYERS = [ # Main base layers - TileSource( - "osm", - "OSM", - "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - LayerType.BASE, - 19, - LayerAccess.PUBLIC, - "".join( + TileLayerConfig( + layer_id="osm", + name="OSM", + url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=19, + access=LayerAccess.PUBLIC, + attribution="".join( [ _jb_copy, " | Map data © ", @@ -71,15 +53,15 @@ DEFAULT_TILE_LAYERS = [ ] ), ), - TileSource( - "satellite", - "Satellit", - "https://server.arcgisonline.com/ArcGIS/rest/services/" + TileLayerConfig( + layer_id="satellite", + name="Satellit", + url="https://server.arcgisonline.com/ArcGIS/rest/services/" "World_Imagery/MapServer/tile/{z}/{y}/{x}", - LayerType.BASE, - 21, - LayerAccess.PUBLIC, - "".join( + layer_type=LayerType.BASE, + zoom=21, + access=LayerAccess.PUBLIC, + attribution="".join( [ _jb_copy, " | Map data © ", @@ -89,14 +71,14 @@ DEFAULT_TILE_LAYERS = [ ] ), ), - TileSource( - "osmde", - "OSMDE", - "https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png", - LayerType.BASE, - 19, - LayerAccess.PUBLIC, - "".join( + TileLayerConfig( + layer_id="osmde", + name="OSMDE", + url="https://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=19, + access=LayerAccess.PUBLIC, + attribution="".join( [ _jb_copy, " | Map data © ", @@ -106,14 +88,14 @@ DEFAULT_TILE_LAYERS = [ ] ), ), - TileSource( - "opentopo", - "Open Topo", - "https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", - LayerType.BASE, - 17, - LayerAccess.PUBLIC, - "".join( + TileLayerConfig( + layer_id="opentopo", + name="Open Topo", + url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=17, + access=LayerAccess.PUBLIC, + attribution="".join( [ _jb_copy, " | Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © ", @@ -122,15 +104,15 @@ DEFAULT_TILE_LAYERS = [ ] ), ), - TileSource( - "topplusopen", - "TopPlusOpen", - "https://sgx.geodatenzentrum.de/wmts_topplus_open/tile/" + TileLayerConfig( + layer_id="topplusopen", + name="TopPlusOpen", + url="https://sgx.geodatenzentrum.de/wmts_topplus_open/tile/" "1.0.0/web/default/WEBMERCATOR/{z}/{y}/{x}.png", - LayerType.BASE, - 18, - LayerAccess.PUBLIC, - "".join( + layer_type=LayerType.BASE, + zoom=18, + access=LayerAccess.PUBLIC, + attribution="".join( [ _jb_copy, " | Kartendaten: © ", @@ -143,73 +125,85 @@ DEFAULT_TILE_LAYERS = [ ), ), # Overlay layers - TileSource( - "opensea", - "OpenSea", - "https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png", - LayerType.OVERLAY, - None, - LayerAccess.PUBLIC, - 'Kartendaten: © <a href="http://www.openseamap.org">OpenSeaMap</a> contributors', + TileLayerConfig( + layer_id="opensea", + name="OpenSea", + url="https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png", + layer_type=LayerType.OVERLAY, + zoom=None, + access=LayerAccess.PUBLIC, + attribution=( + 'Kartendaten: © <a href="http://www.openseamap.org">OpenSeaMap</a> contributors' + ), ), - TileSource( - "hiking", - "Hiking", - "https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png", - LayerType.OVERLAY, - None, - LayerAccess.PUBLIC, - f'© {_href("http://waymarkedtrails.org", "Sarah Hoffmann")} ' - f'({_href("https://creativecommons.org/licenses/by-sa/3.0/", "CC-BY-SA")})', + TileLayerConfig( + layer_id="hiking", + name="Hiking", + url="https://tile.waymarkedtrails.org/hiking/{z}/{x}/{y}.png", + layer_type=LayerType.OVERLAY, + zoom=None, + access=LayerAccess.PUBLIC, + attribution=( + f'© {_href("http://waymarkedtrails.org", "Sarah Hoffmann")} ' + f'({_href("https://creativecommons.org/licenses/by-sa/3.0/", "CC-BY-SA")})' + ), ), - TileSource( - "cycling", - "Cycling", - "https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png", - LayerType.OVERLAY, - None, - LayerAccess.PUBLIC, - f'© {_href("http://waymarkedtrails.org", "Sarah Hoffmann")} ' - f'({_href("https://creativecommons.org/licenses/by-sa/3.0/", "CC-BY-SA")})', + TileLayerConfig( + layer_id="cycling", + name="Cycling", + url="https://tile.waymarkedtrails.org/cycling/{z}/{x}/{y}.png", + layer_type=LayerType.OVERLAY, + zoom=None, + access=LayerAccess.PUBLIC, + attribution=( + f'© {_href("http://waymarkedtrails.org", "Sarah Hoffmann")} ' + f'({_href("https://creativecommons.org/licenses/by-sa/3.0/", "CC-BY-SA")})' + ), ), ] STAMEN_LAYERS = [ - TileSource( - "stamen-toner", - "Stamen Toner", - "https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png", - LayerType.BASE, - 12, - LayerAccess.PUBLIC, - f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' - 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' - 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' - '<a href="http://www.openstreetmap.org/copyright">ODbL</a>.', + TileLayerConfig( + layer_id="stamen-toner", + name="Stamen Toner", + url="https://stamen-tiles.a.ssl.fastly.net/toner/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=12, + access=LayerAccess.PUBLIC, + attribution=( + f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' + 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' + 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' + '<a href="http://www.openstreetmap.org/copyright">ODbL</a>.' + ), ), - TileSource( - "stamen-terrain", - "Stamen Terrain", - "https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png", - LayerType.BASE, - 12, - LayerAccess.PUBLIC, - f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' - 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' - 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' - '<a href="http://www.openstreetmap.org/copyright">ODbL</a>.', + TileLayerConfig( + layer_id="stamen-terrain", + name="Stamen Terrain", + url="https://stamen-tiles.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=12, + access=LayerAccess.PUBLIC, + attribution=( + f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' + 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' + 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' + '<a href="http://www.openstreetmap.org/copyright">ODbL</a>.' + ), ), - TileSource( - "stamen-watercolor", - "Stamen Watercolor", - "https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png", - LayerType.BASE, - 12, - LayerAccess.PUBLIC, - f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' - 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' - 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' - '<a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.', + TileLayerConfig( + layer_id="stamen-watercolor", + name="Stamen Watercolor", + url="https://stamen-tiles.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png", + layer_type=LayerType.BASE, + zoom=12, + access=LayerAccess.PUBLIC, + attribution=( + f'{_jb_copy} | Map tiles by <a href="http://stamen.com">Stamen Design</a>, ' + 'under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. ' + 'Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under ' + '<a href="http://creativecommons.org/licenses/by-sa/3.0">CC BY SA</a>.' + ), ), ] @@ -239,7 +233,7 @@ def tile_proxy(request): raise HTTPBadRequest("Tile proxying is disabled") provider = request.matchdict["provider"] - tile_sources = {source.key: source for source in sources_for(request)} + tile_sources = {source.layer_id: source for source in sources_for(request)} if provider not in tile_sources: raise HTTPBadRequest("Invalid provider") @@ -262,7 +256,7 @@ def tile_proxy(request): LOGGER.debug("Aborted attempt to contact %s due to previous timeouts", provider) raise HTTPGatewayTimeout(f"Avoiding request to {provider}") - url = tile_sources[provider].url_template.format(x=x, y=y, z=z, s=random.choice("abc")) + url = tile_sources[provider].url.format(x=x, y=y, z=z, s=random.choice("abc")) headers = { "user-agent": f"Fietsboek-Tile-Proxy/{__VERSION__}", } @@ -287,13 +281,11 @@ def tile_proxy(request): return Response(resp.content, content_type=resp.headers.get("Content-type", content_type)) -def sources_for(request): +def sources_for(request: Request) -> List[TileLayerConfig]: """Returns all eligible tile sources for the given request. :param request: The Pyramid request. - :type request: pyramid.request.Request :return: A list of tile sources. - :rtype: list[TileSource] """ return [ source @@ -301,7 +293,7 @@ def sources_for(request): ( default_layer for default_layer in DEFAULT_TILE_LAYERS - if default_layer.key in request.config.default_tile_layers + if default_layer.layer_id in request.config.default_tile_layers ), extract_tile_layers(request.config), ) @@ -309,18 +301,16 @@ def sources_for(request): ] -def extract_tile_layers(config): +def extract_tile_layers(config: Config) -> List[TileLayerConfig]: """Extract all defined tile layers from the settings. :param config: The fietsboek config. - :type config: fietsboek.config.Config :return: A list of extracted tile sources. - :rtype: list[TileSource] """ layers = [] layers.extend(_extract_thunderforest(config)) layers.extend(_extract_stamen(config)) - layers.extend(_extract_user_layers(config)) + layers.extend(config.tile_layers) return layers @@ -341,32 +331,18 @@ def _extract_thunderforest(config): f"https://tile.thunderforest.com/{tf_map}/" f"{{z}}/{{x}}/{{y}}.png?apikey={tf_api_key}" ) - yield TileSource( - f"tf-{tf_map}", - f"TF {tf_map.title()}", - url, - LayerType.BASE, - 22, - tf_access, - tf_attribution, + yield TileLayerConfig( + layer_id=f"tf-{tf_map}", + name=f"TF {tf_map.title()}", + url=url, + layer_type=LayerType.BASE, + zoom=22, + access=tf_access, + attribution=tf_attribution, ) def _extract_stamen(config): - layers = {layer.key: layer for layer in STAMEN_LAYERS} + layers = {layer.layer_id: layer for layer in STAMEN_LAYERS} for name in config.stamen_maps: yield layers[f"stamen-{name}"] - - -def _extract_user_layers(config): - # Any other custom maps - for layer in config.tile_layers: - yield TileSource( - layer.layer_id, - layer.name, - layer.url, - layer.layer_type, - layer.zoom, - layer.access, - layer.attribution, - ) |