20#include <QApplication>
30#include <pdal/StageFactory.hpp>
31#include <pdal/io/BufferReader.hpp>
32#include <pdal/Dimension.hpp>
40 return QStringLiteral(
"GPKG" );
42 return QStringLiteral(
"DXF" );
44 return QStringLiteral(
"ESRI Shapefile" );
46 return QStringLiteral(
"CSV" );
55 : mLayerAttributeCollection( layer->attributes() )
56 , mIndex( layer->dataProvider()->index()->clone().release() )
63 mPointRecordFormat = 3;
87 mFilterGeometryEngine.reset(
new QgsGeos( geometry ) );
88 mFilterGeometryEngine->prepareGeometry();
97 QVector< QgsGeometry > allGeometries;
100 if ( selectedFeaturesOnly )
112 allGeometries.append( f.
geometry() );
122 QgsDebugMsg( QStringLiteral(
"Error transforming union of filter layer: %1" ).arg( cse.
what() ) );
123 QgsDebugMsg( QStringLiteral(
"FilterGeometry will be ignored." ) );
131 mRequestedAttributes.clear();
133 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.
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() ) )
143 mRequestedAttributes.append( attribute.name() );
150 QStringList allAttributeNames;
151 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.
attributes();
154 allAttributeNames.append( attribute.name() );
161 const QVector<QgsPointCloudAttribute> allAttributes = mLayerAttributeCollection.
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 ) )
171 requestAttributes.
push_back( attribute );
174 return requestAttributes;
177QgsFields QgsPointCloudLayerExporter::outputFields()
184 if ( mRequestedAttributes.contains( attribute.name(), Qt::CaseInsensitive ) )
185 fields.
append(
QgsField( attribute.name(), attribute.variantType(), attribute.displayType() ) );
193 mMemoryLayer =
nullptr;
197 if ( QApplication::instance()->thread() != QThread::currentThread() )
198 QgsDebugMsgLevel( QStringLiteral(
"prepareExport() should better be called from the main thread!" ), 2 );
215 QgsDebugMsg( QStringLiteral(
"Error transforming extent: %1" ).arg( cse.
what() ) );
219 QStringList layerCreationOptions;
228 ExporterMemory exp(
this );
240 ExporterPdal exp(
this );
243 catch ( std::runtime_error &e )
245 setLastError( QString::fromLatin1( e.what() ) );
246 QgsDebugMsg( QStringLiteral(
"PDAL has thrown an exception: {}" ).arg( e.what() ) );
253 layerCreationOptions << QStringLiteral(
"GEOMETRY=AS_XYZ" )
254 << QStringLiteral(
"SEPARATOR=COMMA" );
271 ExporterVector exp(
this );
285 mMemoryLayer =
nullptr;
291 const QFileInfo fileInfo( mFilename );
292 return new QgsPointCloudLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral(
"pdal" ) );
297 QString uri( mFilename );
298 uri +=
"|layername=" + mName;
306 const QFileInfo fileInfo( mFilename );
307 return new QgsVectorLayer( mFilename, fileInfo.completeBaseName(), QStringLiteral(
"ogr" ) );
317void QgsPointCloudLayerExporter::ExporterBase::run()
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(),
324 if ( mParent->mFilterGeometryEngine )
331 QVector<IndexedPointCloudNode> nodes;
332 qint64 pointCount = 0;
333 QQueue<IndexedPointCloudNode> queue;
334 queue.push_back( mParent->mIndex->root() );
335 while ( !queue.empty() )
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 ) )
344 pointCount += mParent->mIndex->nodePointCount( node );
345 nodes.push_back( node );
349 queue.push_back( child );
355 int pointsSkipped = 0;
356 const qint64 pointsToExport = mParent->mPointsLimit > 0 ? std::min( mParent->mPointsLimit, pointCount ) : pointCount;
358 request.
setAttributes( mParent->requestedAttributeCollection() );
359 std::unique_ptr<QgsPointCloudBlock> block =
nullptr;
360 qint64 pointsExported = 0;
363 block.reset( mParent->mIndex->nodeData( node, request ) );
365 const char *ptr = block->data();
366 int count = block->pointCount();
370 int xOffset = 0, yOffset = 0, zOffset = 0;
374 for (
int i = 0; i < count; ++i )
377 if ( mParent->mFeedback &&
380 mParent->mFeedback->setProgress( 100 *
static_cast< float >( pointsExported ) / pointsToExport );
381 if ( mParent->mFeedback->isCanceled() )
383 mParent->setLastError( QObject::tr(
"Canceled by user" ) );
388 if ( pointsExported >= pointsToExport )
398 if ( ! mParent->mZRange.contains( z ) ||
399 ! mParent->mExtent.contains( x, y ) ||
400 ( mParent->mFilterGeometryEngine && ! mParent->mFilterGeometryEngine->contains( x, y ) ) )
408 mParent->mTransform->transformInPlace( x, y, z );
410 handlePoint( x, y, z, attributeMap, pointsExported );
415 QgsDebugMsg( QStringLiteral(
"Error transforming point: %1" ).arg( cse.
what() ) );
433QgsPointCloudLayerExporter::ExporterMemory::~ExporterMemory()
435 mParent->mMemoryLayer->moveToThread( QApplication::instance()->thread() );
438void QgsPointCloudLayerExporter::ExporterMemory::handlePoint(
double x,
double y,
double z,
const QVariantMap &map,
const qint64 pointNumber )
440 Q_UNUSED( pointNumber )
445 for (
const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
447 const double val = map[ attribute ].toDouble();
448 featureAttributes.append( val );
451 mFeatures.append( feature );
454void QgsPointCloudLayerExporter::ExporterMemory::handleNode()
456 QgsVectorLayer *vl = qgis::down_cast<QgsVectorLayer *>( mParent->mMemoryLayer );
467void QgsPointCloudLayerExporter::ExporterMemory::handleAll()
481QgsPointCloudLayerExporter::ExporterVector::~ExporterVector()
483 delete mParent->mVectorSink;
484 mParent->mVectorSink =
nullptr;
487void QgsPointCloudLayerExporter::ExporterVector::handlePoint(
double x,
double y,
double z,
const QVariantMap &map,
const qint64 pointNumber )
489 Q_UNUSED( pointNumber )
494 for (
const QString &attribute : std::as_const( mParent->mRequestedAttributes ) )
496 const double val = map[ attribute ].toDouble();
497 featureAttributes.append( val );
500 mFeatures.append( feature );
503void QgsPointCloudLayerExporter::ExporterVector::handleNode()
505 if ( ! mParent->mVectorSink->addFeatures( mFeatures ) )
507 mParent->setLastError( mParent->mVectorSink->lastError() );
512void QgsPointCloudLayerExporter::ExporterVector::handleAll()
524 : mPointFormat( exp->mPointRecordFormat )
528 mOptions.add(
"filename", mParent->mFilename.toStdString() );
529 mOptions.add(
"a_srs", mParent->mTargetCrs.toWkt().toStdString() );
530 mOptions.add(
"minor_version", QStringLiteral(
"4" ).toStdString() );
531 mOptions.add(
"format", QString::number( mPointFormat ).toStdString() );
532 if ( mParent->mTransform->isShortCircuited() )
534 mOptions.add(
"offset_x", QString::number( mParent->mIndex->offset().x() ).toStdString() );
535 mOptions.add(
"offset_y", QString::number( mParent->mIndex->offset().y() ).toStdString() );
536 mOptions.add(
"offset_z", QString::number( mParent->mIndex->offset().z() ).toStdString() );
537 mOptions.add(
"scale_x", QString::number( mParent->mIndex->scale().x() ).toStdString() );
538 mOptions.add(
"scale_y", QString::number( mParent->mIndex->scale().y() ).toStdString() );
539 mOptions.add(
"scale_z", QString::number( mParent->mIndex->scale().z() ).toStdString() );
542 mTable.layout()->registerDim( pdal::Dimension::Id::X );
543 mTable.layout()->registerDim( pdal::Dimension::Id::Y );
544 mTable.layout()->registerDim( pdal::Dimension::Id::Z );
546 mTable.layout()->registerDim( pdal::Dimension::Id::Classification );
547 mTable.layout()->registerDim( pdal::Dimension::Id::Intensity );
548 mTable.layout()->registerDim( pdal::Dimension::Id::ReturnNumber );
549 mTable.layout()->registerDim( pdal::Dimension::Id::NumberOfReturns );
550 mTable.layout()->registerDim( pdal::Dimension::Id::ScanDirectionFlag );
551 mTable.layout()->registerDim( pdal::Dimension::Id::EdgeOfFlightLine );
552 mTable.layout()->registerDim( pdal::Dimension::Id::ScanAngleRank );
553 mTable.layout()->registerDim( pdal::Dimension::Id::UserData );
554 mTable.layout()->registerDim( pdal::Dimension::Id::PointSourceId );
556 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
558 mTable.layout()->registerDim( pdal::Dimension::Id::ScanChannel );
559 mTable.layout()->registerDim( pdal::Dimension::Id::ClassFlags );
562 if ( mPointFormat != 0 && mPointFormat != 2 )
564 mTable.layout()->registerDim( pdal::Dimension::Id::GpsTime );
567 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
569 mTable.layout()->registerDim( pdal::Dimension::Id::Red );
570 mTable.layout()->registerDim( pdal::Dimension::Id::Green );
571 mTable.layout()->registerDim( pdal::Dimension::Id::Blue );
574 if ( mPointFormat == 8 || mPointFormat == 10 )
576 mTable.layout()->registerDim( pdal::Dimension::Id::Infrared );
579 mView.reset(
new pdal::PointView( mTable ) );
582void QgsPointCloudLayerExporter::ExporterPdal::handlePoint(
double x,
double y,
double z,
const QVariantMap &map,
const qint64 pointNumber )
584 mView->setField( pdal::Dimension::Id::X, pointNumber, x );
585 mView->setField( pdal::Dimension::Id::Y, pointNumber, y );
586 mView->setField( pdal::Dimension::Id::Z, pointNumber, z );
589 mView->setField( pdal::Dimension::Id::Classification, pointNumber, map[ QStringLiteral(
"Classification" ) ].toInt() );
590 mView->setField( pdal::Dimension::Id::Intensity, pointNumber, map[ QStringLiteral(
"Intensity" ) ].toInt() );
591 mView->setField( pdal::Dimension::Id::ReturnNumber, pointNumber, map[ QStringLiteral(
"ReturnNumber" ) ].toInt() );
592 mView->setField( pdal::Dimension::Id::NumberOfReturns, pointNumber, map[ QStringLiteral(
"NumberOfReturns" ) ].toInt() );
593 mView->setField( pdal::Dimension::Id::ScanDirectionFlag, pointNumber, map[ QStringLiteral(
"ScanDirectionFlag" ) ].toInt() );
594 mView->setField( pdal::Dimension::Id::EdgeOfFlightLine, pointNumber, map[ QStringLiteral(
"EdgeOfFlightLine" ) ].toInt() );
595 mView->setField( pdal::Dimension::Id::ScanAngleRank, pointNumber, map[ QStringLiteral(
"ScanAngleRank" ) ].toInt() );
596 mView->setField( pdal::Dimension::Id::UserData, pointNumber, map[ QStringLiteral(
"UserData" ) ].toInt() );
597 mView->setField( pdal::Dimension::Id::PointSourceId, pointNumber, map[ QStringLiteral(
"PointSourceId" ) ].toInt() );
599 if ( mPointFormat == 6 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 9 || mPointFormat == 10 )
601 mView->setField( pdal::Dimension::Id::ScanChannel, pointNumber, map[ QStringLiteral(
"ScannerChannel" ) ].toInt() );
602 mView->setField( pdal::Dimension::Id::ClassFlags, pointNumber, map[ QStringLiteral(
"ClassificationFlags" ) ].toInt() );
605 if ( mPointFormat != 0 && mPointFormat != 2 )
607 mView->setField( pdal::Dimension::Id::GpsTime, pointNumber, map[ QStringLiteral(
"GpsTime" ) ].toDouble() );
610 if ( mPointFormat == 2 || mPointFormat == 3 || mPointFormat == 5 || mPointFormat == 7 || mPointFormat == 8 || mPointFormat == 10 )
612 mView->setField( pdal::Dimension::Id::Red, pointNumber, map[ QStringLiteral(
"Red" ) ].toInt() );
613 mView->setField( pdal::Dimension::Id::Green, pointNumber, map[ QStringLiteral(
"Green" ) ].toInt() );
614 mView->setField( pdal::Dimension::Id::Blue, pointNumber, map[ QStringLiteral(
"Blue" ) ].toInt() );
617 if ( mPointFormat == 8 || mPointFormat == 10 )
619 mView->setField( pdal::Dimension::Id::Infrared, pointNumber, map[ QStringLiteral(
"Infrared" ) ].toInt() );
623void QgsPointCloudLayerExporter::ExporterPdal::handleNode()
628void QgsPointCloudLayerExporter::ExporterPdal::handleAll()
630 pdal::BufferReader reader;
631 reader.addView( mView );
633 pdal::StageFactory factory;
635 pdal::Stage *writer = factory.createStage(
"writers.las" );
637 writer->setInput( reader );
638 writer->setOptions( mOptions );
639 writer->prepare( mTable );
640 writer->execute( mTable );
657 mOwnedFeedback->cancel();
Represents a indexed point cloud node in octree.
Abstract base class for all geometries.
virtual QgsRectangle boundingBox() const =0
Returns the minimal bounding box for the geometry.
This class represents a coordinate reference system (CRS).
Contains information about the context in which a coordinate transform is executed.
Custom exception class for Coordinate Reference System related exceptions.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
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...
void setAttributes(const QgsAttributes &attrs)
Sets the feature's attributes.
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.
void progressChanged(double progress)
Emitted when the feedback object reports a progress change.
Encapsulate a field in an attribute table or data source.
Container of fields for a vector layer.
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)
A geometry is the spatial representation of a feature.
const QgsAbstractGeometry * constGet() const SIP_HOLDGIL
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
Qgis::GeometryOperationResult transform(const QgsCoordinateTransform &ct, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters ¶meters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
Does vector analysis using the geos library and handles import, export, exception handling*.
Base class for all map layer types.
QgsCoordinateReferenceSystem crs
static QgsVectorLayer * createMemoryLayer(const QString &name, const QgsFields &fields, QgsWkbTypes::Type geometryType=QgsWkbTypes::NoGeometry, const QgsCoordinateReferenceSystem &crs=QgsCoordinateReferenceSystem()) 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.
@ Csv
Comma separated values.
@ Las
LAS/LAZ point cloud.
~QgsPointCloudLayerExporter()
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.
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.
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 driverName
OGR driver to use.
QString layerName
Layer name. If let empty, it will be derived from the filename.
QStringList layerOptions
List of OGR layer creation options.
QgsVectorFileWriter::SymbologyExport 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 QgsVectorFileWriter * create(const QString &fileName, const QgsFields &fields, QgsWkbTypes::Type 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 defaultLayerOptions(const QString &driverName)
Returns a list of the default layer options for a specified driver.
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
#define QgsDebugMsgLevel(str, level)
const QgsCoordinateReferenceSystem & crs