Skip to content
Permalink
Browse files

[FEATURE] Allow overide of geometry type in vector save as dialog

Also allows forcing output file to be multi type, or include z
dimension.

This makes it possible to do things like save a geometryless
table WITH a geometry type, so that geometries can then be
manually added to rows. Previously this was only possible to do in
QGIS by resorting to dummy joins or other hacks.
  • Loading branch information
nyalldawson committed Nov 23, 2015
1 parent 09e7102 commit ffebfd9bb719daf1ffa9bf7dde68c6e5892dcb89
@@ -121,7 +121,10 @@ class QgsVectorFileWriter
@param newFilename QString pointer which will contain the new file name created (in case it is different to fileName).
@param symbologyExport symbology to export
@param symbologyScale scale of symbology
@param filterExtent if not a null pointer, only features intersecting the extent will be saved
@param filterExtent if not a null pointer, only features intersecting the extent will be saved (added in QGIS 2.4)
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)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
@@ -136,10 +139,33 @@ class QgsVectorFileWriter
QString *newFilename = 0,
SymbologyExport symbologyExport = NoSymbology,
double symbologyScale = 1.0,
const QgsRectangle* filterExtent = 0 // added in 2.4
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
);

//! @note added in v2.2
/** 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 driverName OGR driver to use
* @param onlySelected write only selected features of layer
* @param errorMessage pointer to buffer fo error message
* @param datasourceOptions list of OGR data source creation options
* @param layerOptions list of OGR layer creation options
* @param skipAttributeCreation only write geometries
* @param newFilename QString pointer which will contain the new file name created (in case it is different to fileName).
* @param symbologyExport symbology to export
* @param symbologyScale scale of symbology
* @param filterExtent if not a null pointer, only features intersecting the extent will be saved (added in QGIS 2.4)
* @param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
* 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
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
const QString& fileEncoding,
@@ -153,7 +179,10 @@ class QgsVectorFileWriter
QString *newFilename = 0,
SymbologyExport symbologyExport = NoSymbology,
double symbologyScale = 1.0,
const QgsRectangle* filterExtent = 0 // added in 2.4
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
);

/** Create shapefile and initialize it */
@@ -67,6 +67,15 @@ void QgsVectorLayerSaveAsDialog::setup()
mFormatComboBox->setCurrentIndex( mFormatComboBox->findData( format ) );
mFormatComboBox->blockSignals( false );

//add geometry types to combobox
mGeometryTypeComboBox->addItem( tr( "Automatic" ), -1 );
mGeometryTypeComboBox->addItem( QgsWKBTypes::displayString( QgsWKBTypes::Point ), QgsWKBTypes::Point );
mGeometryTypeComboBox->addItem( QgsWKBTypes::displayString( QgsWKBTypes::LineString ), QgsWKBTypes::LineString );
mGeometryTypeComboBox->addItem( QgsWKBTypes::displayString( QgsWKBTypes::Polygon ), QgsWKBTypes::Polygon );
mGeometryTypeComboBox->addItem( QgsWKBTypes::displayString( QgsWKBTypes::GeometryCollection ), QgsWKBTypes::GeometryCollection );
mGeometryTypeComboBox->addItem( tr( "No geometry" ), QgsWKBTypes::NoGeometry );
mGeometryTypeComboBox->setCurrentIndex( mGeometryTypeComboBox->findData( -1 ) );

mEncodingComboBox->addItems( QgsVectorDataProvider::availableEncodings() );

QString enc = settings.value( "/UI/encoding", "System" ).toString();
@@ -458,6 +467,44 @@ bool QgsVectorLayerSaveAsDialog::onlySelected() const
return mSelectedOnly->isChecked();
}

QgsWKBTypes::Type QgsVectorLayerSaveAsDialog::geometryType() const
{
int currentIndexData = mGeometryTypeComboBox->itemData( mGeometryTypeComboBox->currentIndex() ).toInt();
if ( currentIndexData == -1 )
{
//automatic
return QgsWKBTypes::Unknown;
}

return ( QgsWKBTypes::Type )currentIndexData;
}

bool QgsVectorLayerSaveAsDialog::automaticGeometryType() const
{
int currentIndexData = mGeometryTypeComboBox->itemData( mGeometryTypeComboBox->currentIndex() ).toInt();
return currentIndexData == -1;
}

bool QgsVectorLayerSaveAsDialog::forceMulti() const
{
return mForceMultiCheckBox->isChecked();
}

void QgsVectorLayerSaveAsDialog::setForceMulti( bool checked )
{
mForceMultiCheckBox->setChecked( checked );
}

bool QgsVectorLayerSaveAsDialog::includeZ() const
{
return mIncludeZCheckBox->isChecked();
}

void QgsVectorLayerSaveAsDialog::setIncludeZ( bool checked )
{
mIncludeZCheckBox->setChecked( checked );
}

void QgsVectorLayerSaveAsDialog::on_mSymbologyExportComboBox_currentIndexChanged( const QString& text )
{
bool scaleEnabled = true;
@@ -468,3 +515,11 @@ void QgsVectorLayerSaveAsDialog::on_mSymbologyExportComboBox_currentIndexChanged
mScaleSpinBox->setEnabled( scaleEnabled );
mScaleLabel->setEnabled( scaleEnabled );
}

void QgsVectorLayerSaveAsDialog::on_mGeometryTypeComboBox_currentIndexChanged( int index )
{
int currentIndexData = mGeometryTypeComboBox->itemData( index ).toInt();

mForceMultiCheckBox->setEnabled( currentIndexData != -1 );
mIncludeZCheckBox->setEnabled( currentIndexData != -1 );
}
@@ -65,13 +65,44 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav

bool onlySelected() const;

/** Returns the selected flat geometry type for the export.
* @see automaticGeometryType()
* @see forceMulti()
* @see includeZ()
*/
QgsWKBTypes::Type geometryType() const;

/** Returns true if geometry type is set to automatic.
* @see geometryType()
*/
bool automaticGeometryType() const;

/** Returns true if force multi geometry type is checked.
* @see includeZ()
*/
bool forceMulti() const;

/** Sets whether the force multi geometry checkbox should be checked.
*/
void setForceMulti( bool checked );

/** Returns true if include z dimension is checked.
* @see forceMulti()
*/
bool includeZ() const;

/** Sets whether the include z dimension checkbox should be checked.
*/
void setIncludeZ( bool checked );

private slots:
void on_mFormatComboBox_currentIndexChanged( int idx );
void on_leFilename_textChanged( const QString& text );
void on_browseFilename_clicked();
void on_mCrsSelector_crsChanged( const QgsCoordinateReferenceSystem& crs );
void on_buttonBox_helpRequested() { QgsContextHelp::run( metaObject()->className() ); }
void on_mSymbologyExportComboBox_currentIndexChanged( const QString& text );
void on_mGeometryTypeComboBox_currentIndexChanged( int index );
void accept() override;

private:
@@ -5476,13 +5476,16 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), vlayer->extent(), vlayer->selectedFeatureCount() != 0, options, this );

dialog->setCanvasExtent( mMapCanvas->mapSettings().visibleExtent(), mMapCanvas->mapSettings().destinationCrs() );
dialog->setIncludeZ( QgsWKBTypes::hasZ( QGis::fromOldWkbType( vlayer->wkbType() ) ) );

if ( dialog->exec() == QDialog::Accepted )
{
QString encoding = dialog->encoding();
QString vectorFilename = dialog->filename();
QString format = dialog->format();
QStringList datasourceOptions = dialog->datasourceOptions();
bool autoGeometryType = dialog->automaticGeometryType();
QgsWKBTypes::Type forcedGeometryType = dialog->geometryType();

QgsCoordinateTransform* ct = 0;
destCRS = QgsCoordinateReferenceSystem( dialog->crs(), QgsCoordinateReferenceSystem::InternalCrsId );
@@ -5529,7 +5532,11 @@ void QgisApp::saveAsVectorFileGeneral( QgsVectorLayer* vlayer, bool symbologyOpt
&newFilename,
( QgsVectorFileWriter::SymbologyExport )( dialog->symbologyExport() ),
dialog->scaleDenominator(),
dialog->hasFilterExtent() ? &filterExtent : 0 );
dialog->hasFilterExtent() ? &filterExtent : 0,
autoGeometryType ? QgsWKBTypes::Unknown : forcedGeometryType,
dialog->forceMulti(),
dialog->includeZ()
);

delete ct;

@@ -1725,7 +1725,8 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
QgsGeometry *geom = feature.geometry();

// turn single geoemetry to multi geometry if needed
if ( geom && geom->wkbType() != mWkbType && geom->wkbType() == QGis::singleType( mWkbType ) )
if ( geom && geom->wkbType() != mWkbType &&
QgsWKBTypes::flatType( geom->geometry()->wkbType() ) == QgsWKBTypes::flatType( QgsWKBTypes::singleType( QGis::fromOldWkbType( mWkbType ) ) ) )
{
geom->convertToMultiType();
}
@@ -1738,6 +1739,10 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
// we must force the use of 25D.
if ( mWkbType >= QGis::WKBPoint25D && mWkbType <= QGis::WKBMultiPolygon25D )
{
//ND: I suspect there's a bug here, in that this is NOT converting the geometry's WKB type,
//so the exported WKB has a different type to what the OGRGeometry is expecting.
//possibly this is handled already in OGR, but it should be fixed regardless by actually converting
//geom to the correct WKB type
QgsWKBTypes::Type wkbType = QGis::fromOldWkbType( geom->wkbType() );
if ( wkbType >= QgsWKBTypes::PointZ && wkbType <= QgsWKBTypes::MultiPolygonZ )
{
@@ -1799,6 +1804,10 @@ OGRFeatureH QgsVectorFileWriter::createFeature( QgsFeature& feature )
// set geometry (ownership is not passed to OGR)
OGR_F_SetGeometry( poFeature, mGeom );
}
else
{
OGR_F_SetGeometry( poFeature, createEmptyGeometry( mWkbType ) );
}
}
return poFeature;
}
@@ -1848,7 +1857,10 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
QString *newFilename,
SymbologyExport symbologyExport,
double symbologyScale,
const QgsRectangle* filterExtent )
const QgsRectangle* filterExtent,
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ )
{
QgsCoordinateTransform* ct = 0;
if ( destCRS && layer )
@@ -1857,7 +1869,7 @@ QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer,
}

QgsVectorFileWriter::WriterError error = writeAsVectorFormat( layer, fileName, fileEncoding, ct, driverName, onlySelected,
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent );
errorMessage, datasourceOptions, layerOptions, skipAttributeCreation, newFilename, symbologyExport, symbologyScale, filterExtent, overrideGeometryType, forceMulti, includeZ );
delete ct;
return error;
}
@@ -1875,7 +1887,10 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
QString *newFilename,
SymbologyExport symbologyExport,
double symbologyScale,
const QgsRectangle* filterExtent )
const QgsRectangle* filterExtent,
QgsWKBTypes::Type overrideGeometryType,
bool forceMulti,
bool includeZ )
{
if ( !layer )
{
@@ -1896,7 +1911,18 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
outputCRS = &layer->crs();
}

QGis::WkbType wkbType = layer->wkbType();
QgsWKBTypes::Type destWkbType = QGis::fromOldWkbType( layer->wkbType() );
if ( overrideGeometryType != QgsWKBTypes::Unknown )
{
destWkbType = QgsWKBTypes::flatType( overrideGeometryType );
if ( QgsWKBTypes::hasZ( overrideGeometryType ) || includeZ )
destWkbType = QgsWKBTypes::addZ( destWkbType );
}
if ( forceMulti )
{
destWkbType = QgsWKBTypes::multiType( destWkbType );
}

QgsFields fields = skipAttributeCreation ? QgsFields() : layer->fields();

if ( layer->providerType() == "ogr" && layer->dataProvider() )
@@ -1912,19 +1938,21 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
}

// Shapefiles might contain multi types although wkbType() only reports singles
if ( layer->storageType() == "ESRI Shapefile" )
if ( layer->storageType() == "ESRI Shapefile" && !QgsWKBTypes::isMultiType( destWkbType ) )
{
const QgsFeatureIds &ids = layer->selectedFeaturesIds();
QgsFeatureIterator fit = layer->getFeatures();
QgsFeatureRequest req;
if ( onlySelected )
{
req.setFilterFids( layer->selectedFeaturesIds() );
}
QgsFeatureIterator fit = layer->getFeatures( req );
QgsFeature fet;

while ( fit.nextFeature( fet ) )
{
if ( onlySelected && !ids.contains( fet.id() ) )
continue;

if ( fet.constGeometry() && fet.constGeometry()->wkbType() == QGis::multiType( wkbType ) )
if ( fet.constGeometry() && !fet.constGeometry()->isEmpty() && QgsWKBTypes::isMultiType( fet.constGeometry()->geometry()->wkbType() ) )
{
wkbType = QGis::multiType( wkbType );
destWkbType = QgsWKBTypes::multiType( destWkbType );
break;
}
}
@@ -1947,7 +1975,7 @@ QgsVectorFileWriter::WriterError QgsVectorFileWriter::writeAsVectorFormat( QgsVe
}

QgsVectorFileWriter* writer =
new QgsVectorFileWriter( fileName, fileEncoding, fields, wkbType, outputCRS, driverName, datasourceOptions, layerOptions, newFilename, symbologyExport );
new QgsVectorFileWriter( fileName, fileEncoding, fields, QGis::fromNewWkbType( destWkbType ), outputCRS, driverName, datasourceOptions, layerOptions, newFilename, symbologyExport );
writer->setSymbologyScaleDenominator( symbologyScale );

if ( newFilename )
@@ -174,7 +174,11 @@ class CORE_EXPORT QgsVectorFileWriter
@param newFilename QString pointer which will contain the new file name created (in case it is different to fileName).
@param symbologyExport symbology to export
@param symbologyScale scale of symbology
@param filterExtent if not a null pointer, only features intersecting the extent will be saved
@param filterExtent if not a null pointer, only features intersecting the extent will be saved (added in QGIS 2.4)
@param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
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)
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
@@ -189,10 +193,33 @@ class CORE_EXPORT QgsVectorFileWriter
QString *newFilename = 0,
SymbologyExport symbologyExport = NoSymbology,
double symbologyScale = 1.0,
const QgsRectangle* filterExtent = 0 // added in 2.4
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
);

//! @note added in v2.2
/** 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 driverName OGR driver to use
* @param onlySelected write only selected features of layer
* @param errorMessage pointer to buffer fo error message
* @param datasourceOptions list of OGR data source creation options
* @param layerOptions list of OGR layer creation options
* @param skipAttributeCreation only write geometries
* @param newFilename QString pointer which will contain the new file name created (in case it is different to fileName).
* @param symbologyExport symbology to export
* @param symbologyScale scale of symbology
* @param filterExtent if not a null pointer, only features intersecting the extent will be saved (added in QGIS 2.4)
* @param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
* 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
*/
static WriterError writeAsVectorFormat( QgsVectorLayer* layer,
const QString& fileName,
const QString& fileEncoding,
@@ -206,7 +233,10 @@ class CORE_EXPORT QgsVectorFileWriter
QString *newFilename = 0,
SymbologyExport symbologyExport = NoSymbology,
double symbologyScale = 1.0,
const QgsRectangle* filterExtent = 0 // added in 2.4
const QgsRectangle* filterExtent = 0,
QgsWKBTypes::Type overrideGeometryType = QgsWKBTypes::Unknown,
bool forceMulti = false,
bool includeZ = false
);

/** Create shapefile and initialize it */

0 comments on commit ffebfd9

Please sign in to comment.
You can’t perform that action at this time.