def test_extent_metatile(self): self.assertEqual(self.ftg.extent(TileCoord(1, 0, 0, 3)), (420000, 349998.5, 420001.5, 350000)) self.assertEqual(self.ftg2.extent(TileCoord(1, 0, 0, 3)), (420000, 349923.2, 420076.8, 350000))
def test_extent_metatile(self) -> None: assert self.ftg.extent(TileCoord(1, 0, 0, 3)) == (420000, 349998.5, 420001.5, 350000) assert self.ftg2.extent(TileCoord(1, 0, 0, 3)) == (420000, 349923.2, 420076.8, 350000)
def test_children_root(self) -> None: tc = TileCoord(0, 0, 0) assert sorted(self.ftg.children(tc)) == sorted(self.qtg.children(tc))
def test_extent_metatile(self) -> None: assert self.ftg.extent(TileCoord(1, 4, 6, 2)) == (428000, 334000, 432000, 338000)
def test_tilecoord(self) -> None: assert self.ftg.tilecoord(1, 428000, 336000) == TileCoord(1, 4, 7) assert self.ftg.tilecoord(1, 430000, 334000) == TileCoord(1, 5, 8) assert self.ftg.tilecoord(1, 432000, 332000) == TileCoord(1, 6, 9)
def test_init_kwargs(self) -> None: tile = Tile(TileCoord(0, 0, 0), kwarg=None) self.assertEqual(tile.kwarg, None)
def test_extent(self) -> None: assert self.ftg.extent(TileCoord(1, 4, 6)) == (428000, 336000, 430000, 338000) assert self.ftg.extent(TileCoord(1, 5, 7)) == (430000, 334000, 432000, 336000)
def test_parent(self): self.assertEqual(self.qtg.parent(TileCoord(5, 11, 21)), TileCoord(4, 5, 10))
def test_parent_root(self): self.assertEqual(self.qtg.parent(TileCoord(0, 0, 0)), None)
def test_parent(self): tc = TileCoord(3, 3, 5) self.assertEqual(self.ftg.parent(tc), self.qtg.parent(tc))
def test_extent_z0(self): self.assertEqual(self.qtg.extent(TileCoord(0, 0, 0)), (0.0, 1.0, 2.0, 3.0))
def test_children_root(self): tc = TileCoord(0, 0, 0) self.assertEqual(sorted(self.ftg.children(tc)), sorted(self.qtg.children(tc)))
def test_children(self): tc = TileCoord(2, 2, 3) self.assertEqual(sorted(self.ftg.children(tc)), sorted(self.qtg.children(tc)))
def test_extent_metatile_border(self): self.assertEqual(self.ftg.extent(TileCoord(1, 0, 0, 3), 50), (419999.75, 349998.25, 420001.75, 350000.25)) self.assertEqual(self.ftg2.extent(TileCoord(1, 0, 0, 3), 50), (419995, 349918.2, 420081.8, 350005))
def _tilecoord(self, match): return TileCoord(*map(int, match.groups()))
def test_roots(self): self.assertEqual(list(self.qtg.roots()), [TileCoord(0, 0, 0)])
def serve(self, path, params, **kwargs): dimensions = [] metadata = {} if path: if tuple(path[: len(self.static_path)]) == tuple(self.static_path): body, mime = self._get( # pylint: disable=not-callable "/".join(path[len(self.static_path) :]), **kwargs ) if mime is not None: return self.response( body, { "Content-Type": mime, "Expires": ( datetime.datetime.utcnow() + datetime.timedelta(hours=self.expires_hours) ).isoformat(), "Cache-Control": "max-age={}".format((3600 * self.expires_hours)), "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", }, **kwargs, ) else: # pragma: no cover return body elif len(path) >= 1 and path[0] != self.wmts_path: # pragma: no cover return self.error( 404, "Type '{}' don't exists, allows values: '{}' or '{}'".format( path[0], self.wmts_path, "/".join(self.static_path) ), **kwargs, ) path = path[1:] # remove type if path: if len(path) == 2 and path[0] == "1.0.0" and path[1].lower() == "wmtscapabilities.xml": params["SERVICE"] = "WMTS" params["VERSION"] = "1.0.0" params["REQUEST"] = "GetCapabilities" elif len(path) < 7: return self.error(400, "Not enough path", **kwargs) else: params["SERVICE"] = "WMTS" params["VERSION"] = path[0] params["LAYER"] = path[1] params["STYLE"] = path[2] if params["LAYER"] in self.layers: layer = tilegeneration.layers[params["LAYER"]] else: return self.error(400, "Wrong Layer '{}'".format(params["LAYER"]), **kwargs) index = 3 dimensions = path[index : index + len(layer["dimensions"])] for dimension in layer["dimensions"]: metadata["dimension_" + dimension["name"]] = path[index] params[dimension["name"].upper()] = path[index] index += 1 last = path[-1].split(".") if len(path) < index + 4: # pragma: no cover return self.error(400, "Not enough path", **kwargs) params["TILEMATRIXSET"] = path[index] params["TILEMATRIX"] = path[index + 1] params["TILEROW"] = path[index + 2] if len(path) == index + 4: params["REQUEST"] = "GetTile" params["TILECOL"] = last[0] if last[1] != layer["extension"]: # pragma: no cover return self.error(400, "Wrong extension '{}'".format(last[1]), **kwargs) elif len(path) == index + 6: params["REQUEST"] = "GetFeatureInfo" params["TILECOL"] = path[index + 3] params["I"] = path[index + 4] params["J"] = last[0] params["INFO_FORMAT"] = layer.get("info_formats", ["application/vnd.ogc.gml"])[0] else: # pragma: no cover return self.error(400, "Wrong path length", **kwargs) params["FORMAT"] = layer["mime_type"] else: if "SERVICE" not in params or "REQUEST" not in params or "VERSION" not in params: return self.error(400, "Not all required parameters are present", **kwargs) if params["SERVICE"] != "WMTS": return self.error(400, "Wrong Service '{}'".format(params["SERVICE"]), **kwargs) if params["VERSION"] != "1.0.0": return self.error(400, "Wrong Version '{}'".format(params["VERSION"]), **kwargs) if params["REQUEST"] == "GetCapabilities": if "wmtscapabilities_file" in self.cache: wmtscapabilities_file = self.cache["wmtscapabilities_file"] body, mime = self._get(wmtscapabilities_file, **kwargs) # pylint: disable=not-callable else: body = controller.get_wmts_capabilities(tilegeneration, self.cache).encode("utf-8") mime = "application/xml" if mime is not None: return self.response( body, headers={ "Content-Type": "application/xml", "Expires": ( datetime.datetime.utcnow() + datetime.timedelta(hours=self.expires_hours) ).isoformat(), "Cache-Control": "max-age={}".format((3600 * self.expires_hours)), "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", }, **kwargs, ) else: # pragma: no cover return body if ( "FORMAT" not in params or "LAYER" not in params or "TILEMATRIXSET" not in params or "TILEMATRIX" not in params or "TILEROW" not in params or "TILECOL" not in params ): # pragma: no cover return self.error(400, "Not all required parameters are present", **kwargs) if not path: if params["LAYER"] in self.layers: layer = tilegeneration.layers[params["LAYER"]] else: return self.error(400, "Wrong Layer '{}'".format(params["LAYER"]), **kwargs) for dimension in layer["dimensions"]: value = ( params[dimension["name"].upper()] if dimension["name"].upper() in params else dimension["default"] ) dimensions.append(value) metadata["dimension_" + dimension["name"]] = value if params["STYLE"] != layer["wmts_style"]: return self.error(400, "Wrong Style '{}'".format(params["STYLE"]), **kwargs) if params["TILEMATRIXSET"] != layer["grid"]: return self.error(400, "Wrong TileMatrixSet '{}'".format(params["TILEMATRIXSET"]), **kwargs) metadata["layer"] = layer["name"] tile = Tile( TileCoord( # TODO fix for matrix_identifier = resolution int(params["TILEMATRIX"]), int(params["TILECOL"]), int(params["TILEROW"]), ), metadata=metadata, ) if params["REQUEST"] == "GetFeatureInfo": if "I" not in params or "J" not in params or "INFO_FORMAT" not in params: # pragma: no cover return self.error(400, "Not all required parameters are present", **kwargs) if "query_layers" in layer: return self.forward( layer["url"] + "?" + urlencode( { "SERVICE": "WMS", "VERSION": layer["version"], "REQUEST": "GetFeatureInfo", "LAYERS": layer["layers"], "QUERY_LAYERS": layer["query_layers"], "STYLES": params["STYLE"], "FORMAT": params["FORMAT"], "INFO_FORMAT": params["INFO_FORMAT"], "WIDTH": layer["grid_ref"]["tile_size"], "HEIGHT": layer["grid_ref"]["tile_size"], "SRS": layer["grid_ref"]["srs"], "BBOX": layer["grid_ref"]["obj"].extent(tile.tilecoord), "X": params["I"], "Y": params["J"], } ), no_cache=True, **kwargs, ) else: # pragma: no cover return self.error(400, "Layer '{}' not queryable".format(layer["name"]), **kwargs) if params["REQUEST"] != "GetTile": return self.error(400, "Wrong Request '{}'".format(params["REQUEST"]), **kwargs) if params["FORMAT"] != layer["mime_type"]: return self.error(400, "Wrong Format '{}'".format(params["FORMAT"]), **kwargs) if tile.tilecoord.z > self.max_zoom_seed[layer["name"]]: # pragma: no cover return self._map_cache(layer, tile, params, kwargs) if layer["name"] in self.filters: layer_filter = self.filters[layer["name"]] meta_size = layer["meta_size"] meta_tilecoord = ( TileCoord( # TODO fix for matrix_identifier = resolution tile.tilecoord.z, tile.tilecoord.x / meta_size * meta_size, tile.tilecoord.y / meta_size * meta_size, meta_size, ) if meta_size != 1 else tile.tilecoord ) if not layer_filter.filter_tilecoord(meta_tilecoord, layer["name"]): # pragma: no cover return self._map_cache(layer, tile, params, kwargs) store_ref = "/".join([params["LAYER"]] + list(dimensions)) if store_ref in self.stores: # pragma: no cover store = self.stores[store_ref] else: # pragma: no cover return self.error( 400, "No store found for layer '{}' and dimensions {}".format( layer["name"], ", ".join(dimensions) ), **kwargs, ) tile = store.get_one(tile) if tile: if tile.error: return self.error(500, tile.error, **kwargs) return self.response( tile.data, headers={ "Content-Type": tile.content_type, "Expires": ( datetime.datetime.utcnow() + datetime.timedelta(hours=self.expires_hours) ).isoformat(), "Cache-Control": "max-age={}".format((3600 * self.expires_hours)), "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", "Tile-Backend": "Cache", }, **kwargs, ) else: return self.error(204, **kwargs)
def test_root_parents(self): for root_z in set(root.z for root in self.ftg.roots()): self.assertEquals(self.ftg.parent(TileCoord(root_z, 0, 0)), None)
def test_empty(self) -> None: tile = Tile(TileCoord(0, 0, 0)) self.assertEqual(tile.content_type, None) self.assertEqual(tile.content_encoding, None) self.assertEqual(tile.data, None) self.assertEqual(tile.error, None)
def test_root_zero(self): self.assertEquals(self.ftg.parent(TileCoord(0, 0, 0)), None)
def test_extent_border(self) -> None: assert self.ftg.extent(TileCoord(1, 4, 6), 5) == (427900, 335900, 430100, 338100)
def test_extent_border(self): self.assertEqual(self.ftg.extent(TileCoord(1, 4, 6), 5), (427900, 335900, 430100, 338100))
def test_extent_metatile_border(self) -> None: assert self.ftg.extent(TileCoord(1, 4, 6, 2), 5) == (427900, 333900, 432100, 338100)
def test_extent_metatile(self): self.assertEqual(self.ftg.extent(TileCoord(1, 4, 6, 2)), (428000, 334000, 432000, 338000))
def test_extent(self) -> None: assert self.ftg.extent(TileCoord(0, 0, 0)) == (420000.0, 349000.0, 421000.0, 350000.0) assert self.ftg.extent(TileCoord(2, 0, 0)) == (420000.0, 349750.0, 420250.0, 350000.0)
def test_extent_metatile_border(self): self.assertEqual(self.ftg.extent(TileCoord(1, 4, 6, 2), 5), (427900, 333900, 432100, 338100))
def test_children(self) -> None: tc = TileCoord(2, 2, 3) assert sorted(self.ftg.children(tc)) == sorted(self.qtg.children(tc))
def serve(self, path, params, **kwargs): dimensions = [] metadata = {} if path: if len(path) >= 1 and path[0] == self.static_path: body, mime = self._get('/'.join(path[1:]), **kwargs) if mime is not None: return self.response( body, { 'Content-Type': mime, 'Expires': (datetime.datetime.utcnow() + datetime.timedelta( hours=self.expires_hours)).isoformat(), 'Cache-Control': "max-age={}".format((3600 * self.expires_hours)), 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET', }, **kwargs) else: # pragma: no cover return body elif len(path ) >= 1 and path[0] != self.wmts_path: # pragma: no cover return self.error( 404, "Type '{}' don't exists, allows values: '{}' or '{}'". format(path[0], self.wmts_path, self.static_path), **kwargs) path = path[1:] # remove type if len(path) == 2 and path[0] == '1.0.0' and path[1].lower( ) == 'wmtscapabilities.xml': params['SERVICE'] = 'WMTS' params['VERSION'] = '1.0.0' params['REQUEST'] = 'GetCapabilities' elif len(path) < 7: return self.error(400, "Not enough path", **kwargs) else: params['SERVICE'] = 'WMTS' params['VERSION'] = path[0] params['LAYER'] = path[1] params['STYLE'] = path[2] if params['LAYER'] in self.layers: layer = self.tilegeneration.layers[params['LAYER']] else: return self.error( 400, "Wrong Layer '{}'".format(params['LAYER']), **kwargs) index = 3 dimensions = path[index:index + len(layer['dimensions'])] for dimension in layer['dimensions']: metadata["dimension_" + dimension['name']] = path[index] params[dimension['name'].upper()] = path[index] index += 1 last = path[-1].split('.') if len(path) < index + 4: # pragma: no cover return self.error(400, "Not enough path", **kwargs) params['TILEMATRIXSET'] = path[index] params['TILEMATRIX'] = path[index + 1] params['TILEROW'] = path[index + 2] if len(path) == index + 4: params['REQUEST'] = 'GetTile' params['TILECOL'] = last[0] if last[1] != layer['extension']: # pragma: no cover return self.error( 400, "Wrong extension '{}'".format(last[1]), **kwargs) elif len(path) == index + 6: params['REQUEST'] = 'GetFeatureInfo' params['TILECOL'] = path[index + 3] params['I'] = path[index + 4] params['J'] = last[0] params['INFO_FORMAT'] = layer.get( 'info_formats', ['application/vnd.ogc.gml'])[0] else: # pragma: no cover return self.error(400, "Wrong path length", **kwargs) params['FORMAT'] = layer['mime_type'] else: if \ 'SERVICE' not in params or \ 'REQUEST' not in params or \ 'VERSION' not in params: return self.error(400, "Not all required parameters are present", **kwargs) if params['SERVICE'] != 'WMTS': return self.error(400, "Wrong Service '{}'".format(params['SERVICE']), **kwargs) if params['VERSION'] != '1.0.0': return self.error(400, "Wrong Version '{}'".format(params['VERSION']), **kwargs) if params['REQUEST'] == 'GetCapabilities': if 'wmtscapabilities_file' in self.cache: wmtscapabilities_file = self.cache['wmtscapabilities_file'] body, mime = self._get(wmtscapabilities_file, **kwargs) else: body = controller.get_wmts_capabilities( self.tilegeneration, self.cache).encode('utf-8') mime = "application/xml" if mime is not None: return self.response( body, headers={ 'Content-Type': "application/xml", 'Expires': (datetime.datetime.utcnow() + datetime.timedelta( hours=self.expires_hours)).isoformat(), 'Cache-Control': "max-age={}".format((3600 * self.expires_hours)), 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET', }, **kwargs) else: # pragma: no cover return body if \ 'FORMAT' not in params or \ 'LAYER' not in params or \ 'TILEMATRIXSET' not in params or \ 'TILEMATRIX' not in params or \ 'TILEROW' not in params or \ 'TILECOL' not in params: # pragma: no cover return self.error(400, "Not all required parameters are present", **kwargs) if not path: if params['LAYER'] in self.layers: layer = self.tilegeneration.layers[params['LAYER']] else: return self.error(400, "Wrong Layer '{}'".format(params['LAYER']), **kwargs) for dimension in layer['dimensions']: value = params[dimension['name'].upper()] \ if dimension['name'].upper() in params \ else dimension['default'] dimensions.append(value) metadata["dimension_" + dimension['name']] = value if params['STYLE'] != layer['wmts_style']: return self.error(400, "Wrong Style '{}'".format(params['STYLE']), **kwargs) if params['TILEMATRIXSET'] != layer['grid']: return self.error( 400, "Wrong TileMatrixSet '{}'".format(params['TILEMATRIXSET']), **kwargs) tile = Tile( TileCoord( # TODO fix for matrix_identifier = resolution int(params['TILEMATRIX']), int(params['TILECOL']), int(params['TILEROW']), ), metadata=metadata) if params['REQUEST'] == 'GetFeatureInfo': if \ 'I' not in params or \ 'J' not in params or \ 'INFO_FORMAT' not in params: # pragma: no cover return self.error(400, "Not all required parameters are present", **kwargs) if 'query_layers' in layer: return self.forward(layer['url'] + '?' + urlencode( { 'SERVICE': 'WMS', 'VERSION': layer['version'], 'REQUEST': 'GetFeatureInfo', 'LAYERS': layer['layers'], 'QUERY_LAYERS': layer['query_layers'], 'STYLES': params['STYLE'], 'FORMAT': params['FORMAT'], 'INFO_FORMAT': params['INFO_FORMAT'], 'WIDTH': layer['grid_ref']['tile_size'], 'HEIGHT': layer['grid_ref']['tile_size'], 'SRS': layer['grid_ref']['srs'], 'BBOX': layer['grid_ref']['obj'].extent( tile.tilecoord), 'X': params['I'], 'Y': params['J'], }), no_cache=True, **kwargs) else: # pragma: no cover return self.error( 400, "Layer '{}' not queryable".format(layer['name']), **kwargs) if params['REQUEST'] != 'GetTile': return self.error(400, "Wrong Request '{}'".format(params['REQUEST']), **kwargs) if params['FORMAT'] != layer['mime_type']: return self.error(400, "Wrong Format '{}'".format(params['FORMAT']), **kwargs) if tile.tilecoord.z > self.max_zoom_seed[ layer['name']]: # pragma: no cover return self.forward(self.mapcache_baseurl + '?' + urlencode(params), headers=self.mapcache_header, **kwargs) if layer['name'] in self.filters: layer_filter = self.filters[layer['name']] meta_size = layer['meta_size'] meta_tilecoord = TileCoord( # TODO fix for matrix_identifier = resolution tile.tilecoord.z, tile.tilecoord.x / meta_size * meta_size, tile.tilecoord.y / meta_size * meta_size, meta_size, ) if meta_size != 1 else tile.tilecoord if not layer_filter.filter_tilecoord( meta_tilecoord): # pragma: no cover return self.forward(self.mapcache_baseurl + '?' + urlencode(params), headers=self.mapcache_header, **kwargs) store_ref = '/'.join([params['LAYER']] + list(dimensions)) if store_ref in self.stores: # pragma: no cover store = self.stores[store_ref] else: # pragma: no cover return self.error( 400, "No store found for layer '{}' and dimensions {}".format( layer['name'], ', '.join(dimensions)), **kwargs) tile = store.get_one(tile) if tile: if tile.error: return self.error(500, tile.error, **kwargs) return self.response( tile.data, headers={ 'Content-Type': tile.content_type, 'Expires': (datetime.datetime.utcnow() + datetime.timedelta(hours=self.expires_hours)).isoformat(), 'Cache-Control': "max-age={}".format((3600 * self.expires_hours)), 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET', }, **kwargs) else: return self.error(204, **kwargs)
def test_parent(self) -> None: tc = TileCoord(3, 3, 5) assert self.ftg.parent(tc) == self.qtg.parent(tc)
def test_extent(self): self.assertEqual(self.ftg.extent(TileCoord(1, 4, 6)), (428000, 336000, 430000, 338000)) self.assertEqual(self.ftg.extent(TileCoord(1, 5, 7)), (430000, 334000, 432000, 336000))