diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e79f5348..323fcdff2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -402,6 +402,9 @@ file( PATTERN "*.cmake" EXCLUDE PATTERN "vcpkg*" EXCLUDE ) +file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/app/assets/us_nga_egm96_15.tif + DESTINATION ${ASSETS_DIR_PATH}/app/android/assets/qgis-data/proj +) # ######################################################################################## # SUBDIRECTORIES # ######################################################################################## diff --git a/app/assets/us_nga_egm96_15.tif b/app/assets/us_nga_egm96_15.tif new file mode 100644 index 000000000..94a9f967e Binary files /dev/null and b/app/assets/us_nga_egm96_15.tif differ diff --git a/app/inpututils.cpp b/app/inpututils.cpp index 0850cb93b..4196b6797 100644 --- a/app/inpututils.cpp +++ b/app/inpututils.cpp @@ -476,7 +476,7 @@ QString InputUtils::geometryLengthAsString( const QgsGeometry &geometry ) { QgsDistanceArea distanceArea; distanceArea.setEllipsoid( QStringLiteral( "WGS84" ) ); - distanceArea.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ), QgsCoordinateTransformContext() ); + distanceArea.setSourceCrs( PositionKit::positionCrs2D(), QgsCoordinateTransformContext() ); qreal length = distanceArea.measureLength( geometry ); @@ -879,24 +879,22 @@ QgsPoint InputUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs, // QGIS would convert them to a valid (0, 0) points if ( srcPoint.isEmpty() ) { - return QgsPoint(); + return {}; } try { - QgsCoordinateTransform ct( srcCrs, destCrs, context ); + const QgsCoordinateTransform ct( srcCrs, destCrs, context ); if ( ct.isValid() ) { if ( !ct.isShortCircuited() ) { - const QgsPointXY transformed = ct.transform( srcPoint.x(), srcPoint.y() ); - const QgsPoint pt( transformed.x(), transformed.y(), srcPoint.z(), srcPoint.m() ); + const QgsVector3D transformed = ct.transform( QgsVector3D( srcPoint.x(), srcPoint.y(), srcPoint.z() ) ); + const QgsPoint pt( transformed.x(), transformed.y(), transformed.z(), srcPoint.m() ); return pt; } - else - { - return srcPoint; - } + + return srcPoint; } } catch ( QgsCsException &cse ) @@ -904,7 +902,7 @@ QgsPoint InputUtils::transformPoint( const QgsCoordinateReferenceSystem &srcCrs, Q_UNUSED( cse ) } - return QgsPoint(); + return {}; } QPointF InputUtils::transformPointToScreenCoordinates( const QgsCoordinateReferenceSystem &srcCrs, InputMapSettings *mapSettings, const QgsPoint &srcPoint ) @@ -953,11 +951,10 @@ QgsPoint InputUtils::mapPointToGps( QPointF mapPosition, InputMapSettings *mapSe return QgsPoint(); QgsPoint positionMapCrs = mapSettings->screenToCoordinate( mapPosition ); - QgsCoordinateReferenceSystem crsGPS = coordinateReferenceSystemFromEpsgId( 4326 ); const QgsPointXY transformedXY = transformPoint( mapSettings->destinationCrs(), - crsGPS, + PositionKit::positionCrs2D(), QgsCoordinateTransformContext(), positionMapCrs ); @@ -1715,7 +1712,7 @@ qreal InputUtils::distanceBetweenGpsAndFeature( QgsPoint gpsPosition, const Feat // Transform gps position to map CRS QgsPointXY transformedPosition = transformPoint( - coordinateReferenceSystemFromEpsgId( 4326 ), + PositionKit::positionCrs3D(), mapSettings->destinationCrs(), mapSettings->transformContext(), gpsPosition @@ -1763,7 +1760,7 @@ qreal InputUtils::angleBetweenGpsAndFeature( QgsPoint gpsPoint, const FeatureLay // Transform gps position to map CRS QgsPointXY transformedPosition = transformPoint( - coordinateReferenceSystemFromEpsgId( 4326 ), + PositionKit::positionCrs3D(), mapSettings->destinationCrs(), mapSettings->transformContext(), gpsPoint diff --git a/app/main.cpp b/app/main.cpp index 2e0bb1bfe..c2fd5523f 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -544,13 +544,13 @@ int main( int argc, char *argv[] ) LayerDetailLegendImageProvider *layerDetailLegendImageProvider( new LayerDetailLegendImageProvider ); // build position kit, save active provider to QSettings and load previously active provider - PositionKit pk; - QObject::connect( &pk, &PositionKit::positionProviderChanged, as, [as]( AbstractPositionProvider * provider ) + PositionKit *pk = engine.singletonInstance( "MMInput", "PositionKit" ); + QObject::connect( pk, &PositionKit::positionProviderChanged, as, [as]( AbstractPositionProvider * provider ) { as->setActivePositionProviderId( provider ? provider->id() : QLatin1String() ); } ); - pk.setPositionProvider( pk.constructActiveProvider( as ) ); - pk.setAppSettings( as ); + pk->setPositionProvider( PositionKit::constructActiveProvider( as ) ); + pk->setAppSettings( as ); // Lambda context object can be used in all lambda functions defined here, // it secures lambdas, so that they are destroyed when this object is destroyed to avoid crashes. @@ -648,7 +648,7 @@ int main( int argc, char *argv[] ) notificationModel.addError( message ); } ); // Direct connections - QObject::connect( &app, &QGuiApplication::applicationStateChanged, &pk, &PositionKit::appStateChanged ); + QObject::connect( &app, &QGuiApplication::applicationStateChanged, pk, &PositionKit::appStateChanged ); QObject::connect( &pw, &ProjectWizard::projectCreated, &localProjectsManager, &LocalProjectsManager::addLocalProject ); QObject::connect( &activeProject, &ActiveProject::projectReloaded, vm.get(), &VariablesManager::merginProjectChanged ); QObject::connect( &activeProject, &ActiveProject::projectWillBeReloaded, &inputProjUtils, &InputProjUtils::resetHandlers ); @@ -679,7 +679,7 @@ int main( int argc, char *argv[] ) if ( tests.testingRequested() ) { tests.initTestDeclarative(); - tests.init( ma.get(), &iu, vm.get(), &pk, as ); + tests.init( ma.get(), &iu, vm.get(), pk, as ); return tests.runTest(); } #endif @@ -722,7 +722,6 @@ int main( int argc, char *argv[] ) engine.rootContext()->setContextProperty( "__projectWizard", &pw ); engine.rootContext()->setContextProperty( "__localProjectsManager", &localProjectsManager ); engine.rootContext()->setContextProperty( "__variablesManager", vm.get() ); - engine.rootContext()->setContextProperty( "__positionKit", &pk ); // add image provider to pass QIcons/QImages from C++ to QML engine.rootContext()->setContextProperty( "__layerTreeModelPixmapProvider", layerTreeModelPixmapProvider ); diff --git a/app/map/inputcoordinatetransformer.cpp b/app/map/inputcoordinatetransformer.cpp index 3caf41e10..1d45a356e 100644 --- a/app/map/inputcoordinatetransformer.cpp +++ b/app/map/inputcoordinatetransformer.cpp @@ -13,12 +13,14 @@ */ #include "inputcoordinatetransformer.h" + +#include "positionkit.h" #include "qgslogger.h" InputCoordinateTransformer::InputCoordinateTransformer( QObject *parent ) : QObject( parent ) { - mCoordinateTransform.setSourceCrs( QgsCoordinateReferenceSystem::fromEpsgId( 4326 ) ); + mCoordinateTransform.setSourceCrs( PositionKit::positionCrs3D() ); } QgsPoint InputCoordinateTransformer::projectedPosition() const diff --git a/app/maptools/recordingmaptool.cpp b/app/maptools/recordingmaptool.cpp index 91402114b..ab2ee44eb 100644 --- a/app/maptools/recordingmaptool.cpp +++ b/app/maptools/recordingmaptool.cpp @@ -59,7 +59,7 @@ void RecordingMapTool::addPoint( const QgsPoint &point ) pointToAdd = mPositionKit->positionCoordinate(); QgsPoint transformed = InputUtils::transformPoint( - PositionKit::positionCRS(), + PositionKit::positionCrs3D(), mActiveLayer->crs(), mActiveLayer->transformContext(), pointToAdd @@ -594,7 +594,7 @@ void RecordingMapTool::onPositionChanged() QgsPoint position = mPositionKit->positionCoordinate(); QgsPointXY transformed = InputUtils::transformPoint( - PositionKit::positionCRS(), + PositionKit::positionCrs3D(), mActiveLayer->sourceCrs(), mActiveLayer->transformContext(), position diff --git a/app/position/mapposition.cpp b/app/position/mapposition.cpp index 6659981c4..be6e18177 100644 --- a/app/position/mapposition.cpp +++ b/app/position/mapposition.cpp @@ -105,7 +105,7 @@ void MapPosition::recalculateMapPosition() { QgsPointXY srcPoint = QgsPointXY( geoposition.x(), geoposition.y() ); QgsPointXY mapPositionXY = InputUtils::transformPointXY( - mPositionKit->positionCRS(), + mPositionKit->positionCrs3D(), mMapSettings->destinationCrs(), mMapSettings->transformContext(), srcPoint diff --git a/app/position/positionkit.cpp b/app/position/positionkit.cpp index f2b65c224..ae3cdad91 100644 --- a/app/position/positionkit.cpp +++ b/app/position/positionkit.cpp @@ -33,11 +33,21 @@ PositionKit::PositionKit( QObject *parent ) { } -QgsCoordinateReferenceSystem PositionKit::positionCRS() +QgsCoordinateReferenceSystem PositionKit::positionCrs3D() +{ + return QgsCoordinateReferenceSystem::fromEpsgId( 9707 ); +} + +QgsCoordinateReferenceSystem PositionKit::positionCrs2D() { return QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); } +QgsCoordinateReferenceSystem PositionKit::positionCrs3DEllipsoidHeight() +{ + return QgsCoordinateReferenceSystem::fromEpsgId( 4979 ); +} + void PositionKit::startUpdates() { if ( mPositionProvider ) diff --git a/app/position/positionkit.h b/app/position/positionkit.h index 374ca497a..f5ef48973 100644 --- a/app/position/positionkit.h +++ b/app/position/positionkit.h @@ -15,6 +15,7 @@ #include "qgspoint.h" #include "qgscoordinatereferencesystem.h" #include +#include class AppSettings; @@ -27,6 +28,8 @@ class AppSettings; class PositionKit : public QObject { Q_OBJECT + QML_ELEMENT + QML_SINGLETON Q_PROPERTY( double latitude READ latitude NOTIFY latitudeChanged ) Q_PROPERTY( double longitude READ longitude NOTIFY longitudeChanged ) @@ -112,8 +115,12 @@ class PositionKit : public QObject double vdop() const; double pdop() const; - // Coordinate reference system of position - WGS84 (constant) - Q_INVOKABLE static QgsCoordinateReferenceSystem positionCRS(); + // Coordinate reference system - WGS84 (EPSG:4326) + static QgsCoordinateReferenceSystem positionCrs2D(); + // Coordinate reference system - WGS84 + ellipsoid height (EPSG:4979) + static QgsCoordinateReferenceSystem positionCrs3DEllipsoidHeight(); + // Coordinate reference system of position - WGS84 + geoid height - egm96_15 (EPSG:9707) + static QgsCoordinateReferenceSystem positionCrs3D(); Q_INVOKABLE static AbstractPositionProvider *constructProvider( const QString &type, const QString &id, const QString &name = QString() ); Q_INVOKABLE static AbstractPositionProvider *constructActiveProvider( AppSettings *appsettings ); diff --git a/app/position/providers/androidpositionprovider.cpp b/app/position/providers/androidpositionprovider.cpp index 41d3ef4bf..3edc63122 100644 --- a/app/position/providers/androidpositionprovider.cpp +++ b/app/position/providers/androidpositionprovider.cpp @@ -19,6 +19,8 @@ #include +#include "inpututils.h" + int AndroidPositionProvider::sLastInstanceId = 0; QMap AndroidPositionProvider::sInstances; @@ -53,14 +55,17 @@ void jniOnPositionUpdated( JNIEnv *env, jclass clazz, jint instanceId, jobject l { const jdouble value = location.callMethod( "getAltitude" ); if ( !qFuzzyIsNull( value ) ) - pos.elevation = value; + { + // transform the altitude from EPSG:4979 (WGS84 (EPSG:4326) + ellipsoidal height) to specified geoid model + const QgsPoint geoidPosition = InputUtils::transformPoint( + PositionKit::positionCrs3DEllipsoidHeight(), + PositionKit::positionCrs3D(), + QgsCoordinateTransformContext(), + {longitude, latitude, value} ); + pos.elevation = geoidPosition.z(); + } } - // TODO: we are getting ellipsoid elevation here. From API level 34 (Android 14), - // there is AltitudeConverter() class in Java that can be used to add MSL altitude - // to Location object. How to deal with this correctly? (we could also convert - // to MSL (orthometric) altitude ourselves if we add geoid model to our APK - // horizontal accuracy if ( location.callMethod( "hasAccuracy" ) ) { diff --git a/app/position/providers/internalpositionprovider.cpp b/app/position/providers/internalpositionprovider.cpp index fc8a02670..b5e041a62 100644 --- a/app/position/providers/internalpositionprovider.cpp +++ b/app/position/providers/internalpositionprovider.cpp @@ -9,6 +9,7 @@ #include "internalpositionprovider.h" #include "coreutils.h" +#include "inpututils.h" #include "qgis.h" @@ -140,9 +141,15 @@ void InternalPositionProvider::parsePositionUpdate( const QGeoPositionInfo &posi positionDataHasChanged = true; } - if ( !qgsDoubleNear( position.coordinate().altitude(), mLastPosition.elevation ) ) + // transform the altitude from EPSG:4979 (WGS84 (EPSG:4326) + ellipsoidal height) to specified geoid model + const QgsPoint geoidPosition = InputUtils::transformPoint( + PositionKit::positionCrs3DEllipsoidHeight(), + PositionKit::positionCrs3D(), + QgsProject::instance()->transformContext(), + {position.coordinate().longitude(), position.coordinate().latitude(), position.coordinate().altitude()} ); + if ( !qgsDoubleNear( geoidPosition.z(), mLastPosition.elevation ) ) { - mLastPosition.elevation = position.coordinate().altitude(); + mLastPosition.elevation = geoidPosition.z(); positionDataHasChanged = true; } diff --git a/app/position/tracking/positiontrackingmanager.cpp b/app/position/tracking/positiontrackingmanager.cpp index 6bd7332a7..0f1ff6109 100644 --- a/app/position/tracking/positiontrackingmanager.cpp +++ b/app/position/tracking/positiontrackingmanager.cpp @@ -95,7 +95,7 @@ void PositionTrackingManager::commitTrackedPath() } // convert captured geometry to the destination layer's CRS - QgsGeometry geometryInLayerCRS = InputUtils::transformGeometry( mTrackedGeometry, QgsCoordinateReferenceSystem::fromEpsgId( 4326 ), trackingLayer ); + QgsGeometry geometryInLayerCRS = InputUtils::transformGeometry( mTrackedGeometry, PositionKit::positionCrs3D(), trackingLayer ); // create feature - add tracking variables to scope QgsExpressionContextScope *scope = new QgsExpressionContextScope( QStringLiteral( "MM_Tracking" ) ); @@ -332,7 +332,7 @@ void PositionTrackingManager::setVariablesManager( VariablesManager *newVariable QgsCoordinateReferenceSystem PositionTrackingManager::crs() const { - return QgsCoordinateReferenceSystem::fromEpsgId( 4326 ); + return PositionKit::positionCrs3D(); } void PositionTrackingManager::tryAgain() diff --git a/app/qml/gps/MMBluetoothConnectionDrawer.qml b/app/qml/gps/MMBluetoothConnectionDrawer.qml index 5e018337b..64d5c9af4 100644 --- a/app/qml/gps/MMBluetoothConnectionDrawer.qml +++ b/app/qml/gps/MMBluetoothConnectionDrawer.qml @@ -12,13 +12,14 @@ import QtQuick.Controls import QtQuick.Layouts import mm 1.0 as MM +import MMInput import "../components" as MMComponents MMComponents.MMDrawer { id: root - property var positionProvider: __positionKit.positionProvider + property var positionProvider: PositionKit.positionProvider property string howToConnectGPSLink: __inputHelp.howToConnectGPSLink property string titleText: { @@ -52,7 +53,7 @@ MMComponents.MMDrawer { } else if ( rootstate.state === "waitingToReconnect" ) { - return __positionKit.positionProvider.stateMessage + "

" + + return PositionKit.positionProvider.stateMessage + "

" + qsTr( "You can close this message, we will try to repeatedly connect to your device." ) } diff --git a/app/qml/gps/MMGpsDataDrawer.qml b/app/qml/gps/MMGpsDataDrawer.qml index d7b2f7597..7c1aa4df9 100644 --- a/app/qml/gps/MMGpsDataDrawer.qml +++ b/app/qml/gps/MMGpsDataDrawer.qml @@ -62,7 +62,7 @@ MMComponents.MMDrawer { width: parent.width / 2 title: qsTr( "Source" ) - value: __positionKit.positionProvider ? __positionKit.positionProvider.name() : qsTr( "No receiver" ) + value: PositionKit.positionProvider ? PositionKit.positionProvider.name() : qsTr( "No receiver" ) alignmentRight: Positioner.index % 2 === 1 } @@ -72,10 +72,10 @@ MMComponents.MMDrawer { width: parent.width / 2 - visible: __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" + visible: PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" title: qsTr( "Status" ) - value: __positionKit.positionProvider ? __positionKit.positionProvider.stateMessage : "" + value: PositionKit.positionProvider ? PositionKit.positionProvider.stateMessage : "" alignmentRight: Positioner.index % 2 === 1 } @@ -93,7 +93,7 @@ MMComponents.MMDrawer { title: qsTr( "Longitude") value: { - if ( !__positionKit.hasPosition || Number.isNaN( __positionKit.longitude ) ) { + if ( !PositionKit.hasPosition || Number.isNaN( PositionKit.longitude ) ) { return qsTr( "N/A" ) } @@ -112,7 +112,7 @@ MMComponents.MMDrawer { title: qsTr( "Latitude" ) value: { - if ( !__positionKit.hasPosition || Number.isNaN( __positionKit.latitude ) ) { + if ( !PositionKit.hasPosition || Number.isNaN( PositionKit.latitude ) ) { return qsTr( "N/A" ) } @@ -131,7 +131,7 @@ MMComponents.MMDrawer { title: qsTr( "X" ) value: { - if ( !__positionKit.hasPosition || Number.isNaN( mapPositioning.mapPosition.x ) ) { + if ( !PositionKit.hasPosition || Number.isNaN( mapPositioning.mapPosition.x ) ) { return qsTr( "N/A" ) } @@ -146,7 +146,7 @@ MMComponents.MMDrawer { title: qsTr( "Y" ) value: { - if ( !__positionKit.hasPosition || Number.isNaN( mapPositioning.mapPosition.y ) ) { + if ( !PositionKit.hasPosition || Number.isNaN( mapPositioning.mapPosition.y ) ) { return qsTr( "N/A" ) } @@ -161,11 +161,11 @@ MMComponents.MMDrawer { title: qsTr( "Horizontal accuracy" ) value: { - if ( !__positionKit.hasPosition || __positionKit.horizontalAccuracy < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.horizontalAccuracy < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.horizontalAccuracy, 2 ) + " m" + __inputUtils.formatNumber( PositionKit.horizontalAccuracy, 2 ) + " m" } alignmentRight: Positioner.index % 2 === 1 @@ -176,11 +176,11 @@ MMComponents.MMDrawer { title: qsTr( "Vertical accuracy" ) value: { - if ( !__positionKit.hasPosition || __positionKit.verticalAccuracy < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.verticalAccuracy < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.verticalAccuracy, 2 ) + " m" + __inputUtils.formatNumber( PositionKit.verticalAccuracy, 2 ) + " m" } alignmentRight: Positioner.index % 2 === 1 @@ -191,13 +191,14 @@ MMComponents.MMDrawer { title: qsTr( "Altitude" ) value: { - if ( !__positionKit.hasPosition || Number.isNaN( __positionKit.altitude ) ) { + if ( !PositionKit.hasPosition || Number.isNaN( PositionKit.altitude ) ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.altitude, 2 ) + " m" + __inputUtils.formatNumber( PositionKit.altitude, 2 ) + " m" } alignmentRight: Positioner.index % 2 === 1 + desc: qsTr("Orthometric height, using EGM96 geoid") } MMGpsComponents.MMGpsDataText { @@ -205,14 +206,14 @@ MMComponents.MMDrawer { title: qsTr( "Fix quality" ) value: { - if ( !__positionKit.hasPosition ) { + if ( !PositionKit.hasPosition ) { return qsTr( "N/A" ) } - __positionKit.fix + PositionKit.fix } - visible: __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" + visible: PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" alignmentRight: Positioner.index % 2 === 1 } @@ -222,12 +223,12 @@ MMComponents.MMDrawer { title: qsTr( "Satellites (in use/view)" ) value: { - if ( __positionKit.satellitesUsed < 0 || __positionKit.satellitesVisible < 0 ) + if ( PositionKit.satellitesUsed < 0 || PositionKit.satellitesVisible < 0 ) { return qsTr( "N/A" ) } - __positionKit.satellitesUsed + "/" + __positionKit.satellitesVisible + PositionKit.satellitesUsed + "/" + PositionKit.satellitesVisible } alignmentRight: Positioner.index % 2 === 1 @@ -238,14 +239,14 @@ MMComponents.MMDrawer { title: qsTr( "HDOP" ) value: { - if ( !__positionKit.hasPosition || __positionKit.hdop < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.hdop < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.hdop, 2 ) + __inputUtils.formatNumber( PositionKit.hdop, 2 ) } - visible: __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" + visible: PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" alignmentRight: Positioner.index % 2 === 1 } @@ -255,14 +256,14 @@ MMComponents.MMDrawer { title: qsTr( "VDOP" ) value: { - if ( !__positionKit.hasPosition || __positionKit.vdop < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.vdop < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.vdop, 2 ) + __inputUtils.formatNumber( PositionKit.vdop, 2 ) } - visible: __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" + visible: PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" alignmentRight: Positioner.index % 2 === 1 } @@ -272,14 +273,14 @@ MMComponents.MMDrawer { title: qsTr( "PDOP" ) value: { - if ( !__positionKit.hasPosition || __positionKit.pdop < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.pdop < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.pdop, 2 ) + __inputUtils.formatNumber( PositionKit.pdop, 2 ) } - visible: __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" + visible: PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" alignmentRight: Positioner.index % 2 === 1 } @@ -289,11 +290,11 @@ MMComponents.MMDrawer { title: qsTr( "Speed" ) value: { - if ( !__positionKit.hasPosition || __positionKit.speed < 0 ) { + if ( !PositionKit.hasPosition || PositionKit.speed < 0 ) { return qsTr( "N/A" ) } - __inputUtils.formatNumber( __positionKit.speed, 2 ) + " km/h" + __inputUtils.formatNumber( PositionKit.speed, 2 ) + " km/h" } alignmentRight: Positioner.index % 2 === 1 @@ -303,7 +304,7 @@ MMComponents.MMDrawer { width: parent.width / 2 title: qsTr( "Last Fix" ) - value: __positionKit.lastRead.toLocaleTimeString( Qt.locale() ) || qsTr( "N/A" ) + value: PositionKit.lastRead.toLocaleTimeString( Qt.locale() ) || qsTr( "N/A" ) alignmentRight: Positioner.index % 2 === 1 } @@ -333,13 +334,13 @@ MMComponents.MMDrawer { MM.MapPosition { id: mapPositioning - positionKit: __positionKit + positionKit: PositionKit mapSettings: root.mapSettings } QtObject { id: internal - property string coordinatesInDegrees: __inputUtils.degreesString( __positionKit.positionCoordinate ) + property string coordinatesInDegrees: __inputUtils.degreesString( PositionKit.positionCoordinate ) } } diff --git a/app/qml/gps/MMPositionProviderPage.qml b/app/qml/gps/MMPositionProviderPage.qml index 9f3b501f4..c98d9a0c8 100644 --- a/app/qml/gps/MMPositionProviderPage.qml +++ b/app/qml/gps/MMPositionProviderPage.qml @@ -81,7 +81,7 @@ MMComponents.MMPage { } text: model.ProviderName ? model.ProviderName : qsTr( "Unknown device" ) - secondaryText: listdelegate.isActive ? __positionKit.positionProvider.stateMessage : model.ProviderDescription + secondaryText: listdelegate.isActive ? PositionKit.positionProvider.stateMessage : model.ProviderDescription rightContent: MMComponents.MMRoundButton { visible: model.ProviderType !== "internal" @@ -205,7 +205,7 @@ MMComponents.MMPage { MMAddPositionProviderDrawer { onInitiatedConnectionTo: function ( deviceAddress, deviceName ) { - __positionKit.positionProvider = __positionKit.constructProvider( "external", deviceAddress, deviceName ) + PositionKit.positionProvider = PositionKit.constructProvider( "external", deviceAddress, deviceName ) providersModel.addProvider( deviceName, deviceAddress ) list.model.discovering = false @@ -226,7 +226,7 @@ MMComponents.MMPage { onClosed: connectingDialogLoader.active = false // revert position provider back to internal provider - onFailure: __positionKit.positionProvider = __positionKit.constructProvider( "internal", "devicegps", "" ) + onFailure: PositionKit.positionProvider = PositionKit.constructProvider( "internal", "devicegps", "" ) Component.onCompleted: open() } @@ -251,7 +251,7 @@ MMComponents.MMPage { return // do not construct the same provider again } - __positionKit.positionProvider = __positionKit.constructProvider( type, id, name ) + PositionKit.positionProvider = PositionKit.constructProvider( type, id, name ) if ( type === "external" ) { connectingDialogLoader.open() diff --git a/app/qml/gps/MMStakeoutDrawer.qml b/app/qml/gps/MMStakeoutDrawer.qml index 61f6ca354..1958e3b6a 100644 --- a/app/qml/gps/MMStakeoutDrawer.qml +++ b/app/qml/gps/MMStakeoutDrawer.qml @@ -14,6 +14,7 @@ import Qt5Compat.GraphicalEffects import QtQuick.Shapes import mm 1.0 as MM +import MMInput import "../components" import "../map/components" @@ -26,7 +27,7 @@ MMDrawer { property var targetPair: null property real remainingDistance: targetPair ? __inputUtils.distanceBetweenGpsAndFeature( - __positionKit.positionCoordinate, + PositionKit.positionCoordinate, targetPair, mapCanvas.mapSettings ) : -1 property var extent @@ -220,7 +221,7 @@ MMDrawer { MM.PositionDirection { id: positionDirection - positionKit: __positionKit + positionKit: PositionKit compass: MM.Compass { id: ccompass } } @@ -228,7 +229,7 @@ MMDrawer { id: positionMarker property real bearing: root.targetPair ? __inputUtils.angleBetweenGpsAndFeature( - __positionKit.positionCoordinate, + PositionKit.positionCoordinate, root.targetPair, root.mapCanvas.mapSettings ) : 0 @@ -238,9 +239,9 @@ MMDrawer { hasDirection: positionDirection.hasDirection direction: positionDirection.direction - hasPosition: __positionKit.hasPosition + hasPosition: PositionKit.hasPosition - horizontalAccuracy: __positionKit.horizontalAccuracy + horizontalAccuracy: PositionKit.horizontalAccuracy accuracyRingSize: 0 // do not show any accuracy ring in stakeout mode trackingMode: closeRangeModeComponent.state === "notAtTarget" diff --git a/app/qml/gps/components/MMGpsDataText.qml b/app/qml/gps/components/MMGpsDataText.qml index ed3ce98fa..69409a4eb 100644 --- a/app/qml/gps/components/MMGpsDataText.qml +++ b/app/qml/gps/components/MMGpsDataText.qml @@ -8,6 +8,7 @@ ***************************************************************************/ import QtQuick +import QtQuick.Layouts import "../../components" as MMComponents @@ -17,6 +18,7 @@ Item { property alias title: titletxt.text property alias value: valuetxt.text + property string desc: "" property bool alignmentRight: false implicitHeight: contentColumn.implicitHeight @@ -36,18 +38,41 @@ Item { spacing: 0 - MMComponents.MMText { - id: titletxt - - leftPadding: alignmentRight ? __style.margin4 : 0 - rightPadding: alignmentRight ? 0 : __style.margin4 - - width: parent.width - leftPadding - rightPadding - x: leftPadding - - font: __style.p6 - - horizontalAlignment: alignmentRight ? Text.AlignRight : Text.AlignLeft + RowLayout { + width: parent.width + spacing: __style.margin10 + + MMComponents.MMText { + id: titletxt + + leftPadding: alignmentRight ? __style.margin4 : 0 + rightPadding: alignmentRight ? 0 : __style.margin4 + + font: __style.p6 + + horizontalAlignment: alignmentRight ? Text.AlignRight : Text.AlignLeft + Layout.alignment: root.alignmentRight? Qt.AlignRight: Qt.AlignLeft + Layout.fillWidth: root.alignmentRight + } + + MMComponents.MMIcon { + id: infoIcon + source: __style.infoIcon + visible: root.desc + Layout.alignment: root.alignmentRight? Qt.AlignRight | Qt.AlignBaseline : Qt.AlignLeft | Qt.AlignBaseline + Layout.preferredWidth: __style.icon16 + Layout.preferredHeight: __style.icon16 + + TapHandler{ + gesturePolicy: TapHandler.ReleaseWithinBounds + onTapped: () => infoPopup.open() + } + } + + MMComponents.MMListSpacer{ + visible: !root.alignmentRight + Layout.fillWidth: !root.alignmentRight + } } MMComponents.MMText { @@ -65,4 +90,13 @@ Item { horizontalAlignment: alignmentRight ? Text.AlignRight : Text.AlignLeft } } + + MMComponents.MMPopup { + id: infoPopup + y: ( -root.height / 2 ) - __style.margin8 + MMComponents.MMText { + font: __style.p6 + text: root.desc + } + } } diff --git a/app/qml/main.qml b/app/qml/main.qml index a4edfbc0b..8301ba261 100644 --- a/app/qml/main.qml +++ b/app/qml/main.qml @@ -258,10 +258,10 @@ ApplicationWindow { Component.onCompleted: { __activeProject.mapSettings = map.mapSettings - __iosUtils.positionKit = __positionKit + __iosUtils.positionKit = PositionKit __iosUtils.compass = map.compass __variablesManager.compass = map.compass - __variablesManager.positionKit = __positionKit + __variablesManager.positionKit = PositionKit } } @@ -771,7 +771,7 @@ ApplicationWindow { onStakeoutFeature: function( feature ) { if ( !__inputUtils.isPointLayerFeature( feature ) ) return; - if ( !__positionKit.hasPosition ) + if ( !PositionKit.hasPosition ) { __notificationModel.addWarning( qsTr( "Stake out is disabled because location is unavailable!" ) ); return; diff --git a/app/qml/map/MMMapController.qml b/app/qml/map/MMMapController.qml index 01efc9958..ae892281d 100644 --- a/app/qml/map/MMMapController.qml +++ b/app/qml/map/MMMapController.qml @@ -203,7 +203,7 @@ Item { states: [ State { name: "good" // GPS provides position AND horizontal accuracy is below set tolerance (threshold) - when: __positionKit.hasPosition && __positionKit.horizontalAccuracy > 0 && __positionKit.horizontalAccuracy <= AppSettings.gpsAccuracyTolerance + when: PositionKit.hasPosition && PositionKit.horizontalAccuracy > 0 && PositionKit.horizontalAccuracy <= AppSettings.gpsAccuracyTolerance PropertyChanges { target: gpsStateGroup indicatorColor: __style.positiveColor @@ -211,7 +211,7 @@ Item { }, State { name: "low" // below accuracy tolerance OR GPS does not provide horizontal accuracy - when: __positionKit.hasPosition && (__positionKit.horizontalAccuracy < 0 || __positionKit.horizontalAccuracy > AppSettings.gpsAccuracyTolerance ) + when: PositionKit.hasPosition && (PositionKit.horizontalAccuracy < 0 || PositionKit.horizontalAccuracy > AppSettings.gpsAccuracyTolerance ) PropertyChanges { target: gpsStateGroup indicatorColor: __style.warningColor @@ -219,7 +219,7 @@ Item { }, State { name: "unavailable" // GPS does not provide position - when: !__positionKit.hasPosition + when: !PositionKit.hasPosition PropertyChanges { target: gpsStateGroup indicatorColor: __style.negativeColor @@ -275,9 +275,9 @@ Item { onLongPressed: function( point ) { // Alter position of simulated provider - if ( __positionKit.positionProvider && __positionKit.positionProvider.id() === "simulated" ) + if ( PositionKit.positionProvider && PositionKit.positionProvider.id() === "simulated" ) { - __positionKit.positionProvider.setPosition( __inputUtils.mapPointToGps( Qt.point( point.x, point.y ), mapCanvas.mapSettings ) ) + PositionKit.positionProvider.setPosition( __inputUtils.mapPointToGps( Qt.point( point.x, point.y ), mapCanvas.mapSettings ) ) } if ( root.state === "view" ) @@ -410,7 +410,7 @@ Item { } Component.onCompleted: { - trackingManager.trackingBackend = trackingManager.constructTrackingBackend( __activeProject.qgsProject, __positionKit ) + trackingManager.trackingBackend = trackingManager.constructTrackingBackend( __activeProject.qgsProject, PositionKit ) } Connections { @@ -433,9 +433,9 @@ Item { hasDirection: positionDirectionSource.hasDirection direction: positionDirectionSource.direction - hasPosition: __positionKit.hasPosition + hasPosition: PositionKit.hasPosition - horizontalAccuracy: __positionKit.horizontalAccuracy + horizontalAccuracy: PositionKit.horizontalAccuracy accuracyRingSize: mapPositionSource.screenAccuracy trackingMode: root.state !== "inactive" && tracking.active @@ -679,7 +679,7 @@ Item { visible: { if ( root.mapExtentOffset > 0 && root.state !== "stakeout" ) return false - if ( __positionKit.positionProvider && __positionKit.positionProvider.type() === "external" ) { + if ( PositionKit.positionProvider && PositionKit.positionProvider.type() === "external" ) { // for external receivers we want to show gps panel and accuracy button // even when the GPS receiver is not sending position data return true @@ -695,36 +695,36 @@ Item { } text: { - if ( !__positionKit.positionProvider ) + if ( !PositionKit.positionProvider ) { return "" } - else if ( __positionKit.positionProvider.type() === "external" ) + else if ( PositionKit.positionProvider.type() === "external" ) { - if ( __positionKit.positionProvider.state === MM.PositionProvider.Connecting ) + if ( PositionKit.positionProvider.state === MM.PositionProvider.Connecting ) { - return qsTr( "Connecting to %1" ).arg( __positionKit.positionProvider.name() ) + return qsTr( "Connecting to %1" ).arg( PositionKit.positionProvider.name() ) } - else if ( __positionKit.positionProvider.state === MM.PositionProvider.WaitingToReconnect ) + else if ( PositionKit.positionProvider.state === MM.PositionProvider.WaitingToReconnect ) { - return __positionKit.positionProvider.stateMessage + return PositionKit.positionProvider.stateMessage } - else if ( __positionKit.positionProvider.state === MM.PositionProvider.NoConnection ) + else if ( PositionKit.positionProvider.state === MM.PositionProvider.NoConnection ) { - return __positionKit.positionProvider.stateMessage + return PositionKit.positionProvider.stateMessage } } - if ( !__positionKit.hasPosition ) + if ( !PositionKit.hasPosition ) { return qsTr( "Connected, no position" ) } - else if ( Number.isNaN( __positionKit.horizontalAccuracy ) || __positionKit.horizontalAccuracy < 0 ) + else if ( Number.isNaN( PositionKit.horizontalAccuracy ) || PositionKit.horizontalAccuracy < 0 ) { return qsTr( "Unknown accuracy" ) } - let accuracyText = __inputUtils.formatNumber( __positionKit.horizontalAccuracy, __positionKit.horizontalAccuracy > 1 ? 1 : 2 ) + " m" + let accuracyText = __inputUtils.formatNumber( PositionKit.horizontalAccuracy, PositionKit.horizontalAccuracy > 1 ? 1 : 2 ) + " m" if ( AppSettings.gpsAntennaHeight > 0 ) { let gpsText = Number( AppSettings.gpsAntennaHeight.toFixed( 3 ) ) + " m" @@ -1092,14 +1092,14 @@ Item { id: mapPositionSource mapSettings: mapCanvas.mapSettings - positionKit: __positionKit + positionKit: PositionKit onScreenPositionChanged: root.updatePosition() } MM.PositionDirection { id: positionDirectionSource - positionKit: __positionKit + positionKit: PositionKit compass: deviceCompass } @@ -1386,7 +1386,7 @@ Item { } function centerToPosition( animate = false ) { - if ( __positionKit.hasPosition ) { + if ( PositionKit.hasPosition ) { if ( animate ) { let screenPt = mapCanvas.mapSettings.coordinateToScreen( mapPositionSource.mapPosition ) diff --git a/app/qml/map/MMRecordingTools.qml b/app/qml/map/MMRecordingTools.qml index e9ebf8a92..da976654c 100644 --- a/app/qml/map/MMRecordingTools.qml +++ b/app/qml/map/MMRecordingTools.qml @@ -77,7 +77,7 @@ Item { recordingInterval: AppSettings.lineRecordingInterval recordingIntervalType: AppSettings.intervalType - positionKit: __positionKit + positionKit: PositionKit activeLayer: __activeLayer.vectorLayer activeFeature: root.activeFeature diff --git a/app/qml/map/MMStakeoutTools.qml b/app/qml/map/MMStakeoutTools.qml index 0464e4b4f..530987503 100644 --- a/app/qml/map/MMStakeoutTools.qml +++ b/app/qml/map/MMStakeoutTools.qml @@ -10,6 +10,7 @@ import QtQuick import mm 1.0 as MM +import MMInput Item { id: root @@ -34,7 +35,7 @@ Item { id: mapPositioning mapSettings: map.mapSettings - positionKit: __positionKit + positionKit: PositionKit onMapPositionChanged: updateStakeout() } diff --git a/app/qml/settings/MMSettingsPage.qml b/app/qml/settings/MMSettingsPage.qml index d3cf495cf..69145e351 100644 --- a/app/qml/settings/MMSettingsPage.qml +++ b/app/qml/settings/MMSettingsPage.qml @@ -78,7 +78,7 @@ MMPage { MMSettingsComponents.MMSettingsItem { width: parent.width title: qsTr("Manage GPS receivers") - value: __positionKit.positionProvider.name() + value: PositionKit.positionProvider.name() onClicked: root.manageGpsClicked() } diff --git a/gallery/main.cpp b/gallery/main.cpp index 5a82fd026..3dfbbaba8 100644 --- a/gallery/main.cpp +++ b/gallery/main.cpp @@ -22,7 +22,6 @@ #include "qrcodedecoder.h" #include "inpututils.h" #include "scalebarkit.h" -#include "positionkit.h" #include "formfeaturesmodel.h" #include "enums.h" @@ -80,9 +79,6 @@ int main( int argc, char *argv[] ) NotificationModel notificationModel; - PositionKit pk; - engine.rootContext()->setContextProperty( "__positionKit", &pk ); - engine.rootContext()->setContextProperty( "__notificationModel", ¬ificationModel ); // path to local wrapper pages engine.rootContext()->setContextProperty( "_qmlWrapperPath", QGuiApplication::applicationDirPath() + "/HotReload/qml/pages/" ); diff --git a/gallery/positionkit.h b/gallery/positionkit.h index e889f1a17..059a3e737 100644 --- a/gallery/positionkit.h +++ b/gallery/positionkit.h @@ -12,10 +12,13 @@ #include #include +#include class PositionKit : public QObject { Q_OBJECT + QML_ELEMENT + QML_SINGLETON Q_PROPERTY( double latitude READ latitude CONSTANT ) Q_PROPERTY( double longitude READ longitude CONSTANT ) diff --git a/vcpkg/ports/qtpositioning/android15_altitude_fix.patch b/vcpkg/ports/qtpositioning/android15_altitude_fix.patch new file mode 100644 index 000000000..453b830a2 --- /dev/null +++ b/vcpkg/ports/qtpositioning/android15_altitude_fix.patch @@ -0,0 +1,68 @@ +diff --git a/src/plugins/position/android/jar/src/org/qtproject/qt/android/positioning/QtPositioning.java b/src/plugins/position/android/jar/src/org/qtproject/qt/android/positioning/QtPositioning.java +index e4163b09..afb2889e 100644 +--- a/src/plugins/position/android/jar/src/org/qtproject/qt/android/positioning/QtPositioning.java ++++ b/src/plugins/position/android/jar/src/org/qtproject/qt/android/positioning/QtPositioning.java +@@ -112,6 +112,8 @@ class QtPositioning implements LocationListener + static private void addMslAltitude(Location location) + { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { ++ if (location.hasMslAltitude()) // Nothing to be done ++ return; + if (altitudeConverter == null) + altitudeConverter = new AltitudeConverter(); + try { +diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp +index 2b2919fa..0abc2cad 100644 +--- a/src/plugins/position/android/src/jnipositioning.cpp ++++ b/src/plugins/position/android/src/jnipositioning.cpp +@@ -220,7 +220,7 @@ namespace AndroidPositioning { + return ret; + } + +- QGeoPositionInfo positionInfoFromJavaLocation(const jobject &location) ++ QGeoPositionInfo positionInfoFromJavaLocation(const jobject &location, bool useAltConverter) + { + QGeoPositionInfo info; + +@@ -241,8 +241,11 @@ namespace AndroidPositioning { + coordinate.setAltitude(value); + } + // MSL altitude, available in API Level 34+. +- // It will be available only if we requested it when starting updates. +- if (QNativeInterface::QAndroidApplication::sdkVersion() >= 34) { ++ // In API Level 34 it was available only if we manually added it. ++ // In API Level 35 (and potentially later), it's automatically added ++ // to the location object, so we need to use it *only* when the user ++ // set the relevant plugin parameter. ++ if (useAltConverter && QNativeInterface::QAndroidApplication::sdkVersion() >= 34) { + attributeExists = jniObject.callMethod("hasMslAltitude"); + if (attributeExists) { + const jdouble value = jniObject.callMethod("getMslAltitudeMeters"); +@@ -451,7 +454,7 @@ namespace AndroidPositioning { + if (location == nullptr) + return QGeoPositionInfo(); + +- const QGeoPositionInfo info = positionInfoFromJavaLocation(location); ++ const QGeoPositionInfo info = positionInfoFromJavaLocation(location, useAltitudeConverter); + + return info; + } +@@ -615,7 +618,6 @@ static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location loca + { + Q_UNUSED(env); + Q_UNUSED(thiz); +- QGeoPositionInfo info = AndroidPositioning::positionInfoFromJavaLocation(location.object()); + + QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey); + if (!source) { +@@ -623,6 +625,10 @@ static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location loca + return; + } + ++ const bool useAltitudeConverter = source->useAltitudeConverter(); ++ QGeoPositionInfo info = ++ AndroidPositioning::positionInfoFromJavaLocation(location.object(), useAltitudeConverter); ++ + //we need to invoke indirectly as the Looper thread is likely to be not the same thread + if (!isSingleUpdate) + QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection, diff --git a/vcpkg/ports/qtpositioning/ios_wgs84_ellipsoid.patch b/vcpkg/ports/qtpositioning/ios_wgs84_ellipsoid.patch new file mode 100644 index 000000000..518419a15 --- /dev/null +++ b/vcpkg/ports/qtpositioning/ios_wgs84_ellipsoid.patch @@ -0,0 +1,13 @@ +diff --git a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm +index 95d51a86..7529df9d 100644 +--- a/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm ++++ b/src/plugins/position/corelocation/qgeopositioninfosource_cl.mm +@@ -51,7 +51,7 @@ + // Construct position info from location data + QGeoPositionInfo location(QGeoCoordinate(newLocation.coordinate.latitude, + newLocation.coordinate.longitude, +- newLocation.altitude), ++ newLocation.ellipsoidalAltitude), + timeStamp); + if (newLocation.horizontalAccuracy >= 0) + location.setAttribute(QGeoPositionInfo::HorizontalAccuracy, newLocation.horizontalAccuracy); diff --git a/vcpkg/ports/qtpositioning/portfile.cmake b/vcpkg/ports/qtpositioning/portfile.cmake index a5b211b1f..576381ac8 100644 --- a/vcpkg/ports/qtpositioning/portfile.cmake +++ b/vcpkg/ports/qtpositioning/portfile.cmake @@ -3,7 +3,10 @@ include("${SCRIPT_PATH}/qt_install_submodule.cmake") set(${PORT}_PATCHES devendor-poly2tri.patch - foregroundservice.patch) + foregroundservice.patch + ios_wgs84_ellipsoid.patch + # TODO: The android patch should be removed after migration to Qt 6.9+ as it is a backport of their bugfix + android15_altitude_fix.patch) vcpkg_check_features(OUT_FEATURE_OPTIONS FEATURE_OPTIONS FEATURES