Skip to content

Commit

Permalink
pylint
Browse files Browse the repository at this point in the history
  • Loading branch information
Lowtower committed Jun 3, 2024
1 parent 03ce4d0 commit d90c166
Show file tree
Hide file tree
Showing 36 changed files with 239 additions and 4 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ help: ## Print help for each target
| sort | awk 'BEGIN {FS=":.* ## "}; {printf "%-25s %s\n", $$1, $$2};'

clean: ## Cleanup
@rm -rf ./.env
@rm -f ./*.pyc
@rm -rf ./__pycache__
@rm -f $(SRC_CORE)/*.pyc
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/calendar_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Draw a calendar poster."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/circular_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Draw a circular Poster."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Exceptions"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/github_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Draw a GitHub style poster."""

# Copyright 2020-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/grid_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Draw a grid poster."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
173 changes: 173 additions & 0 deletions gpxtrackposter/heatmap_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Draw a heatmap poster."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand All @@ -7,11 +8,17 @@
import argparse
import logging
import math
import os
import sys
import uuid
from operator import itemgetter
from typing import Dict, List, Optional, Tuple

import s2sphere # type: ignore
import staticmaps # type: ignore
import svgwrite # type: ignore
from geopy.distance import distance # type: ignore
from PIL import Image # type: ignore

from gpxtrackposter import utils
from gpxtrackposter.exceptions import ParameterError, PosterError
Expand All @@ -38,6 +45,7 @@ class HeatmapDrawer(TracksDrawer):
create_args: Create arguments for heatmap.
fetch_args: Get arguments passed.
draw: Draw the heatmap based on the Poster's tracks.
draw_background: Draw the heatmaps background image if requested.
"""

def __init__(self, the_poster: Poster):
Expand All @@ -49,6 +57,11 @@ def __init__(self, the_poster: Poster):
self._heatmap_line_width_lower: List[Tuple[float, float]] = [(0.10, 5.0), (0.20, 2.0), (1.0, 0.30)]
self._heatmap_line_width_upper: List[Tuple[float, float]] = [(0.02, 0.5), (0.05, 0.2), (1.0, 0.05)]
self._heatmap_line_width: Optional[List[Tuple[float, float]]] = None
self._heatmap_renderer: str = "pillow"
self._tile_provider: Optional[staticmaps.TileProvider] = None
self._tile_context: staticmaps.Context = staticmaps.Context()
self._bg_max_size: int = 1200
self._transformer: Optional[staticmaps.Transformer] = None

def create_args(self, args_parser: argparse.ArgumentParser) -> None:
"""Add arguments to the parser
Expand Down Expand Up @@ -80,6 +93,36 @@ def create_args(self, args_parser: argparse.ArgumentParser) -> None:
help="Define three transparency and width tuples for the heatmap lines or set it to "
"`automatic` for automatic calculation (default: 0.1,5.0, 0.2,2.0, 1.0,0.3).",
)
tile_provider = staticmaps.default_tile_providers.keys()
group.add_argument(
"--heatmap-tile-provider",
dest="heatmap_tile_provider",
metavar="TILE_PROVIDER",
type=str,
choices=tile_provider,
help="Optionally, choose a tile provider from the list for a background map image: "
f"{', '.join(tile_provider)}. (Default: None)",
)
group.add_argument(
"--heatmap-tile-max-size",
dest="heatmap_tile_max_size",
metavar="PIXEL",
type=int,
default=1200,
help="Set the maximum background image size (which is afterwards scaled to the poster size). "
"This setting defines how much details will be shown on the map. "
"Be sure to choose a reasonable value! (default: 1200 px)",
)
bg_renderer = ["pillow", "cairo"]
group.add_argument(
"--heatmap-tile-renderer",
dest="heatmap_renderer",
metavar="RENDERER",
choices=bg_renderer,
default=self._heatmap_renderer,
help=f"Choose a renderer for generating the background image, one of {', '.join(bg_renderer)}. "
f"(default: {self._heatmap_renderer})",
)

def fetch_args(self, args: argparse.Namespace) -> None:
"""Get arguments that were passed, and also perform basic validation on them.
Expand All @@ -93,6 +136,23 @@ def fetch_args(self, args: argparse.Namespace) -> None:
self._center = self.validate_heatmap_center(args.heatmap_center)
self._radius = self.validate_heatmap_radius(args.heatmap_radius)
self._heatmap_line_width = self.validate_heatmap_line_width(args.heatmap_line_width)
self._center = self.validate_heatmap_center(args.heatmap_center)
self._radius = self.validate_heatmap_radius(args.heatmap_radius)
self._heatmap_line_width = self.validate_heatmap_line_width(args.heatmap_line_width)

if args.heatmap_tile_provider:
self._tile_provider = args.heatmap_tile_provider
if args.heatmap_tile_max_size:
self._bg_max_size = args.heatmap_tile_max_size
if args.heatmap_tile_max_size > 4800:
msg = (
f"A size of < {args.heatmap_tile_max_size} > pixels for the background image is very high.\n"
"Fetching large tiles takes time and consumes much disk space.\n"
"Consider choosing a smaller size!"
)
log.warning(msg)
# set background image renderer
self._heatmap_renderer = args.heatmap_renderer

def get_line_transparencies_and_widths(self, bbox: s2sphere.sphere.LatLngRect) -> List[Tuple[float, float]]:
"""Get a list of tuples of line widths and transparencies
Expand Down Expand Up @@ -174,6 +234,7 @@ def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY, offs
if len(self.poster.tracks) == 0:
raise PosterError("No tracks to draw.")
bbox = self._determine_bbox()
size, offset = self._get_tracks_size_offset(bbox, size, offset)
line_transparencies_and_widths = self.get_line_transparencies_and_widths(bbox)
year_groups: Dict[int, svgwrite.container.Group] = {}
for tr in self.poster.tracks:
Expand Down Expand Up @@ -281,3 +342,115 @@ def validate_heatmap_line_width(
raise ParameterError(f"Not three valid TRANSPARENCY,WIDTH pairs: {heatmap_line_width}") from e
return self._heatmap_line_width
return None

def draw_background(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY, offset: XY) -> None:
"""Draw background with background static map if requested
Args:
dr: svg drawing
g: svg group
size: Size
offset: Offset
"""
super().draw_background(dr, g, size, offset)
if not self._tile_provider:
return

# retrieve static map
bbox = self._determine_bbox()
self._tile_context.set_tile_provider(staticmaps.default_tile_providers[self._tile_provider])
self._tile_context.set_center(bbox.get_center())
# remove padding from poster size to retrieve background image size
size = size - XY(
self.poster.padding["l"] + self.poster.padding["r"], self.poster.padding["t"] + self.poster.padding["b"]
)
offset = offset + XY(self.poster.padding["l"], self.poster.padding["t"])
bg_size = size.scale_to_max_value(self._bg_max_size).round()

# get maximum track line width, scale and add to background image boundary
scale = max([bg_size.x / size.x, bg_size.y / size.y])
self._heatmap_line_width = self.get_line_transparencies_and_widths(bbox)
half_stroke = round(scale * (max(self._heatmap_line_width, key=itemgetter(1))[1] / 2))
bbox_corner_list = [bbox.get_vertex(0), bbox.get_vertex(1), bbox.get_vertex(2), bbox.get_vertex(3)]
bounds = staticmaps.Bounds(bbox_corner_list, half_stroke)
self._tile_context.add_object(bounds)
# tighten the background map to bounds
self._tile_context.set_tighten_to_bounds(True)

# set transformer with center and zoom
center, zoom = self._tile_context.determine_center_zoom(bg_size.x, bg_size.y)
self._transformer = staticmaps.Transformer(
bg_size.x,
bg_size.y,
zoom,
center,
staticmaps.default_tile_providers[self._tile_provider].tile_size(),
)

# TODOX: remove testing code
from staticmaps.color import BLACK, RED # type: ignore

self._tile_context.add_object(staticmaps.Line([bbox.lo(), bbox.hi()], RED, 1))
self._tile_context.add_object(
staticmaps.Line(
[
s2sphere.LatLng.from_angles(bbox.lat_lo(), bbox.lng_lo()),
s2sphere.LatLng.from_angles(bbox.lat_lo(), bbox.lng_hi()),
s2sphere.LatLng.from_angles(bbox.lat_hi(), bbox.lng_hi()),
s2sphere.LatLng.from_angles(bbox.lat_hi(), bbox.lng_lo()),
s2sphere.LatLng.from_angles(bbox.lat_lo(), bbox.lng_lo()),
],
BLACK,
1,
)
)

try:
# generate a unique filename
tmp_file = f"{uuid.uuid4()}.png"

# render background image based on command line argument
if self._heatmap_renderer == "cairo":
try:
__import__("cairo")
except ImportError:
msg = (
"The cairo module cannot be imported. "
"Please consider choosing 'pillow' as background image renderer instead!"
)
sys.exit(msg)
image = self._tile_context.render_cairo(bg_size.x, bg_size.y)
image.write_to_png(tmp_file)
else:
image = self._tile_context.render_pillow(bg_size.x, bg_size.y)
image.save(tmp_file)
with open(tmp_file, "rb") as f:
img_inl = staticmaps.SvgRenderer.create_inline_image(f.read())
dr.add(dr.image(img_inl, insert=(offset.x, offset.y), size=(size.x, size.y)))
os.remove(tmp_file)
except (Image.DecompressionBombError, FileNotFoundError):
print("Something went wrong generating the background image!")

def _get_tracks_size_offset(self, bbox: s2sphere.LatLngRect, size: XY, offset: XY) -> Tuple[XY, XY]:
if not self._tile_provider:
return size, offset

# background image size
bg_size = size.scale_to_max_value(self._bg_max_size)
tracks_scale = size / bg_size

transformer = self._transformer
assert transformer is not None
tracks_width = math.fabs(transformer.ll2pixel(bbox.hi())[0] - transformer.ll2pixel(bbox.lo())[0])
tracks_height = math.fabs(transformer.ll2pixel(bbox.hi())[1] - transformer.ll2pixel(bbox.lo())[1])
# add maximum track line width
assert self._heatmap_line_width
max_stroke = max(self._heatmap_line_width, key=itemgetter(1))[1]
half_stroke = max_stroke / 2
tracks_size = XY(tracks_width, tracks_height) + max_stroke
tracks_size_scaled = tracks_scale * tracks_size
tracks_offset = offset + tracks_scale * (
XY(math.fabs(transformer.ll2pixel(bbox.lo())[0]), math.fabs(transformer.ll2pixel(bbox.hi())[1]))
- half_stroke
)
return tracks_size_scaled, tracks_offset
1 change: 1 addition & 0 deletions gpxtrackposter/localization.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Localization helpers."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
17 changes: 16 additions & 1 deletion gpxtrackposter/poster.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Create a poster from track data."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down Expand Up @@ -76,6 +77,7 @@ def __init__(self) -> None:
self.special_distance: Dict[str, float] = {"special_distance1": 10, "special_distance2": 20}
self.width: int = 200
self.height: int = 300
self.padding: Dict[str, int] = {"l": 10, "t": 30, "r": 10, "b": 30}
self.years: YearRange = YearRange()
self.tracks_drawer: Optional["TracksDrawer"] = None
self._trans: Optional[Callable[[str], str]] = None
Expand Down Expand Up @@ -220,9 +222,14 @@ def draw(self, drawer: "TracksDrawer", output: str) -> None:
d = svgwrite.Drawing(output, (f"{self.width}mm", f"{self.height}mm"))
d.viewbox(width=self.width, height=self.height)
d.add(d.rect((0, 0), (self.width, self.height), fill=self.colors["background"]))
self._draw_background(d, XY(self.width, self.height), XY(0.0, 0.0))
self._draw_header(d)
self._draw_footer(d)
self._draw_tracks(d, XY(self.width - 20, self.height - 30 - 30), XY(10, 30))
self._draw_tracks(
d,
XY(self.width - self.padding["l"] - self.padding["r"], self.height - self.padding["t"] - self.padding["b"]),
XY(self.padding["l"], self.padding["t"]),
)
d.save()

def m2u(self, m: pint.Quantity) -> float:
Expand Down Expand Up @@ -267,6 +274,14 @@ def _draw_tracks(self, d: svgwrite.Drawing, size: XY, offset: XY) -> None:

self.tracks_drawer.draw(d, g, size, offset)

def _draw_background(self, d: svgwrite.Drawing, size: XY, offset: XY) -> None:
assert self.tracks_drawer

g = d.g(id="background")
d.add(g)

self.tracks_drawer.draw_background(d, g, size, offset)

def _draw_header(self, d: svgwrite.Drawing) -> None:
g = d.g(id="header")
d.add(g)
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/quantity_range.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Represent a range of pint quantities"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/timezone_adjuster.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""TimezoneAdjuster"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/track.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Create and maintain info about a given activity track (corresponding to one GPX file)."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
12 changes: 12 additions & 0 deletions gpxtrackposter/tracks_drawer.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Contains the base class TracksDrawer, which other Drawers inherit from."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down Expand Up @@ -36,6 +37,17 @@ def fetch_args(self, args: argparse.Namespace) -> None:
"""

def draw_background(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY, offset: XY) -> None:
"""Draw background for all poster types - rectangle with 'background' color
Args:
dr: svg drawing
g: svg group
size: Size
offset: Offset
"""
g.add(dr.rect((offset.x, offset.y), (size.x, size.y), fill=self.poster.colors["background"]))

def draw(self, dr: svgwrite.Drawing, g: svgwrite.container.Group, size: XY, offset: XY) -> None:
"""Draw the circular Poster using distances broken down by time.
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/units.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Units"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Assorted utility methods for use in creating posters."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/value_range.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Represent a range of numerical values"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
1 change: 1 addition & 0 deletions gpxtrackposter/xy.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Represent x,y values with properly overloaded operations."""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down
4 changes: 2 additions & 2 deletions gpxtrackposter/year_range.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""Represent a range of years, with ability to update based on a track"""

# Copyright 2016-2023 Florian Pigorsch & Contributors. All rights reserved.
#
# Use of this source code is governed by a MIT-style
Expand Down Expand Up @@ -123,5 +124,4 @@ def iter(self) -> Generator[int, None, None]:
return

assert self.to_year is not None
for year in range(self.from_year, self.to_year + 1):
yield year
yield from range(self.from_year, self.to_year + 1)
Loading

0 comments on commit d90c166

Please sign in to comment.