31#include <QRegularExpression>
34#include "moc_qgspointcloudlayersaveasdialog.cpp"
36using namespace Qt::StringLiterals;
39 : QDialog( parent, fl )
44 mSelectedCrs = layer->
crs();
45 mLayerExtent = layer->
extent();
50void QgsPointCloudLayerSaveAsDialog::setup()
55 connect( mFormatComboBox,
static_cast<void ( QComboBox::* )(
int )
>( &QComboBox::currentIndexChanged ),
this, &QgsPointCloudLayerSaveAsDialog::mFormatComboBox_currentIndexChanged );
57 connect( mSelectAllAttributes, &QPushButton::clicked,
this, &QgsPointCloudLayerSaveAsDialog::mSelectAllAttributes_clicked );
58 connect( mDeselectAllAttributes, &QPushButton::clicked,
this, &QgsPointCloudLayerSaveAsDialog::mDeselectAllAttributes_clicked );
60 connect( mFilterGeometryGroupBox, &QgsCollapsibleGroupBox::toggled,
this, &QgsPointCloudLayerSaveAsDialog::mFilterGeometryGroupBoxCheckToggled );
61 connect( mMinimumZSpinBox,
static_cast<void (
QgsDoubleSpinBox::* )(
double )
>( &QgsDoubleSpinBox::valueChanged ),
this, &QgsPointCloudLayerSaveAsDialog::mMinimumZSpinBoxValueChanged );
62 connect( mMaximumZSpinBox,
static_cast<void (
QgsDoubleSpinBox::* )(
double )
>( &QgsDoubleSpinBox::valueChanged ),
this, &QgsPointCloudLayerSaveAsDialog::mMaximumZSpinBoxValueChanged );
65 mHelpButtonBox->setVisible(
false );
66 mButtonBox->addButton( QDialogButtonBox::Help );
67 connect( mButtonBox, &QDialogButtonBox::helpRequested,
this, &QgsPointCloudLayerSaveAsDialog::showHelp );
69 connect( mHelpButtonBox, &QDialogButtonBox::helpRequested,
this, &QgsPointCloudLayerSaveAsDialog::showHelp );
71 connect( mButtonBox, &QDialogButtonBox::accepted,
this, &QgsPointCloudLayerSaveAsDialog::accept );
72 connect( mButtonBox, &QDialogButtonBox::rejected,
this, &QgsPointCloudLayerSaveAsDialog::reject );
74 mFormatComboBox->blockSignals(
true );
76 for (
const auto &format : supportedFormats )
77 mFormatComboBox->addItem( getTranslatedNameForFormat( format ),
static_cast<int>( format ) );
80 const int defaultFormat = settings.
value( u
"UI/lastPointCloudFormat"_s, 0 ).toInt();
81 mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( defaultFormat ) );
82 mFormatComboBox->blockSignals(
false );
83 mFormatComboBox_currentIndexChanged( 0 );
85 mCrsSelector->setCrs( mSelectedCrs );
86 mCrsSelector->setLayerCrs( mSelectedCrs );
87 mCrsSelector->setMessage( tr(
88 "Select the coordinate reference system for the vector file. "
89 "The data points will be transformed from the layer coordinate reference system."
97 QStringList availableAttributes;
100 const QString attribute =
attributes.at( i ).name();
101 if ( attribute.compare(
'X'_L1, Qt::CaseInsensitive ) && attribute.compare(
'Y'_L1, Qt::CaseInsensitive ) && attribute.compare(
'Z'_L1, Qt::CaseInsensitive ) )
103 availableAttributes.append( attribute );
107 mAttributeTable->setRowCount( availableAttributes.count() );
108 QStringList horizontalHeaders = QStringList() << tr(
"Attribute" );
109 mAttributeTable->setColumnCount( horizontalHeaders.size() );
110 mAttributeTable->setHorizontalHeaderLabels( horizontalHeaders );
112 for (
int i = 0; i < availableAttributes.count(); ++i )
114 QTableWidgetItem *item =
new QTableWidgetItem( availableAttributes.at( i ) );
115 item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsUserCheckable );
116 item->setCheckState( Qt::Checked );
117 mAttributeTable->setItem( i, 0, item );
119 mAttributeTable->resizeColumnsToContents();
123 mExtentGroupBox->setOutputCrs( mSelectedCrs );
124 mExtentGroupBox->setOriginalExtent( mLayerExtent, mSelectedCrs );
125 mExtentGroupBox->setOutputExtentFromOriginal();
126 mExtentGroupBox->setCheckable(
true );
127 mExtentGroupBox->setChecked(
false );
128 mExtentGroupBox->setCollapsed(
true );
134 mMinimumZSpinBox->setRange( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
135 mMaximumZSpinBox->setRange( std::numeric_limits<double>::lowest(), std::numeric_limits<double>::max() );
138 mMinimumZSpinBox->setValue( mLayer->statistics().minimum( u
"Z"_s ) );
139 mMinimumZSpinBox->setClearValue( mMinimumZSpinBox->value() );
140 mMaximumZSpinBox->setValue( mLayer->statistics().maximum( u
"Z"_s ) );
141 mMaximumZSpinBox->setClearValue( mMaximumZSpinBox->value() );
145 mPointsLimitSpinBox->setMinimum( 1 );
146 mPointsLimitSpinBox->setMaximum( std::numeric_limits<int>::max() );
147 mPointsLimitSpinBox->setValue( 1e6 );
148 mPointsLimitSpinBox->setClearValue( 1e6 );
151 mFilename->setDialogTitle( tr(
"Save Layer As" ) );
152 mFilename->setDefaultRoot( settings.
value( u
"UI/lastPointCloudFileFilterDir"_s, QDir::homePath() ).toString() );
153 mFilename->setConfirmOverwrite(
false );
155 QgsSettings settings;
156 if ( !filePath.isEmpty() )
157 mLastUsedFilename = filePath;
159 const QFileInfo fileInfo( filePath );
160 settings.
setValue( u
"UI/lastPointCloudFileFilterDir"_s, fileInfo.absolutePath() );
162 if ( mDefaultOutputLayerNameFromInputLayerName.isEmpty() )
164 leLayername->setDefaultValue( suggestedLayerName );
168 if ( leLayername->text().isEmpty() && !filePath.isEmpty() && leLayername->isEnabled() )
170 leLayername->setText( suggestedLayerName );
172 mButtonBox->button( QDialogButtonBox::Ok )->setEnabled( !filePath.isEmpty() );
177 const QgsDatumEnsemble ensemble = mSelectedCrs.datumEnsemble();
180 mCrsSelector->setSourceEnsemble( ensemble.
name() );
183 catch ( QgsNotSupportedException & )
186 mCrsSelector->setShowAccuracyWarnings(
true );
193 leLayername->setDefaultValue( mDefaultOutputLayerNameFromInputLayerName );
195 if ( leLayername->isEnabled() )
196 leLayername->setText( mDefaultOutputLayerNameFromInputLayerName );
202void QgsPointCloudLayerSaveAsDialog::accept()
209 msgBox.setIcon( QMessageBox::Question );
210 msgBox.setWindowTitle( tr(
"Save Point Cloud Layer As" ) );
211 QPushButton *overwriteFileButton = msgBox.addButton( tr(
"Overwrite File" ), QMessageBox::ActionRole );
212 QPushButton *overwriteLayerButton = msgBox.addButton( tr(
"Overwrite Layer" ), QMessageBox::ActionRole );
213 QPushButton *appendToLayerButton = msgBox.addButton( tr(
"Append to Layer" ), QMessageBox::ActionRole );
214 msgBox.setStandardButtons( QMessageBox::Cancel );
215 msgBox.setDefaultButton( QMessageBox::Cancel );
216 overwriteFileButton->hide();
217 overwriteLayerButton->hide();
218 appendToLayerButton->hide();
223 msgBox.setText( tr(
"The layer already exists. Do you want to overwrite the whole file or overwrite the layer?" ) );
224 overwriteFileButton->setVisible(
true );
225 overwriteLayerButton->setVisible(
true );
229 msgBox.setText( tr(
"The file already exists. Do you want to overwrite it?" ) );
230 overwriteFileButton->setVisible(
true );
234 msgBox.setText( tr(
"The layer already exists. Do you want to overwrite the whole file, overwrite the layer or append features to the layer?" ) );
235 appendToLayerButton->setVisible(
true );
236 overwriteFileButton->setVisible(
true );
237 overwriteLayerButton->setVisible(
true );
241 msgBox.setText( tr(
"The layer already exists. Do you want to overwrite the whole file or append features to the layer?" ) );
242 appendToLayerButton->setVisible(
true );
243 overwriteFileButton->setVisible(
true );
246 int ret = msgBox.exec();
247 if ( ret == QMessageBox::Cancel )
249 if ( msgBox.clickedButton() == overwriteFileButton )
251 else if ( msgBox.clickedButton() == overwriteLayerButton )
253 else if ( msgBox.clickedButton() == appendToLayerButton )
265 if ( QMessageBox::question(
this, tr(
"Save Point Cloud Layer As" ), tr(
"The file already exists. Do you want to overwrite it?" ) ) == QMessageBox::NoButton )
276 QStringList layerList;
277 layerList.reserve( sublayers.size() );
278 for (
const QgsProviderSublayerDetails &sublayer : sublayers )
280 layerList.append( sublayer.name() );
282 if ( layerList.length() > 1 )
284 layerList.sort( Qt::CaseInsensitive );
286 msgBox.setIcon( QMessageBox::Warning );
287 msgBox.setWindowTitle( tr(
"Overwrite File" ) );
288 msgBox.setText( tr(
"This file contains %1 layers that will be lost!\n" ).arg( QLocale().toString( layerList.length() ) ) );
289 msgBox.setDetailedText( tr(
"The following layers will be permanently lost:\n\n%1" ).arg( layerList.join(
"\n" ) ) );
290 msgBox.setStandardButtons( QMessageBox::Ok | QMessageBox::Cancel );
291 if ( msgBox.exec() == QMessageBox::Cancel )
296 QgsSettings settings;
297 settings.
setValue( u
"UI/lastPointCloudFileFilterDir"_s, QFileInfo(
filename() ).absolutePath() );
302void QgsPointCloudLayerSaveAsDialog::mFormatComboBox_currentIndexChanged(
int idx )
314 mAttributesSelection->setEnabled(
true );
319 mAttributesSelection->setEnabled(
false );
327 leLayername->setEnabled(
true );
334 leLayername->setEnabled(
false );
341 mWasAddToCanvasForced = !mAddToCanvas->isChecked();
342 mAddToCanvas->setEnabled(
false );
343 mAddToCanvas->setChecked(
true );
344 mFilename->setEnabled(
false );
352 mAddToCanvas->setEnabled(
true );
353 if ( mWasAddToCanvasForced )
355 mAddToCanvas->setChecked( !mAddToCanvas->isChecked() );
356 mWasAddToCanvasForced =
false;
358 mFilename->setEnabled(
true );
362 if ( mFilename->isEnabled() )
364 mFilename->setFilter( getFilterForFormat( format ) );
368 if ( !mLastUsedFilename.isEmpty() )
370 const thread_local QRegularExpression rx(
"\\.(.*?)[\\s]" );
372 ext = rx.match( getFilterForFormat( format ) ).captured( 1 );
373 if ( !ext.isEmpty() )
375 QFileInfo fi( mLastUsedFilename );
376 mFilename->setFilePath( u
"%1/%2.%3"_s.arg( fi.path(), fi.baseName(), ext ) );
381 if ( !mFilename->isEnabled() )
382 mFilename->setFilePath( QString() );
384 if ( !leLayername->isEnabled() )
386 leLayername->setText( QString() );
388 else if ( leLayername->text().isEmpty() )
390 QString layerName = mDefaultOutputLayerNameFromInputLayerName;
391 if ( layerName.isEmpty() && !mFilename->filePath().isEmpty() )
393 layerName = QFileInfo( mFilename->filePath() ).baseName();
394 leLayername->setDefaultValue( layerName );
396 if ( layerName.isEmpty() )
398 layerName = tr(
"new_layer" );
400 leLayername->setText( layerName );
406void QgsPointCloudLayerSaveAsDialog::mFilterGeometryGroupBoxCheckToggled(
bool checked )
409 mFilterGeometryLayerChanged( mFilterGeometryLayerComboBox->currentLayer() );
412void QgsPointCloudLayerSaveAsDialog::mFilterGeometryLayerChanged(
QgsMapLayer *layer )
414 QgsVectorLayer *vlayer =
dynamic_cast<QgsVectorLayer *
>( layer );
415 mSelectedFeaturesCheckBox->setChecked(
false );
419void QgsPointCloudLayerSaveAsDialog::mMinimumZSpinBoxValueChanged(
const double value )
421 mMaximumZSpinBox->setMinimum( value );
424void QgsPointCloudLayerSaveAsDialog::mMaximumZSpinBoxValueChanged(
const double value )
426 mMinimumZSpinBox->setMaximum( value );
432 mExtentGroupBox->setOutputCrs( mSelectedCrs );
437 return mFilename->filePath();
442 return leLayername->text();
459 for (
int i = 0; i < mAttributeTable->rowCount(); i++ )
461 if ( mAttributeTable->item( i, 0 )->checkState() == Qt::Checked )
463 attributes.append( mAttributeTable->item( i, 0 )->text() );
472 return mAddToCanvas->isChecked();
477 mAddToCanvas->setChecked( enabled );
488 return mExtentGroupBox->isChecked();
493 return mExtentGroupBox->outputExtent();
498 return mFilterGeometryGroupBox->isChecked() && mFilterGeometryLayerComboBox->count() > 0;
503 return mFilterGeometryLayerComboBox->currentLayer();
508 return hasFilterLayer() && mSelectedFeaturesCheckBox->isChecked();
513 return mAttributesSelection->isChecked() && mAttributesSelection->isEnabled();
518 return mZRangeGroupBox->isChecked();
523 return QgsDoubleRange( mMinimumZSpinBox->value(), mMaximumZSpinBox->value() );
528 return mPointsLimitGroupBox->isChecked();
533 return mPointsLimitSpinBox->value();
538 return mActionOnExistingFile;
541void QgsPointCloudLayerSaveAsDialog::mSelectAllAttributes_clicked()
543 for (
int i = 0; i < mAttributeTable->rowCount(); i++ )
545 mAttributeTable->item( i, 0 )->setCheckState( Qt::Checked );
549void QgsPointCloudLayerSaveAsDialog::mDeselectAllAttributes_clicked()
552 for (
int i = 0; i < mAttributeTable->rowCount(); i++ )
554 mAttributeTable->item( i, 0 )->setCheckState( Qt::Unchecked );
559void QgsPointCloudLayerSaveAsDialog::showHelp()
561 QgsHelp::openHelp( u
"managing_data_source/create_layers.html#creating-new-layers-from-an-existing-layer"_s );
569 return u
"LAZ point cloud (*.laz *.LAZ);;LAS point cloud (*.las *.LAS)"_s;
571 return u
"GeoPackage (*.gpkg *.GPKG)"_s;
573 return u
"AutoCAD DXF (*.dxf *.dxf)"_s;
575 return u
"ESRI Shapefile (*.shp *.SHP)"_s;
577 return u
"Comma separated values (*.csv *.CSV)"_s;
589 return tr(
"Temporary Scratch Layer" );
591 return tr(
"GeoPackage" );
593 return tr(
"AutoCAD DXF" );
595 return tr(
"ESRI Shapefile" );
597 return tr(
"LAS/LAZ point cloud" );
599 return tr(
"Comma separated values" );
Represents a coordinate reference system (CRS).
bool isValid() const
Returns true if the datum ensemble is a valid object, or false if it is a null/invalid object.
QString name() const
Display name of datum ensemble.
QgsRange which stores a range of double values.
The QgsSpinBox is a spin box with a clear button that will set the value to the defined clear value.
@ ClearToDefault
Reset value to default value (see defaultValue() ).
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
static void openHelp(const QString &key)
Opens help topic for the given help key using default system web browser.
Map canvas is a class for displaying all GIS data types on a canvas.
const QgsMapSettings & mapSettings() const
Gets access to properties used for map rendering.
void layerChanged(QgsMapLayer *layer)
Emitted whenever the currently selected layer changes.
static QString launderLayerName(const QString &name)
Launders a layer's name, converting it into a format which is general suitable for file names or data...
Base class for all map layer types.
QgsCoordinateReferenceSystem crs
QgsRectangle visibleExtent() const
Returns the actual extent derived from requested extent that takes output image size into account.
QgsCoordinateReferenceSystem destinationCrs() const
Returns the destination coordinate reference system for the map render.
ExportFormat
Supported export formats for point clouds.
@ Csv
Comma separated values.
@ Las
LAS/LAZ point cloud.
static QList< ExportFormat > supportedFormats()
Gets a list of the supported export formats.
void setMapCanvas(QgsMapCanvas *canvas)
Sets a map canvas to associate with the dialog.
QgsPointCloudLayerSaveAsDialog(QgsPointCloudLayer *layer, QWidget *parent=nullptr, Qt::WindowFlags fl=Qt::WindowFlags())
Construct a new QgsPointCloudLayerSaveAsDialog.
QString filename() const
Returns the target filename.
bool hasPointsLimit() const
Determines if limiting the number of exported points is enabled.
bool hasFilterLayer() const
Determines if points will be spatially filtered by a layer's features.
QgsRectangle filterExtent() const
Determines the extent to be exported.
bool hasZRange() const
Determines if filtering by Z values is activated.
QgsCoordinateReferenceSystem crsObject() const
Returns the CRS chosen for export.
bool hasAttributes() const
Determines if attributes will be exported as fields.
QgsDoubleRange zRange() const
Determines the Z range of points to be exported.
QgsPointCloudLayerExporter::ExportFormat exportFormat() const
The format in which the export should be written.
bool hasFilterExtent() const
Determines if filtering the export by an extent is activated.
int pointsLimit() const
Determines the limit to the total number of points.
bool filterLayerSelectedOnly() const
Determines if only the selected features from the filterLayer will be used for spatial filtering.
QString layername() const
Returns the target layer name.
void setAddToCanvas(bool checked)
Sets whether the "add to canvas" checkbox should be checked.
bool addToCanvas() const
Returns true if the "add to canvas" checkbox is checked.
QgsMapLayer * filterLayer() const
Returns the layer responsible for spatially filtering points.
QStringList attributes() const
Returns a list of attributes which are selected for saving.
QgsVectorFileWriter::ActionOnExistingFile creationActionOnExistingFile() const
Returns creation action.
Represents a map layer supporting display of point clouds.
QgsRectangle extent() const override
Returns the extent of the layer.
QgsPointCloudAttributeCollection attributes() const
Returns the attributes available from the layer.
QList< QgsProviderSublayerDetails > querySublayers(const QString &uri, Qgis::SublayerQueryFlags flags=Qgis::SublayerQueryFlags(), QgsFeedback *feedback=nullptr) const
Queries the specified uri and returns a list of any valid sublayers found in the dataset which can be...
static QgsProviderRegistry * instance(const QString &pluginPath=QString())
Means of accessing canonical single instance.
A rectangle specified with double values.
Stores settings for use within QGIS.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
@ CanAppendToExistingLayer
Flag to indicate that new features can be added to an existing layer.
@ CanAddNewLayer
Flag to indicate that a new layer can be added to the dataset.
@ CanDeleteLayer
Flag to indicate that an existing layer can be deleted.
static QgsVectorFileWriter::EditionCapabilities editionCapabilities(const QString &datasetName)
Returns edition capabilities for an existing dataset name.
QFlags< EditionCapability > EditionCapabilities
Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteL...
static bool targetLayerExists(const QString &datasetName, const QString &layerName)
Returns whether the target layer already exists.
ActionOnExistingFile
Enumeration to describe how to handle existing files.
@ CreateOrOverwriteLayer
Create or overwrite layer.
@ CreateOrOverwriteFile
Create or overwrite file.
@ AppendToLayerNoNewFields
Append features to existing layer, but do not create new fields.
int selectedFeatureCount() const
Returns the number of features that are selected in this layer.