Exemplo n.º 1
0
    def worker(band: str):
        asset = f"{landsat_prefix}_B{band}.TIF"

        if band == "QA":
            nodata = 1
            resamp = "nearest"
        else:
            nodata = 0
            resamp = "bilinear"

        with rasterio.open(asset) as src_dst:
            bounds = transform_bounds(
                src_dst.crs, constants.WGS84_CRS, *src_dst.bounds, densify_pts=21
            )
            data, mask = reader.preview(
                src_dst, nodata=nodata, resampling_method=resamp, **kwargs
            )

        if band != "QA":
            data = data.astype("float32", casting="unsafe")
            data = _convert(data, band, meta)

        data = numpy.ma.array(data)
        data.mask = mask == 0

        statistics = raster_stats(data, percentiles=(pmin, pmax), **hist_options)
        return dict(bounds=bounds, statistics=statistics)
Exemplo n.º 2
0
    async def stats(
        self,
        pmin: float = 2.0,
        pmax: float = 98.0,
        hist_options: Optional[Dict] = None,
        indexes: Optional[Union[Sequence[int], int]] = None,
        width: Optional[int] = None,
        height: Optional[int] = None,
        max_size: int = 1024,
        bounds: Optional[Tuple[float, float, float, float]] = None,
        bounds_crs: CRS = CRS.from_epsg(4326),
        resampling_method: str = "nearest",
    ) -> Dict[str, ImageStatistics]:
        """stats"""

        hist_options = hist_options or {}

        if self.colormap and not "bins" not in hist_options:
            hist_options["bins"] = [
                k for k, v in self.colormap.items() if v != (0, 0, 0, 255)
            ]

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

        if indexes is None:
            indexes = [
                idx for idx, b in enumerate(self.color_interp) if b != ColorInterp.alpha
            ]
            if len(indexes) != self.profile["count"]:
                warnings.warn("Alpha band was removed from the output data array")
            indexes = range(self.profile["count"])

        if bounds:
            resp = await self.part(
                bounds,
                bbox_crs=bounds_crs,
                width=width,
                height=height,
                resampling_method=resampling_method,
            )
        else:
            resp = await self.preview(
                width=width,
                height=height,
                max_size=max_size,
                resampling_method=resampling_method,
            )

        data = np.ma.array(resp.data)

        return {
            str(indexes[b]): ImageStatistics.parse_obj(
                raster_stats(data[b], percentiles=(pmin, pmax), **hist_options)
            )
            for b in range(data.shape[0])
        }
Exemplo n.º 3
0
def metadata(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    bounds: Optional[Tuple[float, float, float, float]] = None,
    indexes: Optional[Union[Sequence[int], int]] = None,
    max_size: int = 1024,
    bounds_crs: CRS = constants.WGS84_CRS,
    percentiles: Tuple[float, float] = (2.0, 98.0),
    hist_options: Dict = {},
    **kwargs: Any,
) -> Dict:
    """
    Retrieve metadata and statistics from an image.

    Attributes
    ----------
        src_dst : rasterio.io.DatasetReader
            rasterio.io.DatasetReader object
        bounds : tuple, optional
            Bounding box coordinates from which to calculate image statistics.
        max_size : int
            `max_size` of the longest dimension, respecting
            bounds X/Y aspect ratio.
        indexes : list of ints or a single int, optional
            Band indexes.
        bounds_crs: CRS or str, optional
            Specify bounds coordinate reference system, default WGS84/EPSG4326.
        percentiles: tuple, optional
            Tuple of Min/Max percentiles to compute. Default is (2, 98).
        hist_options : dict, optional
            Options to forward to numpy.histogram function.
        kwargs : Any, optional
            Additional options to forward to part or preview

    Returns
    -------
        dict

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

    if indexes is None:
        indexes = non_alpha_indexes(src_dst)
        if indexes != src_dst.indexes:
            warnings.warn("Alpha band was removed from the output data array",
                          AlphaBandWarning)

    if bounds:
        data, mask = part(
            src_dst,
            bounds,
            max_size=max_size,
            indexes=indexes,
            bounds_crs=bounds_crs,
            **kwargs,
        )
        bounds = transform_bounds(bounds_crs,
                                  constants.WGS84_CRS,
                                  *bounds,
                                  densify_pts=21)

    else:
        data, mask = preview(src_dst,
                             max_size=max_size,
                             indexes=indexes,
                             **kwargs)
        bounds = transform_bounds(src_dst.crs,
                                  constants.WGS84_CRS,
                                  *src_dst.bounds,
                                  densify_pts=21)

    data = numpy.ma.array(data)
    data.mask = mask == 0

    statistics = {
        indexes[b]: raster_stats(data[b],
                                 percentiles=percentiles,
                                 **hist_options)
        for b in range(data.shape[0])
    }

    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]
    tags = [(ix, src_dst.tags(ix)) for ix in 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(
        bounds=bounds,
        statistics=statistics,
        band_metadata=tags,
        band_descriptions=band_descriptions,
        dtype=src_dst.meta["dtype"],
        colorinterp=[src_dst.colorinterp[ix - 1].name for ix in indexes],
        nodata_type=nodata_type,
        **other_meta,
    )
Exemplo n.º 4
0
def stats(
    src_dst: Union[DatasetReader, DatasetWriter, WarpedVRT],
    bounds: Optional[Tuple[float, float, float, float]] = None,
    indexes: Optional[Union[Sequence[int], int]] = None,
    max_size: int = 1024,
    bounds_crs: CRS = constants.WGS84_CRS,
    percentiles: Tuple[float, float] = (2.0, 98.0),
    hist_options: Dict = {},
    **kwargs: Any,
) -> Dict:
    """
    Retrieve statistics from an image.

    Attributes
    ----------
    src_dst : rasterio.io.DatasetReader
        rasterio.io.DatasetReader object
    bounds : tuple, optional
        Bounding box coordinates from which to calculate image statistics.
    max_size : int
        `max_size` of the longest dimension, respecting
        bounds X/Y aspect ratio.
    indexes : list of ints or a single int, optional
        Band indexes.
    bounds_crs: CRS or str, optional
        Specify bounds coordinate reference system, default WGS84/EPSG4326.
    percentiles: tuple, optional
        Tuple of Min/Max percentiles to compute. Default is (2, 98).
    hist_options : dict, optional
        Options to forward to numpy.histogram function.
    kwargs : Any, optional
        Additional options to forward to part or preview

    Returns
    -------
    dict

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

    if indexes is None:
        indexes = non_alpha_indexes(src_dst)
        if indexes != src_dst.indexes:
            warnings.warn("Alpha band was removed from the output data array",
                          AlphaBandWarning)

    if bounds:
        data, mask = part(
            src_dst,
            bounds,
            max_size=max_size,
            indexes=indexes,
            bounds_crs=bounds_crs,
            **kwargs,
        )
    else:
        data, mask = preview(src_dst,
                             max_size=max_size,
                             indexes=indexes,
                             **kwargs)

    data = numpy.ma.array(data)
    data.mask = mask == 0

    return {
        indexes[b]: raster_stats(data[b],
                                 percentiles=percentiles,
                                 **hist_options)
        for b in range(data.shape[0])
    }
Exemplo n.º 5
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)