def get_web_optimized_params( src_dst, zoom_level_strategy: str = "auto", zoom_level: Optional[int] = None, aligned_levels: Optional[int] = None, tms: morecantile.TileMatrixSet = morecantile.tms.get("WebMercatorQuad"), ) -> Dict: """Return VRT parameters for a WebOptimized COG.""" if src_dst.crs != tms.rasterio_crs: with WarpedVRT(src_dst, crs=tms.rasterio_crs) as vrt: bounds = vrt.bounds aff = list(vrt.transform) else: bounds = src_dst.bounds aff = list(src_dst.transform) resolution = max(abs(aff[0]), abs(aff[4])) if zoom_level is None: # find max zoom (closest to the raster resolution) max_zoom = tms.zoom_for_res( resolution, max_z=30, zoom_level_strategy=zoom_level_strategy, ) else: max_zoom = zoom_level # defined the zoom level we want to align the raster aligned_levels = aligned_levels or 0 base_zoom = max_zoom - aligned_levels # find new raster bounds (bounds of UL tile / LR tile) ul_tile = tms._tile(bounds[0], bounds[3], base_zoom) w, _, _, n = tms.xy_bounds(ul_tile) # The output resolution should match the TMS resolution at MaxZoom vrt_res = tms._resolution(tms.matrix(max_zoom)) # Output transform is built from the origin (UL tile) and output resolution vrt_transform = Affine(vrt_res, 0, w, 0, -vrt_res, n) lr_tile = tms._tile(bounds[2], bounds[1], base_zoom) e, _, _, s = tms.xy_bounds( morecantile.Tile(lr_tile.x + 1, lr_tile.y + 1, lr_tile.z) ) vrt_width = max(1, round((e - w) / vrt_transform.a)) vrt_height = max(1, round((s - n) / vrt_transform.e)) return dict( crs=tms.rasterio_crs, transform=vrt_transform, width=vrt_width, height=vrt_height, )
def get_web_optimized_params( src_dst, tilesize=256, warp_resampling: str = "nearest", zoom_level_strategy: str = "auto", aligned_levels: Optional[int] = None, tms: morecantile.TileMatrixSet = morecantile.tms.get("WebMercatorQuad"), ) -> Dict: """Return VRT parameters for a WebOptimized COG.""" bounds = list( transform_bounds(src_dst.crs, CRS.from_epsg(4326), *src_dst.bounds, densify_pts=21)) min_zoom, max_zoom = get_zooms( src_dst, tilesize=tilesize, tms=tms, zoom_level_strategy=zoom_level_strategy, ) if aligned_levels is not None: min_zoom = max_zoom - aligned_levels ul_tile = tms.tile(bounds[0], bounds[3], min_zoom) left, _, _, top = tms.xy_bounds(ul_tile.x, ul_tile.y, ul_tile.z) vrt_res = tms._resolution(tms.matrix(max_zoom)) vrt_transform = Affine(vrt_res, 0, left, 0, -vrt_res, top) lr_tile = tms.tile(bounds[2], bounds[1], min_zoom) extrema = { "x": { "min": ul_tile.x, "max": lr_tile.x + 1 }, "y": { "min": ul_tile.y, "max": lr_tile.y + 1 }, } vrt_width = ((extrema["x"]["max"] - extrema["x"]["min"]) * tilesize * 2**(max_zoom - min_zoom)) vrt_height = ((extrema["y"]["max"] - extrema["y"]["min"]) * tilesize * 2**(max_zoom - min_zoom)) return dict( crs=tms.crs, transform=vrt_transform, width=vrt_width, height=vrt_height, resampling=ResamplingEnums[warp_resampling], )
def wmts( request: Request, tms: TileMatrixSet = Depends(self.tms_dependency), src_path=Depends(self.path_dependency), tile_format: ImageType = Query( ImageType.png, description="Output image type. Default is png." ), tile_scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..." ), minzoom: Optional[int] = Query( None, description="Overwrite default minzoom." ), maxzoom: Optional[int] = Query( None, description="Overwrite default maxzoom." ), layer_params=Depends(self.layer_dependency), # noqa dataset_params=Depends(self.dataset_dependency), # noqa render_params=Depends(self.render_dependency), # noqa kwargs: Dict = Depends(self.additional_dependency), # noqa ): """OGC WMTS endpoint.""" route_params = { "z": "{TileMatrix}", "x": "{TileCol}", "y": "{TileRow}", "scale": tile_scale, "format": tile_format.value, "TileMatrixSetId": tms.identifier, } tiles_url = self.url_for(request, "tile", **route_params) q = dict(request.query_params) q.pop("TileMatrixSetId", None) q.pop("tile_format", None) q.pop("tile_scale", None) q.pop("minzoom", None) q.pop("maxzoom", None) q.pop("SERVICE", None) q.pop("REQUEST", None) qs = urlencode(list(q.items())) tiles_url += f"?{qs}" with rasterio.Env(**self.gdal_config): with self.reader( src_path.url, tms=tms, **self.reader_options ) as src_dst: bounds = src_dst.bounds minzoom = minzoom if minzoom is not None else src_dst.minzoom maxzoom = maxzoom if maxzoom is not None else src_dst.maxzoom tileMatrix = [] for zoom in range(minzoom, maxzoom + 1): matrix = tms.matrix(zoom) tm = f""" <TileMatrix> <ows:Identifier>{matrix.identifier}</ows:Identifier> <ScaleDenominator>{matrix.scaleDenominator}</ScaleDenominator> <TopLeftCorner>{matrix.topLeftCorner[0]} {matrix.topLeftCorner[1]}</TopLeftCorner> <TileWidth>{matrix.tileWidth}</TileWidth> <TileHeight>{matrix.tileHeight}</TileHeight> <MatrixWidth>{matrix.matrixWidth}</MatrixWidth> <MatrixHeight>{matrix.matrixHeight}</MatrixHeight> </TileMatrix>""" tileMatrix.append(tm) return templates.TemplateResponse( "wmts.xml", { "request": request, "tiles_endpoint": tiles_url, "bounds": bounds, "tileMatrix": tileMatrix, "tms": tms, "title": "Cloud Optimized GeoTIFF", "layer_name": "cogeo", "media_type": tile_format.mimetype, }, media_type=MimeTypes.xml.value, )
def ogc_wmts( endpoint: str, tms: morecantile.TileMatrixSet, bounds: List[float] = [-180.0, -90.0, 180.0, 90.0], minzoom: int = 0, maxzoom: int = 24, query_string: str = "", title: str = "Cloud Optimizied GeoTIFF", ) -> str: """ Create WMTS XML template. Attributes ---------- endpoint : str, required tiler endpoint. tms : morecantile.TileMatrixSet Custom Tile Matrix Set. bounds : tuple, optional WGS84 layer bounds (default: [-180.0, -90.0, 180.0, 90.0]). query_string : str, optional Endpoint querystring. minzoom : int, optional (default: 0) min zoom. maxzoom : int, optional (default: 25) max zoom. title: str, optional (default: "Cloud Optimizied GeoTIFF") Layer title. Returns ------- xml : str OGC Web Map Tile Service (WMTS) XML template. """ content_type = "image/png" layer = tms.identifier tileMatrixArray = [] for zoom in range(minzoom, maxzoom + 1): matrix = tms.matrix(zoom) tm = f""" <TileMatrix> <ows:Identifier>{matrix.identifier}</ows:Identifier> <ScaleDenominator>{matrix.scaleDenominator}</ScaleDenominator> <TopLeftCorner>{matrix.topLeftCorner[0]} {matrix.topLeftCorner[1]}</TopLeftCorner> <TileWidth>{matrix.tileWidth}</TileWidth> <TileHeight>{matrix.tileHeight}</TileHeight> <MatrixWidth>{matrix.matrixWidth}</MatrixWidth> <MatrixHeight>{matrix.matrixHeight}</MatrixHeight> </TileMatrix>""" tileMatrixArray.append(tm) tileMatrix = "\n".join(tileMatrixArray) xml = f"""<Capabilities xmlns="http://www.opengis.net/wmts/1.0" xmlns:ows="http://www.opengis.net/ows/1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:gml="http://www.opengis.net/gml" xsi:schemaLocation="http://www.opengis.net/wmts/1.0 http://schemas.opengis.net/wmts/1.0/wmtsGetCapabilities_response.xsd" version="1.0.0"> <ows:ServiceIdentification> <ows:Title>{title}</ows:Title> <ows:ServiceType>OGC WMTS</ows:ServiceType> <ows:ServiceTypeVersion>1.0.0</ows:ServiceTypeVersion> </ows:ServiceIdentification> <ows:OperationsMetadata> <ows:Operation name="GetCapabilities"> <ows:DCP> <ows:HTTP> <ows:Get xlink:href="{endpoint}/{layer}/wmts?{query_string}"> <ows:Constraint name="GetEncoding"> <ows:AllowedValues> <ows:Value>RESTful</ows:Value> </ows:AllowedValues> </ows:Constraint> </ows:Get> </ows:HTTP> </ows:DCP> </ows:Operation> <ows:Operation name="GetTile"> <ows:DCP> <ows:HTTP> <ows:Get xlink:href="{endpoint}/{layer}/wmts?{query_string}"> <ows:Constraint name="GetEncoding"> <ows:AllowedValues> <ows:Value>RESTful</ows:Value> </ows:AllowedValues> </ows:Constraint> </ows:Get> </ows:HTTP> </ows:DCP> </ows:Operation> </ows:OperationsMetadata> <Contents> <Layer> <ows:Identifier>{layer}</ows:Identifier> <ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84"> <ows:LowerCorner>{bounds[0]} {bounds[1]}</ows:LowerCorner> <ows:UpperCorner>{bounds[2]} {bounds[3]}</ows:UpperCorner> </ows:WGS84BoundingBox> <Style isDefault="true"> <ows:Identifier>default</ows:Identifier> </Style> <Format>{content_type}</Format> <TileMatrixSetLink> <TileMatrixSet>{layer}</TileMatrixSet> </TileMatrixSetLink> <ResourceURL format="{content_type}" resourceType="tile" template="{endpoint}/tiles/{layer}/{{TileMatrix}}/{{TileCol}}/{{TileRow}}.png?{query_string}"/> </Layer> <TileMatrixSet> <ows:Identifier>{layer}</ows:Identifier> <ows:SupportedCRS>EPSG:{tms.crs.to_epsg()}</ows:SupportedCRS> {tileMatrix} </TileMatrixSet> </Contents> <ServiceMetadataURL xlink:href='{endpoint}/{layer}/wmts?{query_string}'/> </Capabilities>""" return xml
def wmts( request: Request, tms: TileMatrixSet = Depends(self.tms_dependency), src_path=Depends(self.path_dependency), tile_format: ImageType = Query( ImageType.png, description="Output image type. Default is png."), tile_scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."), minzoom: Optional[int] = Query( None, description="Overwrite default minzoom."), maxzoom: Optional[int] = Query( None, description="Overwrite default maxzoom."), layer_params=Depends(self.layer_dependency), # noqa dataset_params=Depends(self.dataset_dependency), # noqa render_params=Depends(self.render_dependency), # noqa colormap=Depends(self.colormap_dependency), # noqa pixel_selection: PixelSelectionMethod = Query( PixelSelectionMethod.first, description="Pixel selection method."), # noqa kwargs: Dict = Depends(self.additional_dependency), # noqa ): """OGC WMTS endpoint.""" route_params = { "z": "{TileMatrix}", "x": "{TileCol}", "y": "{TileRow}", "scale": tile_scale, "format": tile_format.value, "TileMatrixSetId": tms.identifier, } tiles_url = self.url_for(request, "tile", **route_params) qs_key_to_remove = [ "tilematrixsetid", "tile_format", "tile_scale", "minzoom", "maxzoom", "service", "request", ] qs = [(key, value) for (key, value) in request.query_params._list if key.lower() not in qs_key_to_remove] if qs: tiles_url += f"?{urlencode(qs)}" with self.reader(src_path, **self.backend_options) as src_dst: bounds = src_dst.bounds minzoom = minzoom if minzoom is not None else src_dst.minzoom maxzoom = maxzoom if maxzoom is not None else src_dst.maxzoom tileMatrix = [] for zoom in range(minzoom, maxzoom + 1): matrix = tms.matrix(zoom) tm = f""" <TileMatrix> <ows:Identifier>{matrix.identifier}</ows:Identifier> <ScaleDenominator>{matrix.scaleDenominator}</ScaleDenominator> <TopLeftCorner>{matrix.topLeftCorner[0]} {matrix.topLeftCorner[1]}</TopLeftCorner> <TileWidth>{matrix.tileWidth}</TileWidth> <TileHeight>{matrix.tileHeight}</TileHeight> <MatrixWidth>{matrix.matrixWidth}</MatrixWidth> <MatrixHeight>{matrix.matrixHeight}</MatrixHeight> </TileMatrix>""" tileMatrix.append(tm) return templates.TemplateResponse( "wmts.xml", { "request": request, "tiles_endpoint": tiles_url, "bounds": bounds, "tileMatrix": tileMatrix, "tms": tms, "title": "Cloud Optimized GeoTIFF", "layer_name": "cogeo", "media_type": tile_format.mediatype, }, media_type=MediaType.xml.value, )