Add polished macOS location plotter experience
Ubuntu build / build-and-test (ubuntu-22.04) (push) Has been cancelled
Ubuntu build / build-and-test (ubuntu-24.04) (push) Has been cancelled

This commit is contained in:
2026-07-18 15:20:37 +03:00
parent 049f6301e8
commit e648eca324
11 changed files with 438 additions and 50 deletions
+82 -13
View File
@@ -4,6 +4,7 @@
#include <QLabel>
#include <QLineF>
#include <QMouseEvent>
#include <QNativeGestureEvent>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QNetworkRequest>
@@ -58,7 +59,7 @@ MapWidget::MapWidget(QWidget *parent)
: QWidget(parent)
, m_network(new QNetworkAccessManager(this))
{
setMinimumSize(640, 420);
setMinimumSize(480, 320);
setMouseTracking(true);
const QString cacheRoot = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
@@ -115,23 +116,55 @@ void MapWidget::fitToPoints()
}
double minimumLatitude = m_points.first().latitude;
double maximumLatitude = minimumLatitude;
double minimumLongitude = m_points.first().longitude;
double maximumLongitude = minimumLongitude;
QVector<double> longitudes;
longitudes.reserve(m_points.size());
for (const TelemetryPoint &point : m_points) {
minimumLatitude = std::min(minimumLatitude, point.latitude);
maximumLatitude = std::max(maximumLatitude, point.latitude);
minimumLongitude = std::min(minimumLongitude, point.longitude);
maximumLongitude = std::max(maximumLongitude, point.longitude);
double normalizedLongitude = std::fmod(point.longitude + 360.0, 360.0);
if (normalizedLongitude < 0.0) {
normalizedLongitude += 360.0;
}
longitudes.push_back(normalizedLongitude);
}
std::sort(longitudes.begin(), longitudes.end());
// Fit the shortest circular longitude interval. A plain min/max makes
// points around +180/-180 look almost a full world apart.
double arcStart = longitudes.first();
double longitudeSpan = 0.0;
if (longitudes.size() > 1) {
double largestGap = -1.0;
int largestGapIndex = 0;
for (int i = 0; i < longitudes.size(); ++i) {
const double current = longitudes[i];
const double next = i + 1 < longitudes.size()
? longitudes[i + 1] : longitudes.first() + 360.0;
const double gap = next - current;
if (gap > largestGap) {
largestGap = gap;
largestGapIndex = i;
}
}
arcStart = largestGapIndex + 1 < longitudes.size()
? longitudes[largestGapIndex + 1] : longitudes.first();
longitudeSpan = 360.0 - largestGap;
}
m_centerLat = 0.5 * (minimumLatitude + maximumLatitude);
m_centerLon = 0.5 * (minimumLongitude + maximumLongitude);
m_centerLon = std::fmod(arcStart + longitudeSpan * 0.5 + 180.0, 360.0);
if (m_centerLon < 0.0) {
m_centerLon += 360.0;
}
m_centerLon -= 180.0;
const double availableWidth = std::max(100.0, width() * 0.82);
const double availableHeight = std::max(100.0, height() * 0.82);
for (int zoom = MaxZoom; zoom >= MinZoom; --zoom) {
const QPointF topLeft = coordinateToWorld(maximumLatitude, minimumLongitude, zoom);
const QPointF bottomRight = coordinateToWorld(minimumLatitude, maximumLongitude, zoom);
if (std::fabs(bottomRight.x() - topLeft.x()) <= availableWidth
&& std::fabs(bottomRight.y() - topLeft.y()) <= availableHeight) {
const double worldWidth = static_cast<double>(TileSize) * static_cast<double>(1 << zoom);
const double longitudeWidth = longitudeSpan / 360.0 * worldWidth;
const double top = coordinateToWorld(maximumLatitude, m_centerLon, zoom).y();
const double bottom = coordinateToWorld(minimumLatitude, m_centerLon, zoom).y();
if (longitudeWidth <= availableWidth
&& std::fabs(bottom - top) <= availableHeight) {
m_zoom = zoom;
break;
}
@@ -260,16 +293,52 @@ void MapWidget::paintEvent(QPaintEvent *)
void MapWidget::wheelEvent(QWheelEvent *event)
{
const int delta = event->angleDelta().y();
if (delta == 0) {
const QPoint pixelDelta = event->pixelDelta();
const QPoint angleDelta = event->angleDelta();
const double delta = !pixelDelta.isNull()
? pixelDelta.y() / 36.0
: angleDelta.y() / 120.0;
if (qFuzzyIsNull(delta)) {
return;
}
disableAutoFollowFromUserAction();
setZoomKeepingPoint(m_zoom + (delta > 0 ? 1 : -1), event->position());
m_wheelZoomAccumulator += delta;
while (std::abs(m_wheelZoomAccumulator) >= 1.0) {
const int direction = m_wheelZoomAccumulator > 0.0 ? 1 : -1;
setZoomKeepingPoint(m_zoom + direction, event->position());
m_wheelZoomAccumulator -= direction;
}
if (event->phase() == Qt::ScrollEnd) {
m_wheelZoomAccumulator = 0.0;
}
event->accept();
}
bool MapWidget::event(QEvent *event)
{
if (event->type() == QEvent::NativeGesture) {
auto *gesture = static_cast<QNativeGestureEvent *>(event);
if (gesture->gestureType() == Qt::ZoomNativeGesture) {
disableAutoFollowFromUserAction();
// macOS reports pinch magnification as small deltas. Accumulate
// them so map-tile zoom remains stable instead of flickering.
m_wheelZoomAccumulator += gesture->value() * 5.0;
while (std::abs(m_wheelZoomAccumulator) >= 1.0) {
const int direction = m_wheelZoomAccumulator > 0.0 ? 1 : -1;
setZoomKeepingPoint(m_zoom + direction, gesture->position());
m_wheelZoomAccumulator -= direction;
}
gesture->accept();
return true;
}
if (gesture->gestureType() == Qt::EndNativeGesture) {
m_wheelZoomAccumulator = 0.0;
}
}
return QWidget::event(event);
}
void MapWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::RightButton) {