File indexing completed on 2024-05-05 03:49:18
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org> 0004 // 0005 0006 #include "Navigation.h" 0007 0008 #include "Planet.h" 0009 #include "MarbleModel.h" 0010 #include "MarbleQuickItem.h" 0011 #include "routing/Route.h" 0012 #include "routing/RoutingManager.h" 0013 #include "routing/RoutingModel.h" 0014 #include "PositionTracking.h" 0015 #include "AutoNavigation.h" 0016 #include "routing/VoiceNavigationModel.h" 0017 #include "ViewportParams.h" 0018 #include "GeoDataAccuracy.h" 0019 0020 namespace Marble { 0021 0022 class NavigationPrivate 0023 { 0024 public: 0025 NavigationPrivate(); 0026 0027 MarbleQuickItem * m_marbleQuickItem; 0028 0029 bool m_muted; 0030 0031 RouteSegment m_currentSegment; 0032 0033 AutoNavigation* m_autoNavigation; 0034 0035 VoiceNavigationModel m_voiceNavigation; 0036 0037 qreal m_nextInstructionDistance; 0038 0039 qreal m_destinationDistance; 0040 0041 double m_screenAccuracy; 0042 QPointF m_screenPosition; 0043 0044 RouteSegment nextRouteSegment() const; 0045 0046 void updateNextInstructionDistance( const Route &route ); 0047 0048 MarbleModel * model() const; 0049 0050 QPointF positionOnRoute() const; 0051 QPointF currentPosition() const; 0052 0053 RouteSegment m_secondLastSegment; 0054 RouteSegment m_lastSegment; 0055 0056 }; 0057 0058 NavigationPrivate::NavigationPrivate() : 0059 m_marbleQuickItem( nullptr ), m_muted( false ), m_autoNavigation( nullptr ), m_nextInstructionDistance( 0.0 ), 0060 m_destinationDistance( 0.0 ), m_screenAccuracy(0) 0061 { 0062 // nothing to do 0063 } 0064 0065 void NavigationPrivate::updateNextInstructionDistance( const Route &route ) 0066 { 0067 const GeoDataCoordinates position = route.position(); 0068 const GeoDataCoordinates interpolated = route.positionOnRoute(); 0069 const GeoDataCoordinates onRoute = route.currentWaypoint(); 0070 0071 qreal planetRadius = 0; 0072 if (model()){ 0073 planetRadius = model()->planet()->radius(); 0074 } 0075 qreal distance = planetRadius * (position.sphericalDistanceTo(interpolated) + interpolated.sphericalDistanceTo(onRoute)); 0076 qreal remaining = 0.0; 0077 const RouteSegment &segment = route.currentSegment(); 0078 for ( int i=0; i<segment.path().size(); ++i ) { 0079 if ( segment.path()[i] == onRoute ) { 0080 distance += segment.path().length( planetRadius, i ); 0081 break; 0082 } 0083 } 0084 0085 bool upcoming = false; 0086 for ( int i=0; i<route.size(); ++i ) { 0087 const RouteSegment &segment = route.at( i ); 0088 0089 if ( upcoming ) { 0090 remaining += segment.path().length( planetRadius ); 0091 } 0092 0093 if ( segment == route.currentSegment() ) { 0094 upcoming = true; 0095 } 0096 } 0097 0098 m_nextInstructionDistance = distance; 0099 m_destinationDistance = distance + remaining; 0100 } 0101 0102 MarbleModel * NavigationPrivate::model() const 0103 { 0104 return m_marbleQuickItem ? m_marbleQuickItem->model() : nullptr; 0105 } 0106 0107 RouteSegment NavigationPrivate::nextRouteSegment() const 0108 { 0109 // Not using m_currentSegment on purpose 0110 return m_marbleQuickItem ? model()->routingManager()->routingModel()->route().currentSegment().nextRouteSegment() : RouteSegment(); 0111 } 0112 0113 Navigation::Navigation( QObject* parent) : 0114 QObject( parent ), d( new NavigationPrivate ) 0115 { 0116 connect( &d->m_voiceNavigation, SIGNAL(instructionChanged()), this, SIGNAL(voiceNavigationAnnouncementChanged()) ); 0117 } 0118 0119 Navigation::~Navigation() 0120 { 0121 delete d; 0122 } 0123 0124 0125 bool Navigation::guidanceModeEnabled() const 0126 { 0127 return d->m_marbleQuickItem ? d->model()->routingManager()->guidanceModeEnabled() : false; 0128 } 0129 0130 void Navigation::setGuidanceModeEnabled( bool enabled ) 0131 { 0132 if ( d->m_marbleQuickItem ) { 0133 d->model()->routingManager()->setGuidanceModeEnabled( enabled ); 0134 d->m_autoNavigation->setAutoZoom( enabled ); 0135 d->m_autoNavigation->setRecenter( enabled ? AutoNavigation::RecenterOnBorder : AutoNavigation::DontRecenter ); 0136 0137 if ( enabled && !d->m_muted ) { 0138 //d->m_audio.announceStart(); 0139 } 0140 } 0141 } 0142 0143 bool Navigation::muted() const 0144 { 0145 return d->m_muted; 0146 } 0147 0148 void Navigation::setMuted(bool enabled) 0149 { 0150 d->m_muted = enabled; 0151 } 0152 0153 QString Navigation::nextInstructionText() const 0154 { 0155 return d->nextRouteSegment().maneuver().instructionText(); 0156 } 0157 0158 QString Navigation::nextRoad() const 0159 { 0160 return d->nextRouteSegment().maneuver().roadName(); 0161 } 0162 0163 QString Navigation::nextInstructionImage() const 0164 { 0165 switch ( d->nextRouteSegment().maneuver().direction() ) { 0166 case Maneuver::Continue: return QStringLiteral("qrc:/marble/turn-continue.svg"); 0167 case Maneuver::Merge: return QStringLiteral("qrc:/marble/turn-merge.svg"); 0168 case Maneuver::Straight: return QStringLiteral("qrc:/marble/turn-continue.svg"); 0169 case Maneuver::SlightRight: return QStringLiteral("qrc:/marble/turn-slight-right.svg"); 0170 case Maneuver::Right: return QStringLiteral("qrc:/marble/turn-right.svg"); 0171 case Maneuver::SharpRight: return QStringLiteral("qrc:/marble/turn-sharp-right.svg"); 0172 case Maneuver::TurnAround: return QStringLiteral("qrc:/marble/turn-around.svg"); 0173 case Maneuver::SharpLeft: return QStringLiteral("qrc:/marble/turn-sharp-left.svg"); 0174 case Maneuver::Left: return QStringLiteral("qrc:/marble/turn-left.svg"); 0175 case Maneuver::SlightLeft: return QStringLiteral("qrc:/marble/turn-slight-left.svg"); 0176 case Maneuver::RoundaboutFirstExit: return QStringLiteral("qrc:/marble/turn-roundabout-first.svg"); 0177 case Maneuver::RoundaboutSecondExit: return QStringLiteral("qrc:/marble/turn-roundabout-second.svg"); 0178 case Maneuver::RoundaboutThirdExit: return QStringLiteral("qrc:/marble/turn-roundabout-third.svg"); 0179 case Maneuver::RoundaboutExit: return QStringLiteral("qrc:/marble/turn-roundabout-far.svg"); 0180 case Maneuver::ExitLeft: return QStringLiteral("qrc:/marble/turn-exit-left.svg"); 0181 case Maneuver::ExitRight: return QStringLiteral("qrc:/marble/turn-exit-right.svg"); 0182 case Maneuver::Unknown: 0183 default: 0184 return QString(); 0185 } 0186 } 0187 0188 qreal Navigation::nextInstructionDistance() const 0189 { 0190 return d->m_nextInstructionDistance; 0191 } 0192 0193 qreal Navigation::destinationDistance() const 0194 { 0195 return d->m_destinationDistance; 0196 } 0197 0198 QString Navigation::voiceNavigationAnnouncement() const 0199 { 0200 return d->m_voiceNavigation.instruction(); 0201 } 0202 0203 QString Navigation::speaker() const 0204 { 0205 return d->m_voiceNavigation.speaker(); 0206 } 0207 0208 void Navigation::setSpeaker( const QString &speaker ) 0209 { 0210 d->m_voiceNavigation.setSpeaker( speaker ); 0211 } 0212 0213 bool Navigation::deviated() const 0214 { 0215 if ( d->m_marbleQuickItem ) { 0216 RoutingModel const * routingModel = d->model()->routingManager()->routingModel(); 0217 return routingModel->deviatedFromRoute(); 0218 } 0219 0220 return true; 0221 } 0222 0223 MarbleQuickItem *Navigation::marbleQuickItem() const 0224 { 0225 return d->m_marbleQuickItem; 0226 } 0227 0228 QPointF NavigationPrivate::positionOnRoute() const 0229 { 0230 RoutingModel const * routingModel = model()->routingManager()->routingModel(); 0231 GeoDataCoordinates coordinates = routingModel->route().positionOnRoute(); 0232 qreal x = 0; 0233 qreal y = 0; 0234 if (coordinates.isValid()) { 0235 m_marbleQuickItem->map()->viewport()->screenCoordinates(coordinates, x, y); 0236 } 0237 return QPointF(x,y); 0238 } 0239 0240 QPointF NavigationPrivate::currentPosition() const 0241 { 0242 GeoDataCoordinates coordinates = model()->positionTracking()->currentLocation(); 0243 qreal x = 0; 0244 qreal y = 0; 0245 m_marbleQuickItem->map()->viewport()->screenCoordinates(coordinates, x, y); 0246 return QPointF(x,y); 0247 } 0248 0249 QPointF Navigation::screenPosition() const 0250 { 0251 return d->m_screenPosition; 0252 } 0253 0254 double Navigation::screenAccuracy() const 0255 { 0256 return d->m_screenAccuracy; 0257 } 0258 0259 void Navigation::setMarbleQuickItem(MarbleQuickItem *marbleQuickItem) 0260 { 0261 if ( d->m_marbleQuickItem == marbleQuickItem) { 0262 return; 0263 } 0264 0265 if (d->m_marbleQuickItem) { 0266 disconnect( d->model()->routingManager()->routingModel(), 0267 SIGNAL(positionChanged()), this, SLOT(update()) ); 0268 disconnect( d->m_autoNavigation, SIGNAL(zoomIn(FlyToMode)), 0269 d->m_marbleQuickItem, SLOT(zoomIn()) ); 0270 disconnect( d->m_autoNavigation, SIGNAL(zoomOut(FlyToMode)), 0271 d->m_marbleQuickItem, SLOT(zoomOut()) ); 0272 disconnect( d->m_autoNavigation, SIGNAL(centerOn(GeoDataCoordinates,bool)), 0273 d->m_marbleQuickItem, SLOT(centerOn(GeoDataCoordinates)) ); 0274 0275 disconnect( d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), 0276 d->m_autoNavigation, SLOT(inhibitAutoAdjustments()) ); 0277 } 0278 0279 d->m_marbleQuickItem = marbleQuickItem; 0280 if ( d->m_marbleQuickItem ) { 0281 d->model()->routingManager()->setShowGuidanceModeStartupWarning( false ); 0282 connect( d->model()->routingManager()->routingModel(), 0283 SIGNAL(positionChanged()), this, SLOT(update()) ); 0284 connect( d->model()->routingManager()->routingModel(), 0285 SIGNAL(deviatedFromRoute(bool)), this, SIGNAL(deviationChanged()) ); 0286 0287 delete d->m_autoNavigation; 0288 d->m_autoNavigation = new AutoNavigation( d->model(), d->m_marbleQuickItem->map()->viewport(), this ); 0289 connect( d->m_autoNavigation, SIGNAL(zoomIn(FlyToMode)), 0290 d->m_marbleQuickItem, SLOT(zoomIn()) ); 0291 connect( d->m_autoNavigation, SIGNAL(zoomOut(FlyToMode)), 0292 d->m_marbleQuickItem, SLOT(zoomOut()) ); 0293 connect( d->m_autoNavigation, SIGNAL(centerOn(GeoDataCoordinates,bool)), 0294 d->m_marbleQuickItem, SLOT(centerOn(GeoDataCoordinates)) ); 0295 0296 connect( d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), 0297 d->m_autoNavigation, SLOT(inhibitAutoAdjustments()) ); 0298 0299 connect( d->m_marbleQuickItem, SIGNAL(visibleLatLonAltBoxChanged()), 0300 this, SLOT(updateScreenPosition()) ); 0301 connect( d->model()->positionTracking(), SIGNAL(gpsLocation(GeoDataCoordinates,qreal)), 0302 this, SLOT(updateScreenPosition()) ); 0303 connect( d->model()->positionTracking(), SIGNAL(statusChanged(PositionProviderStatus)), 0304 this, SLOT(updateScreenPosition()) ); 0305 0306 } 0307 emit marbleQuickItemChanged(marbleQuickItem); 0308 } 0309 0310 void Navigation::update() 0311 { 0312 if (!d->model()) { 0313 return; 0314 } 0315 0316 RoutingModel const * routingModel = d->model()->routingManager()->routingModel(); 0317 d->updateNextInstructionDistance( routingModel->route() ); 0318 emit nextInstructionDistanceChanged(); 0319 emit destinationDistanceChanged(); 0320 RouteSegment segment = routingModel->route().currentSegment(); 0321 0322 if ( !d->m_muted ) { 0323 d->m_voiceNavigation.update( routingModel->route(), d->m_nextInstructionDistance, d->m_destinationDistance, routingModel->deviatedFromRoute() ); 0324 } 0325 if ( segment != d->m_currentSegment ) { 0326 d->m_currentSegment = segment; 0327 emit nextInstructionTextChanged(); 0328 emit nextInstructionImageChanged(); 0329 emit nextRoadChanged(); 0330 } 0331 0332 updateScreenPosition(); 0333 } 0334 0335 void Navigation::updateScreenPosition() 0336 { 0337 if(d->m_marbleQuickItem) { 0338 double distanceMeter = d->model()->positionTracking()->accuracy().horizontal; 0339 d->m_screenAccuracy = distanceMeter * d->m_marbleQuickItem->map()->radius() / d->model()->planetRadius(); 0340 emit screenAccuracyChanged(); 0341 0342 d->m_screenPosition = deviated() ? d->currentPosition() : d->positionOnRoute(); 0343 emit screenPositionChanged(); 0344 } 0345 } 0346 0347 } 0348 0349 #include "moc_Navigation.cpp"