Skip to content

Commit c42dd4e

Browse files
WithaliontomasMizerakaustuvpokharelgabriel-bolbotina
authored
GNSS enhancements bundle (#4234)
* Geoid separation support for external providers (#4176) * Recalculate ellipsoid elevation to orthometric (#4210) * Enhance position altitude processing Add EGM96_15 geoid model, which recalculates ellipsoid altitudes returned by position providers. Expose this information in GPS information panel. * Fix broken builds * Fix formatting * Add geoid info for iOS * Refactor PositionKit to singleton from context property * Patch ios internal positioning provider Provider returns now WGS84 ellipsoidal height on iOS * Fix elevation transform & android workaround Create new 3D transform utils function. Fix coordinate order passing. Rework android 15+ Qt positioning workaround to VCPKG patch. * Clean up & format code * Add patch TODO * Fix some review issues * Refactor code to use existing QgsCoordinateTransformContext (#4228) * Geoid separation support for internal providers (#4216) * Squashed commit of the following: commit 27b19a4 Author: Matej Bagar <matej.bagar@lutraconsulting.co.uk> Date: Mon Nov 24 15:22:46 2025 +0200 Add mock location detection for android position provider * Change default value for elevation_diff * Custom geoid support (#4238) * Fixed plural translation forms (#4183) * Fixed android build to sync projects (#4243) * Refactor support for orthometric heights on iOS * Subtract antenna height from altitude * Fix formatting & vcpkg bug * Fix ios patch * Fix ios patch v2 * Fix iOS error & constness * Fix iOS error v3 * Add ellipsoidal elevation expr variable * Change elevation calculation * Increase click area for elevation info button * Use default transform for internal providers * Use default geoid model for android fused provider * Add vertical CRS transform pass through for mock providers * Add vertical CRS transfrom pass through for bluetooth provider * Change android fused provider to default on android * Fix GPS panel geoid model name info * Change behaviour for internal provider on desktops * Use EGM96 geoid model for simulated provider * Fix iOS build * Small refactor * Set internal providers to use mixture of vertical CRS * Fix review issues * Refactor providers to use PositionTransformer * Add PositionTransform reload on project load * Add unit tests for PositionTransformer * Rename project setting name * Remove commented code, minor typo fixes, simulated provider cleanup * Fix tests & formatting * Fix tests * Add test debug logs for CI * Fix wrong test initialization for bluetoothless devices * Revert "Fix wrong test initialization for bluetoothless devices" This reverts commit 1541538. * Refactor PositionTransformer creation & clear QgsProject refs in PositionKit * Remove InputCoordinateTransformer * Fix review issues * Fix function typo --------- Co-authored-by: Tomas Mizera <tomas.mizera@lutraconsulting.co.uk> Co-authored-by: Kaustuv Pokharel <85729205+kaustuvpokharel@users.noreply.github.com> Co-authored-by: Gabriel Bolbotina <80618569+gabriel-bolbotina@users.noreply.github.com>
1 parent fed97d3 commit c42dd4e

47 files changed

Lines changed: 1203 additions & 489 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,9 @@ file(
389389
PATTERN "*.cmake" EXCLUDE
390390
PATTERN "vcpkg*" EXCLUDE
391391
)
392+
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/app/assets/us_nga_egm96_15.tif
393+
DESTINATION ${ASSETS_DIR_PATH}/app/android/assets/qgis-data/proj
394+
)
392395

393396
message(STATUS "QGIS_QUICK_DATA_PATH set to ${QGIS_QUICK_DATA_PATH}")
394397
# ########################################################################################

app/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ set(MM_SRCS
2121
layer/layertreemodel.cpp
2222
layer/layertreemodelpixmapprovider.cpp
2323
layer/layertreesortfiltermodel.cpp
24-
map/inputcoordinatetransformer.cpp
2524
map/inputmapcanvasmap.cpp
2625
map/inputmapsettings.cpp
2726
map/inputmaptransform.cpp
@@ -43,6 +42,7 @@ set(MM_SRCS
4342
position/mapposition.cpp
4443
position/positiondirection.cpp
4544
position/positionkit.cpp
45+
position/positiontransformer.cpp
4646
activelayer.cpp
4747
activeproject.cpp
4848
androidutils.cpp
@@ -111,7 +111,6 @@ set(MM_HDRS
111111
layer/layertreemodel.h
112112
layer/layertreemodelpixmapprovider.h
113113
layer/layertreesortfiltermodel.h
114-
map/inputcoordinatetransformer.h
115114
map/inputmapcanvasmap.h
116115
map/inputmapsettings.h
117116
map/inputmaptransform.h
@@ -133,6 +132,7 @@ set(MM_HDRS
133132
position/mapposition.h
134133
position/positiondirection.h
135134
position/positionkit.h
135+
position/positiontransformer.h
136136
activelayer.h
137137
activeproject.h
138138
androidutils.h

app/assets/us_nga_egm96_15.tif

2.59 MB
Binary file not shown.

app/inpututils.cpp

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -477,7 +477,7 @@ QString InputUtils::geometryLengthAsString( const QgsGeometry &geometry )
477477
{
478478
QgsDistanceArea distanceArea;
479479
distanceArea.setEllipsoid( QStringLiteral( "WGS84" ) );
480-
distanceArea.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ), QgsCoordinateTransformContext() );
480+
distanceArea.setSourceCrs( PositionKit::positionCrs2D(), QgsProject::instance()->transformContext() );
481481

482482
qreal length = distanceArea.measureLength( geometry );
483483

@@ -875,37 +875,45 @@ QgsPoint InputUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
875875
const QgsCoordinateReferenceSystem &destCrs,
876876
const QgsCoordinateTransformContext &context,
877877
const QgsPoint &srcPoint )
878+
{
879+
bool dummyFallbackOperationHappened; //ignored
880+
return transformPoint( srcCrs, destCrs, context, srcPoint, dummyFallbackOperationHappened );
881+
}
882+
883+
QgsPoint InputUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
884+
const QgsCoordinateReferenceSystem &destCrs,
885+
const QgsCoordinateTransformContext &context,
886+
const QgsPoint &srcPoint, bool &fallbackOperationOccurred )
878887
{
879888
// we do not want to transform empty points,
880889
// QGIS would convert them to a valid (0, 0) points
881890
if ( srcPoint.isEmpty() )
882891
{
883-
return QgsPoint();
892+
return {};
884893
}
885894

886895
try
887896
{
888-
QgsCoordinateTransform ct( srcCrs, destCrs, context );
897+
const QgsCoordinateTransform ct( srcCrs, destCrs, context );
889898
if ( ct.isValid() )
890899
{
891900
if ( !ct.isShortCircuited() )
892901
{
893-
const QgsPointXY transformed = ct.transform( srcPoint.x(), srcPoint.y() );
894-
const QgsPoint pt( transformed.x(), transformed.y(), srcPoint.z(), srcPoint.m() );
902+
const QgsVector3D transformed = ct.transform( QgsVector3D( srcPoint.x(), srcPoint.y(), srcPoint.z() ) );
903+
fallbackOperationOccurred = ct.fallbackOperationOccurred();
904+
const QgsPoint pt( transformed.x(), transformed.y(), transformed.z(), srcPoint.m() );
895905
return pt;
896906
}
897-
else
898-
{
899-
return srcPoint;
900-
}
907+
908+
return srcPoint;
901909
}
902910
}
903911
catch ( QgsCsException &cse )
904912
{
905913
Q_UNUSED( cse )
906914
}
907915

908-
return QgsPoint();
916+
return {};
909917
}
910918

911919
QPointF InputUtils::transformPointToScreenCoordinates( const QgsCoordinateReferenceSystem &srcCrs, InputMapSettings *mapSettings, const QgsPoint &srcPoint )
@@ -954,12 +962,11 @@ QgsPoint InputUtils::mapPointToGps( QPointF mapPosition, InputMapSettings *mapSe
954962
return QgsPoint();
955963

956964
QgsPoint positionMapCrs = mapSettings->screenToCoordinate( mapPosition );
957-
QgsCoordinateReferenceSystem crsGPS = coordinateReferenceSystemFromEpsgId( 4326 );
958965

959966
const QgsPointXY transformedXY = transformPoint(
960967
mapSettings->destinationCrs(),
961-
crsGPS,
962-
QgsCoordinateTransformContext(),
968+
PositionKit::positionCrs2D(),
969+
mapSettings->transformContext(),
963970
positionMapCrs
964971
);
965972

@@ -1716,7 +1723,7 @@ qreal InputUtils::distanceBetweenGpsAndFeature( QgsPoint gpsPosition, const Feat
17161723

17171724
// Transform gps position to map CRS
17181725
QgsPointXY transformedPosition = transformPoint(
1719-
coordinateReferenceSystemFromEpsgId( 4326 ),
1726+
PositionKit::positionCrs3DEllipsoidHeight(),
17201727
mapSettings->destinationCrs(),
17211728
mapSettings->transformContext(),
17221729
gpsPosition
@@ -1764,7 +1771,7 @@ qreal InputUtils::angleBetweenGpsAndFeature( QgsPoint gpsPoint, const FeatureLay
17641771

17651772
// Transform gps position to map CRS
17661773
QgsPointXY transformedPosition = transformPoint(
1767-
coordinateReferenceSystemFromEpsgId( 4326 ),
1774+
PositionKit::positionCrs3DEllipsoidHeight(),
17681775
mapSettings->destinationCrs(),
17691776
mapSettings->transformContext(),
17701777
gpsPoint

app/inpututils.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,14 @@ class InputUtils: public QObject
305305
const QgsCoordinateTransformContext &context,
306306
const QgsPoint &srcPoint );
307307

308+
/**
309+
* Overload of transformPoint function, which also notifies the caller if PROJ fallback operation occurred
310+
*/
311+
static QgsPoint transformPoint( const QgsCoordinateReferenceSystem &srcCrs,
312+
const QgsCoordinateReferenceSystem &destCrs,
313+
const QgsCoordinateTransformContext &context,
314+
const QgsPoint &srcPoint, bool &fallbackOperationOccurred );
315+
308316
/**
309317
* Transforms point between CRS and screen pixels
310318
* Return empty QgsPoint if the transformation could not be applied or srcPoint is empty

app/main.cpp

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@
8080
#include "attributeformproxymodel.h"
8181
#include "attributetabmodel.h"
8282
#include "attributetabproxymodel.h"
83-
#include "inputcoordinatetransformer.h"
8483
#include "identifykit.h"
8584
#include "featurelayerpair.h"
8685

@@ -349,7 +348,6 @@ void initDeclarative()
349348
qmlRegisterType< InputMapCanvasMap >( "mm", 1, 0, "MapCanvasMap" );
350349
qmlRegisterType< InputMapSettings >( "mm", 1, 0, "MapSettings" );
351350
qmlRegisterType< InputMapTransform >( "mm", 1, 0, "MapTransform" );
352-
qmlRegisterType< InputCoordinateTransformer >( "mm", 1, 0, "CoordinateTransformer" );
353351
qmlRegisterUncreatableType< AbstractPositionProvider >( "mm", 1, 0, "PositionProvider", "Must be instantiated via its construct method" );
354352

355353
// map tools
@@ -544,18 +542,34 @@ int main( int argc, char *argv[] )
544542
LayerDetailLegendImageProvider *layerDetailLegendImageProvider( new LayerDetailLegendImageProvider );
545543

546544
// build position kit, save active provider to QSettings and load previously active provider
547-
PositionKit pk;
548-
QObject::connect( &pk, &PositionKit::positionProviderChanged, as, [as]( AbstractPositionProvider * provider )
545+
PositionKit *pk = engine.singletonInstance<PositionKit *>( "MMInput", "PositionKit" );
546+
QObject::connect( pk, &PositionKit::positionProviderChanged, as, [as]( AbstractPositionProvider * provider )
549547
{
550548
as->setActivePositionProviderId( provider ? provider->id() : QLatin1String() );
551549
} );
552-
pk.setPositionProvider( pk.constructActiveProvider( as ) );
553-
pk.setAppSettings( as );
550+
pk->setPositionProvider( pk->constructActiveProvider( as ) );
551+
pk->setAppSettings( as );
554552

555553
// Lambda context object can be used in all lambda functions defined here,
556554
// it secures lambdas, so that they are destroyed when this object is destroyed to avoid crashes.
557555
QObject lambdaContext;
558556

557+
QObject::connect( &activeProject, &ActiveProject::projectReloaded, &lambdaContext, [&pk]( QgsProject * project )
558+
{
559+
// read and set new vertical CRS definition
560+
bool crsExists = false;
561+
const QString crsWktDef = project->readEntry( QStringLiteral( "Mergin" ), QStringLiteral( "TargetVerticalCRS" ), QString(), &crsExists );
562+
const QgsCoordinateReferenceSystem crs = QgsCoordinateReferenceSystem::fromWkt( crsWktDef );
563+
pk->setVerticalCrs( crs );
564+
565+
// read and set new elevation transformation behavior
566+
bool valueRead = false;
567+
const bool skipElevationTransformation = project->readBoolEntry( QStringLiteral( "Mergin" ), QStringLiteral( "SkipElevationTransformation" ), true, &valueRead );
568+
pk->setSkipElevationTransformation( skipElevationTransformation );
569+
570+
pk->refreshPositionTransformer( project->transformContext() );
571+
} );
572+
559573
// the automatic sync request on cold start will fail on MerginApiStatus not being initialized yet, so we call it again
560574
// after server ping is done
561575
if ( activeProject.autosyncController() )
@@ -658,7 +672,7 @@ int main( int argc, char *argv[] )
658672
notificationModel.addError( message );
659673
} );
660674
// Direct connections
661-
QObject::connect( &app, &QGuiApplication::applicationStateChanged, &pk, &PositionKit::appStateChanged );
675+
QObject::connect( &app, &QGuiApplication::applicationStateChanged, pk, &PositionKit::appStateChanged );
662676
QObject::connect( &app, &QGuiApplication::applicationStateChanged, &activeProject, &ActiveProject::appStateChanged );
663677
QObject::connect( &pw, &ProjectWizard::projectCreated, &localProjectsManager, &LocalProjectsManager::addLocalProject );
664678
QObject::connect( &activeProject, &ActiveProject::projectReloaded, vm.get(), &VariablesManager::merginProjectChanged );
@@ -690,7 +704,7 @@ int main( int argc, char *argv[] )
690704
if ( tests.testingRequested() )
691705
{
692706
tests.initTestDeclarative();
693-
tests.init( ma.get(), &iu, vm.get(), &pk, as );
707+
tests.init( ma.get(), &iu, vm.get(), pk, as );
694708
return tests.runTest();
695709
}
696710
#endif
@@ -733,7 +747,6 @@ int main( int argc, char *argv[] )
733747
engine.rootContext()->setContextProperty( "__projectWizard", &pw );
734748
engine.rootContext()->setContextProperty( "__localProjectsManager", &localProjectsManager );
735749
engine.rootContext()->setContextProperty( "__variablesManager", vm.get() );
736-
engine.rootContext()->setContextProperty( "__positionKit", &pk );
737750

738751
// add image provider to pass QIcons/QImages from C++ to QML
739752
engine.rootContext()->setContextProperty( "__layerTreeModelPixmapProvider", layerTreeModelPixmapProvider );

app/map/inputcoordinatetransformer.cpp

Lines changed: 0 additions & 114 deletions
This file was deleted.

0 commit comments

Comments
 (0)