Marble

Navigation.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org>
4//
5
6#include "Navigation.h"
7
8#include "AutoNavigation.h"
9#include "GeoDataAccuracy.h"
10#include "MarbleModel.h"
11#include "MarbleQuickItem.h"
12#include "Planet.h"
13#include "PositionTracking.h"
14#include "ViewportParams.h"
15#include "routing/Route.h"
16#include "routing/RoutingManager.h"
17#include "routing/RoutingModel.h"
18#include "routing/VoiceNavigationModel.h"
19
20namespace Marble
21{
22
23class NavigationPrivate
24{
25public:
26 NavigationPrivate();
27
28 MarbleQuickItem *m_marbleQuickItem;
29
30 bool m_muted;
31
32 RouteSegment m_currentSegment;
33
34 AutoNavigation *m_autoNavigation;
35
36 VoiceNavigationModel m_voiceNavigation;
37
38 qreal m_nextInstructionDistance;
39
40 qreal m_destinationDistance;
41
42 double m_screenAccuracy;
43 QPointF m_screenPosition;
44
45 RouteSegment nextRouteSegment() const;
46
47 void updateNextInstructionDistance(const Route &route);
48
49 MarbleModel *model() const;
50
51 QPointF positionOnRoute() const;
52 QPointF currentPosition() const;
53
54 RouteSegment m_secondLastSegment;
55 RouteSegment m_lastSegment;
56};
57
58NavigationPrivate::NavigationPrivate()
59 : m_marbleQuickItem(nullptr)
60 , m_muted(false)
61 , m_autoNavigation(nullptr)
62 , m_nextInstructionDistance(0.0)
63 , m_destinationDistance(0.0)
64 , m_screenAccuracy(0)
65{
66 // nothing to do
67}
68
69void NavigationPrivate::updateNextInstructionDistance(const Route &route)
70{
71 const GeoDataCoordinates position = route.position();
72 const GeoDataCoordinates interpolated = route.positionOnRoute();
73 const GeoDataCoordinates onRoute = route.currentWaypoint();
74
75 qreal planetRadius = 0;
76 if (model()) {
77 planetRadius = model()->planet()->radius();
78 }
79 qreal distance = planetRadius * (position.sphericalDistanceTo(interpolated) + interpolated.sphericalDistanceTo(onRoute));
80 qreal remaining = 0.0;
81 const RouteSegment &segment = route.currentSegment();
82 for (int i = 0; i < segment.path().size(); ++i) {
83 if (segment.path()[i] == onRoute) {
84 distance += segment.path().length(planetRadius, i);
85 break;
86 }
87 }
88
89 bool upcoming = false;
90 for (int i = 0; i < route.size(); ++i) {
91 const RouteSegment &segment = route.at(i);
92
93 if (upcoming) {
94 remaining += segment.path().length(planetRadius);
95 }
96
97 if (segment == route.currentSegment()) {
98 upcoming = true;
99 }
100 }
101
102 m_nextInstructionDistance = distance;
103 m_destinationDistance = distance + remaining;
104}
105
106MarbleModel *NavigationPrivate::model() const
107{
108 return m_marbleQuickItem ? m_marbleQuickItem->model() : nullptr;
109}
110
111RouteSegment NavigationPrivate::nextRouteSegment() const
112{
113 // Not using m_currentSegment on purpose
114 return m_marbleQuickItem ? model()->routingManager()->routingModel()->route().currentSegment().nextRouteSegment() : RouteSegment();
115}
116
117Navigation::Navigation(QObject *parent)
118 : QObject(parent)
119 , d(new NavigationPrivate)
120{
121 connect(&d->m_voiceNavigation, SIGNAL(instructionChanged()), this, SIGNAL(voiceNavigationAnnouncementChanged()));
122}
123
124Navigation::~Navigation()
125{
126 delete d;
127}
128
129bool Navigation::guidanceModeEnabled() const
130{
131 return d->m_marbleQuickItem ? d->model()->routingManager()->guidanceModeEnabled() : false;
132}
133
134void Navigation::setGuidanceModeEnabled(bool enabled)
135{
136 if (d->m_marbleQuickItem) {
137 d->model()->routingManager()->setGuidanceModeEnabled(enabled);
138 d->m_autoNavigation->setAutoZoom(enabled);
139 d->m_autoNavigation->setRecenter(enabled ? AutoNavigation::RecenterOnBorder : AutoNavigation::DontRecenter);
140
141 if (enabled && !d->m_muted) {
142 // d->m_audio.announceStart();
143 }
144 }
145}
146
147bool Navigation::muted() const
148{
149 return d->m_muted;
150}
151
152void Navigation::setMuted(bool enabled)
153{
154 d->m_muted = enabled;
155}
156
157QString Navigation::nextInstructionText() const
158{
159 return d->nextRouteSegment().maneuver().instructionText();
160}
161
162QString Navigation::nextRoad() const
163{
164 return d->nextRouteSegment().maneuver().roadName();
165}
166
167QString Navigation::nextInstructionImage() const
168{
169 switch (d->nextRouteSegment().maneuver().direction()) {
170 case Maneuver::Continue:
171 return QStringLiteral("qrc:/marble/turn-continue.svg");
172 case Maneuver::Merge:
173 return QStringLiteral("qrc:/marble/turn-merge.svg");
174 case Maneuver::Straight:
175 return QStringLiteral("qrc:/marble/turn-continue.svg");
176 case Maneuver::SlightRight:
177 return QStringLiteral("qrc:/marble/turn-slight-right.svg");
178 case Maneuver::Right:
179 return QStringLiteral("qrc:/marble/turn-right.svg");
180 case Maneuver::SharpRight:
181 return QStringLiteral("qrc:/marble/turn-sharp-right.svg");
182 case Maneuver::TurnAround:
183 return QStringLiteral("qrc:/marble/turn-around.svg");
184 case Maneuver::SharpLeft:
185 return QStringLiteral("qrc:/marble/turn-sharp-left.svg");
186 case Maneuver::Left:
187 return QStringLiteral("qrc:/marble/turn-left.svg");
188 case Maneuver::SlightLeft:
189 return QStringLiteral("qrc:/marble/turn-slight-left.svg");
190 case Maneuver::RoundaboutFirstExit:
191 return QStringLiteral("qrc:/marble/turn-roundabout-first.svg");
192 case Maneuver::RoundaboutSecondExit:
193 return QStringLiteral("qrc:/marble/turn-roundabout-second.svg");
194 case Maneuver::RoundaboutThirdExit:
195 return QStringLiteral("qrc:/marble/turn-roundabout-third.svg");
196 case Maneuver::RoundaboutExit:
197 return QStringLiteral("qrc:/marble/turn-roundabout-far.svg");
198 case Maneuver::ExitLeft:
199 return QStringLiteral("qrc:/marble/turn-exit-left.svg");
200 case Maneuver::ExitRight:
201 return QStringLiteral("qrc:/marble/turn-exit-right.svg");
202 case Maneuver::Unknown:
203 default:
204 return {};
205 }
206}
207
208qreal Navigation::nextInstructionDistance() const
209{
210 return d->m_nextInstructionDistance;
211}
212
213qreal Navigation::destinationDistance() const
214{
215 return d->m_destinationDistance;
216}
217
218QString Navigation::voiceNavigationAnnouncement() const
219{
220 return d->m_voiceNavigation.instruction();
221}
222
223QString Navigation::speaker() const
224{
225 return d->m_voiceNavigation.speaker();
226}
227
228void Navigation::setSpeaker(const QString &speaker)
229{
230 d->m_voiceNavigation.setSpeaker(speaker);
231}
232
233bool Navigation::deviated() const
234{
235 if (d->m_marbleQuickItem) {
236 RoutingModel const *routingModel = d->model()->routingManager()->routingModel();
237 return routingModel->deviatedFromRoute();
238 }
239
240 return true;
241}
242
243MarbleQuickItem *Navigation::marbleQuickItem() const
244{
245 return d->m_marbleQuickItem;
246}
247
248QPointF NavigationPrivate::positionOnRoute() const
249{
250 RoutingModel const *routingModel = model()->routingManager()->routingModel();
251 GeoDataCoordinates coordinates = routingModel->route().positionOnRoute();
252 qreal x = 0;
253 qreal y = 0;
254 if (coordinates.isValid()) {
255 m_marbleQuickItem->map()->viewport()->screenCoordinates(coordinates, x, y);
256 }
257 return {x, y};
258}
259
260QPointF NavigationPrivate::currentPosition() const
261{
262 GeoDataCoordinates coordinates = model()->positionTracking()->currentLocation();
263 qreal x = 0;
264 qreal y = 0;
265 m_marbleQuickItem->map()->viewport()->screenCoordinates(coordinates, x, y);
266 return {x, y};
267}
268
269QPointF Navigation::screenPosition() const
270{
271 return d->m_screenPosition;
272}
273
274double Navigation::screenAccuracy() const
275{
276 return d->m_screenAccuracy;
277}
278
279void Navigation::setMarbleQuickItem(MarbleQuickItem *marbleQuickItem)
280{
281 if (d->m_marbleQuickItem == marbleQuickItem) {
282 return;
283 }
284
285 if (d->m_marbleQuickItem) {
286 disconnect(d->model()->routingManager()->routingModel(), SIGNAL(positionChanged()), this, SLOT(update()));
287 disconnect(d->m_autoNavigation, SIGNAL(zoomIn(FlyToMode)), d->m_marbleQuickItem, SLOT(zoomIn()));
288 disconnect(d->m_autoNavigation, SIGNAL(zoomOut(FlyToMode)), d->m_marbleQuickItem, SLOT(zoomOut()));
289 disconnect(d->m_autoNavigation, SIGNAL(centerOn(GeoDataCoordinates, bool)), d->m_marbleQuickItem, SLOT(centerOn(GeoDataCoordinates)));
290
291 disconnect(d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), d->m_autoNavigation, SLOT(inhibitAutoAdjustments()));
292 }
293
294 d->m_marbleQuickItem = marbleQuickItem;
295 if (d->m_marbleQuickItem) {
296 d->model()->routingManager()->setShowGuidanceModeStartupWarning(false);
297 connect(d->model()->routingManager()->routingModel(), SIGNAL(positionChanged()), this, SLOT(update()));
298 connect(d->model()->routingManager()->routingModel(), SIGNAL(deviatedFromRoute(bool)), this, SIGNAL(deviationChanged()));
299
300 delete d->m_autoNavigation;
301 d->m_autoNavigation = new AutoNavigation(d->model(), d->m_marbleQuickItem->map()->viewport(), this);
302 connect(d->m_autoNavigation, SIGNAL(zoomIn(FlyToMode)), d->m_marbleQuickItem, SLOT(zoomIn()));
303 connect(d->m_autoNavigation, SIGNAL(zoomOut(FlyToMode)), d->m_marbleQuickItem, SLOT(zoomOut()));
304 connect(d->m_autoNavigation, SIGNAL(centerOn(GeoDataCoordinates, bool)), d->m_marbleQuickItem, SLOT(centerOn(GeoDataCoordinates)));
305
306 connect(d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), d->m_autoNavigation, SLOT(inhibitAutoAdjustments()));
307
308 connect(d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), this, SLOT(updateScreenPosition()));
309 connect(d->model()->positionTracking(), SIGNAL(gpsLocation(GeoDataCoordinates, qreal)), this, SLOT(updateScreenPosition()));
310 connect(d->model()->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)), this, SLOT(updateScreenPosition()));
311 }
312 Q_EMIT marbleQuickItemChanged(marbleQuickItem);
313}
314
315void Navigation::update()
316{
317 if (!d->model()) {
318 return;
319 }
320
321 RoutingModel const *routingModel = d->model()->routingManager()->routingModel();
322 d->updateNextInstructionDistance(routingModel->route());
323 Q_EMIT nextInstructionDistanceChanged();
324 Q_EMIT destinationDistanceChanged();
325 RouteSegment segment = routingModel->route().currentSegment();
326
327 if (!d->m_muted) {
328 d->m_voiceNavigation.update(routingModel->route(), d->m_nextInstructionDistance, d->m_destinationDistance, routingModel->deviatedFromRoute());
329 }
330 if (segment != d->m_currentSegment) {
331 d->m_currentSegment = segment;
332 Q_EMIT nextInstructionTextChanged();
333 Q_EMIT nextInstructionImageChanged();
334 Q_EMIT nextRoadChanged();
335 }
336
337 updateScreenPosition();
338}
339
340void Navigation::updateScreenPosition()
341{
342 if (d->m_marbleQuickItem) {
343 double distanceMeter = d->model()->positionTracking()->accuracy().horizontal;
344 d->m_screenAccuracy = distanceMeter * d->m_marbleQuickItem->map()->radius() / d->model()->planetRadius();
345 Q_EMIT screenAccuracyChanged();
346
347 d->m_screenPosition = deviated() ? d->currentPosition() : d->positionOnRoute();
348 Q_EMIT screenPositionChanged();
349 }
350}
351
352}
353
354#include "moc_Navigation.cpp"
This file contains the headers for MarbleModel.
This file contains the headers for ViewportParams.
void update(Part *part, const QByteArray &data, qint64 dataSize)
const QList< QKeySequence > & zoomIn()
const QList< QKeySequence > & zoomOut()
Binds a QML item to a specific geodetic location in screen coordinates.
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:15:45 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.