Skip to content

Commit 8d3199f

Browse files
committed
[FEATURE] Paste features as new vector layer
1 parent 8303ce0 commit 8d3199f

File tree

6 files changed

+287
-36
lines changed

6 files changed

+287
-36
lines changed

src/app/ogr/qgsvectorlayersaveasdialog.cpp

+20-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,26 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* par
2929
: QDialog( parent, fl )
3030
, mCRS( srsid )
3131
{
32-
setupUi( this );
32+
setup();
33+
}
3334

35+
QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, int options, QWidget* parent, Qt::WFlags fl )
36+
: QDialog( parent, fl )
37+
, mCRS( srsid )
38+
{
39+
setup();
40+
if ( !( options & Symbology ) )
41+
{
42+
mSymbologyExportLabel->hide();
43+
mSymbologyExportComboBox->hide();
44+
mScaleLabel->hide();
45+
mScaleSpinBox->hide();
46+
}
47+
}
48+
49+
void QgsVectorLayerSaveAsDialog::setup()
50+
{
51+
setupUi( this );
3452
QSettings settings;
3553
restoreGeometry( settings.value( "/Windows/VectorLayerSaveAs/geometry" ).toByteArray() );
3654
QMap<QString, QString> map = QgsVectorFileWriter::ogrDriverList();
@@ -57,7 +75,7 @@ QgsVectorLayerSaveAsDialog::QgsVectorLayerSaveAsDialog( long srsid, QWidget* par
5775
mCRSSelection->clear();
5876
mCRSSelection->addItems( QStringList() << tr( "Layer CRS" ) << tr( "Project CRS" ) << tr( "Selected CRS" ) );
5977

60-
QgsCoordinateReferenceSystem srs( srsid, QgsCoordinateReferenceSystem::InternalCrsId );
78+
QgsCoordinateReferenceSystem srs( mCRS, QgsCoordinateReferenceSystem::InternalCrsId );
6179
leCRS->setText( srs.description() );
6280

6381
mEncodingComboBox->setCurrentIndex( idx );

src/app/ogr/qgsvectorlayersaveasdialog.h

+10
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,15 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
3030
Q_OBJECT
3131

3232
public:
33+
// bitmask of options to be shown
34+
enum Options
35+
{
36+
Symbology = 1,
37+
AllOptions = ~0
38+
};
39+
3340
QgsVectorLayerSaveAsDialog( long srsid, QWidget* parent = 0, Qt::WFlags fl = 0 );
41+
QgsVectorLayerSaveAsDialog( long srsid, int options = AllOptions, QWidget* parent = 0, Qt::WFlags fl = 0 );
3442
~QgsVectorLayerSaveAsDialog();
3543

3644
QString format() const;
@@ -58,6 +66,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
5866
void accept();
5967

6068
private:
69+
void setup();
70+
6171
long mCRS;
6272
};
6373

src/app/qgisapp.cpp

+151-4
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,7 @@ QgisApp *QgisApp::smInstance = 0;
431431
QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent, Qt::WFlags fl )
432432
: QMainWindow( parent, fl )
433433
, mSplash( splash )
434+
, mInternalClipboard( 0 )
434435
, mShowProjectionTab( false )
435436
, mPythonUtils( NULL )
436437
#ifdef Q_OS_WIN
@@ -530,7 +531,6 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
530531
updateProjectFromTemplates();
531532
legendLayerSelectionChanged();
532533
mSaveRollbackInProgress = false;
533-
activateDeactivateLayerRelatedActions( NULL );
534534

535535
// initialize the plugin manager
536536
mPluginManager = new QgsPluginManager( this, restorePlugins );
@@ -584,6 +584,8 @@ QgisApp::QgisApp( QSplashScreen *splash, bool restorePlugins, QWidget * parent,
584584
addWindow( mWindowAction );
585585
#endif
586586

587+
activateDeactivateLayerRelatedActions( NULL ); // after members were created
588+
587589
// set application's caption
588590
QString caption = tr( "QGIS - %1 ('%2')" ).arg( QGis::QGIS_VERSION ).arg( QGis::QGIS_RELEASE_NAME );
589591
setWindowTitle( caption );
@@ -928,6 +930,8 @@ void QgisApp::createActions()
928930
connect( mActionCutFeatures, SIGNAL( triggered() ), this, SLOT( editCut() ) );
929931
connect( mActionCopyFeatures, SIGNAL( triggered() ), this, SLOT( editCopy() ) );
930932
connect( mActionPasteFeatures, SIGNAL( triggered() ), this, SLOT( editPaste() ) );
933+
connect( mActionPasteAsNewVector, SIGNAL( triggered() ), this, SLOT( pasteAsNewVector() ) );
934+
connect( mActionPasteAsNewMemoryVector, SIGNAL( triggered() ), this, SLOT( pasteAsNewMemoryVector() ) );
931935
connect( mActionCopyStyle, SIGNAL( triggered() ), this, SLOT( copyStyle() ) );
932936
connect( mActionPasteStyle, SIGNAL( triggered() ), this, SLOT( pasteStyle() ) );
933937
connect( mActionAddFeature, SIGNAL( triggered() ), this, SLOT( addFeature() ) );
@@ -4531,21 +4535,31 @@ void QgisApp::saveSelectionAsVectorFile()
45314535
saveAsVectorFileGeneral( true );
45324536
}
45334537

4534-
void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection )
4538+
void QgisApp::saveAsVectorFileGeneral( bool saveOnlySelection, QgsVectorLayer* vlayer, bool symbologyOption )
45354539
{
45364540
if ( mMapCanvas && mMapCanvas->isDrawing() )
45374541
return;
45384542

45394543
if ( !mMapLegend )
45404544
return;
45414545

4542-
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() ); // FIXME: output of multiple layers at once?
4546+
if ( !vlayer )
4547+
{
4548+
vlayer = qobject_cast<QgsVectorLayer *>( activeLayer() ); // FIXME: output of multiple layers at once?
4549+
}
4550+
45434551
if ( !vlayer )
45444552
return;
45454553

45464554
QgsCoordinateReferenceSystem destCRS;
45474555

4548-
QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), this );
4556+
int options = QgsVectorLayerSaveAsDialog::AllOptions;
4557+
if ( !symbologyOption )
4558+
{
4559+
options &= ~QgsVectorLayerSaveAsDialog::Symbology;
4560+
}
4561+
4562+
QgsVectorLayerSaveAsDialog *dialog = new QgsVectorLayerSaveAsDialog( vlayer->crs().srsid(), options, this );
45494563

45504564
if ( dialog->exec() == QDialog::Accepted )
45514565
{
@@ -5538,6 +5552,137 @@ void QgisApp::editPaste( QgsMapLayer *destinationLayer )
55385552
mMapCanvas->refresh();
55395553
}
55405554

5555+
void QgisApp::pasteAsNewVector()
5556+
{
5557+
if ( mMapCanvas && mMapCanvas->isDrawing() ) return;
5558+
5559+
QgsVectorLayer * layer = pasteToNewMemoryVector();
5560+
if ( !layer ) return;
5561+
5562+
saveAsVectorFileGeneral( false, layer, false );
5563+
5564+
delete layer;
5565+
}
5566+
5567+
void QgisApp::pasteAsNewMemoryVector()
5568+
{
5569+
if ( mMapCanvas && mMapCanvas->isDrawing() ) return;
5570+
5571+
QgsVectorLayer * layer = pasteToNewMemoryVector();
5572+
if ( !layer ) return;
5573+
5574+
mMapCanvas->freeze();
5575+
5576+
QgsMapLayerRegistry::instance()->addMapLayer( layer );
5577+
5578+
mMapCanvas->freeze( false );
5579+
mMapCanvas->refresh();
5580+
5581+
qApp->processEvents();
5582+
}
5583+
5584+
QgsVectorLayer * QgisApp::pasteToNewMemoryVector()
5585+
{
5586+
// Decide geometry type from features, switch to multi type if at least one multi is found
5587+
QMap<QGis::WkbType, int> typeCounts;
5588+
QgsFeatureList features = clipboard()->copyOf();
5589+
for ( int i = 0; i < features.size(); i++ )
5590+
{
5591+
QgsFeature &feature = features[i];
5592+
if ( !feature.geometry() ) continue;
5593+
QGis::WkbType type = QGis::flatType( feature.geometry()->wkbType() );
5594+
5595+
if ( type == QGis::WKBUnknown || type == QGis::WKBNoGeometry ) continue;
5596+
if ( QGis::isSingleType( type ) )
5597+
{
5598+
if ( typeCounts.contains( QGis::multiType( type ) ) )
5599+
{
5600+
typeCounts[ QGis::multiType( type )] = typeCounts[ QGis::multiType( type )] + 1;
5601+
}
5602+
else
5603+
{
5604+
typeCounts[ type ] = typeCounts[ type ] + 1;
5605+
}
5606+
}
5607+
else if ( QGis::isMultiType( type ) )
5608+
{
5609+
if ( typeCounts.contains( QGis::singleType( type ) ) )
5610+
{
5611+
// switch to multi type
5612+
typeCounts[type] = typeCounts[ QGis::singleType( type )];
5613+
typeCounts.remove( QGis::singleType( type ) );
5614+
}
5615+
typeCounts[type] = typeCounts[type] + 1;
5616+
}
5617+
}
5618+
5619+
QGis::WkbType wkbType = typeCounts.size() > 0 ? typeCounts.keys().value( 0 ) : QGis::WKBPoint;
5620+
5621+
QString typeName = QString( QGis::featureType( wkbType ) ).replace( "WKB", "" );
5622+
5623+
QString message;
5624+
5625+
if ( features.size() == 0 )
5626+
{
5627+
message = tr( "No features in clipboard." ); // should not happen
5628+
}
5629+
else if ( typeCounts.size() == 0 )
5630+
{
5631+
message = tr( "No features with geometry found, point type layer will be created." );
5632+
}
5633+
else if ( typeCounts.size() > 1 )
5634+
{
5635+
message = tr( "Multiple geometry types found, features with geometry different from %1 will be created without geometry." ).arg( typeName );
5636+
}
5637+
5638+
if ( !message.isEmpty() )
5639+
{
5640+
QMessageBox::warning( this, tr( "Warning" ), message , QMessageBox::Ok );
5641+
}
5642+
5643+
QgsVectorLayer * layer = new QgsVectorLayer( typeName, "pasted_features", "memory" );
5644+
5645+
if ( !layer->isValid() || !layer->dataProvider() )
5646+
{
5647+
delete layer;
5648+
QMessageBox::warning( this, tr( "Warning" ), tr( "Cannot create new layer" ), QMessageBox::Ok );
5649+
return 0;
5650+
}
5651+
5652+
layer->startEditing();
5653+
layer->setCrs( clipboard()->crs(), false );
5654+
5655+
foreach ( QgsField f, clipboard()->fields().toList() )
5656+
{
5657+
layer->addAttribute( f );
5658+
}
5659+
5660+
// Convert to multi if necessary
5661+
for ( int i = 0; i < features.size(); i++ )
5662+
{
5663+
QgsFeature &feature = features[i];
5664+
if ( !feature.geometry() ) continue;
5665+
QGis::WkbType type = QGis::flatType( feature.geometry()->wkbType() );
5666+
if ( type == QGis::WKBUnknown || type == QGis::WKBNoGeometry ) continue;
5667+
5668+
QgsDebugMsg( QString( "type = %1" ).arg( type ) );
5669+
5670+
if ( QGis::singleType( wkbType ) != QGis::singleType( type ) )
5671+
{
5672+
feature.setGeometry( 0 );
5673+
}
5674+
5675+
if ( QGis::isMultiType( wkbType ) && QGis::isSingleType( type ) )
5676+
{
5677+
feature.geometry()->convertToMultiType();
5678+
}
5679+
}
5680+
layer->addFeatures( features );
5681+
layer->commitChanges();
5682+
5683+
return layer;
5684+
}
5685+
55415686
void QgisApp::copyStyle( QgsMapLayer * sourceLayer )
55425687
{
55435688
QgsMapLayer *selectionLayer = sourceLayer ? sourceLayer : activeLayer();
@@ -8003,6 +8148,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
80038148
mActionMoveLabel->setEnabled( enableMove );
80048149
mActionRotateLabel->setEnabled( enableRotate );
80058150
mActionChangeLabelProperties->setEnabled( enableChange );
8151+
mMenuPasteAs->setEnabled( clipboard() && !clipboard()->empty() );
80068152

80078153
updateLayerModifiedActions();
80088154

@@ -8036,6 +8182,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
80368182
mActionCutFeatures->setEnabled( false );
80378183
mActionCopyFeatures->setEnabled( false );
80388184
mActionPasteFeatures->setEnabled( false );
8185+
mMenuPasteAs->setEnabled( false );
80398186
mActionCopyStyle->setEnabled( false );
80408187
mActionPasteStyle->setEnabled( false );
80418188

src/app/qgisapp.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
249249
QAction *actionCutFeatures() { return mActionCutFeatures; }
250250
QAction *actionCopyFeatures() { return mActionCopyFeatures; }
251251
QAction *actionPasteFeatures() { return mActionPasteFeatures; }
252+
QAction *actionPasteAsNewVector() { return mActionPasteAsNewVector; }
253+
QAction *actionPasteAsNewMemoryVector() { return mActionPasteAsNewMemoryVector; }
252254
QAction *actionDeleteSelected() { return mActionDeleteSelected; }
253255
QAction *actionAddFeature() { return mActionAddFeature; }
254256
QAction *actionMoveFeature() { return mActionMoveFeature; }
@@ -533,6 +535,10 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
533535
(defaults to the active layer on the legend)
534536
*/
535537
void editPaste( QgsMapLayer * destinationLayer = 0 );
538+
//! copies features on the clipboard to a new vector layer
539+
void pasteAsNewVector();
540+
//! copies features on the clipboard to a new memory vector layer
541+
void pasteAsNewMemoryVector();
536542
//! copies style of the active layer to the clipboard
537543
/**
538544
\param sourceLayer The layer where the style will be taken from
@@ -1212,7 +1218,13 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
12121218
/**Deletes all the composer objects and clears mPrintComposers*/
12131219
void deletePrintComposers();
12141220

1215-
void saveAsVectorFileGeneral( bool saveOnlySelection );
1221+
void saveAsVectorFileGeneral( bool saveOnlySelection, QgsVectorLayer* vlayer = 0, bool symbologyOption = true );
1222+
1223+
/** Paste features from clipboard into a new memory layer.
1224+
* If no features are in clipboard an empty layer is returned.
1225+
* @return pointer to a new layer or 0 if failed
1226+
*/
1227+
QgsVectorLayer * pasteToNewMemoryVector();
12161228

12171229
/**Returns all annotation items in the canvas*/
12181230
QList<QgsAnnotationItem*> annotationItems();

src/core/qgis.h

+40
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,20 @@ class CORE_EXPORT QGis
8282
}
8383
}
8484

85+
static WkbType multiType( WkbType type )
86+
{
87+
switch ( type )
88+
{
89+
case WKBPoint: return WKBMultiPoint;
90+
case WKBLineString: return WKBMultiLineString;
91+
case WKBPolygon: return WKBMultiPolygon;
92+
case WKBPoint25D: return WKBMultiPoint25D;
93+
case WKBLineString25D: return WKBMultiLineString25D;
94+
case WKBPolygon25D: return WKBMultiPolygon25D;
95+
default: return type;
96+
}
97+
}
98+
8599
static WkbType flatType( WkbType type )
86100
{
87101
switch ( type )
@@ -96,6 +110,32 @@ class CORE_EXPORT QGis
96110
}
97111
}
98112

113+
static bool isSingleType( WkbType type )
114+
{
115+
switch ( flatType( type ) )
116+
{
117+
case WKBPoint:
118+
case WKBLineString:
119+
case WKBPolygon:
120+
return true;
121+
default:
122+
return false;
123+
}
124+
}
125+
126+
static bool isMultiType( WkbType type )
127+
{
128+
switch ( flatType( type ) )
129+
{
130+
case WKBMultiPoint:
131+
case WKBMultiLineString:
132+
case WKBMultiPolygon:
133+
return true;
134+
default:
135+
return false;
136+
}
137+
}
138+
99139
static int wkbDimensions( WkbType type )
100140
{
101141
switch ( type )

0 commit comments

Comments
 (0)