def _info(mosaicid: str = None, url: str = None) -> Tuple[str, str, str]: """Handle /info requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") mosaic_def = fetch_mosaic_definition(url) bounds = mosaic_def["bounds"] center = [ (bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, mosaic_def["minzoom"], ] quadkeys = list(mosaic_def["tiles"].keys()) # read layernames from the first file src_path = mosaic_def["tiles"][quadkeys[0]][0] with rasterio.open(src_path) as src_dst: layer_names = _get_layer_names(src_dst) dtype = src_dst.dtypes[0] meta = { "bounds": bounds, "center": center, "maxzoom": mosaic_def["maxzoom"], "minzoom": mosaic_def["minzoom"], "name": mosaicid if mosaicid else url, "quadkeys": quadkeys, "layers": layer_names, "dtype": dtype, } return ("OK", "application/json", json.dumps(meta))
def _point(mosaicid: str = None, lng: float = None, lat: float = None, url: str = None) -> Tuple[str, str, str]: """Handle point requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") if not lat or not lng: return ("NOK", "text/plain", "Missing 'Lon/Lat' parameter") if isinstance(lng, str): lng = float(lng) if isinstance(lat, str): lat = float(lat) assets = fetch_and_find_assets_point(url, lng, lat) if not assets: return ("EMPTY", "text/plain", f"No assets found for lat/lng ({lat}, {lng})") with rasterio.Env(aws_session): meta = { "coordinates": [lng, lat], "values": get_point_values(assets, lng, lat) } return ("OK", "application/json", json.dumps(meta))
def _mvt( mosaicid: str = None, z: int = None, x: int = None, y: int = None, url: str = None, tile_size: Union[str, int] = 256, pixel_selection: str = "first", feature_type: str = "point", resampling_method: str = "nearest", ) -> Tuple[str, str, BinaryIO]: """Handle MVT requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") assets = fetch_and_find_assets(url, x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") if tile_size is not None and isinstance(tile_size, str): tile_size = int(tile_size) if pixel_selection == "last": pixel_selection = "first" assets = list(reversed(assets)) with rasterio.Env(aws_session): pixsel_method = PIXSEL_METHODS[pixel_selection] tile, mask = mosaic_tiler( assets, x, y, z, cogeoTiler, tilesize=tile_size, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") with rasterio.open(assets[0]) as src_dst: band_descriptions = _get_layer_names(src_dst) return ( "OK", "application/x-protobuf", mvtEncoder( tile, mask, band_descriptions, os.path.basename(url), feature_type=feature_type, ), )
def _tilejson( mosaicid: str = None, url: str = None, tile_scale: int = 1, tile_format: str = None, **kwargs: Any, ) -> Tuple[str, str, str]: """Handle /tilejson.json requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") mosaic_def = fetch_mosaic_definition(url) bounds = mosaic_def["bounds"] center = [ (bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, mosaic_def["minzoom"], ] if not mosaicid: kwargs.update(dict(url=url)) host = app.host else: host = f"{app.host}/{mosaicid}" if tile_format in ["pbf", "mvt"]: tile_url = f"{host}/{{z}}/{{x}}/{{y}}.{tile_format}" elif tile_format in ["png", "jpg", "webp", "tif", "npy"]: tile_url = f"{host}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}" else: tile_url = f"{host}/{{z}}/{{x}}/{{y}}@{tile_scale}x" qs = urllib.parse.urlencode(list(kwargs.items())) if qs: tile_url += f"?{qs}" meta = { "bounds": bounds, "center": center, "maxzoom": mosaic_def["maxzoom"], "minzoom": mosaic_def["minzoom"], "name": url, "tilejson": "2.1.0", "tiles": [tile_url], } return ("OK", "application/json", json.dumps(meta))
def _geojson(mosaicid: str = None, url: str = None) -> Tuple[str, str, str]: """Handle /geojson requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") mosaic_def = fetch_mosaic_definition(url) geojson = { "type": "FeatureCollection", "features": [ mercantile.feature(mercantile.quadkey_to_tile(qk), props=dict(files=files)) for qk, files in mosaic_def["tiles"].items() ], } return ("OK", "application/json", json.dumps(geojson))
def _wmts( mosaicid: str = None, url: str = None, tile_format: str = "png", tile_scale: int = 1, title: str = "Cloud Optimizied GeoTIFF Mosaic", **kwargs: Any, ) -> Tuple[str, str, str]: """Handle /wmts requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") if tile_scale is not None and isinstance(tile_scale, str): tile_scale = int(tile_scale) mosaic_def = fetch_mosaic_definition(url) kwargs.pop("SERVICE", None) kwargs.pop("REQUEST", None) kwargs.update(dict(url=url)) query_string = urllib.parse.urlencode(list(kwargs.items())) query_string = query_string.replace( "&", "&") # & is an invalid character in XML return ( "OK", "application/xml", wmts_template( f"{app.host}", query_string, minzoom=mosaic_def["minzoom"], maxzoom=mosaic_def["maxzoom"], bounds=mosaic_def["bounds"], tile_scale=tile_scale, tile_format=tile_format, title=title, ), )
def _create( body: str, minzoom: Union[str, int] = None, maxzoom: Union[str, int] = None, min_tile_cover: Union[str, float] = None, tile_cover_sort: Union[str, bool] = False, tile_format: str = None, tile_scale: Union[str, int] = 1, **kwargs: Any, ) -> Tuple[str, str, str]: minzoom = int(minzoom) if isinstance(minzoom, str) else minzoom maxzoom = int(maxzoom) if isinstance(maxzoom, str) else maxzoom min_tile_cover = (float(min_tile_cover) if isinstance( min_tile_cover, float) else min_tile_cover) mosaicid = get_hash(body=body, version=mosaic_version) try: mosaic_definition = fetch_mosaic_definition(_create_path(mosaicid)) except ClientError: body = json.loads(body) with rasterio.Env(aws_session): mosaic_definition = create_mosaic( body, minzoom=minzoom, maxzoom=maxzoom, minimum_tile_cover=min_tile_cover, tile_cover_sort=tile_cover_sort, ) key = f"mosaics/{mosaicid}.json.gz" bucket = os.environ["MOSAIC_DEF_BUCKET"] _aws_put_data(key, bucket, _compress_gz_json(mosaic_definition), client=s3_client) if tile_format in ["pbf", "mvt"]: tile_url = f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}.{tile_format}" elif tile_format in ["png", "jpg", "webp", "tif", "npy"]: tile_url = ( f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}" ) else: tile_url = f"{app.host}/{mosaicid}/{{z}}/{{x}}/{{y}}@{tile_scale}x" qs = urllib.parse.urlencode(list(kwargs.items())) if qs: tile_url += f"?{qs}" meta = { "bounds": mosaic_definition["bounds"], "center": mosaic_definition["center"], "maxzoom": mosaic_definition["maxzoom"], "minzoom": mosaic_definition["minzoom"], "name": mosaicid, "tilejson": "2.1.0", "tiles": [tile_url], } return ("OK", "application/json", json.dumps(meta))
def _img( mosaicid: str = None, z: int = None, x: int = None, y: int = None, scale: int = 1, ext: str = None, url: str = None, indexes: str = None, rescale: str = None, color_ops: str = None, color_map: str = None, pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" if mosaicid: url = _create_path(mosaicid) elif url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") assets = fetch_and_find_assets(url, x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") if indexes: indexes = list(map(int, indexes.split(","))) tilesize = 256 * scale if pixel_selection == "last": pixel_selection = "first" assets = list(reversed(assets)) with rasterio.Env(aws_session): pixsel_method = PIXSEL_METHODS[pixel_selection] tile, mask = mosaic_tiler( assets, x, y, z, cogeoTiler, indexes=indexes, tilesize=tilesize, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") rtile = _postprocess(tile, mask, rescale=rescale, color_formula=color_ops) if color_map: if color_map.startswith("custom_"): color_map = get_custom_cmap(color_map) else: color_map = get_colormap(color_map, format="gdal") if not ext: ext = "jpg" if mask.all() else "png" driver = "jpeg" if ext == "jpg" else ext options = img_profiles.get(driver, {}) if ext == "tif": ext = "tiff" driver = "GTiff" tile_bounds = mercantile.xy_bounds(mercantile.Tile(x=x, y=y, z=z)) options = dict( crs={"init": "EPSG:3857"}, transform=from_bounds(*tile_bounds, tilesize, tilesize), ) return ( "OK", f"image/{ext}", array_to_image(rtile, mask, img_format=driver, color_map=color_map, **options), )