Esempio n. 1
0
def test_get_zooms_valid():
    """Should work as expected (return zooms)."""
    with rasterio.open(dataset) as src_dst:
        minzoom, maxzoom = mercator.get_zooms(src_dst)
        assert minzoom == 4
        assert maxzoom == 8

        minzoom, maxzoom = mercator.get_zooms(src_dst, tilesize=512)
        assert minzoom == 4
        assert maxzoom == 7

        minzoom, maxzoom = mercator.get_zooms(src_dst, ensure_global_max_zoom=True)
        assert minzoom == 6
        assert maxzoom == 10
Esempio n. 2
0
def _get_tiles(src_path, nb_tiles=5):
    with rasterio.open(src_path) as src_dst:
        bounds = warp.transform_bounds(
            src_dst.crs, "epsg:4326", *src_dst.bounds, densify_pts=21
        )
        minzoom, maxzoom = mercator.get_zooms(src_dst)

    while True:
        zoom = random.randint(maxzoom - 2, maxzoom)
        tiles = list(mercantile.tiles(*bounds, zoom))

        def _f(tile):
            x, y, z = tile
            ulx, uly = mercantile.ul(x, y, z)
            lrx, lry = mercantile.ul(x + 1, y + 1, zoom)
            return (
                (bounds[0] < ulx < bounds[2])
                and (bounds[1] < uly < bounds[3])
                and (bounds[0] < lrx < bounds[2])
                and (bounds[1] < lry < bounds[3])
            )

        tiles = list(filter(lambda x: _f(x), tiles))
        if not tiles:
            continue

        if len(tiles) > nb_tiles:
            return random.sample(tiles, nb_tiles)
        else:
            return tiles
Esempio n. 3
0
def tilejson_handler(url: str,
                     tile_format: str = "png",
                     tile_scale: int = 1,
                     **kwargs: Any) -> Tuple[str, str, str]:
    """Handle /tilejson.json requests."""
    kwargs.update(dict(url=url))
    qs = urllib.parse.urlencode(list(kwargs.items()))
    tile_url = f"{APP.host}/tiles/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}"
    if qs:
        tile_url += f"?{qs}"

    with rasterio.open(url) as src_dst:
        bounds = warp.transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                       list(src_dst.bounds),
                                       densify_pts=21)
        minzoom, maxzoom = get_zooms(src_dst)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2,
                  minzoom]

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=os.path.basename(url),
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 4
0
def tilejson_handler(url: str, tile_format: str = "png", **kwargs: Dict):
    """Handle /tilejson.json requests."""
    qs = urllib.parse.urlencode(list(kwargs.items()))
    tile_url = f"{APP.host}/tiles/{{z}}/{{x}}/{{y}}.{tile_format}?url={url}"
    if qs:
        tile_url += f"&{qs}"

    with rasterio.open(url) as src_dst:
        bounds = warp.transform_bounds(src_dst.crs,
                                       "epsg:4326",
                                       *src_dst.bounds,
                                       densify_pts=21)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
        minzoom, maxzoom = get_zooms(src_dst)

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=os.path.basename(url),
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 5
0
def get_dataset_info(src_path: str) -> Dict:
    """Get rasterio dataset meta."""
    with rasterio.open(src_path) as src_dst:
        bounds = transform_bounds(src_dst.crs,
                                  "epsg:4326",
                                  *src_dst.bounds,
                                  densify_pts=21)
        min_zoom, max_zoom = get_zooms(src_dst, ensure_global_max_zoom=True)
        return {
            "geometry": {
                "type":
                "Polygon",
                "coordinates": [[
                    [bounds[0], bounds[3]],
                    [bounds[0], bounds[1]],
                    [bounds[2], bounds[1]],
                    [bounds[2], bounds[3]],
                    [bounds[0], bounds[3]],
                ]],
            },
            "properties": {
                "path": src_path,
                "bounds": bounds,
                "minzoom": min_zoom,
                "maxzoom": max_zoom,
                "datatype": src_dst.meta["dtype"],
            },
            "type": "Feature",
        }
Esempio n. 6
0
def spatial_info(address: str) -> Dict:
    """
    Return COGEO spatial info.

    Attributes
    ----------
        address : str or PathLike object
            A dataset path or URL. Will be opened in "r" mode.

    Returns
    -------
        out : dict.

    """
    with rasterio.open(address) as src_dst:
        minzoom, maxzoom = get_zooms(src_dst)
        bounds = transform_bounds(src_dst.crs,
                                  constants.WGS84_CRS,
                                  *src_dst.bounds,
                                  densify_pts=21)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2,
                  minzoom]

    return dict(address=address,
                bounds=bounds,
                center=center,
                minzoom=minzoom,
                maxzoom=maxzoom)
Esempio n. 7
0
def tilejson_handler(
    url: str, tile_scale: int = 1, tile_format: str = None, **kwargs: Any
) -> Tuple[str, str, str]:
    """Handle /tilejson.json requests."""
    if tile_scale is not None and isinstance(tile_scale, str):
        tile_scale = int(tile_scale)

    kwargs.update(dict(url=url))
    qs = urlencode(list(kwargs.items()))
    if tile_format:
        tile_url = f"{app.host}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}"
    else:
        tile_url = f"{app.host}/{{z}}/{{x}}/{{y}}@{tile_scale}x?{qs}"

    with rasterio.Env(aws_session):
        with rasterio.open(url) as src_dst:
            bounds = list(
                warp.transform_bounds(
                    src_dst.crs, "epsg:4326", *src_dst.bounds, densify_pts=21
                )
            )
            minzoom, maxzoom = get_zooms(src_dst)
            center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, minzoom]

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=os.path.basename(url),
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 8
0
    def __init__(self,
                 src_path: str,
                 dataset: Union[DatasetReader, WarpedVRT] = None):
        self.src_path = src_path

        if src_path:
            self.src_dst = rasterio.open(self.src_path)
        elif dataset:
            self.src_dst = dataset
        else:
            raise Exception("Missing path or dataset")

        self._bounds = transform_bounds(self.src_dst.crs,
                                        WGS84_CRS,
                                        *self.src_dst.bounds,
                                        densify_pts=21)
        minzoom, maxzoom = get_zooms(self.src_dst)
        self._minzoom = minzoom
        self._maxzoom = maxzoom

        try:
            self._colormap = self.src_dst.colormap(1)
        except ValueError:
            self._colormap = None
            pass
Esempio n. 9
0
    def get(self, request, pk=None, project_pk=None, tile_type=""):
        """
        Get tile.json for this tasks's asset type
        """
        task = self.get_and_check_task(request, pk)

        raster_path = get_raster_path(task, tile_type)
        if not os.path.isfile(raster_path):
            raise exceptions.NotFound()

        with rasterio.open(raster_path) as src_dst:
            minzoom, maxzoom = get_zooms(src_dst)

        return Response({
            'tilejson':
            '2.1.0',
            'name':
            task.name,
            'version':
            '1.0.0',
            'scheme':
            'xyz',
            'tiles':
            [get_tile_url(task, tile_type, self.request.query_params)],
            'minzoom':
            minzoom,
            'maxzoom':
            maxzoom,
            'bounds':
            get_extent(task, tile_type).extent
        })
Esempio n. 10
0
def tilejson_handler(request: Dict, url: str, tile_format: str = "png", **kwargs: Dict):
    """Handle /tilejson.json requests."""
    host = request["headers"].get(
        "X-Forwarded-Host", request["headers"].get("Host", "")
    )
    # Check for API gateway stage
    if ".execute-api." in host and ".amazonaws.com" in host:
        stage = request["requestContext"].get("stage", "")
        host = f"{host}/{stage}"

    scheme = "http" if host.startswith("127.0.0.1") else "https"

    qs = urllib.parse.urlencode(list(kwargs.items()))
    tile_url = f"{scheme}://{host}/tiles/{{z}}/{{x}}/{{y}}.{tile_format}?url={url}"
    if qs:
        tile_url += f"&{qs}"

    with rasterio.open(url) as src_dst:
        bounds = warp.transform_bounds(
            *[src_dst.crs, "epsg:4326"] + list(src_dst.bounds), densify_pts=21
        )
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
        minzoom, maxzoom = get_zooms(src_dst)

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=os.path.basename(url),
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 11
0
def wtms(
    request: Request,
    response: Response,
    url: str = Query(..., description="Cloud Optimized GeoTIFF URL."),
    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..."),
):
    """Wmts endpoit."""
    scheme = request.url.scheme
    host = request.headers["host"]
    if config.API_VERSION_STR:
        host += config.API_VERSION_STR
    endpoint = f"{scheme}://{host}"

    kwargs = dict(request.query_params)
    kwargs.pop("tile_format", None)
    kwargs.pop("tile_scale", None)
    qs = urlencode(list(kwargs.items()))

    with rasterio.open(url) as src_dst:
        bounds = list(
            warp.transform_bounds(src_dst.crs,
                                  constants.WGS84_CRS,
                                  *src_dst.bounds,
                                  densify_pts=21))
        minzoom, maxzoom = get_zooms(src_dst)

    media_type = mimetype[tile_format.value]
    tilesize = tile_scale * 256
    tileMatrix = []
    for zoom in range(minzoom, maxzoom + 1):
        tileMatrix.append(f"""<TileMatrix>
                <ows:Identifier>{zoom}</ows:Identifier>
                <ScaleDenominator>{559082264.02872 / 2 ** zoom / tile_scale}</ScaleDenominator>
                <TopLeftCorner>-20037508.34278925 20037508.34278925</TopLeftCorner>
                <TileWidth>{tilesize}</TileWidth>
                <TileHeight>{tilesize}</TileHeight>
                <MatrixWidth>{2 ** zoom}</MatrixWidth>
                <MatrixHeight>{2 ** zoom}</MatrixHeight>
            </TileMatrix>""")

    return templates.TemplateResponse(
        "wmts.xml",
        {
            "request": request,
            "endpoint": endpoint,
            "bounds": bounds,
            "tileMatrix": tileMatrix,
            "title": "Cloud Optimized GeoTIFF",
            "query_string": qs,
            "tile_scale": tile_scale,
            "tile_format": tile_format.value,
            "media_type": media_type,
        },
        media_type="application/xml",
    )
Esempio n. 12
0
def tilejson(request, url, tile_format="png", **kwargs):
    """
    Handle /tilejson.json requests.

    Note: All the querystring parameters are translated to function keywords
    and passed as string value by lambda_proxy

    Attributes
    ----------
    url : str, required
        Dataset url to read from.
    image_format : str
        Image format to return (default: png).
    kwargs: dict, optional
        Querystring parameters to forward to the tile url.

    Returns
    -------
    status : str
        Status of the request (e.g. OK, NOK).
    MIME type : str
        response body MIME type (e.g. application/json).
    body : str
        String encoded TileJSON.

    """
    host = request["headers"].get("X-Forwarded-Host",
                                  request["headers"].get("Host", ""))
    # Check for API gateway stage
    if ".execute-api." in host and ".amazonaws.com" in host:
        stage = request["requestContext"].get("stage", "")
        host = f"{host}/{stage}"

    scheme = "http" if host.startswith("127.0.0.1") else "https"

    qs = [f"{k}={v}" for k, v in kwargs.items()]
    qs = "&".join(qs)

    tile_url = f"{scheme}://{host}/tiles/{{z}}/{{x}}/{{y}}.{tile_format}?url={url}"
    if qs:
        tile_url += f"&{qs}"

    with rasterio.open(url) as src_dst:
        bounds = warp.transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                       list(src_dst.bounds),
                                       densify_pts=21)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
        minzoom, maxzoom = get_zooms(src_dst)

    meta = dict(bounds=bounds,
                center=center,
                minzoom=minzoom,
                maxzoom=maxzoom,
                name=os.path.basename(url),
                tilejson="2.1.0",
                tiles=[tile_url])

    return ("OK", "application/json", json.dumps(meta))
Esempio n. 13
0
    def _get_info(url):
        with rasterio.open(url) as src_dst:
            wgs_bounds = warp.transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                               list(src_dst.bounds),
                                               densify_pts=21)
            minzoom, maxzoom = get_zooms(src_dst)

        return {
            "bounds": list(wgs_bounds),
            "minzoom": minzoom,
            "maxzoom": maxzoom
        }
Esempio n. 14
0
async def test_cog_read_single_band(create_cog_reader):
    infile = "https://async-cog-reader-test-data.s3.amazonaws.com/int16_deflate.tif"
    async with create_cog_reader(infile) as cog:
        assert cog.profile["count"] == 1
        with rasterio.open(infile) as ds:
            _, zoom = get_zooms(ds)
        centroid = Polygon.from_bounds(
            *transform_bounds(cog.epsg, "EPSG:4326", *cog.bounds)).centroid
        tile = mercantile.tile(centroid.x, centroid.y, zoom)
        tile_native_bounds = transform_bounds("EPSG:4326", cog.epsg,
                                              *mercantile.bounds(tile))
        arr = await cog.read(tile_native_bounds, (256, 256))
        assert arr.shape == (256, 256)
        assert arr.dtype == cog.profile["dtype"]
Esempio n. 15
0
def _get_info(src_path):
    with rasterio.open(src_path) as src_dst:
        bounds = transform_bounds(
            *[src_dst.crs, "epsg:4326"] + list(src_dst.bounds), densify_pts=21
        )
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2]
        minzoom, maxzoom = get_zooms(src_dst)

        def _get_name(ix):
            name = src_dst.descriptions[ix - 1]
            if not name:
                name = f"band{ix}"
            return name

        band_descriptions = [_get_name(ix) for ix in src_dst.indexes]

    return bounds, center, minzoom, maxzoom, band_descriptions
Esempio n. 16
0
def wmts_handler(
    url: str = None,
    tile_format: str = "png",
    tile_scale: int = 1,
    title: str = "Cloud Optimizied GeoTIFF",
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /wmts requests."""
    if tile_scale is not None and isinstance(tile_scale, str):
        tile_scale = int(tile_scale)

    # Remove QGIS arguments
    kwargs.pop("SERVICE", None)
    kwargs.pop("REQUEST", None)

    kwargs.update(dict(url=url))
    query_string = urllib.parse.urlencode(list(kwargs.items()))

    # & is an invalid character in XML
    query_string = query_string.replace("&", "&amp;")

    with rasterio.Env(aws_session):
        with rasterio.open(url) as src_dst:
            bounds = list(
                warp.transform_bounds(src_dst.crs,
                                      "epsg:4326",
                                      *src_dst.bounds,
                                      densify_pts=21))
            minzoom, maxzoom = get_zooms(src_dst)

    return (
        "OK",
        "application/xml",
        wmts_template(
            app.host,
            query_string,
            minzoom=minzoom,
            maxzoom=maxzoom,
            bounds=bounds,
            tile_scale=tile_scale,
            tile_format=tile_format,
            title=title,
        ),
    )
Esempio n. 17
0
async def test_cog_tiler_tile(create_cog_reader):
    infile = "https://async-cog-reader-test-data.s3.amazonaws.com/webp_cog.tif"
    async with create_cog_reader(infile) as cog:
        tiler = COGTiler(cog)

        with rasterio.open(infile) as ds:
            _, zoom = get_zooms(ds)

        centroid = Polygon.from_bounds(
            *transform_bounds(cog.epsg, "EPSG:4326", *cog.bounds)).centroid
        tile = mercantile.tile(centroid.x, centroid.y, zoom)
        # tile_native_bounds = transform_bounds("EPSG:4326", cog.epsg, *mercantile.bounds(tile))

        arr = await tiler.tile(tile.x,
                               tile.y,
                               tile.z,
                               tile_size=256,
                               resample_method=Image.BILINEAR)

        with cogeo_reader(infile) as ds:
            rio_tile_arr, rio_tile_mask = ds.tile(tile.x,
                                                  tile.y,
                                                  tile.z,
                                                  tilesize=256,
                                                  resampling_method="bilinear")

        if cog.is_masked:
            tile_arr = np.ma.getdata(arr)
            tile_mask = np.ma.getmask(arr)

            # Make sure image data is the same
            assert pytest.approx(tile_arr - rio_tile_arr,
                                 1) == np.zeros(tile_arr.shape)

            # Make sure mask data is the same
            rio_mask_counts = np.unique(rio_tile_mask, return_counts=True)
            tile_mask_counts = np.unique(tile_mask, return_counts=True)
            assert len(rio_mask_counts[0]) == len(tile_mask_counts[0])
            assert (rio_mask_counts[1][0] *
                    cog.profile["count"] == tile_mask_counts[1][0])

        else:
            assert pytest.approx(arr - rio_tile_arr, 1) == np.zeros(arr.shape)
Esempio n. 18
0
def _wmts(
    mosaicid: str = None,
    url: str = None,
    tile_format: str = "png",
    tile_scale: int = 1,
    title: str = "Cloud Optimizied GeoTIFF Mosaic",
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /wmts requests."""
    if tile_scale is not None and isinstance(tile_scale, str):
        tile_scale = int(tile_scale)

    kwargs.pop("SERVICE", None)
    kwargs.pop("REQUEST", None)
    kwargs.update(dict(url=url))
    query_string = urllib.parse.urlencode(list(kwargs.items()))
    query_string = query_string.replace(
        "&", "&amp;")  # & is an invalid character in XML

    with rasterio.open(url) as src_dst:
        bounds = warp.transform_bounds(src_dst.crs,
                                       "epsg:4326",
                                       *src_dst.bounds,
                                       densify_pts=21)
        minzoom, maxzoom = get_zooms(src_dst)

    return (
        "OK",
        "application/xml",
        wmts_template(
            f"{APP.host}",
            os.path.basename(url),
            query_string,
            minzoom=minzoom,
            maxzoom=maxzoom,
            bounds=bounds,
            tile_scale=tile_scale,
            tile_format=tile_format,
            title=title,
        ),
    )
Esempio n. 19
0
def tilejson_handler(
    event: Dict,
    sceneid: str,
    tile_format: str = "png",
    tile_scale: int = 1,
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /tilejson.json requests."""
    # HACK
    token = event["multiValueQueryStringParameters"].get("access_token")
    if token:
        kwargs.update(dict(access_token=token[0]))

    qs = urllib.parse.urlencode(list(kwargs.items()))
    tile_url = (
        f"{APP.host}/tiles/{sceneid}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}"
    )

    scene_params = landsat8._landsat_parse_scene_id(sceneid)
    landsat_address = f"{LANDSAT_BUCKET}/{scene_params['key']}_BQA.TIF"
    with rasterio.open(landsat_address) as src_dst:
        bounds = warp.transform_bounds(src_dst.crs,
                                       "epsg:4326",
                                       *src_dst.bounds,
                                       densify_pts=21)
        minzoom, maxzoom = get_zooms(src_dst)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2,
                  minzoom]

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=sceneid,
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 20
0
def _get_info(src_path: str) -> Any:
    with rasterio.open(src_path) as src_dst:
        bounds = transform_bounds(
            src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21
        )
        minzoom, maxzoom = get_zooms(src_dst)
        center = ((bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2, minzoom)

        def _get_name(ix):
            name = src_dst.descriptions[ix - 1]
            if not name:
                name = f"band{ix}"
            return name

        band_descriptions = [_get_name(ix) for ix in src_dst.indexes]
        data_type = src_dst.meta["dtype"]
        try:
            cmap = src_dst.colormap(1)
        except ValueError:
            cmap = None

    return bounds, center, minzoom, maxzoom, band_descriptions, data_type, cmap
Esempio n. 21
0
async def test_cog_read(infile, create_cog_reader):
    async with create_cog_reader(infile) as cog:
        with rasterio.open(infile) as ds:
            _, zoom = get_zooms(ds)
        centroid = Polygon.from_bounds(
            *transform_bounds(cog.epsg, "EPSG:4326", *cog.bounds)).centroid
        tile = mercantile.tile(centroid.x, centroid.y, zoom)

        tile_native_bounds = transform_bounds("EPSG:4326", cog.epsg,
                                              *mercantile.bounds(tile))

        arr = await cog.read(tile_native_bounds, (256, 256))

        with cogeo_reader(infile) as ds:
            rio_tile_arr, rio_tile_mask = ds.tile(tile.x,
                                                  tile.y,
                                                  tile.z,
                                                  tilesize=256,
                                                  resampling_method="bilinear")

        if cog.is_masked:
            tile_arr = np.ma.getdata(arr)
            tile_mask = np.ma.getmask(arr)

            # Make sure image data is the same
            assert pytest.approx(tile_arr - rio_tile_arr,
                                 1) == np.zeros(tile_arr.shape)

            # Make sure mask data is the same
            rio_mask_counts = np.unique(rio_tile_mask, return_counts=True)
            tile_mask_counts = np.unique(tile_mask, return_counts=True)
            assert len(rio_mask_counts[0]) == len(tile_mask_counts[0])
            assert (rio_mask_counts[1][0] *
                    cog.profile["count"] == tile_mask_counts[1][0])

        else:
            assert pytest.approx(arr - rio_tile_arr, 1) == np.zeros(arr.shape)
Esempio n. 22
0
def tilejson_handler(
    event: Dict,
    scene: str,
    tile_format: str = "png",
    tile_scale: int = 1,
    **kwargs: Any,
) -> Tuple[str, str, str]:
    """Handle /tilejson.json requests."""
    # HACK
    token = event["multiValueQueryStringParameters"].get("access_token")
    if token:
        kwargs.update(dict(access_token=token[0]))

    qs = urllib.parse.urlencode(list(kwargs.items()))
    tile_url = f"{APP.host}/s2/tiles/{scene}/{{z}}/{{x}}/{{y}}@{tile_scale}x.{tile_format}?{qs}"

    scene_params = sentinel2._sentinel_parse_scene_id(scene)
    sentinel_address = "s3://{}/{}/B{}.jp2".format(sentinel2.SENTINEL_BUCKET,
                                                   scene_params["key"], "04")
    with rasterio.open(sentinel_address) as src_dst:
        bounds = warp.transform_bounds(*[src_dst.crs, "epsg:4326"] +
                                       list(src_dst.bounds),
                                       densify_pts=21)
        minzoom, maxzoom = get_zooms(src_dst)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2,
                  minzoom]

    meta = dict(
        bounds=bounds,
        center=center,
        minzoom=minzoom,
        maxzoom=maxzoom,
        name=scene,
        tilejson="2.1.0",
        tiles=[tile_url],
    )
    return ("OK", "application/json", json.dumps(meta))
Esempio n. 23
0
def get_zoom_safe(src_dst):
    minzoom, maxzoom = get_zooms(src_dst)
    if maxzoom < minzoom:
        maxzoom = minzoom

    return minzoom, maxzoom
Esempio n. 24
0
def info(address: str) -> Dict:
    """
    Return simple metadata about the file.

    Attributes
    ----------
    address : str or PathLike object
        A dataset path or URL. Will be opened in "r" mode.

    Returns
    -------
    out : dict.

    """
    with rasterio.open(address) as src_dst:
        minzoom, maxzoom = get_zooms(src_dst)
        bounds = transform_bounds(src_dst.crs,
                                  constants.WGS84_CRS,
                                  *src_dst.bounds,
                                  densify_pts=21)
        center = [(bounds[0] + bounds[2]) / 2, (bounds[1] + bounds[3]) / 2,
                  minzoom]

        def _get_descr(ix):
            """Return band description."""
            name = src_dst.descriptions[ix - 1]
            if not name:
                name = "band{}".format(ix)
            return name

        band_descriptions = [(ix, _get_descr(ix)) for ix in src_dst.indexes]
        tags = [(ix, src_dst.tags(ix)) for ix in src_dst.indexes]

        other_meta = dict()
        if src_dst.scales[0] and src_dst.offsets[0]:
            other_meta.update(dict(scale=src_dst.scales[0]))
            other_meta.update(dict(offset=src_dst.offsets[0]))

        if has_alpha_band(src_dst):
            nodata_type = "Alpha"
        elif has_mask_band(src_dst):
            nodata_type = "Mask"
        elif src_dst.nodata is not None:
            nodata_type = "Nodata"
        else:
            nodata_type = "None"

        try:
            cmap = src_dst.colormap(1)
            other_meta.update(dict(colormap=cmap))
        except ValueError:
            pass

        return dict(
            address=address,
            bounds=bounds,
            center=center,
            minzoom=minzoom,
            maxzoom=maxzoom,
            band_metadata=tags,
            band_descriptions=band_descriptions,
            dtype=src_dst.meta["dtype"],
            colorinterp=[
                src_dst.colorinterp[ix - 1].name for ix in src_dst.indexes
            ],
            nodata_type=nodata_type,
            **other_meta,
        )
Esempio n. 25
0
def _raster_get_stats(
    src_dst,
    indexes=None,
    nodata=None,
    overview_level=None,
    max_size=1024,
    percentiles=(2, 98),
    dst_crs=CRS({"init": "EPSG:5514"}),
    histogram_bins=10,
    histogram_range=None,
    resampling_method="bilinear",
    warp_vrt_option={},
    expr=None,
):
    """
    Retrieve dataset statistics.

    Attributes
    ----------
    src_dst : rasterio.io.DatasetReader
        rasterio.io.DatasetReader object
    indexes : tuple, list, int, optional
        Dataset band indexes.
    nodata, int, optional
        Custom nodata value if not preset in dataset.
    overview_level : int, optional
        Overview (decimation) level to fetch.
    max_size: int, optional
        Maximum size of dataset to retrieve
        (will be used to calculate the overview level to fetch).
    percentiles : tulple, optional
        Percentile or sequence of percentiles to compute,
        which must be between 0 and 100 inclusive (default: (2, 98)).
    dst_crs: CRS or dict
        Target coordinate reference system (default: EPSG:5514).
    histogram_bins: int, optional
        Defines the number of equal-width histogram bins (default: 10).
    histogram_range: tuple or list, optional
        The lower and upper range of the bins. If not provided, range is simply
        the min and max of the array.
    resampling_method : str, optional (default: "bilinear")
        Resampling algorithm.
    warp_vrt_option: dict, optional (default: {})
        These will be passed to the rasterio.warp.WarpedVRT class.
    expr : str, optional (default: None)
        Expression to apply to raster before computing statistics

    Returns
    -------
    out : dict
        bounds, mercator zoom range, band descriptions
        and band statistics: (percentiles), min, max, stdev, histogram

        e.g.
        {
            'bounds': {
                'value': (145.72265625, 14.853515625, 145.810546875, 14.94140625),
                'crs': '+init=EPSG:5514'
            },
            'minzoom': 8,
            'maxzoom': 12,
            'band_descriptions': [(1, 'red'), (2, 'green'), (3, 'blue'), (4, 'nir')]
            'statistics': {
                1: {
                    'pc': [38, 147],
                    'min': 20,
                    'max': 180,
                    'std': 28.123562304138662,
                    'histogram': [
                        [1625, 219241, 28344, 15808, 12325, 10687, 8535, 7348, 4656, 1208],
                        [20.0, 36.0, 52.0, 68.0, 84.0, 100.0, 116.0, 132.0, 148.0, 164.0, 180.0]
                    ]
                }
                ...
                3: {...}
                4: {...}
            }
        }

    """
    if isinstance(indexes, int):
        indexes = [indexes]
    elif isinstance(indexes, tuple):
        indexes = list(indexes)

    if expr is not None:
        bands_names = tuple(
            sorted(set(re.findall(r"(?P<bands>b[0-9]+)", expr))))
        if indexes is None:
            indexes = tuple([int(b.replace("b", "")) for b in bands_names])

    levels = src_dst.overviews(1)
    width = src_dst.width
    height = src_dst.height
    indexes = indexes if indexes else src_dst.indexes
    nodata = nodata if nodata is not None else src_dst.nodata
    bounds = transform_bounds(src_dst.crs,
                              dst_crs,
                              *src_dst.bounds,
                              densify_pts=21)

    minzoom, maxzoom = get_zooms(src_dst)

    def _get_descr(ix):
        """Return band description."""
        name = src_dst.descriptions[ix - 1]
        if not name:
            name = "band{}".format(ix)
        return name

    band_descriptions = [(ix, _get_descr(ix)) for ix in indexes]

    if len(levels):
        if overview_level:
            decim = levels[overview_level]
        else:
            # determine which zoom level to read
            for ii, decim in enumerate(levels):
                if (max(_div_round_up(width, decim),
                        _div_round_up(height, decim)) < max_size):
                    break
    else:
        decim = 1
        warnings.warn("Dataset has no overviews, reading the full dataset",
                      NoOverviewWarning)

    out_shape = (
        len(indexes),
        _div_round_up(height, decim),
        _div_round_up(width, decim),
    )

    vrt_params = dict(add_alpha=True)
    if has_alpha_band(src_dst):
        vrt_params.update(dict(add_alpha=False))

    if nodata is not None:
        vrt_params.update(
            dict(nodata=nodata, add_alpha=False, src_nodata=nodata))

    vrt_params.update(warp_vrt_option)
    with WarpedVRT(src_dst, **vrt_params) as vrt:
        arr = vrt.read(
            out_shape=out_shape,
            indexes=indexes,
            resampling=Resampling[resampling_method],
            masked=True,
        )

        if expr is not None:
            rgb = expr.split(",")
            mask = np.array([arr.mask[0]])

            arr = dict(zip(bands_names, arr))
            arr = np.ma.array([
                np.nan_to_num(ne.evaluate(bloc.strip(), local_dict=arr))
                for bloc in rgb
            ],
                              mask=mask)

            # Mask infinite values (we're just calculating stats)
            arr.mask[arr > 1e+300] = True
            arr.mask[arr < -1e+300] = True

        params = {}
        if histogram_bins:
            params.update(dict(bins=histogram_bins))
        if histogram_range:
            params.update(dict(range=histogram_range))

        stats = {
            indexes[b]: _stats(arr[b], percentiles=percentiles, **params)
            for b in range(arr.shape[0])
            if vrt.colorinterp[b] != ColorInterp.alpha
        }

    return {
        "bounds": {
            "value": bounds,
            "crs":
            dst_crs.to_string() if isinstance(dst_crs, CRS) else dst_crs,
        },
        "minzoom": minzoom,
        "maxzoom": maxzoom,
        "band_descriptions": band_descriptions,
        "statistics": stats,
    }
Esempio n. 26
0
    def get(self,
            request,
            pk=None,
            project_pk=None,
            tile_type="",
            z="",
            x="",
            y="",
            scale=1):
        """
        Get a tile image
        """
        task = self.get_and_check_task(request, pk)

        z = int(z)
        x = int(x)
        y = int(y)

        if x == 0 and y == 0 and z == 0:
            raise exceptions.NotFound()

        scale = int(scale)
        ext = "png"
        driver = "jpeg" if ext == "jpg" else ext

        indexes = None
        nodata = None

        formula = self.request.query_params.get('formula')
        bands = self.request.query_params.get('bands')
        rescale = self.request.query_params.get('rescale')
        color_map = self.request.query_params.get('color_map')
        hillshade = self.request.query_params.get('hillshade')

        if formula == '': formula = None
        if bands == '': bands = None
        if rescale == '': rescale = None
        if color_map == '': color_map = None
        if hillshade == '' or hillshade == '0': hillshade = None

        try:
            expr, _ = lookup_formula(formula, bands)
        except ValueError as e:
            raise exceptions.ValidationError(str(e))

        if tile_type in ['dsm', 'dtm'] and rescale is None:
            rescale = "0,1000"

        if tile_type in ['dsm', 'dtm'] and color_map is None:
            color_map = "gray"

        if tile_type == 'orthophoto' and formula is not None:
            if color_map is None:
                color_map = "gray"
            if rescale is None:
                rescale = "-1,1"

        if nodata is not None:
            nodata = np.nan if nodata == "nan" else float(nodata)
        tilesize = scale * 256

        url = get_raster_path(task, tile_type)
        if not os.path.isfile(url):
            raise exceptions.NotFound()

        try:
            if expr is not None:
                tile, mask = expression(url,
                                        x,
                                        y,
                                        z,
                                        expr=expr,
                                        tilesize=tilesize,
                                        nodata=nodata)
            else:
                tile, mask = main.tile(url,
                                       x,
                                       y,
                                       z,
                                       indexes=indexes,
                                       tilesize=tilesize,
                                       nodata=nodata)
        except TileOutsideBounds:
            raise exceptions.NotFound("Outside of bounds")

        # Use alpha channel for transparency, don't use the mask if one is provided (redundant)
        if tile.shape[0] == 4:
            mask = None

        if color_map:
            try:
                color_map = get_colormap(color_map, format="gdal")
            except FileNotFoundError:
                raise exceptions.ValidationError("Not a valid color_map value")

        intensity = None

        if hillshade is not None:
            try:
                hillshade = float(hillshade)
                if hillshade <= 0:
                    hillshade = 1.0
                print(hillshade)
            except ValueError:
                raise exceptions.ValidationError("Invalid hillshade value")

            if tile.shape[0] != 1:
                raise exceptions.ValidationError(
                    "Cannot compute hillshade of non-elevation raster (multiple bands found)"
                )

            with rasterio.open(url) as src:
                minzoom, maxzoom = get_zooms(src)
                z_value = min(maxzoom, max(z, minzoom))
                delta_scale = (maxzoom + 1 - z_value) * 4
                dx = src.meta["transform"][0] * delta_scale
                dy = -src.meta["transform"][4] * delta_scale

            ls = LightSource(azdeg=315, altdeg=45)

            # Hillshading is not a local tile operation and
            # requires neighbor tiles to be rendered seamlessly
            elevation = get_elevation_tiles(tile[0], url, x, y, z, tilesize,
                                            nodata)
            intensity = ls.hillshade(elevation,
                                     dx=dx,
                                     dy=dy,
                                     vert_exag=hillshade)
            intensity = intensity[tilesize:tilesize * 2, tilesize:tilesize * 2]

        rgb, rmask = rescale_tile(tile, mask, rescale=rescale)
        rgb = apply_colormap(rgb, color_map)

        if intensity is not None:
            # Quick check
            if rgb.shape[0] != 3:
                raise exceptions.ValidationError(
                    "Cannot process tile: intensity image provided, but no RGB data was computed."
                )

            intensity = intensity * 255.0
            rgb = hsv_blend(rgb, intensity)

        options = img_profiles.get(driver, {})
        return HttpResponse(array_to_image(rgb,
                                           rmask,
                                           img_format=driver,
                                           **options),
                            content_type="image/{}".format(ext))