Esempio n. 1
0
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,
    )
Esempio n. 2
0
    async def tile_from_function(
        self,
        db: AsyncSession,
        tile: morecantile.Tile,
        tms: morecantile.TileMatrixSet,
        obj_in: VectorTileFunction,
        **kwargs: Any,
    ) -> Any:
        """Get Tile Data."""
        bbox = tms.xy_bounds(tile)
        async with pool.acquire() as conn:
            transaction = conn.transaction()
            await transaction.start()
            await conn.execute(obj_in.sql)

            function_params = ":xmin, :ymin, :xmax, :ymax, :epsg"
            if kwargs:
                params = ", ".join([f"{k} => {v}" for k, v in kwargs.items()])
                function_params += f", {params}"
            sql_query = text(
                f"SELECT {obj_in.function_name}({function_params})")
            content = await conn.fetchval_b(
                sql_query,
                xmin=bbox.left,
                ymin=bbox.bottom,
                xmax=bbox.right,
                ymax=bbox.top,
                epsg=tms.crs.to_epsg(),
            )

            await transaction.rollback()

        return content
Esempio n. 3
0
 async def tile(
     self,
     x: int,
     y: int,
     z: int,
     tile_size: int = 256,
     tms: TileMatrixSet = DEFAULT_TMS,
     resample_method: int = Image.NEAREST,
 ) -> np.ndarray:
     tile = morecantile.Tile(x=x, y=y, z=z)
     tile_bounds = tms.xy_bounds(tile)
     width = height = tile_size
     if self.cog.epsg != tms.crs:
         arr = await self._warped_read(
             tile_bounds,
             width,
             height,
             bounds_crs=tms.crs,
             resample_method=resample_method,
         )
     else:
         arr = await self.cog.read(tile_bounds,
                                   shape=(width, height),
                                   resample_method=resample_method)
     return arr
Esempio n. 4
0
def geotiff_options(
    x: int,
    y: int,
    z: int,
    tilesize: int = 256,
    tms: morecantile.TileMatrixSet = default_tms,
) -> Dict:
    """GeoTIFF options."""
    bounds = tms.xy_bounds(morecantile.Tile(x=x, y=y, z=z))
    dst_transform = from_bounds(*bounds, tilesize, tilesize)
    return dict(crs=tms.crs, transform=dst_transform)
Esempio n. 5
0
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],
    )
Esempio n. 6
0
async def tile(
    request: Request,
    table: TableMetadata = Depends(TableParams),
    tile: Tile = Depends(TileParams),
    tms: TileMatrixSet = Depends(TileMatrixSetParams),
    db_pool: Pool = Depends(_get_db_pool),
    columns: str = None,
) -> TileResponse:
    """Handle /tiles requests."""
    timings = []
    headers: Dict[str, str] = {}

    bbox = tms.xy_bounds(tile)
    epsg = tms.crs.to_epsg()
    segSize = (bbox.xmax - bbox.xmin) / 4

    geometry_column = table.geometry_column
    cols = table.properties
    if geometry_column in cols:
        del cols[geometry_column]

    if columns is not None:
        include_cols = [c.strip() for c in columns.split(",")]
        for c in cols.copy():
            if c not in include_cols:
                del cols[c]

    colstring = ", ".join(list(cols))

    limitval = str(int(MAX_FEATURES_PER_TILE))
    limit = f"LIMIT {limitval}" if MAX_FEATURES_PER_TILE > -1 else ""

    sql_query = f"""
        WITH
        bounds AS (
            SELECT
                ST_Segmentize(
                    ST_MakeEnvelope(
                        $1,
                        $2,
                        $3,
                        $4,
                        $5
                    ),
                    $6
                ) AS geom
        ),
        mvtgeom AS (
            SELECT ST_AsMVTGeom(
                ST_Transform(t.{geometry_column}, $5),
                bounds.geom,
                $7,
                $8
            ) AS geom, {colstring}
            FROM {table.id} t, bounds
            WHERE ST_Intersects(
                ST_Transform(t.geom, 4326), ST_Transform(bounds.geom, 4326)
            ) {limit}
        )
        SELECT ST_AsMVT(mvtgeom.*) FROM mvtgeom
    """

    with Timer() as t:
        async with db_pool.acquire() as conn:
            q = await conn.prepare(sql_query)
            content = await q.fetchval(
                bbox.xmin,  # 1
                bbox.ymin,  # 2
                bbox.xmax,  # 3
                bbox.ymax,  # 4
                epsg,  # 5
                segSize,  # 6
                TILE_RESOLUTION,  # 7
                TILE_BUFFER,  # 8
            )
    timings.append(("db-read", t.elapsed))

    if timings:
        headers["X-Server-Timings"] = "; ".join([
            "{} - {:0.2f}".format(name, time * 1000)
            for (name, time) in timings
        ])

    return TileResponse(bytes(content),
                        media_type=MimeTypes.pbf.value,
                        headers=headers)
Esempio n. 7
0
    async def tile_from_table(
        self,
        db: AsyncSession,
        tile: morecantile.Tile,
        tms: morecantile.TileMatrixSet,
        obj_in: VectorTileTable,
        **kwargs: Any,
    ) -> Any:
        """Get Tile Data."""
        bbox = tms.xy_bounds(tile)

        limit = kwargs.get("limit", str(settings.MAX_FEATURES_PER_TILE)
                           )  # Number of features to write to a tile.
        columns = kwargs.get(
            "columns"
        )  # Comma-seprated list of properties (column's name) to include in the tile
        resolution = kwargs.get("resolution", str(
            settings.TILE_RESOLUTION))  # Tile's resolution
        buffer = kwargs.get("buffer", str(
            settings.TILE_BUFFER))  # Size of extra data to add for a tile.

        limitstr = f"LIMIT {limit}" if int(limit) > -1 else ""
        # create list of columns to return
        geometry_column = obj_in.geometry_column
        cols = obj_in.properties
        if geometry_column in cols:
            del cols[geometry_column]

        if columns is not None:
            include_cols = [c.strip() for c in columns.split(",")]
            for c in cols.copy():
                if c not in include_cols:
                    del cols[c]
        colstring = ", ".join(list(cols))

        segSize = bbox.right - bbox.left
        sql_query = f"""
            WITH
            bounds AS (
                SELECT
                    ST_Segmentize(
                        ST_MakeEnvelope(
                            :xmin,
                            :ymin,
                            :xmax,
                            :ymax,
                            {tms.crs.to_epsg()}
                        ),
                        :seg_size
                    ) AS geom
            ),
            mvtgeom AS (
                SELECT ST_AsMVTGeom(
                    ST_Transform(t.{geometry_column}, {tms.crs.to_epsg()}),
                    bounds.geom,
                    :tile_resolution,
                    :tile_buffer
                ) AS geom, {colstring}
                FROM {obj_in.id} t, bounds
                WHERE ST_Intersects(
                    ST_Transform(t.{geometry_column}, 4326),
                    ST_Transform(bounds.geom, 4326)
                ) {limitstr}
            )
            SELECT ST_AsMVT(mvtgeom.*) FROM mvtgeom
        """
        input_data = {
            "xmin": bbox.left,
            "ymin": bbox.bottom,
            "xmax": bbox.right,
            "ymax": bbox.top,
            "seg_size": segSize,
            "tile_resolution": int(resolution),
            "tile_buffer": int(buffer),
        }
        cursor = await db.execute(sql_query, input_data)
        feature = cursor.first()["st_asmvt"]
        return feature
Esempio n. 8
0
        def 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"),
                tms: TileMatrixSet = Depends(self.tms_dependency),
                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."),
                src_path=Depends(self.path_dependency),
                layer_params=Depends(self.layer_dependency),
                dataset_params=Depends(self.dataset_dependency),
                render_params=Depends(self.render_dependency),
                pixel_selection: PixelSelectionMethod = Query(
                    PixelSelectionMethod.first,
                    description="Pixel selection method."),
                kwargs: Dict = Depends(self.additional_dependency),
        ):
            """Create map tile from a COG."""
            timings = []
            headers: Dict[str, str] = {}

            tilesize = scale * 256

            threads = int(os.getenv("MOSAIC_CONCURRENCY", MAX_THREADS))
            with utils.Timer() as t:
                with self.reader(
                        src_path.url,
                        reader=self.dataset_reader,
                        reader_options=self.reader_options,
                ) as src_dst:
                    mosaic_read = t.from_start
                    timings.append(("mosaicread", round(mosaic_read * 1000,
                                                        2)))

                    (data, mask), assets_used = src_dst.tile(
                        x,
                        y,
                        z,
                        pixel_selection=pixel_selection.method(),
                        threads=threads,
                        tilesize=tilesize,
                        **layer_params.kwargs,
                        **dataset_params.kwargs,
                        **kwargs,
                    )
            timings.append(
                ("dataread", round((t.elapsed - mosaic_read) * 1000, 2)))

            if data 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:
                data = utils.postprocess(
                    data,
                    mask,
                    rescale=render_params.rescale,
                    color_formula=render_params.color_formula,
                )
            timings.append(("postprocess", round(t.elapsed * 1000, 2)))

            bounds = tms.xy_bounds(x, y, z)
            dst_transform = from_bounds(*bounds, tilesize, tilesize)
            with utils.Timer() as t:
                content = utils.reformat(
                    data,
                    mask if render_params.return_mask else None,
                    format,
                    colormap=render_params.colormap,
                    transform=dst_transform,
                    crs=tms.crs,
                )
            timings.append(("format", round(t.elapsed * 1000, 2)))

            if timings:
                headers["Server-Timing"] = ", ".join(
                    [f"{name};dur={time}" for (name, time) in timings])

            if assets_used:
                headers["X-Assets"] = ",".join(assets_used)

            return Response(
                content,
                media_type=ImageMimeTypes[format.value].value,
                headers=headers,
            )
Esempio n. 9
0
        def 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"),
                tms: TileMatrixSet = Depends(self.tms_dependency),
                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."),
                src_path=Depends(self.path_dependency),
                layer_params=Depends(self.layer_dependency),
                dataset_params=Depends(self.dataset_dependency),
                render_params=Depends(self.render_dependency),
                kwargs: Dict = Depends(self.additional_dependency),
        ):
            """Create map tile from a dataset."""
            timings = []
            headers: Dict[str, str] = {}

            tilesize = scale * 256

            with utils.Timer() as t:
                with self.reader(src_path.url, tms=tms,
                                 **self.reader_options) as src_dst:
                    tile, mask = src_dst.tile(
                        x,
                        y,
                        z,
                        tilesize=tilesize,
                        **layer_params.kwargs,
                        **dataset_params.kwargs,
                        **kwargs,
                    )
                    colormap = render_params.colormap or getattr(
                        src_dst, "colormap", None)
            timings.append(("dataread", round(t.elapsed * 1000, 2)))

            if not format:
                format = ImageType.jpg if mask.all() else ImageType.png

            with utils.Timer() as t:
                tile = utils.postprocess(
                    tile,
                    mask,
                    rescale=render_params.rescale,
                    color_formula=render_params.color_formula,
                )
            timings.append(("postprocess", round(t.elapsed * 1000, 2)))

            bounds = tms.xy_bounds(x, y, z)
            dst_transform = from_bounds(*bounds, tilesize, tilesize)
            with utils.Timer() as t:
                content = utils.reformat(
                    tile,
                    mask if render_params.return_mask else None,
                    format,
                    colormap=colormap,
                    transform=dst_transform,
                    crs=tms.crs,
                )
            timings.append(("format", round(t.elapsed * 1000, 2)))

            if timings:
                headers["Server-Timing"] = ", ".join(
                    [f"{name};dur={time}" for (name, time) in timings])

            return Response(
                content,
                media_type=ImageMimeTypes[format.value].value,
                headers=headers,
            )