Skip to content

Commit

Permalink
[FEATURE] Paste features as new vector layer
Browse files Browse the repository at this point in the history
  • Loading branch information
blazek committed Sep 5, 2013
1 parent 8303ce0 commit 8d3199f
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 36 deletions.
22 changes: 20 additions & 2 deletions src/app/ogr/qgsvectorlayersaveasdialog.cpp
Expand Up @@ -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();
Expand All @@ -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 );
Expand Down
10 changes: 10 additions & 0 deletions src/app/ogr/qgsvectorlayersaveasdialog.h
Expand Up @@ -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;
Expand Down Expand Up @@ -58,6 +66,8 @@ class QgsVectorLayerSaveAsDialog : public QDialog, private Ui::QgsVectorLayerSav
void accept();

private:
void setup();

long mCRS;
};

Expand Down
155 changes: 151 additions & 4 deletions src/app/qgisapp.cpp
Expand Up @@ -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
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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() ) );
Expand Down Expand Up @@ -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 )
{
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -8003,6 +8148,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer* layer )
mActionMoveLabel->setEnabled( enableMove );
mActionRotateLabel->setEnabled( enableRotate );
mActionChangeLabelProperties->setEnabled( enableChange );
mMenuPasteAs->setEnabled( clipboard() && !clipboard()->empty() );

updateLayerModifiedActions();

Expand Down Expand Up @@ -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 );

Expand Down
14 changes: 13 additions & 1 deletion src/app/qgisapp.h
Expand Up @@ -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; }
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
Expand Down
40 changes: 40 additions & 0 deletions src/core/qgis.h
Expand Up @@ -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 )
Expand All @@ -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 )
Expand Down

0 comments on commit 8d3199f

Please sign in to comment.