Skip to content

Commit

Permalink
Refactor to move gps logged positions to QgsGeometry method out
Browse files Browse the repository at this point in the history
to base class
  • Loading branch information
nyalldawson committed Nov 23, 2022
1 parent ff03e4c commit 4afcddf
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 98 deletions.
12 changes: 12 additions & 0 deletions python/core/auto_generated/gps/qgsgpslogger.sip.in
Expand Up @@ -84,6 +84,18 @@ Returns the recorded points in the current track.
These points will always be in WGS84 coordinate reference system.
%End

QgsGeometry currentGeometry( QgsWkbTypes::Type type, QString &error /Out/ ) const;
%Docstring
Returns the current logged GPS positions as a geometry of the specified ``type``.

The returned geometries will always be in the WGS84 (EPSG:4326) coordinate reference system.

:param type: desired geometry type

:return: - logged GPS positions as a geometry.
- error: Will be set to a user-friendly error if the logged positions could not be converted to an appropriate geometry
%End

QgsPointXY lastPosition() const;
%Docstring
Returns the last recorded position of the device.
Expand Down
140 changes: 42 additions & 98 deletions src/app/gps/qgsappgpsdigitizing.cpp
Expand Up @@ -136,19 +136,51 @@ void QgsAppGpsDigitizing::createFeature()
if ( !vlayer )
return;

const QVector< QgsPoint > captureListWgs84 = currentTrack();
if ( vlayer->geometryType() == QgsWkbTypes::LineGeometry && captureListWgs84.size() < 2 )
QString error;
const QgsGeometry resultWgs84 = QgsGpsLogger::currentGeometry( vlayer->wkbType(), error );
if ( !error.isEmpty() )
{
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ), tr( "Creating a line feature requires a track with at least two vertices." ) );
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ), error );
return;
}
else if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry && captureListWgs84.size() < 3 )

const QgsCoordinateTransform wgs84ToLayerTransform( mWgs84CRS, vlayer->crs(), QgsProject::instance() );
QgsGeometry geometryLayerCrs;
try
{
geometryLayerCrs = resultWgs84;
geometryLayerCrs.transform( wgs84ToLayerTransform );
}
catch ( QgsCsException & )
{
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ),
tr( "Creating a polygon feature requires a track with at least three vertices." ) );
QgisApp::instance()->messageBar()->pushCritical( tr( "Add Feature" ),
tr( "Error reprojecting feature to layer CRS." ) );
return;
}

if ( geometryLayerCrs.type() == QgsWkbTypes::PolygonGeometry )
{
const int avoidIntersectionsReturn = geometryLayerCrs.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
if ( avoidIntersectionsReturn == 1 )
{
//not a polygon type. Impossible to get there
}
else if ( avoidIntersectionsReturn == 2 )
{
//bail out...
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type." ) );
return;
}
else if ( avoidIntersectionsReturn == 3 )
{
QgisApp::instance()->messageBar()->pushCritical( tr( "Add Feature" ), tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ) );
return;
}
}

if ( geometryLayerCrs.isNull() )
return;

if ( !vlayer->isEditable() )
{
if ( vlayer->startEditing() )
Expand Down Expand Up @@ -177,35 +209,13 @@ void QgsAppGpsDigitizing::createFeature()
}
}

const QgsCoordinateTransform wgs84ToLayerTransform( mWgs84CRS, vlayer->crs(), QgsProject::instance() );
const bool is3D = QgsWkbTypes::hasZ( vlayer->wkbType() );
QgsFeature f;
f.setGeometry( geometryLayerCrs );

switch ( vlayer->geometryType() )
{
case QgsWkbTypes::PointGeometry:
{
QgsFeature f;
try
{
const QgsPointXY pointXYLayerCrs = wgs84ToLayerTransform.transform( lastPosition() );

QgsGeometry g;
if ( is3D )
g = QgsGeometry( new QgsPoint( pointXYLayerCrs.x(), pointXYLayerCrs.y(), lastElevation() ) );
else
g = QgsGeometry::fromPointXY( pointXYLayerCrs );

if ( QgsWkbTypes::isMultiType( vlayer->wkbType() ) )
g.convertToMultiType();

f.setGeometry( g );
}
catch ( QgsCsException & )
{
QgisApp::instance()->messageBar()->pushCritical( tr( "Add Feature" ),
tr( "Error reprojecting feature to layer CRS." ) );
return;
}

QgsFeatureAction action( tr( "Feature Added" ), f, vlayer, QUuid(), -1, this );
if ( action.addFeature( attrMap ) )
{
Expand Down Expand Up @@ -237,73 +247,6 @@ void QgsAppGpsDigitizing::createFeature()
{
mBlockGpsStateChanged++;

QgsFeature f;
QgsGeometry g;

std::unique_ptr<QgsLineString> ringWgs84( new QgsLineString( captureListWgs84 ) );
if ( ! is3D )
ringWgs84->dropZValue();

if ( vlayer->geometryType() == QgsWkbTypes::LineGeometry )
{

g = QgsGeometry( ringWgs84.release() );
try
{
g.transform( wgs84ToLayerTransform );
}
catch ( QgsCsException & )
{
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ),
tr( "Error reprojecting feature to layer CRS." ) );
return;
}
if ( QgsWkbTypes::isMultiType( vlayer->wkbType() ) )
g.convertToMultiType();
}
else if ( vlayer->geometryType() == QgsWkbTypes::PolygonGeometry )
{
ringWgs84->close();
std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
polygon->setExteriorRing( ringWgs84.release() );

g = QgsGeometry( polygon.release() );
try
{
g.transform( wgs84ToLayerTransform );
}
catch ( QgsCsException & )
{
mBlockGpsStateChanged--;
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ),
tr( "Error reprojecting feature to layer CRS." ) );
return;
}

if ( QgsWkbTypes::isMultiType( vlayer->wkbType() ) )
g.convertToMultiType();

const int avoidIntersectionsReturn = g.avoidIntersections( QgsProject::instance()->avoidIntersectionsLayers() );
if ( avoidIntersectionsReturn == 1 )
{
//not a polygon type. Impossible to get there
}
else if ( avoidIntersectionsReturn == 2 )
{
//bail out...
QgisApp::instance()->messageBar()->pushWarning( tr( "Add Feature" ), tr( "The feature could not be added because removing the polygon intersections would change the geometry type." ) );
mBlockGpsStateChanged--;
return;
}
else if ( avoidIntersectionsReturn == 3 )
{
QgisApp::instance()->messageBar()->pushCritical( tr( "Add Feature" ), tr( "The feature has been added, but at least one geometry intersected is invalid. These geometries must be manually repaired." ) );
mBlockGpsStateChanged--;
return;
}
}

f.setGeometry( g );
QgsFeatureAction action( tr( "Feature added" ), f, vlayer, QUuid(), -1, this );

if ( action.addFeature( attrMap ) )
Expand Down Expand Up @@ -418,3 +361,4 @@ QVariant QgsAppGpsDigitizing::timestamp( QgsVectorLayer *vlayer, int idx )
}
return value;
}

72 changes: 72 additions & 0 deletions src/core/gps/qgsgpslogger.cpp
Expand Up @@ -17,6 +17,8 @@
#include "qgsgpsconnection.h"
#include "gmath.h"
#include "qgsgeometry.h"
#include "qgslinestring.h"
#include "qgspolygon.h"

#include <QTimer>
#include <QTimeZone>
Expand Down Expand Up @@ -92,6 +94,76 @@ QVector<QgsPoint> QgsGpsLogger::currentTrack() const
return mCaptureListWgs84;
}

QgsGeometry QgsGpsLogger::currentGeometry( QgsWkbTypes::Type type, QString &error ) const
{
const QgsWkbTypes::GeometryType geometryType = QgsWkbTypes::geometryType( type );
const QVector< QgsPoint > captureListWgs84 = currentTrack();
if ( geometryType == QgsWkbTypes::LineGeometry && captureListWgs84.size() < 2 )
{
error = tr( "Creating a line feature requires a track with at least two vertices." );
return QgsGeometry();
}
else if ( geometryType == QgsWkbTypes::PolygonGeometry && captureListWgs84.size() < 3 )
{
error = tr( "Creating a polygon feature requires a track with at least three vertices." );
return QgsGeometry();
}

const bool is3D = QgsWkbTypes::hasZ( type );
switch ( geometryType )
{
case QgsWkbTypes::PointGeometry:
{
const QgsPointXY pointXYWgs84 = lastPosition();

QgsGeometry g;
if ( is3D )
g = QgsGeometry( new QgsPoint( pointXYWgs84.x(), pointXYWgs84.y(), lastElevation() ) );
else
g = QgsGeometry::fromPointXY( pointXYWgs84 );

if ( QgsWkbTypes::isMultiType( type ) )
g.convertToMultiType();

return g;
}

case QgsWkbTypes::LineGeometry:
case QgsWkbTypes::PolygonGeometry:
{
QgsGeometry g;

std::unique_ptr<QgsLineString> ringWgs84( new QgsLineString( captureListWgs84 ) );
if ( ! is3D )
ringWgs84->dropZValue();

if ( geometryType == QgsWkbTypes::LineGeometry )
{
g = QgsGeometry( ringWgs84.release() );
if ( QgsWkbTypes::isMultiType( type ) )
g.convertToMultiType();
}
else if ( geometryType == QgsWkbTypes::PolygonGeometry )
{
ringWgs84->close();
std::unique_ptr<QgsPolygon> polygon( new QgsPolygon() );
polygon->setExteriorRing( ringWgs84.release() );

g = QgsGeometry( polygon.release() );

if ( QgsWkbTypes::isMultiType( type ) )
g.convertToMultiType();
}
return g;
}

case QgsWkbTypes::NullGeometry:
case QgsWkbTypes::UnknownGeometry:
break;
}
return QgsGeometry();
}

QgsPointXY QgsGpsLogger::lastPosition() const
{
return mLastGpsPositionWgs84;
Expand Down
13 changes: 13 additions & 0 deletions src/core/gps/qgsgpslogger.h
Expand Up @@ -22,6 +22,7 @@
#include "qgscoordinatereferencesystem.h"
#include "qgsdistancearea.h"
#include "qgscoordinatetransformcontext.h"
#include "qgswkbtypes.h"

#include <QObject>
#include <QPointer>
Expand Down Expand Up @@ -106,6 +107,18 @@ class CORE_EXPORT QgsGpsLogger : public QObject
*/
QVector<QgsPoint> currentTrack() const;

/**
* Returns the current logged GPS positions as a geometry of the specified \a type.
*
* The returned geometries will always be in the WGS84 (EPSG:4326) coordinate reference system.
*
* \param type desired geometry type
* \param error Will be set to a user-friendly error if the logged positions could not be converted to an appropriate geometry
*
* \returns logged GPS positions as a geometry.
*/
QgsGeometry currentGeometry( QgsWkbTypes::Type type, QString &error SIP_OUT ) const;

/**
* Returns the last recorded position of the device.
*
Expand Down

0 comments on commit 4afcddf

Please sign in to comment.