diff --git a/platform/android/res/values/strings.xml b/platform/android/res/values/strings.xml
index f76751448c..c765194922 100644
--- a/platform/android/res/values/strings.xml
+++ b/platform/android/res/values/strings.xml
@@ -65,4 +65,7 @@
Your device does not support this import operation.
Your device does not support this export operation.
Processing…
+ Positioning
+ Positioning service running
+ Copy to clipboard
diff --git a/platform/android/src/ch/opengis/qfield/QFieldCloudService.java b/platform/android/src/ch/opengis/qfield/QFieldCloudService.java
index 9d0d20b845..3aab00a6c1 100644
--- a/platform/android/src/ch/opengis/qfield/QFieldCloudService.java
+++ b/platform/android/src/ch/opengis/qfield/QFieldCloudService.java
@@ -93,7 +93,7 @@ private void showNotification() {
new Notification.Builder(this)
.setSmallIcon(R.drawable.qfield_logo)
.setWhen(System.currentTimeMillis())
- .setContentTitle("QField")
+ .setContentTitle("QFieldCloud")
.setContentText(getString(R.string.upload_pending_attachments))
.setProgress(0, 0, true);
diff --git a/platform/android/src/ch/opengis/qfield/QFieldPositioningService.java b/platform/android/src/ch/opengis/qfield/QFieldPositioningService.java
index fb26ddb94a..b19f1db038 100644
--- a/platform/android/src/ch/opengis/qfield/QFieldPositioningService.java
+++ b/platform/android/src/ch/opengis/qfield/QFieldPositioningService.java
@@ -34,6 +34,8 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
@@ -66,9 +68,10 @@ public static void stopQFieldPositioningService(Context context) {
context.stopService(intent);
}
- public static void triggerShowNotification(String message) {
+ public static void triggerShowNotification(String message,
+ boolean addCopyToClipboard) {
if (getInstance() != null) {
- getInstance().showNotification(message);
+ getInstance().showNotification(message, addCopyToClipboard);
} else {
Log.v("QFieldPositioningService",
"Showing message failed, no instance available.");
@@ -91,7 +94,7 @@ public void onCreate() {
if (getInstance() != null) {
Log.v("QFieldPositioningService",
- "service already running, aborting.");
+ "service already running, aborting onCreate.");
stopSelf();
return;
}
@@ -102,15 +105,24 @@ public void onDestroy() {
Log.v("QFieldPositioningService", "onDestroy triggered");
notificationManager.cancel(NOTIFICATION_ID);
super.onDestroy();
+ instance = null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("QFieldPositioningService", "onStartCommand triggered");
- int ret = super.onStartCommand(intent, flags, startId);
+ if (intent.hasExtra("content")) {
+ ClipboardManager clipboard =
+ (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
+ clipboard.setText(intent.getStringExtra("content"));
+ return START_NOT_STICKY;
+ }
+
+ int ret = super.onStartCommand(intent, flags, startId);
if (instance != null) {
- stopSelf();
+ Log.v("QFieldPositioningService",
+ "service already running, aborting onStartCommand.");
return START_NOT_STICKY;
}
@@ -122,7 +134,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
notificationChannel = new NotificationChannel(
CHANNEL_ID, "QField", NotificationManager.IMPORTANCE_DEFAULT);
- notificationChannel.setDescription("QField positioning");
+ notificationChannel.setDescription("QField Positioning");
notificationChannel.setImportance(
NotificationManager.IMPORTANCE_LOW);
notificationChannel.enableLights(false);
@@ -135,8 +147,8 @@ public int onStartCommand(Intent intent, int flags, int startId) {
.setSmallIcon(R.drawable.qfield_logo)
.setWhen(System.currentTimeMillis())
.setOngoing(true)
- .setContentTitle("QField")
- .setContentText("Positioning service running");
+ .setContentTitle(getString(R.string.positioning_title))
+ .setContentText(getString(R.string.positioning_running));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
builder.setChannelId(CHANNEL_ID);
@@ -154,17 +166,35 @@ public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
- public void showNotification(String contentText) {
- Notification.Builder builder = new Notification.Builder(this)
- .setSmallIcon(R.drawable.qfield_logo)
- .setWhen(System.currentTimeMillis())
- .setOngoing(true)
- .setContentTitle("QField")
- .setContentText(contentText);
+ public void showNotification(String contentText,
+ boolean addCopyToClipboard) {
+ // Return to QField activity when clicking on the notification
+ PendingIntent contentIntent = PendingIntent.getActivity(
+ this, 0, new Intent(this, QFieldActivity.class),
+ PendingIntent.FLAG_MUTABLE);
+
+ Notification.Builder builder =
+ new Notification.Builder(this)
+ .setSmallIcon(R.drawable.qfield_logo)
+ .setWhen(System.currentTimeMillis())
+ .setOngoing(true)
+ .setContentTitle(getString(R.string.positioning_title))
+ .setContentText(contentText)
+ .setContentIntent(contentIntent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
builder.setChannelId(CHANNEL_ID);
}
+ if (addCopyToClipboard) {
+ // Allow for position details to be copied to the clipboard
+ Intent copyIntent =
+ new Intent(this, QFieldPositioningService.class);
+ copyIntent.putExtra("content", contentText);
+ PendingIntent copyPendingIntent = PendingIntent.getService(
+ this, 0, copyIntent, PendingIntent.FLAG_MUTABLE);
+ builder.addAction(0, getString(R.string.copy_to_clipboard),
+ copyPendingIntent);
+ }
Notification notification = builder.build();
notificationManager.notify(NOTIFICATION_ID, notification);
diff --git a/src/core/featuremodel.cpp b/src/core/featuremodel.cpp
index 2b22e5d925..50dd1643f8 100644
--- a/src/core/featuremodel.cpp
+++ b/src/core/featuremodel.cpp
@@ -484,78 +484,88 @@ bool FeatureModel::save()
if ( !mLayer )
return false;
- bool rv = true;
+ bool isSuccess = true;
- if ( !startEditing() )
+ if ( mBatchMode )
{
- rv = false;
- }
+ // We take charge of default values that are set to be applied on feature update to take into account positioning and cloud context
+ updateDefaultValues();
- switch ( mModelMode )
+ QgsFeature temporaryFeature = mFeature;
+ isSuccess = !mLayer->updateFeature( temporaryFeature, true );
+ }
+ else
{
- case SingleFeatureModel:
+ if ( !startEditing() )
{
- // We take charge of default values that are set to be applied on feature update to take into account positioning and cloud context
- updateDefaultValues();
-
- QgsFeature feat = mFeature;
- if ( !mLayer->updateFeature( feat, true ) )
- QgsMessageLog::logMessage( tr( "Cannot update feature" ), QStringLiteral( "QField" ), Qgis::Warning );
+ isSuccess = false;
+ }
- if ( mProject && mProject->topologicalEditing() )
+ switch ( mModelMode )
+ {
+ case SingleFeatureModel:
{
- applyVertexModelTopography();
- }
+ // We take charge of default values that are set to be applied on feature update to take into account positioning and cloud context
+ updateDefaultValues();
- rv &= commit();
+ QgsFeature temporaryFeature = mFeature;
+ if ( !mLayer->updateFeature( temporaryFeature, true ) )
+ QgsMessageLog::logMessage( tr( "Cannot update feature" ), QStringLiteral( "QField" ), Qgis::Warning );
- if ( rv )
- {
- QgsFeature modifiedFeature;
- if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( modifiedFeature ) )
+ if ( mProject && mProject->topologicalEditing() )
{
- if ( modifiedFeature != mFeature )
+ applyVertexModelTopography();
+ }
+
+ isSuccess &= commit();
+ if ( isSuccess )
+ {
+ QgsFeature modifiedFeature;
+ if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( mFeature.id() ) ).nextFeature( modifiedFeature ) )
{
- setFeature( modifiedFeature );
+ if ( modifiedFeature != mFeature )
+ {
+ setFeature( modifiedFeature );
+ }
+ else
+ {
+ emit featureUpdated();
+ }
}
else
{
- emit featureUpdated();
+ QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Warning );
}
}
- else
- {
- QgsMessageLog::logMessage( tr( "Feature %1 could not be fetched after commit" ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Warning );
- }
+ break;
}
- break;
- }
- case MultiFeatureModel:
- {
- // We need to copy these members as the first feature updated triggers a refresh of the selected features, leading to changes in feature model members
- const QgsFeature referenceFeature = mFeature;
- const QList attributesAllowEdit = mAttributesAllowEdit;
- QList features = mFeatures;
- for ( QgsFeature &feature : features )
+ case MultiFeatureModel:
{
- for ( int i = 0; i < referenceFeature.attributes().count(); i++ )
+ // We need to copy these members as the first feature updated triggers a refresh of the selected features, leading to changes in feature model members
+ const QgsFeature referenceFeature = mFeature;
+ const QList attributesAllowEdit = mAttributesAllowEdit;
+ QList features = mFeatures;
+ for ( QgsFeature &feature : features )
{
- if ( !attributesAllowEdit[i] )
- continue;
+ for ( int i = 0; i < referenceFeature.attributes().count(); i++ )
+ {
+ if ( !attributesAllowEdit[i] )
+ continue;
- feature.setAttribute( i, referenceFeature.attributes().at( i ) );
- }
- if ( !mLayer->updateFeature( feature ) )
- {
- QgsMessageLog::logMessage( tr( "Cannot update feature" ), QStringLiteral( "QField" ), Qgis::Warning );
+ feature.setAttribute( i, referenceFeature.attributes().at( i ) );
+ }
+ if ( !mLayer->updateFeature( feature ) )
+ {
+ QgsMessageLog::logMessage( tr( "Cannot update feature" ), QStringLiteral( "QField" ), Qgis::Warning );
+ }
}
+ isSuccess &= commit();
}
- rv &= commit();
}
}
- return rv;
+ return isSuccess;
}
void FeatureModel::reset()
@@ -777,114 +787,148 @@ void FeatureModel::removeLayer( QObject *layer )
sRememberings->remove( static_cast( layer ) );
}
-bool FeatureModel::create()
+void FeatureModel::setBatchMode( bool batchMode )
{
- if ( !mLayer )
- return false;
+ if ( mBatchMode == batchMode )
+ return;
- if ( !startEditing() )
- {
- QgsMessageLog::logMessage( tr( "Cannot start editing on layer \"%1\" to create feature %2" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
- return false;
- }
+ mBatchMode = batchMode;
- bool hasRelations = false;
- QList> revisitRelations;
- if ( mProject )
+ if ( mLayer )
{
- // Gather any relationship children which would have relied on an auto-generated field value
- const QList relations = mProject->relationManager()->referencedRelations( mLayer );
- hasRelations = !relations.isEmpty();
- QgsFeature temporaryFeature = mFeature;
- for ( const QgsRelation &relation : relations )
+ if ( mBatchMode )
{
- const QgsAttributeList rereferencedFields = relation.referencedFields();
- bool needsRevisit = false;
- for ( const int fieldIndex : rereferencedFields )
- {
- if ( mLayer->dataProvider() && !mLayer->dataProvider()->defaultValueClause( fieldIndex ).isEmpty() )
- {
- temporaryFeature.setAttribute( fieldIndex, QVariant() );
- needsRevisit = true;
- }
- }
- if ( needsRevisit )
- {
- revisitRelations << qMakePair( relation, relation.getRelatedFeaturesRequest( temporaryFeature ) );
- }
+ mLayer->startEditing();
+ }
+ else
+ {
+ mLayer->commitChanges();
}
}
+ emit batchModeChanged();
+}
+bool FeatureModel::create()
+{
+ if ( !mLayer )
+ return false;
+
+ bool isSuccess = true;
+
// The connection below will be triggered when the new feature is committed and will provide
// the saved feature ID needed to fetch the saved feature back from the data provider
QgsFeatureId createdFeatureId;
QMetaObject::Connection connection = connect( mLayer, &QgsVectorLayer::featureAdded, this, [&createdFeatureId]( QgsFeatureId fid ) { createdFeatureId = fid; } );
- bool isSuccess = true;
- if ( mLayer->addFeature( mFeature ) )
+ if ( mBatchMode )
+ {
+ isSuccess = mLayer->addFeature( mFeature );
+ if ( isSuccess )
+ {
+ mFeature.setId( createdFeatureId );
+ }
+ }
+ else
{
- if ( mProject && mProject->topologicalEditing() )
- mLayer->addTopologicalPoints( mFeature.geometry() );
+ if ( !startEditing() )
+ {
+ QgsMessageLog::logMessage( tr( "Cannot start editing on layer \"%1\" to create feature %2" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
+ return false;
+ }
- if ( commit() )
+ bool hasRelations = false;
+ QList> revisitRelations;
+ if ( mProject )
{
- QgsFeature feat;
- if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( createdFeatureId ) ).nextFeature( feat ) )
+ // Gather any relationship children which would have relied on an auto-generated field value
+ const QList relations = mProject->relationManager()->referencedRelations( mLayer );
+ hasRelations = !relations.isEmpty();
+ QgsFeature temporaryFeature = mFeature;
+ for ( const QgsRelation &relation : relations )
{
- setFeature( feat );
+ const QgsAttributeList rereferencedFields = relation.referencedFields();
+ bool needsRevisit = false;
+ for ( const int fieldIndex : rereferencedFields )
+ {
+ if ( mLayer->dataProvider() && !mLayer->dataProvider()->defaultValueClause( fieldIndex ).isEmpty() )
+ {
+ temporaryFeature.setAttribute( fieldIndex, QVariant() );
+ needsRevisit = true;
+ }
+ }
+ if ( needsRevisit )
+ {
+ revisitRelations << qMakePair( relation, relation.getRelatedFeaturesRequest( temporaryFeature ) );
+ }
+ }
+ }
+
+ if ( mLayer->addFeature( mFeature ) )
+ {
+ if ( mProject && mProject->topologicalEditing() )
+ mLayer->addTopologicalPoints( mFeature.geometry() );
- if ( hasRelations )
+ if ( commit() )
+ {
+ QgsFeature feat;
+ if ( mLayer->getFeatures( QgsFeatureRequest().setFilterFid( createdFeatureId ) ).nextFeature( feat ) )
{
- // Revisit relations in need of attribute updates
- for ( const QPair &revisitRelation : std::as_const( revisitRelations ) )
+ setFeature( feat );
+
+ if ( hasRelations )
{
- const QList fieldPairs = revisitRelation.first.fieldPairs();
- revisitRelation.first.referencingLayer()->startEditing();
- QgsFeatureIterator it = revisitRelation.first.referencingLayer()->getFeatures( revisitRelation.second );
- QgsFeature childFeature;
- while ( it.nextFeature( childFeature ) )
+ // Revisit relations in need of attribute updates
+ for ( const QPair &revisitRelation : std::as_const( revisitRelations ) )
{
- for ( const QgsRelation::FieldPair fieldPair : fieldPairs )
+ const QList fieldPairs = revisitRelation.first.fieldPairs();
+ revisitRelation.first.referencingLayer()->startEditing();
+ QgsFeatureIterator it = revisitRelation.first.referencingLayer()->getFeatures( revisitRelation.second );
+ QgsFeature childFeature;
+ while ( it.nextFeature( childFeature ) )
{
- childFeature.setAttribute( fieldPair.referencingField(), feat.attribute( fieldPair.referencedField() ) );
+ for ( const QgsRelation::FieldPair &fieldPair : fieldPairs )
+ {
+ childFeature.setAttribute( fieldPair.referencingField(), feat.attribute( fieldPair.referencedField() ) );
+ }
+ revisitRelation.first.referencingLayer()->updateFeature( childFeature );
}
- revisitRelation.first.referencingLayer()->updateFeature( childFeature );
+ revisitRelation.first.referencingLayer()->commitChanges();
}
- revisitRelation.first.referencingLayer()->commitChanges();
- }
- // We need to update default values after creation to insure expression relying on relation children compute properly
- updateDefaultValues();
- save();
+ // We need to update default values after creation to insure expression relying on relation children compute properly
+ updateDefaultValues();
+ save();
+ }
+ }
+ else
+ {
+ QgsMessageLog::logMessage( tr( "Layer \"%1\" has been commited but the newly created feature %2 could not be fetched" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
+ isSuccess = false;
}
}
else
{
- QgsMessageLog::logMessage( tr( "Layer \"%1\" has been commited but the newly created feature %2 could not be fetched" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
+ const QString msgs = mLayer->commitErrors().join( QStringLiteral( "\n" ) );
+ QgsMessageLog::logMessage( tr( "Layer \"%1\" cannot be commited with the newly created feature %2. Reason:\n%3" ).arg( mLayer->name() ).arg( mFeature.id() ).arg( msgs ), QStringLiteral( "QField" ), Qgis::Critical );
isSuccess = false;
}
}
else
{
- const QString msgs = mLayer->commitErrors().join( QStringLiteral( "\n" ) );
- QgsMessageLog::logMessage( tr( "Layer \"%1\" cannot be commited with the newly created feature %2. Reason:\n%3" ).arg( mLayer->name() ).arg( mFeature.id() ).arg( msgs ), QStringLiteral( "QField" ), Qgis::Critical );
+ QgsMessageLog::logMessage( tr( "Feature %2 could not be added in layer \"%1\"" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
isSuccess = false;
}
- }
- else
- {
- QgsMessageLog::logMessage( tr( "Feature %2 could not be added in layer \"%1\"" ).arg( mLayer->name() ).arg( mFeature.id() ), QStringLiteral( "QField" ), Qgis::Critical );
- isSuccess = false;
- }
- if ( isSuccess && sRememberings->contains( mLayer ) )
- {
- QMutex *mutex = sMutex;
- QMutexLocker locker( mutex );
- ( *sRememberings )[mLayer].rememberedFeature = mFeature;
+ if ( isSuccess && sRememberings->contains( mLayer ) )
+ {
+ QMutex *mutex = sMutex;
+ QMutexLocker locker( mutex );
+ ( *sRememberings )[mLayer].rememberedFeature = mFeature;
+ }
}
disconnect( connection );
+
return isSuccess;
}
diff --git a/src/core/featuremodel.h b/src/core/featuremodel.h
index 7244858dbe..c433e09a39 100644
--- a/src/core/featuremodel.h
+++ b/src/core/featuremodel.h
@@ -52,6 +52,7 @@ class FeatureModel : public QAbstractListModel
Q_PROPERTY( bool positionLocked READ positionLocked WRITE setPositionLocked NOTIFY positionLockedChanged )
Q_PROPERTY( CloudUserInformation cloudUserInformation READ cloudUserInformation WRITE setCloudUserInformation NOTIFY cloudUserInformationChanged )
Q_PROPERTY( QgsProject *project READ project WRITE setProject NOTIFY projectChanged )
+ Q_PROPERTY( bool batchMode READ batchMode WRITE setBatchMode NOTIFY batchModeChanged )
public:
//! keeping the information what attributes are remembered and the last edited feature
@@ -271,6 +272,18 @@ class FeatureModel : public QAbstractListModel
QgsExpressionContext createExpressionContext() const;
+ /**
+ * Returns TRUE if the feature model is in batch mode. When enabled, the vector layer
+ * will remain in editing mode until batch mode is disabled.
+ */
+ bool batchMode() const { return mBatchMode; }
+
+ /**
+ * Toggles the feature model batch mode. When enabled, the vector layer
+ * will remain in editing mode until batch mode is disabled.
+ */
+ void setBatchMode( bool batchMode );
+
public slots:
void applyGeometry();
void removeLayer( QObject *layer );
@@ -297,6 +310,7 @@ class FeatureModel : public QAbstractListModel
void positionLockedChanged();
void projectChanged();
void cloudUserInformationChanged();
+ void batchModeChanged();
void warning( const QString &text );
@@ -331,6 +345,7 @@ class FeatureModel : public QAbstractListModel
QgsProject *mProject = nullptr;
QString mTempName;
bool mPositionLocked = false;
+ bool mBatchMode = false;
};
#endif // FEATUREMODEL_H
diff --git a/src/core/positioning/positioning.cpp b/src/core/positioning/positioning.cpp
index 0c6066f4e7..a8e9b25c23 100644
--- a/src/core/positioning/positioning.cpp
+++ b/src/core/positioning/positioning.cpp
@@ -14,7 +14,6 @@
* *
***************************************************************************/
-#include "platformutilities.h"
#include "positioning.h"
#include "positioningutils.h"
#include "tcpreceiver.h"
@@ -23,6 +22,11 @@
#include "serialportreceiver.h"
#endif
+#if defined( Q_OS_ANDROID )
+#include "platformutilities.h"
+#include "qfield_android.h"
+#endif
+
#include
#include
#include
@@ -37,9 +41,8 @@ Positioning::Positioning( QObject *parent )
if ( QFile::exists( PositioningSource::backgroundFilePath ) )
{
QFile::remove( PositioningSource::backgroundFilePath );
+ mPropertiesToSync["backgroundMode"] = false;
}
-
- connect( QgsApplication::instance(), &QGuiApplication::applicationStateChanged, this, &Positioning::onApplicationStateChanged );
}
void Positioning::setupSource()
@@ -48,7 +51,7 @@ void Positioning::setupSource()
#if defined( Q_OS_ANDROID )
PlatformUtilities::instance()->startPositioningService();
- mNode.connectToNode( QUrl( QStringLiteral( "localabstract:replica" ) ) );
+ mNode.connectToNode( QUrl( QStringLiteral( "localabstract:" APP_PACKAGE_NAME "replica" ) ) );
positioningService = true;
#endif
@@ -64,6 +67,11 @@ void Positioning::setupSource()
mPositioningSourceReplica.reset( mNode.acquireDynamic( "PositioningSource" ) );
mPositioningSourceReplica->waitForSource();
+ const QList properties = mPropertiesToSync.keys();
+ for ( const QString &property : properties )
+ {
+ mPositioningSourceReplica->setProperty( property.toLatin1(), mPropertiesToSync[property] );
+ }
connect( mPositioningSourceReplica.data(), SIGNAL( activeChanged() ), this, SIGNAL( activeChanged() ) );
connect( mPositioningSourceReplica.data(), SIGNAL( validChanged() ), this, SIGNAL( validChanged() ) );
@@ -83,11 +91,12 @@ void Positioning::setupSource()
connect( this, SIGNAL( triggerConnectDevice() ), mPositioningSourceReplica.data(), SLOT( triggerConnectDevice() ) );
connect( this, SIGNAL( triggerDisconnectDevice() ), mPositioningSourceReplica.data(), SLOT( triggerDisconnectDevice() ) );
- const QList properties = mPropertiesToSync.keys();
- for ( const QString &property : properties )
- {
- mPositioningSourceReplica->setProperty( property.toLatin1(), mPropertiesToSync[property] );
- }
+ connect( QgsApplication::instance(), &QGuiApplication::applicationStateChanged, this, &Positioning::onApplicationStateChanged );
+}
+
+bool Positioning::isSourceAvailable() const
+{
+ return mPositioningSourceReplica && mPositioningSourceReplica->isInitialized();
}
void Positioning::onApplicationStateChanged( Qt::ApplicationState state )
@@ -95,9 +104,6 @@ void Positioning::onApplicationStateChanged( Qt::ApplicationState state )
#ifdef Q_OS_ANDROID
// Google Play policy only allows for background access if it's explicitly stated and justified
// Not stopping on Activity::onPause is detected as violation
- if ( !mPositioningSourceReplica )
- return;
-
if ( !mPositioningSource )
{
// Service path
@@ -133,7 +139,7 @@ void Positioning::onApplicationStateChanged( Qt::ApplicationState state )
bool Positioning::active() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "active" ).toBool() : false;
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "active" ).toBool() : false;
}
void Positioning::setActive( bool active )
@@ -229,12 +235,12 @@ void Positioning::setActive( bool active )
bool Positioning::valid() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "valid" ).toBool() : mValid;
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "valid" ).toBool() : mValid;
}
void Positioning::setValid( bool valid )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "valid", valid );
}
@@ -247,12 +253,12 @@ void Positioning::setValid( bool valid )
QString Positioning::deviceId() const
{
- return ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "deviceId" ) : mPropertiesToSync.value( "deviceId" ) ).toString();
+ return ( isSourceAvailable() ? mPositioningSourceReplica->property( "deviceId" ) : mPropertiesToSync.value( "deviceId" ) ).toString();
}
void Positioning::setDeviceId( const QString &id )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "deviceId", id );
}
@@ -266,7 +272,7 @@ void Positioning::setDeviceId( const QString &id )
GnssPositionDetails Positioning::deviceDetails() const
{
GnssPositionDetails list;
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
list = mPositioningSourceReplica->property( "deviceDetails" ).value();
}
@@ -275,22 +281,22 @@ GnssPositionDetails Positioning::deviceDetails() const
QString Positioning::deviceLastError() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "deviceLastError" ).toString() : QString();
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "deviceLastError" ).toString() : QString();
}
QAbstractSocket::SocketState Positioning::deviceSocketState() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "deviceSocketState" ).value() : QAbstractSocket::UnconnectedState;
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "deviceSocketState" ).value() : QAbstractSocket::UnconnectedState;
}
QString Positioning::deviceSocketStateString() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "deviceSocketStateString" ).toString() : QString();
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "deviceSocketStateString" ).toString() : QString();
}
AbstractGnssReceiver::Capabilities Positioning::deviceCapabilities() const
{
- const QString deviceId = ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "deviceId" ) : mPropertiesToSync.value( "deviceId" ) ).toString();
+ const QString deviceId = ( isSourceAvailable() ? mPositioningSourceReplica->property( "deviceId" ) : mPropertiesToSync.value( "deviceId" ) ).toString();
if ( !deviceId.isEmpty() || deviceId.startsWith( TcpReceiver::identifier + ":" ) || deviceId.startsWith( UdpReceiver::identifier + ":" ) )
{
// NMEA-based devices
@@ -309,17 +315,17 @@ AbstractGnssReceiver::Capabilities Positioning::deviceCapabilities() const
int Positioning::averagedPositionCount() const
{
- return mPositioningSourceReplica ? mPositioningSourceReplica->property( "averagedPositionCount" ).toInt() : 0;
+ return isSourceAvailable() ? mPositioningSourceReplica->property( "averagedPositionCount" ).toInt() : 0;
}
bool Positioning::averagedPosition() const
{
- return ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "averagedPosition" ) : mPropertiesToSync.value( "averagedPosition", false ) ).toBool();
+ return ( isSourceAvailable() ? mPositioningSourceReplica->property( "averagedPosition" ) : mPropertiesToSync.value( "averagedPosition", false ) ).toBool();
}
void Positioning::setAveragedPosition( bool averaged )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "averagedPosition", averaged );
}
@@ -332,12 +338,12 @@ void Positioning::setAveragedPosition( bool averaged )
bool Positioning::logging() const
{
- return ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "logging" ) : mPropertiesToSync.value( "logging", false ) ).toBool();
+ return ( isSourceAvailable() ? mPositioningSourceReplica->property( "logging" ) : mPropertiesToSync.value( "logging", false ) ).toBool();
}
void Positioning::setLogging( bool logging )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "logging", logging );
}
@@ -374,7 +380,7 @@ void Positioning::setBackgroundMode( bool backgroundMode )
}
}
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
// Note that on Android, the property will not be set if the application is suspended _until_ it has become active again
mPositioningSourceReplica->setProperty( "backgroundMode", backgroundMode );
@@ -383,14 +389,29 @@ void Positioning::setBackgroundMode( bool backgroundMode )
emit backgroundModeChanged();
}
+QList Positioning::getBackgroundPositionInformation() const
+{
+ QList positionInformationList;
+
+ if ( isSourceAvailable() )
+ {
+ QRemoteObjectPendingCall call;
+ QMetaObject::invokeMethod( mPositioningSourceReplica.data(), "getBackgroundPositionInformation", Qt::DirectConnection, Q_RETURN_ARG( QRemoteObjectPendingCall, call ) );
+ call.waitForFinished();
+ positionInformationList = call.returnValue().value>();
+ }
+
+ return std::move( positionInformationList );
+}
+
PositioningSource::ElevationCorrectionMode Positioning::elevationCorrectionMode() const
{
- return static_cast( ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "elevationCorrectionMode" ) : mPropertiesToSync.value( "elevationCorrectionMode", static_cast( PositioningSource::ElevationCorrectionMode::None ) ) ).toInt() );
+ return static_cast( ( isSourceAvailable() ? mPositioningSourceReplica->property( "elevationCorrectionMode" ) : mPropertiesToSync.value( "elevationCorrectionMode", static_cast( PositioningSource::ElevationCorrectionMode::None ) ) ).toInt() );
}
void Positioning::setElevationCorrectionMode( PositioningSource::ElevationCorrectionMode elevationCorrectionMode )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "elevationCorrectionMode", static_cast( elevationCorrectionMode ) );
}
@@ -403,12 +424,12 @@ void Positioning::setElevationCorrectionMode( PositioningSource::ElevationCorrec
double Positioning::antennaHeight() const
{
- return ( mPositioningSourceReplica ? mPositioningSourceReplica->property( "antennaHeight" ) : mPropertiesToSync.value( "antennaHeight", 0.0 ) ).toDouble();
+ return ( isSourceAvailable() ? mPositioningSourceReplica->property( "antennaHeight" ) : mPropertiesToSync.value( "antennaHeight", 0.0 ) ).toDouble();
}
void Positioning::setAntennaHeight( double antennaHeight )
{
- if ( mPositioningSourceReplica )
+ if ( isSourceAvailable() )
{
mPositioningSourceReplica->setProperty( "antennaHeight", antennaHeight );
}
@@ -426,7 +447,7 @@ GnssPositionInformation Positioning::positionInformation() const
double Positioning::orientation() const
{
- return mPositioningSourceReplica ? adjustOrientation( mPositioningSourceReplica->property( "orientation" ).toDouble() ) : std::numeric_limits::quiet_NaN();
+ return isSourceAvailable() ? adjustOrientation( mPositioningSourceReplica->property( "orientation" ).toDouble() ) : std::numeric_limits::quiet_NaN();
}
double Positioning::adjustOrientation( double orientation ) const
@@ -454,13 +475,7 @@ void Positioning::setCoordinateTransformer( QgsQuickCoordinateTransformer *coord
if ( mCoordinateTransformer == coordinateTransformer )
return;
- if ( mCoordinateTransformer )
- {
- disconnect( mCoordinateTransformer, &QgsQuickCoordinateTransformer::projectedPositionChanged, this, &Positioning::projectedPositionTransformed );
- }
-
mCoordinateTransformer = coordinateTransformer;
- connect( mCoordinateTransformer, &QgsQuickCoordinateTransformer::projectedPositionChanged, this, &Positioning::projectedPositionTransformed );
emit coordinateTransformerChanged();
}
@@ -495,7 +510,20 @@ void Positioning::processGnssPositionInformation()
if ( mCoordinateTransformer )
{
- mCoordinateTransformer->setSourcePosition( mSourcePosition );
+ mProjectedPosition = mCoordinateTransformer->transformPosition( mSourcePosition );
+ mProjectedHorizontalAccuracy = mPositionInformation.hacc();
+ if ( mPositionInformation.haccValid() )
+ {
+ if ( mCoordinateTransformer->destinationCrs().mapUnits() != Qgis::DistanceUnit::Unknown )
+ {
+ mProjectedHorizontalAccuracy *= QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters,
+ mCoordinateTransformer->destinationCrs().mapUnits() );
+ }
+ else
+ {
+ mProjectedHorizontalAccuracy = 0.0;
+ }
+ }
}
if ( mPositionInformation.orientationValid() )
@@ -505,23 +533,3 @@ void Positioning::processGnssPositionInformation()
emit positionInformationChanged();
}
-
-void Positioning::projectedPositionTransformed()
-{
- mProjectedPosition = mCoordinateTransformer->projectedPosition();
- mProjectedHorizontalAccuracy = mPositionInformation.hacc();
- if ( mPositionInformation.haccValid() )
- {
- if ( mCoordinateTransformer->destinationCrs().mapUnits() != Qgis::DistanceUnit::Unknown )
- {
- mProjectedHorizontalAccuracy *= QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters,
- mCoordinateTransformer->destinationCrs().mapUnits() );
- }
- else
- {
- mProjectedHorizontalAccuracy = 0.0;
- }
- }
-
- emit projectedPositionChanged();
-}
diff --git a/src/core/positioning/positioning.h b/src/core/positioning/positioning.h
index 0b1d3e5404..6f537559db 100644
--- a/src/core/positioning/positioning.h
+++ b/src/core/positioning/positioning.h
@@ -51,8 +51,8 @@ class Positioning : public QObject
Q_PROPERTY( GnssPositionInformation positionInformation READ positionInformation NOTIFY positionInformationChanged )
Q_PROPERTY( QgsPoint sourcePosition READ sourcePosition NOTIFY positionInformationChanged )
- Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY projectedPositionChanged )
- Q_PROPERTY( double projectedHorizontalAccuracy READ projectedHorizontalAccuracy NOTIFY projectedPositionChanged )
+ Q_PROPERTY( QgsPoint projectedPosition READ projectedPosition NOTIFY positionInformationChanged )
+ Q_PROPERTY( double projectedHorizontalAccuracy READ projectedHorizontalAccuracy NOTIFY positionInformationChanged )
Q_PROPERTY( bool averagedPosition READ averagedPosition WRITE setAveragedPosition NOTIFY averagedPositionChanged )
Q_PROPERTY( int averagedPositionCount READ averagedPositionCount NOTIFY averagedPositionCountChanged )
@@ -218,15 +218,26 @@ class Positioning : public QObject
void setLogging( bool logging );
/**
- * Returns TRUE if the background mode is active.
+ * Returns TRUE if the background mode is active. When activated, position information details
+ * will not be signaled but instead saved to disk until deactivated.
+ * \see getBackgroundPositionInformation()
*/
bool backgroundMode() const;
/**
- * Sets whether the background mode is active.
+ * Sets whether the background mode is active. When activated, position information details
+ * will not be signaled but instead saved to disk until deactivated.
+ * \see getBackgroundPositionInformation()
*/
void setBackgroundMode( bool backgroundMode );
+ /**
+ * Returns a list of position information collected while background mode is active.
+ * \see backgroundMode()
+ * \see setBackgroundMode()
+ */
+ Q_INVOKABLE QList getBackgroundPositionInformation() const;
+
signals:
void activeChanged();
void validChanged();
@@ -250,11 +261,12 @@ class Positioning : public QObject
private slots:
void onApplicationStateChanged( Qt::ApplicationState state );
- void projectedPositionTransformed();
void processGnssPositionInformation();
private:
void setupSource();
+ bool isSourceAvailable() const;
+
double adjustOrientation( double orientation ) const;
bool mValid = true;
@@ -269,7 +281,7 @@ class Positioning : public QObject
QgsQuickCoordinateTransformer *mCoordinateTransformer = nullptr;
QgsPoint mSourcePosition;
QgsPoint mProjectedPosition;
- double mProjectedHorizontalAccuracy;
+ double mProjectedHorizontalAccuracy = 0.0;
virtual QList> details() const { return {}; }
bool mInternalPermissionChecked = false;
diff --git a/src/core/positioning/positioningsource.cpp b/src/core/positioning/positioningsource.cpp
index 2cf09781a2..8179486335 100644
--- a/src/core/positioning/positioningsource.cpp
+++ b/src/core/positioning/positioningsource.cpp
@@ -150,9 +150,39 @@ void PositioningSource::setBackgroundMode( bool backgroundMode )
mBackgroundMode = backgroundMode;
+ if ( mBackgroundMode )
+ {
+ if ( QFile::exists( QStringLiteral( "%1.information" ).arg( backgroundFilePath ) ) )
+ {
+ // Remove previously collected position information
+ QFile::remove( QStringLiteral( "%1.information" ).arg( backgroundFilePath ) );
+ }
+ }
+
emit backgroundModeChanged();
}
+QList PositioningSource::getBackgroundPositionInformation() const
+{
+ QList positionInformationList;
+
+ QFile file( QStringLiteral( "%1.information" ).arg( backgroundFilePath ) );
+ if ( file.exists() )
+ {
+ file.open( QFile::ReadOnly );
+ QDataStream stream( &file );
+ while ( !stream.atEnd() )
+ {
+ GnssPositionInformation positionInformation;
+ stream >> positionInformation;
+ positionInformationList << positionInformation;
+ }
+ file.close();
+ }
+
+ return std::move( positionInformationList );
+}
+
void PositioningSource::setElevationCorrectionMode( ElevationCorrectionMode elevationCorrectionMode )
{
if ( mElevationCorrectionMode == elevationCorrectionMode )
@@ -265,7 +295,7 @@ void PositioningSource::lastGnssPositionInformationChanged( const GnssPositionIn
lastGnssPositionInformation.vdop(),
lastGnssPositionInformation.hacc(),
lastGnssPositionInformation.vacc(),
- lastGnssPositionInformation.utcDateTime(),
+ lastGnssPositionInformation.utcDateTime().isValid() ? lastGnssPositionInformation.utcDateTime() : QDateTime::currentDateTimeUtc(),
lastGnssPositionInformation.fixMode(),
lastGnssPositionInformation.fixType(),
lastGnssPositionInformation.quality(),
@@ -298,6 +328,14 @@ void PositioningSource::lastGnssPositionInformationChanged( const GnssPositionIn
emit averagedPositionCountChanged();
}
}
+ else
+ {
+ QFile file( QStringLiteral( "%1.information" ).arg( backgroundFilePath ) );
+ file.open( QFile::Append );
+ QDataStream stream( &file );
+ stream << mPositionInformation;
+ file.close();
+ }
}
void PositioningSource::processCompassReading()
diff --git a/src/core/positioning/positioningsource.h b/src/core/positioning/positioningsource.h
index dc22ba353c..2ee898de6e 100644
--- a/src/core/positioning/positioningsource.h
+++ b/src/core/positioning/positioningsource.h
@@ -197,15 +197,26 @@ class PositioningSource : public QObject
void setLogging( bool logging );
/**
- * Returns TRUE if the background mode is active.
+ * Returns TRUE if the background mode is active. When activated, position information details
+ * will not be signaled but instead saved to disk until deactivated.
+ * \see getBackgroundPositionInformation()
*/
bool backgroundMode() const { return mBackgroundMode; }
/**
- * Sets whether the background mode is active.
+ * Sets whether the background mode is active. When activated, position information details
+ * will not be signaled but instead saved to disk until deactivated.
+ * \see getBackgroundPositionInformation()
*/
void setBackgroundMode( bool backgroundMode );
+ /**
+ * Returns a list of position information collected while background mode is active.
+ * \see backgroundMode()
+ * \see setBackgroundMode()
+ */
+ Q_INVOKABLE QList getBackgroundPositionInformation() const;
+
static QString backgroundFilePath;
signals:
diff --git a/src/core/projectinfo.cpp b/src/core/projectinfo.cpp
index bb3b8cea4a..bf8e42820f 100644
--- a/src/core/projectinfo.cpp
+++ b/src/core/projectinfo.cpp
@@ -152,24 +152,25 @@ QModelIndex ProjectInfo::restoreTracker( QgsVectorLayer *layer )
return QModelIndex();
QModelIndex index = mTrackingModel->createTracker( layer );
+ Tracker *tracker = mTrackingModel->data( index, TrackingModel::TrackerPointer ).value();
mSettings.beginGroup( QStringLiteral( "/qgis/projectInfo/trackers/%1" ).arg( layer->id() ) );
- mTrackingModel->setData( index, mSettings.value( "minimumDistance", 0 ).toDouble(), TrackingModel::MinimumDistance );
- mTrackingModel->setData( index, mSettings.value( "timeInterval", 0 ).toDouble(), TrackingModel::TimeInterval );
- mTrackingModel->setData( index, mSettings.value( "sensorCapture", false ).toBool(), TrackingModel::SensorCapture );
- mTrackingModel->setData( index, mSettings.value( "conjunction", false ).toBool(), TrackingModel::Conjunction );
- mTrackingModel->setData( index, mSettings.value( "maximumDistance", 0 ).toDouble(), TrackingModel::MaximumDistance );
- mTrackingModel->setData( index, static_cast( mSettings.value( "measureType", 0 ).toInt() ), TrackingModel::MeasureType );
- mTrackingModel->setData( index, mSettings.value( "visible", true ).toBool(), TrackingModel::Visible );
+ tracker->setTimeInterval( mSettings.value( "timeInterval", 0 ).toInt() );
+ tracker->setMinimumDistance( mSettings.value( "minimumDistance", 0 ).toDouble() );
+ tracker->setMaximumDistance( mSettings.value( "maximumDistance", 0 ).toDouble() );
+ tracker->setSensorCapture( mSettings.value( "sensorCapture", false ).toBool() );
+ tracker->setConjunction( mSettings.value( "conjunction", false ).toBool() );
+ tracker->setMeasureType( static_cast( mSettings.value( "measureType", 0 ).toInt() ) );
+ tracker->setVisible( mSettings.value( "visible", true ).toBool() );
const QgsFeatureId fid = mSettings.value( "featureId", FID_NULL ).toLongLong();
if ( fid >= 0 )
{
QgsFeature feature = layer->getFeature( fid );
- mTrackingModel->setData( index, QVariant::fromValue( feature ), TrackingModel::Feature );
+ tracker->setFeature( feature );
}
else
{
- mTrackingModel->setData( index, QVariant::fromValue( QgsFeature( layer->fields() ) ), TrackingModel::Feature );
+ tracker->setFeature( QgsFeature( layer->fields() ) );
}
mSettings.endGroup();
diff --git a/src/core/qgsquick/qgsquickcoordinatetransformer.cpp b/src/core/qgsquick/qgsquickcoordinatetransformer.cpp
index 83468c61c5..3060072249 100644
--- a/src/core/qgsquick/qgsquickcoordinatetransformer.cpp
+++ b/src/core/qgsquick/qgsquickcoordinatetransformer.cpp
@@ -107,11 +107,22 @@ QgsCoordinateTransformContext QgsQuickCoordinateTransformer::transformContext()
return mCoordinateTransform.context();
}
+QgsPoint QgsQuickCoordinateTransformer::transformPosition( const QgsPoint &position ) const
+{
+ return processPosition( position );
+}
+
void QgsQuickCoordinateTransformer::updatePosition()
{
- double x = mSourcePosition.x();
- double y = mSourcePosition.y();
- double z = mSourcePosition.z();
+ mProjectedPosition = processPosition( mSourcePosition );
+ emit projectedPositionChanged();
+}
+
+QgsPoint QgsQuickCoordinateTransformer::processPosition( const QgsPoint &position ) const
+{
+ double x = position.x();
+ double y = position.y();
+ double z = position.z();
// If Z is NaN, proj's coordinate transformation will
// also set X and Y to NaN. But we also want to get projected
@@ -137,13 +148,13 @@ void QgsQuickCoordinateTransformer::updatePosition()
}
if ( mSkipAltitudeTransformation )
- z = mSourcePosition.z();
+ z = position.z();
if ( !mVerticalGridPath.isEmpty() )
{
- std::vector xVector = { mSourcePosition.x() };
- std::vector yVector = { mSourcePosition.y() };
- std::vector zVector = { !std::isnan( mSourcePosition.z() ) ? mSourcePosition.z() : 0 };
+ std::vector xVector = { position.x() };
+ std::vector yVector = { position.y() };
+ std::vector zVector = { !std::isnan( position.z() ) ? position.z() : 0 };
try
{
double zDummy = 0.0; // we don't want to manipulate the elevation data yet, use a dummy z value to transform coordinates first
@@ -173,10 +184,10 @@ void QgsQuickCoordinateTransformer::updatePosition()
}
}
- mProjectedPosition = QgsPoint( x, y );
- mProjectedPosition.addZValue( z + mDeltaZ );
+ QgsPoint projectedPoint( x, y );
+ projectedPoint.addZValue( z + mDeltaZ );
- emit projectedPositionChanged();
+ return std::move( projectedPoint );
}
bool QgsQuickCoordinateTransformer::skipAltitudeTransformation() const
@@ -242,52 +253,54 @@ void QgsQuickCoordinateTransformer::setVerticalGrid( const QString &grid )
mVerticalGrid = grid;
mVerticalGridPath.clear();
- if ( mVerticalGrid.isEmpty() )
- return;
-
- if ( QFile::exists( mVerticalGrid ) )
+ if ( !mVerticalGrid.isEmpty() )
{
- mVerticalGridPath = mVerticalGrid;
- }
- else
- {
- QStringList dataDirs = PlatformUtilities::instance()->appDataDirs();
- if ( !dataDirs.isEmpty() )
+ if ( QFile::exists( mVerticalGrid ) )
{
- for ( const QString &dataDir : dataDirs )
+ mVerticalGridPath = mVerticalGrid;
+ }
+ else
+ {
+ QStringList dataDirs = PlatformUtilities::instance()->appDataDirs();
+ if ( !dataDirs.isEmpty() )
{
- const QString verticalGridPath = QStringLiteral( "%1proj/%2" ).arg( dataDir, mVerticalGrid );
- if ( QFile::exists( verticalGridPath ) )
+ for ( const QString &dataDir : dataDirs )
{
- mVerticalGridPath = verticalGridPath;
- break;
+ const QString verticalGridPath = QStringLiteral( "%1proj/%2" ).arg( dataDir, mVerticalGrid );
+ if ( QFile::exists( verticalGridPath ) )
+ {
+ mVerticalGridPath = verticalGridPath;
+ break;
+ }
}
}
}
- }
- if ( !mVerticalGridPath.isEmpty() )
- {
- GDALDatasetH hDataset;
- GDALAllRegister();
- hDataset = GDALOpen( mVerticalGridPath.toUtf8().constData(), GA_ReadOnly );
- if ( hDataset != NULL )
+ if ( !mVerticalGridPath.isEmpty() )
{
- OGRSpatialReferenceH spatialRef = GDALGetSpatialRef( hDataset );
- char *pszWkt = nullptr;
- const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
- const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
- const char *const options[] = { multiLineOption.constData(), formatOption.constData(), nullptr };
- OSRExportToWktEx( spatialRef, &pszWkt, options );
- mCoordinateVerticalGridTransform.setDestinationCrs( QgsCoordinateReferenceSystem::fromWkt( QString( pszWkt ) ) );
- mCoordinateVerticalGridTransform.setContext( mCoordinateTransform.context() );
- CPLFree( pszWkt );
- }
- else
- {
- // Invalid grid file, skip
- mVerticalGridPath.clear();
+ GDALDatasetH hDataset;
+ GDALAllRegister();
+ hDataset = GDALOpen( mVerticalGridPath.toUtf8().constData(), GA_ReadOnly );
+ if ( hDataset != NULL )
+ {
+ OGRSpatialReferenceH spatialRef = GDALGetSpatialRef( hDataset );
+ char *pszWkt = nullptr;
+ const QByteArray multiLineOption = QStringLiteral( "MULTILINE=NO" ).toLocal8Bit();
+ const QByteArray formatOption = QStringLiteral( "FORMAT=WKT2" ).toLocal8Bit();
+ const char *const options[] = { multiLineOption.constData(), formatOption.constData(), nullptr };
+ OSRExportToWktEx( spatialRef, &pszWkt, options );
+ mCoordinateVerticalGridTransform.setDestinationCrs( QgsCoordinateReferenceSystem::fromWkt( QString( pszWkt ) ) );
+ mCoordinateVerticalGridTransform.setContext( mCoordinateTransform.context() );
+ CPLFree( pszWkt );
+ }
+ else
+ {
+ // Invalid grid file, skip
+ mVerticalGridPath.clear();
+ }
+ GDALClose( hDataset );
}
- GDALClose( hDataset );
}
+
+ emit verticalGridChanged();
}
diff --git a/src/core/qgsquick/qgsquickcoordinatetransformer.h b/src/core/qgsquick/qgsquickcoordinatetransformer.h
index 95636a51fc..add01f58a4 100644
--- a/src/core/qgsquick/qgsquickcoordinatetransformer.h
+++ b/src/core/qgsquick/qgsquickcoordinatetransformer.h
@@ -125,6 +125,8 @@ class QgsQuickCoordinateTransformer : public QObject
//!\copydoc QgsQuickCoordinateTransformer::verticalGrid
void setVerticalGrid( const QString &grid );
+ Q_INVOKABLE QgsPoint transformPosition( const QgsPoint &position ) const;
+
signals:
//!\copydoc QgsQuickCoordinateTransformer::projectedPosition
void projectedPositionChanged();
@@ -154,6 +156,7 @@ class QgsQuickCoordinateTransformer : public QObject
private:
void updatePosition();
+ QgsPoint processPosition( const QgsPoint &position ) const;
QgsPoint mProjectedPosition;
QgsPoint mSourcePosition;
diff --git a/src/core/rubberbandmodel.cpp b/src/core/rubberbandmodel.cpp
index 80c229ee42..d8a44cddab 100644
--- a/src/core/rubberbandmodel.cpp
+++ b/src/core/rubberbandmodel.cpp
@@ -319,6 +319,8 @@ void RubberbandModel::removeVertex()
void RubberbandModel::reset()
{
removeVertices( 0, mPointList.size() - 1 );
+ mPointList.replace( 0, QgsPoint() );
+
mFrozen = false;
emit frozenChanged();
}
diff --git a/src/core/tracker.cpp b/src/core/tracker.cpp
index d6fae6c980..12bb18568f 100644
--- a/src/core/tracker.cpp
+++ b/src/core/tracker.cpp
@@ -13,32 +13,77 @@
* *
***************************************************************************/
+#include "featuremodel.h"
+#include "qgsquickcoordinatetransformer.h"
#include "rubberbandmodel.h"
#include "tracker.h"
-#include
#include
#include
#include
#define MAXIMUM_DISTANCE_FAILURES 20
-Tracker::Tracker( QgsVectorLayer *layer )
- : mLayer( layer )
+Tracker::Tracker( QgsVectorLayer *vectorLayer )
+ : mVectorLayer( vectorLayer )
{
}
-RubberbandModel *Tracker::model() const
+void Tracker::setVisible( bool visible )
+{
+ if ( mVisible == visible )
+ return;
+
+ mVisible = visible;
+ emit visibleChanged();
+}
+
+void Tracker::setVectorLayer( QgsVectorLayer *vectorLayer )
+{
+ if ( mVectorLayer == vectorLayer )
+ return;
+
+ mVectorLayer = vectorLayer;
+ emit vectorLayerChanged();
+}
+
+RubberbandModel *Tracker::rubberbandModel() const
{
return mRubberbandModel;
}
-void Tracker::setModel( RubberbandModel *model )
+void Tracker::setRubberbandModel( RubberbandModel *rubberbandModel )
{
- if ( mRubberbandModel == model )
+ if ( mRubberbandModel == rubberbandModel )
return;
- mRubberbandModel = model;
+ if ( mRubberbandModel )
+ {
+ disconnect( mRubberbandModel, &RubberbandModel::vertexCountChanged, this, &Tracker::rubberbandModelVertexCountChanged );
+ }
+
+ mRubberbandModel = rubberbandModel;
+
+ if ( mRubberbandModel )
+ {
+ connect( mRubberbandModel, &RubberbandModel::vertexCountChanged, this, &Tracker::rubberbandModelVertexCountChanged );
+ }
+
+ emit rubberbandModelChanged();
+}
+
+FeatureModel *Tracker::featureModel() const
+{
+ return mFeatureModel;
+}
+
+void Tracker::setFeatureModel( FeatureModel *featureModel )
+{
+ if ( mFeatureModel == featureModel )
+ return;
+
+ mFeatureModel = featureModel;
+ emit featureModelChanged();
}
QgsFeature Tracker::feature() const
@@ -52,11 +97,66 @@ void Tracker::setFeature( const QgsFeature &feature )
return;
mFeature = feature;
+ emit featureChanged();
+}
+
+void Tracker::setTimeInterval( double timeInterval )
+{
+ if ( mTimeInterval == timeInterval )
+ return;
+
+ mTimeInterval = timeInterval;
+ emit timeIntervalChanged();
+}
+
+void Tracker::setMinimumDistance( double minimumDistance )
+{
+ if ( mMinimumDistance == minimumDistance )
+ return;
+
+ mMinimumDistance = minimumDistance;
+ emit minimumDistanceChanged();
+}
+
+void Tracker::setMaximumDistance( double maximumDistance )
+{
+ if ( mMaximumDistance == maximumDistance )
+ return;
+
+ mMaximumDistance = maximumDistance;
+ emit maximumDistanceChanged();
+}
+
+void Tracker::setSensorCapture( bool capture )
+{
+ if ( mSensorCapture == capture )
+ return;
+
+ mSensorCapture = capture;
+ emit sensorCaptureChanged();
+}
+
+void Tracker::setConjunction( bool conjunction )
+{
+ if ( mConjunction == conjunction )
+ return;
+
+ mConjunction = conjunction;
+ emit conjunctionChanged();
+}
+
+void Tracker::setMeasureType( MeasureType type )
+{
+ if ( mMeasureType == type )
+ return;
+
+ mMeasureType = type;
+ emit measureTypeChanged();
}
void Tracker::trackPosition()
{
- if ( !model() || std::isnan( model()->currentCoordinate().x() ) || std::isnan( model()->currentCoordinate().y() ) )
+ if ( !mRubberbandModel || std::isnan( mRubberbandModel->currentCoordinate().x() ) || std::isnan( mRubberbandModel->currentCoordinate().y() ) )
{
return;
}
@@ -71,8 +171,9 @@ void Tracker::trackPosition()
}
mSkipPositionReceived = true;
- model()->addVertex();
+ mRubberbandModel->addVertex();
+ mLastVertexPositionTimestamp = mLastDevicePositionTimestamp;
mMaximumDistanceFailuresCount = 0;
mCurrentDistance = 0.0;
mTimeIntervalFulfilled = qgsDoubleNear( mTimeInterval, 0.0 );
@@ -84,7 +185,7 @@ void Tracker::positionReceived()
{
if ( mSkipPositionReceived )
{
- // When calling model()->addVertex(), the signal we listen to for new position received is triggered, skip that one
+ // When calling mRubberbandModel->addVertex(), the signal we listen to for new position received is triggered, skip that one
mSkipPositionReceived = false;
return;
}
@@ -109,27 +210,30 @@ void Tracker::positionReceived()
if ( !qgsDoubleNear( mMinimumDistance, 0.0 ) )
{
- if ( mCurrentDistance > mMinimumDistance )
+ mMinimumDistanceFulfilled = mCurrentDistance >= mMinimumDistance;
+
+ if ( !mConjunction && mMinimumDistanceFulfilled )
{
- mMinimumDistanceFulfilled = true;
+ trackPosition();
+ return;
}
}
- else
- {
- mMinimumDistanceFulfilled = true;
- }
- if ( ( !mConjunction && mMinimumDistanceFulfilled ) || ( mMinimumDistanceFulfilled && mTimeIntervalFulfilled && mSensorCaptureFulfilled ) )
+ if ( !qgsDoubleNear( mTimeInterval, 0.0 ) )
{
- trackPosition();
- }
-}
+ mTimeIntervalFulfilled = ( mLastDevicePositionTimestamp.toMSecsSinceEpoch() - mLastVertexPositionTimestamp.toMSecsSinceEpoch() ) >= mTimeInterval * 1000;
+ qDebug() << mLastDevicePositionTimestamp;
+ qDebug() << mTimeInterval;
+ qDebug() << ( mLastDevicePositionTimestamp.toMSecsSinceEpoch() - mLastVertexPositionTimestamp.toMSecsSinceEpoch() );
-void Tracker::timeReceived()
-{
- mTimeIntervalFulfilled = true;
+ if ( !mConjunction && mTimeIntervalFulfilled )
+ {
+ trackPosition();
+ return;
+ }
+ }
- if ( !mConjunction || ( mMinimumDistanceFulfilled && mSensorCaptureFulfilled ) )
+ if ( mMinimumDistanceFulfilled && mTimeIntervalFulfilled && mSensorCaptureFulfilled )
{
trackPosition();
}
@@ -145,51 +249,40 @@ void Tracker::sensorDataReceived()
}
}
-void Tracker::start()
+void Tracker::start( const GnssPositionInformation &positionInformation, const QgsPoint &projectedPosition )
{
mIsActive = true;
emit isActiveChanged();
- if ( mTimeInterval > 0 )
- {
- connect( &mTimer, &QTimer::timeout, this, &Tracker::timeReceived );
- mTimer.start( mTimeInterval * 1000 );
- }
- else
- {
- mTimeIntervalFulfilled = true;
- }
- if ( mMinimumDistance > 0 || ( qgsDoubleNear( mTimeInterval, 0.0 ) && !mSensorCapture ) )
+ if ( mMinimumDistance > 0 || mTimeInterval > 0 || !mSensorCapture )
{
connect( mRubberbandModel, &RubberbandModel::currentCoordinateChanged, this, &Tracker::positionReceived );
}
- else
- {
- mMinimumDistanceFulfilled = true;
- }
if ( mSensorCapture )
{
connect( QgsProject::instance()->sensorManager(), &QgsSensorManager::sensorDataCaptured, this, &Tracker::sensorDataReceived );
}
- else
- {
- mSensorCaptureFulfilled = true;
- }
-
- //set the start time
- setStartPositionTimestamp( QDateTime::currentDateTime() );
if ( mMeasureType == Tracker::SecondsSinceStart )
{
- model()->setMeasureValue( 0 );
+ mRubberbandModel->setMeasureValue( 0 );
}
mSkipPositionReceived = false;
mMaximumDistanceFailuresCount = 0;
mCurrentDistance = mMaximumDistance;
+ mTimeIntervalFulfilled = qgsDoubleNear( mTimeInterval, 0.0 );
+ mMinimumDistanceFulfilled = qgsDoubleNear( mMinimumDistance, 0.0 );
+ mSensorCaptureFulfilled = !mSensorCapture;
- //track first position
- trackPosition();
+ if ( !projectedPosition.isEmpty() )
+ {
+ //set the start time of first position
+ setStartPositionTimestamp( positionInformation.utcDateTime().isValid() ? positionInformation.utcDateTime() : QDateTime::currentDateTime() );
+
+ //track first position
+ processPositionInformation( positionInformation, projectedPosition );
+ }
}
void Tracker::stop()
@@ -200,12 +293,7 @@ void Tracker::stop()
mIsActive = false;
emit isActiveChanged();
- if ( mTimeInterval > 0 )
- {
- mTimer.stop();
- disconnect( &mTimer, &QTimer::timeout, this, &Tracker::trackPosition );
- }
- if ( mMinimumDistance > 0 || ( qgsDoubleNear( mTimeInterval, 0.0 ) && !mSensorCapture ) )
+ if ( mMinimumDistance > 0 || mTimeInterval > 0 || !mSensorCapture )
{
disconnect( mRubberbandModel, &RubberbandModel::currentCoordinateChanged, this, &Tracker::positionReceived );
}
@@ -214,3 +302,130 @@ void Tracker::stop()
disconnect( QgsProject::instance()->sensorManager(), &QgsSensorManager::sensorDataCaptured, this, &Tracker::sensorDataReceived );
}
}
+
+void Tracker::processPositionInformation( const GnssPositionInformation &positionInformation, const QgsPoint &projectedPosition )
+{
+ if ( !mIsActive && !mIsReplaying )
+ return;
+
+ mLastDevicePositionTimestamp = positionInformation.utcDateTime();
+
+ switch ( mMeasureType )
+ {
+ case Tracker::SecondsSinceStart:
+ mRubberbandModel->setMeasureValue( positionInformation.utcDateTime().toSecsSinceEpoch() - mStartPositionTimestamp.toSecsSinceEpoch() );
+ break;
+ case Tracker::Timestamp:
+ mRubberbandModel->setMeasureValue( positionInformation.utcDateTime().toSecsSinceEpoch() );
+ break;
+ case Tracker::GroundSpeed:
+ mRubberbandModel->setMeasureValue( positionInformation.speed() );
+ break;
+ case Tracker::Bearing:
+ mRubberbandModel->setMeasureValue( positionInformation.direction() );
+ break;
+ case Tracker::HorizontalAccuracy:
+ mRubberbandModel->setMeasureValue( positionInformation.hacc() );
+ break;
+ case Tracker::VerticalAccuracy:
+ mRubberbandModel->setMeasureValue( positionInformation.vacc() );
+ break;
+ case Tracker::PDOP:
+ mRubberbandModel->setMeasureValue( positionInformation.pdop() );
+ break;
+ case Tracker::HDOP:
+ mRubberbandModel->setMeasureValue( positionInformation.hdop() );
+ break;
+ case Tracker::VDOP:
+ mRubberbandModel->setMeasureValue( positionInformation.vdop() );
+ break;
+ }
+
+ mRubberbandModel->setCurrentCoordinate( projectedPosition );
+}
+
+void Tracker::replayPositionInformationList( const QList &positionInformationList, QgsQuickCoordinateTransformer *coordinateTransformer )
+{
+ qDebug() << "ttt replayPositionInformationList with count " << positionInformationList.size();
+ bool wasActive = false;
+ if ( mIsActive )
+ {
+ wasActive = true;
+ stop();
+ }
+
+ mIsReplaying = true;
+ emit isReplayingChanged();
+
+ const Qgis::GeometryType geometryType = mRubberbandModel->geometryType();
+ mFeatureModel->setBatchMode( geometryType == Qgis::GeometryType::Point );
+ connect( mRubberbandModel, &RubberbandModel::currentCoordinateChanged, this, &Tracker::positionReceived );
+ for ( const GnssPositionInformation &positionInformation : positionInformationList )
+ {
+ processPositionInformation( positionInformation,
+ coordinateTransformer ? coordinateTransformer->transformPosition( QgsPoint( positionInformation.longitude(), positionInformation.latitude(), positionInformation.elevation() ) ) : QgsPoint() );
+ }
+ disconnect( mRubberbandModel, &RubberbandModel::currentCoordinateChanged, this, &Tracker::positionReceived );
+ mFeatureModel->setBatchMode( false );
+
+ const int vertexCount = mRubberbandModel->vertexCount();
+ if ( ( geometryType == Qgis::GeometryType::Line && vertexCount > 2 ) || ( geometryType == Qgis::GeometryType::Polygon && vertexCount > 3 ) )
+ {
+ mFeatureModel->applyGeometry();
+ if ( mFeature.id() == FID_NULL )
+ {
+ mFeatureModel->create();
+ mFeature = mFeatureModel->feature();
+ emit featureCreated();
+ }
+ else
+ {
+ mFeatureModel->save();
+ }
+ }
+
+ mIsReplaying = false;
+ emit isReplayingChanged();
+
+ if ( wasActive )
+ {
+ start();
+ }
+}
+
+void Tracker::rubberbandModelVertexCountChanged()
+{
+ if ( ( !mIsActive && !mIsReplaying ) || mRubberbandModel->vertexCount() == 0 )
+ return;
+
+ const Qgis::GeometryType geometryType = mRubberbandModel->geometryType();
+ const int vertexCount = mRubberbandModel->vertexCount();
+ if ( geometryType == Qgis::GeometryType::Point )
+ {
+ mFeatureModel->applyGeometry();
+ mFeatureModel->resetFeatureId();
+ mFeatureModel->resetAttributes( true );
+ mFeatureModel->create();
+ }
+ else
+ {
+ // When replaying, we can optimize things and do this only once
+ if ( mIsActive )
+ {
+ if ( ( geometryType == Qgis::GeometryType::Line && vertexCount > 2 ) || ( geometryType == Qgis::GeometryType::Polygon && vertexCount > 3 ) )
+ {
+ mFeatureModel->applyGeometry();
+ if ( ( geometryType == Qgis::GeometryType::Line && vertexCount == 3 ) || ( geometryType == Qgis::GeometryType::Polygon && vertexCount == 4 ) )
+ {
+ mFeatureModel->create();
+ mFeature = mFeatureModel->feature();
+ emit featureCreated();
+ }
+ else
+ {
+ mFeatureModel->save();
+ }
+ }
+ }
+ }
+}
diff --git a/src/core/tracker.h b/src/core/tracker.h
index 17bd89e876..6aa2624e47 100644
--- a/src/core/tracker.h
+++ b/src/core/tracker.h
@@ -16,11 +16,15 @@
#ifndef TRACKER_H
#define TRACKER_H
-#include "qgsvectorlayer.h"
+#include "gnsspositioninformation.h"
#include
#include
+#include
+#include
+class FeatureModel;
+class QgsQuickCoordinateTransformer;
class RubberbandModel;
/**
@@ -30,6 +34,24 @@ class Tracker : public QObject
{
Q_OBJECT
+ Q_PROPERTY( bool isActive READ isActive NOTIFY isActiveChanged )
+ Q_PROPERTY( bool isReplaying READ isReplaying NOTIFY isReplayingChanged )
+
+ Q_PROPERTY( bool visible READ visible WRITE setVisible NOTIFY visibleChanged )
+
+ Q_PROPERTY( QgsVectorLayer *vectorLayer READ vectorLayer WRITE setVectorLayer NOTIFY vectorLayerChanged )
+ Q_PROPERTY( QgsFeature feature READ feature WRITE setFeature NOTIFY featureChanged )
+
+ Q_PROPERTY( RubberbandModel *rubberbandModel READ rubberbandModel WRITE setRubberbandModel NOTIFY rubberbandModelChanged )
+ Q_PROPERTY( FeatureModel *featureModel READ featureModel WRITE setFeatureModel NOTIFY featureModelChanged )
+
+ Q_PROPERTY( double timeInterval READ timeInterval WRITE setTimeInterval NOTIFY timeIntervalChanged )
+ Q_PROPERTY( double minimumDistance READ minimumDistance WRITE setMinimumDistance NOTIFY minimumDistanceChanged )
+ Q_PROPERTY( double maximumDistance READ maximumDistance WRITE setMaximumDistance NOTIFY maximumDistanceChanged )
+ Q_PROPERTY( bool sensorCapture READ sensorCapture WRITE setSensorCapture NOTIFY sensorCaptureChanged )
+ Q_PROPERTY( bool conjunction READ conjunction WRITE setConjunction NOTIFY conjunctionChanged )
+ Q_PROPERTY( MeasureType measureType READ measureType WRITE setMeasureType NOTIFY measureTypeChanged )
+
Q_PROPERTY( QDateTime startPositionTimestamp READ startPositionTimestamp WRITE setStartPositionTimestamp NOTIFY startPositionTimestampChanged )
public:
@@ -47,35 +69,42 @@ class Tracker : public QObject
};
Q_ENUM( MeasureType )
- explicit Tracker( QgsVectorLayer *layer );
+ explicit Tracker( QgsVectorLayer *vectorLayer );
+
+ //! Returns the rubber band model used to generate the track geometry
+ RubberbandModel *rubberbandModel() const;
+ //! Sets the rubber band model used to generate the track geometry
+ void setRubberbandModel( RubberbandModel *rubberbandModel );
- RubberbandModel *model() const;
- void setModel( RubberbandModel *model );
+ //! Returns the feature model used to generate the track attributes
+ FeatureModel *featureModel() const;
+ //! Sets the feature model used to generate the track attributes
+ void setFeatureModel( FeatureModel *featureModel );
//! Returns the minimum time interval constraint between each tracked point
double timeInterval() const { return mTimeInterval; }
//! Sets the minimum time interval constraint between each tracked point
- void setTimeInterval( const double timeInterval ) { mTimeInterval = timeInterval; }
+ void setTimeInterval( double timeInterval );
//! Returns the minimum distance constraint between each tracked point
double minimumDistance() const { return mMinimumDistance; }
//! Sets the minimum distance constraint between each tracked point
- void setMinimumDistance( const double minimumDistance ) { mMinimumDistance = minimumDistance; };
+ void setMinimumDistance( double minimumDistance );
//! Returns the maximum distance tolerated beyond which a position will be considered errenous
double maximumDistance() const { return mMaximumDistance; }
//! Sets the maximum distance tolerated beyond which a position will be considered errenous
- void setMaximumDistance( const double maximumDistance ) { mMaximumDistance = maximumDistance; };
+ void setMaximumDistance( double maximumDistance );
//! Returns if TRUE, newly captured sensor data is needed between each tracked point
bool sensorCapture() const { return mSensorCapture; }
//! Sets whether newly captured sensor data is needed between each tracked point
- void setSensorCapture( const bool capture ) { mSensorCapture = capture; }
+ void setSensorCapture( bool capture );
//! Returns TRUE if all constraints need to be fulfilled between each tracked point
bool conjunction() const { return mConjunction; }
//! Sets where all constraints need to be fulfilled between each tracked point
- void setConjunction( const bool conjunction ) { mConjunction = conjunction; }
+ void setConjunction( bool conjunction );
//! Returns the timestamp of the first recorded point
QDateTime startPositionTimestamp() const { return mStartPositionTimestamp; }
@@ -83,9 +112,9 @@ class Tracker : public QObject
void setStartPositionTimestamp( const QDateTime &startPositionTimestamp ) { mStartPositionTimestamp = startPositionTimestamp; }
//! Returns the current layer
- QgsVectorLayer *layer() const { return mLayer.data(); }
+ QgsVectorLayer *vectorLayer() const { return mVectorLayer.data(); }
//! Sets the current layer
- void setLayer( QgsVectorLayer *layer ) { mLayer = layer; }
+ void setVectorLayer( QgsVectorLayer *vectorLayer );
//! Returns the created feature
QgsFeature feature() const;
@@ -95,36 +124,64 @@ class Tracker : public QObject
//! Returns TRUE if the tracker rubberband is visible
bool visible() const { return mVisible; }
//! Sets whether the tracker rubberband is visible
- void setVisible( const bool visible ) { mVisible = visible; }
+ void setVisible( bool visible );
//! Returns the measure type used with the tracker geometry's M dimension when available
MeasureType measureType() const { return mMeasureType; }
//! Sets the measure type used with the tracker geometry's M dimension when available
- void setMeasureType( MeasureType type ) { mMeasureType = type; }
+ void setMeasureType( MeasureType type );
//! Returns whether the tracker has been started
bool isActive() const { return mIsActive; }
- void start();
+ //! Returns whether the tracker is replaying positions
+ bool isReplaying() const { return mIsReplaying; }
+
+ //! Starts tracking
+ void start( const GnssPositionInformation &positionInformation = GnssPositionInformation(), const QgsPoint &projectedPosition = QgsPoint() );
+ //! Stops tracking
void stop();
+ //! Process the given position information and projected position passed onto the tracker
+ Q_INVOKABLE void processPositionInformation( const GnssPositionInformation &positionInformation, const QgsPoint &projectedPosition );
+
+ //! Replays a list of position information taking into account the tracker settings
+ void replayPositionInformationList( const QList &positionInformationList, QgsQuickCoordinateTransformer *coordinateTransformer = nullptr );
+
signals:
- void startPositionTimestampChanged();
void isActiveChanged();
+ void isReplayingChanged();
+ void visibleChanged();
+ void vectorLayerChanged();
+ void rubberbandModelChanged();
+ void featureModelChanged();
+
+ void timeIntervalChanged();
+ void minimumDistanceChanged();
+ void maximumDistanceChanged();
+ void sensorCaptureChanged();
+ void conjunctionChanged();
+ void measureTypeChanged();
+
+ void featureCreated();
+ void featureChanged();
+
+ void startPositionTimestampChanged();
private slots:
void positionReceived();
- void timeReceived();
void sensorDataReceived();
+ void rubberbandModelVertexCountChanged();
private:
void trackPosition();
bool mIsActive = false;
+ bool mIsReplaying = false;
RubberbandModel *mRubberbandModel = nullptr;
+ FeatureModel *mFeatureModel = nullptr;
- QTimer mTimer;
double mTimeInterval = 0.0;
double mMinimumDistance = 0.0;
double mMaximumDistance = 0.0;
@@ -137,12 +194,14 @@ class Tracker : public QObject
bool mSensorCaptureFulfilled = false;
bool mSkipPositionReceived = false;
- QPointer mLayer;
+ QPointer mVectorLayer;
QgsFeature mFeature;
bool mVisible = true;
QDateTime mStartPositionTimestamp;
+ QDateTime mLastDevicePositionTimestamp;
+ QDateTime mLastVertexPositionTimestamp;
MeasureType mMeasureType = Tracker::SecondsSinceStart;
};
diff --git a/src/core/trackingmodel.cpp b/src/core/trackingmodel.cpp
index b3636582f2..9aa89422db 100644
--- a/src/core/trackingmodel.cpp
+++ b/src/core/trackingmodel.cpp
@@ -14,7 +14,6 @@
* *
***************************************************************************/
-#include "rubberbandmodel.h"
#include "trackingmodel.h"
#include
@@ -35,18 +34,7 @@ QHash TrackingModel::roleNames() const
QHash roles = QAbstractItemModel::roleNames();
roles[DisplayString] = "displayString";
- roles[VectorLayer] = "vectorLayer";
- roles[TimeInterval] = "timeInterval";
- roles[MinimumDistance] = "minimumDistance";
- roles[MaximumDistance] = "maximumDistance";
- roles[Conjunction] = "conjunction";
- roles[Feature] = "feature";
- roles[RubberModel] = "rubberModel";
- roles[Visible] = "visible";
- roles[StartPositionTimestamp] = "startPositionTimestamp";
- roles[MeasureType] = "measureType";
- roles[SensorCapture] = "sensorCapture";
- roles[IsActive] = "isActive";
+ roles[TrackerPointer] = "tracker";
return roles;
}
@@ -87,31 +75,9 @@ QVariant TrackingModel::data( const QModelIndex &index, int role ) const
switch ( role )
{
case DisplayString:
- return QString( "Tracker on layer %1" ).arg( tracker->layer()->name() );
- case VectorLayer:
- return QVariant::fromValue( tracker->layer() );
- case Feature:
- return QVariant::fromValue( tracker->feature() );
- case Visible:
- return tracker->visible();
- case StartPositionTimestamp:
- return tracker->startPositionTimestamp();
- case TimeInterval:
- return tracker->timeInterval();
- case MinimumDistance:
- return tracker->minimumDistance();
- case Conjunction:
- return tracker->conjunction();
- case RubberModel:
- return QVariant::fromValue( tracker->model() );
- case MeasureType:
- return tracker->measureType();
- case SensorCapture:
- return tracker->sensorCapture();
- case MaximumDistance:
- return tracker->maximumDistance();
- case IsActive:
- return tracker->isActive();
+ return QString( "Tracker on layer %1" ).arg( tracker->vectorLayer()->name() );
+ case TrackerPointer:
+ return QVariant::fromValue( tracker );
default:
return QVariant();
}
@@ -119,44 +85,7 @@ QVariant TrackingModel::data( const QModelIndex &index, int role ) const
bool TrackingModel::setData( const QModelIndex &index, const QVariant &value, int role )
{
- if ( index.row() < 0 || index.row() >= mTrackers.size() )
- return false;
-
- Tracker *tracker = mTrackers[index.row()];
- switch ( role )
- {
- case Feature:
- tracker->setFeature( value.value() );
- break;
- case Visible:
- tracker->setVisible( value.toBool() );
- break;
- case TimeInterval:
- tracker->setTimeInterval( value.toDouble() );
- break;
- case MinimumDistance:
- tracker->setMinimumDistance( value.toDouble() );
- break;
- case Conjunction:
- tracker->setConjunction( value.toBool() );
- break;
- case RubberModel:
- tracker->setModel( value.value() );
- break;
- case MeasureType:
- tracker->setMeasureType( static_cast( value.toInt() ) );
- break;
- case SensorCapture:
- tracker->setSensorCapture( value.toBool() );
- break;
- case MaximumDistance:
- tracker->setMaximumDistance( value.toDouble() );
- break;
- default:
- return false;
- }
- emit dataChanged( index, index, QVector() << role );
- return true;
+ return false;
}
bool TrackingModel::featureInTracking( QgsVectorLayer *layer, const QgsFeatureId featureId )
@@ -210,37 +139,49 @@ QModelIndex TrackingModel::createTracker( QgsVectorLayer *layer )
return index( mTrackers.size() - 1, 0 );
}
-void TrackingModel::startTracker( QgsVectorLayer *layer )
+void TrackingModel::startTracker( QgsVectorLayer *layer, const GnssPositionInformation &positionInformation, const QgsPoint &projectedPosition )
{
- int listIndex = trackerIterator( layer ) - mTrackers.constBegin();
- mTrackers[listIndex]->start();
-
- QModelIndex idx = index( listIndex, 0 );
- emit dataChanged( idx, idx, QVector() << TrackingModel::IsActive );
- emit layerInTrackingChanged( layer, true );
+ const int idx = trackerIterator( layer ) - mTrackers.constBegin();
+ if ( idx >= 0 )
+ {
+ mTrackers[idx]->start( positionInformation, projectedPosition );
+ emit layerInTrackingChanged( layer, true );
+ }
}
void TrackingModel::stopTracker( QgsVectorLayer *layer )
{
- int listIndex = trackerIterator( layer ) - mTrackers.constBegin();
- mTrackers[listIndex]->stop();
-
- QModelIndex idx = index( listIndex, 0 );
- emit dataChanged( idx, idx, QVector() << TrackingModel::IsActive );
+ const int idx = trackerIterator( layer ) - mTrackers.constBegin();
+ if ( idx >= 0 )
+ {
+ mTrackers[idx]->stop();
- beginRemoveRows( QModelIndex(), listIndex, listIndex );
- delete mTrackers.takeAt( listIndex );
- endRemoveRows();
+ beginRemoveRows( QModelIndex(), idx, idx );
+ Tracker *tracker = mTrackers.takeAt( idx );
+ endRemoveRows();
+ delete tracker;
+ emit layerInTrackingChanged( layer, false );
+ }
+}
- emit layerInTrackingChanged( layer, false );
+void TrackingModel::replayPositionInformationList( const QList &positionInformationList, QgsQuickCoordinateTransformer *coordinateTransformer )
+{
+ for ( int i = 0; i < mTrackers.size(); i++ )
+ {
+ Tracker *tracker = mTrackers[i];
+ if ( tracker->isActive() )
+ {
+ tracker->replayPositionInformationList( positionInformationList, coordinateTransformer );
+ }
+ }
}
void TrackingModel::setTrackerVisibility( QgsVectorLayer *layer, bool visible )
{
if ( trackerIterator( layer ) != mTrackers.constEnd() )
{
- int listIndex = trackerIterator( layer ) - mTrackers.constBegin();
- setData( index( listIndex, 0, QModelIndex() ), visible, Visible );
+ const int idx = trackerIterator( layer ) - mTrackers.constBegin();
+ mTrackers[idx]->setVisible( visible );
}
}
diff --git a/src/core/trackingmodel.h b/src/core/trackingmodel.h
index 8f15b198be..59600e1562 100644
--- a/src/core/trackingmodel.h
+++ b/src/core/trackingmodel.h
@@ -20,6 +20,7 @@
#include
+class QgsQuickCoordinateTransformer;
class RubberbandModel;
class Track;
@@ -37,18 +38,7 @@ class TrackingModel : public QAbstractItemModel
enum TrackingRoles
{
DisplayString = Qt::UserRole,
- VectorLayer, //! layer in the current tracking session
- RubberModel, //! rubberbandmodel used in the current tracking session
- TimeInterval, //! minimum time interval constraint between each tracked point
- MinimumDistance, //! minimum distance constraint between each tracked point
- Conjunction, //! if TRUE, all constraints needs to be fulfilled before tracking a point
- Visible, //! if TRUE, the tracking session rubberband is visible
- Feature, //! feature in the current tracking session
- StartPositionTimestamp, //! timestamp when the current tracking session started
- MeasureType, //! measurement type used to set the measure value
- SensorCapture, //! if TRUE, newly captured sensor data constraint will be required between each tracked point
- MaximumDistance, //! maximum distance tolerated beyond which a position will be considered errenous
- IsActive, //! if TRUE, the tracker has been started
+ TrackerPointer,
};
QHash roleNames() const override;
@@ -64,7 +54,7 @@ class TrackingModel : public QAbstractItemModel
//! Creates a tracking session for the provided vector \a layer.
Q_INVOKABLE QModelIndex createTracker( QgsVectorLayer *layer );
//! Starts tracking for the provided vector \a layer provided it has a tracking session created.
- Q_INVOKABLE void startTracker( QgsVectorLayer *layer );
+ Q_INVOKABLE void startTracker( QgsVectorLayer *layer, const GnssPositionInformation &positionInformation = GnssPositionInformation(), const QgsPoint &projectedPosition = QgsPoint() );
//! Stops the tracking session of the provided vector \a layer.
Q_INVOKABLE void stopTracker( QgsVectorLayer *layer );
//! Sets whether the tracking session rubber band is \a visible.
@@ -78,6 +68,9 @@ class TrackingModel : public QAbstractItemModel
//! Returns the tracker for the vector \a layer if a tracking session is present, otherwise returns NULLPTR.
Tracker *trackerForLayer( QgsVectorLayer *layer );
+ //! Replays a list of position information for all active trackers
+ Q_INVOKABLE void replayPositionInformationList( const QList &positionInformationList, QgsQuickCoordinateTransformer *coordinateTransformer = nullptr );
+
void reset();
/**
@@ -98,7 +91,7 @@ class TrackingModel : public QAbstractItemModel
QList mTrackers;
QList::const_iterator trackerIterator( QgsVectorLayer *layer )
{
- return std::find_if( mTrackers.constBegin(), mTrackers.constEnd(), [layer]( const Tracker *tracker ) { return tracker->layer() == layer; } );
+ return std::find_if( mTrackers.constBegin(), mTrackers.constEnd(), [layer]( const Tracker *tracker ) { return tracker->vectorLayer() == layer; } );
}
};
diff --git a/src/core/utils/geometryutils.h b/src/core/utils/geometryutils.h
index 7b87bf2443..429beb7587 100644
--- a/src/core/utils/geometryutils.h
+++ b/src/core/utils/geometryutils.h
@@ -100,8 +100,8 @@ class QFIELD_CORE_EXPORT GeometryUtils : public QObject
//! Returns an empty (i.e. null) point.
static Q_INVOKABLE QgsPoint emptyPoint() { return QgsPoint(); }
- //! Creates a point from \a x and \a y.
- static Q_INVOKABLE QgsPoint point( double x, double y ) { return QgsPoint( x, y ); }
+ //! Creates a point from \a x and \a y with optional \a z and \a values
+ static Q_INVOKABLE QgsPoint point( double x, double y, double z = std::numeric_limits::quiet_NaN(), double m = std::numeric_limits::quiet_NaN() ) { return QgsPoint( x, y, z, m ); }
//! Creates a centroid point from a given \a geometry.
static Q_INVOKABLE QgsPoint centroid( const QgsGeometry &geometry );
diff --git a/src/qml/Legend.qml b/src/qml/Legend.qml
index ee4730b169..ae98aec222 100644
--- a/src/qml/Legend.qml
+++ b/src/qml/Legend.qml
@@ -248,7 +248,12 @@ ListView {
icon.color: Theme.mainTextColor
onClicked: {
- displayToast(qsTr('This layer is is currently tracking the device position.'));
+ displayToast(qsTr('This layer is is currently tracking positions.'), 'info', qsTr('Stop'), function () {
+ if (trackingModel.layerInTracking(VectorLayerPointer)) {
+ trackingModel.stopTracker(VectorLayerPointer);
+ displayToast(qsTr('Track on layer %1 stopped').arg(VectorLayerPointer.name));
+ }
+ });
}
SequentialAnimation on bgcolor {
diff --git a/src/qml/TrackerSettings.qml b/src/qml/TrackerSettings.qml
index 620c1990bb..a8e82e0528 100644
--- a/src/qml/TrackerSettings.qml
+++ b/src/qml/TrackerSettings.qml
@@ -37,7 +37,7 @@ Popup {
if (embeddedAttributeFormModel.rowCount() > 0 && !featureModel.suppressFeatureForm()) {
embeddedFeatureForm.active = true;
} else {
- trackingModel.startTracker(tracker.vectorLayer);
+ trackingModel.startTracker(tracker.vectorLayer, positionSource.positionInformation, positionSource.projectedPosition);
displayToast(qsTr('Track on layer %1 started').arg(tracker.vectorLayer.name));
if (featureModel.currentLayer.geometryType === Qgis.GeometryType.Point) {
projectInfo.saveTracker(featureModel.currentLayer);
@@ -483,7 +483,7 @@ Popup {
if (embeddedAttributeFormModel.rowCount() > 0 && !featureModel.suppressFeatureForm()) {
embeddedFeatureForm.active = true;
} else {
- trackingModel.startTracker(tracker.vectorLayer);
+ trackingModel.startTracker(tracker.vectorLayer, positionSource.positionInformation, positionSource.projectedPosition);
displayToast(qsTr('Track on layer %1 started').arg(tracker.vectorLayer.name));
if (featureModel.currentLayer.geometryType === Qgis.GeometryType.Point) {
projectInfo.saveTracker(featureModel.currentLayer);
@@ -508,7 +508,7 @@ Popup {
onClicked: {
applySettings();
- trackingModel.startTracker(tracker.vectorLayer);
+ trackingModel.startTracker(tracker.vectorLayer, positionSource.positionInformation, positionSource.projectedPosition);
displayToast(qsTr('Track on layer %1 started').arg(tracker.vectorLayer.name));
projectInfo.saveTracker(featureModel.currentLayer);
trackerSettings.close();
@@ -577,7 +577,7 @@ Popup {
tracker.feature = featureModel.feature;
embeddedFeatureFormPopup.close();
embeddedFeatureForm.active = false;
- trackingModel.startTracker(tracker.vectorLayer);
+ trackingModel.startTracker(tracker.vectorLayer, positionSource.positionInformation, positionSource.projectedPosition);
displayToast(qsTr('Track on layer %1 started').arg(tracker.vectorLayer.name));
if (featureModel.currentLayer.geometryType === Qgis.GeometryType.Point) {
projectInfo.saveTracker(featureModel.currentLayer);
diff --git a/src/qml/TrackingSession.qml b/src/qml/TrackingSession.qml
index ee541e8835..2c52f9ed6d 100644
--- a/src/qml/TrackingSession.qml
+++ b/src/qml/TrackingSession.qml
@@ -11,72 +11,40 @@ import Theme
Item {
id: trackingSession
- property var tracker: model
+ property var tracker: model.tracker
Component.onCompleted: {
- tracker.rubberModel = rubberbandModel;
+ tracker.rubberbandModel = rubberbandModel;
+ tracker.featureModel = featureModel;
}
- RubberbandModel {
- id: rubberbandModel
- frozen: false
- vectorLayer: tracker.vectorLayer
- currentCoordinate: positionSource.projectedPosition
+ Connections {
+ target: positionSource
+ enabled: tracker.isActive
- property int measureType: tracker.measureType
- measureValue: {
- switch (measureType) {
- case Tracker.SecondsSinceStart:
- return (positionSource.positionInformation.utcDateTime - tracker.startPositionTimestamp) / 1000;
- case Tracker.Timestamp:
- return positionSource.positionInformation.utcDateTime.getTime();
- case Tracker.GroundSpeed:
- return positionSource.positionInformation.speed;
- case Tracker.Bearing:
- return positionSource.positionInformation.direction;
- case Tracker.HorizontalAccuracy:
- return positionSource.positionInformation.hacc;
- case Tracker.VerticalAccuracy:
- return positionSource.positionInformation.vacc;
- case Tracker.PDOP:
- return positionSource.positionInformation.pdop;
- case Tracker.HDOP:
- return positionSource.positionInformation.hdop;
- case Tracker.VDOP:
- return positionSource.positionInformation.vdop;
- }
- return 0;
+ function onPositionInformationChanged() {
+ featureModel.positionInformation = positionSource.positionInformation;
+ tracker.processPositionInformation(positionSource.positionInformation, positionSource.projectedPosition);
}
+ }
- currentPositionTimestamp: positionSource.positionInformation.utcDateTime
- crs: mapCanvas.mapSettings.destinationCrs
+ Connections {
+ target: tracker
- onVertexCountChanged: {
- if (!tracker.isActive || vertexCount == 0) {
- return;
- }
- if (geometryType === Qgis.GeometryType.Point) {
- featureModel.applyGeometry();
- featureModel.resetFeatureId();
- featureModel.resetAttributes(true);
- featureModel.create();
- } else {
- if ((geometryType === Qgis.GeometryType.Line && vertexCount > 2) || (geometryType === Qgis.GeometryType.Polygon && vertexCount > 3)) {
- featureModel.applyGeometry();
- if ((geometryType === Qgis.GeometryType.Line && vertexCount == 3) || (geometryType === Qgis.GeometryType.Polygon && vertexCount == 4)) {
- // indirect action, no need to check for success and display a toast, the log is enough
- featureModel.create();
- tracker.feature = featureModel.feature;
- projectInfo.saveTracker(featureModel.currentLayer);
- } else {
- // indirect action, no need to check for success and display a toast, the log is enough
- featureModel.save();
- }
- }
+ function onFeatureCreated() {
+ if (tracker.isActive) {
+ projectInfo.saveTracker(featureModel.currentLayer);
}
}
}
+ RubberbandModel {
+ id: rubberbandModel
+ frozen: false
+ vectorLayer: tracker.vectorLayer
+ crs: mapCanvas.mapSettings.destinationCrs
+ }
+
Rubberband {
id: rubberband
visible: tracker.visible
@@ -95,7 +63,7 @@ Item {
feature: tracker.feature
onFeatureChanged: {
- if (!tracker.isActive) {
+ if (!tracker.isActive && !tracker.isReplaying) {
updateRubberband();
}
}
@@ -106,7 +74,6 @@ Item {
vectorLayer: tracker.vectorLayer
}
- positionInformation: coordinateLocator.positionInformation
positionLocked: true
cloudUserInformation: projectInfo.cloudUserInformation
}
diff --git a/src/qml/qgismobileapp.qml b/src/qml/qgismobileapp.qml
index 71650207b9..a069f25d71 100644
--- a/src/qml/qgismobileapp.qml
+++ b/src/qml/qgismobileapp.qml
@@ -251,7 +251,7 @@ ApplicationWindow {
antennaHeight: positioningSettings.antennaHeightActivated ? positioningSettings.antennaHeight : 0
logging: positioningSettings.logging
- onProjectedPositionChanged: {
+ onPositionInformationChanged: {
if (active) {
bearingTrueNorth = PositioningUtils.bearingTrueNorth(positionSource.projectedPosition, mapCanvas.mapSettings.destinationCrs);
if (gnssButton.followActive) {
@@ -269,6 +269,18 @@ ApplicationWindow {
onDeviceLastErrorChanged: {
displayToast(qsTr('Positioning device error: %1').arg(positionSource.deviceLastError), 'error');
}
+
+ onBackgroundModeChanged: {
+ if (!backgroundMode) {
+ console.log('qqq onBackgroundModeChanged');
+ mapCanvasMap.freeze('trackerreplay');
+ let list = positionSource.getBackgroundPositionInformation();
+ // Qt bug weirdly returns an empty list on first invokation to source, call twice to insure we've got the actual list
+ list = positionSource.getBackgroundPositionInformation();
+ trackingModel.replayPositionInformationList(list, coordinateTransformer);
+ mapCanvasMap.unfreeze('trackerreplay');
+ }
+ }
}
PositioningSettings {
@@ -1833,16 +1845,6 @@ ApplicationWindow {
anchors.right: parent.right
- onIconSourceChanged: {
- if (state === "On") {
- if (positionSource.positionInformation && positionSource.positionInformation.latitudeValid) {
- displayToast(qsTr("Received position"));
- } else {
- displayToast(qsTr("Searching for position"));
- }
- }
- }
-
/*
/ When set to true, the map will follow the device's current position; the map
/ will stop following the position whe the user manually drag the map.
diff --git a/src/service/qfieldpositioningservice.cpp b/src/service/qfieldpositioningservice.cpp
index 297fb03361..88f9154184 100644
--- a/src/service/qfieldpositioningservice.cpp
+++ b/src/service/qfieldpositioningservice.cpp
@@ -28,8 +28,10 @@
QFieldPositioningService::QFieldPositioningService( int &argc, char **argv )
: QAndroidService( argc, argv )
{
+ qRegisterMetaType( "GnssPositionInformation" );
+
mPositioningSource = new PositioningSource( this );
- mHost.setHostUrl( QUrl( QStringLiteral( "localabstract:replica" ) ) );
+ mHost.setHostUrl( QUrl( QStringLiteral( "localabstract:" APP_PACKAGE_NAME "replica" ) ) );
mHost.enableRemoting( mPositioningSource, "PositioningSource" );
mNotificationTimer.setInterval( 1000 );
@@ -44,15 +46,33 @@ QFieldPositioningService::QFieldPositioningService( int &argc, char **argv )
} );
connect( mPositioningSource, &PositioningSource::backgroundModeChanged, this, [=] {
- if ( mPositioningSource->backgroundMode() && mPositioningSource->active() )
+ if ( mPositioningSource->active() )
+ {
+ if ( mPositioningSource->backgroundMode() )
+ {
+ triggerShowNotification();
+ mNotificationTimer.start();
+ }
+ else
+ {
+ mNotificationTimer.stop();
+ triggerReturnNotification();
+ }
+ }
+ } );
+
+ connect( mPositioningSource, &PositioningSource::activeChanged, this, [=] {
+ if ( mPositioningSource->active() )
{
- triggerShowNotification();
- mNotificationTimer.start();
+ if ( mPositioningSource->backgroundMode() )
+ {
+ triggerShowNotification();
+ mNotificationTimer.start();
+ }
}
else
{
mNotificationTimer.stop();
- triggerCloseNotification();
}
} );
}
@@ -60,18 +80,20 @@ QFieldPositioningService::QFieldPositioningService( int &argc, char **argv )
void QFieldPositioningService::triggerShowNotification()
{
const GnssPositionInformation pos = mPositioningSource->positionInformation();
- QJniObject message = QJniObject::fromString( tr( "Latitude %1 | Longitude %2 | Altitude %3 | Orientation %4" ).arg( QLocale::system().toString( pos.latitude(), 'f', 7 ), QLocale::system().toString( pos.longitude(), 'f', 7 ), QLocale::system().toString( pos.elevation(), 'f', 3 ), QLocale::system().toString( mPositioningSource->orientation(), 'f', 1 ) ) );
+ QJniObject message = QJniObject::fromString( tr( "Latitude %1 | Longitude %2 | Altitude %3 m | Speed %4 m/s | Direction %5°" ).arg( QLocale::system().toString( pos.latitude(), 'f', 7 ), QLocale::system().toString( pos.longitude(), 'f', 7 ), QLocale::system().toString( pos.elevation(), 'f', 3 ), QLocale::system().toString( pos.speed(), 'f', 1 ), QLocale::system().toString( pos.direction(), 'f', 1 ) ) );
QJniObject::callStaticMethod( "ch/opengis/" APP_PACKAGE_NAME "/QFieldPositioningService",
"triggerShowNotification",
- message.object() );
+ message.object(),
+ true );
}
-void QFieldPositioningService::triggerCloseNotification()
+void QFieldPositioningService::triggerReturnNotification()
{
QJniObject message = QJniObject::fromString( tr( "Positioning service running" ) );
QJniObject::callStaticMethod( "ch/opengis/" APP_PACKAGE_NAME "/QFieldPositioningService",
"triggerShowNotification",
- message.object() );
+ message.object(),
+ false );
}
QFieldPositioningService::~QFieldPositioningService()
diff --git a/src/service/qfieldpositioningservice.h b/src/service/qfieldpositioningservice.h
index d0fd83cc9f..b863a3b150 100644
--- a/src/service/qfieldpositioningservice.h
+++ b/src/service/qfieldpositioningservice.h
@@ -36,7 +36,7 @@ class QFIELD_SERVICE_EXPORT QFieldPositioningService : public QAndroidService
private slots:
void triggerShowNotification();
- void triggerCloseNotification();
+ void triggerReturnNotification();
private:
PositioningSource *mPositioningSource = nullptr;