Exemplo n.º 1
0
def get_zooms(
    src_dst,
    tilesize: int = 256,
    tms: morecantile.TileMatrixSet = morecantile.tms.get("WebMercatorQuad"),
    zoom_level_strategy: str = "auto",
) -> Tuple[int, int]:
    """Calculate raster min/max zoom level."""
    if src_dst.crs != tms.crs:
        aff, w, h = calculate_default_transform(
            src_dst.crs,
            tms.crs,
            src_dst.width,
            src_dst.height,
            *src_dst.bounds,
        )
    else:
        aff = list(src_dst.transform)
        w = src_dst.width
        h = src_dst.height

    resolution = max(abs(aff[0]), abs(aff[4]))

    max_zoom = tms.zoom_for_res(
        resolution,
        max_z=30,
        zoom_level_strategy=zoom_level_strategy,
    )

    overview_level = get_maximum_overview_level(w, h, minsize=tilesize)
    ovr_resolution = resolution * (2**overview_level)
    min_zoom = tms.zoom_for_res(ovr_resolution, max_z=30)

    return (min_zoom, max_zoom)
Exemplo n.º 2
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],
    )
Exemplo 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
Exemplo n.º 4
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
Exemplo n.º 5
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)
Exemplo n.º 6
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,
    )
Exemplo n.º 7
0
def get_zooms(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    tilesize: int = 256,
    tms: morecantile.TileMatrixSet = morecantile.tms.get("WebMercatorQuad"),
    zoom_level_strategy: str = "auto",
) -> Tuple[int, int]:
    """Calculate raster min/max zoom level."""
    # If the raster is not in the TMS CRS we calculate its projected properties (height, width, resolution)
    if src_dst.crs != tms.rasterio_crs:
        aff, w, h = calculate_default_transform(
            src_dst.crs,
            tms.rasterio_crs,
            src_dst.width,
            src_dst.height,
            *src_dst.bounds,
        )
    else:
        aff = list(src_dst.transform)
        w = src_dst.width
        h = src_dst.height

    resolution = max(abs(aff[0]), abs(aff[4]))

    # The maxzoom is defined by finding the minimum difference between
    # the raster resolution and the zoom level resolution
    max_zoom = tms.zoom_for_res(
        resolution,
        max_z=30,
        zoom_level_strategy=zoom_level_strategy,
    )

    # The minzoom is defined by the resolution of the maximum theoretical overview level
    max_possible_overview_level = get_maximum_overview_level(w, h, minsize=tilesize)
    ovr_resolution = resolution * (2**max_possible_overview_level)
    min_zoom = tms.zoom_for_res(ovr_resolution, max_z=30)

    return (min_zoom, max_zoom)
Exemplo n.º 8
0
def test_nonearthbody():
    """COGReader should work with non-earth dataset."""
    with pytest.warns(UserWarning):
        with COGReader(COG_EUROPA) as cog:
            assert cog.minzoom == 0
            assert cog.maxzoom == 24

    with pytest.warns(None) as warnings:
        with COGReader(COG_EUROPA) as cog:
            assert cog.info()
            assert len(warnings) == 2

            img = cog.read()
            assert numpy.array_equal(img.data, cog.dataset.read(indexes=(1, )))
            assert img.width == cog.dataset.width
            assert img.height == cog.dataset.height
            assert img.count == cog.dataset.count

            img = cog.preview()
            assert img.bounds == cog.bounds

            part = cog.part(cog.bounds, bounds_crs=cog.crs)
            assert part.bounds == cog.bounds

            lon = (cog.bounds[0] + cog.bounds[2]) / 2
            lat = (cog.bounds[1] + cog.bounds[3]) / 2
            assert cog.point(lon, lat, coord_crs=cog.crs)[0] is not None

    europa_crs = CRS.from_authority("ESRI", 104915)
    tms = TileMatrixSet.custom(
        crs=europa_crs,
        extent=europa_crs.area_of_use.bounds,
        matrix_scale=[2, 1],
    )
    with pytest.warns(None) as warnings:
        with COGReader(COG_EUROPA, tms=tms) as cog:
            assert cog.minzoom == 4
            assert cog.maxzoom == 6

            # Get Tile covering the UL corner
            bounds = transform_bounds(cog.crs, tms.rasterio_crs, *cog.bounds)
            t = tms._tile(bounds[0], bounds[1], cog.minzoom)
            img = cog.tile(t.x, t.y, t.z)

            assert img.height == 256
            assert img.width == 256
            assert img.crs == tms.rasterio_crs

            assert len(warnings) == 0
Exemplo n.º 9
0
def test_nonearth_custom():
    """Test Custom geographic_crs."""
    MARS2000_SPHERE = CRS.from_proj4("+proj=longlat +R=3396190 +no_defs")
    MARS_MERCATOR = CRS.from_proj4(
        "+proj=merc +R=3396190 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +no_defs"
    )

    mars_tms = TileMatrixSet.custom(
        [
            -179.9999999999996,
            -85.05112877980656,
            179.9999999999996,
            85.05112877980656,
        ],
        MARS_MERCATOR,
        extent_crs=MARS2000_SPHERE,
        title="Web Mercator Mars",
        geographic_crs=MARS2000_SPHERE,
    )

    @attr.s
    class MarsReader(COGReader):
        """Use custom geographic CRS."""

        geographic_crs: rasterio.crs.CRS = attr.ib(
            init=False,
            default=rasterio.crs.CRS.from_proj4(
                "+proj=longlat +R=3396190 +no_defs"),
        )

    with pytest.warns(None) as warnings:
        with MarsReader(COG_MARS, tms=mars_tms) as cog:
            assert cog.geographic_bounds[0] > -180

    assert len(warnings) == 0

    with pytest.warns(None) as warnings:
        with COGReader(
                COG_MARS,
                tms=mars_tms,
                geographic_crs=rasterio.crs.CRS.from_proj4(
                    "+proj=longlat +R=3396190 +no_defs"),
        ) as cog:
            assert cog.geographic_bounds[0] > -180

    assert len(warnings) == 0
Exemplo n.º 10
0
        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,
            )
Exemplo n.º 11
0
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
Exemplo n.º 12
0
        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,
            )
Exemplo n.º 13
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)
Exemplo n.º 14
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
Exemplo n.º 15
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,
            )
Exemplo n.º 16
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,
            )