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

1from typing import Optional 

2 

3import gws 

4import gws.lib.extent 

5import gws.lib.uom as units 

6 

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)""" 

31 

32OSM_RESOLUTIONS = list(reversed([units.scale_to_res(s) for s in OSM_SCALES])) 

33"""Resolutions corresponding to OSM zoom levels.""" 

34 

35 

36class Config(gws.Config): 

37 """Zoom levels and resolutions""" 

38 

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.""" 

55 

56 

57def resolutions_from_config(cfg, parent_resolutions: list[float] = None) -> list[float]: 

58 """Loads resolution from a config. 

59 

60 Args: 

61 cfg: A config. 

62 parent_resolutions: List of parent resolutions. 

63 

64 Returns: 

65 A list of resolutions. 

66 """ 

67 

68 # see also https://mapproxy.org/docs/1.11.0/configuration.html#res and below 

69 

70 # @TODO deal with scales separately 

71 

72 rmin = _res_or_scale(cfg, 'minResolution', 'minScale') 

73 rmax = _res_or_scale(cfg, 'maxResolution', 'maxScale') 

74 

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) 

82 

83 return sorted(set(res)) 

84 

85 

86def resolutions_from_source_layers(source_layers: list[gws.SourceLayer], parent_resolutions: list[float]) -> list[float]: 

87 """Loads resolution from a source layers. 

88 

89 Args: 

90 source_layers: Source layers. 

91 parent_resolutions: List of parent resolutions. 

92 

93 Returns: 

94 A list of resolutions. 

95 """ 

96 

97 smin = [] 

98 smax = [] 

99 

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]) 

106 

107 if not smin: 

108 return parent_resolutions 

109 

110 rmin = units.scale_to_res(min(smin)) 

111 rmax = units.scale_to_res(max(smax)) 

112 

113 pmin = min(parent_resolutions) 

114 pmax = max(parent_resolutions) 

115 

116 if rmin > pmax or rmax < pmin: 

117 return [] 

118 

119 lt = [r for r in parent_resolutions if r <= rmin] 

120 gt = [r for r in parent_resolutions if r >= rmax] 

121 

122 rmin = max(lt) if lt else pmin 

123 rmax = min(gt) if gt else pmax 

124 

125 return [r for r in parent_resolutions if rmin <= r <= rmax] 

126 

127 

128def resolutions_from_bounds(b: gws.Bounds, tile_size: int) -> list[float]: 

129 """Loads resolutions from bounds. 

130 

131 Args: 

132 b: Bounds object. 

133 tile_size: The tile size. 

134 

135 Returns: 

136 A list of resolutions. 

137 """ 

138 

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 

144 

145 

146def init_resolution(cfg, resolutions: list) -> float: 

147 """Returns the initial resolution. 

148 

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)) 

157 

158 

159def _explicit_resolutions(cfg): 

160 ls = gws.u.get(cfg, 'resolutions') 

161 if ls: 

162 return ls 

163 

164 ls = gws.u.get(cfg, 'scales') 

165 if ls: 

166 return [units.scale_to_res(x) for x in ls] 

167 

168 

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)