zhongrj
2025-11-24 276323dce9613867abb3f58a4cc2abbfb2fd0dea
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import os
 
from rest_framework import status
from rest_framework.response import Response
from app.plugins.views import TaskView, CheckTask, GetTaskResult
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, zfactor = 1):
    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 ID, ROUND(level * {zfactor}, 5) AS level, GeomFromGML(AsGML(ATM_Transform(GEOM, ATM_Scale(ATM_Create(), 1, 1, {zfactor})), 10)) as GEOM 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)
 
        layer = request.data.get('layer', None)
        if layer == 'DSM' and task.dsm_extent is None:
            return Response({'error': _('No DSM layer is available.')})
        elif layer == 'DTM' and task.dtm_extent is None:
            return Response({'error': _('No DTM layer is available.')})
 
        try:
            if layer == 'DSM':
                dem = os.path.abspath(task.get_asset_download_path("dsm.tif"))
            elif layer == 'DTM':
                dem = os.path.abspath(task.get_asset_download_path("dtm.tif"))
            else:
                raise ContoursException('{} is not a valid layer.'.format(layer))
 
            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 ContoursException("Invalid format {} (must be one of: {})".format(format, ",".join(supported_formats)))
            simplify = float(request.data.get('simplify', 0.01))
            zfactor = float(request.data.get('zfactor', 1))
 
            celery_task_id = run_function_async(calc_contours, dem, epsg, interval, format, simplify, zfactor).task_id
            return Response({'celery_task_id': celery_task_id}, status=status.HTTP_200_OK)
        except ContoursException as e:
            return Response({'error': str(e)}, status=status.HTTP_200_OK)
 
 
class TaskContoursDownload(GetTaskResult):
    pass