diff --git a/app/api/tiler.py b/app/api/tiler.py index c0743521..80aacd86 100644 --- a/app/api/tiler.py +++ b/app/api/tiler.py @@ -18,6 +18,7 @@ from .formulas import lookup_formula, get_algorithm_list from .tasks import TaskNestedView from rest_framework import exceptions from rest_framework.response import Response +from worker.tasks import export_raster_index ZOOM_EXTRA_LEVELS = 2 @@ -410,21 +411,19 @@ class Tiles(TaskNestedView): ) class Export(TaskNestedView): - def get(self, request, pk=None, project_pk=None): + def post(self, request, pk=None, project_pk=None): """ Export an orthophoto after applying a formula """ task = self.get_and_check_task(request, pk) - nodata = None - - formula = self.request.query_params.get('formula') - bands = self.request.query_params.get('bands') - rescale = self.request.query_params.get('rescale') + formula = request.data.get('formula') + bands = request.data.get('bands') + # rescale = request.data.get('rescale') if formula == '': formula = None if bands == '': bands = None - if rescale == '': rescale = None + # if rescale == '': rescale = None if not formula: raise exceptions.ValidationError("You need to specify a formula parameter") @@ -437,17 +436,13 @@ class Export(TaskNestedView): except ValueError as e: raise exceptions.ValidationError(str(e)) - if formula is not None and rescale is None: - rescale = "-1,1" - - if nodata is not None: - nodata = np.nan if nodata == "nan" else float(nodata) + # if formula is not None and rescale is None: + # rescale = "-1,1" url = get_raster_path(task, "orthophoto") if not os.path.isfile(url): raise exceptions.NotFound() - export_raster_index(url, expr, "/webodm/app/media/project/2/task/5392337b-cd3f-42ef-879d-b36149ef442f/assets/odm_orthophoto/export.tif") - - return HttpResponse("OK") \ No newline at end of file + celery_task_id = export_raster_index.delay(url, expr).task_id + return Response({'celery_task_id': celery_task_id}) \ No newline at end of file diff --git a/app/api/workers.py b/app/api/workers.py index daa0f828..6d2b2c8a 100644 --- a/app/api/workers.py +++ b/app/api/workers.py @@ -49,7 +49,7 @@ class GetTaskResult(APIView): return Response({'error': 'Task not ready'}) if file is not None: - filename = os.path.basename(file) + filename = request.query_params.get('filename', os.path.basename(file)) filesize = os.stat(file).st_size f = open(file, "rb") diff --git a/app/static/app/js/components/LayersControlLayer.jsx b/app/static/app/js/components/LayersControlLayer.jsx index b1e8d797..ee3c518a 100644 --- a/app/static/app/js/components/LayersControlLayer.jsx +++ b/app/static/app/js/components/LayersControlLayer.jsx @@ -4,6 +4,8 @@ import '../css/LayersControlLayer.scss'; import Histogram from './Histogram'; import { Checkbox, ExpandButton } from './Toggle'; import Utils from '../classes/Utils'; +import Workers from '../classes/Workers'; +import ErrorMessage from './ErrorMessage'; import $ from 'jquery'; export default class LayersControlLayer extends React.Component { @@ -39,7 +41,8 @@ export default class LayersControlLayer extends React.Component { bands: params.bands || "", hillshade: params.hillshade || "", histogramLoading: false, - exportLoading: false + exportLoading: false, + error: "" }; this.rescale = params.rescale || ""; @@ -79,6 +82,11 @@ export default class LayersControlLayer extends React.Component { this.updateHistogramReq.abort(); this.updateHistogramReq = null; } + + if (this.exportReq){ + this.exportReq.abort(); + this.exportReq = null; + } } handleLayerClick = () => { @@ -188,9 +196,29 @@ export default class LayersControlLayer extends React.Component { } handleExport = e => { - this.setState({exportLoading: true}); - - + this.setState({exportLoading: true, error: ""}); + + this.exportReq = $.ajax({ + type: 'POST', + url: `/api/projects/${this.meta.task.project}/tasks/${this.meta.task.id}/orthophoto/export`, + data: this.getLayerParams() + }).done(result => { + if (result.celery_task_id){ + Workers.waitForCompletion(result.celery_task_id, error => { + if (error) this.setState({exportLoading: false, error}); + else{ + this.setState({exportLoading: false}); + Workers.downloadFile(result.celery_task_id, "odm_orthophoto_" + encodeURIComponent(this.state.formula) + ".tif"); + } + }); + }else if (result.error){ + this.setState({exportLoading: false, error: result.error}); + }else{ + this.setState({exportLoading: false, error: "Invalid response: " + result}); + } + }).fail(error => { + this.setState({exportLoading: false, error: JSON.stringify(error)}); + }); } render(){ @@ -216,6 +244,8 @@ export default class LayersControlLayer extends React.Component { colorMap={cmapValues} onUpdate={this.handleHistogramUpdate} /> + + {formula !== "" && algorithms ?
diff --git a/plugins/contours/public/ContoursPanel.jsx b/plugins/contours/public/ContoursPanel.jsx index 728420ef..b3b848eb 100644 --- a/plugins/contours/public/ContoursPanel.jsx +++ b/plugins/contours/public/ContoursPanel.jsx @@ -4,6 +4,7 @@ import Storage from 'webodm/classes/Storage'; import L from 'leaflet'; import './ContoursPanel.scss'; import ErrorMessage from 'webodm/components/ErrorMessage'; +import Workers from 'webodm/classes/Workers'; export default class ContoursPanel extends React.Component { static defaultProps = { @@ -115,32 +116,6 @@ export default class ContoursPanel extends React.Component { }; } - waitForCompletion = (taskId, celery_task_id, cb) => { - let errorCount = 0; - - const check = () => { - $.ajax({ - type: 'GET', - url: `/api/plugins/contours/task/${taskId}/contours/check/${celery_task_id}` - }).done(result => { - if (result.error){ - cb(result.error); - }else if (result.ready){ - cb(); - }else{ - // Retry - setTimeout(() => check(), 2000); - } - }).fail(error => { - console.warn(error); - if (errorCount++ < 10) setTimeout(() => check(), 2000); - else cb(JSON.stringify(error)); - }); - }; - - check(); - } - addGeoJSONFromURL = (url, cb) => { const { map } = this.props; @@ -197,7 +172,7 @@ export default class ContoursPanel extends React.Component { data: data }).done(result => { if (result.celery_task_id){ - this.waitForCompletion(taskId, result.celery_task_id, error => { + Workers.waitForCompletion(result.celery_task_id, error => { if (error) this.setState({[loadingProp]: false, error}); else{ const fileUrl = `/api/plugins/contours/task/${taskId}/contours/download/${result.celery_task_id}`; @@ -214,7 +189,7 @@ export default class ContoursPanel extends React.Component { this.setState({[loadingProp]: false}); } } - }); + }, `/api/plugins/contours/task/${taskId}/contours/check/`); }else if (result.error){ this.setState({[loadingProp]: false, error: result.error}); }else{ diff --git a/worker/tasks.py b/worker/tasks.py index 7ded98cc..b0900e2f 100644 --- a/worker/tasks.py +++ b/worker/tasks.py @@ -151,6 +151,7 @@ def execute_grass_script(script, serialized_context = {}, out_key='output'): @app.task def export_raster_index(input, expression): try: + logger.info("Exporting raster index {} with expression: {}".format(input, expression)) tmpfile = tempfile.mktemp('_raster_index.tif', dir=settings.MEDIA_TMP) export_raster_index_sync(input, expression, tmpfile) return {'file': tmpfile}