Index: src/core/qgsvectorfilewriter.h =================================================================== --- src/core/qgsvectorfilewriter.h (revision 12940) +++ src/core/qgsvectorfilewriter.h (working copy) @@ -53,7 +53,8 @@ ErrProjection // added in 1.5 }; - /** Write contents of vector layer to a shapefile */ + /** Write contents of vector layer to a shapefile + @note: deprecated. Use writeAsVectorFormat instead*/ static WriterError writeAsShapefile( QgsVectorLayer* layer, const QString& shapefileName, const QString& fileEncoding, @@ -61,6 +62,16 @@ bool onlySelected = FALSE, QString *errorMessage = 0 ); + /** Write contents of vector layer to an (OGR supported) vector formt + @note: this method was added in version 1.5*/ + static WriterError writeAsVectorFormat( QgsVectorLayer* layer, + const QString& fileName, + const QString& fileEncoding, + const QgsCoordinateReferenceSystem *destCRS, + const QString& driverName = "ESRI Shapefile", + bool onlySelected = FALSE, + QString *errorMessage = 0 ); + /** create shapefile and initialize it */ QgsVectorFileWriter( const QString& shapefileName, const QString& fileEncoding, @@ -69,6 +80,15 @@ const QgsCoordinateReferenceSystem* srs, const QString& driverName = "ESRI Shapefile" ); + /**Returns map with format filter string as key and OGR format key as value*/ + static QMap< QString, QString> supportedFiltersAndFormats(); + + /**Returns filter string that can be used for dialogs*/ + static QString fileFilterString(); + + /**Creates a filter for an OGR driver key*/ + static QString filterForDriver( const QString& driverName ); + /** checks whether there were any errors in constructor */ WriterError hasError(); Index: src/core/qgsvectorfilewriter.cpp =================================================================== --- src/core/qgsvectorfilewriter.cpp (revision 12940) +++ src/core/qgsvectorfilewriter.cpp (working copy) @@ -386,7 +386,8 @@ bool onlySelected, QString *errorMessage ) { - + return writeAsVectorFormat( layer, shapefileName, fileEncoding, destCRS, "ESRI Shapefile", onlySelected, errorMessage ); +#if 0 const QgsCoordinateReferenceSystem* outputCRS; QgsCoordinateTransform* ct = 0; int shallTransform = false; @@ -471,9 +472,105 @@ } return NoError; +#endif //0 } +QgsVectorFileWriter::WriterError +QgsVectorFileWriter::writeAsVectorFormat( QgsVectorLayer* layer, + const QString& fileName, + const QString& fileEncoding, + const QgsCoordinateReferenceSystem *destCRS, + const QString& driverName, + bool onlySelected, + QString *errorMessage ) +{ + const QgsCoordinateReferenceSystem* outputCRS; + QgsCoordinateTransform* ct = 0; + int shallTransform = false; + if ( destCRS && destCRS->isValid() ) + { + // This means we should transform + outputCRS = destCRS; + shallTransform = true; + } + else + { + // This means we shouldn't transform, use source CRS as output (if defined) + outputCRS = &layer->srs(); + } + QgsVectorFileWriter* writer = + new QgsVectorFileWriter( fileName, fileEncoding, layer->pendingFields(), layer->wkbType(), outputCRS, driverName ); + + // check whether file creation was successful + WriterError err = writer->hasError(); + if ( err != NoError ) + { + if ( errorMessage ) + *errorMessage = writer->errorMessage(); + delete writer; + return err; + } + + QgsAttributeList allAttr = layer->pendingAllAttributesList(); + QgsFeature fet; + + layer->select( allAttr, QgsRectangle(), true ); + + const QgsFeatureIds& ids = layer->selectedFeaturesIds(); + + // Create our transform + if ( destCRS ) + { + ct = new QgsCoordinateTransform( layer->srs(), *destCRS ); + } + + // Check for failure + if ( ct == NULL ) + { + shallTransform = false; + } + + // write all features + while ( layer->nextFeature( fet ) ) + { + if ( onlySelected && !ids.contains( fet.id() ) ) + continue; + + if ( shallTransform ) + { + try + { + fet.geometry()->transform( *ct ); + } + catch ( QgsCsException &e ) + { + delete ct; + delete writer; + + QString msg = QObject::tr( "Failed to transform a point while drawing a feature of type '%1'. Writing stopped. (Exception: %2)" ) + .arg( fet.typeName() ).arg( e.what() ); + QgsLogger::warning( msg ); + if ( errorMessage ) + *errorMessage = msg; + + return ErrProjection; + } + } + writer->addFeature( fet ); + } + + delete writer; + + if ( shallTransform ) + { + delete ct; + } + + return NoError; +} + + bool QgsVectorFileWriter::deleteShapeFile( QString theFileName ) { QFileInfo fi( theFileName ); @@ -498,3 +595,157 @@ return ok; } + +QMap< QString, QString> QgsVectorFileWriter::supportedFiltersAndFormats() +{ + QMap resultMap; + + QgsApplication::registerOgrDrivers(); + int const drvCount = OGRGetDriverCount(); + + QString drvName; + QString filterString; + for ( int i = 0; i < drvCount; ++i ) + { + OGRSFDriverH drv = OGRGetDriver( i ); + if ( drv ) + { + drvName = OGR_Dr_GetName( drv ); + if ( OGR_Dr_TestCapability( drv, ODrCCreateDataSource ) != 0 ) + { + //add driver name and filter to map + filterString = QgsVectorFileWriter::filterForDriver( drvName ); + if ( !filterString.isEmpty() ) + { + resultMap.insert( filterString, drvName ); + } + } + } + } + + return resultMap; +} + +QString QgsVectorFileWriter::fileFilterString() +{ + QString filterString; + QMap< QString, QString> driverFormatMap = QgsVectorFileWriter::supportedFiltersAndFormats(); + QMap< QString, QString>::const_iterator it = driverFormatMap.constBegin(); + for ( ; it != driverFormatMap.constEnd(); ++it ) + { + filterString += it.key(); + } + return filterString; +} + +QString QgsVectorFileWriter::filterForDriver( const QString& driverName ) +{ + QString longName; + QString glob; + + if ( driverName.startsWith( "AVCE00" ) ) + { + longName = "Arc/Info ASCII Coverage"; + glob = "*.e00"; + } + else if ( driverName.startsWith( "BNA" ) ) + { + longName = "Atlas BNA"; + glob = "*.bna"; + } + else if ( driverName.startsWith( "CSV" ) ) + { + longName = "Comma Separated Value"; + glob = "*.csv"; + } + else if ( driverName.startsWith( "ESRI" ) ) + { + longName = "ESRI Shapefiles"; + glob = "*.shp"; + } + else if ( driverName.startsWith( "FMEObjects Gateway" ) ) + { + longName = "FMEObjects Gateway"; + glob = "*.fdd"; + } + else if ( driverName.startsWith( "GeoJSON" ) ) + { + longName = "GeoJSON"; + glob = "*.geojson"; + } + else if ( driverName.startsWith( "GeoRSS" ) ) + { + longName = "GeoRSS"; + glob = "*.xml"; + } + else if ( driverName.startsWith( "GML" ) ) + { + longName = "Geography Markup Language"; + glob = "*.gml"; + } + else if ( driverName.startsWith( "GMT" ) ) + { + longName = "GMT"; + glob = "*.gmt"; + } + else if ( driverName.startsWith( "GPX" ) ) + { + longName = "GPX"; + glob = "*.gpx"; + } + else if ( driverName.startsWith( "Interlis 1" ) ) + { + longName = "INTERLIS 1"; + glob = "*.itf *.xml *.ili"; + } + else if ( driverName.startsWith( "Interlis 2" ) ) + { + longName = "INTERLIS 2"; + glob = "*.itf *.xml *.ili"; + } + else if ( driverName.startsWith( "KML" ) ) + { + longName = "KML"; + glob = "*.kml" ; + } + else if ( driverName.startsWith( "MapInfo File" ) ) + { + longName = "Mapinfo File"; + glob = "*.mif *.tab"; + } + else if ( driverName.startsWith( "DGN" ) ) + { + longName = "Microstation DGN"; + glob = "*.dgn"; + } + else if ( driverName.startsWith( "S57" ) ) + { + longName = "S-57 Base file"; + glob = "*.000"; + } + else if ( driverName.startsWith( "SDTS" ) ) + { + longName = "Spatial Data Transfer Standard"; + glob = "*catd.ddf"; + } + else if ( driverName.startsWith( "SQLite" ) ) + { + longName = "SQLite"; + glob = "*.sqlite"; + } + else if ( driverName.startsWith( "VRT" ) ) + { + longName = "VRT - Virtual Datasource "; + glob = "*.vrt"; + } + else if ( driverName.startsWith( "XPlane" ) ) + { + longName = "X-Plane/Flighgear"; + glob = "apt.dat nav.dat fix.dat awy.dat"; + } + else + { + return QString(); + } + return "[OGR] " + longName + " (" + glob.toLower() + " " + glob.toUpper() + ");;"; +} Index: src/app/legend/qgslegendlayer.h =================================================================== --- src/app/legend/qgslegendlayer.h (revision 12940) +++ src/app/legend/qgslegendlayer.h (working copy) @@ -87,6 +87,9 @@ void saveAsShapefile(); void saveSelectionAsShapefile(); + void saveAsVectorFile(); + void saveSelectionAsVectorFile(); + /**update the layer's icon to show whether is in editing mode or in overview */ void updateIcon(); @@ -111,6 +114,8 @@ /**Save as shapefile (called from saveAsShapefile and saveSelectionAsShapefile)*/ void saveAsShapefileGeneral( bool saveOnlySelection ); + void saveAsVectorFileGeneral( bool saveOnlySelection ); + private: /** Helper method to make the font bold from all ctors. * Not to be confused with setFont() which is inherited Index: src/app/legend/qgslegendlayer.cpp =================================================================== --- src/app/legend/qgslegendlayer.cpp (revision 12940) +++ src/app/legend/qgslegendlayer.cpp (working copy) @@ -423,14 +423,14 @@ } } - // save as shapefile - theMenu.addAction( tr( "Save as shapefile..." ), this, SLOT( saveAsShapefile() ) ); + // save as vector file + theMenu.addAction( tr( "Save as..." ), this, SLOT( saveAsVectorFile() ) ); - // save selection as shapefile - QAction* saveSelectionAction = theMenu.addAction( tr( "Save selection as shapefile..." ), this, SLOT( saveSelectionAsShapefile() ) ); + // save selection as vector file + QAction* saveSelectionAsAction = theMenu.addAction( tr( "Save selection as..." ), this, SLOT( saveSelectionAsVectorFile() ) ); if ( vlayer->selectedFeatureCount() == 0 ) { - saveSelectionAction->setEnabled( false ); + saveSelectionAsAction->setEnabled( false ); } theMenu.addSeparator(); @@ -460,6 +460,16 @@ saveAsShapefileGeneral( TRUE ); } +void QgsLegendLayer::saveAsVectorFile() +{ + saveAsVectorFileGeneral( false ); +} + +void QgsLegendLayer::saveSelectionAsVectorFile() +{ + saveAsVectorFileGeneral( true ); +} + ////////// void QgsLegendLayer::setVisible( bool visible ) @@ -492,9 +502,83 @@ legend()->updateOverview(); } +void QgsLegendLayer::saveAsVectorFileGeneral( bool saveOnlySelection ) +{ + if ( mLyr.layer()->type() != QgsMapLayer::VectorLayer ) + return; + QgsVectorLayer* vlayer = qobject_cast( mLyr.layer() ); + //get output name and format + QSettings settings; + QString filter = QString( "Shapefiles (*.shp)" ); + QString dirName = settings.value( "/UI/lastVectorfileDir", "." ).toString(); + QString filterString = QgsVectorFileWriter::fileFilterString(); + QString selectedFilter = settings.value( "/UI/lastVectorFilter", "[OGR] ESRI Shapefiles (*.shp *.SHP)" ).toString(); + QString outputFile = QFileDialog::getSaveFileName( 0, tr( "Save layer as..." ), dirName, filterString, &selectedFilter ); + if ( outputFile.isNull() ) + { + return; //cancelled + } + settings.setValue( "/UI/lastVectorfileDir", QFileInfo( outputFile ).absolutePath() ); + settings.setValue( "/UI/lastVectorFilter", selectedFilter ); + + QMap< QString, QString> filterDriverMap = QgsVectorFileWriter::supportedFiltersAndFormats(); + QMap< QString, QString>::const_iterator it = filterDriverMap.find( selectedFilter + ";;" ); + if ( it == filterDriverMap.constEnd() ) + { + return; //unknown format + } + + QString driverKey = *it; + + //output CRS + QgsCoordinateReferenceSystem destCRS = vlayer->srs(); + // Find out if we have projections enabled or not + if ( QgisApp::instance()->mapCanvas()->mapRenderer()->hasCrsTransformEnabled() ) + { + destCRS = QgisApp::instance()->mapCanvas()->mapRenderer()->destinationSrs(); + } + + QgsGenericProjectionSelector * mySelector = new QgsGenericProjectionSelector(); + mySelector->setSelectedCrsId( destCRS.srsid() ); + mySelector->setMessage( tr( "Select the coordinate reference system for the saved shapefile. " + "The data points will be transformed from the layer coordinate reference system." ) ); + + if ( mySelector->exec() ) + { + QgsCoordinateReferenceSystem srs( mySelector->selectedCrsId(), QgsCoordinateReferenceSystem::InternalCrsId ); + destCRS = srs; + // destCRS->createFromId(mySelector->selectedCrsId(), QgsCoordinateReferenceSystem::InternalCrsId) + } + else + { + // Aborted CS selection, don't save. + delete mySelector; + return; + } + delete mySelector; + + // overwrite the file - user will already have been prompted + // to verify they want to overwrite by the file dialog above + // might not even exists in the given case. + if ( driverKey == "ESRI Shapefile" ) + { + // add the extension if not present + if ( !outputFile.endsWith( ".shp", Qt::CaseInsensitive ) ) + { + outputFile += ".shp"; + } + QgsVectorFileWriter::deleteShapeFile( outputFile ); + } + + QString errorMessage; + QgsVectorFileWriter::WriterError error; + error = QgsVectorFileWriter::writeAsVectorFormat( vlayer, outputFile, "utf-8", &destCRS, driverKey, saveOnlySelection, &errorMessage ); +} + + void QgsLegendLayer::saveAsShapefileGeneral( bool saveOnlySelection ) { QgsCoordinateReferenceSystem destCRS; Index: python/core/qgsvectorfilewriter.sip =================================================================== --- python/core/qgsvectorfilewriter.sip (revision 12940) +++ python/core/qgsvectorfilewriter.sip (working copy) @@ -31,6 +31,16 @@ const QgsCoordinateReferenceSystem*, bool onlySelected = FALSE); + /** Write contents of vector layer to an (OGR supported) vector formt + @note: this method was added in version 1.5*/ + static WriterError writeAsVectorFormat( QgsVectorLayer* layer, + const QString& fileName, + const QString& fileEncoding, + const QgsCoordinateReferenceSystem *destCRS, + const QString& driverName = "ESRI Shapefile", + bool onlySelected = FALSE, + QString *errorMessage = 0 ); + /** create shapefile and initialize it */ QgsVectorFileWriter(const QString& shapefileName, const QString& fileEncoding, @@ -38,6 +48,15 @@ QGis::WkbType geometryType, const QgsCoordinateReferenceSystem* srs, const QString& driverName = "ESRI Shapefile" ); + + /**Returns map with format filter string as key and OGR format key as value*/ + static QMap< QString, QString> supportedFiltersAndFormats(); + + /**Returns filter string that can be used for dialogs*/ + static QString fileFilterString(); + + /**Creates a filter for an OGR driver key*/ + static QString filterForDriver( const QString& driverName ); /** checks whether there were any errors in constructor */ WriterError hasError();