diff options
author | Daniel Schadt <kingdread@gmx.de> | 2025-04-23 21:22:38 +0200 |
---|---|---|
committer | Daniel Schadt <kingdread@gmx.de> | 2025-04-23 21:22:38 +0200 |
commit | 72d919085144a2a78198d6eac4213ab2751ab6e8 (patch) | |
tree | 33843664da9d0201102ff69d0b7cdf1a92e5f931 | |
parent | 7d1a1de773f0d0746003e58507898beafb18c57a (diff) | |
download | fietsboek-72d919085144a2a78198d6eac4213ab2751ab6e8.tar.gz fietsboek-72d919085144a2a78198d6eac4213ab2751ab6e8.tar.bz2 fietsboek-72d919085144a2a78198d6eac4213ab2751ab6e8.zip |
fix lints
-rw-r--r-- | fietsboek/config.py | 2 | ||||
-rw-r--r-- | fietsboek/routes.py | 6 | ||||
-rw-r--r-- | fietsboek/trackmap.py | 45 | ||||
-rw-r--r-- | fietsboek/views/detail.py | 26 | ||||
-rw-r--r-- | fietsboek/views/tileproxy.py | 9 |
5 files changed, 76 insertions, 12 deletions
diff --git a/fietsboek/config.py b/fietsboek/config.py index 637dd17..beea820 100644 --- a/fietsboek/config.py +++ b/fietsboek/config.py @@ -283,7 +283,9 @@ class Config(BaseModel): :return: A list of public :class:`TileLayerConfig`s. """ + # pylint: disable=import-outside-toplevel,cyclic-import from .views.tileproxy import DEFAULT_TILE_LAYERS, extract_tile_layers + return [ source for source in chain( diff --git a/fietsboek/routes.py b/fietsboek/routes.py index 9c2af7b..f1d4ee2 100644 --- a/fietsboek/routes.py +++ b/fietsboek/routes.py @@ -46,7 +46,11 @@ def includeme(config): config.add_route( "image", "/track/{track_id}/images/{image_name}", factory="fietsboek.models.Track.factory" ) - config.add_route("track-map", "/track/{track_id}/preview", factory="fietsboek.models.Track.factory") + config.add_route( + "track-map", + "/track/{track_id}/preview", + factory="fietsboek.models.Track.factory", + ) config.add_route("badge", "/badge/{badge_id}", factory="fietsboek.models.Badge.factory") diff --git a/fietsboek/trackmap.py b/fietsboek/trackmap.py index b5fc62b..2457876 100644 --- a/fietsboek/trackmap.py +++ b/fietsboek/trackmap.py @@ -1,8 +1,7 @@ """Module to render tracks to static images on OSM tiles.""" + import io -import itertools import math -from urllib.parse import quote from gpxpy.gpx import GPX from PIL import Image, ImageDraw @@ -15,19 +14,42 @@ TILE_SIZE = 256 def to_web_mercator(lat: float, lon: float, zoom: int) -> tuple[int, int]: + """Convert a pari of latitude/longitude coordinates to web mercator form. + + :param lat: Latitude (in degrees). + :param lon: Longitude (in degrees). + :param zoom: Zoom level. + :return: The web mercator x/y coordinates. Both will be between 0 and + 2**zoom * 256. + """ width = height = TILE_SIZE la = math.radians(lon) phi = math.radians(lat) x = float(2**zoom) / (2 * math.pi) * width * (la + math.pi) - y = float(2**zoom) / (2 * math.pi) * height * (math.pi - math.log(math.tan((math.pi / 4 + phi / 2)))) + y = ( + float(2**zoom) + / (2 * math.pi) + * height + * (math.pi - math.log(math.tan((math.pi / 4 + phi / 2)))) + ) return (int(math.floor(x)), int(math.floor(y))) class TrackMapRenderer: - def __init__(self, track: GPX, requester: TileRequester, size: tuple[int, int], layer: TileLayerConfig): + """A renderer that renders GPX tracks onto small map excerpts.""" + + # pylint: disable=too-few-public-methods + + def __init__( + self, + track: GPX, + requester: TileRequester, + size: tuple[int, int], + layer: TileLayerConfig, + ): self.track = track self.requester = requester self.size = size @@ -37,6 +59,10 @@ class TrackMapRenderer: self.line_width = 5 def render(self) -> Image.Image: + """Render the track. + + :return: The image containing the rendered preview. + """ zoom, bbox = self._find_zoom() image = Image.new("RGB", self.size) start_x, start_y = self._draw_base(image, zoom, bbox) @@ -85,7 +111,6 @@ class TrackMapRenderer: return Image.open(io.BytesIO(tile_data)) def _draw_lines(self, image, zoom, start_x, start_y): - starts = self.track.walk(only_points=True) coords = ( to_web_mercator(point.latitude, point.longitude, zoom) for point in self.track.walk(only_points=True) @@ -97,4 +122,14 @@ class TrackMapRenderer: def render(track: GPX, layer: TileLayerConfig, requester: TileRequester) -> Image.Image: + """Shorthand to construct a :class:`TrackMapRenderer` and render the preview. + + :param track: Parsed track to render. + :param layer: The tile layer to take the map tiles from. + :param requester: The requester which will be used to request the tiles. + :return: The image containing the rendered preview. + """ return TrackMapRenderer(track, requester, (300, 300), layer).render() + + +__all__ = ["to_web_mercator", "TrackMapRenderer", "render"] diff --git a/fietsboek/views/detail.py b/fietsboek/views/detail.py index c5e18b4..d7f35a8 100644 --- a/fietsboek/views/detail.py +++ b/fietsboek/views/detail.py @@ -18,7 +18,6 @@ from pyramid.response import FileResponse, Response from pyramid.view import view_config from sqlalchemy import select -from . import tileproxy from .tileproxy import ITileRequester from .. import models, trackmap, util from ..models.track import Track, TrackWithMetadata @@ -229,6 +228,14 @@ def add_comment(request): @view_config(route_name="track-map", http_cache=3600, permission="track.view") def track_map(request: Request): + """Endpoint to provide the track's preview image. + + Will use the cached version if available. Otherwise, will create the + preview and cache it. + + :param request: The pyramid request. + :return: The HTTP response. + """ track = request.context manager = request.data_manager.open(track.id) preview_path = manager.preview_path() @@ -242,11 +249,11 @@ def track_map(request: Request): loader: ITileRequester = request.registry.getUtility(ITileRequester) layer = request.config.public_tile_layers()[0] - gpx = gpxpy.parse(manager.decompress_gpx()) - image = trackmap.render(gpx, layer, loader) + parsed_gpx = gpxpy.parse(manager.decompress_gpx()) + track_image = trackmap.render(parsed_gpx, layer, loader) imageio = io.BytesIO() - image.save(imageio, "png") + track_image.save(imageio, "png") tile_data = imageio.getvalue() with manager.lock(): @@ -257,4 +264,13 @@ def track_map(request: Request): return response -__all__ = ["details", "gpx", "invalidate_share", "delete_track", "badge", "image", "add_comment", "track_map"] +__all__ = [ + "details", + "gpx", + "invalidate_share", + "delete_track", + "badge", + "image", + "add_comment", + "track_map", +] diff --git a/fietsboek/views/tileproxy.py b/fietsboek/views/tileproxy.py index b52d974..f472d6d 100644 --- a/fietsboek/views/tileproxy.py +++ b/fietsboek/views/tileproxy.py @@ -242,6 +242,8 @@ Note that new requests reset the timeout. class ITileRequester(Interface): # pylint: disable=inherit-non-class """An interface to define the tile requester.""" + # pylint: disable=too-many-arguments + def load_url(self, url: str, headers: Optional[dict[str, str]] = None) -> requests.Response: """Loads a tile at the given URL. @@ -277,7 +279,7 @@ class ITileRequester(Interface): # pylint: disable=inherit-non-class @implementer(ITileRequester) -class TileRequester: # pylint: disable=too-few-public-methods +class TileRequester: """Implementation of the tile requester using requests sessions. The benefit of this over doing ``requests.get`` is that we can re-use @@ -285,6 +287,8 @@ class TileRequester: # pylint: disable=too-few-public-methods servers by not hammering them with too many connections. """ + # pylint: disable=too-many-arguments + def __init__(self, redis): self.session = requests.Session() adapter = requests.adapters.HTTPAdapter( @@ -429,6 +433,9 @@ def sources_for(request: Request) -> list[TileLayerConfig]: :param request: The Pyramid request. :return: A list of tile sources. """ + # This code is similar to Config.public_tile_layers. Maybe it's worth + # refactoring it when we refactor this module? + # pylint: disable=duplicate-code return [ source for source in chain( |