From f012b7a52a4bb1136ea5a73488a53c4075874290 Mon Sep 17 00:00:00 2001 From: onur Date: Sat, 18 Jul 2026 18:06:02 +0300 Subject: [PATCH] Lock map tracking to corrected IMU output --- src/GlobeWidget.cpp | 11 ++++++--- src/MainWindow.cpp | 1 + src/MapWidget.cpp | 46 ++++++++++++++++++++++++------------- src/MapWidget.h | 2 ++ tests/main_window_tests.cpp | 22 ++++++++++++++++++ 5 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/GlobeWidget.cpp b/src/GlobeWidget.cpp index 7a5b025..d6661a8 100644 --- a/src/GlobeWidget.cpp +++ b/src/GlobeWidget.cpp @@ -465,9 +465,14 @@ void GlobeWidget::drawBlueMarble(QPainter &painter) const } const double radius = std::min(width(), height()) * 0.255 * m_zoom; - const double pixelRatio = std::min(devicePixelRatioF(), 2.0); - const int textureSize = std::clamp( - static_cast(std::ceil(radius * 2.0 * pixelRatio)), 420, 1200); + // Interactive rotation favors latency; mouse release immediately causes + // a full Retina-quality rebuild. Satellite animation frames reuse the + // cached texture and therefore do not repeat this projection work. + const double pixelRatio = m_dragging ? std::min(devicePixelRatioF(), 1.0) + : std::min(devicePixelRatioF(), 2.0); + const int textureSize = m_dragging + ? std::clamp(static_cast(std::ceil(radius * 2.0 * pixelRatio)), 280, 650) + : std::clamp(static_cast(std::ceil(radius * 2.0 * pixelRatio)), 420, 1200); const QPointF center(width() * 0.5, height() * 0.52); const QRectF target(center.x() - radius, center.y() - radius, radius * 2.0, radius * 2.0); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index fd533c1..c841c80 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -164,6 +164,7 @@ LocationAnalysis analyzeLocations(const QVector &points) QVector preferredAnalysisPoints(const QVector &points) { QVector corrected; + corrected.reserve(points.size()); for (const TelemetryPoint &point : points) { if (point.source == LocationSource::ImuCorrected) { corrected.push_back(point); diff --git a/src/MapWidget.cpp b/src/MapWidget.cpp index e305f85..9d5eb87 100644 --- a/src/MapWidget.cpp +++ b/src/MapWidget.cpp @@ -91,9 +91,9 @@ void MapWidget::setPoints(const QVector &points) m_points = points; if (!m_points.isEmpty() && m_autoFollow) { - const TelemetryPoint &last = m_points.last(); - m_centerLat = last.latitude; - m_centerLon = last.longitude; + const TelemetryPoint &focus = m_points[preferredFocusIndex()]; + m_centerLat = focus.latitude; + m_centerLon = focus.longitude; if (wasEmpty) { m_zoom = MaxZoom; } @@ -232,9 +232,9 @@ void MapWidget::setAutoFollow(bool enabled) m_autoFollow = enabled; if (m_autoFollow && !m_points.isEmpty()) { - const TelemetryPoint &last = m_points.last(); - m_centerLat = last.latitude; - m_centerLon = last.longitude; + const TelemetryPoint &focus = m_points[preferredFocusIndex()]; + m_centerLat = focus.latitude; + m_centerLon = focus.longitude; update(); } emit autoFollowChanged(m_autoFollow); @@ -522,9 +522,10 @@ void MapWidget::drawTelemetry(QPainter &painter) drawCepMarker(painter); const bool drawNumbers = m_points.size() <= 150; - const int firstPoint = m_showRecentPoints ? 0 : m_points.size() - 1; - for (int i = firstPoint; i < m_points.size(); ++i) { - const bool isLast = i == m_points.size() - 1; + const int focusIndex = preferredFocusIndex(); + for (int i = 0; i < m_points.size(); ++i) { + if (!m_showRecentPoints && i != focusIndex) continue; + const bool isLast = i == focusIndex; const QPointF screen = coordinateToScreen(m_points[i].latitude, m_points[i].longitude); const double radius = isLast ? 8.0 : 5.0; @@ -547,7 +548,7 @@ void MapWidget::drawTelemetry(QPainter &painter) } } - const TelemetryPoint &last = m_points.last(); + const TelemetryPoint &last = m_points[focusIndex]; const QPointF lastScreen = coordinateToScreen(last.latitude, last.longitude); QStringList lines; const QString latitudeText = last.latitudeText.isEmpty() @@ -655,9 +656,10 @@ void MapWidget::drawCepMarker(QPainter &painter) painter.drawLine(QPointF(screen.x(), screen.y() - 11.0), QPointF(screen.x(), screen.y() + 11.0)); - const bool overlapsLast = !m_points.isEmpty() - && QLineF(screen, coordinateToScreen(m_points.last().latitude, - m_points.last().longitude)).length() < 30.0; + const int focusIndex = preferredFocusIndex(); + const bool overlapsLast = focusIndex >= 0 + && QLineF(screen, coordinateToScreen(m_points[focusIndex].latitude, + m_points[focusIndex].longitude)).length() < 30.0; if (!overlapsLast) { painter.setFont(QFont(painter.font().family(), 9, QFont::Bold)); painter.setPen(QColor(20, 83, 78)); @@ -691,9 +693,10 @@ void MapWidget::drawAverageOverlay(QPainter &painter) painter.drawLine(QPointF(screen.x() - 12.0, screen.y()), QPointF(screen.x() + 12.0, screen.y())); painter.drawLine(QPointF(screen.x(), screen.y() - 12.0), QPointF(screen.x(), screen.y() + 12.0)); - const bool overlapsLast = !m_points.isEmpty() - && QLineF(screen, coordinateToScreen(m_points.last().latitude, - m_points.last().longitude)).length() < 30.0; + const int focusIndex = preferredFocusIndex(); + const bool overlapsLast = focusIndex >= 0 + && QLineF(screen, coordinateToScreen(m_points[focusIndex].latitude, + m_points[focusIndex].longitude)).length() < 30.0; if (!overlapsLast) { painter.setFont(QFont(painter.font().family(), 9, QFont::Bold)); const QRectF labelRect(screen + QPointF(12.0, 8.0), QSizeF(72.0, 20.0)); @@ -773,3 +776,14 @@ void MapWidget::disableAutoFollowFromUserAction() m_autoFollow = false; emit autoFollowChanged(false); } + +int MapWidget::preferredFocusIndex() const +{ + // Once corrected ESKF output is present, both camera and detail bubble + // stay locked to its newest point. Raw GPS and truth records may arrive + // later in an interleaved stream and must not pull the camera away. + for (int index = m_points.size() - 1; index >= 0; --index) { + if (m_points[index].source == LocationSource::ImuCorrected) return index; + } + return m_points.isEmpty() ? -1 : m_points.size() - 1; +} diff --git a/src/MapWidget.h b/src/MapWidget.h index 9f42459..9d99767 100644 --- a/src/MapWidget.h +++ b/src/MapWidget.h @@ -27,6 +27,7 @@ public: void setCepMarker(double latitude, double longitude, const std::optional &radiusMeters); void clearCepMarker(); + [[nodiscard]] QPointF centerCoordinate() const { return {m_centerLat, m_centerLon}; } public slots: void setAutoFollow(bool enabled); @@ -66,6 +67,7 @@ private: void requestTile(int tileX, int tileY, int zoom); void setZoomKeepingPoint(int newZoom, const QPointF &screenAnchor); void disableAutoFollowFromUserAction(); + int preferredFocusIndex() const; QVector m_points; QNetworkAccessManager *m_network = nullptr; diff --git a/tests/main_window_tests.cpp b/tests/main_window_tests.cpp index 9c19132..4592436 100644 --- a/tests/main_window_tests.cpp +++ b/tests/main_window_tests.cpp @@ -17,6 +17,7 @@ #include #include +#include namespace { @@ -93,6 +94,27 @@ int main(int argc, char **argv) assert(toggleSerialAction != nullptr && !toggleSerialAction->shortcut().isEmpty()); assert(rinexUpdateAction != nullptr && !rinexUpdateAction->shortcut().isEmpty()); + QVector followPoints; + TelemetryPoint gpsPoint; + gpsPoint.source = LocationSource::Gps; + gpsPoint.latitude = 40.0; + gpsPoint.longitude = 29.0; + followPoints.push_back(gpsPoint); + TelemetryPoint correctedPoint = gpsPoint; + correctedPoint.source = LocationSource::ImuCorrected; + correctedPoint.latitude = 40.25; + correctedPoint.longitude = 29.5; + followPoints.push_back(correctedPoint); + TelemetryPoint trajectoryPoint = gpsPoint; + trajectoryPoint.source = LocationSource::Trajectory; + trajectoryPoint.latitude = 41.0; + trajectoryPoint.longitude = 30.0; + followPoints.push_back(trajectoryPoint); + map->setPoints(followPoints); + assert(std::abs(map->centerCoordinate().x() - 40.25) < 0.000001); + assert(std::abs(map->centerCoordinate().y() - 29.5) < 0.000001); + map->clearPoints(); + assert(QMetaObject::invokeMethod(map, "referenceLocationSelected", Qt::DirectConnection, Q_ARG(double, 40.25), Q_ARG(double, 29.5))); assert(referenceLatInput->text() == QStringLiteral("40.250000000000000"));