Skip to content

Commit

Permalink
[FEATURE] vector file writer: allow selection of attributes to export
Browse files Browse the repository at this point in the history
  • Loading branch information
jef-n committed Apr 2, 2016
1 parent c93187d commit 99d5e42
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 55 deletions.
12 changes: 8 additions & 4 deletions python/core/qgsvectorfilewriter.sip
Expand Up @@ -126,6 +126,7 @@ class QgsVectorFileWriter
* allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @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)
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
Expand All @@ -143,14 +144,15 @@ class QgsVectorFileWriter
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
);

/** Writes a layer out to a vector file.
* @param layer layer to write
* @param fileName file name to write to
* @param fileEncoding encoding to use
* @param ct
* @param ct pointer to coordinate transform to reproject exported geometries with
* @param driverName OGR driver to use
* @param onlySelected write only selected features of layer
* @param errorMessage pointer to buffer fo error message
Expand All @@ -165,7 +167,8 @@ class QgsVectorFileWriter
* allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
* @param forceMulti set to true to force creation of multi* geometries (added in QGIS 2.14)
* @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)
* @note added in v2.2
* @param attributes attributes to export (empty means all unless skipAttributeCreation is set)
* @note added in 2.2
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
Expand All @@ -183,7 +186,8 @@ class QgsVectorFileWriter
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
);

/** Create a new vector file writer */
Expand Down
80 changes: 70 additions & 10 deletions src/app/ogr/qgsvectorlayersaveasdialog.cpp
Expand Up @@ -28,15 +28,20 @@
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mCRS( srsid )
, mLayer( 0 )
{
setup();
}

QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, const QgsRectangle& layerExtent, bool layerHasSelectedFeatures, int options, QWidget* parent, Qt::WindowFlags fl )
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( QgsVectorLayer *layer, int options, QWidget* parent, Qt::WindowFlags fl )
: QDialog( parent, fl )
, mCRS( srsid )
, mLayerExtent( layerExtent )
, mLayer( layer )
{
if ( layer )
{
mCRS = layer->crs().srsid();
mLayerExtent = layer->extent();
}
setup();
if ( !( options & Symbology ) )
{
Expand All @@ -46,7 +51,7 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, const QgsRec
mScaleSpinBox->hide();
}

mSelectedOnly->setEnabled( layerHasSelectedFeatures );
mSelectedOnly->setEnabled( layer && layer->selectedFeatureCount() != 0 );
buttonBox->button( QDialogButtonBox::Ok )->setDisabled( true );
}

Expand Down Expand Up @@ -213,17 +218,40 @@ void QgsVectorLayerSaveAsDialog::on_mFormatComboBox_currentIndexChanged( int idx
{
mEncodingComboBox->setCurrentIndex( mEncodingComboBox->findText( "UTF-8" ) );
mEncodingComboBox->setDisabled( true );
mSkipAttributeCreation->setEnabled( true );
mAttributesSelection->setChecked( true );
}
else if ( format() == "DXF" )
{
mSkipAttributeCreation->setChecked( true );
mSkipAttributeCreation->setDisabled( true );
mAttributesSelection->setChecked( true );
mAttributesSelection->setDisabled( true );
}
else
{
mEncodingComboBox->setEnabled( true );
mSkipAttributeCreation->setEnabled( true );
mAttributesSelection->setEnabled( true );
}

if ( mLayer )
{
mAttributeTable->setRowCount( mLayer->fields().count() );
mAttributeTable->setColumnCount( 2 );
mAttributeTable->setHorizontalHeaderLabels( QStringList() << tr( "Name" ) << tr( "Type" ) );

int i = 0;
Q_FOREACH ( const QgsField &fld, mLayer->fields() )
{
Qt::ItemFlags flags = mLayer->providerType() != "oracle" || !fld.typeName().contains( "SDO_GEOMETRY" ) ? Qt::ItemIsEnabled : Qt::NoItemFlags;
QTableWidgetItem *item;
item = new QTableWidgetItem( fld.name() );
item->setFlags( flags | Qt::ItemIsUserCheckable );
item->setCheckState( Qt::Unchecked );
mAttributeTable->setItem( i, 0, item );
item = new QTableWidgetItem( fld.typeName() );
item->setFlags( flags );
mAttributeTable->setItem( i++, 1, item );
}

mAttributeTable->resizeColumnsToContents();
}

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

bool QgsVectorLayerSaveAsDialog::skipAttributeCreation() const
bool QgsVectorLayerSaveAsDialog::attributeSelection() const
{
return mSkipAttributeCreation->isChecked();
return mAttributesSelection->isChecked();
}

QgsAttributeList QgsVectorLayerSaveAsDialog::selectedAttributes() const
{
QgsAttributeList attributes;

for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, 0 )->checkState() == Qt::Checked )
{
attributes.append( i );
}
}

return attributes;
}

bool QgsVectorLayerSaveAsDialog::addToCanvas() const
Expand Down Expand Up @@ -526,3 +569,20 @@ void QgsVectorLayerSaveAsDialog::on_mGeometryTypeComboBox_currentIndexChanged( i
mForceMultiCheckBox->setEnabled( currentIndexData != -1 );
mIncludeZCheckBox->setEnabled( currentIndexData != -1 );
}

void QgsVectorLayerSaveAsDialog::on_mSelectAllAttributes_clicked()
{
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
if ( mAttributeTable->item( i, 0 )->flags() & Qt::ItemIsEnabled )
mAttributeTable->item( i, 0 )->setCheckState( Qt::Checked );
}
}

void QgsVectorLayerSaveAsDialog::on_mDeselectAllAttributes_clicked()
{
for ( int i = 0; i < mAttributeTable->rowCount(); i++ )
{
mAttributeTable->item( i, 0 )->setCheckState( Qt::Unchecked );
}
}
8 changes: 6 additions & 2 deletions src/app/ogr/qgsvectorlayersaveasdialog.h
Expand Up @@ -39,7 +39,7 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
};

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

QString format() const;
Expand All @@ -48,7 +48,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
QStringList datasourceOptions() const;
QStringList layerOptions() const;
long crs() const;
bool skipAttributeCreation() const;
bool attributeSelection() const;
QgsAttributeList selectedAttributes() const;
bool addToCanvas() const;
/** Returns type of symbology export.
0: No symbology
Expand Down Expand Up @@ -104,6 +105,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
void on_mSymbologyExportComboBox_currentIndexChanged( const QString& text );
void on_mGeometryTypeComboBox_currentIndexChanged( int index );
void accept() override;
void on_mSelectAllAttributes_clicked();
void on_mDeselectAllAttributes_clicked();

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

QgsRectangle mLayerExtent;
QgsCoordinateReferenceSystem mLayerCrs;
QgsVectorLayer *mLayer;
};

#endif // QGSVECTORLAYERSAVEASDIALOG_H
7 changes: 4 additions & 3 deletions src/app/qgisapp.cpp
Expand Up @@ -5690,7 +5690,7 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
options &= ~QgsVectorLayerSaveAsDialog::Symbology;
}

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

dialog->setCanvasExtent( mMapCanvas->mapSettings().visibleExtent(), mMapCanvas->mapSettings().destinationCrs() );
dialog->setIncludeZ( QgsWKBTypes::hasZ( QGis::fromOldWkbType( vlayer->wkbType() ) ) );
Expand Down Expand Up @@ -5745,14 +5745,15 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
dialog->onlySelected(),
&errorMessage,
datasourceOptions, dialog->layerOptions(),
dialog->skipAttributeCreation(),
dialog->attributeSelection() && dialog->selectedAttributes().isEmpty(),
&newFilename,
static_cast< QgsVectorFileWriter::SymbologyExport >( dialog->symbologyExport() ),
dialog->scaleDenominator(),
dialog->hasFilterExtent() ? &filterExtent : nullptr,
autoGeometryType ? QgsWKBTypes::Unknown : forcedGeometryType,
dialog->forceMulti(),
dialog->includeZ()
dialog->includeZ(),
dialog->selectedAttributes()
);

delete ct;
Expand Down
60 changes: 45 additions & 15 deletions src/core/qgsvectorfilewriter.cpp
Expand Up @@ -1759,16 +1759,12 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
}

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

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

if ( !attrValue.isValid() || attrValue.isNull() )
continue;
Expand Down Expand Up @@ -1950,6 +1946,16 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
return poFeature;
}

void QgsVectorFileWriter::resetMap( const QgsAttributeList &attributes )
{
QMap<int, int> omap( mAttrIdxToOgrIdx );
mAttrIdxToOgrIdx.clear();
for ( int i = 0; i < attributes.size(); i++ )
{
mAttrIdxToOgrIdx.insert( attributes[i], omap[i] );
}
}

bool QgsVectorFileWriter::writeFeature( OGRLayerH layer, OGRFeatureH feature )
{
if ( OGR_L_CreateFeature( layer, feature ) != OGRERR_NONE )
Expand Down Expand Up @@ -1998,7 +2004,8 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
const QgsRectangle* filterExtent,
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ )
bool includeZ,
QgsAttributeList attributes )
{
QgsCoordinateTransform* ct = nullptr;
if ( destCRS && layer )
Expand All @@ -2007,7 +2014,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
}

QgsVectorFileWriter::WriterError error = writeAsVectorFormat( layer, fileName, fileEncoding, ct, driverName, onlySelected,
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ );
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ, attributes );
delete ct;
return error;
}
Expand All @@ -2028,7 +2035,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
const QgsRectangle* filterExtent,
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ )
bool includeZ,
QgsAttributeList attributes )
{
if ( !layer )
{
Expand Down Expand Up @@ -2061,7 +2069,27 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
destWkbType = QgsWKBTypes::multiType( destWkbType );
}

QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();
if ( skipAttributeCreation )
attributes.clear();
else if ( attributes.isEmpty() )
{
Q_FOREACH ( int idx, layer->attributeList() )
{
const QgsField &fld = layer->fields()[idx];
if ( layer->providerType() == "oracle" && fld.typeName().contains( "SDO_GEOMETRY" ) )
continue;
attributes.append( idx );
}
}

QgsFields fields;
if ( !attributes.isEmpty() )
{
Q_FOREACH ( int attrIdx, attributes )
{
fields.append( layer->fields()[attrIdx] );
}
}

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


if ( newFilename )
{
QgsDebugMsg( "newFilename = " + *newFilename );
Expand All @@ -2136,18 +2165,17 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
errorMessage->clear();
}

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

//add possible attributes needed by renderer
writer->addRendererAttributes( layer, allAttr );
writer->addRendererAttributes( layer, attributes );

QgsFeatureRequest req;
if ( layer->wkbType() == QGis::WKBNoGeometry )
{
req.setFlags( QgsFeatureRequest::NoGeometry );
}
req.setSubsetOfAttributes( allAttr );
req.setSubsetOfAttributes( attributes );
if ( onlySelected )
req.setFilterFids( layer->selectedFeaturesIds() );
QgsFeatureIterator fit = layer->getFeatures( req );
Expand Down Expand Up @@ -2190,6 +2218,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
transactionsEnabled = false;
}

writer->resetMap( attributes );

// write all features
while ( fit.nextFeature( fet ) )
{
Expand Down Expand Up @@ -2219,7 +2249,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
if ( fet.constGeometry() && filterExtent && !fet.constGeometry()->intersects( *filterExtent ) )
continue;

if ( allAttr.size() < 1 && skipAttributeCreation )
if ( attributes.size() < 1 && skipAttributeCreation )
{
fet.initAttributes( 0 );
}
Expand Down

1 comment on commit 99d5e42

@nirvn
Copy link
Contributor

@nirvn nirvn commented on 99d5e42 Apr 3, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jef-n , very useful, thanks.

Please sign in to comment.