diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 03f4481a12..2328f5d004 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -34,6 +34,7 @@ set(QFIELD_CORE_SRCS positioning/positioning.cpp positioning/positioningdevicemodel.cpp positioning/geofencer.cpp + positioning/positioninginformationmodel.cpp processing/processingalgorithm.cpp processing/processingalgorithmparametersmodel.cpp processing/processingalgorithmsmodel.cpp @@ -152,6 +153,7 @@ set(QFIELD_CORE_HDRS positioning/tcpreceiver.h positioning/udpreceiver.h positioning/geofencer.h + positioning/positioninginformationmodel.h processing/processingalgorithm.h processing/processingalgorithmparametersmodel.h processing/processingalgorithmsmodel.h diff --git a/src/core/positioning/abstractgnssreceiver.h b/src/core/positioning/abstractgnssreceiver.h index 14110fb8e3..84cb6b2e20 100644 --- a/src/core/positioning/abstractgnssreceiver.h +++ b/src/core/positioning/abstractgnssreceiver.h @@ -61,6 +61,8 @@ class AbstractGnssReceiver : public QObject Q_INVOKABLE virtual AbstractGnssReceiver::Capabilities capabilities() const { return NoCapabilities; } + virtual QList> details() { return {}; } + signals: void validChanged(); void lastGnssPositionInformationChanged( GnssPositionInformation &lastGnssPositionInformation ); diff --git a/src/core/positioning/nmeagnssreceiver.cpp b/src/core/positioning/nmeagnssreceiver.cpp index 348eced029..3c67744500 100644 --- a/src/core/positioning/nmeagnssreceiver.cpp +++ b/src/core/positioning/nmeagnssreceiver.cpp @@ -116,6 +116,20 @@ void NmeaGnssReceiver::handleStopLogging() mLogFile.close(); } +QList> NmeaGnssReceiver::details() +{ + QList> dataList; + + dataList.append( qMakePair( "PDOP", QLocale::system().toString( mCurrentNmeaGnssPositionInformation.pdop(), 'f', 1 ) ) ); + dataList.append( qMakePair( "HDOP", QLocale::system().toString( mCurrentNmeaGnssPositionInformation.hdop(), 'f', 1 ) ) ); + dataList.append( qMakePair( "VDOP", QLocale::system().toString( mCurrentNmeaGnssPositionInformation.vdop(), 'f', 1 ) ) ); + dataList.append( qMakePair( "Valid", mCurrentNmeaGnssPositionInformation.isValid() ? "True" : "False" ) ); + dataList.append( qMakePair( "Fix", mCurrentNmeaGnssPositionInformation.fixStatusDescription() ) ); + dataList.append( qMakePair( "Quality", mCurrentNmeaGnssPositionInformation.qualityDescription() ) ); + + return dataList; +} + void NmeaGnssReceiver::processImuSentence( const QString &sentence ) { static const int PARAMETER_STATUS_INDEX = 19; diff --git a/src/core/positioning/nmeagnssreceiver.h b/src/core/positioning/nmeagnssreceiver.h index e48cf87e79..ec389f3427 100644 --- a/src/core/positioning/nmeagnssreceiver.h +++ b/src/core/positioning/nmeagnssreceiver.h @@ -50,6 +50,7 @@ class NmeaGnssReceiver : public AbstractGnssReceiver private: void handleStartLogging() override; void handleStopLogging() override; + QList> details() override; void processImuSentence( const QString &sentence ); diff --git a/src/core/positioning/positioninginformationmodel.cpp b/src/core/positioning/positioninginformationmodel.cpp new file mode 100644 index 0000000000..b83f391b7d --- /dev/null +++ b/src/core/positioning/positioninginformationmodel.cpp @@ -0,0 +1,307 @@ +#include "coordinatereferencesystemutils.h" +#include "geometryutils.h" +#include "positioninginformationmodel.h" + +#include +#include + +PositioningInformationModel::PositioningInformationModel( QObject *parent ) + : QStandardItemModel( parent ) +{ + connect( this, &QStandardItemModel::dataChanged, this, &PositioningInformationModel::onDataChanged ); +} + +void PositioningInformationModel::refreshData() +{ + if ( !mPositioningSource ) + { + return; + } + + const double distanceUnitFactor = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, distanceUnits() ); + const QString distanceUnitAbbreviation = QgsUnitTypes::toAbbreviatedString( distanceUnits() ); + const QList> deviceDetails = mPositioningSource->device()->details(); + + updateCoordinates(); + + const QString altitude = getAltitude( distanceUnitFactor, distanceUnitAbbreviation ); + const QString speed = getSpeed(); + const QString hAccuracy = getHorizontalAccuracy( distanceUnitFactor, distanceUnitAbbreviation ); + const QString vAccuracy = getVerticalAccuracy( distanceUnitFactor, distanceUnitAbbreviation ); + + updateInfo( tr( "Altitude" ), altitude ); + updateInfo( tr( "Speed" ), speed ); + updateInfo( tr( "H. Accuracy" ), hAccuracy ); + updateInfo( tr( "V. Accuracy" ), vAccuracy ); + + for ( int i = 0; i < deviceDetails.size(); ++i ) + { + const QString key = deviceDetails[i].first; + const QVariant value = deviceDetails[i].second; + + updateInfo( key, value ); + } +} + +void PositioningInformationModel::getCoordinateLabels( QString &coord1Label, QString &coord2Label, bool coordinatesIsXY, bool isGeographic ) +{ + if ( coordinatesIsXY ) + { + coord1Label = isGeographic ? tr( "Lon" ) : tr( "X" ); + coord2Label = isGeographic ? tr( "Lat" ) : tr( "Y" ); + } + else + { + coord1Label = isGeographic ? tr( "Lat" ) : tr( "Y" ); + coord2Label = isGeographic ? tr( "Lon" ) : tr( "X" ); + } +} + +void PositioningInformationModel::getCoordinateValues( QString &coord1Value, QString &coord2Value, const QgsPoint &coordinates, bool coordinatesIsXY, bool isGeographic ) +{ + if ( coordinatesIsXY ) + { + if ( positioningSource()->positionInformation().longitudeValid() ) + { + coord1Value = QLocale::system().toString( coordinates.x(), 'f', isGeographic ? 7 : 3 ); + coord2Value = QLocale::system().toString( coordinates.y(), 'f', isGeographic ? 7 : 3 ); + } + else + { + coord1Value = coord2Value = tr( "N/A" ); + } + } + else + { + if ( positioningSource()->positionInformation().latitudeValid() ) + { + coord1Value = QLocale::system().toString( coordinates.y(), 'f', isGeographic ? 7 : 3 ); + coord2Value = QLocale::system().toString( coordinates.x(), 'f', isGeographic ? 7 : 3 ); + } + else + { + coord1Value = coord2Value = tr( "N/A" ); + } + } +} + +QString PositioningInformationModel::getAltitude( double distanceUnitFactor, const QString &distanceUnitAbbreviation ) +{ + if ( positioningSource()->positionInformation().elevationValid() ) + { + QString altitude = QLocale::system().toString( positioningSource()->projectedPosition().z() * distanceUnitFactor, 'f', 3 ) + ' ' + distanceUnitAbbreviation + ' '; + QStringList details; + + if ( positioningSource()->elevationCorrectionMode() == Positioning::ElevationCorrectionMode::OrthometricFromGeoidFile ) + { + details.push_back( tr( "grid" ) ); + } + else if ( positioningSource()->elevationCorrectionMode() == Positioning::ElevationCorrectionMode::OrthometricFromDevice ) + { + details.push_back( tr( "ortho." ) ); + } + if ( antennaHeight() != 0 ) + { + details.push_back( tr( "ant." ) ); + } + if ( details.length() > 0 ) + { + altitude += QString( " (%1)" ).arg( details.join( ", " ) ); + } + return altitude; + } + return tr( "N/A" ); +} + +QString PositioningInformationModel::getSpeed() +{ + return positioningSource()->positionInformation().speedValid() ? QLocale::system().toString( positioningSource()->positionInformation().speed(), 'f', 3 ) + " m/s" : tr( "N/A" ); +} + +QString PositioningInformationModel::getHorizontalAccuracy( double distanceUnitFactor, const QString &distanceUnitAbbreviation ) +{ + return positioningSource()->positionInformation().haccValid() ? QLocale::system().toString( positioningSource()->positionInformation().hacc() * distanceUnitFactor, 'f', 3 ) + ' ' + distanceUnitAbbreviation : tr( "N/A" ); +} + +QString PositioningInformationModel::getVerticalAccuracy( double distanceUnitFactor, const QString &distanceUnitAbbreviation ) +{ + return positioningSource()->positionInformation().vaccValid() ? QLocale::system().toString( positioningSource()->positionInformation().vacc() * distanceUnitFactor, 'f', 3 ) + ' ' + distanceUnitAbbreviation : tr( "N/A" ); +} + +void PositioningInformationModel::updateInfo( const QString &name, const QVariant &value ) +{ + for ( int row = 0; row < rowCount(); ++row ) + { + QStandardItem *rowItem = item( row ); + + if ( rowItem->data( NameRole ).toString() == name ) + { + rowItem->setData( value.toString(), ValueRole ); + return; + } + } + + QStandardItem *nameItem = new QStandardItem( name ); + nameItem->setData( name, NameRole ); + nameItem->setData( value.toString(), ValueRole ); + insertRow( rowCount(), QList() << nameItem ); +} + + +void PositioningInformationModel::updateCoordinates() +{ + const bool coordinatesIsXY = CoordinateReferenceSystemUtils::defaultCoordinateOrderForCrsIsXY( coordinateDisplayCrs() ); + const bool coordinatesIsGeographic = coordinateDisplayCrs().isGeographic(); + const QgsPoint coordinates = GeometryUtils::reprojectPoint( positioningSource()->sourcePosition(), CoordinateReferenceSystemUtils::wgs84Crs(), coordinateDisplayCrs() ); + + QString coord1Label, coord2Label; + QString coord1Value, coord2Value; + + getCoordinateLabels( coord1Label, coord2Label, coordinatesIsXY, coordinatesIsGeographic ); + getCoordinateValues( coord1Value, coord2Value, coordinates, coordinatesIsXY, coordinatesIsGeographic ); + + if ( rowCount() == 0 ) + { + updateInfo( coord1Label, coord1Value ); + updateInfo( coord2Label, coord2Value ); + } + else + { + QStandardItem *coordinates1 = item( 0 ); + QStandardItem *coordinates2 = item( 1 ); + + coordinates1->setData( coord1Label, NameRole ); + coordinates1->setData( coord1Value, ValueRole ); + coordinates2->setData( coord2Label, NameRole ); + coordinates2->setData( coord2Value, ValueRole ); + } +} + +bool PositioningInformationModel::setData( const QModelIndex &index, const QVariant &value, int role ) +{ + QStandardItem *rowItem = item( index.row() ); + if ( !rowItem ) + { + return false; + } + + switch ( role ) + { + case NameRole: + if ( rowItem->data( NameRole ) == value ) + { + return false; + } + + rowItem->setData( value, NameRole ); + return true; + + case ValueRole: + + if ( rowItem->data( ValueRole ) == value ) + { + return false; + } + + rowItem->setData( value, ValueRole ); + return true; + + default: + break; + } + + return false; +} + +QHash PositioningInformationModel::roleNames() const +{ + QHash names = QStandardItemModel::roleNames(); + names[NameRole] = "Name"; + names[ValueRole] = "Value"; + return names; +} + +void PositioningInformationModel::onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ) +{ + Q_UNUSED( bottomRight ) + Q_UNUSED( roles ) +} + +Positioning *PositioningInformationModel::positioningSource() const +{ + return mPositioningSource; +} + +void PositioningInformationModel::setPositioningSource( Positioning *positioningSource ) +{ + if ( mPositioningSource == positioningSource ) + return; + + if ( mPositioningSource ) + { + disconnect( mPositioningSource, &Positioning::positionInformationChanged, this, &PositioningInformationModel::refreshData ); + disconnect( mPositioningSource, &Positioning::deviceChanged, this, &PositioningInformationModel::softReset ); + } + + mPositioningSource = positioningSource; + emit positioningSourceChanged(); + + if ( mPositioningSource ) + { + connect( mPositioningSource, &Positioning::positionInformationChanged, this, &PositioningInformationModel::refreshData ); + connect( mPositioningSource, &Positioning::deviceChanged, this, &PositioningInformationModel::softReset ); + refreshData(); + } +} + +void PositioningInformationModel::softReset() +{ + if ( mPositioningSource->deviceId() == "" ) + removeRows( 6, rowCount() - 6 ); +} + +double PositioningInformationModel::antennaHeight() const +{ + return mAntennaHeight; +} + +void PositioningInformationModel::setAntennaHeight( double antennaHeight ) +{ + if ( qFuzzyCompare( mAntennaHeight, antennaHeight ) ) + return; + mAntennaHeight = antennaHeight; + emit antennaHeightChanged(); + + const double distanceUnitFactor = QgsUnitTypes::fromUnitToUnitFactor( Qgis::DistanceUnit::Meters, distanceUnits() ); + const QString distanceUnitAbbreviation = QgsUnitTypes::toAbbreviatedString( distanceUnits() ); + const QString altitude = getAltitude( distanceUnitFactor, distanceUnitAbbreviation ); + updateInfo( tr( "Altitude" ), altitude ); +} + +Qgis::DistanceUnit PositioningInformationModel::distanceUnits() const +{ + return mDistanceUnits; +} + +void PositioningInformationModel::setDistanceUnits( Qgis::DistanceUnit distanceUnits ) +{ + if ( mDistanceUnits == distanceUnits ) + return; + mDistanceUnits = distanceUnits; + emit distanceUnitsChanged(); +} + +QgsCoordinateReferenceSystem PositioningInformationModel::coordinateDisplayCrs() const +{ + return mCoordinateDisplayCrs; +} + +void PositioningInformationModel::setCoordinateDisplayCrs( const QgsCoordinateReferenceSystem &coordinateDisplayCrs ) +{ + if ( mCoordinateDisplayCrs == coordinateDisplayCrs ) + return; + mCoordinateDisplayCrs = coordinateDisplayCrs; + emit coordinateDisplayCrsChanged(); + + updateCoordinates(); +} diff --git a/src/core/positioning/positioninginformationmodel.h b/src/core/positioning/positioninginformationmodel.h new file mode 100644 index 0000000000..d574c0facd --- /dev/null +++ b/src/core/positioning/positioninginformationmodel.h @@ -0,0 +1,120 @@ +#pragma once + +#include "positioning.h" + +#include +#include + +class PositioningInformationModel : public QStandardItemModel +{ + Q_OBJECT + + Q_PROPERTY( Positioning *positioningSource READ positioningSource WRITE setPositioningSource NOTIFY positioningSourceChanged ) + Q_PROPERTY( double antennaHeight READ antennaHeight WRITE setAntennaHeight NOTIFY antennaHeightChanged ) + Q_PROPERTY( Qgis::DistanceUnit distanceUnits READ distanceUnits WRITE setDistanceUnits NOTIFY distanceUnitsChanged ) + Q_PROPERTY( QgsCoordinateReferenceSystem coordinateDisplayCrs READ coordinateDisplayCrs WRITE setCoordinateDisplayCrs NOTIFY coordinateDisplayCrsChanged ) + + public: + enum Roles + { + NameRole = Qt::UserRole, + ValueRole = Qt::UserRole + 1, + }; + + PositioningInformationModel( QObject *parent = nullptr ); + + /** + * @brief Sets the data for the given index + * @param index The index to set the data for + * @param value The new value + * @param role The role of the data + */ + bool setData( const QModelIndex &index, const QVariant &value, int role ) override; + + /** + * @brief Returns the names of the roles used by the model + */ + QHash roleNames() const override; + + /** + * @brief Refreshes the data in the model + */ + void refreshData(); + + /** + * @brief Updates the information in the model with the given name and value + * @param name The name of the information to update + * @param value The new value + */ + void updateInfo( const QString &name, const QVariant &value ); + + /** + * @brief Returns the positioning source + */ + Positioning *positioningSource() const; + + /** + * @brief Updates the positioning source and reconnects the positionInformationChanged signal to refreshData, while disconnecting the previous connection. + * @param positioningSource The new positioning source to update with + */ + void setPositioningSource( Positioning *positioningSource ); + + /** + * @brief Returns the antenna height + */ + double antennaHeight() const; + + /** + * @brief Sets the antenna height + * @param newAntennaHeight The new antenna height + */ + void setAntennaHeight( double antennaHeight ); + + /** + * @brief Returns the distance units + */ + Qgis::DistanceUnit distanceUnits() const; + + /** + * @brief Sets the distance units + * @param newDistanceUnits The new distance units + */ + void setDistanceUnits( Qgis::DistanceUnit distanceUnits ); + + /** + * @brief Returns the coordinate display CRS + */ + QgsCoordinateReferenceSystem coordinateDisplayCrs() const; + + /** + * @brief Sets the coordinate display CRS + * @param newCoordinateDisplayCrs The new CRS + */ + void setCoordinateDisplayCrs( const QgsCoordinateReferenceSystem &coordinateDisplayCrs ); + + signals: + void positioningSourceChanged(); + void antennaHeightChanged(); + void distanceUnitsChanged(); + void coordinateDisplayCrsChanged(); + + private slots: + void onDataChanged( const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector &roles ); + + private: + void getCoordinateLabels( QString &coord1Label, QString &coord2Label, bool coordinatesIsXY, bool isGeographic ); + void getCoordinateValues( QString &coord1Value, QString &coord2Value, const QgsPoint &coordinates, bool coordinatesIsXY, bool isGeographic ); + void softReset(); + void updateCoordinates(); + QString getAltitude( double distanceUnitFactor, const QString &distanceUnitAbbreviation ); + QString getSpeed(); + QString getHorizontalAccuracy( double distanceUnitFactor, const QString &distanceUnitAbbreviation ); + QString getVerticalAccuracy( double distanceUnitFactor, const QString &distanceUnitAbbreviation ); + + private: + Positioning *mPositioningSource = nullptr; + double mAntennaHeight = -1; + Qgis::DistanceUnit mDistanceUnits; + QgsCoordinateReferenceSystem mCoordinateDisplayCrs; + QMetaObject::Connection positioningSourceConnection; +}; diff --git a/src/core/qgismobileapp.cpp b/src/core/qgismobileapp.cpp index 60258dfb87..301f3a10ff 100644 --- a/src/core/qgismobileapp.cpp +++ b/src/core/qgismobileapp.cpp @@ -88,6 +88,7 @@ #include "platformutilities.h" #include "positioning.h" #include "positioningdevicemodel.h" +#include "positioninginformationmodel.h" #include "positioningutils.h" #include "printlayoutlistmodel.h" #include "processingalgorithm.h" @@ -502,6 +503,7 @@ void QgisMobileapp::initDeclarative( QQmlEngine *engine ) qmlRegisterType( "org.qfield", 1, 0, "Navigation" ); qmlRegisterType( "org.qfield", 1, 0, "NavigationModel" ); qmlRegisterType( "org.qfield", 1, 0, "Positioning" ); + qmlRegisterType( "org.qfield", 1, 0, "PositioningInformationModel" ); qmlRegisterType( "org.qfield", 1, 0, "PositioningDeviceModel" ); qmlRegisterType( "org.qfield", 1, 0, "AudioRecorder" ); qmlRegisterType( "org.qfield", 1, 0, "BarcodeDecoder" ); diff --git a/src/qml/InformationDrawer.qml b/src/qml/InformationDrawer.qml index b2bf051c43..3beab032fc 100644 --- a/src/qml/InformationDrawer.qml +++ b/src/qml/InformationDrawer.qml @@ -65,10 +65,10 @@ Item { PositioningInformationView { id: positioningInformationView width: parent.width - height: contentHeight + height: Math.min(contentHeight, mainWindow.height / 3) visible: positioningInformationViewEnabled positionSource: controller.positionSource - antennaHeight: positioningSettings.antennaHeightActivated ? positioningSettings.antennaHeight : NaN + antennaHeight: positioningSettings.antennaHeightActivated ? positioningSettings.antennaHeight : 0 } } diff --git a/src/qml/PositioningInformationView.qml b/src/qml/PositioningInformationView.qml index e8dbe1851e..d69454f7ae 100644 --- a/src/qml/PositioningInformationView.qml +++ b/src/qml/PositioningInformationView.qml @@ -8,392 +8,82 @@ import Theme Rectangle { id: positioningInformationView - property Positioning positionSource - property var coordinates: GeometryUtils.reprojectPoint(positionSource.sourcePosition, CoordinateReferenceSystemUtils.wgs84Crs(), projectInfo.coordinateDisplayCrs) - property bool coordinatesIsXY: CoordinateReferenceSystemUtils.defaultCoordinateOrderForCrsIsXY(projectInfo.coordinateDisplayCrs) - property bool coordinatesIsGeographic: projectInfo.coordinateDisplayCrs.isGeographic + property alias positionSource: positioningInformationModel.positioningSource + property alias antennaHeight: positioningInformationModel.antennaHeight - property double distanceUnitFactor: UnitTypes.fromUnitToUnitFactor(Qgis.DistanceUnit.Meters, projectInfo.distanceUnits) - property string distanceUnitAbbreviation: UnitTypes.toAbbreviatedString(projectInfo.distanceUnits) - - property double antennaHeight: NaN - property double cellHeight: 26 - property double cellPadding: 6 property color backgroundColor: "transparent" property color alternateBackgroundColor: Theme.positionBackgroundColor property color textColor: positionSource.currentness ? Theme.mainTextColor : Theme.secondaryTextColor - - property real contentHeight: grid.rows * positioningInformationView.cellHeight - width: parent.width - anchors.margins: 20 + property double cellHeight: 26 + property double cellPadding: 6 + property real contentHeight: grid.numberOfRows * cellHeight color: Theme.mainBackgroundColorSemiOpaque + anchors.margins: 20 + width: parent.width + clip: true - Grid { - id: grid - flow: GridLayout.TopToBottom - rows: (positionSource.deviceId === '' ? 1 : 2) * (parent.width > 1000 ? 1 : parent.width > 620 ? 2 : 3) + Flickable { width: parent.width - property double cellWidth: grid.width / ((positionSource.deviceId === '' ? 1 : 2) * 6 / grid.rows) - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: alternateBackgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: coordinatesIsXY ? coordinatesIsGeographic ? qsTr("Lon") : qsTr("X") : coordinatesIsGeographic ? qsTr("Lat") : qsTr("Y") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: coordinatesIsXY ? positionSource.positionInformation && positionSource.positionInformation.longitudeValid ? Number(coordinates.x).toLocaleString(Qt.locale(), 'f', coordinatesIsGeographic ? 7 : 3) : qsTr("N/A") : positionSource.positionInformation && positionSource.positionInformation.latitudeValid ? Number(coordinates.y).toLocaleString(Qt.locale(), 'f', coordinatesIsGeographic ? 7 : 3) : qsTr("N/A") - elide: Text.ElideRight - } - } + height: positioningInformationView.height + contentHeight: grid.height + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: QfScrollBar { } - Rectangle { - height: cellHeight - width: grid.cellWidth - color: backgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right + GridView { + id: grid - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: coordinatesIsXY ? coordinatesIsGeographic ? qsTr("Lat") : qsTr("Y") : coordinatesIsGeographic ? qsTr("Lon") : qsTr("X") - } + readonly property real numberOfColumns: parent.width > 1000 ? 6 : parent.width > 620 ? 3 : 2 + readonly property real numberOfRows: grid.count / numberOfColumns - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: coordinatesIsXY ? positionSource.positionInformation && positionSource.positionInformation.longitudeValid ? Number(coordinates.y).toLocaleString(Qt.locale(), 'f', coordinatesIsGeographic ? 7 : 3) : qsTr("N/A") : positionSource.positionInformation && positionSource.positionInformation.latitudeValid ? Number(coordinates.x).toLocaleString(Qt.locale(), 'f', coordinatesIsGeographic ? 7 : 3) : qsTr("N/A") - elide: Text.ElideRight - } + flow: GridView.FlowTopToBottom + boundsBehavior: Flickable.StopAtBounds + model: PositioningInformationModel { + id: positioningInformationModel + distanceUnits: projectInfo.distanceUnits + coordinateDisplayCrs: projectInfo.coordinateDisplayCrs } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 2 ? backgroundColor : alternateBackgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("Altitude") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: { - var altitude = ''; - if (positionSource.positionInformation && positionSource.positionInformation.elevationValid) { - altitude += Number(positionSource.projectedPosition.z * distanceUnitFactor).toLocaleString(Qt.locale(), 'f', 3) + ' ' + distanceUnitAbbreviation + ' '; - var details = []; - if (positionSource.elevationCorrectionMode === Positioning.ElevationCorrectionMode.OrthometricFromGeoidFile) { - details.push('grid'); - } else if (positionSource.elevationCorrectionMode === Positioning.ElevationCorrectionMode.OrthometricFromDevice) { - details.push('ortho.'); - } - if (!isNaN(parseFloat(antennaHeight))) { - details.push('ant.'); - } - if (details.length > 0) { - altitude += ' (%1)'.arg(details.join(', ')); - } - } else { - altitude += qsTr('N/A'); - } - return altitude; + height: numberOfRows * cellHeight + width: parent.width + cellHeight: positioningInformationView.cellHeight + cellWidth: parent.width / numberOfColumns + clip: true + + delegate: Rectangle { + readonly property real currentColumn: parseInt(index / (grid.count / grid.numberOfColumns)) + readonly property real currentRow: index % (grid.count / grid.numberOfColumns) + + width: grid.cellWidth + height: grid.cellHeight + color: { + if (currentColumn % 2 == 0) { + return currentRow % 2 == 0 ? alternateBackgroundColor : backgroundColor; + } else { + return currentRow % 2 == 0 ? backgroundColor : alternateBackgroundColor; } - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 2 ? alternateBackgroundColor : backgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("Speed") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && positionSource.positionInformation.speedValid ? positionSource.positionInformation.speed.toLocaleString(Qt.locale(), 'f', 3) + " m/s" : qsTr("N/A") - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 4 ? backgroundColor : alternateBackgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("H. Accuracy") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && positionSource.positionInformation.haccValid ? Number(positionSource.positionInformation.hacc * distanceUnitFactor).toLocaleString(Qt.locale(), 'f', 3) + ' ' + distanceUnitAbbreviation : qsTr("N/A") - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 4 ? alternateBackgroundColor : backgroundColor - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("V. Accuracy") } - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && positionSource.positionInformation.vaccValid ? Number(positionSource.positionInformation.vacc * distanceUnitFactor).toLocaleString(Qt.locale(), 'f', 3) + ' ' + distanceUnitAbbreviation : qsTr("N/A") - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows % 2 === 0 ? backgroundColor : alternateBackgroundColor - visible: positionSource.deviceId !== '' - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("PDOP") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && !isNaN(positionSource.positionInformation.pdop) ? positionSource.positionInformation.pdop.toLocaleString(Qt.locale(), 'f', 1) : qsTr('N/A') - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows % 2 === 0 ? alternateBackgroundColor : backgroundColor - visible: positionSource.deviceId !== '' - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("HDOP") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && !isNaN(positionSource.positionInformation.hdop) ? positionSource.positionInformation.hdop.toLocaleString(Qt.locale(), 'f', 1) : qsTr('N/A') - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 6 ? backgroundColor : alternateBackgroundColor - visible: positionSource.deviceId !== '' - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("VDOP") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && !isNaN(positionSource.positionInformation.vdop) ? positionSource.positionInformation.vdop.toLocaleString(Qt.locale(), 'f', 1) : qsTr('N/A') - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 6 ? alternateBackgroundColor : backgroundColor - visible: positionSource.deviceId !== '' - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("Valid") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation && positionSource.positionInformation.isValid ? 'True' : 'False' - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 2 || grid.rows === 6 ? backgroundColor : alternateBackgroundColor - visible: positionSource.deviceId !== '' + RowLayout { + anchors.margins: cellPadding + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.right: parent.right - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("Fix") - } - - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation ? positionSource.positionInformation.fixStatusDescription : qsTr('N/A') - elide: Text.ElideRight - } - } - } - - Rectangle { - height: cellHeight - width: grid.cellWidth - color: grid.rows === 2 || grid.rows === 6 ? alternateBackgroundColor : backgroundColor - visible: positionSource.deviceId !== '' - - RowLayout { - anchors.margins: cellPadding - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.right: parent.right - - Text { - Layout.fillWidth: false - font: Theme.tipFont - color: Theme.secondaryTextColor - text: qsTr("Quality") - } + Text { + font: Theme.tipFont + color: Theme.secondaryTextColor + text: Name + } - Text { - Layout.fillWidth: true - font: Theme.tipFont - color: textColor - text: positionSource.positionInformation ? positionSource.positionInformation.qualityDescription : qsTr('N/A') - elide: Text.ElideRight + Text { + Layout.fillWidth: true + font: Theme.tipFont + color: positioningInformationView.textColor + text: Value ? Value : qsTr("N/A") + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } } } }