kopia lustrzana https://github.com/OpenDroneMap/WebODM
Add export progress
rodzic
617aac6a92
commit
ba2e23cca2
|
|
@ -5,6 +5,7 @@ import os
|
|||
import subprocess
|
||||
import numpy as np
|
||||
import numexpr as ne
|
||||
import time
|
||||
from django.contrib.gis.geos import GEOSGeometry
|
||||
from rasterio.enums import ColorInterp
|
||||
from rasterio.vrt import WarpedVRT
|
||||
|
|
@ -75,7 +76,14 @@ def compute_subwindows(window, max_window_size, overlap_pixels=0):
|
|||
def padded_window(w, pad):
|
||||
return Window(w.col_off - pad, w.row_off - pad, w.width + pad * 2, w.height + pad * 2)
|
||||
|
||||
def export_raster(input, output, **opts):
|
||||
def export_raster(input, output, progress_callback=None, **opts):
|
||||
current_progress = 0
|
||||
def p(text, perc=0):
|
||||
nonlocal current_progress
|
||||
current_progress += perc
|
||||
if progress_callback is not None:
|
||||
progress_callback(text, current_progress)
|
||||
|
||||
epsg = opts.get('epsg')
|
||||
expression = opts.get('expression')
|
||||
export_format = opts.get('format')
|
||||
|
|
@ -87,8 +95,6 @@ def export_raster(input, output, **opts):
|
|||
crop_wkt = opts.get('crop')
|
||||
|
||||
dem = asset_type in ['dsm', 'dtm']
|
||||
|
||||
import time
|
||||
now = time.time()
|
||||
|
||||
if crop_wkt is not None:
|
||||
|
|
@ -282,6 +288,11 @@ def export_raster(input, output, **opts):
|
|||
if dem and rgb and profile.get('nodata') is not None:
|
||||
profile.update(nodata=None)
|
||||
|
||||
|
||||
post_perc = 20 if reproject or kmz else 0
|
||||
num_wins = len(subwins)
|
||||
progress_per_win = (100 - post_perc) / num_wins if num_wins > 0 else 0
|
||||
|
||||
if expression is not None:
|
||||
# Apply band math
|
||||
if rgb:
|
||||
|
|
@ -297,7 +308,9 @@ def export_raster(input, output, **opts):
|
|||
indexes += (alpha_index, )
|
||||
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
for w, dst_w in subwins:
|
||||
for idx, (w, dst_w) in enumerate(subwins):
|
||||
p(f"Processing tile {idx}/{num_wins}", progress_per_win)
|
||||
|
||||
data = reader.read(indexes=indexes, window=w, out_dtype=np.float32)
|
||||
arr = dict(zip(bands_names, data))
|
||||
arr = np.array([np.nan_to_num(ne.evaluate(bloc.strip(), local_dict=arr)) for bloc in rgb_expr])
|
||||
|
|
@ -332,7 +345,9 @@ def export_raster(input, output, **opts):
|
|||
elif dem:
|
||||
# Apply hillshading, colormaps to elevation
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
for w, dst_w in subwins:
|
||||
for idx, (w, dst_w) in enumerate(subwins):
|
||||
p(f"Processing tile {idx}/{num_wins}", progress_per_win)
|
||||
|
||||
# Apply colormap?
|
||||
if rgb and cmap is not None:
|
||||
nodata = profile.get('nodata')
|
||||
|
|
@ -382,7 +397,9 @@ def export_raster(input, output, **opts):
|
|||
else:
|
||||
# Copy bands as-is
|
||||
with rasterio.open(output_raster, 'w', **profile) as dst:
|
||||
for w, dst_w in subwins:
|
||||
for idx, (w, dst_w) in enumerate(subwins):
|
||||
p(f"Processing tile {idx}/{num_wins}", progress_per_win)
|
||||
|
||||
arr = reader.read(indexes=indexes, window=w)
|
||||
dst.write(process(arr, drop_last_band=not with_alpha), window=dst_w)
|
||||
|
||||
|
|
@ -400,6 +417,8 @@ def export_raster(input, output, **opts):
|
|||
subprocess.check_output(["gdal_translate", "-of", "KMLSUPEROVERLAY",
|
||||
"-co", "Name={}".format(name),
|
||||
"-co", "FORMAT=AUTO", output_raster, output])
|
||||
p("Finalizing", post_perc)
|
||||
|
||||
elif reproject:
|
||||
output_vrt = path_base + ".vrt"
|
||||
|
||||
|
|
@ -428,5 +447,7 @@ def export_raster(input, output, **opts):
|
|||
if os.path.isfile(output_raster):
|
||||
os.unlink(output_raster)
|
||||
|
||||
logger.info(f"Finished in {time.time() - now}s")
|
||||
p("Finalizing", post_perc)
|
||||
|
||||
logger.info(f"Exported {output} in {round(time.time() - now, 2)}s")
|
||||
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ export default class ExportAssetPanel extends React.Component {
|
|||
epsg: this.props.task.epsg || null,
|
||||
customEpsg: Storage.getItem("last_export_custom_epsg") || "4326",
|
||||
resample: 0,
|
||||
exporting: false
|
||||
exporting: false,
|
||||
progress: null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +133,7 @@ export default class ExportAssetPanel extends React.Component {
|
|||
if (typeof cb !== 'function') cb = undefined;
|
||||
|
||||
const { task } = this.props;
|
||||
this.setState({exporting: true, error: ""});
|
||||
this.setState({exporting: true, error: "", progress: null});
|
||||
const data = this.getExportParams(format);
|
||||
|
||||
if (this.state.epsg === "custom") Storage.setItem("last_export_custom_epsg", data.epsg);
|
||||
|
|
@ -152,6 +153,8 @@ export default class ExportAssetPanel extends React.Component {
|
|||
Workers.downloadFile(result.celery_task_id, result.filename);
|
||||
if (cb !== undefined) cb();
|
||||
}
|
||||
}, (_, progress) => {
|
||||
this.setState({progress});
|
||||
});
|
||||
}else if (result.url){
|
||||
// Simple download
|
||||
|
|
@ -182,7 +185,7 @@ export default class ExportAssetPanel extends React.Component {
|
|||
}
|
||||
|
||||
render(){
|
||||
const {epsg, customEpsg, exporting, format, resample } = this.state;
|
||||
const {epsg, customEpsg, exporting, format, resample, progress } = this.state;
|
||||
const { exportFormats } = this.props;
|
||||
const utmEPSG = this.props.task.epsg;
|
||||
|
||||
|
|
@ -233,7 +236,7 @@ export default class ExportAssetPanel extends React.Component {
|
|||
<div className={"btn-group " + (this.props.dropUp ? "dropup" : "")}>
|
||||
<button onClick={this.handleExport(exportFormats[0])}
|
||||
disabled={disabled} type="button" className="btn btn-sm btn-primary btn-export">
|
||||
{exporting ? <i className="fa fa-spin fa-circle-notch"/> : <i className={this.efInfo[exportFormats[0]].icon + " fa-fw"}/>} {exporting ? _("Exporting...") : this.efInfo[exportFormats[0]].label}
|
||||
{exporting ? <i className="fa fa-spin fa-circle-notch"/> : <i className={this.efInfo[exportFormats[0]].icon + " fa-fw"}/>} {exporting ? _("Exporting...") : this.efInfo[exportFormats[0]].label} {exporting && progress !== null ? ` (${progress.toFixed(0)}%)` : ""}
|
||||
</button>
|
||||
<button disabled={disabled} type="button" className="btn btn-sm dropdown-toggle btn-primary" data-toggle="dropdown"><span className="caret"></span></button>
|
||||
<ul className="dropdown-menu pull-right">
|
||||
|
|
|
|||
|
|
@ -195,7 +195,10 @@ def export_raster(self, input, **opts):
|
|||
try:
|
||||
logger.info("Exporting raster {} with options: {}".format(input, json.dumps(opts)))
|
||||
tmpfile = tempfile.mktemp('_raster.{}'.format(extension_for_export_format(opts.get('format', 'gtiff'))), dir=settings.MEDIA_TMP)
|
||||
export_raster_sync(input, tmpfile, **opts)
|
||||
def progress_callback(status, perc):
|
||||
self.update_state(state="PROGRESS", meta={"status": status, "progress": perc})
|
||||
|
||||
export_raster_sync(input, tmpfile, progress_callback=progress_callback, **opts)
|
||||
result = {'file': tmpfile}
|
||||
|
||||
if settings.TESTING:
|
||||
|
|
@ -203,7 +206,7 @@ def export_raster(self, input, **opts):
|
|||
|
||||
return result
|
||||
except Exception as e:
|
||||
logger.error(traceback.format_exc())
|
||||
# logger.error(traceback.format_exc())
|
||||
logger.error(str(e))
|
||||
return {'error': str(e)}
|
||||
|
||||
|
|
|
|||
Ładowanie…
Reference in New Issue