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)
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]) }
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, )
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]) }
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)