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"