def test_dynamoDB_backend(client): """Test DynamoDB backend.""" client.return_value.Table = MockTable with MosaicBackend("dynamodb:///thiswaskylebarronidea:mosaic") as mosaic: assert mosaic._backend_name == "AWS DynamoDB" assert isinstance(mosaic, DynamoDBBackend) assert mosaic.quadkey_zoom == 7 assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [ "mosaicjson", "version", "minzoom", "maxzoom", "quadkey_zoom", "bounds", "center", ] assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"] assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"] info = mosaic.info() assert not info["quadkeys"] assert list(info.dict()) == [ "bounds", "center", "minzoom", "maxzoom", "name", "quadkeys", ] info = mosaic.info(quadkeys=True) assert info["quadkeys"] with MosaicBackend("dynamodb:///thiswaskylebarronidea:mosaic2", mosaic_def=mosaic_content) as mosaic: assert isinstance(mosaic, DynamoDBBackend) items = mosaic._create_items() assert len(items) == 10 assert items[-1] == { "mosaicId": "mosaic2", "quadkey": "0302330", "assets": ["cog1.tif", "cog2.tif"], } mosaic._create_table() with MosaicBackend("dynamodb:///thiswaskylebarronidea:mosaic2", mosaic_def=mosaic_content) as mosaic: items = mosaic._create_items() assert len(items) == len(mosaic.mosaic_def.tiles.items()) + 1 assert "quadkey" in list(items[0]) assert "mosaicId" in list(items[0]) assert "bounds" in list(items[0]) assert "center" in list(items[0])
def test_http_backend(requests): """Test HTTP backend.""" with open(mosaic_json, "r") as f: requests.get.return_value = MockResponse(f.read()) requests.exceptions.HTTPError = HTTPError requests.exceptions.RequestException = RequestException with MosaicBackend("https://mymosaic.json") as mosaic: assert mosaic._backend_name == "HTTP" assert isinstance(mosaic, HttpBackend) assert (mosaic.mosaicid == "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075") assert mosaic.quadkey_zoom == 7 assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [ "mosaicjson", "version", "minzoom", "maxzoom", "quadkey_zoom", "bounds", "center", ] assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"] assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"] requests.get.assert_called_once() requests.mock_reset() with open(mosaic_json, "r") as f: requests.get.return_value = MockResponse(f.read()) with pytest.raises(NotImplementedError): with MosaicBackend("https://mymosaic.json") as mosaic: mosaic.update() requests.get.assert_called_once() requests.mock_reset() with pytest.raises(NotImplementedError): with MosaicBackend("https://mymosaic.json", mosaic_def=mosaic_content) as mosaic: mosaic.write() requests.get.assert_not_called() requests.mock_reset() with open(mosaic_gz, "rb") as f: requests.get.return_value = MockResponse(f.read()) with MosaicBackend("https://mymosaic.json.gz") as mosaic: assert isinstance(mosaic, HttpBackend) assert (mosaic.mosaicid == "24d43802c19ef67cc498c327b62514ecf70c2bbb1bbc243dda1ee075")
def _point( mosaicid: str = None, lng: float = None, lat: float = None, url: str = None ) -> Tuple[str, str, str]: """Handle point requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or 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) mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.point(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": [ {"asset": assets[ix], "values": value} for ix, value in enumerate(multi_point(assets, coordinates=(lng, lat))) ], } return ("OK", "application/json", json.dumps(meta, separators=(",", ":")))
def info(): """return info.""" with MosaicBackend(self.src_path) as mosaic: info = mosaic.info(quadkeys=True).dict(exclude_none=True) bounds = info.pop("bounds", None) info.pop("center", None) return bbox_to_feature(bounds, properties=info)
def create_from_features( features, output, minzoom, maxzoom, property, quadkey_zoom, min_tile_cover, tile_cover_sort, quiet, ): """Create mosaic definition file.""" mosaicjson = MosaicJSON.from_features( list(features), minzoom, maxzoom, quadkey_zoom=quadkey_zoom, accessor=lambda feature: feature["properties"][property], minimum_tile_cover=min_tile_cover, tile_cover_sort=tile_cover_sort, quiet=quiet, ) if output: with MosaicBackend(output, mosaic_def=mosaicjson) as mosaic: mosaic.write() else: click.echo(json.dumps(mosaicjson.dict(exclude_none=True)))
def shapes(qk: str = Query(...)): """return info.""" x, y, z = mercantile.quadkey_to_tile(qk) with MosaicBackend(self.src_path) as mosaic: assets = mosaic.assets_for_tile(x, y, z) features = get_footprints(assets, max_threads=MAX_THREADS) return FeatureCollection(features=features)
def _info(mosaicid: str = None, url: str = None) -> Tuple: """Handle /info requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: meta = mosaic.metadata response = { "bounds": meta["bounds"], "center": meta["center"], "maxzoom": meta["maxzoom"], "minzoom": meta["minzoom"], "name": mosaicid or url, } if not mosaic_path.startswith("dynamodb://"): mosaic_quadkeys = set(mosaic._quadkeys) tile = mercantile.quadkey_to_tile(random.sample(mosaic_quadkeys, 1)[0]) assets = mosaic.tile(*tile) with rasterio.open(assets[0]) as src_dst: layer_names = _get_layer_names(src_dst) dtype = src_dst.dtypes[0] response["quadkeys"] = list(mosaic_quadkeys) response["layers"] = layer_names response["dtype"] = dtype else: warnings.warn( "Cannot retrieve 'quadkeys,layers and dtype' from dynamoDB mosaic." ) return ("OK", "application/json", json.dumps(response, separators=(",", ":")))
def test_dynamoDB_backend(client): """Test DynamoDB backend.""" client.return_value.Table = MockTable with MosaicBackend("dynamodb:///thiswaskylebarronidea:mosaic") as mosaic: assert mosaic._backend_name == "AWS DynamoDB" assert isinstance(mosaic, DynamoDBBackend) assert mosaic.quadkey_zoom == 7 assert list(mosaic.metadata.dict(exclude_none=True).keys()) == [ "mosaicjson", "version", "minzoom", "maxzoom", "quadkey_zoom", "bounds", "center", ] assert mosaic.assets_for_tile(150, 182, 9) == ["cog1.tif", "cog2.tif"] assert mosaic.assets_for_point(-73, 45) == ["cog1.tif", "cog2.tif"] info = mosaic.info() assert not info["quadkeys"] assert list(info.dict()) == [ "bounds", "center", "minzoom", "maxzoom", "name", "quadkeys", ] info = mosaic.info(quadkeys=True) assert info["quadkeys"]
def create( input_files, output, minzoom, maxzoom, quadkey_zoom, min_tile_cover, tile_cover_sort, threads, quiet, ): """Create mosaic definition file.""" input_files = input_files.read().splitlines() mosaicjson = MosaicJSON.from_urls( input_files, minzoom=minzoom, maxzoom=maxzoom, quadkey_zoom=quadkey_zoom, minimum_tile_cover=min_tile_cover, tile_cover_sort=tile_cover_sort, max_threads=threads, quiet=quiet, ) if output: with MosaicBackend(output, mosaic_def=mosaicjson) as mosaic: mosaic.write() else: click.echo(json.dumps(mosaicjson.dict(exclude_none=True)))
def test_http_backend(requests): """Test HTTP backend.""" with open(mosaic_json, "r") as f: requests.get.return_value = MockResponse(f.read()) with MosaicBackend("https://mymosaic.json") as mosaic: assert isinstance(mosaic, HttpBackend) assert (mosaic.mosaicid == "f39f05644731addf1d183fa094ff6478900a27912ad035ef570231b1") assert mosaic.quadkey_zoom == 7 assert list(mosaic.metadata.keys()) == [ "mosaicjson", "version", "minzoom", "maxzoom", "quadkey_zoom", "bounds", "center", ] assert mosaic.tile(150, 182, 9) == ["cog1.tif", "cog2.tif"] assert mosaic.point(-73, 45) == ["cog1.tif", "cog2.tif"] requests.get.assert_called_once() requests.mock_reset() with open(mosaic_json, "r") as f: requests.get.return_value = MockResponse(f.read()) with pytest.raises(NotImplementedError): with MosaicBackend("https://mymosaic.json") as mosaic: mosaic.update() requests.get.assert_called_once() requests.mock_reset() with pytest.raises(NotImplementedError): with MosaicBackend("https://mymosaic.json", mosaic_def=mosaic_content) as mosaic: mosaic.write() requests.get.assert_not_called() requests.mock_reset() with open(mosaic_gz, "rb") as f: requests.get.return_value = MockResponse(f.read()) with MosaicBackend("https://mymosaic.json.gz") as mosaic: assert isinstance(mosaic, HttpBackend) assert (mosaic.mosaicid == "f39f05644731addf1d183fa094ff6478900a27912ad035ef570231b1")
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: """Handle MVT requests.""" from rio_tiler_mvt.mvt import encoder as mvtEncoder # noqa if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.tile(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, mosaicid or os.path.basename(url), feature_type=feature_type, ), )
def _img( mosaicid: str = None, z: int = None, x: int = None, y: int = None, scale: int = 1, ext: str = None, url: str = None, pixel_selection: str = "first", resampling_method: str = "nearest", ) -> Tuple: """Handle tile requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.tile(x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") 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, usgs_tiler, tilesize=tilesize, pixel_selection=pixsel_method(), resampling_method=resampling_method, ) if tile is None: return ("EMPTY", "text/plain", "empty tiles") 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" options = geotiff_options(x, y, z, tilesize) return ( "OK", f"image/{ext}", render(tile, mask, img_format=driver, **options), )
def info(url: str) -> Tuple[str, str, str]: """Handle /info requests.""" if url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") with MosaicBackend(url) as mosaic: mosaic_def = dict(mosaic.mosaic_def) return ("OK", "application/json", json.dumps(mosaic_def))
def wmts( request: Request, 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..." ), mosaic_path: str = Depends(MosaicPath), ): """OGC WMTS endpoint.""" endpoint = request.url_for("read_mosaicjson") kwargs = dict(request.query_params) kwargs.pop("tile_format", None) kwargs.pop("tile_scale", None) qs = urlencode(list(kwargs.items())) tms = morecantile.tms.get("WebMercatorQuad") with MosaicBackend(mosaic_path) as mosaic: minzoom = mosaic.mosaic_def.minzoom maxzoom = mosaic.mosaic_def.maxzoom bounds = mosaic.mosaic_def.bounds media_type = ImageMimeTypes[tile_format.value].value 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) tile_ext = f"@{tile_scale}x.{tile_format.value}" return templates.TemplateResponse( "wmts.xml", { "request": request, "endpoint": endpoint, "bounds": bounds, "tileMatrix": tileMatrix, "tms": tms, "title": "Cloud Optimized GeoTIFF", "query_string": qs, "tile_format": tile_ext, "media_type": media_type, }, media_type=MimeTypes.xml.value, )
def update(input_files, input_mosaic, min_tile_cover, add_first, threads, quiet): """Update mosaic definition file.""" input_files = input_files.read().splitlines() features = get_footprints(input_files, max_threads=threads) with MosaicBackend(input_mosaic) as mosaic: mosaic.update( features, add_first=add_first, minimum_tile_cover=min_tile_cover, quiet=quiet, )
async def mosaic_point( lon: float = Path(..., description="Longitude"), lat: float = Path(..., description="Latitude"), bidx: Optional[str] = Query( None, title="Band indexes", description="comma (',') delimited band indexes", ), expression: Optional[str] = Query( None, title="Band Math expression", description="rio-tiler's band math expression (e.g B1/B2)", ), mosaic_path: str = Depends(MosaicPath), ): """Get Point value for a MosaicJSON.""" indexes = tuple(int(s) for s in re.findall(r"\d+", bidx)) if bidx else None timings = [] headers: Dict[str, str] = {} with utils.Timer() as t: with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.point(lon, lat) timings.append(("Read-mosaic", t.elapsed)) # Rio-tiler provides a helper function (``rio_tiler.reader.multi_point``) for reading a point from multiple assets # using an external threadpool. For similar reasons as described below, we will transcribe the rio-tiler code to # use the default executor provided by the event loop. futures = [ run_in_threadpool( _read_point, asset, lon, lat, indexes=indexes, expression=expression ) for asset in assets ] values = [] with utils.Timer() as t: async for fut in _process_futures( futures, concurrency=int(os.getenv("MOSAIC_CONCURRENCY", 10)) ): try: values.append(await fut) except Exception: continue timings.append(("Read-tiles", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join( ["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings] ) return {"coordinates": [lon, lat], "values": values}
def update_mosaicjson(body: UpdateMosaicJSON): """Update an existing MosaicJSON""" mosaic_path = MosaicPath(body.url) with MosaicBackend(mosaic_path) as mosaic: features = get_footprints(body.files, max_threads=body.max_threads) try: mosaic.update(features, add_first=body.add_first, quiet=True) except NotImplementedError: raise BadRequestError( f"{mosaic.__class__.__name__} does not support update operations" ) return mosaic.mosaic_def
def npy_tiles( url: str, z: int, x: int, y: int, scale: int = 1, bands: str = None, expr: str = None, pixel_selection: str = "first", ) -> Tuple[str, str, BinaryIO]: """Handle tile requests.""" if url is None: return ("NOK", "text/plain", "Missing 'URL' parameter") with MosaicBackend(url) as mosaic: assets = mosaic.tile(x, y, z) if not assets: return ("EMPTY", "text/plain", f"No assets found for tile {z}-{x}-{y}") tilesize = 256 * scale pixel_selection = pixSel[pixel_selection] if expr is not None: results = mosaic_tiler( assets, x, y, z, expressionTiler, pixel_selection=pixel_selection(), expr=expr, tilesize=tilesize, ) elif bands is not None: results = mosaic_tiler( assets, x, y, z, landsatTiler, pixel_selection=pixel_selection(), bands=tuple(bands.split(",")), tilesize=tilesize, ) else: return ("NOK", "text/plain", "No bands nor expression given") sio = io.BytesIO() numpy.save(sio, results) sio.seek(0) return ("OK", "application/x-binary", sio.getvalue())
def _add(body: str, url: str) -> Tuple: mosaic_definition = MosaicJSON(**json.loads(body)) with MosaicBackend(url, mosaic_def=mosaic_definition) as mosaic: mosaic.write() return ( "OK", "application/json", json.dumps({ "id": url, "status": "READY" }, separators=(",", ":")), )
def _add(body: str, mosaicid: str) -> Tuple: if _aws_head_object(_create_mosaic_path(mosaicid), client=s3_client): return ("NOK", "text/plain", f"Mosaic: {mosaicid} already exist.") mosaic_definition = MosaicJSON(**json.loads(body)) with MosaicBackend( _create_mosaic_path(mosaicid), mosaic_def=mosaic_definition ) as mosaic: mosaic.write() return ( "OK", "application/json", json.dumps({"id": mosaicid, "status": "READY"}, separators=(",", ":")), )
def mosaicjson_info(mosaic_path: str = Depends(MosaicPath)): """ Read MosaicJSON info Ref: https://github.com/developmentseed/cogeo-mosaic-tiler/blob/master/cogeo_mosaic_tiler/handlers/app.py#L164-L198 """ with MosaicBackend(mosaic_path) as mosaic: meta = mosaic.metadata response = { "bounds": meta["bounds"], "center": meta["center"], "maxzoom": meta["maxzoom"], "minzoom": meta["minzoom"], "name": mosaic_path, "quadkeys": list(mosaic.mosaic_def.tiles), } return response
def create_mosaicjson(body: CreateMosaicJSON): """Create a MosaicJSON""" mosaic = MosaicJSON.from_urls( body.files, minzoom=body.minzoom, maxzoom=body.maxzoom, max_threads=body.max_threads, ) mosaic_path = MosaicPath(body.url) with MosaicBackend(mosaic_path, mosaic_def=mosaic) as mosaic: try: mosaic.write() except NotImplementedError: raise BadRequestError( f"{mosaic.__class__.__name__} does not support write operations" ) return mosaic.mosaic_def
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: """Handle /wmts requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") if tile_scale is not None and isinstance(tile_scale, str): tile_scale = int(tile_scale) if not mosaicid: kwargs.update(dict(url=url)) host = app.host else: host = f"{app.host}/{mosaicid}" query_string = urllib.parse.urlencode(list(kwargs.items())) query_string = query_string.replace( "&", "&" ) # & is an invalid character in XML kwargs.pop("SERVICE", None) kwargs.pop("REQUEST", None) mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: meta = mosaic.metadata return ( "OK", "application/xml", wmts_template( host, query_string, minzoom=meta["minzoom"], maxzoom=meta["maxzoom"], bounds=meta["bounds"], tile_scale=tile_scale, tile_format=tile_format, title=title, ), )
def find_assets(x, y, z, mosaic_url, tile_size): """Find assets for input Args: - x: OSM tile index - y: OSM tile index - z: OSM tile index - mosaic_url: either url to MosaicJSON file, or the strings "terrarium" or "geotiff" to load terrarium or geotiff tiles from AWS Terrain Tiles - tile_size, one of 256, 258, 512, 514 """ if mosaic_url == 'terrarium': return _find_terrarium_assets(x, y, z, tile_size) if mosaic_url == 'geotiff': return _find_geotiff_assets(x, y, z, tile_size) with MosaicBackend(mosaic_url) as mosaic: return mosaic.tile(x, y, z)
def mosaic_tilejson( request: Request, tile_scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..." ), tile_format: Optional[ImageType] = Query( None, description="Output image type. Default is auto." ), mosaic_path: str = Depends(MosaicPath), ): """Create TileJSON""" kwargs = {"z": "{z}", "x": "{x}", "y": "{y}", "scale": tile_scale} if tile_format: kwargs["format"] = tile_format tile_url = request.url_for("mosaic_tile", **kwargs).replace("\\", "") with MosaicBackend(mosaic_path) as mosaic: tjson = TileJSON(**mosaic.metadata, tiles=[tile_url]) return tjson
def _geojson(mosaicid: str = None, url: str = None) -> Tuple: """Handle /geojson requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: geojson = { "type": "FeatureCollection", "features": [ mercantile.feature( mercantile.quadkey_to_tile(qk), props=dict(files=files) ) for qk, files in mosaic.mosaic_def.tiles.items() ], } return ("OK", "application/json", json.dumps(geojson, separators=(",", ":")))
def to_geojson(input, collect): """Read MosaicJSON document and create GeoJSON features.""" features = [] with MosaicBackend(input) as mosaic: for qk, assets in mosaic.mosaic_def.tiles.items(): tile = mercantile.quadkey_to_tile(qk) west, south, east, north = mercantile.bounds(tile) geom = { "type": "Polygon", "coordinates": [[ [west, south], [west, north], [east, north], [east, south], [west, south], ]], } feature = { "type": "Feature", "id": str(tile), "geometry": geom, "properties": { "nb_assets": len(assets), "assets": assets }, } if collect: features.append(feature) else: click.echo(json.dumps(feature)) if collect and features: click.echo( json.dumps({ "type": "FeatureCollection", "features": features }, ))
def _tilejson( mosaicid: str = None, url: str = None, tile_scale: int = 1, tile_format: str = None, **kwargs: Any, ) -> Tuple: """Handle /tilejson.json requests.""" if not mosaicid and not url: return ("NOK", "text/plain", "Missing 'MosaicID or URL' parameter") 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}" mosaic_path = _create_mosaic_path(mosaicid) if mosaicid else url with MosaicBackend(mosaic_path) as mosaic: meta = mosaic.metadata response = { "bounds": meta["bounds"], "center": meta["center"], "maxzoom": meta["maxzoom"], "minzoom": meta["minzoom"], "name": mosaicid or url, "tilejson": "2.1.0", "tiles": [tile_url], } return ("OK", "application/json", json.dumps(response, separators=(",", ":")))
def create( input_files, output, minzoom, maxzoom, quadkey_zoom, min_tile_cover, tile_cover_sort, threads, name, description, attribution, quiet, ): """Create mosaic definition file.""" input_files = [file.strip() for file in input_files if file.strip()] mosaicjson = MosaicJSON.from_urls( input_files, minzoom=minzoom, maxzoom=maxzoom, quadkey_zoom=quadkey_zoom, minimum_tile_cover=min_tile_cover, tile_cover_sort=tile_cover_sort, max_threads=threads, quiet=quiet, ) if name: mosaicjson.name = name if description: mosaicjson.description = description if attribution: mosaicjson.attribution = attribution if output: with MosaicBackend(output, mosaic_def=mosaicjson) as mosaic: mosaic.write(overwrite=True) else: click.echo(mosaicjson.json(exclude_none=True))
def create_from_features( features, output, minzoom, maxzoom, property, quadkey_zoom, min_tile_cover, tile_cover_sort, name, description, attribution, quiet, ): """Create mosaic definition file.""" mosaicjson = MosaicJSON.from_features( list(features), minzoom, maxzoom, quadkey_zoom=quadkey_zoom, accessor=lambda feature: feature["properties"][property], minimum_tile_cover=min_tile_cover, tile_cover_sort=tile_cover_sort, quiet=quiet, ) if name: mosaicjson.name = name if description: mosaicjson.description = description if attribution: mosaicjson.attribution = attribution if output: with MosaicBackend(output, mosaic_def=mosaicjson) as mosaic: mosaic.write(overwrite=True) else: click.echo(mosaicjson.json(exclude_none=True))