Esempio n. 1
0
def test_cutline():
    """Test rio_tiler.utils.create_cutline."""
    feat = {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type":
            "Polygon",
            "coordinates": [[
                [-52.6025390625, 73.86761239709705],
                [-52.6025390625, 73.59679245247814],
                [-51.591796875, 73.60299628304274],
                [-51.591796875, 73.90420357134279],
                [-52.4267578125, 74.0437225981325],
                [-52.6025390625, 73.86761239709705],
            ]],
        },
    }

    feature_bounds = featureBounds(feat)

    with COGReader(COGEO) as cog:
        cutline = utils.create_cutline(cog.dataset,
                                       feat,
                                       geometry_crs="epsg:4326")
        data, mask = cog.part(feature_bounds, vrt_options={"cutline": cutline})
        assert not mask.all()

        cutline = utils.create_cutline(cog.dataset,
                                       feat["geometry"],
                                       geometry_crs="epsg:4326")
        data, mask = cog.part(feature_bounds, vrt_options={"cutline": cutline})
        assert not mask.all()

    feat_line = {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type":
            "LineString",
            "coordinates": [
                [-55.37109374999999, 74.17607298699065],
                [-53.85498046874999, 75.06734898853098],
                [-54.16259765625, 75.11822201684025],
                [-54.228515625, 75.23066741281573],
            ],
        },
    }

    with COGReader(COGEO) as cog:
        with pytest.raises(RioTilerError):
            utils.create_cutline(cog.dataset,
                                 feat_line,
                                 geometry_crs="epsg:4326")
Esempio n. 2
0
    def __enter__(self):
        """Support using with Context Managers."""
        self.scene_params = s1_sceneid_parser(self.sceneid)

        prefix = self._prefix.format(**self.scene_params)
        self.productInfo = json.loads(
            get_object(self._hostname,
                       f"{prefix}/productInfo.json",
                       request_pays=True))
        self.datageom = self.productInfo["footprint"]
        self.bounds = featureBounds(self.datageom)
        return self
Esempio n. 3
0
    def feature(
        self,
        shape: Dict,
        dst_crs: Optional[CRS] = None,
        shape_crs: CRS = WGS84_CRS,
        indexes: Optional[Indexes] = None,
        expression: Optional[str] = None,
        max_size: Optional[int] = None,
        height: Optional[int] = None,
        width: Optional[int] = None,
        **kwargs: Any,
    ) -> ImageData:
        """Read part of a COG defined by a geojson feature.

        Args:
            shape (dict): Valid GeoJSON feature.
            dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system.
            shape_crs (rasterio.crs.CRS, optional): Input geojson coordinate reference system. Defaults to `epsg:4326`.
            indexes (sequence of int or int, optional): Band indexes.
            expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
            max_size (int, optional): Limit the size of the longest dimension of the dataset read, respecting bounds X/Y aspect ratio.
            height (int, optional): Output height of the array.
            width (int, optional): Output width of the array.
            kwargs (optional): Options to forward to the `COGReader.part` method.

        Returns:
            rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info.

        """
        if not dst_crs:
            dst_crs = shape_crs

        # Get BBOX of the polygon
        bbox = featureBounds(shape)

        cutline = create_cutline(self.dataset, shape, geometry_crs=shape_crs)
        vrt_options = kwargs.pop("vrt_options", {})
        vrt_options.update({"cutline": cutline})

        return self.part(
            bbox,
            dst_crs=dst_crs,
            bounds_crs=shape_crs,
            indexes=indexes,
            expression=expression,
            max_size=max_size,
            width=width,
            height=height,
            vrt_options=vrt_options,
            **kwargs,
        )
Esempio n. 4
0
    def __enter__(self):
        """Support using with Context Managers."""
        self.scene_params = s2_sceneid_parser(self.sceneid)

        prefix = self._prefix.format(**self.scene_params)
        self.tileInfo = json.loads(
            get_object(self._hostname,
                       f"{prefix}/tileInfo.json",
                       request_pays=True))
        input_geom = self.tileInfo["tileDataGeometry"]
        input_crs = CRS.from_user_input(
            input_geom["crs"]["properties"]["name"])
        self.datageom = transform_geom(input_crs, constants.WGS84_CRS,
                                       input_geom)
        self.bounds = featureBounds(self.datageom)
        return self
Esempio n. 5
0
    def feature(
        self,
        shape: Dict,
        dst_crs: Optional[CRS] = None,
        shape_crs: CRS = constants.WGS84_CRS,
        max_size: int = 1024,
        indexes: Optional[Union[int, Sequence]] = None,
        expression: Optional[str] = "",
        **kwargs: Any,
    ) -> ImageData:
        """
        Read a COG for a geojson feature.

        Attributes
        ----------
        shape: dict
            Valid GeoJSON feature.
        dst_crs: CRS or str, optional
            Target coordinate reference system, default is the bbox CRS.
        shape_crs: CRS or str, optional
            shape coordinate reference system, default is "epsg:4326"
        max_size: int, optional
            Limit output size array, default is 1024.
        indexes: int or sequence of int
            Band indexes (e.g. 1 or (1, 2, 3))
        expression: str
            rio-tiler expression (e.g. b1/b2+b3)
        kwargs: dict, optional
            These will be passed to the 'rio_tiler.reader.part' function.

        Returns
        -------
        data: numpy ndarray
        mask: numpy array

        """
        kwargs = {**self._kwargs, **kwargs}

        if isinstance(indexes, int):
            indexes = (indexes,)

        if indexes and expression:
            warnings.warn(
                "Both expression and indexes passed; expression will overwrite indexes parameter.",
                ExpressionMixingWarning,
            )

        if expression:
            indexes = parse_expression(expression)

        if not dst_crs:
            dst_crs = shape_crs

        # Get BBOX of the polygon
        bbox = featureBounds(shape)

        cutline = create_cutline(self.dataset, shape, geometry_crs=shape_crs)
        vrt_options = kwargs.pop("vrt_options", {})
        vrt_options.update({"cutline": cutline})

        data, mask = reader.part(
            self.dataset,
            bbox,
            max_size=max_size,
            bounds_crs=shape_crs,
            dst_crs=dst_crs,
            indexes=indexes,
            vrt_options=vrt_options,
            **kwargs,
        )

        if expression:
            blocks = expression.lower().split(",")
            bands = [f"b{bidx}" for bidx in indexes]
            data = apply_expression(blocks, bands, data)

        if dst_crs == shape_crs:
            bounds = bbox
        else:
            bounds = transform_bounds(shape_crs, dst_crs, *bbox, densify_pts=21)

        return ImageData(
            data, mask, bounds=bounds, crs=dst_crs, assets=[self.filepath],
        )
Esempio n. 6
0
    def get(self, request, pk=None, project_pk=None, tile_type=""):
        """
        Get the metadata for this tasks's asset type
        """
        task = self.get_and_check_task(request, pk)
        formula = self.request.query_params.get('formula')
        bands = self.request.query_params.get('bands')
        defined_range = self.request.query_params.get('range')
        boundaries_feature = self.request.query_params.get('boundaries')
        if formula == '': formula = None
        if bands == '': bands = None
        if defined_range == '': defined_range = None
        if boundaries_feature == '': boundaries_feature = None
        if boundaries_feature is not None:
            boundaries_feature = json.loads(boundaries_feature)
        try:
            expr, hrange = lookup_formula(formula, bands)
            if defined_range is not None:
                new_range = tuple(map(float, defined_range.split(",")[:2]))
                #Validate rescaling range
                if hrange is not None and (new_range[0] < hrange[0]
                                           or new_range[1] > hrange[1]):
                    pass
                else:
                    hrange = new_range

        except ValueError as e:
            raise exceptions.ValidationError(str(e))
        pmin, pmax = 2.0, 98.0
        raster_path = get_raster_path(task, tile_type)
        if not os.path.isfile(raster_path):
            raise exceptions.NotFound()
        try:
            with COGReader(raster_path) as src:
                band_count = src.dataset.meta['count']
                if boundaries_feature is not None:
                    boundaries_cutline = create_cutline(
                        src.dataset, boundaries_feature,
                        CRS.from_string('EPSG:4326'))
                    boundaries_bbox = featureBounds(boundaries_feature)
                else:
                    boundaries_cutline = None
                    boundaries_bbox = None
                if has_alpha_band(src.dataset):
                    band_count -= 1
                nodata = None
                # Workaround for https://github.com/OpenDroneMap/WebODM/issues/894
                if tile_type == 'orthophoto':
                    nodata = 0
                histogram_options = {"bins": 255, "range": hrange}
                if expr is not None:
                    if boundaries_cutline is not None:
                        data, mask = src.preview(
                            expression=expr,
                            vrt_options={'cutline': boundaries_cutline})
                    else:
                        data, mask = src.preview(expression=expr)
                    data = numpy.ma.array(data)
                    data.mask = mask == 0
                    stats = {
                        str(b + 1): raster_stats(data[b],
                                                 percentiles=(pmin, pmax),
                                                 bins=255,
                                                 range=hrange)
                        for b in range(data.shape[0])
                    }
                    stats = {b: ImageStatistics(**s) for b, s in stats.items()}
                    metadata = RioMetadata(statistics=stats,
                                           **src.info().dict())
                else:
                    if (boundaries_cutline is not None) and (boundaries_bbox
                                                             is not None):
                        metadata = src.metadata(
                            pmin=pmin,
                            pmax=pmax,
                            hist_options=histogram_options,
                            nodata=nodata,
                            bounds=boundaries_bbox,
                            vrt_options={'cutline': boundaries_cutline})
                    else:
                        metadata = src.metadata(pmin=pmin,
                                                pmax=pmax,
                                                hist_options=histogram_options,
                                                nodata=nodata)
                info = json.loads(metadata.json())
        except IndexError as e:
            # Caught when trying to get an invalid raster metadata
            raise exceptions.ValidationError(
                "Cannot retrieve raster metadata: %s" % str(e))
        # Override min/max
        if hrange:
            for b in info['statistics']:
                info['statistics'][b]['min'] = hrange[0]
                info['statistics'][b]['max'] = hrange[1]

        cmap_labels = {
            "viridis": "Viridis",
            "jet": "Jet",
            "terrain": "Terrain",
            "gist_earth": "Earth",
            "rdylgn": "RdYlGn",
            "rdylgn_r": "RdYlGn (Reverse)",
            "spectral": "Spectral",
            "spectral_r": "Spectral (Reverse)",
            "discrete_ndvi": "Contrast NDVI",
            "better_discrete_ndvi": "Custom NDVI Index",
            "rplumbo": "Rplumbo (Better NDVI)",
            "pastel1": "Pastel",
        }

        colormaps = []
        algorithms = []
        if tile_type in ['dsm', 'dtm']:
            colormaps = ['viridis', 'jet', 'terrain', 'gist_earth', 'pastel1']
        elif formula and bands:
            colormaps = [
                'rdylgn', 'spectral', 'rdylgn_r', 'spectral_r', 'rplumbo',
                'discrete_ndvi', 'better_discrete_ndvi'
            ]
            algorithms = *get_algorithm_list(band_count),

        info['color_maps'] = []
        info['algorithms'] = algorithms
        if colormaps:
            for cmap in colormaps:
                try:
                    info['color_maps'].append({
                        'key':
                        cmap,
                        'color_map':
                        colormap.get(cmap).values(),
                        'label':
                        cmap_labels.get(cmap, cmap)
                    })
                except FileNotFoundError:
                    raise exceptions.ValidationError(
                        "Not a valid color_map value: %s" % cmap)

        info['name'] = task.name
        info['scheme'] = 'xyz'
        info['tiles'] = [
            get_tile_url(task, tile_type, self.request.query_params)
        ]

        if info['maxzoom'] < info['minzoom']:
            info['maxzoom'] = info['minzoom']
        info['maxzoom'] += ZOOM_EXTRA_LEVELS
        info['minzoom'] -= ZOOM_EXTRA_LEVELS
        info['bounds'] = {'value': src.bounds, 'crs': src.dataset.crs}
        return Response(info)
Esempio n. 7
0
def test_cutline():
    """Test rio_tiler.utils.create_cutline."""
    feat = {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type":
            "Polygon",
            "coordinates": [[
                [-52.6025390625, 73.86761239709705],
                [-52.6025390625, 73.59679245247814],
                [-51.591796875, 73.60299628304274],
                [-51.591796875, 73.90420357134279],
                [-52.4267578125, 74.0437225981325],
                [-52.6025390625, 73.86761239709705],
            ]],
        },
    }

    feature_bounds = featureBounds(feat)

    with COGReader(COGEO) as cog:
        cutline = utils.create_cutline(cog.dataset,
                                       feat,
                                       geometry_crs="epsg:4326")
        data, mask = cog.part(feature_bounds, vrt_options={"cutline": cutline})
        assert not mask.all()

        cutline = utils.create_cutline(cog.dataset,
                                       feat["geometry"],
                                       geometry_crs="epsg:4326")
        data, mask = cog.part(feature_bounds, vrt_options={"cutline": cutline})
        assert not mask.all()

    feat_line = {
        "type": "Feature",
        "properties": {},
        "geometry": {
            "type":
            "LineString",
            "coordinates": [
                [-55.37109374999999, 74.17607298699065],
                [-53.85498046874999, 75.06734898853098],
                [-54.16259765625, 75.11822201684025],
                [-54.228515625, 75.23066741281573],
            ],
        },
    }

    with COGReader(COGEO) as cog:
        with pytest.raises(RioTilerError):
            utils.create_cutline(cog.dataset,
                                 feat_line,
                                 geometry_crs="epsg:4326")

    feat_mp = {
        "type":
        "MultiPolygon",
        "coordinates": [
            [[
                [7.305908203125, 52.14697334064471],
                [7.84423828125, 52.14697334064471],
                [7.84423828125, 52.52958999943304],
                [7.305908203125, 52.52958999943304],
                [7.305908203125, 52.14697334064471],
            ]],
            [[
                [9.920654296875, 53.25206880589411],
                [10.404052734375, 53.25206880589411],
                [10.404052734375, 53.48804553605622],
                [9.920654296875, 53.48804553605622],
                [9.920654296875, 53.25206880589411],
            ]],
        ],
    }

    with COGReader(COGEO) as cog:
        c = utils.create_cutline(cog.dataset,
                                 feat_mp,
                                 geometry_crs="epsg:4326")
        assert "MULTIPOLYGON" in c

    bad_poly = {
        "type":
        "Polygon",
        "coordinates": [
            [[
                [7.305908203125, 52.14697334064471],
                [7.84423828125, 52.14697334064471],
                [7.84423828125, 52.52958999943304],
                [7.305908203125, 52.52958999943304],
                [7.305908203125, 52.14697334064471],
            ]],
        ],
    }

    with COGReader(COGEO) as cog:
        with pytest.raises(RioTilerError):
            utils.create_cutline(cog.dataset,
                                 bad_poly,
                                 geometry_crs="epsg:4326")
Esempio n. 8
0
    def feature(
        self,
        shape: Dict,
        dst_crs: Optional[CRS] = None,
        shape_crs: CRS = constants.WGS84_CRS,
        max_size: int = 1024,
        indexes: Optional[Union[int, Sequence]] = None,
        expression: Optional[str] = None,
        **kwargs: Any,
    ) -> ImageData:
        """Read part of a COG defined by a geojson feature.

        Args:
            shape (dict): Valid GeoJSON feature.
            dst_crs (rasterio.crs.CRS, optional): Overwrite target coordinate reference system.
            shape_crs (rasterio.crs.CRS, optional): Input geojson coordinate reference system. Defaults to `epsg:4326`.
            max_size (int, optional): Limit the size of the longest dimension of the dataset read, respecting bounds X/Y aspect ratio. Defaults to `1024`.
            indexes (sequence of int or int, optional): Band indexes.
            expression (str, optional): rio-tiler expression (e.g. b1/b2+b3).
            kwargs (optional): Options to forward to the `rio_tiler.reader.part` function.

        Returns:
            rio_tiler.models.ImageData: ImageData instance with data, mask and input spatial info.

        """
        kwargs = {**self._kwargs, **kwargs}

        if isinstance(indexes, int):
            indexes = (indexes, )

        if indexes and expression:
            warnings.warn(
                "Both expression and indexes passed; expression will overwrite indexes parameter.",
                ExpressionMixingWarning,
            )

        if expression:
            indexes = parse_expression(expression)

        if not dst_crs:
            dst_crs = shape_crs

        # Get BBOX of the polygon
        bbox = featureBounds(shape)

        cutline = create_cutline(self.dataset, shape, geometry_crs=shape_crs)
        vrt_options = kwargs.pop("vrt_options", {})
        vrt_options.update({"cutline": cutline})

        data, mask = reader.part(
            self.dataset,
            bbox,
            max_size=max_size,
            bounds_crs=shape_crs,
            dst_crs=dst_crs,
            indexes=indexes,
            vrt_options=vrt_options,
            **kwargs,
        )

        if expression:
            blocks = expression.lower().split(",")
            bands = [f"b{bidx}" for bidx in indexes]
            data = apply_expression(blocks, bands, data)

        if dst_crs == shape_crs:
            bounds = bbox
        else:
            bounds = transform_bounds(shape_crs,
                                      dst_crs,
                                      *bbox,
                                      densify_pts=21)

        return ImageData(
            data,
            mask,
            bounds=bounds,
            crs=dst_crs,
            assets=[self.filepath],
        )
Esempio n. 9
0
def get_grid_bbox(name: str) -> Tuple[float, float, float, float]:
    """Get grid bbox."""
    feat = list(
        filter(lambda x: x["properties"]["name"] == name, mgrs_grid["features"])
    )[0]
    return featureBounds(feat["geometry"])