kopia lustrzana https://github.com/OpenDroneMap/WebODM
Tiler metadata unit tests
rodzic
2bbcefa77a
commit
fdebc8f157
|
@ -135,7 +135,10 @@ class Metadata(TaskNestedView):
|
|||
if formula == '': formula = None
|
||||
if bands == '': bands = None
|
||||
|
||||
expr, hrange = lookup_formula(formula, bands)
|
||||
try:
|
||||
expr, hrange = lookup_formula(formula, bands)
|
||||
except ValueError as e:
|
||||
raise exceptions.ValidationError(str(e))
|
||||
|
||||
pmin, pmax = 2.0, 98.0
|
||||
raster_path = get_raster_path(task, tile_type)
|
||||
|
@ -167,13 +170,16 @@ class Metadata(TaskNestedView):
|
|||
}
|
||||
|
||||
colormaps = []
|
||||
algorithms = []
|
||||
if tile_type in ['dsm', 'dtm']:
|
||||
colormaps = ['jet_r', 'terrain', 'gist_earth', 'pastel1']
|
||||
elif formula and bands:
|
||||
colormaps = ['rdylgn', 'spectral', 'rdylgn_r', 'spectral_r']
|
||||
info['algorithms'] = *get_algorithm_list(),
|
||||
algorithms = *get_algorithm_list(),
|
||||
|
||||
info['color_maps'] = []
|
||||
info['algorithms'] = algorithms
|
||||
|
||||
if colormaps:
|
||||
for cmap in colormaps:
|
||||
try:
|
||||
|
@ -257,7 +263,10 @@ class Tiles(TaskNestedView):
|
|||
if color_map == '': color_map = None
|
||||
if hillshade == '' or hillshade == '0': hillshade = None
|
||||
|
||||
expr, _ = lookup_formula(formula, bands)
|
||||
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"
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import io
|
||||
import os
|
||||
import time
|
||||
|
||||
|
@ -16,6 +17,7 @@ import worker
|
|||
from django.utils import timezone
|
||||
|
||||
from app import pending_actions
|
||||
from app.api.formulas import algos
|
||||
from app.models import Project, Task, ImageUpload
|
||||
from app.models.task import task_directory_path, full_task_directory_path, TaskInterruptedException
|
||||
from app.plugins.signals import task_completed, task_removed, task_removing
|
||||
|
@ -235,11 +237,13 @@ class TestApiTask(BootTransactionTestCase):
|
|||
# No processing node is set
|
||||
self.assertTrue(task.processing_node is None)
|
||||
|
||||
# tiles.json should not be accessible at this point
|
||||
# tiles.json, bounds, metadata should not be accessible at this point
|
||||
tile_types = ['orthophoto', 'dsm', 'dtm']
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/tiles.json".format(project.id, task.id, tile_type))
|
||||
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
|
||||
endpoints = ['tiles.json', 'bounds', 'metadata']
|
||||
for ep in endpoints:
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/{}".format(project.id, task.id, tile_type, ep))
|
||||
self.assertEqual(res.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
# Neither should an individual tile
|
||||
# Z/X/Y coords are chosen based on node-odm test dataset for orthophoto_tiles/
|
||||
|
@ -345,22 +349,128 @@ class TestApiTask(BootTransactionTestCase):
|
|||
res = client.get("/api/projects/{}/tasks/{}/assets/odm_orthophoto/odm_orthophoto.tif".format(project.id, task.id))
|
||||
self.assertTrue(res.status_code == status.HTTP_200_OK)
|
||||
|
||||
# Can access tiles.json
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/tiles.json".format(project.id, task.id, tile_type))
|
||||
self.assertTrue(res.status_code == status.HTTP_200_OK)
|
||||
# Can access tiles.json, bounds and metadata
|
||||
for ep in endpoints:
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/{}".format(project.id, task.id, tile_type, ep))
|
||||
self.assertTrue(res.status_code == status.HTTP_200_OK)
|
||||
|
||||
# Bounds are what we expect them to be
|
||||
# (4 coords in lat/lon)
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/tiles.json".format(project.id, task.id))
|
||||
tiles = json.loads(res.content.decode("utf-8"))
|
||||
self.assertTrue(len(tiles['bounds']) == 4)
|
||||
self.assertTrue(round(tiles['bounds'][0], 7) == -91.9945132)
|
||||
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/bounds".format(project.id, task.id))
|
||||
bounds = json.loads(res.content.decode("utf-8"))
|
||||
self.assertTrue(len(bounds['bounds']) == 4)
|
||||
self.assertTrue(round(bounds['bounds'][0], 7) == -91.9945132)
|
||||
|
||||
# Metadata checks for orthophoto
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/metadata".format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
metadata = json.loads(res.content.decode("utf-8"))
|
||||
fields = ['bounds', 'minzoom', 'maxzoom', 'statistics', 'algorithms', 'color_maps', 'tiles', 'scheme', 'name']
|
||||
for f in fields:
|
||||
self.assertTrue(f in metadata)
|
||||
|
||||
# Colormaps and algorithms should be empty lists
|
||||
self.assertEqual(metadata['algorithms'], [])
|
||||
self.assertEqual(metadata['color_maps'], [])
|
||||
|
||||
# Address key is removed
|
||||
self.assertFalse('address' in metadata)
|
||||
|
||||
# Scheme is xyz
|
||||
self.assertEqual(metadata['scheme'], 'xyz')
|
||||
|
||||
# Tiles URL has no extra params
|
||||
self.assertTrue(metadata['tiles'][0].endswith('.png'))
|
||||
|
||||
# Histogram stats are available (3 bands for orthophoto)
|
||||
self.assertTrue(len(metadata['statistics']) == 3)
|
||||
for b in ['1', '2', '3']:
|
||||
self.assertEqual(len(metadata['statistics'][b]['histogram']), 255) # bins
|
||||
self.assertEqual(metadata['statistics'][b]['min'], 0)
|
||||
self.assertEqual(metadata['statistics'][b]['max'], 255)
|
||||
|
||||
# Metadata with invalid formula
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/metadata?formula=INVALID".format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Metadata with a valid formula but invalid bands
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/metadata?formula=NDVI&bands=ABC".format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
# Medatata with valid formula and bands
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/metadata?formula=NDVI&bands=RGN".format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
metadata = json.loads(res.content.decode("utf-8"))
|
||||
|
||||
# Colormaps and algorithms are populated
|
||||
self.assertTrue(len(metadata['algorithms']) > 0)
|
||||
self.assertTrue(len(metadata['color_maps']) > 0)
|
||||
|
||||
# Colormap is for algorithms
|
||||
self.assertTrue('rdylgn' in metadata['color_maps'])
|
||||
self.assertFalse('jet_r' in metadata['color_maps'])
|
||||
|
||||
# Formula parameters are copied to tile URL
|
||||
self.assertTrue(metadata['tiles'][0].endswith('?formula=NDVI&bands=RGN'))
|
||||
|
||||
# Histogram stats are available (1 band)
|
||||
self.assertTrue(len(metadata['statistics']) == 1)
|
||||
|
||||
# Medatata with valid formula and bands that specifies a scale range
|
||||
res = client.get("/api/projects/{}/tasks/{}/orthophoto/metadata?formula=VARI".format(project.id, task.id))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
metadata = json.loads(res.content.decode("utf-8"))
|
||||
self.assertTrue(len(metadata['statistics']) == 1)
|
||||
|
||||
# Min/max values have been replaced
|
||||
self.assertEqual(metadata['statistics']['1']['min'], algos['VARI']['range'][0])
|
||||
self.assertEqual(metadata['statistics']['1']['max'], algos['VARI']['range'][1])
|
||||
|
||||
# Metadata for DSM/DTM
|
||||
for tile_type in ['dsm', 'dtm']:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/metadata".format(project.id, task.id, tile_type))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
metadata = json.loads(res.content.decode("utf-8"))
|
||||
|
||||
# Colormaps are populated
|
||||
self.assertTrue(len(metadata['color_maps']) > 0)
|
||||
|
||||
# Colormaps are for elevation
|
||||
self.assertTrue('jet_r' in metadata['color_maps'])
|
||||
self.assertFalse('rdylgn' in metadata['color_maps'])
|
||||
|
||||
# Algorithms are empty
|
||||
self.assertEqual(len(metadata['algorithms']), 0)
|
||||
|
||||
# Min/max values are what we expect them to be
|
||||
self.assertEqual(len(metadata['statistics']), 1)
|
||||
self.assertEqual(round(metadata['statistics']['1']['min'], 2), 156.91)
|
||||
self.assertEqual(round(metadata['statistics']['1']['max'], 2), 164.94)
|
||||
|
||||
# Can access individual tiles
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/tiles/17/32042/46185.png".format(project.id, task.id, tile_type))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
|
||||
with Image.open(io.BytesIO(res.data)) as i:
|
||||
self.assertEqual(i.width, 256)
|
||||
self.assertEqual(i.height, 256)
|
||||
|
||||
# Can access retina tiles
|
||||
for tile_type in tile_types:
|
||||
res = client.get("/api/projects/{}/tasks/{}/{}/tiles/17/32042/46185@2x.png".format(project.id, task.id, tile_type))
|
||||
self.assertEqual(res.status_code, status.HTTP_200_OK)
|
||||
|
||||
with Image.open(io.BytesIO(res.data)) as i:
|
||||
self.assertEqual(i.width, 512)
|
||||
self.assertEqual(i.height, 512)
|
||||
|
||||
# Another user does not have access to the resources
|
||||
other_client = APIClient()
|
||||
other_client.login(username="testuser2", password="test1234")
|
||||
|
|
Ładowanie…
Reference in New Issue