async def stac_part( minx: float = Path(..., description="Bounding box min X"), miny: float = Path(..., description="Bounding box min Y"), maxx: float = Path(..., description="Bounding box max X"), maxy: float = Path(..., description="Bounding box max Y"), format: ImageType = Query(None, description="Output image type."), url: str = Query(..., description="STAC Item URL."), assets: str = Query("", description="comma (,) separated list of asset names."), image_params: CommonImageParams = Depends(), ): """Create image from part of STAC assets.""" timings = [] headers: Dict[str, str] = {} if not image_params.expression and not assets: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Must pass Expression or Asset list.", ) with utils.Timer() as t: with STACReader(url) as stac: data, mask = stac.part( [minx, miny, maxx, maxy], height=image_params.height, width=image_params.width, max_size=image_params.max_size, assets=assets.split(","), expression=image_params.expression, indexes=image_params.indexes, nodata=image_params.nodata, **image_params.kwargs, ) timings.append(("Read", t.elapsed)) with utils.Timer() as t: data = utils.postprocess( data, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: content = utils.reformat( data, mask, img_format=format, colormap=image_params.color_map ) timings.append(("Format", t.elapsed)) timings.append(("Format", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join( ["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings] ) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
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}
async def cog_point( lon: float = Path(..., description="Longitude"), lat: float = Path(..., description="Latitude"), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), assets: str = Query( "", description="comma (,) separated list of asset names."), expression: Optional[str] = Query( None, title="Band Math expression", description="rio-tiler's band math expression (e.g B1/B2)", ), bidx: Optional[str] = Query( None, title="Band indexes", description="comma (',') delimited band indexes", ), asset_expression: Optional[str] = Query( None, title="Band Math expression for assets bands", description="rio-tiler's band math expression (e.g B1/B2)", ), ): """Get Point value for a COG.""" if not expression and not assets: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Must pass Expression or Asset list.", ) 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 STACReader(url) as stac: values = stac.point( lon, lat, assets=assets, expression=expression, indexes=indexes, asset_expression=asset_expression, ) timings.append(("Read", 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}
async def cog_point( lon: float = Path(..., description="Longitude"), lat: float = Path(..., description="Latitude"), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), 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)", ), ): """Get Point value for a COG.""" 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 COGReader(url) as cog: values = cog.point(lon, lat, indexes=indexes, expression=expression) timings.append(("Read", 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}
async def cog_part( minx: float = Path(..., description="Bounding box min X"), miny: float = Path(..., description="Bounding box min Y"), maxx: float = Path(..., description="Bounding box max X"), maxy: float = Path(..., description="Bounding box max Y"), format: ImageType = Query(None, description="Output image type."), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), image_params: CommonImageParams = Depends(), ): """Create image from part of a COG.""" timings = [] headers: Dict[str, str] = {} with utils.Timer() as t: with COGReader(url) as cog: data, mask = cog.part( [minx, miny, maxx, maxy], height=image_params.height, width=image_params.width, max_size=image_params.max_size, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, **image_params.kwargs, ) colormap = image_params.color_map or cog.colormap timings.append(("Read", t.elapsed)) with utils.Timer() as t: data = utils.postprocess( data, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: if format == ImageType.npy: sio = BytesIO() numpy.save(sio, (data, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[format.value] options = img_profiles.get(driver.lower(), {}) content = render(data, mask, img_format=driver, colormap=colormap, **options) timings.append(("Format", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
async def cog_tile( z: int = Path( ..., ge=0, le=30, description="Mercator tiles's zoom level"), x: int = Path(..., description="Mercator tiles's column"), y: int = Path(..., description="Mercator tiles's row"), TileMatrixSetId: TileMatrixSetNames = Query( TileMatrixSetNames.WebMercatorQuad, # type: ignore description="TileMatrixSet Name (default: 'WebMercatorQuad')", ), scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."), format: ImageType = Query( None, description="Output image type. Default is auto."), url: str = Query(..., description="Cloud Optimized GeoTIFF URL."), image_params: CommonTileParams = Depends(), cache_client: CacheLayer = Depends(utils.get_cache), request_id: str = Depends(request_hash), ): """Create map tile from a COG.""" timings = [] headers: Dict[str, str] = {} tilesize = scale * 256 tms = morecantile.tms.get(TileMatrixSetId.name) content = None if cache_client: try: content, ext = cache_client.get_image_from_cache(request_id) format = ImageType[ext] headers["X-Cache"] = "HIT" except Exception: content = None if not content: with utils.Timer() as t: with COGReader(url, tms=tms) as cog: tile, mask = cog.tile( x, y, z, tilesize=tilesize, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, **image_params.kwargs, ) colormap = image_params.color_map or cog.colormap timings.append(("Read", t.elapsed)) if not format: format = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: tile = utils.postprocess( tile, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: if format == ImageType.npy: sio = BytesIO() numpy.save(sio, (tile, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[format.value] options = img_profiles.get(driver.lower(), {}) if format == ImageType.tif: bounds = tms.xy_bounds(x, y, z) dst_transform = from_bounds(*bounds, tilesize, tilesize) options = {"crs": tms.crs, "transform": dst_transform} content = render(tile, mask, img_format=driver, colormap=colormap, **options) timings.append(("Format", t.elapsed)) if cache_client and content: cache_client.set_image_cache(request_id, (content, format.value)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
async def mosaic_tile( z: int = Path(..., ge=0, le=30, description="Mercator tiles's zoom level"), x: int = Path(..., description="Mercator tiles's column"), y: int = Path(..., description="Mercator tiles's row"), scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..." ), format: ImageType = Query(None, description="Output image type. Default is auto."), pixel_selection: PixelSelectionMethod = Query( PixelSelectionMethod.first, description="Pixel selection method." ), image_params: CommonTileParams = Depends(), mosaic_path: str = Depends(MosaicPath), ): """Read MosaicJSON tile""" pixsel = pixel_selection.method() timings = [] headers: Dict[str, str] = {} with utils.Timer() as t: with MosaicBackend(mosaic_path) as mosaic: assets = mosaic.tile(x=x, y=y, z=z) if not assets: raise TileNotFoundError(f"No assets found for tile {z}/{x}/{y}") timings.append(("Read-mosaic", t.elapsed)) tilesize = 256 * scale # Rio-tiler-mosaic uses an external ThreadPoolExecutor to process multiple assets at once but we want to use the # executor provided by the event loop. Instead of calling ``rio_tiler_mosaic.mosaic.mosaic_tiler`` directly we will # transcribe the code here and use the executor provided by the event loop. This also means we define this function # as a coroutine (even though nothing that is called is a coroutine), since the event loop's executor isn't # available in normal ``def`` functions. futures = [ run_in_threadpool( _read_tile, asset, x, y, z, tilesize=tilesize, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, **image_params.kwargs, ) for asset in assets ] with utils.Timer() as t: async for fut in _process_futures( futures, concurrency=int(os.getenv("MOSAIC_CONCURRENCY", 10)) ): try: tile, mask = await fut except Exception: # Gracefully handle exceptions continue tile = numpy.ma.array(tile) tile.mask = mask == 0 pixsel.feed(tile) if pixsel.is_done: break timings.append(("Read-tiles", t.elapsed)) tile, mask = pixsel.data if tile is None: raise TileNotFoundError(f"Tile {z}/{x}/{y} was not found") if not format: format = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: tile = utils.postprocess( tile, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) opts = {} if ImageType.tif in format: opts = geotiff_options(x, y, z, tilesize=tilesize) with utils.Timer() as t: content = utils.reformat( tile, mask, img_format=format, colormap=image_params.color_map, **opts ) timings.append(("Format", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join( ["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings] ) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers )
async def stac_tile( z: int = Path(..., ge=0, le=30, description="Mercator tiles's zoom level"), x: int = Path(..., description="Mercator tiles's column"), y: int = Path(..., description="Mercator tiles's row"), TileMatrixSetId: TileMatrixSetNames = Query( TileMatrixSetNames.WebMercatorQuad, # type: ignore description="TileMatrixSet Name (default: 'WebMercatorQuad')", ), scale: int = Query( 1, gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..." ), format: ImageType = Query(None, description="Output image type. Default is auto."), url: str = Query(..., description="STAC Item URL."), assets: str = Query("", description="comma (,) separated list of asset names."), image_params: CommonTileParams = Depends(), cache_client: CacheLayer = Depends(utils.get_cache), request_id: str = Depends(request_hash), ): """Create map tile from a STAC item.""" timings = [] headers: Dict[str, str] = {} if not image_params.expression and not assets: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Must pass Expression or Asset list.", ) tilesize = scale * 256 tms = morecantile.tms.get(TileMatrixSetId.name) content = None if cache_client: try: content, ext = cache_client.get_image_from_cache(request_id) format = ImageType[ext] headers["X-Cache"] = "HIT" except Exception: content = None if not content: with utils.Timer() as t: with STACReader(url, tms=tms) as stac: tile, mask = stac.tile( x, y, z, assets=assets.split(","), tilesize=tilesize, indexes=image_params.indexes, expression=image_params.expression, nodata=image_params.nodata, ) timings.append(("Read", t.elapsed)) if not format: format = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: tile = utils.postprocess( tile, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) bounds = tms.xy_bounds(x, y, z) dst_transform = from_bounds(*bounds, tilesize, tilesize) with utils.Timer() as t: content = utils.reformat( tile, mask, img_format=format, colormap=image_params.color_map, transform=dst_transform, crs=tms.crs, ) timings.append(("Format", t.elapsed)) if cache_client and content: cache_client.set_image_cache(request_id, (content, format.value)) if timings: headers["X-Server-Timings"] = "; ".join( ["{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings] ) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )
async def stac_preview( format: ImageType = Query( None, description="Output image type. Default is auto."), url: str = Query(..., description="STAC Item URL."), assets: str = Query( "", description="comma (,) separated list of asset names."), image_params: CommonImageParams = Depends(), ): """Create preview of STAC assets.""" timings = [] headers: Dict[str, str] = {} if not image_params.expression and not assets: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Must pass Expression or Asset list.", ) with utils.Timer() as t: with STACReader(url) as stac: data, mask = stac.preview( assets=assets.split(","), expression=image_params.expression, height=image_params.height, width=image_params.width, max_size=image_params.max_size, indexes=image_params.indexes, nodata=image_params.nodata, **image_params.kwargs, ) timings.append(("Read", t.elapsed)) if not format: format = ImageType.jpg if mask.all() else ImageType.png with utils.Timer() as t: data = utils.postprocess( data, mask, rescale=image_params.rescale, color_formula=image_params.color_formula, ) timings.append(("Post-process", t.elapsed)) with utils.Timer() as t: if format == ImageType.npy: sio = BytesIO() numpy.save(sio, (data, mask)) sio.seek(0) content = sio.getvalue() else: driver = drivers[format.value] options = img_profiles.get(driver.lower(), {}) content = render( data, mask, img_format=driver, colormap=image_params.color_map, **options, ) timings.append(("Format", t.elapsed)) if timings: headers["X-Server-Timings"] = "; ".join([ "{} - {:0.2f}".format(name, time * 1000) for (name, time) in timings ]) return ImgResponse( content, media_type=ImageMimeTypes[format.value].value, headers=headers, )