Skip to content

Commit

Permalink
Merge pull request #1405 from pierotofy/contours
Browse files Browse the repository at this point in the history
GDAL based contours
  • Loading branch information
pierotofy authored Sep 26, 2023
2 parents bf3eec2 + a852dfb commit 9b49ad7
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 115 deletions.
96 changes: 77 additions & 19 deletions coreplugins/contours/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,80 @@
from rest_framework import status
from rest_framework.response import Response
from app.plugins.views import TaskView, CheckTask, GetTaskResult
from worker.tasks import execute_grass_script
from app.plugins.grass_engine import grass, GrassEngineException, cleanup_grass_context
from app.plugins.worker import run_function_async
from django.utils.translation import gettext_lazy as _

class ContoursException(Exception):
pass

def calc_contours(dem, epsg, interval, output_format, simplify):
import os
import subprocess
import tempfile
import shutil
import glob
from webodm import settings

ext = ""
if output_format == "GeoJSON":
ext = "json"
elif output_format == "GPKG":
ext = "gpkg"
elif output_format == "DXF":
ext = "dxf"
elif output_format == "ESRI Shapefile":
ext = "shp"
MIN_CONTOUR_LENGTH = 10

tmpdir = os.path.join(settings.MEDIA_TMP, os.path.basename(tempfile.mkdtemp('_contours', dir=settings.MEDIA_TMP)))
gdal_contour_bin = shutil.which("gdal_contour")
ogr2ogr_bin = shutil.which("ogr2ogr")

if gdal_contour_bin is None:
return {'error': 'Cannot find gdal_contour'}
if ogr2ogr_bin is None:
return {'error': 'Cannot find ogr2ogr'}

contours_file = f"contours.gpkg"
p = subprocess.Popen([gdal_contour_bin, "-q", "-a", "level", "-3d", "-f", "GPKG", "-i", str(interval), dem, contours_file], cwd=tmpdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()

out = out.decode('utf-8').strip()
err = err.decode('utf-8').strip()
success = p.returncode == 0

if not success:
return {'error', f'Error calling gdal_contour: {str(err)}'}

outfile = os.path.join(tmpdir, f"output.{ext}")
p = subprocess.Popen([ogr2ogr_bin, outfile, contours_file, "-simplify", str(simplify), "-f", output_format, "-t_srs", f"EPSG:{epsg}", "-nln", "contours",
"-dialect", "sqlite", "-sql", f"SELECT * FROM contour WHERE ST_Length(GEOM) >= {MIN_CONTOUR_LENGTH}"], cwd=tmpdir, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()

out = out.decode('utf-8').strip()
err = err.decode('utf-8').strip()
success = p.returncode == 0

if not success:
return {'error', f'Error calling ogr2ogr: {str(err)}'}

if not os.path.isfile(outfile):
return {'error': f'Cannot find output file: {outfile}'}

if output_format == "ESRI Shapefile":
ext="zip"
shp_dir = os.path.join(tmpdir, "contours")
os.makedirs(shp_dir)
contour_files = glob.glob(os.path.join(tmpdir, "output.*"))
for cf in contour_files:
shutil.move(cf, shp_dir)

shutil.make_archive(os.path.join(tmpdir, 'output'), 'zip', shp_dir)
outfile = os.path.join(tmpdir, f"output.{ext}")

return {'file': outfile}


class TaskContoursGenerate(TaskView):
def post(self, request, pk=None):
task = self.get_and_check_task(request, pk)
Expand All @@ -23,36 +93,24 @@ def post(self, request, pk=None):
elif layer == 'DTM':
dem = os.path.abspath(task.get_asset_download_path("dtm.tif"))
else:
raise GrassEngineException('{} is not a valid layer.'.format(layer))
raise ContoursException('{} is not a valid layer.'.format(layer))

context = grass.create_context({'auto_cleanup' : False})
epsg = int(request.data.get('epsg', '3857'))
interval = float(request.data.get('interval', 1))
format = request.data.get('format', 'GPKG')
supported_formats = ['GPKG', 'ESRI Shapefile', 'DXF', 'GeoJSON']
if not format in supported_formats:
raise GrassEngineException("Invalid format {} (must be one of: {})".format(format, ",".join(supported_formats)))
raise ContoursException("Invalid format {} (must be one of: {})".format(format, ",".join(supported_formats)))
simplify = float(request.data.get('simplify', 0.01))

context.add_param('dem_file', dem)
context.add_param('interval', interval)
context.add_param('format', format)
context.add_param('simplify', simplify)
context.add_param('epsg', epsg)
context.set_location(dem)

celery_task_id = execute_grass_script.delay(os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"calc_contours.py"
), context.serialize(), 'file').task_id

celery_task_id = run_function_async(calc_contours, dem, epsg, interval, format, simplify).task_id
return Response({'celery_task_id': celery_task_id}, status=status.HTTP_200_OK)
except GrassEngineException as e:
except ContoursException as e:
return Response({'error': str(e)}, status=status.HTTP_200_OK)

class TaskContoursCheck(CheckTask):
def on_error(self, result):
cleanup_grass_context(result['context'])
pass

def error_check(self, result):
contours_file = result.get('file')
Expand Down
93 changes: 0 additions & 93 deletions coreplugins/contours/calc_contours.py

This file was deleted.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "WebODM",
"version": "2.1.2",
"version": "2.1.3",
"description": "User-friendly, extendable application and API for processing aerial imagery.",
"main": "index.js",
"scripts": {
Expand Down
2 changes: 0 additions & 2 deletions worker/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ def execute_grass_script(script, serialized_context = {}, out_key='output'):
logger.error(str(e))
return {'error': str(e), 'context': ctx.serialize()}


@app.task(bind=True)
def export_raster(self, input, **opts):
try:
Expand Down Expand Up @@ -238,4 +237,3 @@ def check_quotas():
break
else:
p.clear_quota_deadline()

0 comments on commit 9b49ad7

Please sign in to comment.