Merge pull request #1068 from AIDI-solar/cutline

Added support of getting tiles and metadata with geoJSON polygon specified at request param
pull/1077/head
Piero Toffanin 2021-10-08 12:03:39 -04:00 zatwierdzone przez GitHub
commit 5ae17b1e54
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
1 zmienionych plików z 47 dodań i 14 usunięć

Wyświetl plik

@ -2,12 +2,14 @@ import json
import numpy import numpy
import rio_tiler.utils import rio_tiler.utils
from rasterio.enums import ColorInterp from rasterio.enums import ColorInterp
from rasterio.crs import CRS
from rasterio.features import bounds as featureBounds
import urllib import urllib
import os import os
from django.http import HttpResponse from django.http import HttpResponse
from rio_tiler.errors import TileOutsideBounds from rio_tiler.errors import TileOutsideBounds
from rio_tiler.utils import has_alpha_band, \ from rio_tiler.utils import has_alpha_band, \
non_alpha_indexes, render, mapzen_elevation_rgb non_alpha_indexes, render, create_cutline
from rio_tiler.utils import _stats as raster_stats from rio_tiler.utils import _stats as raster_stats
from rio_tiler.models import ImageStatistics, ImageData from rio_tiler.models import ImageStatistics, ImageData
from rio_tiler.models import Metadata as RioMetadata from rio_tiler.models import Metadata as RioMetadata
@ -121,10 +123,13 @@ class Metadata(TaskNestedView):
formula = self.request.query_params.get('formula') formula = self.request.query_params.get('formula')
bands = self.request.query_params.get('bands') bands = self.request.query_params.get('bands')
defined_range = self.request.query_params.get('range') defined_range = self.request.query_params.get('range')
boundaries_feature = self.request.query_params.get('boundaries')
if formula == '': formula = None if formula == '': formula = None
if bands == '': bands = None if bands == '': bands = None
if defined_range == '': defined_range = 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: try:
expr, hrange = lookup_formula(formula, bands) expr, hrange = lookup_formula(formula, bands)
if defined_range is not None: if defined_range is not None:
@ -144,27 +149,36 @@ class Metadata(TaskNestedView):
try: try:
with COGReader(raster_path) as src: with COGReader(raster_path) as src:
band_count = src.metadata()['count'] band_count = src.metadata()['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): if has_alpha_band(src.dataset):
band_count -= 1 band_count -= 1
nodata = None nodata = None
# Workaround for https://github.com/OpenDroneMap/WebODM/issues/894 # Workaround for https://github.com/OpenDroneMap/WebODM/issues/894
if tile_type == 'orthophoto': if tile_type == 'orthophoto':
nodata = 0 nodata = 0
# info = src.metadata(pmin=pmin, pmax=pmax, histogram_bins=255, histogram_range=hrange, expr=expr,
# nodata=nodata)
histogram_options = {"bins": 255, "range": hrange} histogram_options = {"bins": 255, "range": hrange}
if expr is not None: 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, mask = src.preview(expression=expr)
data = numpy.ma.array(data) data = numpy.ma.array(data)
data.mask = mask == 0 data.mask = mask == 0
expression_bloc = expr.lower().split(",")
stats = { stats = {
str(b + 1): raster_stats(data[b], percentiles=(pmin, pmax), bins=255, range=hrange) str(b + 1): raster_stats(data[b], percentiles=(pmin, pmax), bins=255, range=hrange)
for b in range(data.shape[0]) for b in range(data.shape[0])
} }
stats = {b: ImageStatistics(**s) for b, s in stats.items()} stats = {b: ImageStatistics(**s) for b, s in stats.items()}
metadata = RioMetadata(statistics=stats, **src.info().dict()) 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: else:
metadata = src.metadata(pmin=pmin, pmax=pmax, hist_options=histogram_options, nodata=nodata) metadata = src.metadata(pmin=pmin, pmax=pmax, hist_options=histogram_options, nodata=nodata)
info = json.loads(metadata.json()) info = json.loads(metadata.json())
@ -202,7 +216,6 @@ class Metadata(TaskNestedView):
info['color_maps'] = [] info['color_maps'] = []
info['algorithms'] = algorithms info['algorithms'] = algorithms
if colormaps: if colormaps:
for cmap in colormaps: for cmap in colormaps:
try: try:
@ -282,6 +295,12 @@ class Tiles(TaskNestedView):
rescale = self.request.query_params.get('rescale') rescale = self.request.query_params.get('rescale')
color_map = self.request.query_params.get('color_map') color_map = self.request.query_params.get('color_map')
hillshade = self.request.query_params.get('hillshade') hillshade = self.request.query_params.get('hillshade')
boundaries_feature = self.request.query_params.get('boundaries')
if boundaries_feature == '':
boundaries_feature = None
if boundaries_feature is not None:
boundaries_feature = json.loads(boundaries_feature)
if formula == '': formula = None if formula == '': formula = None
if bands == '': bands = None if bands == '': bands = None
@ -324,6 +343,10 @@ class Tiles(TaskNestedView):
has_alpha = has_alpha_band(src.dataset) has_alpha = has_alpha_band(src.dataset)
if z < minzoom - ZOOM_EXTRA_LEVELS or z > maxzoom + ZOOM_EXTRA_LEVELS: if z < minzoom - ZOOM_EXTRA_LEVELS or z > maxzoom + ZOOM_EXTRA_LEVELS:
raise exceptions.NotFound() raise exceptions.NotFound()
if boundaries_feature is not None:
boundaries_cutline = create_cutline(src.dataset, boundaries_feature, CRS.from_string('EPSG:4326'))
else:
boundaries_cutline = None
# Handle N-bands datasets for orthophotos (not plant health) # Handle N-bands datasets for orthophotos (not plant health)
if tile_type == 'orthophoto' and expr is None: if tile_type == 'orthophoto' and expr is None:
ci = src.dataset.colorinterp ci = src.dataset.colorinterp
@ -355,9 +378,19 @@ class Tiles(TaskNestedView):
try: try:
with COGReader(url) as src: with COGReader(url) as src:
if expr is not None: if expr is not None:
if boundaries_cutline is not None:
tile = src.tile(x, y, z, expression=expr, tilesize=tilesize, nodata=nodata,
padding=padding,
resampling_method=resampling, vrt_options={'cutline': boundaries_cutline})
else:
tile = src.tile(x, y, z, expression=expr, tilesize=tilesize, nodata=nodata, tile = src.tile(x, y, z, expression=expr, tilesize=tilesize, nodata=nodata,
padding=padding, padding=padding,
resampling_method=resampling) resampling_method=resampling)
else:
if boundaries_cutline is not None:
tile = src.tile(x, y, z, tilesize=tilesize, nodata=nodata,
padding=padding,
resampling_method=resampling, vrt_options={'cutline': boundaries_cutline})
else: else:
tile = src.tile(x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata, tile = src.tile(x, y, z, indexes=indexes, tilesize=tilesize, nodata=nodata,
padding=padding, resampling_method=resampling) padding=padding, resampling_method=resampling)