Marble

CurrentLocationWidget.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2004-2007 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5// SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
6// SPDX-FileCopyrightText: 2010 Bastian Holst <bastianholst@gmx.de>
7//
8
9// Self
10#include "CurrentLocationWidget.h"
11
12// Marble
13#include "GeoDataCoordinates.h"
14#include "MarbleDebug.h"
15#include "MarbleLocale.h"
16#include "MarbleModel.h"
17#include "MarbleWidget.h"
18#include "MarbleWidgetPopupMenu.h"
19#include "PluginManager.h"
20#include "PositionProviderPlugin.h"
21#include "PositionTracking.h"
22#include "routing/RoutingManager.h"
23
24using namespace Marble;
25/* TRANSLATOR Marble::CurrentLocationWidget */
26
27// Ui
28#include "ui_CurrentLocationWidget.h"
29
30#include <QDateTime>
31#include <QFileDialog>
32#include <QMessageBox>
33
34namespace Marble
35{
36
37class CurrentLocationWidgetPrivate
38{
39public:
40 CurrentLocationWidgetPrivate();
41
42 Ui::CurrentLocationWidget m_currentLocationUi;
43 MarbleWidget *m_widget;
44 AutoNavigation *m_adjustNavigation;
45
46 QList<const PositionProviderPlugin *> m_positionProviderPlugins;
47 GeoDataCoordinates m_currentPosition;
48
49 QString m_lastOpenPath;
50 QString m_lastSavePath;
51
52 void receiveGpsCoordinates(const GeoDataCoordinates &position, qreal speed);
53 void adjustPositionTrackingStatus(PositionProviderStatus status);
54 void centerOnCurrentLocation();
55 void updateRecenterComboBox(AutoNavigation::CenterMode centerMode);
56 void updateAutoZoomCheckBox(bool autoZoom);
57 void updateActivePositionProvider(PositionProviderPlugin *);
58 void updateGuidanceMode();
59 void saveTrack();
60 void openTrack();
61 void clearTrack();
62};
63
64CurrentLocationWidgetPrivate::CurrentLocationWidgetPrivate()
65 : m_widget(nullptr)
66 , m_adjustNavigation(nullptr)
67 , m_positionProviderPlugins()
68 , m_currentPosition()
69 , m_lastOpenPath()
70 , m_lastSavePath()
71{
72}
73
74CurrentLocationWidget::CurrentLocationWidget(QWidget *parent, Qt::WindowFlags f)
75 : QWidget(parent, f)
76 , d(new CurrentLocationWidgetPrivate())
77{
78 d->m_currentLocationUi.setupUi(this);
79 layout()->setContentsMargins({});
80
81 connect(d->m_currentLocationUi.recenterComboBox, &QComboBox::currentIndexChanged, this, &CurrentLocationWidget::setRecenterMode);
82
83 connect(d->m_currentLocationUi.autoZoomCheckBox, &QAbstractButton::clicked, this, &CurrentLocationWidget::setAutoZoom);
84
85 bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen;
86 d->m_currentLocationUi.positionTrackingComboBox->setVisible(!smallScreen);
87 d->m_currentLocationUi.locationLabel->setVisible(!smallScreen);
88}
89
90CurrentLocationWidget::~CurrentLocationWidget()
91{
92 delete d;
93}
94
95void CurrentLocationWidget::setMarbleWidget(MarbleWidget *widget)
96{
97 d->m_widget = widget;
98
99 delete d->m_adjustNavigation;
100 d->m_adjustNavigation = new AutoNavigation(widget->model(), widget->viewport(), this);
101
102 const PluginManager *pluginManager = d->m_widget->model()->pluginManager();
103 d->m_positionProviderPlugins = pluginManager->positionProviderPlugins();
104 for (const PositionProviderPlugin *plugin : std::as_const(d->m_positionProviderPlugins)) {
105 d->m_currentLocationUi.positionTrackingComboBox->addItem(plugin->guiString());
106 }
107 if (d->m_positionProviderPlugins.isEmpty()) {
108 d->m_currentLocationUi.positionTrackingComboBox->setEnabled(false);
109 QString html = tr("<p>No Position Tracking Plugin installed.</p>");
110 d->m_currentLocationUi.locationLabel->setText(html);
111 d->m_currentLocationUi.locationLabel->setEnabled(true);
112 bool const hasTrack = !d->m_widget->model()->positionTracking()->isTrackEmpty();
113 d->m_currentLocationUi.showTrackCheckBox->setEnabled(hasTrack);
114 d->m_currentLocationUi.saveTrackButton->setEnabled(hasTrack);
115 d->m_currentLocationUi.clearTrackButton->setEnabled(hasTrack);
116 }
117
118 // disconnect CurrentLocation Signals
119 disconnect(d->m_widget->model()->positionTracking(),
120 SIGNAL(gpsLocation(GeoDataCoordinates, qreal)),
121 this,
122 SLOT(receiveGpsCoordinates(GeoDataCoordinates, qreal)));
123 disconnect(d->m_widget->model()->positionTracking(),
124 SIGNAL(positionProviderPluginChanged(PositionProviderPlugin *)),
125 this,
126 SLOT(updateActivePositionProvider(PositionProviderPlugin *)));
127 disconnect(d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changePositionProvider(int)));
128 disconnect(d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)), this, SLOT(centerOnCurrentLocation()));
129 disconnect(d->m_widget->model()->positionTracking(),
130 SIGNAL(statusChanged(PositionProviderStatus)),
131 this,
132 SLOT(adjustPositionTrackingStatus(PositionProviderStatus)));
133
134 disconnect(d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark *)), this, SLOT(trackPlacemark()));
135
136 // connect CurrentLocation signals
137 connect(d->m_widget->model()->positionTracking(),
138 SIGNAL(gpsLocation(GeoDataCoordinates, qreal)),
139 this,
140 SLOT(receiveGpsCoordinates(GeoDataCoordinates, qreal)));
141 connect(d->m_widget->model()->positionTracking(),
142 SIGNAL(positionProviderPluginChanged(PositionProviderPlugin *)),
143 this,
144 SLOT(updateActivePositionProvider(PositionProviderPlugin *)));
145 d->updateActivePositionProvider(d->m_widget->model()->positionTracking()->positionProviderPlugin());
146 connect(d->m_currentLocationUi.positionTrackingComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changePositionProvider(int)));
147 connect(d->m_currentLocationUi.locationLabel, SIGNAL(linkActivated(QString)), this, SLOT(centerOnCurrentLocation()));
148 connect(d->m_widget->model()->positionTracking(),
149 SIGNAL(statusChanged(PositionProviderStatus)),
150 this,
151 SLOT(adjustPositionTrackingStatus(PositionProviderStatus)));
152
153 connect(d->m_adjustNavigation, SIGNAL(recenterModeChanged(AutoNavigation::CenterMode)), this, SLOT(updateRecenterComboBox(AutoNavigation::CenterMode)));
154 connect(d->m_adjustNavigation, SIGNAL(autoZoomToggled(bool)), this, SLOT(updateAutoZoomCheckBox(bool)));
155 connect(d->m_adjustNavigation, SIGNAL(zoomIn(FlyToMode)), d->m_widget, SLOT(zoomIn(FlyToMode)));
156 connect(d->m_adjustNavigation, SIGNAL(zoomOut(FlyToMode)), d->m_widget, SLOT(zoomOut(FlyToMode)));
157 connect(d->m_adjustNavigation, SIGNAL(centerOn(GeoDataCoordinates, bool)), d->m_widget, SLOT(centerOn(GeoDataCoordinates, bool)));
158
159 connect(d->m_widget, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), d->m_adjustNavigation, SLOT(inhibitAutoAdjustments()));
160 connect(d->m_widget->model()->routingManager(), SIGNAL(guidanceModeEnabledChanged(bool)), this, SLOT(updateGuidanceMode()));
161
162 connect(d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)), d->m_widget->model()->positionTracking(), SLOT(setTrackVisible(bool)));
163 connect(d->m_currentLocationUi.showTrackCheckBox, SIGNAL(clicked(bool)), d->m_widget, SLOT(update()));
164 if (d->m_widget->model()->positionTracking()->trackVisible()) {
165 d->m_currentLocationUi.showTrackCheckBox->setCheckState(Qt::Checked);
166 }
167 connect(d->m_currentLocationUi.saveTrackButton, SIGNAL(clicked(bool)), this, SLOT(saveTrack()));
168 connect(d->m_currentLocationUi.openTrackButton, SIGNAL(clicked(bool)), this, SLOT(openTrack()));
169 connect(d->m_currentLocationUi.clearTrackButton, SIGNAL(clicked(bool)), this, SLOT(clearTrack()));
170 connect(d->m_widget->model(), SIGNAL(trackedPlacemarkChanged(const GeoDataPlacemark *)), this, SLOT(trackPlacemark()));
171}
172
173void CurrentLocationWidgetPrivate::adjustPositionTrackingStatus(PositionProviderStatus status)
174{
175 if (status == PositionProviderStatusAvailable) {
176 return;
177 }
178
179 QString html = QStringLiteral("<html><body><p>");
180
181 switch (status) {
182 case PositionProviderStatusUnavailable:
183 html += QObject::tr("No position available.");
184 break;
185 case PositionProviderStatusAcquiring:
186 html += QObject::tr("Waiting for current location information...");
187 break;
188 case PositionProviderStatusAvailable:
189 Q_ASSERT(false);
190 break;
191 case PositionProviderStatusError:
192 html += QObject::tr("Error when determining current location: ");
193 html += m_widget->model()->positionTracking()->error();
194 break;
195 }
196
197 html += QLatin1StringView("</p></body></html>");
198 m_currentLocationUi.locationLabel->setEnabled(true);
199 m_currentLocationUi.locationLabel->setText(html);
200}
201
202void CurrentLocationWidgetPrivate::updateActivePositionProvider(PositionProviderPlugin *plugin)
203{
204 m_currentLocationUi.positionTrackingComboBox->blockSignals(true);
205 if (!plugin) {
206 m_currentLocationUi.positionTrackingComboBox->setCurrentIndex(0);
207 } else {
208 for (int i = 0; i < m_currentLocationUi.positionTrackingComboBox->count(); ++i) {
209 if (m_currentLocationUi.positionTrackingComboBox->itemText(i) == plugin->guiString()) {
210 m_currentLocationUi.positionTrackingComboBox->setCurrentIndex(i);
211 break;
212 }
213 }
214 }
215 m_currentLocationUi.positionTrackingComboBox->blockSignals(false);
216 m_currentLocationUi.recenterLabel->setEnabled(plugin);
217 m_currentLocationUi.recenterComboBox->setEnabled(plugin);
218 m_currentLocationUi.autoZoomCheckBox->setEnabled(plugin);
219}
220
221void CurrentLocationWidgetPrivate::updateGuidanceMode()
222{
223 const bool enabled = m_widget->model()->routingManager()->guidanceModeEnabled();
224
225 m_adjustNavigation->setAutoZoom(enabled);
226 m_adjustNavigation->setRecenter(enabled ? AutoNavigation::RecenterOnBorder : AutoNavigation::DontRecenter);
227}
228
229void CurrentLocationWidgetPrivate::receiveGpsCoordinates(const GeoDataCoordinates &position, qreal speed)
230{
231 m_currentPosition = position;
232 QString unitString;
233 QString altitudeUnitString;
234 QString distanceUnitString;
235 qreal unitSpeed = 0.0;
236 qreal altitude = 0.0;
237 qreal length = m_widget->model()->positionTracking()->length(m_widget->model()->planetRadius());
238
240 "<html><body>"
241 "<table cellspacing=\"2\" cellpadding=\"2\">"
242 "<tr><td>Longitude</td><td><a href=\"https://edu.kde.org/marble\">%1</a></td></tr>"
243 "<tr><td>Latitude</td><td><a href=\"https://edu.kde.org/marble\">%2</a></td></tr>"
244 "<tr><td>Altitude</td><td>%3</td></tr>"
245 "<tr><td>Speed</td><td>%4</td></tr>"
246 "<tr><td>Distance</td><td>%5</td></tr>"
247 "</table>"
248 "</body></html>");
249
250 switch (MarbleGlobal::getInstance()->locale()->measurementSystem()) {
251 case MarbleLocale::MetricSystem:
252 // kilometers per hour
253 unitString = QObject::tr("km/h");
254 unitSpeed = speed * HOUR2SEC * METER2KM;
255 altitudeUnitString = QObject::tr("m");
256 distanceUnitString = QObject::tr("m");
257 if (length > 1000.0) {
258 length /= 1000.0;
259 distanceUnitString = QObject::tr("km");
260 }
261 altitude = position.altitude();
262 break;
263 case MarbleLocale::ImperialSystem:
264 // miles per hour
265 unitString = QObject::tr("m/h");
266 unitSpeed = speed * HOUR2SEC * METER2KM * KM2MI;
267 altitudeUnitString = QObject::tr("ft");
268 distanceUnitString = QObject::tr("ft");
269 altitude = position.altitude() * M2FT;
270 length *= M2FT;
271 break;
272
273 case MarbleLocale::NauticalSystem:
274 // nautical miles
275 unitString = QObject::tr("kt");
276 unitSpeed = speed * HOUR2SEC * METER2KM * KM2NM;
277 altitudeUnitString = QObject::tr("m");
278 distanceUnitString = QObject::tr("nm");
279 altitude = position.altitude();
280 length *= METER2KM * KM2NM;
281 break;
282 }
283 // TODO read this value from the incoming signal
284 const QString speedString = QLocale::system().toString(unitSpeed, 'f', 1);
285 const QString altitudeString = QStringLiteral("%1 %2").arg(altitude, 0, 'f', 1, QLatin1Char(' ')).arg(altitudeUnitString);
286 const QString distanceString = QStringLiteral("%1 %2").arg(length, 0, 'f', 1, QLatin1Char(' ')).arg(distanceUnitString);
287
288 html = html.arg(position.lonToString(), position.latToString());
289 html = html.arg(altitudeString, speedString + QLatin1Char(' ') + unitString);
290 html = html.arg(distanceString);
291 m_currentLocationUi.locationLabel->setText(html);
292 m_currentLocationUi.showTrackCheckBox->setEnabled(true);
293 m_currentLocationUi.saveTrackButton->setEnabled(true);
294 m_currentLocationUi.clearTrackButton->setEnabled(true);
295}
296
297void CurrentLocationWidget::changePositionProvider(int index)
298{
299 auto const combo = dynamic_cast<QComboBox *>(sender());
300
301 if (!combo)
302 return;
303
304 QString provider = combo->itemText(index);
305
306 changePositionProvider(provider);
307}
308
309void CurrentLocationWidget::changePositionProvider(const QString &provider)
310{
311 for (const PositionProviderPlugin *plugin : std::as_const(d->m_positionProviderPlugins)) {
312 if (plugin->guiString() == provider) {
313 d->m_currentLocationUi.locationLabel->setEnabled(true);
314 PositionProviderPlugin *instance = plugin->newInstance();
315 PositionTracking *tracking = d->m_widget->model()->positionTracking();
316 tracking->setPositionProviderPlugin(instance);
317 d->m_widget->update();
318 return;
319 }
320 }
321
322 // requested provider not found -> disable position tracking
323 d->m_currentLocationUi.locationLabel->setEnabled(false);
324 d->m_widget->model()->positionTracking()->setPositionProviderPlugin(nullptr);
325 d->m_widget->update();
326}
327
328void CurrentLocationWidget::trackPlacemark()
329{
330 changePositionProvider(QObject::tr("Placemark"));
331 d->m_adjustNavigation->setRecenter(AutoNavigation::AlwaysRecenter);
332}
333
334void CurrentLocationWidget::setRecenterMode(int mode)
335{
336 if (mode >= 0 && mode <= AutoNavigation::RecenterOnBorder) {
337 auto centerMode = (AutoNavigation::CenterMode)mode;
338 d->m_adjustNavigation->setRecenter(centerMode);
339 }
340}
341
342void CurrentLocationWidget::setAutoZoom(bool autoZoom)
343{
344 d->m_adjustNavigation->setAutoZoom(autoZoom);
345}
346
347void CurrentLocationWidgetPrivate::updateAutoZoomCheckBox(bool autoZoom)
348{
349 m_currentLocationUi.autoZoomCheckBox->setChecked(autoZoom);
350}
351
352void CurrentLocationWidgetPrivate::updateRecenterComboBox(AutoNavigation::CenterMode centerMode)
353{
354 m_currentLocationUi.recenterComboBox->setCurrentIndex(centerMode);
355}
356
357void CurrentLocationWidgetPrivate::centerOnCurrentLocation()
358{
359 m_widget->centerOn(m_currentPosition, true);
360}
361
362void CurrentLocationWidgetPrivate::saveTrack()
363{
364 QString suggested = m_lastSavePath;
366 m_widget,
367 QObject::tr("Save Track"), // krazy:exclude=qclasses
368 suggested.append(QLatin1Char('/') + QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hhmmss")) + QLatin1StringView(".kml")),
369 QObject::tr("KML File (*.kml)"));
370 if (fileName.isEmpty()) {
371 return;
372 }
373 if (!fileName.endsWith(QLatin1StringView(".kml"), Qt::CaseInsensitive)) {
374 fileName += QLatin1StringView(".kml");
375 }
376 QFileInfo file(fileName);
377 m_lastSavePath = file.absolutePath();
378 m_widget->model()->positionTracking()->saveTrack(fileName);
379}
380
381void CurrentLocationWidgetPrivate::openTrack()
382{
383 QString suggested = m_lastOpenPath;
384 QString fileName = QFileDialog::getOpenFileName(m_widget,
385 QObject::tr("Open Track"), // krazy:exclude=qclasses
386 suggested,
387 QObject::tr("KML File (*.kml)"));
388 if (!fileName.isEmpty()) {
389 QFileInfo file(fileName);
390 m_lastOpenPath = file.absolutePath();
391 m_widget->model()->addGeoDataFile(fileName);
392 }
393}
394
395void CurrentLocationWidgetPrivate::clearTrack()
396{
397 const int result = QMessageBox::question(m_widget,
398 QObject::tr("Clear current track"),
399 QObject::tr("Are you sure you want to clear the current track?"),
402
403 if (result == QMessageBox::Yes) {
404 m_widget->model()->positionTracking()->clearTrack();
405 m_widget->update();
406 m_currentLocationUi.saveTrackButton->setEnabled(false);
407 m_currentLocationUi.clearTrackButton->setEnabled(false);
408 }
409}
410
411AutoNavigation::CenterMode CurrentLocationWidget::recenterMode() const
412{
413 return d->m_adjustNavigation->recenterMode();
414}
415
416bool CurrentLocationWidget::autoZoom() const
417{
418 return d->m_adjustNavigation->autoZoom();
419}
420
421bool CurrentLocationWidget::trackVisible() const
422{
423 return d->m_widget->model()->positionTracking()->trackVisible();
424}
425
426QString CurrentLocationWidget::lastOpenPath() const
427{
428 return d->m_lastOpenPath;
429}
430
431QString CurrentLocationWidget::lastSavePath() const
432{
433 return d->m_lastSavePath;
434}
435
436void CurrentLocationWidget::setTrackVisible(bool visible)
437{
438 d->m_currentLocationUi.showTrackCheckBox->setChecked(visible);
439 d->m_widget->model()->positionTracking()->setTrackVisible(visible);
440}
441
442void CurrentLocationWidget::setLastOpenPath(const QString &path)
443{
444 d->m_lastOpenPath = path;
445}
446
447void CurrentLocationWidget::setLastSavePath(const QString &path)
448{
449 d->m_lastSavePath = path;
450}
451
452}
453
454#include "moc_CurrentLocationWidget.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for MarbleWidget.
A 3d point representation.
static QString latToString(qreal lat, GeoDataCoordinates::Notation notation, GeoDataCoordinates::Unit unit=Radian, int precision=-1, char format='f')
qreal altitude() const
return the altitude of the Point in meters
static QString lonToString(qreal lon, GeoDataCoordinates::Notation notation, GeoDataCoordinates::Unit unit=Radian, int precision=-1, char format='f')
A class that defines a 3D bounding box for geographic data.
a class representing a point of interest on the map
void addGeoDataFile(const QString &filename)
Handle file loading into the treeModel.
A widget class that displays a view of the earth.
MarbleModel * model()
Return the model that this view shows.
void centerOn(const qreal lon, const qreal lat, bool animated=false)
Center the view on a geographical point.
The class that handles Marble's plugins.
QList< const PositionProviderPlugin * > positionProviderPlugins() const
Returns all available PositionProviderPlugins.
The abstract class that provides position information.
virtual QString guiString() const =0
Returns the string that should appear in the user interface.
virtual PositionProviderPlugin * newInstance() const =0
Create a new PositionProvider Plugin and return it.
Q_SCRIPTABLE CaptureState status()
char * toString(const EngineQuery &query)
QString path(const QString &relativePath)
QAction * zoomIn(const QObject *recvr, const char *slot, QObject *parent)
QAction * zoomOut(const QObject *recvr, const char *slot, QObject *parent)
Binds a QML item to a specific geodetic location in screen coordinates.
FlyToMode
Describes possible flight mode (interpolation between source and target camera positions)
void clicked(bool checked)
void currentIndexChanged(int index)
QDateTime currentDateTime()
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QLocale system()
QString toString(QDate date, FormatType format) const const
StandardButton question(QWidget *parent, const QString &title, const QString &text, StandardButtons buttons, StandardButton defaultButton)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
QObject * sender() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
QString & append(QChar ch)
QString arg(Args &&... args) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
CaseInsensitive
typedef WindowFlags
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setupUi(QWidget *widget)
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.