Coverage for gws-app/gws/plugin/ows_client/wmts/layer.py: 0%
108 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 12:46 +0200
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-24 12:46 +0200
1from typing import Optional
3import gws
4import gws.base.layer
5import gws.config.util
6import gws.lib.bounds
7import gws.lib.crs
8import gws.gis.source
9import gws.gis.zoom
11from . import provider
13gws.ext.new.layer('wmts')
16class Config(gws.base.layer.Config):
17 """WMTS layer"""
19 provider: provider.Config
20 """WMTS provider."""
21 display: gws.LayerDisplayMode = gws.LayerDisplayMode.tile
22 """Layer display mode."""
23 sourceLayers: Optional[gws.gis.source.LayerFilter]
24 """Source layer filter."""
25 style: Optional[str]
26 """WMTS style name."""
29class Object(gws.base.layer.image.Object):
30 serviceProvider: provider.Object
31 sourceLayers: list[gws.SourceLayer]
33 activeLayer: gws.SourceLayer
34 activeStyle: gws.SourceStyle
35 activeTms: gws.TileMatrixSet
37 def configure(self):
38 self.configure_layer()
40 def configure_provider(self):
41 return gws.config.util.configure_service_provider_for(self, provider.Object)
43 def configure_sources(self):
44 if super().configure_sources():
45 return True
47 gws.u.require(self.serviceProvider, 'failed to configure service provider')
49 self.configure_source_layers()
50 if not self.sourceLayers:
51 raise gws.Error('no source layers found')
53 self.activeLayer = self.sourceLayers[0]
54 self.configure_tms()
55 self.configure_style()
57 def configure_source_layers(self):
58 return gws.config.util.configure_source_layers_for(self, self.serviceProvider.sourceLayers, is_image=True)
60 def configure_tms(self):
61 crs = self.serviceProvider.forceCrs
62 if not crs:
63 crs = gws.lib.crs.best_match(self.mapCrs, [tms.crs for tms in self.activeLayer.tileMatrixSets])
64 tms_list = [tms for tms in self.activeLayer.tileMatrixSets if tms.crs == crs]
65 if not tms_list:
66 raise gws.Error(f'no TMS for {crs} in {self.serviceProvider.url}')
67 self.activeTms = tms_list[0]
69 def configure_style(self):
70 p = self.cfg('styleName')
71 if p:
72 for style in self.activeLayer.styles:
73 if style.name == p:
74 self.activeStyle = style
75 return True
76 raise gws.Error(f'style {p!r} not found')
78 for style in self.activeLayer.styles:
79 if style.isDefault:
80 self.activeStyle = style
81 return True
83 self.activeStyle = gws.SourceStyle(name='default')
84 return True
86 #
87 # reprojecting the world doesn't make sense, just use the map extent here
88 # @TODO maybe look for more sensible grid alignment
89 #
90 # def configure_bounds(self):
91 # if super().configure_bounds():
92 # return True
93 # src_bounds = gws.Bounds(crs=self.activeTms.crs, extent=self.activeTms.matrices[0].extent)
94 # self.bounds = gws.lib.bounds.transform(src_bounds, self.mapCrs)
95 # return True
97 def configure_resolutions(self):
98 if super().configure_resolutions():
99 return True
100 res = [gws.lib.uom.scale_to_res(m.scale) for m in self.activeTms.matrices]
101 self.resolutions = sorted(res, reverse=True)
102 return True
104 def configure_grid(self):
105 p = self.cfg('grid', default=gws.Config())
106 self.grid = gws.TileGrid(
107 origin=p.origin or gws.Origin.nw,
108 tileSize=p.tileSize or self.activeTms.matrices[0].tileWidth,
109 )
110 if p.extent:
111 self.grid.bounds = gws.Bounds(crs=self.mapCrs, extent=p.extent)
112 elif self.activeTms.crs == self.mapCrs:
113 self.grid.bounds = gws.Bounds(crs=self.mapCrs, extent=self.activeTms.matrices[0].extent)
114 else:
115 self.grid.bounds = self.bounds
117 if p.resolutions:
118 self.grid.resolutions = p.resolutions
119 else:
120 self.grid.resolutions = gws.gis.zoom.resolutions_from_bounds(self.grid.bounds, self.grid.tileSize)
122 def configure_legend(self):
123 if super().configure_legend():
124 return True
125 url = self.activeStyle.legendUrl
126 if url:
127 self.legend = self.create_child(gws.ext.object.legend, type='remote', urls=[url])
128 return True
130 def configure_metadata(self):
131 if super().configure_metadata():
132 return True
133 self.metadata = self.serviceProvider.metadata
134 return True
136 def mapproxy_config(self, mc):
137 url = self.serviceProvider.tile_url_template(self.activeLayer, self.activeTms, self.activeStyle)
139 # mapproxy encoding
141 url = url.replace('{TileMatrix}', '%(z)02d')
142 url = url.replace('{TileCol}', '%(x)d')
143 url = url.replace('{TileRow}', '%(y)d')
145 source_grid = self.serviceProvider.grid_for_tms(self.activeTms)
147 if source_grid.origin == gws.Origin.nw:
148 origin = 'nw'
149 elif source_grid.origin == gws.Origin.sw:
150 origin = 'sw'
151 else:
152 raise gws.Error(f'invalid grid origin {source_grid.origin!r}')
154 source_grid_uid = mc.grid(
155 gws.u.compact(
156 {
157 'origin': origin,
158 'srs': source_grid.bounds.crs.epsg,
159 'bbox': source_grid.bounds.extent,
160 'res': source_grid.resolutions,
161 'tile_size': [source_grid.tileSize, source_grid.tileSize],
162 }
163 )
164 )
166 src_uid = gws.base.layer.util.mapproxy_back_cache_config(self, mc, url, source_grid_uid)
167 gws.base.layer.util.mapproxy_layer_config(self, mc, src_uid)
169 ##
171 def render(self, lri):
172 return gws.base.layer.util.mpx_raster_render(self, lri)