Skip to content

Commit

Permalink
[FEATURE] Allow copying features from QGIS in GeoJSON format
Browse files Browse the repository at this point in the history
The previous setting for include WKT when copying features has been
replaced with a choice of copying features as "Plain text, attributes
only", "Plain text, WKT geometry" and a new "GeoJSON" option.

When set to "GeoJSON", copying features in QGIS will place a GeoJSON
text representation of the features on the clipboard for easy
pasting into other applications/javascript code.

Sponsored by North Road
  • Loading branch information
nyalldawson committed May 9, 2016
1 parent 718c5fd commit c3d6c79
Show file tree
Hide file tree
Showing 5 changed files with 457 additions and 104 deletions.
96 changes: 61 additions & 35 deletions src/app/qgsclipboard.cpp
Expand Up @@ -34,6 +34,7 @@
#include "qgslogger.h"
#include "qgsvectorlayer.h"
#include "qgsogrutils.h"
#include "qgsjsonutils.h"

QgsClipboard::QgsClipboard()
: QObject()
Expand Down Expand Up @@ -76,56 +77,81 @@ void QgsClipboard::replaceWithCopyOf( QgsFeatureStore & featureStore )
emit changed();
}

void QgsClipboard::setSystemClipboard()
QString QgsClipboard::generateClipboardText() const
{
// Replace the system clipboard.
QSettings settings;
bool copyWKT = settings.value( "qgis/copyGeometryAsWKT", true ).toBool();

QStringList textLines;
QStringList textFields;

// first do the field names
if ( copyWKT )
CopyFormat format = AttributesWithWKT;
if ( settings.contains( "/qgis/copyFeatureFormat" ) )
format = static_cast< CopyFormat >( settings.value( "/qgis/copyFeatureFormat", true ).toInt() );
else
{
textFields += "wkt_geom";
//old format setting
format = settings.value( "/qgis/copyGeometryAsWKT", true ).toBool() ? AttributesWithWKT : AttributesOnly;
}

Q_FOREACH ( const QgsField& field, mFeatureFields )
switch ( format )
{
textFields += field.name();
}
textLines += textFields.join( "\t" );
textFields.clear();
case AttributesOnly:
case AttributesWithWKT:
{
QStringList textLines;
QStringList textFields;

// then the field contents
for ( QgsFeatureList::const_iterator it = mFeatureClipboard.constBegin(); it != mFeatureClipboard.constEnd(); ++it )
{
QgsAttributes attributes = it->attributes();
// first do the field names
if ( format == AttributesWithWKT )
{
textFields += "wkt_geom";
}

// TODO: Set up Paste Transformations to specify the order in which fields are added.
if ( copyWKT )
{
if ( it->constGeometry() )
textFields += it->constGeometry()->exportToWkt();
else
Q_FOREACH ( const QgsField& field, mFeatureFields )
{
textFields += settings.value( "qgis/nullValue", "NULL" ).toString();
textFields += field.name();
}
}
textLines += textFields.join( "\t" );
textFields.clear();

// QgsDebugMsg("about to traverse fields.");
for ( int idx = 0; idx < attributes.count(); ++idx )
// then the field contents
for ( QgsFeatureList::const_iterator it = mFeatureClipboard.constBegin(); it != mFeatureClipboard.constEnd(); ++it )
{
QgsAttributes attributes = it->attributes();

// TODO: Set up Paste Transformations to specify the order in which fields are added.
if ( format == AttributesWithWKT )
{
if ( it->constGeometry() )
textFields += it->constGeometry()->exportToWkt();
else
{
textFields += settings.value( "qgis/nullValue", "NULL" ).toString();
}
}

// QgsDebugMsg("about to traverse fields.");
for ( int idx = 0; idx < attributes.count(); ++idx )
{
// QgsDebugMsg(QString("inspecting field '%1'.").arg(it2->toString()));
textFields += attributes.at( idx ).toString();
}

textLines += textFields.join( "\t" );
textFields.clear();
}

return textLines.join( "\n" );
}
case GeoJSON:
{
// QgsDebugMsg(QString("inspecting field '%1'.").arg(it2->toString()));
textFields += attributes.at( idx ).toString();
QgsJSONExporter exporter;
exporter.setSourceCrs( mCRS );
return exporter.exportFeatures( mFeatureClipboard );
}

textLines += textFields.join( "\t" );
textFields.clear();
}
return QString();
}

QString textCopy = textLines.join( "\n" );
void QgsClipboard::setSystemClipboard()
{
QString textCopy = generateClipboardText();

QClipboard *cb = QApplication::clipboard();

Expand Down
17 changes: 17 additions & 0 deletions src/app/qgsclipboard.h
Expand Up @@ -53,6 +53,15 @@ class APP_EXPORT QgsClipboard : public QObject
{
Q_OBJECT
public:

//! Available formats for copying features as text
enum CopyFormat
{
AttributesOnly, /*!< Tab delimited text, attributes only */
AttributesWithWKT, /*!< Tab delimited text, with geometry in WKT format */
GeoJSON, /*!< GeoJSON FeatureCollection format */
};

/**
* Constructor for the clipboard.
*/
Expand Down Expand Up @@ -146,6 +155,11 @@ class APP_EXPORT QgsClipboard : public QObject
*/
void setSystemClipboard();

/** Creates a text representation of the clipboard features.
* @returns clipboard text, respecting user export format
*/
QString generateClipboardText() const;

/** Attempts to convert a string to a list of features, by parsing the string as WKT and GeoJSON
* @param string string to convert
* @param fields fields for resultant features
Expand All @@ -169,6 +183,9 @@ class APP_EXPORT QgsClipboard : public QObject

/** True when the data from the system clipboard should be read */
bool mUseSystemClipboard;

friend class TestQgisAppClipboard;

};

#endif
14 changes: 12 additions & 2 deletions src/app/qgsoptions.cpp
Expand Up @@ -42,6 +42,7 @@
#include "qgscolordialog.h"
#include "qgsexpressioncontext.h"
#include "qgsunittypes.h"
#include "qgsclipboard.h"

#include <QInputDialog>
#include <QFileDialog>
Expand Down Expand Up @@ -604,7 +605,15 @@ QgsOptions::QgsOptions( QWidget *parent, Qt::WindowFlags fl )
cbxEvaluateDefaultValues->setChecked( mSettings->value( "/qgis/evaluateDefaultValues", false ).toBool() );
cbxCompileExpressions->setChecked( mSettings->value( "/qgis/compileExpressions", true ).toBool() );
cbxCreateRasterLegendIcons->setChecked( mSettings->value( "/qgis/createRasterLegendIcons", false ).toBool() );
cbxCopyWKTGeomFromTable->setChecked( mSettings->value( "/qgis/copyGeometryAsWKT", true ).toBool() );

mComboCopyFeatureFormat->addItem( tr( "Plain text, no geometry" ), QgsClipboard::AttributesOnly );
mComboCopyFeatureFormat->addItem( tr( "Plain text, WKT geometry" ), QgsClipboard::AttributesWithWKT );
mComboCopyFeatureFormat->addItem( tr( "GeoJSON" ), QgsClipboard::GeoJSON );
if ( mSettings->contains( "/qgis/copyFeatureFormat" ) )
mComboCopyFeatureFormat->setCurrentIndex( mComboCopyFeatureFormat->findData( mSettings->value( "/qgis/copyFeatureFormat", true ).toInt() ) );
else
mComboCopyFeatureFormat->setCurrentIndex( mComboCopyFeatureFormat->findData( mSettings->value( "/qgis/copyGeometryAsWKT", true ).toBool() ?
QgsClipboard::AttributesWithWKT : QgsClipboard::AttributesOnly ) );
leNullValue->setText( mSettings->value( "qgis/nullValue", "NULL" ).toString() );
cbxIgnoreShapeEncoding->setChecked( mSettings->value( "/qgis/ignoreShapeEncoding", true ).toBool() );
cbxCanvasRotation->setChecked( QgsMapCanvas::rotationEnabled() );
Expand Down Expand Up @@ -1157,7 +1166,8 @@ void QgsOptions::saveOptions()
mSettings->setValue( "/qgis/defaultLegendGraphicResolution", mLegendGraphicResolutionSpinBox->value() );
bool createRasterLegendIcons = mSettings->value( "/qgis/createRasterLegendIcons", false ).toBool();
mSettings->setValue( "/qgis/createRasterLegendIcons", cbxCreateRasterLegendIcons->isChecked() );
mSettings->setValue( "/qgis/copyGeometryAsWKT", cbxCopyWKTGeomFromTable->isChecked() );
mSettings->setValue( "/qgis/copyFeatureFormat", mComboCopyFeatureFormat->itemData( mComboCopyFeatureFormat->currentIndex() ).toInt() );

mSettings->setValue( "/qgis/new_layers_visible", chkAddedVisibility->isChecked() );
mSettings->setValue( "/qgis/enable_anti_aliasing", chkAntiAliasing->isChecked() );
mSettings->setValue( "/qgis/enable_render_caching", chkUseRenderCaching->isChecked() );
Expand Down

0 comments on commit c3d6c79

Please sign in to comment.