QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgspointcloudlayerexporter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgspointcloudlayerexporter.cpp
3 ---------------------
4 begin : July 2022
5 copyright : (C) 2022 by Stefanos Natsis
6 email : uclaros at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsgeos.h"
23#include "qgsrectangle.h"
24#include "qgsvectorfilewriter.h"
25
26#include <QApplication>
27#include <QFileInfo>
28#include <QQueue>
29#include <QThread>
30
31#include "moc_qgspointcloudlayerexporter.cpp"
32
33#ifdef HAVE_PDAL_QGIS
34#include <memory>
35#include <pdal/StageFactory.hpp>
36#include <pdal/io/BufferReader.hpp>
37#include <pdal/Dimension.hpp>
38#endif
39
41{
42 switch ( format )
43 {
45 return QStringLiteral( "GPKG" );
47 return QStringLiteral( "DXF" );
49 return QStringLiteral( "ESRI Shapefile" );
51 return QStringLiteral( "CSV" );
54 break;
55 }
56 return QString();
57}
58
60 : mLayerAttributeCollection( layer->attributes() )
61 , mIndex( layer->index() )
62 , mSourceCrs( QgsCoordinateReferenceSystem( layer->crs() ) )
63 , mTargetCrs( QgsCoordinateReferenceSystem( layer->crs() ) )
64{
65 bool ok;
66 mPointRecordFormat = layer->dataProvider()->originalMetadata().value( QStringLiteral( "dataformat_id" ) ).toInt( &ok );
67 if ( !ok )
68 mPointRecordFormat = 3;
69
71}
72
74{
75 delete mMemoryLayer;
76 delete mVectorSink;
77 delete mTransform;
78}
79
81{
82 if ( supportedFormats().contains( format ) )
83 {
84 mFormat = format;
85 return true;
86 }
87 return false;
88}
89
91{
92 mFilterGeometryEngine = std::make_unique<QgsGeos>( geometry );
93 mFilterGeometryEngine->prepareGeometry();
94}
95
96void QgsPointCloudLayerExporter::setFilterGeometry( QgsMapLayer *layer, bool selectedFeaturesOnly )
97{
98 QgsVectorLayer *vlayer = dynamic_cast< QgsVectorLayer * >( layer );
99 if ( !vlayer )
100 return;
101
102 QVector< QgsGeometry > allGeometries;
104 const QgsFeatureRequest request = QgsFeatureRequest( mExtent ).setNoAttributes();
105 if ( selectedFeaturesOnly )
106 fit = vlayer->getSelectedFeatures( request );
107 else
108 fit = vlayer->getFeatures( request );
109
110 QgsCoordinateTransform transform( vlayer->crs(), mSourceCrs, mTransformContext );
111
112 QgsFeature f;
113 while ( fit.nextFeature( f ) )
114 {
115 if ( f.hasGeometry() )
116 {
117 allGeometries.append( f.geometry() );
118 }
119 }
120 QgsGeometry unaryUnion = QgsGeometry::unaryUnion( allGeometries );
121 try
122 {
123 unaryUnion.transform( transform );
124 }
125 catch ( const QgsCsException &cse )
126 {
127 QgsDebugError( QStringLiteral( "Error transforming union of filter layer: %1" ).arg( cse.what() ) );
128 QgsDebugError( QStringLiteral( "FilterGeometry will be ignored." ) );
129 return;
130 }
131 setFilterGeometry( unaryUnion.constGet() );
132}
133
134void QgsPointCloudLayerExporter::setAttributes( const QStringList &attributeList )
135{
136 mRequestedAttributes.clear();
137
138 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
139 for ( const QgsPointCloudAttribute &attribute : allAttributes )
140 {
141 // Don't add x, y, z or duplicate attributes
142 if ( attribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) &&
143 attribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) &&
144 attribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) &&
145 attributeList.contains( attribute.name() ) &&
146 ! mRequestedAttributes.contains( attribute.name() ) )
147 {
148 mRequestedAttributes.append( attribute.name() );
149 }
150 }
151}
152
154{
155 QStringList allAttributeNames;
156 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
157 for ( const QgsPointCloudAttribute &attribute : allAttributes )
158 {
159 allAttributeNames.append( attribute.name() );
160 }
161 setAttributes( allAttributeNames );
162}
163
164const QgsPointCloudAttributeCollection QgsPointCloudLayerExporter::requestedAttributeCollection()
165{
166 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
167 QgsPointCloudAttributeCollection requestAttributes;
168 for ( const QgsPointCloudAttribute &attribute : allAttributes )
169 {
170 // For this collection we also need x, y, z apart from the requested attributes
171 if ( attribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) ||
172 attribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) ||
173 attribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) ||
174 mRequestedAttributes.contains( attribute.name(), Qt::CaseInsensitive ) )
175 {
176 requestAttributes.push_back( attribute );
177 }
178 }
179 return requestAttributes;
180}
181
182QgsFields QgsPointCloudLayerExporter::outputFields()
183{
184 const QVector<QgsPointCloudAttribute> attributes = mLayerAttributeCollection.attributes();
185
186 QgsFields fields;
187 for ( const QgsPointCloudAttribute &attribute : attributes )
188 {
189 if ( mRequestedAttributes.contains( attribute.name(), Qt::CaseInsensitive ) )
190 fields.append( QgsField( attribute.name(), attribute.variantType(), attribute.displayType() ) );
191 }
192 return fields;
193}
194
196{
197 delete mMemoryLayer;
198 mMemoryLayer = nullptr;
199
200 if ( mFormat == ExportFormat::Memory )
201 {
202#ifdef QGISDEBUG
203 if ( QApplication::instance()->thread() != QThread::currentThread() )
204 {
205 QgsDebugMsgLevel( QStringLiteral( "prepareExport() should better be called from the main thread!" ), 2 );
206 }
207#endif
208
209 mMemoryLayer = QgsMemoryProviderUtils::createMemoryLayer( mName, outputFields(), Qgis::WkbType::PointZ, mTargetCrs );
210 }
211}
212
214{
215 mTransform = new QgsCoordinateTransform( mSourceCrs, mTargetCrs, mTransformContext );
216 if ( mExtent.isFinite() )
217 {
218 try
219 {
220 mExtent = mTransform->transformBoundingBox( mExtent, Qgis::TransformDirection::Reverse );
221 }
222 catch ( const QgsCsException &cse )
223 {
224 QgsDebugError( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ) );
225 }
226 }
227
228 QStringList layerCreationOptions;
229
230 switch ( mFormat )
231 {
233 {
234 if ( !mMemoryLayer )
236
237 ExporterMemory exp( this );
238 exp.run();
239 break;
240 }
241
243 {
244#ifdef HAVE_PDAL_QGIS
246 // PDAL may throw exceptions
247 try
248 {
249 ExporterPdal exp( this );
250 exp.run();
251 }
252 catch ( std::runtime_error &e )
253 {
254 setLastError( QString::fromLatin1( e.what() ) );
255 QgsDebugError( QStringLiteral( "PDAL has thrown an exception: {}" ).arg( e.what() ) );
256 }
257#endif
258 break;
259 }
260
262 layerCreationOptions << QStringLiteral( "GEOMETRY=AS_XYZ" )
263 << QStringLiteral( "SEPARATOR=COMMA" ); // just in case ogr changes the default lco
264 [[fallthrough]];
268 {
269 const QString ogrDriver = getOgrDriverName( mFormat );
271 saveOptions.layerName = mName;
272 saveOptions.driverName = ogrDriver;
275 saveOptions.layerOptions << layerCreationOptions;
277 saveOptions.actionOnExistingFile = mActionOnExistingFile;
278 saveOptions.feedback = mFeedback;
279 mVectorSink = QgsVectorFileWriter::create( mFilename, outputFields(), Qgis::WkbType::PointZ, mTargetCrs, QgsCoordinateTransformContext(), saveOptions );
280 ExporterVector exp( this );
281 exp.run();
282 return;
283 }
284 }
285}
286
288{
289 switch ( mFormat )
290 {
292 {
293 QgsMapLayer *retVal = mMemoryLayer;
294 mMemoryLayer = nullptr;
295 return retVal;
296 }
297
299 {
300 const QFileInfo fileInfo( mFilename );
301 return new QgsPointCloudLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral( "pdal" ) );
302 }
303
305 {
306 QString uri( mFilename );
307 uri += "|layername=" + mName;
308 return new QgsVectorLayer( uri, mName, QStringLiteral( "ogr" ) );
309 }
310
314 {
315 const QFileInfo fileInfo( mFilename );
316 return new QgsVectorLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
317 }
318 }
320}
321
322//
323// ExporterBase
324//
325
326void QgsPointCloudLayerExporter::ExporterBase::run()
327{
328 QgsRectangle geometryFilterRectangle( -std::numeric_limits<double>::infinity(),
329 -std::numeric_limits<double>::infinity(),
330 std::numeric_limits<double>::infinity(),
331 std::numeric_limits<double>::infinity(),
332 false );
333 if ( mParent->mFilterGeometryEngine )
334 {
335 const QgsAbstractGeometry *envelope = mParent->mFilterGeometryEngine->envelope();
336 if ( envelope )
337 geometryFilterRectangle = envelope->boundingBox();
338 }
339
340 QVector<QgsPointCloudNodeId> nodes;
341 qint64 pointCount = 0;
342 QQueue<QgsPointCloudNodeId> queue;
343 queue.push_back( mParent->mIndex.root() );
344 while ( !queue.empty() )
345 {
346 QgsPointCloudNode node = mParent->mIndex.getNode( queue.front() );
347 queue.pop_front();
348 const QgsBox3D nodeBounds = node.bounds();
349 if ( mParent->mExtent.intersects( nodeBounds.toRectangle() ) &&
350 mParent->mZRange.overlaps( { nodeBounds.zMinimum(), nodeBounds.zMaximum() } ) &&
351 geometryFilterRectangle.intersects( nodeBounds.toRectangle() ) )
352 {
353 pointCount += node.pointCount();
354 nodes.push_back( node.id() );
355 }
356 for ( const QgsPointCloudNodeId &child : node.children() )
357 {
358 queue.push_back( child );
359 }
360 }
361
362 const qint64 pointsToExport = mParent->mPointsLimit > 0 ? std::min( mParent->mPointsLimit, pointCount ) : pointCount;
363 QgsPointCloudRequest request;
364 request.setAttributes( mParent->requestedAttributeCollection() );
365 std::unique_ptr<QgsPointCloudBlock> block = nullptr;
366 qint64 pointsExported = 0;
367 for ( const QgsPointCloudNodeId &node : nodes )
368 {
369 block = mParent->mIndex.nodeData( node, request );
370 const QgsPointCloudAttributeCollection attributesCollection = block->attributes();
371 const char *ptr = block->data();
372 int count = block->pointCount();
373 int recordSize = attributesCollection.pointRecordSize();
374 const QgsVector3D scale = block->scale();
375 const QgsVector3D offset = block->offset();
376 int xOffset = 0, yOffset = 0, zOffset = 0;
377 const QgsPointCloudAttribute::DataType xType = attributesCollection.find( QStringLiteral( "X" ), xOffset )->type();
378 const QgsPointCloudAttribute::DataType yType = attributesCollection.find( QStringLiteral( "Y" ), yOffset )->type();
379 const QgsPointCloudAttribute::DataType zType = attributesCollection.find( QStringLiteral( "Z" ), zOffset )->type();
380 for ( int i = 0; i < count; ++i )
381 {
382
383 if ( mParent->mFeedback &&
384 i % 1000 == 0 )
385 {
386 if ( pointsToExport > 0 )
387 {
388 mParent->mFeedback->setProgress( 100 * static_cast< float >( pointsExported ) / pointsToExport );
389 }
390 if ( mParent->mFeedback->isCanceled() )
391 {
392 mParent->setLastError( QObject::tr( "Canceled by user" ) );
393 return;
394 }
395 }
396
397 if ( pointsExported >= pointsToExport )
398 break;
399
400 double x, y, z;
401 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize,
402 xOffset, xType,
403 yOffset, yType,
404 zOffset, zType,
405 scale, offset,
406 x, y, z );
407 if ( ! mParent->mZRange.contains( z ) ||
408 ! mParent->mExtent.contains( x, y ) ||
409 ( mParent->mFilterGeometryEngine && ! mParent->mFilterGeometryEngine->contains( x, y ) ) )
410 {
411 continue;
412 }
413
414 try
415 {
416 mParent->mTransform->transformInPlace( x, y, z );
417 const QVariantMap attributeMap = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, attributesCollection );
418 handlePoint( x, y, z, attributeMap, pointsExported );
419 ++pointsExported;
420 }
421 catch ( const QgsCsException &cse )
422 {
423 QgsDebugError( QStringLiteral( "Error transforming point: %1" ).arg( cse.what() ) );
424 }
425 }
426 handleNode();
427 }
428 handleAll();
429}
430
431//
432// ExporterMemory
433//
434
435QgsPointCloudLayerExporter::ExporterMemory::ExporterMemory( QgsPointCloudLayerExporter *exp )
436{
437 mParent = exp;
438}
439
440QgsPointCloudLayerExporter::ExporterMemory::~ExporterMemory()
441{
442 mParent->mMemoryLayer->moveToThread( QApplication::instance()->thread() );
443}
444
445void QgsPointCloudLayerExporter::ExporterMemory::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
446{
447 Q_UNUSED( pointNumber )
448
449 QgsFeature feature;
450 feature.setGeometry( QgsGeometry( new QgsPoint( x, y, z ) ) );
451 QgsAttributes featureAttributes;
452 for ( const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
453 {
454 const double val = map[ attribute ].toDouble();
455 featureAttributes.append( val );
456 }
457 feature.setAttributes( featureAttributes );
458 mFeatures.append( feature );
459}
460
461void QgsPointCloudLayerExporter::ExporterMemory::handleNode()
462{
463 QgsVectorLayer *vl = qgis::down_cast<QgsVectorLayer *>( mParent->mMemoryLayer );
464 if ( vl )
465 {
466 if ( ! vl->dataProvider()->addFeatures( mFeatures ) )
467 {
468 mParent->setLastError( vl->dataProvider()->lastError() );
469 }
470 }
471 mFeatures.clear();
472}
473
474void QgsPointCloudLayerExporter::ExporterMemory::handleAll()
475{
476
477}
478
479//
480// ExporterVector
481//
482
483QgsPointCloudLayerExporter::ExporterVector::ExporterVector( QgsPointCloudLayerExporter *exp )
484{
485 mParent = exp;
486}
487
488QgsPointCloudLayerExporter::ExporterVector::~ExporterVector()
489{
490 delete mParent->mVectorSink;
491 mParent->mVectorSink = nullptr;
492}
493
494void QgsPointCloudLayerExporter::ExporterVector::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
495{
496 Q_UNUSED( pointNumber )
497
498 QgsFeature feature;
499 feature.setGeometry( QgsGeometry( new QgsPoint( x, y, z ) ) );
500 QgsAttributes featureAttributes;
501 for ( const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
502 {
503 const double val = map[ attribute ].toDouble();
504 featureAttributes.append( val );
505 }
506 feature.setAttributes( featureAttributes );
507 mFeatures.append( feature );
508}
509
510void QgsPointCloudLayerExporter::ExporterVector::handleNode()
511{
512 if ( ! mParent->mVectorSink->addFeatures( mFeatures ) )
513 {
514 mParent->setLastError( mParent->mVectorSink->lastError() );
515 }
516 mFeatures.clear();
517}
518
519void QgsPointCloudLayerExporter::ExporterVector::handleAll()
520{
521
522}
523
524//
525// ExporterPdal
526//
527
528#ifdef HAVE_PDAL_QGIS
529
530QgsPointCloudLayerExporter::ExporterPdal::ExporterPdal( QgsPointCloudLayerExporter *exp )
531 : mPointFormat( exp->mPointRecordFormat )
532{
533 mParent = exp;
534
535 mOptions.add( "filename", mParent->mFilename.toStdString() );
536 mOptions.add( "a_srs", mParent->mTargetCrs.toWkt().toStdString() );
537 mOptions.add( "minor_version", QStringLiteral( "4" ).toStdString() ); // delault to LAZ 1.4 to properly handle pdrf >= 6
538 mOptions.add( "format", QString::number( mPointFormat ).toStdString() );
539 if ( mParent->mTransform->isShortCircuited() )
540 {
541 mOptions.add( "offset_x", QString::number( mParent->mIndex.offset().x() ).toStdString() );
542 mOptions.add( "offset_y", QString::number( mParent->mIndex.offset().y() ).toStdString() );
543 mOptions.add( "offset_z", QString::number( mParent->mIndex.offset().z() ).toStdString() );
544 mOptions.add( "scale_x", QString::number( mParent->mIndex.scale().x() ).toStdString() );
545 mOptions.add( "scale_y", QString::number( mParent->mIndex.scale().y() ).toStdString() );
546 mOptions.add( "scale_z", QString::number( mParent->mIndex.scale().z() ).toStdString() );
547 }
548
549 mTable.layout()->registerDim( pdal::Dimension::Id::X );
550 mTable.layout()->registerDim( pdal::Dimension::Id::Y );
551 mTable.layout()->registerDim( pdal::Dimension::Id::Z );
552
553 mTable.layout()->registerDim( pdal::Dimension::Id::Classification );
554 mTable.layout()->registerDim( pdal::Dimension::Id::Intensity );
555 mTable.layout()->registerDim( pdal::Dimension::Id::ReturnNumber );
556 mTable.layout()->registerDim( pdal::Dimension::Id::NumberOfReturns );
557 mTable.layout()->registerDim( pdal::Dimension::Id::ScanDirectionFlag );
558 mTable.layout()->registerDim( pdal::Dimension::Id::EdgeOfFlightLine );
559 mTable.layout()->registerDim( pdal::Dimension::Id::ScanAngleRank );
560 mTable.layout()->registerDim( pdal::Dimension::Id::UserData );
561 mTable.layout()->registerDim( pdal::Dimension::Id::PointSourceId );
562
563 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
564 {
565 mTable.layout()->registerDim( pdal::Dimension::Id::ScanChannel );
566 mTable.layout()->registerDim( pdal::Dimension::Id::ClassFlags );
567 }
568
569 if ( mPointFormat != 0 && mPointFormat != 2 )
570 {
571 mTable.layout()->registerDim( pdal::Dimension::Id::GpsTime );
572 }
573
574 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
575 {
576 mTable.layout()->registerDim( pdal::Dimension::Id::Red );
577 mTable.layout()->registerDim( pdal::Dimension::Id::Green );
578 mTable.layout()->registerDim( pdal::Dimension::Id::Blue );
579 }
580
581 if ( mPointFormat == 8 || mPointFormat == 10 )
582 {
583 mTable.layout()->registerDim( pdal::Dimension::Id::Infrared );
584 }
585
586 mView = std::make_shared<pdal::PointView>( mTable );
587}
588
589void QgsPointCloudLayerExporter::ExporterPdal::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
590{
591 mView->setField( pdal::Dimension::Id::X, pointNumber, x );
592 mView->setField( pdal::Dimension::Id::Y, pointNumber, y );
593 mView->setField( pdal::Dimension::Id::Z, pointNumber, z );
594
595
596 mView->setField( pdal::Dimension::Id::Classification, pointNumber, map[ QStringLiteral( "Classification" ) ].toInt() );
597 mView->setField( pdal::Dimension::Id::Intensity, pointNumber, map[ QStringLiteral( "Intensity" ) ].toInt() );
598 mView->setField( pdal::Dimension::Id::ReturnNumber, pointNumber, map[ QStringLiteral( "ReturnNumber" ) ].toInt() );
599 mView->setField( pdal::Dimension::Id::NumberOfReturns, pointNumber, map[ QStringLiteral( "NumberOfReturns" ) ].toInt() );
600 mView->setField( pdal::Dimension::Id::ScanDirectionFlag, pointNumber, map[ QStringLiteral( "ScanDirectionFlag" ) ].toInt() );
601 mView->setField( pdal::Dimension::Id::EdgeOfFlightLine, pointNumber, map[ QStringLiteral( "EdgeOfFlightLine" ) ].toInt() );
602 mView->setField( pdal::Dimension::Id::ScanAngleRank, pointNumber, map[ QStringLiteral( "ScanAngleRank" ) ].toFloat() );
603 mView->setField( pdal::Dimension::Id::UserData, pointNumber, map[ QStringLiteral( "UserData" ) ].toInt() );
604 mView->setField( pdal::Dimension::Id::PointSourceId, pointNumber, map[ QStringLiteral( "PointSourceId" ) ].toInt() );
605
606 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
607 {
608 mView->setField( pdal::Dimension::Id::ScanChannel, pointNumber, map[ QStringLiteral( "ScannerChannel" ) ].toInt() );
609 const int classificationFlags = ( map[ QStringLiteral( "Synthetic" ) ].toInt() & 0x01 ) << 0 |
610 ( map[ QStringLiteral( "KeyPoint" ) ].toInt() & 0x01 ) << 1 |
611 ( map[ QStringLiteral( "Withheld" ) ].toInt() & 0x01 ) << 2 |
612 ( map[ QStringLiteral( "Overlap" ) ].toInt() & 0x01 ) << 3;
613 mView->setField( pdal::Dimension::Id::ClassFlags, pointNumber, classificationFlags );
614 }
615
616 if ( mPointFormat != 0 && mPointFormat != 2 )
617 {
618 mView->setField( pdal::Dimension::Id::GpsTime, pointNumber, map[ QStringLiteral( "GpsTime" ) ].toDouble() );
619 }
620
621 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
622 {
623 mView->setField( pdal::Dimension::Id::Red, pointNumber, map[ QStringLiteral( "Red" ) ].toInt() );
624 mView->setField( pdal::Dimension::Id::Green, pointNumber, map[ QStringLiteral( "Green" ) ].toInt() );
625 mView->setField( pdal::Dimension::Id::Blue, pointNumber, map[ QStringLiteral( "Blue" ) ].toInt() );
626 }
627
628 if ( mPointFormat == 8 || mPointFormat == 10 )
629 {
630 mView->setField( pdal::Dimension::Id::Infrared, pointNumber, map[ QStringLiteral( "Infrared" ) ].toInt() );
631 }
632}
633
634void QgsPointCloudLayerExporter::ExporterPdal::handleNode()
635{
636
637}
638
639void QgsPointCloudLayerExporter::ExporterPdal::handleAll()
640{
641 pdal::BufferReader reader;
642 reader.addView( mView );
643
644 pdal::StageFactory factory;
645
646 pdal::Stage *writer = factory.createStage( "writers.las" );
647
648 writer->setInput( reader );
649 writer->setOptions( mOptions );
650 writer->prepare( mTable );
651 writer->execute( mTable );
652}
653#endif
654
655//
656// QgsPointCloudLayerExporterTask
657//
658
660 : QgsTask( tr( "Exporting point cloud" ), QgsTask::CanCancel )
661 , mExp( exporter )
662 , mOwnedFeedback( new QgsFeedback() )
663{
664}
665
667{
668 mOwnedFeedback->cancel();
670}
671
673{
674 if ( !mExp )
675 return false;
676
677 connect( mOwnedFeedback.get(), &QgsFeedback::progressChanged, this, &QgsPointCloudLayerExporterTask::setProgress );
678 mExp->setFeedback( mOwnedFeedback.get() );
679
680 mExp->doExport();
681
682 return true;
683}
684
686{
687 Q_UNUSED( result )
688
689 emit exportComplete();
690 delete mExp;
691}
@ PointZ
PointZ.
Definition qgis.h:295
@ NoSymbology
Export only data.
Definition qgis.h:5558
@ Reverse
Reverse/inverse transform (from destination to source).
Definition qgis.h:2673
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:378
Represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Handles coordinate transforms between two coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
QString what() const
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:58
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
QgsGeometry geometry
Definition qgsfeature.h:69
bool hasGeometry() const
Returns true if the feature has an associated geometry.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:73
A geometry is the spatial representation of a feature.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false)
Transforms this geometry as described by the coordinate transform ct.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters &parameters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
Base class for all map layer types.
Definition qgsmaplayer.h:80
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, Qgis::WkbType geometryType=Qgis::WkbType::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem(), bool loadDefaultStyle=true) SIP_FACTORY
Creates a new memory layer using the specified parameters.
A collection of point cloud attributes.
void push_back(const QgsPointCloudAttribute &attribute)
Adds extra attribute.
int pointRecordSize() const
Returns total size of record.
const QgsPointCloudAttribute * find(const QString &attributeName, int &offset) const
Finds the attribute with the name.
QVector< QgsPointCloudAttribute > attributes() const
Returns all attributes.
Attribute for point cloud data pair of name and size in bytes.
DataType
Systems of unit measurement.
static void getPointXYZ(const char *ptr, int i, std::size_t pointRecordSize, int xOffset, QgsPointCloudAttribute::DataType xType, int yOffset, QgsPointCloudAttribute::DataType yType, int zOffset, QgsPointCloudAttribute::DataType zType, const QgsVector3D &indexScale, const QgsVector3D &indexOffset, double &x, double &y, double &z)
Retrieves the x, y, z values for the point at index i.
static QVariantMap getAttributeMap(const char *data, std::size_t recordOffset, const QgsPointCloudAttributeCollection &attributeCollection)
Retrieves all the attributes of a point.
DataType type() const
Returns the data type.
void cancel() override
Notifies the task that it should terminate.
QgsPointCloudLayerExporterTask(QgsPointCloudLayerExporter *exporter)
Constructor for QgsPointCloudLayerExporterTask.
void exportComplete()
Emitted when exporting the layer is successfully completed.
void finished(bool result) override
If the task is managed by a QgsTaskManager, this will be called after the task has finished (whether ...
bool run() override
Performs the task's operation.
Handles exporting point cloud layers to memory layers, OGR supported files and PDAL supported files.
QgsMapLayer * takeExportedLayer()
Gets a pointer to the exported layer.
QgsCoordinateReferenceSystem crs() const
Gets the crs for the exported file.
ExportFormat format() const
Returns the format for the exported file or layer.
void setAttributes(const QStringList &attributes)
Sets the list of point cloud attributes that will be exported.
ExportFormat
Supported export formats for point clouds.
void setAllAttributes()
Sets that all attributes will be exported.
bool setFormat(const ExportFormat format)
Sets the format for the exported file.
static QString getOgrDriverName(ExportFormat format)
Gets the OGR driver name for the specified format.
QStringList attributes() const
Gets the list of point cloud attributes that will be exported.
QgsPointCloudLayerExporter(QgsPointCloudLayer *layer)
Constructor for QgsPointCloudLayerExporter, associated with the specified layer.
void prepareExport()
Creates the QgsVectorLayer for exporting to a memory layer, if necessary.
static QList< ExportFormat > supportedFormats()
Gets a list of the supported export formats.
void setFilterGeometry(const QgsAbstractGeometry *geometry)
Sets a spatial filter for points to be exported based on geom in the point cloud's CRS.
void doExport()
Performs the actual exporting operation.
Represents a map layer supporting display of point clouds.
QgsPointCloudDataProvider * dataProvider() override
Returns the layer's data provider, it may be nullptr.
QList< QgsPointCloudNodeId > children() const
Returns IDs of child nodes.
qint64 pointCount() const
Returns number of points contained in node data.
QgsPointCloudNodeId id() const
Returns node's ID (unique in index).
QgsBox3D bounds() const
Returns node's bounding cube in CRS coords.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
A rectangle specified with double values.
virtual void cancel()
Notifies the task that it should terminate.
QgsTask(const QString &description=QString(), QgsTask::Flags flags=AllFlags)
Constructor for QgsTask.
@ CanCancel
Task can be canceled.
void setProgress(double progress)
Sets the task's current progress.
QString lastError() const override
Returns the most recent error encountered by the sink, e.g.
bool addFeatures(QgsFeatureList &flist, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features to the sink.
Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
QString layerName
Layer name. If let empty, it will be derived from the filename.
QStringList layerOptions
List of OGR layer creation options.
Qgis::FeatureSymbologyExport symbologyExport
Symbology to export.
QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile
Action on existing file.
QStringList datasourceOptions
List of OGR data source creation options.
QgsFeedback * feedback
Optional feedback object allowing cancellation of layer save.
static QStringList defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
static QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, Qgis::WkbType geometryType, const QgsCoordinateReferenceSystem &srs, const QgsCoordinateTransformContext &transformContext, const QgsVectorFileWriter::SaveVectorOptions &options, QgsFeatureSink::SinkFlags sinkFlags=QgsFeatureSink::SinkFlags(), QString *newFilename=nullptr, QString *newLayer=nullptr)
Create a new vector file writer.
static QStringList defaultDatasetOptions(const QString &driverName)
Returns a list of the default dataset options for a specified driver.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
QgsVectorDataProvider * dataProvider() final
Returns the layer's data provider, it may be nullptr.
#define BUILTIN_UNREACHABLE
Definition qgis.h:7208
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57