Skip to content

Commit 99d5e42

Browse files
committed
[FEATURE] vector file writer: allow selection of attributes to export
1 parent c93187d commit 99d5e42

7 files changed

+201
-55
lines changed

python/core/qgsvectorfilewriter.sip

+8-4
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class QgsVectorFileWriter
126126
* allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
127127
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
128128
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
129+
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
129130
*/
130131
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
131132
const QString& fileName,
@@ -143,14 +144,15 @@ class QgsVectorFileWriter
143144
const QgsRectangle* filterExtent = 0,
144145
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
145146
bool forceMulti = false,
146-
bool includeZ = false
147+
bool includeZ = false,
148+
QgsAttributeList attributes = QgsAttributeList()
147149
);
148150

149151
/** Writes a layer out to a vector file.
150152
* @param layer layer to write
151153
* @param fileName file name to write to
152154
* @param fileEncoding encoding to use
153-
* @param ct
155+
* @param ct pointer to coordinate transform to reproject exported geometries with
154156
* @param driverName OGR driver to use
155157
* @param onlySelected write only selected features of layer
156158
* @param errorMessage pointer to buffer fo error message
@@ -165,7 +167,8 @@ class QgsVectorFileWriter
165167
* allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
166168
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
167169
* @param includeZ set to true to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
168-
* @note added in v2.2
170+
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
171+
* @note added in 2.2
169172
*/
170173
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
171174
const QString& fileName,
@@ -183,7 +186,8 @@ class QgsVectorFileWriter
183186
const QgsRectangle* filterExtent = 0,
184187
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
185188
bool forceMulti = false,
186-
bool includeZ = false
189+
bool includeZ = false,
190+
QgsAttributeList attributes = QgsAttributeList()
187191
);
188192

189193
/** Create a new vector file writer */

src/app/ogr/qgsvectorlayersaveasdialog.cpp

+70-10
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,20 @@
2828
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent, Qt::WindowFlags fl )
2929
: QDialog( parent, fl )
3030
, mCRS( srsid )
31+
, mLayer( 0 )
3132
{
3233
setup();
3334
}
3435

35-
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, const QgsRectangle& layerExtent, bool layerHasSelectedFeatures, int options, QWidget* parent, Qt::WindowFlags fl )
36+
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( QgsVectorLayer *layer, int options, QWidget* parent, Qt::WindowFlags fl )
3637
: QDialog( parent, fl )
37-
, mCRS( srsid )
38-
, mLayerExtent( layerExtent )
38+
, mLayer( layer )
3939
{
40+
if ( layer )
41+
{
42+
mCRS = layer->crs().srsid();
43+
mLayerExtent = layer->extent();
44+
}
4045
setup();
4146
if ( !( options & Symbology ) )
4247
{
@@ -46,7 +51,7 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, const QgsRec
4651
mScaleSpinBox->hide();
4752
}
4853

49-
mSelectedOnly->setEnabled( layerHasSelectedFeatures );
54+
mSelectedOnly->setEnabled( layer && layer->selectedFeatureCount() != 0 );
5055
buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
5156
}
5257

@@ -213,17 +218,40 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
213218
{
214219
mEncodingComboBox->setCurrentIndex( mEncodingComboBox->findText( "UTF-8" ) );
215220
mEncodingComboBox->setDisabled( true );
216-
mSkipAttributeCreation->setEnabled( true );
221+
mAttributesSelection->setChecked( true );
217222
}
218223
else if ( format() == "DXF" )
219224
{
220-
mSkipAttributeCreation->setChecked( true );
221-
mSkipAttributeCreation->setDisabled( true );
225+
mAttributesSelection->setChecked( true );
226+
mAttributesSelection->setDisabled( true );
222227
}
223228
else
224229
{
225230
mEncodingComboBox->setEnabled( true );
226-
mSkipAttributeCreation->setEnabled( true );
231+
mAttributesSelection->setEnabled( true );
232+
}
233+
234+
if ( mLayer )
235+
{
236+
mAttributeTable->setRowCount( mLayer->fields().count() );
237+
mAttributeTable->setColumnCount( 2 );
238+
mAttributeTable->setHorizontalHeaderLabels( QStringList() << tr( "Name" ) << tr( "Type" ) );
239+
240+
int i = 0;
241+
Q_FOREACH ( const QgsField &fld, mLayer->fields() )
242+
{
243+
Qt::ItemFlags flags = mLayer->providerType() != "oracle" || !fld.typeName().contains( "SDO_GEOMETRY" ) ? Qt::ItemIsEnabled : Qt::NoItemFlags;
244+
QTableWidgetItem *item;
245+
item = new QTableWidgetItem( fld.name() );
246+
item->setFlags( flags | Qt::ItemIsUserCheckable );
247+
item->setCheckState( Qt::Unchecked );
248+
mAttributeTable->setItem( i, 0, item );
249+
item = new QTableWidgetItem( fld.typeName() );
250+
item->setFlags( flags );
251+
mAttributeTable->setItem( i++, 1, item );
252+
}
253+
254+
mAttributeTable->resizeColumnsToContents();
227255
}
228256

229257
QgsVectorFileWriter::MetaData driverMetaData;
@@ -430,9 +458,24 @@ QStringList QgsVectorLayerSaveAsDialog::layerOptions() const
430458
return options + mOgrLayerOptions->toPlainText().split( '\n' );
431459
}
432460

433-
bool QgsVectorLayerSaveAsDialog::skipAttributeCreation() const
461+
bool QgsVectorLayerSaveAsDialog::attributeSelection() const
434462
{
435-
return mSkipAttributeCreation->isChecked();
463+
return mAttributesSelection->isChecked();
464+
}
465+
466+
QgsAttributeList QgsVectorLayerSaveAsDialog::selectedAttributes() const
467+
{
468+
QgsAttributeList attributes;
469+
470+
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
471+
{
472+
if ( mAttributeTable->item( i, 0 )->checkState() == Qt::Checked )
473+
{
474+
attributes.append( i );
475+
}
476+
}
477+
478+
return attributes;
436479
}
437480

438481
bool QgsVectorLayerSaveAsDialog::addToCanvas() const
@@ -526,3 +569,20 @@ void QgsVectorLayerSaveAsDialog::on_mGeometryTypeComboBox_currentIndexChanged( i
526569
mForceMultiCheckBox->setEnabled( currentIndexData != -1 );
527570
mIncludeZCheckBox->setEnabled( currentIndexData != -1 );
528571
}
572+
573+
void QgsVectorLayerSaveAsDialog::on_mSelectAllAttributes_clicked()
574+
{
575+
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
576+
{
577+
if ( mAttributeTable->item( i, 0 )->flags() & Qt::ItemIsEnabled )
578+
mAttributeTable->item( i, 0 )->setCheckState( Qt::Checked );
579+
}
580+
}
581+
582+
void QgsVectorLayerSaveAsDialog::on_mDeselectAllAttributes_clicked()
583+
{
584+
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
585+
{
586+
mAttributeTable->item( i, 0 )->setCheckState( Qt::Unchecked );
587+
}
588+
}

src/app/ogr/qgsvectorlayersaveasdialog.h

+6-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
3939
};
4040

4141
QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent = nullptr, Qt::WindowFlags fl = nullptr );
42-
QgsVectorLayerSaveAsDialog( long srsid, const QgsRectangle& layerExtent, bool layerHasSelectedFeatures, int options = AllOptions, QWidget* parent = nullptr, Qt::WindowFlags fl = nullptr );
42+
QgsVectorLayerSaveAsDialog( QgsVectorLayer *layer, int options = AllOptions, QWidget* parent = nullptr, Qt::WindowFlags fl = nullptr );
4343
~QgsVectorLayerSaveAsDialog();
4444

4545
QString format() const;
@@ -48,7 +48,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
4848
QStringList datasourceOptions() const;
4949
QStringList layerOptions() const;
5050
long crs() const;
51-
bool skipAttributeCreation() const;
51+
bool attributeSelection() const;
52+
QgsAttributeList selectedAttributes() const;
5253
bool addToCanvas() const;
5354
/** Returns type of symbology export.
5455
0: No symbology
@@ -104,6 +105,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
104105
void on_mSymbologyExportComboBox_currentIndexChanged( const QString& text );
105106
void on_mGeometryTypeComboBox_currentIndexChanged( int index );
106107
void accept() override;
108+
void on_mSelectAllAttributes_clicked();
109+
void on_mDeselectAllAttributes_clicked();
107110

108111
private:
109112
void setup();
@@ -113,6 +116,7 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
113116

114117
QgsRectangle mLayerExtent;
115118
QgsCoordinateReferenceSystem mLayerCrs;
119+
QgsVectorLayer *mLayer;
116120
};
117121

118122
#endif // QGSVECTORLAYERSAVEASDIALOG_H

src/app/qgisapp.cpp

+4-3
Original file line numberDiff line numberDiff line change
@@ -5690,7 +5690,7 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
56905690
options &= ~QgsVectorLayerSaveAsDialog::Symbology;
56915691
}
56925692

5693-
QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), vlayer->extent(), vlayer->selectedFeatureCount() != 0, options, this );
5693+
QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer, options, this );
56945694

56955695
dialog->setCanvasExtent( mMapCanvas->mapSettings().visibleExtent(), mMapCanvas->mapSettings().destinationCrs() );
56965696
dialog->setIncludeZ( QgsWKBTypes::hasZ( QGis::fromOldWkbType( vlayer->wkbType() ) ) );
@@ -5745,14 +5745,15 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
57455745
dialog->onlySelected(),
57465746
&errorMessage,
57475747
datasourceOptions, dialog->layerOptions(),
5748-
dialog->skipAttributeCreation(),
5748+
dialog->attributeSelection() && dialog->selectedAttributes().isEmpty(),
57495749
&newFilename,
57505750
static_cast< QgsVectorFileWriter::SymbologyExport >( dialog->symbologyExport() ),
57515751
dialog->scaleDenominator(),
57525752
dialog->hasFilterExtent() ? &filterExtent : nullptr,
57535753
autoGeometryType ? QgsWKBTypes::Unknown : forcedGeometryType,
57545754
dialog->forceMulti(),
5755-
dialog->includeZ()
5755+
dialog->includeZ(),
5756+
dialog->selectedAttributes()
57565757
);
57575758

57585759
delete ct;

src/core/qgsvectorfilewriter.cpp

+45-15
Original file line numberDiff line numberDiff line change
@@ -1759,16 +1759,12 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
17591759
}
17601760

17611761
// attribute handling
1762-
for ( int fldIdx = 0; fldIdx < mFields.count(); ++fldIdx )
1762+
for ( QMap<int, int>::const_iterator it = mAttrIdxToOgrIdx.constBegin(); it != mAttrIdxToOgrIdx.constEnd(); ++it )
17631763
{
1764-
if ( !mAttrIdxToOgrIdx.contains( fldIdx ) )
1765-
{
1766-
QgsDebugMsg( QString( "no ogr field for field %1" ).arg( fldIdx ) );
1767-
continue;
1768-
}
1764+
int fldIdx = it.key();
1765+
int ogrField = it.value();
17691766

17701767
const QVariant& attrValue = feature.attribute( fldIdx );
1771-
int ogrField = mAttrIdxToOgrIdx[ fldIdx ];
17721768

17731769
if ( !attrValue.isValid() || attrValue.isNull() )
17741770
continue;
@@ -1950,6 +1946,16 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
19501946
return poFeature;
19511947
}
19521948

1949+
void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
1950+
{
1951+
QMap<int, int> omap( mAttrIdxToOgrIdx );
1952+
mAttrIdxToOgrIdx.clear();
1953+
for ( int i = 0; i < attributes.size(); i++ )
1954+
{
1955+
mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
1956+
}
1957+
}
1958+
19531959
bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
19541960
{
19551961
if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
@@ -1998,7 +2004,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
19982004
const QgsRectangle* filterExtent,
19992005
QgsWKBTypes::Type overrideGeometryType,
20002006
bool forceMulti,
2001-
bool includeZ )
2007+
bool includeZ,
2008+
QgsAttributeList attributes )
20022009
{
20032010
QgsCoordinateTransform* ct = nullptr;
20042011
if ( destCRS && layer )
@@ -2007,7 +2014,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
20072014
}
20082015

20092016
QgsVectorFileWriter::WriterError error = writeAsVectorFormat( layer, fileName, fileEncoding, ct, driverName, onlySelected,
2010-
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ );
2017+
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ, attributes );
20112018
delete ct;
20122019
return error;
20132020
}
@@ -2028,7 +2035,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
20282035
const QgsRectangle* filterExtent,
20292036
QgsWKBTypes::Type overrideGeometryType,
20302037
bool forceMulti,
2031-
bool includeZ )
2038+
bool includeZ,
2039+
QgsAttributeList attributes )
20322040
{
20332041
if ( !layer )
20342042
{
@@ -2061,7 +2069,27 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
20612069
destWkbType = QgsWKBTypes::multiType( destWkbType );
20622070
}
20632071

2064-
QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
2072+
if ( skipAttributeCreation )
2073+
attributes.clear();
2074+
else if ( attributes.isEmpty() )
2075+
{
2076+
Q_FOREACH ( int idx, layer->attributeList() )
2077+
{
2078+
const QgsField &fld = layer->fields()[idx];
2079+
if ( layer->providerType() == "oracle" && fld.typeName().contains( "SDO_GEOMETRY" ) )
2080+
continue;
2081+
attributes.append( idx );
2082+
}
2083+
}
2084+
2085+
QgsFields fields;
2086+
if ( !attributes.isEmpty() )
2087+
{
2088+
Q_FOREACH ( int attrIdx, attributes )
2089+
{
2090+
fields.append( layer->fields()[attrIdx] );
2091+
}
2092+
}
20652093

20662094
if ( layer->providerType() == "ogr" && layer->dataProvider() )
20672095
{
@@ -2116,6 +2144,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
21162144
new QgsVectorFileWriter( fileName, fileEncoding, fields, QGis::fromNewWkbType( destWkbType ), outputCRS, driverName, datasourceOptions, layerOptions, newFilename, symbologyExport );
21172145
writer->setSymbologyScaleDenominator( symbologyScale );
21182146

2147+
21192148
if ( newFilename )
21202149
{
21212150
QgsDebugMsg( "newFilename = " + *newFilename );
@@ -2136,18 +2165,17 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
21362165
errorMessage->clear();
21372166
}
21382167

2139-
QgsAttributeList allAttr = skipAttributeCreation ? QgsAttributeList() : layer->attributeList();
21402168
QgsFeature fet;
21412169

21422170
//add possible attributes needed by renderer
2143-
writer->addRendererAttributes( layer, allAttr );
2171+
writer->addRendererAttributes( layer, attributes );
21442172

21452173
QgsFeatureRequest req;
21462174
if ( layer->wkbType() == QGis::WKBNoGeometry )
21472175
{
21482176
req.setFlags( QgsFeatureRequest::NoGeometry );
21492177
}
2150-
req.setSubsetOfAttributes( allAttr );
2178+
req.setSubsetOfAttributes( attributes );
21512179
if ( onlySelected )
21522180
req.setFilterFids( layer->selectedFeaturesIds() );
21532181
QgsFeatureIterator fit = layer->getFeatures( req );
@@ -2190,6 +2218,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
21902218
transactionsEnabled = false;
21912219
}
21922220

2221+
writer->resetMap( attributes );
2222+
21932223
// write all features
21942224
while ( fit.nextFeature( fet ) )
21952225
{
@@ -2219,7 +2249,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
22192249
if ( fet.constGeometry() && filterExtent && !fet.constGeometry()->intersects( *filterExtent ) )
22202250
continue;
22212251

2222-
if ( allAttr.size() < 1 && skipAttributeCreation )
2252+
if ( attributes.size() < 1 && skipAttributeCreation )
22232253
{
22242254
fet.initAttributes( 0 );
22252255
}

0 commit comments

Comments
 (0)