[FEATURE] vector file writer: allow selection of attributes to export

This commit is contained in:
Juergen E. Fischer 2016-04-03 01:46:33 +02:00
parent c93187d78c
commit 99d5e42247
7 changed files with 201 additions and 55 deletions

View File

@ -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,
@ -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
@ -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,
@ -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 */

View File

@ -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 ) )
{
@ -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 );
}
@ -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;
@ -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
@ -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 );
}
}

View File

@ -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;
@ -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
@ -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();
@ -113,6 +116,7 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
QgsRectangle mLayerExtent;
QgsCoordinateReferenceSystem mLayerCrs;
QgsVectorLayer *mLayer;
};
#endif // QGSVECTORLAYERSAVEASDIALOG_H

View File

@ -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() ) ) );
@ -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;

View File

@ -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;
@ -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 )
@ -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 )
@ -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;
}
@ -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 )
{
@ -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() )
{
@ -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 );
@ -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 );
@ -2190,6 +2218,8 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
transactionsEnabled = false;
}
writer->resetMap( attributes );
// write all features
while ( fit.nextFeature( fet ) )
{
@ -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 );
}

View File

@ -179,6 +179,7 @@ class CORE_EXPORT 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,
@ -196,14 +197,15 @@ class CORE_EXPORT QgsVectorFileWriter
const QgsRectangle* filterExtent = nullptr,
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
@ -218,7 +220,8 @@ class CORE_EXPORT 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,
@ -236,7 +239,8 @@ class CORE_EXPORT QgsVectorFileWriter
const QgsRectangle* filterExtent = nullptr,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
bool includeZ = false,
QgsAttributeList attributes = QgsAttributeList()
);
/** Create a new vector file writer */
@ -356,6 +360,7 @@ class CORE_EXPORT QgsVectorFileWriter
private:
void init( QString vectorFileName, QString fileEncoding, const QgsFields& fields, QgsWKBTypes::Type geometryType, const QgsCoordinateReferenceSystem* srs, const QString& driverName, QStringList datasourceOptions, QStringList layerOptions, QString* newFilename );
void resetMap( const QgsAttributeList &attributes );
QgsRenderContext mRenderContext;

View File

@ -71,7 +71,7 @@
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector" native="true">
<widget class="QgsProjectionSelectionWidget" name="mCrsSelector">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
@ -90,8 +90,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>555</width>
<height>650</height>
<width>553</width>
<height>548</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
@ -120,13 +120,55 @@
</widget>
</item>
<item>
<widget class="QCheckBox" name="mSkipAttributeCreation">
<property name="toolTip">
<string>This allows one to suppress attribute creation as some OGR drivers (eg. DGN, DXF) don't support it.</string>
<widget class="QgsCollapsibleGroupBox" name="mAttributesSelection">
<property name="title">
<string>Limit attributes to export</string>
</property>
<property name="text">
<string>Skip attribute creation</string>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<property name="collapsed">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<widget class="QTableWidget" name="mAttributeTable">
<property name="rowCount">
<number>1</number>
</property>
<property name="columnCount">
<number>2</number>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
<row/>
<column/>
<column/>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QPushButton" name="mSelectAllAttributes">
<property name="text">
<string>Select All</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="mDeselectAllAttributes">
<property name="text">
<string>Deselect All</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
@ -246,7 +288,7 @@
<property name="title">
<string>Custom Options</string>
</property>
<property name="collapsed" stdset="0">
<property name="collapsed">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_3">
@ -356,12 +398,6 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsProjectionSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsprojectionselectionwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBox</class>
<extends>QGroupBox</extends>
@ -370,10 +406,16 @@
</customwidget>
<customwidget>
<class>QgsExtentGroupBox</class>
<extends>QGroupBox</extends>
<extends>QgsCollapsibleGroupBox</extends>
<header>qgsextentgroupbox.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsProjectionSelectionWidget</class>
<extends>QWidget</extends>
<header>qgsprojectionselectionwidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mFormatComboBox</tabstop>
@ -383,7 +425,7 @@
<tabstop>scrollArea</tabstop>
<tabstop>mEncodingComboBox</tabstop>
<tabstop>mSelectedOnly</tabstop>
<tabstop>mSkipAttributeCreation</tabstop>
<tabstop>mAttributesSelection</tabstop>
<tabstop>mAddToCanvas</tabstop>
<tabstop>mSymbologyExportComboBox</tabstop>
<tabstop>mScaleSpinBox</tabstop>