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
1212import copy
1313import hashlib
1414import json
1515import math
1616import re
17- from typing import Final , NamedTuple , Optional , Union , cast
17+ from typing import Any , Callable , Final , NamedTuple , Optional , Union , cast
1818
1919import altair as alt
2020import mercantile as mt
@@ -115,6 +115,7 @@ def add_tiles(
115115
116116 return final_chart
117117
118+
118119def 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+
221223def _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+
428438class _XYMinMax (NamedTuple ):
429439 x_min : int
430440 y_min : int
431441 x_max : int
432442 y_max : int
433443
444+
434445def _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+
441460def _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+
459480def _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+
478500def 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+
529552def _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+
534558def _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+
550576def _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 )
0 commit comments