Skip to content

Commit c7d9b47

Browse files
committed
ruff / mypy
1 parent e59834f commit c7d9b47

4 files changed

Lines changed: 4319 additions & 25 deletions

File tree

altair_tiles/__init__.py

Lines changed: 60 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,19 @@
22
__all__ = [
33
"add_attribution",
44
"add_tiles",
5+
"concat",
56
"create_tiles_chart",
6-
"providers",
77
"hconcat",
8+
"providers",
89
"vconcat",
9-
"concat",
1010
]
1111

1212
import copy
1313
import hashlib
1414
import json
1515
import math
1616
import re
17-
from typing import Final, NamedTuple, Optional, Union, cast
17+
from typing import Any, Callable, Final, NamedTuple, Optional, Union, cast
1818

1919
import altair as alt
2020
import mercantile as mt
@@ -115,6 +115,7 @@ def add_tiles(
115115

116116
return final_chart
117117

118+
118119
def create_tiles_chart(
119120
provider: Union[str, TileProvider] = "OpenStreetMap.Mapnik",
120121
zoom: Optional[int] = None,
@@ -218,9 +219,13 @@ def create_tiles_chart(
218219
else:
219220
return tiles
220221

222+
221223
def _build_tile_url(provider: TileProvider, x: str, y: str, z: str) -> str:
222224
"""Build a Vega expression string for a tile URL with x/y/z interpolated."""
223-
fmt = lambda v: f"' + {v} + '"
225+
226+
def fmt(v: str) -> str:
227+
return f"' + {v} + '"
228+
224229
return "'" + provider.build_url(x=fmt(x), y=fmt(y), z=fmt(z)) + "'"
225230

226231

@@ -259,7 +264,9 @@ def pname(base: str) -> str:
259264
)
260265
evaluated_zoom_level_ceil = math.ceil(zoom)
261266
else:
262-
p_base_tile_size = alt.param(value=_DEFAULT_TILE_SIZE, name=pname("base_tile_size"))
267+
p_base_tile_size = alt.param(
268+
value=_DEFAULT_TILE_SIZE, name=pname("base_tile_size")
269+
)
263270
p_zoom_level = alt.param(
264271
expr=f"log((2 * PI * {p_pr_scale.name}) / {p_base_tile_size.name}) /"
265272
+ " log(2)",
@@ -270,9 +277,7 @@ def pname(base: str) -> str:
270277
if evaluated_zoom_level_ceil is not None:
271278
_validate_zoom(evaluated_zoom_level_ceil, provider=provider)
272279

273-
p_zoom_ceil = alt.param(
274-
expr=f"ceil({p_zoom_level.name})", name=pname("zoom_ceil")
275-
)
280+
p_zoom_ceil = alt.param(expr=f"ceil({p_zoom_level.name})", name=pname("zoom_ceil"))
276281
# Number of tiles per column/row, whichever is larger. Total number of tiles
277282
# would then be this number squared. However, this number does not account
278283
# for tiles which will be outside of the view, i.e. what is visible on the chart.
@@ -341,7 +346,9 @@ def pname(base: str) -> str:
341346
.transform_calculate(b=f"sequence(0, {one_side_grid_size})")
342347
.transform_flatten(["b"])
343348
.transform_calculate(
344-
url=_build_tile_url(provider, x=expr_url_x, y=expr_url_y, z=p_zoom_ceil.name),
349+
url=_build_tile_url(
350+
provider, x=expr_url_x, y=expr_url_y, z=p_zoom_ceil.name
351+
),
345352
x=f"datum.a * {p_tile_size.name} + {p_dx.name} + ({p_tile_size.name} / 2)",
346353
y=f"datum.b * {p_tile_size.name} + {p_dy.name} + ({p_tile_size.name} / 2)",
347354
)
@@ -401,8 +408,10 @@ def pname(base: str) -> str:
401408
bounds=provider_bounds, zoom=evaluated_zoom_level_ceil
402409
)
403410
tiles = tiles.transform_filter(
404-
f"{expr_url_x} >= {x_y_min_max.x_min} && {expr_url_y} >= {x_y_min_max.y_min}"
405-
+ f" && {expr_url_x} <= {x_y_min_max.x_max} && {expr_url_y} <= {x_y_min_max.y_max}"
411+
f"{expr_url_x} >= {x_y_min_max.x_min} && {expr_url_y} >= "
412+
f"{x_y_min_max.y_min}"
413+
+ f" && {expr_url_x} <= {x_y_min_max.x_max} && {expr_url_y} <= "
414+
f"{x_y_min_max.y_max}"
406415
)
407416

408417
tiles = tiles.add_params(
@@ -425,19 +434,29 @@ def pname(base: str) -> str:
425434
return add_attribution(tiles, provider, attribution)
426435
return tiles
427436

437+
428438
class _XYMinMax(NamedTuple):
429439
x_min: int
430440
y_min: int
431441
x_max: int
432442
y_max: int
433443

444+
434445
def _bounds_to_x_y_min_max(bounds: list[list[float]], zoom: int) -> _XYMinMax:
435446
south_west, north_east = bounds
436447
south, west = south_west
437448
north, east = north_east
438-
xs, ys = zip(*((t.x, t.y) for t in mt.tiles(west=west, south=south, east=east, north=north, zooms=[zoom])))
449+
xs, ys = zip(
450+
*(
451+
(t.x, t.y)
452+
for t in mt.tiles(
453+
west=west, south=south, east=east, north=north, zooms=[zoom]
454+
)
455+
)
456+
)
439457
return _XYMinMax(x_min=min(xs), y_min=min(ys), x_max=max(xs), y_max=max(ys))
440458

459+
441460
def _validate_zoom(zoom: int, provider: TileProvider) -> None:
442461
# Follows very closely the implementation in contextily.tile._validate_zoom
443462
# https://github.com/geopandas/contextily/blob/0c8c9ce6d99f29e5fd250ee505f52a9bad30642b/contextily/tile.py#LL538C3-L538C3 # noqa: E501
@@ -453,9 +472,11 @@ def _validate_zoom(zoom: int, provider: TileProvider) -> None:
453472
if not (min_zoom <= zoom <= max_zoom):
454473
detail = f" (valid zooms: {min_zoom} - {max_zoom})" if max_zoom_known else ""
455474
raise ValueError(
456-
f"The zoom level of {zoom} is not valid for the current tile provider{detail}."
475+
f"The zoom level of {zoom} is not valid for the current tile "
476+
f"provider{detail}."
457477
)
458478

479+
459480
def _calculate_one_side_grid_size(evaluated_zoom_level_ceil: Optional[int]) -> int:
460481
# Size of tile grid needs to be calculated in Python as it is not possible
461482
# yet to use an expression in a data generator such as a sequence.
@@ -475,6 +496,7 @@ def _calculate_one_side_grid_size(evaluated_zoom_level_ceil: Optional[int]) -> i
475496
return min(2**evaluated_zoom_level_ceil + 2, maximum_value)
476497
return maximum_value
477498

499+
478500
def add_attribution(
479501
chart: Union[alt.Chart, alt.LayerChart],
480502
provider: Union[str, TileProvider] = "OpenStreetMap.Mapnik",
@@ -526,17 +548,20 @@ def add_attribution(
526548

527549
return chart
528550

551+
529552
def _resolve_provider(provider: Union[str, TileProvider]) -> TileProvider:
530553
if isinstance(provider, str):
531-
provider = cast(TileProvider, providers.query_name(provider))
554+
provider = cast("TileProvider", providers.query_name(provider))
532555
return provider
533556

557+
534558
def _validate_projection(projection: alt.Projection) -> None:
535559
if not isinstance(projection, alt.Projection):
536560
raise TypeError("Projection must be an alt.Projection instance.")
537561
if projection.type != "mercator":
538562
raise ValueError("Projection must be of type 'mercator'.")
539563

564+
540565
# ---------------------------------------------------------------------------
541566
# Projection signal name rewriting
542567
# ---------------------------------------------------------------------------
@@ -547,6 +572,7 @@ def _validate_projection(projection: alt.Projection) -> None:
547572
# The internal view name prefix used by altair_tiles for tile layer charts.
548573
_TILE_VIEW_PREFIX = "tiles_"
549574

575+
550576
def _rewrite_params_inplace(chart: alt.LayerChart, replacement: str) -> None:
551577
"""Mutate ``geoScale`` / ``invert`` tile projection references in-place on
552578
*chart* and all of its sublayers, replacing every ``tiles_*_projection``
@@ -569,7 +595,8 @@ def _rewrite_params_inplace(chart: alt.LayerChart, replacement: str) -> None:
569595
for sub in layer:
570596
_rewrite_params_inplace(sub, replacement)
571597

572-
def _rewrite_tile_projection_refs(spec: dict) -> dict:
598+
599+
def _rewrite_tile_projection_refs(spec: dict) -> dict: # noqa: C901
573600
"""Post-process a Vega-Lite spec dict to rewrite tile projection signal
574601
references so that they match the signal names Vega-Lite's normalizer
575602
produces at runtime.
@@ -601,7 +628,8 @@ def _rewrite_tile_projection_refs(spec: dict) -> dict:
601628

602629
def _collect_cells(cells: list, prefix: str) -> None:
603630
for i, cell in enumerate(cells):
604-
# The tile name is on layer[0] (the image mark), not on the outer LayerChart.
631+
# The tile name is on layer[0] (the image mark), not on the outer
632+
# LayerChart.
605633
# Walk the first layer item to find it.
606634
name = cell.get("name", "")
607635
if not name.startswith(_TILE_VIEW_PREFIX):
@@ -610,11 +638,13 @@ def _collect_cells(cells: list, prefix: str) -> None:
610638
if layers:
611639
name = layers[0].get("name", "")
612640
if name.startswith(_TILE_VIEW_PREFIX):
613-
suffix = name[len(_TILE_VIEW_PREFIX):]
641+
suffix = name[len(_TILE_VIEW_PREFIX) :]
614642
suffix_to_signal[suffix] = f"{prefix}{i}_projection"
615643
for nested_key in ("hconcat", "vconcat", "concat"):
616644
if nested_key in cell:
617-
_collect_cells(cell[nested_key], f"{prefix}{i}_{nested_key[0]}concat_")
645+
_collect_cells(
646+
cell[nested_key], f"{prefix}{i}_{nested_key[0]}concat_"
647+
)
618648

619649
for concat_key in ("hconcat", "vconcat", "concat"):
620650
if concat_key in spec:
@@ -642,11 +672,15 @@ def _collect_cells(cells: list, prefix: str) -> None:
642672

643673
return spec
644674

675+
645676
# ---------------------------------------------------------------------------
646677
# Concat wrappers
647678
# ---------------------------------------------------------------------------
648679

649-
def _wrap_concat(alt_fn, *charts, **kwargs):
680+
681+
def _wrap_concat(
682+
alt_fn: Callable[..., Any], *charts: alt.Chart, **kwargs: Any
683+
) -> alt.Chart:
650684
"""Internal helper: call an altair concat function then rewrite projection refs.
651685
652686
We serialise to a dict, rewrite the tile projection signal names to match
@@ -658,7 +692,8 @@ def _wrap_concat(alt_fn, *charts, **kwargs):
658692
rewritten = _rewrite_tile_projection_refs(combined.to_dict())
659693
return alt.Chart.from_dict(rewritten)
660694

661-
def hconcat(*charts, **kwargs) -> alt.ConcatChart:
695+
696+
def hconcat(*charts: alt.Chart, **kwargs: Any) -> alt.Chart:
662697
"""Horizontally concatenate charts, correctly rewriting tile projection
663698
signal references for each concat cell.
664699
@@ -671,7 +706,8 @@ def hconcat(*charts, **kwargs) -> alt.ConcatChart:
671706
"""
672707
return _wrap_concat(alt.hconcat, *charts, **kwargs)
673708

674-
def vconcat(*charts, **kwargs) -> alt.ConcatChart:
709+
710+
def vconcat(*charts: alt.Chart, **kwargs: Any) -> alt.Chart:
675711
"""Vertically concatenate charts, correctly rewriting tile projection
676712
signal references for each concat cell.
677713
@@ -684,7 +720,8 @@ def vconcat(*charts, **kwargs) -> alt.ConcatChart:
684720
"""
685721
return _wrap_concat(alt.vconcat, *charts, **kwargs)
686722

687-
def concat(*charts, **kwargs) -> alt.ConcatChart:
723+
724+
def concat(*charts: alt.Chart, **kwargs: Any) -> alt.Chart:
688725
"""Concatenate charts (wrappable grid), correctly rewriting tile projection
689726
signal references for each concat cell.
690727
@@ -695,4 +732,4 @@ def concat(*charts, **kwargs) -> alt.ConcatChart:
695732
696733
All arguments are forwarded verbatim to ``alt.concat``.
697734
"""
698-
return _wrap_concat(alt.concat, *charts, **kwargs)
735+
return _wrap_concat(alt.concat, *charts, **kwargs)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ build-and-publish = ["clean", "build", "publish"]
8989
[tool.ruff]
9090
target-version = "py39"
9191
line-length = 88
92-
exclude = [".git", "build", "__pycache__", "*.ipynb"]
92+
exclude = [".git", "build", "__pycache__", "*.ipynb", "pyproject.toml"]
9393

9494
[tool.ruff.lint]
9595
select = [

tests/test_altair_tiles.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def test_validate_projection():
4040

4141
# Test invalid projection
4242
projection = alt.Projection(type="albersUsa")
43-
with pytest.raises(ValueError, match="Projection must be of type 'mercator'."):
43+
with pytest.raises(ValueError, match=r"Projection must be of type 'mercator'\."):
4444
til._validate_projection(projection)
4545

4646

0 commit comments

Comments
 (0)