2017-06-27 16:43:36 +00:00
|
|
|
import ecto, os, json
|
2017-06-27 17:14:09 +00:00
|
|
|
from shutil import copyfile
|
2017-06-23 15:20:46 +00:00
|
|
|
|
|
|
|
from opendm import io
|
|
|
|
from opendm import log
|
|
|
|
from opendm import system
|
|
|
|
from opendm import context
|
|
|
|
from opendm import types
|
2018-08-08 19:41:08 +00:00
|
|
|
from opendm import gsd
|
2018-01-16 19:46:30 +00:00
|
|
|
from opendm.dem import commands
|
|
|
|
from opendm.cropper import Cropper
|
2017-06-23 15:20:46 +00:00
|
|
|
|
|
|
|
|
2017-06-23 20:15:13 +00:00
|
|
|
class ODMDEMCell(ecto.Cell):
|
2017-06-23 15:20:46 +00:00
|
|
|
def declare_params(self, params):
|
|
|
|
params.declare("verbose", 'print additional messages to console', False)
|
2018-07-01 23:24:37 +00:00
|
|
|
params.declare("max_concurrency", "Number of threads", context.num_cores)
|
2017-06-23 15:20:46 +00:00
|
|
|
|
|
|
|
def declare_io(self, params, inputs, outputs):
|
2017-06-23 20:15:13 +00:00
|
|
|
inputs.declare("tree", "Struct with paths", [])
|
2017-06-23 15:20:46 +00:00
|
|
|
inputs.declare("args", "The application arguments.", {})
|
2017-06-23 19:28:46 +00:00
|
|
|
inputs.declare("reconstruction", "list of ODMReconstructions", [])
|
2017-06-23 15:20:46 +00:00
|
|
|
|
|
|
|
def process(self, inputs, outputs):
|
|
|
|
# Benchmarking
|
|
|
|
start_time = system.now_raw()
|
|
|
|
|
|
|
|
log.ODM_INFO('Running ODM DEM Cell')
|
|
|
|
|
|
|
|
# get inputs
|
|
|
|
args = self.inputs.args
|
2017-06-23 20:15:13 +00:00
|
|
|
tree = self.inputs.tree
|
2018-06-18 13:57:20 +00:00
|
|
|
las_model_found = io.file_exists(tree.odm_georeferencing_model_laz)
|
2017-06-23 19:28:46 +00:00
|
|
|
|
2018-01-26 15:40:27 +00:00
|
|
|
# check if we rerun cell or not
|
|
|
|
rerun_cell = (args.rerun is not None and
|
|
|
|
args.rerun == 'odm_dem') or \
|
|
|
|
(args.rerun_all) or \
|
|
|
|
(args.rerun_from is not None and
|
|
|
|
'odm_dem' in args.rerun_from)
|
|
|
|
|
2018-01-16 19:46:30 +00:00
|
|
|
log.ODM_INFO('Classify: ' + str(args.pc_classify != "none"))
|
2017-06-23 19:28:46 +00:00
|
|
|
log.ODM_INFO('Create DSM: ' + str(args.dsm))
|
|
|
|
log.ODM_INFO('Create DTM: ' + str(args.dtm))
|
2018-06-18 13:57:20 +00:00
|
|
|
log.ODM_INFO('DEM input file {0} found: {1}'.format(tree.odm_georeferencing_model_laz, str(las_model_found)))
|
2017-06-23 19:28:46 +00:00
|
|
|
|
2018-01-16 19:46:30 +00:00
|
|
|
# Setup terrain parameters
|
|
|
|
terrain_params_map = {
|
2018-07-01 23:24:37 +00:00
|
|
|
'flatnonforest': (1, 3),
|
|
|
|
'flatforest': (1, 2),
|
|
|
|
'complexnonforest': (5, 2),
|
2018-01-16 19:46:30 +00:00
|
|
|
'complexforest': (10, 2)
|
|
|
|
}
|
2018-07-01 23:24:37 +00:00
|
|
|
terrain_params = terrain_params_map[args.dem_terrain_type.lower()]
|
2018-01-16 19:46:30 +00:00
|
|
|
slope, cellsize = terrain_params
|
|
|
|
|
2018-01-26 15:40:27 +00:00
|
|
|
# define paths and create working directories
|
|
|
|
odm_dem_root = tree.path('odm_dem')
|
|
|
|
if not io.dir_exists(odm_dem_root):
|
|
|
|
system.mkdir_p(odm_dem_root)
|
|
|
|
|
2018-01-16 19:46:30 +00:00
|
|
|
if args.pc_classify != "none" and las_model_found:
|
2018-01-26 15:40:27 +00:00
|
|
|
pc_classify_marker = os.path.join(odm_dem_root, 'pc_classify_done.txt')
|
|
|
|
|
|
|
|
if not io.file_exists(pc_classify_marker) or rerun_cell:
|
2018-06-18 13:57:20 +00:00
|
|
|
log.ODM_INFO("Classifying {} using {}".format(tree.odm_georeferencing_model_laz, args.pc_classify))
|
2018-07-01 23:24:37 +00:00
|
|
|
commands.classify(tree.odm_georeferencing_model_laz,
|
2018-01-26 15:40:27 +00:00
|
|
|
args.pc_classify == "smrf",
|
|
|
|
slope,
|
|
|
|
cellsize,
|
|
|
|
approximate=args.dem_approximate,
|
|
|
|
initialDistance=args.dem_initial_distance,
|
|
|
|
verbose=args.verbose
|
|
|
|
)
|
2018-07-01 23:24:37 +00:00
|
|
|
with open(pc_classify_marker, 'w') as f:
|
2018-01-26 15:40:27 +00:00
|
|
|
f.write('Classify: {}\n'.format(args.pc_classify))
|
|
|
|
f.write('Slope: {}\n'.format(slope))
|
|
|
|
f.write('Cellsize: {}\n'.format(cellsize))
|
|
|
|
f.write('Approximate: {}\n'.format(args.dem_approximate))
|
|
|
|
f.write('InitialDistance: {}\n'.format(args.dem_initial_distance))
|
2018-01-16 19:46:30 +00:00
|
|
|
|
2017-06-23 19:28:46 +00:00
|
|
|
# Do we need to process anything here?
|
2018-01-16 19:46:30 +00:00
|
|
|
if (args.dsm or args.dtm) and las_model_found:
|
2017-06-27 16:43:36 +00:00
|
|
|
dsm_output_filename = os.path.join(odm_dem_root, 'dsm.tif')
|
|
|
|
dtm_output_filename = os.path.join(odm_dem_root, 'dtm.tif')
|
2017-06-23 19:28:46 +00:00
|
|
|
|
|
|
|
if (args.dtm and not io.file_exists(dtm_output_filename)) or \
|
|
|
|
(args.dsm and not io.file_exists(dsm_output_filename)) or \
|
|
|
|
rerun_cell:
|
|
|
|
|
|
|
|
products = []
|
2018-07-01 23:24:37 +00:00
|
|
|
if args.dsm: products.append('dsm')
|
2017-06-23 19:28:46 +00:00
|
|
|
if args.dtm: products.append('dtm')
|
2018-08-08 19:41:08 +00:00
|
|
|
|
2018-08-11 16:45:21 +00:00
|
|
|
resolution = gsd.cap_resolution(args.dem_resolution, tree.opensfm_reconstruction, ignore_gsd=args.ignore_gsd)
|
2018-08-08 19:41:08 +00:00
|
|
|
radius_steps = [(resolution / 100.0) / 2.0]
|
2017-06-23 19:28:46 +00:00
|
|
|
for _ in range(args.dem_gapfill_steps - 1):
|
2018-06-18 13:54:09 +00:00
|
|
|
radius_steps.append(radius_steps[-1] * 2) # 2 is arbitrary, maybe there's a better value?
|
2017-06-23 19:28:46 +00:00
|
|
|
|
|
|
|
for product in products:
|
2018-01-16 19:46:30 +00:00
|
|
|
commands.create_dems(
|
2018-07-01 23:24:37 +00:00
|
|
|
[tree.odm_georeferencing_model_laz],
|
2018-01-16 19:46:30 +00:00
|
|
|
product,
|
|
|
|
radius=map(str, radius_steps),
|
|
|
|
gapfill=True,
|
|
|
|
outdir=odm_dem_root,
|
2018-08-08 19:41:08 +00:00
|
|
|
resolution=resolution / 100.0,
|
2018-01-16 19:46:30 +00:00
|
|
|
maxsd=args.dem_maxsd,
|
|
|
|
maxangle=args.dem_maxangle,
|
|
|
|
decimation=args.dem_decimation,
|
2018-07-03 17:01:18 +00:00
|
|
|
verbose=args.verbose,
|
|
|
|
max_workers=args.max_concurrency
|
2018-01-16 19:46:30 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
if args.crop > 0:
|
|
|
|
bounds_shapefile_path = os.path.join(tree.odm_georeferencing, 'odm_georeferenced_model.bounds.shp')
|
|
|
|
if os.path.exists(bounds_shapefile_path):
|
|
|
|
Cropper.crop(bounds_shapefile_path, os.path.join(odm_dem_root, "{}.tif".format(product)), {
|
|
|
|
'TILED': 'YES',
|
|
|
|
'COMPRESS': 'LZW',
|
|
|
|
'BLOCKXSIZE': 512,
|
|
|
|
'BLOCKYSIZE': 512,
|
2018-07-01 23:24:37 +00:00
|
|
|
'NUM_THREADS': self.params.max_concurrency
|
2018-01-16 19:46:30 +00:00
|
|
|
})
|
2017-06-23 19:28:46 +00:00
|
|
|
else:
|
|
|
|
log.ODM_WARNING('Found existing outputs in: %s' % odm_dem_root)
|
2017-06-23 15:20:46 +00:00
|
|
|
else:
|
2017-06-23 19:28:46 +00:00
|
|
|
log.ODM_WARNING('DEM will not be generated')
|
2017-06-23 15:20:46 +00:00
|
|
|
|
|
|
|
if args.time:
|
2017-06-23 19:28:46 +00:00
|
|
|
system.benchmark(start_time, tree.benchmarking, 'Dem')
|
2017-06-23 15:20:46 +00:00
|
|
|
|
2017-06-23 19:28:46 +00:00
|
|
|
log.ODM_INFO('Running ODM DEM Cell - Finished')
|
|
|
|
return ecto.OK if args.end_with != 'odm_dem' else ecto.QUIT
|