QGIS API Documentation 3.41.0-Master (af5edcb665c)
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
18#include <QQueue>
19#include <QFileInfo>
20#include <QApplication>
21#include <QThread>
22
24#include "moc_qgspointcloudlayerexporter.cpp"
27#include "qgsrectangle.h"
28#include "qgsvectorfilewriter.h"
29#include "qgsgeos.h"
30
31#ifdef HAVE_PDAL_QGIS
32#include <pdal/StageFactory.hpp>
33#include <pdal/io/BufferReader.hpp>
34#include <pdal/Dimension.hpp>
35#endif
36
38{
39 switch ( format )
40 {
42 return QStringLiteral( "GPKG" );
44 return QStringLiteral( "DXF" );
46 return QStringLiteral( "ESRI Shapefile" );
48 return QStringLiteral( "CSV" );
51 break;
52 }
53 return QString();
54}
55
57 : mLayerAttributeCollection( layer->attributes() )
58 , mIndex( layer->dataProvider()->index()->clone() )
59 , mSourceCrs( QgsCoordinateReferenceSystem( layer->crs() ) )
60 , mTargetCrs( QgsCoordinateReferenceSystem( layer->crs() ) )
61{
62 bool ok;
63 mPointRecordFormat = layer->dataProvider()->originalMetadata().value( QStringLiteral( "dataformat_id" ) ).toInt( &ok );
64 if ( !ok )
65 mPointRecordFormat = 3;
66
68}
69
71{
72 delete mMemoryLayer;
73 delete mVectorSink;
74 delete mTransform;
75}
76
78{
79 if ( supportedFormats().contains( format ) )
80 {
81 mFormat = format;
82 return true;
83 }
84 return false;
85}
86
88{
89 mFilterGeometryEngine.reset( new QgsGeos( geometry ) );
90 mFilterGeometryEngine->prepareGeometry();
91}
92
93void QgsPointCloudLayerExporter::setFilterGeometry( QgsMapLayer *layer, bool selectedFeaturesOnly )
94{
95 QgsVectorLayer *vlayer = dynamic_cast< QgsVectorLayer * >( layer );
96 if ( !vlayer )
97 return;
98
99 QVector< QgsGeometry > allGeometries;
101 const QgsFeatureRequest request = QgsFeatureRequest( mExtent ).setNoAttributes();
102 if ( selectedFeaturesOnly )
103 fit = vlayer->getSelectedFeatures( request );
104 else
105 fit = vlayer->getFeatures( request );
106
107 QgsCoordinateTransform transform( vlayer->crs(), mSourceCrs, mTransformContext );
108
109 QgsFeature f;
110 while ( fit.nextFeature( f ) )
111 {
112 if ( f.hasGeometry() )
113 {
114 allGeometries.append( f.geometry() );
115 }
116 }
117 QgsGeometry unaryUnion = QgsGeometry::unaryUnion( allGeometries );
118 try
119 {
120 unaryUnion.transform( transform );
121 }
122 catch ( const QgsCsException &cse )
123 {
124 QgsDebugError( QStringLiteral( "Error transforming union of filter layer: %1" ).arg( cse.what() ) );
125 QgsDebugError( QStringLiteral( "FilterGeometry will be ignored." ) );
126 return;
127 }
128 setFilterGeometry( unaryUnion.constGet() );
129}
130
131void QgsPointCloudLayerExporter::setAttributes( const QStringList &attributeList )
132{
133 mRequestedAttributes.clear();
134
135 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
136 for ( const QgsPointCloudAttribute &attribute : allAttributes )
137 {
138 // Don't add x, y, z or duplicate attributes
139 if ( attribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) &&
140 attribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) &&
141 attribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) &&
142 attributeList.contains( attribute.name() ) &&
143 ! mRequestedAttributes.contains( attribute.name() ) )
144 {
145 mRequestedAttributes.append( attribute.name() );
146 }
147 }
148}
149
151{
152 QStringList allAttributeNames;
153 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
154 for ( const QgsPointCloudAttribute &attribute : allAttributes )
155 {
156 allAttributeNames.append( attribute.name() );
157 }
158 setAttributes( allAttributeNames );
159}
160
161const QgsPointCloudAttributeCollection QgsPointCloudLayerExporter::requestedAttributeCollection()
162{
163 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.attributes();
164 QgsPointCloudAttributeCollection requestAttributes;
165 for ( const QgsPointCloudAttribute &attribute : allAttributes )
166 {
167 // For this collection we also need x, y, z apart from the requested attributes
168 if ( attribute.name().compare( QLatin1String( "X" ), Qt::CaseInsensitive ) ||
169 attribute.name().compare( QLatin1String( "Y" ), Qt::CaseInsensitive ) ||
170 attribute.name().compare( QLatin1String( "Z" ), Qt::CaseInsensitive ) ||
171 mRequestedAttributes.contains( attribute.name(), Qt::CaseInsensitive ) )
172 {
173 requestAttributes.push_back( attribute );
174 }
175 }
176 return requestAttributes;
177}
178
179QgsFields QgsPointCloudLayerExporter::outputFields()
180{
181 const QVector<QgsPointCloudAttribute> attributes = mLayerAttributeCollection.attributes();
182
183 QgsFields fields;
184 for ( const QgsPointCloudAttribute &attribute : attributes )
185 {
186 if ( mRequestedAttributes.contains( attribute.name(), Qt::CaseInsensitive ) )
187 fields.append( QgsField( attribute.name(), attribute.variantType(), attribute.displayType() ) );
188 }
189 return fields;
190}
191
193{
194 delete mMemoryLayer;
195 mMemoryLayer = nullptr;
196
197 if ( mFormat == ExportFormat::Memory )
198 {
199#ifdef QGISDEBUG
200 if ( QApplication::instance()->thread() != QThread::currentThread() )
201 {
202 QgsDebugMsgLevel( QStringLiteral( "prepareExport() should better be called from the main thread!" ), 2 );
203 }
204#endif
205
206 mMemoryLayer = QgsMemoryProviderUtils::createMemoryLayer( mName, outputFields(), Qgis::WkbType::PointZ, mTargetCrs );
207 }
208}
209
211{
212 mTransform = new QgsCoordinateTransform( mSourceCrs, mTargetCrs, mTransformContext );
213 if ( mExtent.isFinite() )
214 {
215 try
216 {
217 mExtent = mTransform->transformBoundingBox( mExtent, Qgis::TransformDirection::Reverse );
218 }
219 catch ( const QgsCsException &cse )
220 {
221 QgsDebugError( QStringLiteral( "Error transforming extent: %1" ).arg( cse.what() ) );
222 }
223 }
224
225 QStringList layerCreationOptions;
226
227 switch ( mFormat )
228 {
230 {
231 if ( !mMemoryLayer )
233
234 ExporterMemory exp( this );
235 exp.run();
236 break;
237 }
238
240 {
241#ifdef HAVE_PDAL_QGIS
243 // PDAL may throw exceptions
244 try
245 {
246 ExporterPdal exp( this );
247 exp.run();
248 }
249 catch ( std::runtime_error &e )
250 {
251 setLastError( QString::fromLatin1( e.what() ) );
252 QgsDebugError( QStringLiteral( "PDAL has thrown an exception: {}" ).arg( e.what() ) );
253 }
254#endif
255 break;
256 }
257
259 layerCreationOptions << QStringLiteral( "GEOMETRY=AS_XYZ" )
260 << QStringLiteral( "SEPARATOR=COMMA" ); // just in case ogr changes the default lco
261 [[fallthrough]];
265 {
266 const QString ogrDriver = getOgrDriverName( mFormat );
268 saveOptions.layerName = mName;
269 saveOptions.driverName = ogrDriver;
272 saveOptions.layerOptions << layerCreationOptions;
274 saveOptions.actionOnExistingFile = mActionOnExistingFile;
275 saveOptions.feedback = mFeedback;
276 mVectorSink = QgsVectorFileWriter::create( mFilename, outputFields(), Qgis::WkbType::PointZ, mTargetCrs, QgsCoordinateTransformContext(), saveOptions );
277 ExporterVector exp( this );
278 exp.run();
279 return;
280 }
281 }
282}
283
285{
286 switch ( mFormat )
287 {
289 {
290 QgsMapLayer *retVal = mMemoryLayer;
291 mMemoryLayer = nullptr;
292 return retVal;
293 }
294
296 {
297 const QFileInfo fileInfo( mFilename );
298 return new QgsPointCloudLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral( "pdal" ) );
299 }
300
302 {
303 QString uri( mFilename );
304 uri += "|layername=" + mName;
305 return new QgsVectorLayer( uri, mName, QStringLiteral( "ogr" ) );
306 }
307
311 {
312 const QFileInfo fileInfo( mFilename );
313 return new QgsVectorLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral( "ogr" ) );
314 }
315 }
317}
318
319//
320// ExporterBase
321//
322
323void QgsPointCloudLayerExporter::ExporterBase::run()
324{
325 QgsRectangle geometryFilterRectangle( -std::numeric_limits<double>::infinity(),
326 -std::numeric_limits<double>::infinity(),
327 std::numeric_limits<double>::infinity(),
328 std::numeric_limits<double>::infinity(),
329 false );
330 if ( mParent->mFilterGeometryEngine )
331 {
332 const QgsAbstractGeometry *envelope = mParent->mFilterGeometryEngine->envelope();
333 if ( envelope )
334 geometryFilterRectangle = envelope->boundingBox();
335 }
336
337 QVector<QgsPointCloudNodeId> nodes;
338 qint64 pointCount = 0;
339 QQueue<QgsPointCloudNodeId> queue;
340 queue.push_back( mParent->mIndex->root() );
341 while ( !queue.empty() )
342 {
343 QgsPointCloudNode node = mParent->mIndex->getNode( queue.front() );
344 queue.pop_front();
345 const QgsBox3D nodeBounds = node.bounds();
346 if ( mParent->mExtent.intersects( nodeBounds.toRectangle() ) &&
347 mParent->mZRange.overlaps( { nodeBounds.zMinimum(), nodeBounds.zMaximum() } ) &&
348 geometryFilterRectangle.intersects( nodeBounds.toRectangle() ) )
349 {
350 pointCount += node.pointCount();
351 nodes.push_back( node.id() );
352 }
353 for ( const QgsPointCloudNodeId &child : node.children() )
354 {
355 queue.push_back( child );
356 }
357 }
358
359 const qint64 pointsToExport = mParent->mPointsLimit > 0 ? std::min( mParent->mPointsLimit, pointCount ) : pointCount;
360 QgsPointCloudRequest request;
361 request.setAttributes( mParent->requestedAttributeCollection() );
362 std::unique_ptr<QgsPointCloudBlock> block = nullptr;
363 qint64 pointsExported = 0;
364 for ( const QgsPointCloudNodeId &node : nodes )
365 {
366 block = mParent->mIndex->nodeData( node, request );
367 const QgsPointCloudAttributeCollection attributesCollection = block->attributes();
368 const char *ptr = block->data();
369 int count = block->pointCount();
370 int recordSize = attributesCollection.pointRecordSize();
371 const QgsVector3D scale = block->scale();
372 const QgsVector3D offset = block->offset();
373 int xOffset = 0, yOffset = 0, zOffset = 0;
374 const QgsPointCloudAttribute::DataType xType = attributesCollection.find( QStringLiteral( "X" ), xOffset )->type();
375 const QgsPointCloudAttribute::DataType yType = attributesCollection.find( QStringLiteral( "Y" ), yOffset )->type();
376 const QgsPointCloudAttribute::DataType zType = attributesCollection.find( QStringLiteral( "Z" ), zOffset )->type();
377 for ( int i = 0; i < count; ++i )
378 {
379
380 if ( mParent->mFeedback &&
381 i % 1000 == 0 )
382 {
383 mParent->mFeedback->setProgress( 100 * static_cast< float >( pointsExported ) / pointsToExport );
384 if ( mParent->mFeedback->isCanceled() )
385 {
386 mParent->setLastError( QObject::tr( "Canceled by user" ) );
387 return;
388 }
389 }
390
391 if ( pointsExported >= pointsToExport )
392 break;
393
394 double x, y, z;
395 QgsPointCloudAttribute::getPointXYZ( ptr, i, recordSize,
396 xOffset, xType,
397 yOffset, yType,
398 zOffset, zType,
399 scale, offset,
400 x, y, z );
401 if ( ! mParent->mZRange.contains( z ) ||
402 ! mParent->mExtent.contains( x, y ) ||
403 ( mParent->mFilterGeometryEngine && ! mParent->mFilterGeometryEngine->contains( x, y ) ) )
404 {
405 continue;
406 }
407
408 try
409 {
410 mParent->mTransform->transformInPlace( x, y, z );
411 const QVariantMap attributeMap = QgsPointCloudAttribute::getAttributeMap( ptr, i * recordSize, attributesCollection );
412 handlePoint( x, y, z, attributeMap, pointsExported );
413 ++pointsExported;
414 }
415 catch ( const QgsCsException &cse )
416 {
417 QgsDebugError( QStringLiteral( "Error transforming point: %1" ).arg( cse.what() ) );
418 }
419 }
420 handleNode();
421 }
422 handleAll();
423}
424
425//
426// ExporterMemory
427//
428
429QgsPointCloudLayerExporter::ExporterMemory::ExporterMemory( QgsPointCloudLayerExporter *exp )
430{
431 mParent = exp;
432}
433
434QgsPointCloudLayerExporter::ExporterMemory::~ExporterMemory()
435{
436 mParent->mMemoryLayer->moveToThread( QApplication::instance()->thread() );
437}
438
439void QgsPointCloudLayerExporter::ExporterMemory::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
440{
441 Q_UNUSED( pointNumber )
442
443 QgsFeature feature;
444 feature.setGeometry( QgsGeometry( new QgsPoint( x, y, z ) ) );
445 QgsAttributes featureAttributes;
446 for ( const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
447 {
448 const double val = map[ attribute ].toDouble();
449 featureAttributes.append( val );
450 }
451 feature.setAttributes( featureAttributes );
452 mFeatures.append( feature );
453}
454
455void QgsPointCloudLayerExporter::ExporterMemory::handleNode()
456{
457 QgsVectorLayer *vl = qgis::down_cast<QgsVectorLayer *>( mParent->mMemoryLayer );
458 if ( vl )
459 {
460 if ( ! vl->dataProvider()->addFeatures( mFeatures ) )
461 {
462 mParent->setLastError( vl->dataProvider()->lastError() );
463 }
464 }
465 mFeatures.clear();
466}
467
468void QgsPointCloudLayerExporter::ExporterMemory::handleAll()
469{
470
471}
472
473//
474// ExporterVector
475//
476
477QgsPointCloudLayerExporter::ExporterVector::ExporterVector( QgsPointCloudLayerExporter *exp )
478{
479 mParent = exp;
480}
481
482QgsPointCloudLayerExporter::ExporterVector::~ExporterVector()
483{
484 delete mParent->mVectorSink;
485 mParent->mVectorSink = nullptr;
486}
487
488void QgsPointCloudLayerExporter::ExporterVector::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
489{
490 Q_UNUSED( pointNumber )
491
492 QgsFeature feature;
493 feature.setGeometry( QgsGeometry( new QgsPoint( x, y, z ) ) );
494 QgsAttributes featureAttributes;
495 for ( const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
496 {
497 const double val = map[ attribute ].toDouble();
498 featureAttributes.append( val );
499 }
500 feature.setAttributes( featureAttributes );
501 mFeatures.append( feature );
502}
503
504void QgsPointCloudLayerExporter::ExporterVector::handleNode()
505{
506 if ( ! mParent->mVectorSink->addFeatures( mFeatures ) )
507 {
508 mParent->setLastError( mParent->mVectorSink->lastError() );
509 }
510 mFeatures.clear();
511}
512
513void QgsPointCloudLayerExporter::ExporterVector::handleAll()
514{
515
516}
517
518//
519// ExporterPdal
520//
521
522#ifdef HAVE_PDAL_QGIS
523
524QgsPointCloudLayerExporter::ExporterPdal::ExporterPdal( QgsPointCloudLayerExporter *exp )
525 : mPointFormat( exp->mPointRecordFormat )
526{
527 mParent = exp;
528
529 mOptions.add( "filename", mParent->mFilename.toStdString() );
530 mOptions.add( "a_srs", mParent->mTargetCrs.toWkt().toStdString() );
531 mOptions.add( "minor_version", QStringLiteral( "4" ).toStdString() ); // delault to LAZ 1.4 to properly handle pdrf >= 6
532 mOptions.add( "format", QString::number( mPointFormat ).toStdString() );
533 if ( mParent->mTransform->isShortCircuited() )
534 {
535 mOptions.add( "offset_x", QString::number( mParent->mIndex->offset().x() ).toStdString() );
536 mOptions.add( "offset_y", QString::number( mParent->mIndex->offset().y() ).toStdString() );
537 mOptions.add( "offset_z", QString::number( mParent->mIndex->offset().z() ).toStdString() );
538 mOptions.add( "scale_x", QString::number( mParent->mIndex->scale().x() ).toStdString() );
539 mOptions.add( "scale_y", QString::number( mParent->mIndex->scale().y() ).toStdString() );
540 mOptions.add( "scale_z", QString::number( mParent->mIndex->scale().z() ).toStdString() );
541 }
542
543 mTable.layout()->registerDim( pdal::Dimension::Id::X );
544 mTable.layout()->registerDim( pdal::Dimension::Id::Y );
545 mTable.layout()->registerDim( pdal::Dimension::Id::Z );
546
547 mTable.layout()->registerDim( pdal::Dimension::Id::Classification );
548 mTable.layout()->registerDim( pdal::Dimension::Id::Intensity );
549 mTable.layout()->registerDim( pdal::Dimension::Id::ReturnNumber );
550 mTable.layout()->registerDim( pdal::Dimension::Id::NumberOfReturns );
551 mTable.layout()->registerDim( pdal::Dimension::Id::ScanDirectionFlag );
552 mTable.layout()->registerDim( pdal::Dimension::Id::EdgeOfFlightLine );
553 mTable.layout()->registerDim( pdal::Dimension::Id::ScanAngleRank );
554 mTable.layout()->registerDim( pdal::Dimension::Id::UserData );
555 mTable.layout()->registerDim( pdal::Dimension::Id::PointSourceId );
556
557 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
558 {
559 mTable.layout()->registerDim( pdal::Dimension::Id::ScanChannel );
560 mTable.layout()->registerDim( pdal::Dimension::Id::ClassFlags );
561 }
562
563 if ( mPointFormat != 0 && mPointFormat != 2 )
564 {
565 mTable.layout()->registerDim( pdal::Dimension::Id::GpsTime );
566 }
567
568 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
569 {
570 mTable.layout()->registerDim( pdal::Dimension::Id::Red );
571 mTable.layout()->registerDim( pdal::Dimension::Id::Green );
572 mTable.layout()->registerDim( pdal::Dimension::Id::Blue );
573 }
574
575 if ( mPointFormat == 8 || mPointFormat == 10 )
576 {
577 mTable.layout()->registerDim( pdal::Dimension::Id::Infrared );
578 }
579
580 mView.reset( new pdal::PointView( mTable ) );
581}
582
583void QgsPointCloudLayerExporter::ExporterPdal::handlePoint( double x, double y, double z, const QVariantMap &map, const qint64 pointNumber )
584{
585 mView->setField( pdal::Dimension::Id::X, pointNumber, x );
586 mView->setField( pdal::Dimension::Id::Y, pointNumber, y );
587 mView->setField( pdal::Dimension::Id::Z, pointNumber, z );
588
589
590 mView->setField( pdal::Dimension::Id::Classification, pointNumber, map[ QStringLiteral( "Classification" ) ].toInt() );
591 mView->setField( pdal::Dimension::Id::Intensity, pointNumber, map[ QStringLiteral( "Intensity" ) ].toInt() );
592 mView->setField( pdal::Dimension::Id::ReturnNumber, pointNumber, map[ QStringLiteral( "ReturnNumber" ) ].toInt() );
593 mView->setField( pdal::Dimension::Id::NumberOfReturns, pointNumber, map[ QStringLiteral( "NumberOfReturns" ) ].toInt() );
594 mView->setField( pdal::Dimension::Id::ScanDirectionFlag, pointNumber, map[ QStringLiteral( "ScanDirectionFlag" ) ].toInt() );
595 mView->setField( pdal::Dimension::Id::EdgeOfFlightLine, pointNumber, map[ QStringLiteral( "EdgeOfFlightLine" ) ].toInt() );
596 mView->setField( pdal::Dimension::Id::ScanAngleRank, pointNumber, map[ QStringLiteral( "ScanAngleRank" ) ].toFloat() );
597 mView->setField( pdal::Dimension::Id::UserData, pointNumber, map[ QStringLiteral( "UserData" ) ].toInt() );
598 mView->setField( pdal::Dimension::Id::PointSourceId, pointNumber, map[ QStringLiteral( "PointSourceId" ) ].toInt() );
599
600 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
601 {
602 mView->setField( pdal::Dimension::Id::ScanChannel, pointNumber, map[ QStringLiteral( "ScannerChannel" ) ].toInt() );
603 const int classificationFlags = ( map[ QStringLiteral( "Synthetic" ) ].toInt() & 0x01 ) << 0 |
604 ( map[ QStringLiteral( "KeyPoint" ) ].toInt() & 0x01 ) << 1 |
605 ( map[ QStringLiteral( "Withheld" ) ].toInt() & 0x01 ) << 2 |
606 ( map[ QStringLiteral( "Overlap" ) ].toInt() & 0x01 ) << 3;
607 mView->setField( pdal::Dimension::Id::ClassFlags, pointNumber, classificationFlags );
608 }
609
610 if ( mPointFormat != 0 && mPointFormat != 2 )
611 {
612 mView->setField( pdal::Dimension::Id::GpsTime, pointNumber, map[ QStringLiteral( "GpsTime" ) ].toDouble() );
613 }
614
615 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
616 {
617 mView->setField( pdal::Dimension::Id::Red, pointNumber, map[ QStringLiteral( "Red" ) ].toInt() );
618 mView->setField( pdal::Dimension::Id::Green, pointNumber, map[ QStringLiteral( "Green" ) ].toInt() );
619 mView->setField( pdal::Dimension::Id::Blue, pointNumber, map[ QStringLiteral( "Blue" ) ].toInt() );
620 }
621
622 if ( mPointFormat == 8 || mPointFormat == 10 )
623 {
624 mView->setField( pdal::Dimension::Id::Infrared, pointNumber, map[ QStringLiteral( "Infrared" ) ].toInt() );
625 }
626}
627
628void QgsPointCloudLayerExporter::ExporterPdal::handleNode()
629{
630
631}
632
633void QgsPointCloudLayerExporter::ExporterPdal::handleAll()
634{
635 pdal::BufferReader reader;
636 reader.addView( mView );
637
638 pdal::StageFactory factory;
639
640 pdal::Stage *writer = factory.createStage( "writers.las" );
641
642 writer->setInput( reader );
643 writer->setOptions( mOptions );
644 writer->prepare( mTable );
645 writer->execute( mTable );
646}
647#endif
648
649//
650// QgsPointCloudLayerExporterTask
651//
652
654 : QgsTask( tr( "Exporting point cloud" ), QgsTask::CanCancel )
655 , mExp( exporter )
656 , mOwnedFeedback( new QgsFeedback() )
657{
658}
659
661{
662 mOwnedFeedback->cancel();
664}
665
667{
668 if ( !mExp )
669 return false;
670
671 connect( mOwnedFeedback.get(), &QgsFeedback::progressChanged, this, &QgsPointCloudLayerExporterTask::setProgress );
672 mExp->setFeedback( mOwnedFeedback.get() );
673
674 mExp->doExport();
675
676 return true;
677}
678
680{
681 Q_UNUSED( result )
682
683 emit exportComplete();
684 delete mExp;
685}
@ PointZ
PointZ.
@ NoSymbology
Export only data.
@ Reverse
Reverse/inverse transform (from destination to source)
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const
Returns the minimal bounding box for the geometry.
A vector of attributes.
A 3-dimensional box composed of x, y, z coordinates.
Definition qgsbox3d.h:43
QgsRectangle toRectangle() const
Converts the box to a 2D rectangle.
Definition qgsbox3d.h:394
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Class for doing transforms between two map coordinate systems.
QgsRectangle transformBoundingBox(const QgsRectangle &rectangle, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool handle180Crossover=false) const
Transforms a rectangle from the source CRS to the destination CRS.
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.
This class 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.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
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:70
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.
Does vector analysis using the GEOS library and handles import, export, and exception handling.
Definition qgsgeos.h:139
Base class for all map layer types.
Definition qgsmaplayer.h:76
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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.
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.
virtual QVariantMap originalMetadata() const
Returns a representation of the original metadata included in a point cloud dataset.
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.
void setFeedback(QgsFeedback *feedback)
Sets a QgsFeedback object to allow cancellation / progress reporting.
QgsMapLayer * takeExportedLayer()
Gets a pointer to the exported layer.
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.
Represents a indexed point cloud node's position in octree.
Keeps metadata for indexed point cloud node.
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.
Point cloud data request.
void setAttributes(const QgsPointCloudAttributeCollection &attributes)
Set attributes filter in the request.
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:49
A rectangle specified with double values.
bool isFinite() const
Returns true if the rectangle has finite boundaries.
Abstract base class for long running background tasks.
virtual void cancel()
Notifies the task that it should terminate.
void setProgress(double progress)
Sets the task's current progress.
Class for storage of 3D vectors similar to QVector3D, with the difference that it uses double precisi...
Definition qgsvector3d.h:31
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 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 data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFeatureIterator getSelectedFeatures(QgsFeatureRequest request=QgsFeatureRequest()) const
Returns an iterator of the selected features.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
#define BUILTIN_UNREACHABLE
Definition qgis.h:6720
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
const QgsCoordinateReferenceSystem & crs