Skip to content

Commit d0139fc

Browse files
authored
Merge pull request #1590 from pierotofy/ptsamp
Add point cloud resampling export
2 parents ea4cb43 + e5b1466 commit d0139fc

File tree

4 files changed

+41
-8
lines changed

4 files changed

+41
-8
lines changed

app/api/tiler.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,15 @@ def post(self, request, pk=None, project_pk=None, asset_type=None):
524524
epsg = request.data.get('epsg')
525525
color_map = request.data.get('color_map')
526526
hillshade = request.data.get('hillshade')
527+
resample = request.data.get('resample')
527528

528529
if formula == '': formula = None
529530
if bands == '': bands = None
530531
if rescale == '': rescale = None
531532
if epsg == '': epsg = None
532533
if color_map == '': color_map = None
533534
if hillshade == '': hillshade = None
535+
if resample == '': resample = None
534536

535537
expr = None
536538

@@ -552,6 +554,12 @@ def post(self, request, pk=None, project_pk=None, asset_type=None):
552554
except InvalidColorMapName:
553555
raise exceptions.ValidationError(_("Not a valid color_map value"))
554556

557+
if resample is not None:
558+
try:
559+
resample = float(resample)
560+
except ValueError:
561+
raise exceptions.ValidationError(_("Invalid resample value: %(value)s") % {'value': resample})
562+
555563
if epsg is not None:
556564
try:
557565
epsg = int(epsg)
@@ -627,9 +635,10 @@ def post(self, request, pk=None, project_pk=None, asset_type=None):
627635
return Response({'celery_task_id': celery_task_id, 'filename': filename})
628636
elif asset_type == 'georeferenced_model':
629637
# Shortcut the process if no processing is required
630-
if export_format == 'laz' and (epsg == task.epsg or epsg is None):
638+
if export_format == 'laz' and (epsg == task.epsg or epsg is None) and (resample is None or resample == 0):
631639
return Response({'url': '/api/projects/{}/tasks/{}/download/{}.laz'.format(task.project.id, task.id, asset_type), 'filename': filename})
632640
else:
633641
celery_task_id = export_pointcloud.delay(url, epsg=epsg,
634-
format=export_format).task_id
642+
format=export_format,
643+
resample=resample).task_id
635644
return Response({'celery_task_id': celery_task_id, 'filename': filename})

app/pointcloud_utils.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@
1010
def export_pointcloud(input, output, **opts):
1111
epsg = opts.get('epsg')
1212
export_format = opts.get('format')
13+
resample = float(opts.get('resample', 0))
1314

15+
resample_args = []
1416
reprojection_args = []
1517
extra_args = []
1618

1719
if epsg:
1820
reprojection_args = ["reprojection",
1921
"--filters.reprojection.out_srs=%s" % double_quote("EPSG:" + str(epsg))]
20-
22+
2123
if export_format == "ply":
2224
extra_args = ['--writers.ply.sized_types', 'false',
2325
'--writers.ply.storage_mode', 'little endian']
2426

25-
subprocess.check_output(["pdal", "translate", input, output] + reprojection_args + extra_args)
27+
if resample > 0:
28+
resample_args = ['sample', '--filters.sample.radius=%s' % resample]
29+
30+
subprocess.check_output(["pdal", "translate", input, output] + resample_args + reprojection_args + extra_args)
2631

2732

2833
def is_pointcloud_georeferenced(laz_path):

app/static/app/js/components/ExportAssetPanel.jsx

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export default class ExportAssetPanel extends React.Component {
7575
format: props.exportFormats[0],
7676
epsg: this.props.task.epsg || null,
7777
customEpsg: Storage.getItem("last_export_custom_epsg") || "4326",
78+
resample: 0,
7879
exporting: false
7980
}
8081
}
@@ -97,6 +98,10 @@ export default class ExportAssetPanel extends React.Component {
9798
this.setState({customEpsg: e.target.value});
9899
}
99100

101+
handleChangeResample = e => {
102+
this.setState({resample: e.target.value});
103+
}
104+
100105
getExportParams = (format) => {
101106
let params = {};
102107

@@ -111,9 +116,15 @@ export default class ExportAssetPanel extends React.Component {
111116
const epsg = this.getEpsg();
112117
if (epsg) params.epsg = this.getEpsg();
113118

119+
if (this.state.resample > 0) params.resample = this.state.resample;
120+
114121
return params;
115122
}
116123

124+
isPointCloud = () => {
125+
return this.props.asset == "georeferenced_model";
126+
}
127+
117128
handleExport = (format) => {
118129
if (!format) format = this.state.format;
119130

@@ -171,7 +182,7 @@ export default class ExportAssetPanel extends React.Component {
171182
}
172183

173184
render(){
174-
const {epsg, customEpsg, exporting, format } = this.state;
185+
const {epsg, customEpsg, exporting, format, resample } = this.state;
175186
const { exportFormats } = this.props;
176187
const utmEPSG = this.props.task.epsg;
177188

@@ -200,14 +211,21 @@ export default class ExportAssetPanel extends React.Component {
200211

201212
let exportSelector = null;
202213
if (this.props.selectorOnly){
203-
exportSelector = (<div className="row form-group form-inline">
214+
exportSelector = [<div key={1} className="row form-group form-inline">
204215
<label className="col-sm-3 control-label">{_("Format:")}</label>
205216
<div className="col-sm-9 ">
206217
<select className="form-control" value={format} onChange={this.handleSelectFormat}>
207218
{exportFormats.map(ef => <option key={ef} value={ef}>{this.efInfo[ef].label}</option>)}
208219
</select>
209220
</div>
210-
</div>);
221+
</div>,
222+
this.isPointCloud() ? <div key={2} className="row form-group form-inline">
223+
<label className="col-sm-3 control-label">{_("Resample (meters):")}</label>
224+
<div className="col-sm-9 ">
225+
<input type="number" min="0" className="form-control custom-interval" value={resample} onChange={this.handleChangeResample} />
226+
</div>
227+
</div>
228+
: ""];
211229
}else{
212230
exportSelector = (<div className="row form-group form-inline">
213231
<label className="col-sm-3 control-label">{_("Export:")}</label>

app/tests/test_api_export.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ def test_exports(self):
8282
('orthophoto', {'formula': 'NDVI', 'bands': 'RGN'}, status.HTTP_200_OK),
8383
('dsm', {'epsg': 4326}, status.HTTP_200_OK),
8484
('dtm', {'epsg': 4326}, status.HTTP_200_OK),
85-
('georeferenced_model', {'epsg': 4326}, status.HTTP_200_OK)
85+
('georeferenced_model', {'epsg': 4326}, status.HTTP_200_OK),
86+
('georeferenced_model', {'epsg': 4326, 'resample': 2.5}, status.HTTP_200_OK)
8687
]
8788

8889
# Cannot export stuff

0 commit comments

Comments
 (0)