kopia lustrzana https://github.com/OpenDroneMap/WebODM
KMZ export support
rodzic
dd3b413401
commit
8054b650f9
|
@ -246,26 +246,26 @@ def get_elevation_tiles(elevation, url, x, y, z, tilesize, nodata, resampling, p
|
|||
tile = np.full((tilesize * 3, tilesize * 3), nodata, dtype=elevation.dtype)
|
||||
with COGReader(url) as src:
|
||||
try:
|
||||
left, _ = src.tile(x - 1, y, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
left, _discard_ = src.tile(x - 1, y, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
resampling_method=resampling, padding=padding)
|
||||
tile[tilesize:tilesize * 2, 0:tilesize] = left
|
||||
except TileOutsideBounds:
|
||||
pass
|
||||
|
||||
try:
|
||||
right, _ = src.tile(x + 1, y, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
right, _discard_ = src.tile(x + 1, y, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
resampling_method=resampling, padding=padding)
|
||||
tile[tilesize:tilesize * 2, tilesize * 2:tilesize * 3] = right
|
||||
except TileOutsideBounds:
|
||||
pass
|
||||
try:
|
||||
bottom, _ = src.tile(x, y + 1, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
bottom, _discard_ = src.tile(x, y + 1, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
resampling_method=resampling, padding=padding)
|
||||
tile[tilesize * 2:tilesize * 3, tilesize:tilesize * 2] = bottom
|
||||
except TileOutsideBounds:
|
||||
pass
|
||||
try:
|
||||
top, _ = src.tile(x, y - 1, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
top, _discard_ = src.tile(x, y - 1, z, indexes=1, tilesize=tilesize, nodata=nodata,
|
||||
resampling_method=resampling, padding=padding)
|
||||
tile[0:tilesize, tilesize:tilesize * 2] = top
|
||||
except TileOutsideBounds:
|
||||
|
@ -314,7 +314,7 @@ class Tiles(TaskNestedView):
|
|||
if hillshade == '' or hillshade == '0': hillshade = None
|
||||
|
||||
try:
|
||||
expr, _ = lookup_formula(formula, bands)
|
||||
expr, _discard_ = lookup_formula(formula, bands)
|
||||
except ValueError as e:
|
||||
raise exceptions.ValidationError(str(e))
|
||||
|
||||
|
@ -442,7 +442,7 @@ class Tiles(TaskNestedView):
|
|||
if intensity is not None:
|
||||
rgb = tile.post_process(in_range=(rescale_arr,))
|
||||
if colormap:
|
||||
rgb, _ = apply_cmap(rgb.data, colormap.get(color_map))
|
||||
rgb, _discard_ = apply_cmap(rgb.data, colormap.get(color_map))
|
||||
if rgb.data.shape[0] != 3:
|
||||
raise exceptions.ValidationError(
|
||||
_("Cannot process tile: intensity image provided, but no RGB data was computed."))
|
||||
|
@ -486,21 +486,23 @@ class Export(TaskNestedView):
|
|||
if bands == '': bands = None
|
||||
if rescale == '': rescale = None
|
||||
if epsg == '': epsg = None
|
||||
if color_map == '': color_map = None
|
||||
if hillshade == '': hillshade = None
|
||||
|
||||
expr = None
|
||||
|
||||
if not export_format in ['gtiff', 'gtiff-rgb', 'jpg', 'png']:
|
||||
raise exceptions.ValidationError(_("Unsupported format: %(format)") % {'format': export_format})
|
||||
if not export_format in ['gtiff', 'gtiff-rgb', 'jpg', 'png', 'kmz']:
|
||||
raise exceptions.ValidationError(_("Unsupported format: %(value)s") % {'value': export_format})
|
||||
|
||||
if epsg is not None:
|
||||
try:
|
||||
epsg = int(epsg)
|
||||
except ValueError:
|
||||
raise exception.ValidationError(_("Invalid EPSG code: %(epsg)") % {'epsg': epsg})
|
||||
raise exceptions.ValidationError(_("Invalid EPSG code: %(value)s") % {'value': epsg})
|
||||
|
||||
if formula and bands:
|
||||
try:
|
||||
expr, _ = lookup_formula(formula, bands)
|
||||
expr, _discard_ = lookup_formula(formula, bands)
|
||||
except ValueError as e:
|
||||
raise exceptions.ValidationError(str(e))
|
||||
|
||||
|
@ -516,7 +518,7 @@ class Export(TaskNestedView):
|
|||
try:
|
||||
rescale = list(map(float, rescale.split(",")))
|
||||
except ValueError:
|
||||
raise exception.ValidationError(_("Invalid rescale value: %(value)") % {'value': rescale})
|
||||
raise exceptions.ValidationError(_("Invalid rescale value: %(value)s") % {'value': rescale})
|
||||
|
||||
if hillshade is not None:
|
||||
try:
|
||||
|
@ -524,7 +526,7 @@ class Export(TaskNestedView):
|
|||
if hillshade < 0:
|
||||
raise Exception("Hillshade must be > 0")
|
||||
except:
|
||||
raise exception.ValidationError(_("Invalid hillshade value: %(value)") % {'value': hillshade})
|
||||
raise exceptions.ValidationError(_("Invalid hillshade value: %(value)s") % {'value': hillshade})
|
||||
|
||||
url = get_raster_path(task, asset_type)
|
||||
|
||||
|
@ -549,5 +551,7 @@ class Export(TaskNestedView):
|
|||
rescale=rescale,
|
||||
color_map=color_map,
|
||||
hillshade=hillshade,
|
||||
dem=asset_type in ['dsm', 'dtm']).task_id
|
||||
dem=asset_type in ['dsm', 'dtm'],
|
||||
name=task.name,
|
||||
asset_type=asset_type).task_id
|
||||
return Response({'celery_task_id': celery_task_id, 'filename': filename})
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# Export a raster index after applying a band expression
|
||||
import rasterio
|
||||
import re
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import numpy as np
|
||||
import numexpr as ne
|
||||
from rasterio.enums import ColorInterp
|
||||
|
@ -10,7 +13,6 @@ from rio_tiler.errors import InvalidColorMapName
|
|||
from app.api.hsvblend import hsv_blend
|
||||
from app.api.hillshade import LightSource
|
||||
from rasterio.warp import calculate_default_transform, reproject, Resampling
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger('app.logger')
|
||||
|
||||
|
@ -21,7 +23,8 @@ def extension_for_export_format(export_format):
|
|||
'gtiff': 'tif',
|
||||
'gtiff-rgb': 'tif',
|
||||
'jpg': 'jpg',
|
||||
'png': 'png'
|
||||
'png': 'png',
|
||||
'kmz': 'kmz'
|
||||
}
|
||||
return extensions.get(export_format, 'tif')
|
||||
|
||||
|
@ -33,6 +36,8 @@ def export_raster(input, output, **opts):
|
|||
color_map = opts.get('color_map')
|
||||
hillshade = opts.get('hillshade')
|
||||
dem = opts.get('dem')
|
||||
name = opts.get('name', 'raster') # KMZ specific
|
||||
asset_type = opts.get('asset_type', 'raster') # KMZ specific
|
||||
|
||||
with rasterio.open(input) as src:
|
||||
profile = src.meta.copy()
|
||||
|
@ -43,6 +48,18 @@ def export_raster(input, output, **opts):
|
|||
with_alpha = True
|
||||
rgb = False
|
||||
indexes = src.indexes
|
||||
output_raster = output
|
||||
jpg_background = 255 # white
|
||||
|
||||
# KMZ is special, we just export it as jpg with EPSG:4326
|
||||
# and then call GDAL to tile/package it
|
||||
kmz = export_format == "kmz"
|
||||
if kmz:
|
||||
export_format = "jpg"
|
||||
epsg = 4326
|
||||
path_base, _ = os.path.splitext(output)
|
||||
output_raster = path_base + ".jpg"
|
||||
jpg_background = 0 # black
|
||||
|
||||
if export_format == "jpg":
|
||||
driver = "JPEG"
|
||||
|
@ -95,7 +112,7 @@ def export_raster(input, output, **opts):
|
|||
if not skip_rescale and rescale is not None:
|
||||
arr = linear_rescale(arr, in_range=rescale)
|
||||
if not skip_alpha and not with_alpha:
|
||||
arr[mask==0] = 255 # Set white background
|
||||
arr[mask==0] = jpg_background
|
||||
if not skip_type and rgb and arr.dtype != np.uint8:
|
||||
arr = arr.astype(np.uint8)
|
||||
|
||||
|
@ -173,7 +190,7 @@ def export_raster(input, output, **opts):
|
|||
# Make sure this is float32
|
||||
arr = arr.astype(np.float32)
|
||||
|
||||
with rasterio.open(output, 'w', **profile) as dst:
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
# Apply colormap?
|
||||
if rgb and cmap is not None:
|
||||
rgb_data, _ = apply_cmap(process(arr, skip_alpha=True), cmap)
|
||||
|
@ -190,7 +207,7 @@ def export_raster(input, output, **opts):
|
|||
write_band(process(arr)[0], dst, 1)
|
||||
elif dem:
|
||||
# Apply hillshading, colormaps to elevation
|
||||
with rasterio.open(output, 'w', **profile) as dst:
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
arr = src.read()
|
||||
|
||||
intensity = None
|
||||
|
@ -221,7 +238,7 @@ def export_raster(input, output, **opts):
|
|||
write_band(process(arr)[0], dst, 1)
|
||||
else:
|
||||
# Copy bands as-is
|
||||
with rasterio.open(output, 'w', **profile) as dst:
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
band_num = 1
|
||||
for idx in indexes:
|
||||
ci = src.colorinterp[idx - 1]
|
||||
|
@ -234,3 +251,8 @@ def export_raster(input, output, **opts):
|
|||
else:
|
||||
write_band(process(arr), dst, band_num)
|
||||
band_num += 1
|
||||
|
||||
if kmz:
|
||||
subprocess.check_output(["gdal_translate", "-of", "KMLSUPEROVERLAY",
|
||||
"-co", "Name={}".format(name),
|
||||
"-co", "FORMAT=JPEG", output_raster, output])
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from django.core.exceptions import SuspiciousFileOperation
|
||||
from shlex import _find_unsafe
|
||||
import os
|
||||
|
||||
def path_traversal_check(unsafe_path, known_safe_path):
|
||||
|
@ -9,4 +10,16 @@ def path_traversal_check(unsafe_path, known_safe_path):
|
|||
raise SuspiciousFileOperation("{} is not safe".format(unsafe_path))
|
||||
|
||||
# Passes the check
|
||||
return unsafe_path
|
||||
return unsafe_path
|
||||
|
||||
|
||||
def double_quote(s):
|
||||
"""Return a shell-escaped version of the string *s*."""
|
||||
if not s:
|
||||
return '""'
|
||||
if _find_unsafe(s) is None:
|
||||
return s
|
||||
|
||||
# use double quotes, and prefix double quotes with a \
|
||||
# the string $"b is then quoted as "$\"b"
|
||||
return '"' + s.replace('"', '\\\"') + '"'
|
|
@ -9,7 +9,7 @@ import Workers from '../classes/Workers';
|
|||
|
||||
export default class ExportAssetPanel extends React.Component {
|
||||
static defaultProps = {
|
||||
exportFormats: ["gtiff-rgb", "gtiff", "jpg", "png"],
|
||||
exportFormats: ["gtiff-rgb", "gtiff", "jpg", "png", "kmz"],
|
||||
asset: "",
|
||||
exportParams: {},
|
||||
task: null,
|
||||
|
@ -45,6 +45,10 @@ export default class ExportAssetPanel extends React.Component {
|
|||
'png': {
|
||||
label: _("PNG (RGB)"),
|
||||
icon: "fas fa-palette"
|
||||
},
|
||||
'kmz': {
|
||||
label: _("KMZ (RGB)"),
|
||||
icon: "fa fa-globe"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Ładowanie…
Reference in New Issue