Permalink
Browse files

[FEATURE] Paste features as new vector layer

  • Loading branch information...
blazek committed Jul 2, 2013
1 parent 8303ce0 commit 8d3199fb5f9d774522f4cf8d027aed0ce1e2fa11
Showing with 287 additions and 36 deletions.
  1. +20 −2 src/app/ogr/qgsvectorlayersaveasdialog.cpp
  2. +10 −0 src/app/ogr/qgsvectorlayersaveasdialog.h
  3. +151 −4 src/app/qgisapp.cpp
  4. +13 −1 src/app/qgisapp.h
  5. +40 −0 src/core/qgis.h
  6. +53 −29 src/ui/qgisapp.ui
@@ -29,8 +29,26 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* par
: QDialog( parent, fl )
, mCRS( srsid )
{
- setupUi( this );
+ setup();
+}
+QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, int options, QWidget* parent, Qt::WFlags fl )
+ : QDialog( parent, fl )
+ , mCRS( srsid )
+{
+ setup();
+ if ( !( options & Symbology ) )
+ {
+ mSymbologyExportLabel->hide();
+ mSymbologyExportComboBox->hide();
+ mScaleLabel->hide();
+ mScaleSpinBox->hide();
+ }
+}
+
+void QgsVectorLayerSaveAsDialog::setup()
+{
+ setupUi( this );
QSettings settings;
restoreGeometry( settings.value( "/Windows/VectorLayerSaveAs/geometry" ).toByteArray() );
QMap<QString, QString> map = QgsVectorFileWriter::ogrDriverList();
@@ -57,7 +75,7 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* par
mCRSSelection->clear();
mCRSSelection->addItems( QStringList() << tr( "Layer CRS" ) << tr( "Project CRS" ) << tr( "Selected CRS" ) );
- QgsCoordinateReferenceSystem srs( srsid, QgsCoordinateReferenceSystem::InternalCrsId );
+ QgsCoordinateReferenceSystem srs( mCRS, QgsCoordinateReferenceSystem::InternalCrsId );
leCRS->setText( srs.description() );
mEncodingComboBox->setCurrentIndex( idx );
@@ -30,7 +30,15 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
Q_OBJECT
public:
+ // bitmask of options to be shown
+ enum Options
+ {
+ Symbology = 1,
+ AllOptions = ~0
+ };
+
QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent = 0, Qt::WFlags fl = 0 );
+ QgsVectorLayerSaveAsDialog( long srsid, int options = AllOptions, QWidget* parent = 0, Qt::WFlags fl = 0 );
~QgsVectorLayerSaveAsDialog();
QString format() const;
@@ -58,6 +66,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
void accept();
private:
+ void setup();
+
long mCRS;
};
View
@@ -431,6 +431,7 @@ QgisApp *QgisApp::smInstance = 0;
QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent, Qt::WFlags fl )
: QMainWindow( parent, fl )
, mSplash( splash )
+ , mInternalClipboard( 0 )
, mShowProjectionTab( false )
, mPythonUtils( NULL )
#ifdef Q_OS_WIN
@@ -530,7 +531,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
updateProjectFromTemplates();
legendLayerSelectionChanged();
mSaveRollbackInProgress = false;
- activateDeactivateLayerRelatedActions( NULL );
// initialize the plugin manager
mPluginManager = new QgsPluginManager( this, restorePlugins );
@@ -584,6 +584,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
addWindow( mWindowAction );
#endif
+ activateDeactivateLayerRelatedActions( NULL ); // after members were created
+
// set application's caption
QString caption = tr( "QGIS - %1 ('%2')" ).arg( QGis::QGIS_VERSION ).arg( QGis::QGIS_RELEASE_NAME );
setWindowTitle( caption );
@@ -928,6 +930,8 @@ void QgisApp::createActions()
connect( mActionCutFeatures, SIGNAL( triggered() ), this, SLOT( editCut() ) );
connect( mActionCopyFeatures, SIGNAL( triggered() ), this, SLOT( editCopy() ) );
connect( mActionPasteFeatures, SIGNAL( triggered() ), this, SLOT( editPaste() ) );
+ connect( mActionPasteAsNewVector, SIGNAL( triggered() ), this, SLOT( pasteAsNewVector() ) );
+ connect( mActionPasteAsNewMemoryVector, SIGNAL( triggered() ), this, SLOT( pasteAsNewMemoryVector() ) );
connect( mActionCopyStyle, SIGNAL( triggered() ), this, SLOT( copyStyle() ) );
connect( mActionPasteStyle, SIGNAL( triggered() ), this, SLOT( pasteStyle() ) );
connect( mActionAddFeature, SIGNAL( triggered() ), this, SLOT( addFeature() ) );
@@ -4531,21 +4535,31 @@ void QgisApp::saveSelectionAsVectorFile()
saveAsVectorFileGeneral( true );
}
-void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )
+void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection, QgsVectorLayer* vlayer, bool symbologyOption )
{
if ( mMapCanvas && mMapCanvas->isDrawing() )
return;
if ( !mMapLegend )
return;
- QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() ); // FIXME: output of multiple layers at once?
+ if ( !vlayer )
+ {
+ vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() ); // FIXME: output of multiple layers at once?
+ }
+
if ( !vlayer )
return;
QgsCoordinateReferenceSystem destCRS;
- QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), this );
+ int options = QgsVectorLayerSaveAsDialog::AllOptions;
+ if ( !symbologyOption )
+ {
+ options &= ~QgsVectorLayerSaveAsDialog::Symbology;
+ }
+
+ QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), options, this );
if ( dialog->exec() == QDialog::Accepted )
{
@@ -5538,6 +5552,137 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
mMapCanvas->refresh();
}
+void QgisApp::pasteAsNewVector()
+{
+ if ( mMapCanvas && mMapCanvas->isDrawing() ) return;
+
+ QgsVectorLayer * layer = pasteToNewMemoryVector();
+ if ( !layer ) return;
+
+ saveAsVectorFileGeneral( false, layer, false );
+
+ delete layer;
+}
+
+void QgisApp::pasteAsNewMemoryVector()
+{
+ if ( mMapCanvas && mMapCanvas->isDrawing() ) return;
+
+ QgsVectorLayer * layer = pasteToNewMemoryVector();
+ if ( !layer ) return;
+
+ mMapCanvas->freeze();
+
+ QgsMapLayerRegistry::instance()->addMapLayer( layer );
+
+ mMapCanvas->freeze( false );
+ mMapCanvas->refresh();
+
+ qApp->processEvents();
+}
+
+QgsVectorLayer * QgisApp::pasteToNewMemoryVector()
+{
+ // Decide geometry type from features, switch to multi type if at least one multi is found
+ QMap<QGis::WkbType, int> typeCounts;
+ QgsFeatureList features = clipboard()->copyOf();
+ for ( int i = 0; i < features.size(); i++ )
+ {
+ QgsFeature &feature = features[i];
+ if ( !feature.geometry() ) continue;
+ QGis::WkbType type = QGis::flatType( feature.geometry()->wkbType() );
+
+ if ( type == QGis::WKBUnknown || type == QGis::WKBNoGeometry ) continue;
+ if ( QGis::isSingleType( type ) )
+ {
+ if ( typeCounts.contains( QGis::multiType( type ) ) )
+ {
+ typeCounts[ QGis::multiType( type )] = typeCounts[ QGis::multiType( type )] + 1;
+ }
+ else
+ {
+ typeCounts[ type ] = typeCounts[ type ] + 1;
+ }
+ }
+ else if ( QGis::isMultiType( type ) )
+ {
+ if ( typeCounts.contains( QGis::singleType( type ) ) )
+ {
+ // switch to multi type
+ typeCounts[type] = typeCounts[ QGis::singleType( type )];
+ typeCounts.remove( QGis::singleType( type ) );
+ }
+ typeCounts[type] = typeCounts[type] + 1;
+ }
+ }
+
+ QGis::WkbType wkbType = typeCounts.size() > 0 ? typeCounts.keys().value( 0 ) : QGis::WKBPoint;
+
+ QString typeName = QString( QGis::featureType( wkbType ) ).replace( "WKB", "" );
+
+ QString message;
+
+ if ( features.size() == 0 )
+ {
+ message = tr( "No features in clipboard." ); // should not happen
+ }
+ else if ( typeCounts.size() == 0 )
+ {
+ message = tr( "No features with geometry found, point type layer will be created." );
+ }
+ else if ( typeCounts.size() > 1 )
+ {
+ message = tr( "Multiple geometry types found, features with geometry different from %1 will be created without geometry." ).arg( typeName );
+ }
+
+ if ( !message.isEmpty() )
+ {
+ QMessageBox::warning( this, tr( "Warning" ), message , QMessageBox::Ok );
+ }
+
+ QgsVectorLayer * layer = new QgsVectorLayer( typeName, "pasted_features", "memory" );
+
+ if ( !layer->isValid() || !layer->dataProvider() )
+ {
+ delete layer;
+ QMessageBox::warning( this, tr( "Warning" ), tr( "Cannot create new layer" ), QMessageBox::Ok );
+ return 0;
+ }
+
+ layer->startEditing();
+ layer->setCrs( clipboard()->crs(), false );
+
+ foreach ( QgsField f, clipboard()->fields().toList() )
+ {
+ layer->addAttribute( f );
+ }
+
+ // Convert to multi if necessary
+ for ( int i = 0; i < features.size(); i++ )
+ {
+ QgsFeature &feature = features[i];
+ if ( !feature.geometry() ) continue;
+ QGis::WkbType type = QGis::flatType( feature.geometry()->wkbType() );
+ if ( type == QGis::WKBUnknown || type == QGis::WKBNoGeometry ) continue;
+
+ QgsDebugMsg( QString( "type = %1" ).arg( type ) );
+
+ if ( QGis::singleType( wkbType ) != QGis::singleType( type ) )
+ {
+ feature.setGeometry( 0 );
+ }
+
+ if ( QGis::isMultiType( wkbType ) && QGis::isSingleType( type ) )
+ {
+ feature.geometry()->convertToMultiType();
+ }
+ }
+ layer->addFeatures( features );
+ layer->commitChanges();
+
+ return layer;
+}
+
void QgisApp::copyStyle( QgsMapLayer * sourceLayer )
{
QgsMapLayer *selectionLayer = sourceLayer ? sourceLayer : activeLayer();
@@ -8003,6 +8148,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionMoveLabel->setEnabled( enableMove );
mActionRotateLabel->setEnabled( enableRotate );
mActionChangeLabelProperties->setEnabled( enableChange );
+ mMenuPasteAs->setEnabled( clipboard() && !clipboard()->empty() );
updateLayerModifiedActions();
@@ -8036,6 +8182,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionCutFeatures->setEnabled( false );
mActionCopyFeatures->setEnabled( false );
mActionPasteFeatures->setEnabled( false );
+ mMenuPasteAs->setEnabled( false );
mActionCopyStyle->setEnabled( false );
mActionPasteStyle->setEnabled( false );
View
@@ -249,6 +249,8 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
QAction *actionCutFeatures() { return mActionCutFeatures; }
QAction *actionCopyFeatures() { return mActionCopyFeatures; }
QAction *actionPasteFeatures() { return mActionPasteFeatures; }
+ QAction *actionPasteAsNewVector() { return mActionPasteAsNewVector; }
+ QAction *actionPasteAsNewMemoryVector() { return mActionPasteAsNewMemoryVector; }
QAction *actionDeleteSelected() { return mActionDeleteSelected; }
QAction *actionAddFeature() { return mActionAddFeature; }
QAction *actionMoveFeature() { return mActionMoveFeature; }
@@ -533,6 +535,10 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
(defaults to the active layer on the legend)
*/
void editPaste( QgsMapLayer * destinationLayer = 0 );
+ //! copies features on the clipboard to a new vector layer
+ void pasteAsNewVector();
+ //! copies features on the clipboard to a new memory vector layer
+ void pasteAsNewMemoryVector();
//! copies style of the active layer to the clipboard
/**
\param sourceLayer The layer where the style will be taken from
@@ -1212,7 +1218,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
/**Deletes all the composer objects and clears mPrintComposers*/
void deletePrintComposers();
- void saveAsVectorFileGeneral( bool saveOnlySelection );
+ void saveAsVectorFileGeneral( bool saveOnlySelection, QgsVectorLayer* vlayer = 0, bool symbologyOption = true );
+
+ /** Paste features from clipboard into a new memory layer.
+ * If no features are in clipboard an empty layer is returned.
+ * @return pointer to a new layer or 0 if failed
+ */
+ QgsVectorLayer * pasteToNewMemoryVector();
/**Returns all annotation items in the canvas*/
QList<QgsAnnotationItem*> annotationItems();
View
@@ -82,6 +82,20 @@ class CORE_EXPORT QGis
}
}
+ static WkbType multiType( WkbType type )
+ {
+ switch ( type )
+ {
+ case WKBPoint: return WKBMultiPoint;
+ case WKBLineString: return WKBMultiLineString;
+ case WKBPolygon: return WKBMultiPolygon;
+ case WKBPoint25D: return WKBMultiPoint25D;
+ case WKBLineString25D: return WKBMultiLineString25D;
+ case WKBPolygon25D: return WKBMultiPolygon25D;
+ default: return type;
+ }
+ }
+
static WkbType flatType( WkbType type )
{
switch ( type )
@@ -96,6 +110,32 @@ class CORE_EXPORT QGis
}
}
+ static bool isSingleType( WkbType type )
+ {
+ switch ( flatType( type ) )
+ {
+ case WKBPoint:
+ case WKBLineString:
+ case WKBPolygon:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ static bool isMultiType( WkbType type )
+ {
+ switch ( flatType( type ) )
+ {
+ case WKBMultiPoint:
+ case WKBMultiLineString:
+ case WKBMultiPolygon:
+ return true;
+ default:
+ return false;
+ }
+ }
+
static int wkbDimensions( WkbType type )
{
switch ( type )
Oops, something went wrong.

0 comments on commit 8d3199f

Please sign in to comment.