Skip to content

Commit

Permalink
Add map layer action for current GPS feature destination layer
Browse files Browse the repository at this point in the history
for "Update GPS Information"

Triggering this action from the attribute dialog for a newly
created feature in the GPS destination layer causes the geometry
of that feature to be refreshed with the CURRENT GPS location.

Designed for situations where populating the form attributes
can be a lengthy process and in the meantime the GPS receiver
has moved, and a user needs to re-sync the location of the
new feature back to the GPS location before saving.
  • Loading branch information
nyalldawson committed Nov 23, 2022
1 parent c15b960 commit 7a2598d
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 1 deletion.
88 changes: 88 additions & 0 deletions src/app/gps/qgsappgpsdigitizing.cpp
Expand Up @@ -26,14 +26,95 @@
#include "qgsgpsconnection.h"
#include "qgsappgpsconnection.h"
#include "qgsprojectgpssettings.h"
#include "qgsapplication.h"
#include "qgsattributeform.h"
#include "qgsattributedialog.h"
#include "qgshighlight.h"
#include "qgsidentifymenu.h"

#include <QTimeZone>

QgsUpdateGpsDetailsAction::QgsUpdateGpsDetailsAction( QgsAppGpsConnection *connection, QgsAppGpsDigitizing *digitizing, QObject *parent )
: QgsMapLayerAction( tr( "Update GPS Information" ), parent, QgsMapLayerAction::Target::SingleFeature, QgsApplication::getThemeIcon( QStringLiteral( "/gpsicons/mActionRecenter.svg" ) ) )
, mConnection( connection )
, mDigitizing( digitizing )
{

}

bool QgsUpdateGpsDetailsAction::canRunUsingLayer( QgsMapLayer * ) const
{
return false;
}

bool QgsUpdateGpsDetailsAction::canRunUsingLayer( QgsMapLayer *layer, const QgsMapLayerActionContext &context ) const
{
return mConnection && mConnection->isConnected() && context.attributeDialog() && context.attributeDialog()->attributeForm()->mode() == QgsAttributeEditorContext::Mode::AddFeatureMode
&& layer == QgsProject::instance()->gpsSettings()->destinationLayer();
}

void QgsUpdateGpsDetailsAction::triggerForFeature( QgsMapLayer *layer, const QgsFeature &, const QgsMapLayerActionContext &context )
{
QgsVectorLayer *vlayer = QgsProject::instance()->gpsSettings()->destinationLayer();
if ( !vlayer || ! mConnection || !mConnection->isConnected()
|| layer != vlayer )
return;

QgsAttributeDialog *dialog = context.attributeDialog();
if ( !dialog )
return;

QgsAttributeForm *form = dialog->attributeForm();
if ( !form )
return;

QString error;
const QgsGeometry resultWgs84 = mDigitizing->currentGeometry( vlayer->wkbType(), error );
if ( !error.isEmpty() )
{
if ( QgsMessageBar *messageBar = context.messageBar() )
messageBar->pushWarning( QString(), error );
return;
}

const QgsCoordinateTransform wgs84ToLayerTransform( QgsCoordinateReferenceSystem( "EPSG:4326" ), vlayer->crs(), QgsProject::instance() );
QgsGeometry geometryLayerCrs;
try
{
geometryLayerCrs = resultWgs84;
geometryLayerCrs.transform( wgs84ToLayerTransform );
}
catch ( QgsCsException & )
{
if ( QgsMessageBar *messageBar = context.messageBar() )
messageBar->pushCritical( QString(),
tr( "Error reprojecting GPS location to layer CRS." ) );
return;
}

if ( !geometryLayerCrs.isNull() )
{
form->changeGeometry( geometryLayerCrs );
QgsHighlight *highlight = new QgsHighlight( mDigitizing->canvas(), geometryLayerCrs, vlayer );
highlight->applyDefaultStyle();
highlight->mPointSizeRadiusMM = 1;
highlight->mPointSymbol = QgsHighlight::PointSymbol::Circle;
dialog->setHighlight( highlight );

if ( QgsMessageBar *messageBar = context.messageBar() )
messageBar->pushSuccess( QString(), tr( "Updated feature location from GPS." ) );
}
}


QgsAppGpsDigitizing::QgsAppGpsDigitizing( QgsAppGpsConnection *connection, QgsMapCanvas *canvas, QObject *parent )
: QgsGpsLogger( nullptr, parent )
, mConnection( connection )
, mCanvas( canvas )
{
mUpdateGpsDetailsAction = new QgsUpdateGpsDetailsAction( mConnection, this, this );
QgsGui::mapLayerActionRegistry()->addMapLayerAction( mUpdateGpsDetailsAction );

mCanvasToWgs84Transform = QgsCoordinateTransform( mCanvas->mapSettings().destinationCrs(), mWgs84CRS, QgsProject::instance() );
connect( mCanvas, &QgsMapCanvas::destinationCrsChanged, this, [ = ]
{
Expand Down Expand Up @@ -90,10 +171,17 @@ QgsAppGpsDigitizing::QgsAppGpsDigitizing( QgsAppGpsConnection *connection, QgsMa

QgsAppGpsDigitizing::~QgsAppGpsDigitizing()
{
QgsGui::mapLayerActionRegistry()->removeMapLayerAction( mUpdateGpsDetailsAction );

delete mRubberBand;
mRubberBand = nullptr;
}

QgsMapCanvas *QgsAppGpsDigitizing::canvas()
{
return mCanvas;
}

void QgsAppGpsDigitizing::addVertex( const QgsPoint &wgs84Point )
{
if ( !mRubberBand )
Expand Down
24 changes: 24 additions & 0 deletions src/app/gps/qgsappgpsdigitizing.h
Expand Up @@ -22,6 +22,8 @@
#include "qgis_app.h"
#include "qgssettingsentryimpl.h"
#include "qgsgpslogger.h"
#include "qgsmaplayeractionregistry.h"
#include "qgswkbtypes.h"

class QgsAppGpsConnection;
class QgsMapCanvas;
Expand All @@ -30,6 +32,24 @@ class QgsPoint;
class QgsGpsInformation;
class QgsVectorLayer;

class QgsAppGpsDigitizing;

class QgsUpdateGpsDetailsAction : public QgsMapLayerAction
{
Q_OBJECT

public:

QgsUpdateGpsDetailsAction( QgsAppGpsConnection *connection, QgsAppGpsDigitizing *digitizing, QObject *parent );
bool canRunUsingLayer( QgsMapLayer *layer ) const override;
bool canRunUsingLayer( QgsMapLayer *layer, const QgsMapLayerActionContext &context ) const override;
void triggerForFeature( QgsMapLayer *layer, const QgsFeature &feature, const QgsMapLayerActionContext &context ) override;
private:
QgsAppGpsConnection *mConnection = nullptr;
QgsAppGpsDigitizing *mDigitizing = nullptr;

};

class APP_EXPORT QgsAppGpsDigitizing: public QgsGpsLogger
{
Q_OBJECT
Expand All @@ -41,6 +61,8 @@ class APP_EXPORT QgsAppGpsDigitizing: public QgsGpsLogger
QgsAppGpsDigitizing( QgsAppGpsConnection *connection, QgsMapCanvas *canvas, QObject *parent = nullptr );
~QgsAppGpsDigitizing() override;

QgsMapCanvas *canvas();

public slots:
void createFeature();
void createVertexAtCurrentLocation();
Expand All @@ -65,6 +87,8 @@ class APP_EXPORT QgsAppGpsDigitizing: public QgsGpsLogger

QgsCoordinateTransform mCanvasToWgs84Transform;

QgsUpdateGpsDetailsAction *mUpdateGpsDetailsAction = nullptr;

friend class TestQgsGpsIntegration;
};

Expand Down
2 changes: 1 addition & 1 deletion src/core/gps/qgsgpslogger.h
Expand Up @@ -227,7 +227,7 @@ class CORE_EXPORT QgsGpsLogger : public QObject
QgsCoordinateReferenceSystem mWgs84CRS;

//! Used to pause logging of incoming GPS messages
int mBlockGpsStateChanged = 0;
mutable int mBlockGpsStateChanged = 0;

/**
* Adds a track vertex at the current GPS location.
Expand Down
1 change: 1 addition & 0 deletions src/gui/qgshighlight.h
Expand Up @@ -220,6 +220,7 @@ class GUI_EXPORT QgsHighlight : public QgsMapCanvasItem

// we don't want to make PointSymbol public for now, so just grant access selectively via a friend
friend class QgsMapToolAddFeature;
friend class QgsUpdateGpsDetailsAction;
double mPointSizeRadiusMM = 1.5;
PointSymbol mPointSymbol = PointSymbol::Square;
};
Expand Down

0 comments on commit 7a2598d

Please sign in to comment.