Skip to content
Permalink
Browse files

[feature] Add flag to only copy selected features

This extends the offline editing possibilities to only work on subset of
large layers

Sponsored by DB Fahrwegdienste GmbH
  • Loading branch information
m-kuhn committed Sep 15, 2016
1 parent d393734 commit 59a3bc2ca73696df7d79c5e593cfbe258901bd50
@@ -28,10 +28,10 @@ class QgsOfflineEditing : QObject
* @param offlineDbFile offline db file name
* @param layerIds list of layer names to convert
*/
bool convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds );
bool convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds, bool onlySelected = false );

/** Return true if current project is offline */
bool isOfflineProject();
bool isOfflineProject() const;

/** Synchronize to remote layers */
void synchronize();
@@ -79,7 +79,7 @@ QgsOfflineEditing::~QgsOfflineEditing()
* - remove remote layers
* - mark as offline project
*/
bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds )
bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds, bool onlySelected )
{
if ( layerIds.isEmpty() )
{
@@ -104,9 +104,9 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
QMap<QString, QgsVectorJoinList > joinInfoBuffer;
QMap<QString, QgsVectorLayer*> layerIdMapping;

for ( int i = 0; i < layerIds.count(); i++ )
Q_FOREACH ( const QString& layerId, layerIds )
{
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerIds.at( i ) );
QgsMapLayer* layer = QgsMapLayerRegistry::instance()->mapLayer( layerId );
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( layer );
if ( !vl )
continue;
@@ -116,17 +116,17 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
// Join fields are prefixed with the layer name and we do not want the
// field name to change so we stabilize the field name by defining a
// custom prefix with the layername without _offline suffix.
QgsVectorJoinList::iterator it = joins.begin();
while ( it != joins.end() )
QgsVectorJoinList::iterator joinIt = joins.begin();
while ( joinIt != joins.end() )
{
if (( *it ).prefix.isNull() )
if ( joinIt->prefix.isNull() )
{
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer(( *it ).joinLayerId ) );
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer*>( QgsMapLayerRegistry::instance()->mapLayer( joinIt->joinLayerId ) );

if ( vl )
( *it ).prefix = vl->name() + '_';
joinIt->prefix = vl->name() + '_';
}
++it;
++joinIt;
}
joinInfoBuffer.insert( vl->id(), joins );
}
@@ -141,7 +141,7 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
if ( vl )
{
QString origLayerId = vl->id();
QgsVectorLayer* newLayer = copyVectorLayer( vl, db, dbPath );
QgsVectorLayer* newLayer = copyVectorLayer( vl, db, dbPath, onlySelected );

if ( newLayer )
{
@@ -197,7 +197,7 @@ bool QgsOfflineEditing::convertToOfflineProject( const QString& offlineDataPath,
return false;
}

bool QgsOfflineEditing::isOfflineProject()
bool QgsOfflineEditing::isOfflineProject() const
{
return !QgsProject::instance()->readEntry( PROJECT_ENTRY_SCOPE_OFFLINE, PROJECT_ENTRY_KEY_OFFLINE_DB_PATH ).isEmpty();
}
@@ -228,7 +228,7 @@ void QgsOfflineEditing::synchronize()
QgsDebugMsgLevel( QString( "Found %1 offline layers" ).arg( offlineLayers.count() ), 4 );
for ( int l = 0; l < offlineLayers.count(); l++ )
{
QgsMapLayer* layer = offlineLayers[l];
QgsMapLayer* layer = offlineLayers.at( l );

emit layerProgressUpdated( l + 1, offlineLayers.count() );

@@ -254,8 +254,7 @@ void QgsOfflineEditing::synchronize()
QgsVectorLayer* offlineLayer = qobject_cast<QgsVectorLayer*>( layer );

// register this layer with the central layers registry
QgsMapLayerRegistry::instance()->addMapLayers(
QList<QgsMapLayer *>() << remoteLayer, true );
QgsMapLayerRegistry::instance()->addMapLayers( QList<QgsMapLayer *>() << remoteLayer, true );

// copy style
copySymbology( offlineLayer, remoteLayer );
@@ -317,8 +316,7 @@ void QgsOfflineEditing::synchronize()
// again with the same path
offlineLayer->dataProvider()->invalidateConnections( QgsDataSourceUri( offlineLayer->source() ).database() );
// remove offline layer
QgsMapLayerRegistry::instance()->removeMapLayers(
( QStringList() << qgisLayerId ) );
QgsMapLayerRegistry::instance()->removeMapLayers( QStringList() << qgisLayerId );


// disable offline project
@@ -486,7 +484,7 @@ void QgsOfflineEditing::createLoggingTables( sqlite3* db )
*/
}

QgsVectorLayer* QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlite3* db, const QString& offlineDbPath )
QgsVectorLayer* QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlite3* db, const QString& offlineDbPath , bool onlySelected )
{
if ( !layer )
return nullptr;
@@ -626,12 +624,18 @@ QgsVectorLayer* QgsOfflineEditing::copyVectorLayer( QgsVectorLayer* layer, sqlit

QgsFeatureIterator fit = layer->dataProvider()->getFeatures();

This comment has been minimized.

Copy link
@nyalldawson

nyalldawson Sep 15, 2016

Collaborator

This approach is pretty inefficient for large layers - you are still iterating through everything. Wouldn't it be better to filter fids if the selection ids are used?

This comment has been minimized.

Copy link
@m-kuhn

m-kuhn Sep 15, 2016

Author Member

Thanks, fixed in ab6d796


QgsFeatureIds selectedFids = layer->selectedFeaturesIds();

emit progressModeSet( QgsOfflineEditing::CopyFeatures, layer->dataProvider()->featureCount() );
int featureCount = 1;

QList<QgsFeatureId> remoteFeatureIds;
while ( fit.nextFeature( f ) )
{
// Check if we only want selected feature, if the selection is not empty and the current feature is not selected: dismiss it
if ( onlySelected && !selectedFids.isEmpty() && !selectedFids.contains( f.id() ) )
continue;

remoteFeatureIds << f.id();

// NOTE: Spatialite provider ignores position of geometry column
@@ -51,14 +51,15 @@ class CORE_EXPORT QgsOfflineEditing : public QObject
~QgsOfflineEditing();

/** Convert current project for offline editing
* @param offlineDataPath path to offline db file
* @param offlineDbFile offline db file name
* @param layerIds list of layer names to convert
* @param offlineDataPath Path to offline db file
* @param offlineDbFile Offline db file name
* @param layerIds List of layer names to convert
* @param onlySelected Only copy selected features from layers where a selection is present
*/
bool convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds );
bool convertToOfflineProject( const QString& offlineDataPath, const QString& offlineDbFile, const QStringList& layerIds, bool onlySelected = false );

/** Return true if current project is offline */
bool isOfflineProject();
bool isOfflineProject() const;

/** Synchronize to remote layers */
void synchronize();
@@ -98,7 +99,7 @@ class CORE_EXPORT QgsOfflineEditing : public QObject
void initializeSpatialMetadata( sqlite3 *sqlite_handle );
bool createSpatialiteDB( const QString& offlineDbPath );
void createLoggingTables( sqlite3* db );
QgsVectorLayer* copyVectorLayer( QgsVectorLayer* layer, sqlite3* db, const QString& offlineDbPath );
QgsVectorLayer* copyVectorLayer( QgsVectorLayer* layer, sqlite3* db, const QString& offlineDbPath, bool onlySelected );

void applyAttributesAdded( QgsVectorLayer* remoteLayer, sqlite3* db, int layerId, int commitNo );
void applyFeaturesAdded( QgsVectorLayer* offlineLayer, QgsVectorLayer* remoteLayer, sqlite3* db, int layerId );
@@ -109,7 +109,7 @@ void QgsOfflineEditingPlugin::convertProject()
}

mProgressDialog->setTitle( tr( "Converting to offline project" ) );
if ( mOfflineEditing->convertToOfflineProject( myPluginGui->offlineDataPath(), myPluginGui->offlineDbFile(), selectedLayerIds ) )
if ( mOfflineEditing->convertToOfflineProject( myPluginGui->offlineDataPath(), myPluginGui->offlineDbFile(), selectedLayerIds, myPluginGui->onlySelected() ) )
{
updateActions();
// Redraw, to make the offline layer visible
@@ -102,11 +102,16 @@ QString QgsOfflineEditingPluginGui::offlineDbFile()
return mOfflineDbFile;
}

QStringList& QgsOfflineEditingPluginGui::selectedLayerIds()
QStringList QgsOfflineEditingPluginGui::selectedLayerIds()
{
return mSelectedLayerIds;
}

bool QgsOfflineEditingPluginGui::onlySelected() const
{
return mOnlySelectedCheckBox->checkState() == Qt::Checked;
}

void QgsOfflineEditingPluginGui::on_mBrowseButton_clicked()
{
QString fileName = QFileDialog::getSaveFileName( this,
@@ -46,7 +46,8 @@ class QgsOfflineEditingPluginGui : public QDialog, private Ui::QgsOfflineEditing

QString offlineDataPath();
QString offlineDbFile();
QStringList& selectedLayerIds();
QStringList selectedLayerIds();
bool onlySelected() const;

public slots:
/** Change the selection of layers in the list */
@@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>435</width>
<height>270</height>
<width>590</width>
<height>570</height>
</rect>
</property>
<property name="windowTitle">
@@ -92,6 +92,13 @@
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="mOnlySelectedCheckBox">
<property name="text">
<string>Only synchronize selected features if a selection is present</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>

0 comments on commit 59a3bc2

Please sign in to comment.
You can’t perform that action at this time.