Coverage for gws-app/gws/gis/mpx/config.py: 0%

77 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-17 01:37 +0200

1from typing import Optional, cast 

2 

3import yaml 

4 

5from mapproxy.wsgiapp import make_wsgi_app 

6 

7import gws 

8import gws.lib.osx 

9 

10CONFIG_PATH = gws.c.CONFIG_DIR + '/mapproxy.yaml' 

11 

12DEFAULT_CONFIG = { 

13 "services": { 

14 "wmts": { 

15 } 

16 }, 

17 "sources": { 

18 "test": { 

19 "type": "tile", 

20 "url": "https://osmtiles.gbd-consult.de/ows/%(z)s/%(x)s/%(y)s.png", 

21 } 

22 }, 

23 "layers": [ 

24 { 

25 "name": "test", 

26 "title": "test", 

27 "sources": [ 

28 "test" 

29 ] 

30 } 

31 ] 

32} 

33 

34 

35class _Config: 

36 def __init__(self): 

37 self.c = 0 

38 

39 self.services = { 

40 'wms': { 

41 'image_formats': ['image/png'], 

42 'max_output_pixels': [9000, 9000] 

43 }, 

44 'wmts': { 

45 'kvp': True, 

46 'restful': False 

47 } 

48 } 

49 

50 self.globals = { 

51 # https://mapproxy.org/docs/1.11.0/configuration.html#id14 

52 # "By default MapProxy assumes lat/long (north/east) order for all geographic and x/y (east/north) order for all projected SRS." 

53 # we need to change that because our extents are always x/y (lon/lat) even if a CRS says otherwise 

54 'srs': { 

55 'axis_order_en': ['EPSG:4326'] 

56 }, 

57 'cache': { 

58 'base_dir': gws.c.MAPPROXY_CACHE_DIR, 

59 'lock_dir': gws.u.ensure_dir(gws.c.TRANSIENT_DIR + '/mpx_locks_' + gws.u.random_string(16)), 

60 'tile_lock_dir': gws.u.ensure_dir(gws.c.TRANSIENT_DIR + '/mpx_tile_locks_' + gws.u.random_string(16)), 

61 'concurrent_tile_creators': 1, 

62 'max_tile_limit': 5000, 

63 

64 }, 

65 'image': { 

66 'resampling_method': 'bicubic', 

67 'stretch_factor': 1.15, 

68 'max_shrink_factor': 4.0, 

69 

70 'formats': { 

71 'png8': { 

72 'format': 'image/png', 

73 'mode': 'P', 

74 'colors': 256, 

75 'transparent': True, 

76 'resampling_method': 'bicubic', 

77 }, 

78 'png24': { 

79 'format': 'image/png', 

80 'mode': 'RGBA', 

81 'colors': 0, 

82 'transparent': True, 

83 'resampling_method': 'bicubic', 

84 } 

85 

86 } 

87 }, 

88 'http': { 

89 'hide_error_details': False, 

90 } 

91 } 

92 

93 self.cfg = {} 

94 

95 def _add(self, kind, c): 

96 # mpx doesn't like tuples 

97 for k, v in c.items(): 

98 if isinstance(v, tuple): 

99 c[k] = list(v) 

100 

101 uid = kind + '_' + gws.u.sha256(c) 

102 

103 # clients might add their hash params starting with '$' 

104 c = { 

105 k: v 

106 for k, v in c.items() 

107 if not k.startswith('$') 

108 } 

109 

110 self.cfg[uid] = {'kind': kind, 'c': c} 

111 return uid 

112 

113 def _items(self, kind): 

114 for k, v in self.cfg.items(): 

115 if v['kind'] == kind: 

116 yield k, v['c'] 

117 

118 def cache(self, c): 

119 return self._add('cache', c) 

120 

121 def source(self, c): 

122 return self._add('source', c) 

123 

124 def grid(self, c): 

125 # self._transform_extent(c) 

126 return self._add('grid', c) 

127 

128 def layer(self, c): 

129 c['title'] = '' 

130 return self._add('layer', c) 

131 

132 def to_dict(self): 

133 d = { 

134 'services': self.services, 

135 'globals': self.globals, 

136 } 

137 

138 kinds = ['source', 'grid', 'cache', 'layer'] 

139 for kind in kinds: 

140 d[kind + 's'] = { 

141 key: c 

142 for key, c in self._items(kind) 

143 } 

144 

145 d['layers'] = sorted(d['layers'].values(), key=lambda x: x['name']) 

146 

147 return d 

148 

149 

150def create(root: gws.Root): 

151 mc = _Config() 

152 

153 for layer in root.find_all(gws.ext.object.layer): 

154 m = getattr(layer, 'mapproxy_config', None) 

155 if m: 

156 m(mc) 

157 

158 cfg = mc.to_dict() 

159 if not cfg.get('layers'): 

160 return 

161 

162 crs: list[gws.Crs] = [] 

163 for p in root.find_all(gws.ext.object.map): 

164 crs.append(cast(gws.Map, p).bounds.crs) 

165 for p in root.find_all(gws.ext.object.owsService): 

166 crs.extend(gws.u.get(p, 'supported_crs', default=[])) 

167 cfg['services']['wms']['srs'] = sorted(set(c.epsg for c in crs)) 

168 

169 return cfg 

170 

171 

172def create_and_save(root: gws.Root): 

173 cfg = create(root) 

174 

175 if not cfg: 

176 force = root.app.cfg('server.mapproxy.forceStart') 

177 if force: 

178 gws.log.warning('mapproxy: no configuration, using default') 

179 cfg = DEFAULT_CONFIG 

180 else: 

181 gws.log.warning('mapproxy: no configuration, not starting') 

182 gws.lib.osx.unlink(CONFIG_PATH) 

183 return 

184 

185 cfg_str = yaml.dump(cfg) 

186 

187 # make sure the config is ok before starting the server! 

188 test_path = CONFIG_PATH + '.test.yaml' 

189 gws.u.write_file(test_path, cfg_str) 

190 

191 try: 

192 make_wsgi_app(test_path) 

193 except Exception as e: 

194 raise gws.Error(f'MAPPROXY ERROR: {e!r}') from e 

195 

196 gws.lib.osx.unlink(test_path) 

197 

198 # write into the real config path 

199 gws.u.write_file(CONFIG_PATH, cfg_str) 

200 

201 return cfg