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