Coverage for gws-app/gws/gis/zoom/__init__.py: 98%
83 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 23:09 +0200
1from typing import Optional
3import gws
4import gws.lib.extent
5import gws.lib.uom as units
7OSM_SCALES = [
8 500_000_000,
9 250_000_000,
10 150_000_000,
11 70_000_000,
12 35_000_000,
13 15_000_000,
14 10_000_000,
15 4_000_000,
16 2_000_000,
17 1_000_000,
18 500_000,
19 250_000,
20 150_000,
21 70_000,
22 35_000,
23 15_000,
24 8_000,
25 4_000,
26 2_000,
27 1_000,
28 500,
29]
30"""Scales corresponding to OSM zoom levels. (https://wiki.openstreetmap.org/wiki/Zoom_levels)"""
32OSM_RESOLUTIONS = list(reversed([units.scale_to_res(s) for s in OSM_SCALES]))
33"""Resolutions corresponding to OSM zoom levels."""
36class Config(gws.Config):
37 """Zoom levels and resolutions"""
39 resolutions: Optional[list[float]]
40 """Allowed resolutions."""
41 initResolution: Optional[float]
42 """Initial resolution."""
43 scales: Optional[list[float]]
44 """Allowed scales."""
45 initScale: Optional[float]
46 """Initial scale."""
47 minResolution: Optional[float]
48 """Minimal resolution."""
49 maxResolution: Optional[float]
50 """Maximal resolution."""
51 minScale: Optional[float]
52 """Minimal scale."""
53 maxScale: Optional[float]
54 """Maximal scale."""
57def resolutions_from_config(cfg, parent_resolutions: list[float] = None) -> list[float]:
58 """Loads resolution from a config.
60 Args:
61 cfg: A config.
62 parent_resolutions: List of parent resolutions.
64 Returns:
65 A list of resolutions.
66 """
68 # see also https://mapproxy.org/docs/1.11.0/configuration.html#res and below
70 # @TODO deal with scales separately
72 rmin = _res_or_scale(cfg, 'minResolution', 'minScale')
73 rmax = _res_or_scale(cfg, 'maxResolution', 'maxScale')
75 res = _explicit_resolutions(cfg) or parent_resolutions or list(OSM_RESOLUTIONS)
76 if rmax and rmax < max(res):
77 res = [r for r in res if r < rmax]
78 res.append(rmax)
79 if rmin and rmin > min(res):
80 res = [r for r in res if r > rmin]
81 res.append(rmin)
83 return sorted(set(res))
86def resolutions_from_source_layers(source_layers: list[gws.SourceLayer], parent_resolutions: list[float]) -> list[float]:
87 """Loads resolution from a source layers.
89 Args:
90 source_layers: Source layers.
91 parent_resolutions: List of parent resolutions.
93 Returns:
94 A list of resolutions.
95 """
97 smin = []
98 smax = []
100 for sl in source_layers:
101 sr = sl.scaleRange
102 if not sr:
103 return parent_resolutions
104 smin.append(sr[0])
105 smax.append(sr[1])
107 if not smin:
108 return parent_resolutions
110 rmin = units.scale_to_res(min(smin))
111 rmax = units.scale_to_res(max(smax))
113 pmin = min(parent_resolutions)
114 pmax = max(parent_resolutions)
116 if rmin > pmax or rmax < pmin:
117 return []
119 lt = [r for r in parent_resolutions if r <= rmin]
120 gt = [r for r in parent_resolutions if r >= rmax]
122 rmin = max(lt) if lt else pmin
123 rmax = min(gt) if gt else pmax
125 return [r for r in parent_resolutions if rmin <= r <= rmax]
128def resolutions_from_bounds(b: gws.Bounds, tile_size: int) -> list[float]:
129 """Loads resolutions from bounds.
131 Args:
132 b: Bounds object.
133 tile_size: The tile size.
135 Returns:
136 A list of resolutions.
137 """
139 siz = gws.lib.extent.size(b.extent)
140 res = []
141 for z in range(20):
142 res.append(siz[0] / (tile_size * (1 << z)))
143 return res
146def init_resolution(cfg, resolutions: list) -> float:
147 """Returns the initial resolution.
149 Args:
150 cfg: A config.
151 resolutions: List of Resolutions.
152 """
153 init = _res_or_scale(cfg, 'initResolution', 'initScale')
154 if not init:
155 return resolutions[len(resolutions) >> 1]
156 return min(resolutions, key=lambda r: abs(init - r))
159def _explicit_resolutions(cfg):
160 ls = gws.u.get(cfg, 'resolutions')
161 if ls:
162 return ls
164 ls = gws.u.get(cfg, 'scales')
165 if ls:
166 return [units.scale_to_res(x) for x in ls]
169def _res_or_scale(cfg, r, s):
170 x = gws.u.get(cfg, r)
171 if x:
172 return x
173 x = gws.u.get(cfg, s)
174 if x:
175 return units.scale_to_res(x)