diff --git a/src/lib/marble/routing/RoutingManager.cpp b/src/lib/marble/routing/RoutingManager.cpp index a67ac0266..5bca6ace7 100644 --- a/src/lib/marble/routing/RoutingManager.cpp +++ b/src/lib/marble/routing/RoutingManager.cpp @@ -1,646 +1,646 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Dennis Nienhüser // #include "RoutingManager.h" #include "AlternativeRoutesModel.h" #include "MarbleModel.h" #include "RouteRequest.h" #include "RoutingModel.h" #include "RoutingProfilesModel.h" #include "RoutingRunnerPlugin.h" #include "GeoWriter.h" #include "GeoDataDocument.h" #include "GeoDataExtendedData.h" #include "GeoDataData.h" #include "GeoDataFolder.h" #include "GeoDataParser.h" #include "GeoDataPlacemark.h" #include "GeoDataTreeModel.h" #include "MarbleDirs.h" #include "MarbleDebug.h" #include "PositionTracking.h" #include "PluginManager.h" #include "PositionProviderPlugin.h" #include "Route.h" #include "RoutingRunnerManager.h" #include #include #include #include #include namespace Marble { class RoutingManagerPrivate { public: RoutingManager* q; RouteRequest m_routeRequest; RoutingModel m_routingModel; RoutingProfilesModel m_profilesModel; RoutingManager::State m_state; const PluginManager *const m_pluginManager; GeoDataTreeModel *const m_treeModel; PositionTracking *const m_positionTracking; AlternativeRoutesModel m_alternativeRoutesModel; RoutingRunnerManager m_runnerManager; bool m_haveRoute; bool m_guidanceModeEnabled; QMutex m_fileMutex; bool m_shutdownPositionTracking; bool m_guidanceModeWarning; QString m_lastOpenPath; QString m_lastSavePath; QColor m_routeColorStandard; QColor m_routeColorHighlighted; QColor m_routeColorAlternative; RoutingManagerPrivate( MarbleModel *marbleModel, RoutingManager* manager, QObject *parent ); GeoDataFolder* routeRequest() const; static QString stateFile( const QString &name = QString( "route.kml" ) ); void saveRoute( const QString &filename ); void loadRoute( const QString &filename ); void addRoute( GeoDataDocument* route ); void routingFinished(); void setCurrentRoute( GeoDataDocument *route ); void recalculateRoute( bool deviated ); static void importPlacemark( RouteSegment &outline, QVector &segments, const GeoDataPlacemark *placemark ); }; RoutingManagerPrivate::RoutingManagerPrivate( MarbleModel *model, RoutingManager* manager, QObject *parent ) : q( manager ), m_routeRequest( manager ), - m_routingModel( &m_routeRequest, model, manager ), + m_routingModel(&m_routeRequest, model->positionTracking(), manager), m_profilesModel( model->pluginManager() ), m_state( RoutingManager::Retrieved ), m_pluginManager( model->pluginManager() ), m_treeModel( model->treeModel() ), m_positionTracking( model->positionTracking() ), m_alternativeRoutesModel( parent ), m_runnerManager( model, q ), m_haveRoute( false ), m_guidanceModeEnabled( false ), m_shutdownPositionTracking( false ), m_guidanceModeWarning( true ), m_routeColorStandard( Oxygen::skyBlue4 ), m_routeColorHighlighted( Oxygen::skyBlue1 ), m_routeColorAlternative( Oxygen::aluminumGray4 ) { m_routeColorStandard.setAlpha( 200 ); m_routeColorHighlighted.setAlpha( 200 ); m_routeColorAlternative.setAlpha( 200 ); } GeoDataFolder* RoutingManagerPrivate::routeRequest() const { GeoDataFolder* result = new GeoDataFolder; result->setName(QStringLiteral("Route Request")); for ( int i=0; iappend( placemark ); } return result; } QString RoutingManagerPrivate::stateFile( const QString &name) { QString const subdir = "routing"; QDir dir( MarbleDirs::localPath() ); if ( !dir.exists( subdir ) ) { if ( !dir.mkdir( subdir ) ) { mDebug() << "Unable to create dir " << dir.absoluteFilePath( subdir ); return dir.absolutePath(); } } if ( !dir.cd( subdir ) ) { mDebug() << "Cannot change into " << dir.absoluteFilePath( subdir ); } return dir.absoluteFilePath( name ); } void RoutingManagerPrivate::saveRoute(const QString &filename) { GeoWriter writer; writer.setDocumentType( kml::kmlTag_nameSpaceOgc22 ); QMutexLocker locker( &m_fileMutex ); QFile file( filename ); if ( !file.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) { mDebug() << "Cannot write to " << file.fileName(); return; } GeoDataDocument container; container.setName(QStringLiteral("Route")); GeoDataFolder* request = routeRequest(); if ( request ) { container.append( request ); } GeoDataDocument *route = m_alternativeRoutesModel.currentRoute(); if ( route ) { container.append( new GeoDataDocument( *route ) ); } if ( !writer.write( &file, &container ) ) { mDebug() << "Can not write route state to " << file.fileName(); } file.close(); } void RoutingManagerPrivate::loadRoute(const QString &filename) { QFile file( filename ); if ( !file.open( QIODevice::ReadOnly ) ) { mDebug() << "Can not read route from " << file.fileName(); return; } GeoDataParser parser( GeoData_KML ); if ( !parser.read( &file ) ) { mDebug() << "Could not parse file: " << parser.errorString(); return; } GeoDocument *doc = parser.releaseDocument(); file.close(); bool loaded = false; GeoDataDocument* container = dynamic_cast( doc ); if (container && !container->isEmpty()) { GeoDataFolder* viaPoints = dynamic_cast( &container->first() ); if ( viaPoints ) { loaded = true; QVector placemarks = viaPoints->placemarkList(); for( int i=0; i viaPoints_needed; --i ) { m_routeRequest.remove( viaPoints_needed ); } } else { mDebug() << "Expected a GeoDataDocument with at least one child, didn't get one though"; } } if ( container && container->size() == 2 ) { GeoDataDocument* route = dynamic_cast(&container->last()); if ( route ) { loaded = true; m_alternativeRoutesModel.clear(); m_alternativeRoutesModel.addRoute( new GeoDataDocument(*route), AlternativeRoutesModel::Instant ); m_alternativeRoutesModel.setCurrentRoute( 0 ); m_state = RoutingManager::Retrieved; emit q->stateChanged( m_state ); emit q->routeRetrieved( route ); } else { mDebug() << "Expected a GeoDataDocument child, didn't get one though"; } } if (loaded) { delete doc; // == container } else { mDebug() << "File " << filename << " is not a valid Marble route .kml file"; if ( container ) { m_treeModel->addDocument( container ); } } } RoutingManager::RoutingManager( MarbleModel *marbleModel, QObject *parent ) : QObject( parent ), d( new RoutingManagerPrivate( marbleModel, this, this ) ) { connect( &d->m_runnerManager, SIGNAL(routeRetrieved(GeoDataDocument*)), this, SLOT(addRoute(GeoDataDocument*)) ); connect( &d->m_runnerManager, SIGNAL(routingFinished()), this, SLOT(routingFinished()) ); connect( &d->m_alternativeRoutesModel, SIGNAL(currentRouteChanged(GeoDataDocument*)), this, SLOT(setCurrentRoute(GeoDataDocument*)) ); connect( &d->m_routingModel, SIGNAL(deviatedFromRoute(bool)), this, SLOT(recalculateRoute(bool)) ); } RoutingManager::~RoutingManager() { delete d; } RoutingProfilesModel *RoutingManager::profilesModel() { return &d->m_profilesModel; } RoutingModel *RoutingManager::routingModel() { return &d->m_routingModel; } const RoutingModel *RoutingManager::routingModel() const { return &d->m_routingModel; } RouteRequest* RoutingManager::routeRequest() { return &d->m_routeRequest; } RoutingManager::State RoutingManager::state() const { return d->m_state; } void RoutingManager::retrieveRoute() { d->m_haveRoute = false; int realSize = 0; for ( int i = 0; i < d->m_routeRequest.size(); ++i ) { // Sort out dummy targets if ( d->m_routeRequest.at( i ).isValid() ) { ++realSize; } } d->m_alternativeRoutesModel.newRequest( &d->m_routeRequest ); if ( realSize > 1 ) { d->m_state = RoutingManager::Downloading; d->m_runnerManager.retrieveRoute( &d->m_routeRequest ); } else { d->m_routingModel.clear(); d->m_state = RoutingManager::Retrieved; } emit stateChanged( d->m_state ); } void RoutingManagerPrivate::addRoute( GeoDataDocument* route ) { if ( route ) { m_alternativeRoutesModel.addRoute( route ); } if ( !m_haveRoute ) { m_haveRoute = route != 0; } emit q->routeRetrieved( route ); } void RoutingManagerPrivate::routingFinished() { m_state = RoutingManager::Retrieved; emit q->stateChanged( m_state ); } void RoutingManagerPrivate::setCurrentRoute( GeoDataDocument *document ) { Route route; QVector segments; RouteSegment outline; QVector folders = document->folderList(); for( const GeoDataFolder *folder: folders ) { for( const GeoDataPlacemark *placemark: folder->placemarkList() ) { importPlacemark( outline, segments, placemark ); } } for( const GeoDataPlacemark *placemark: document->placemarkList() ) { importPlacemark( outline, segments, placemark ); } if ( segments.isEmpty() ) { segments << outline; } // Map via points onto segments if ( m_routeRequest.size() > 1 && segments.size() > 1 ) { int index = 0; for ( int j = 0; j < m_routeRequest.size(); ++j ) { QPair minimum( -1, -1.0 ); int viaIndex = -1; for ( int i = index; i < segments.size(); ++i ) { const RouteSegment &segment = segments[i]; GeoDataCoordinates closest; const qreal distance = segment.distanceTo( m_routeRequest.at( j ), closest, closest ); if ( minimum.first < 0 || distance < minimum.second ) { minimum.first = i; minimum.second = distance; viaIndex = j; } } if ( minimum.first >= 0 ) { index = minimum.first; Maneuver viaPoint = segments[ minimum.first ].maneuver(); viaPoint.setWaypoint( m_routeRequest.at( viaIndex ), viaIndex ); segments[ minimum.first ].setManeuver( viaPoint ); } } } if ( segments.size() > 0 ) { for( const RouteSegment &segment: segments ) { route.addRouteSegment( segment ); } } m_routingModel.setRoute( route ); } void RoutingManagerPrivate::importPlacemark( RouteSegment &outline, QVector &segments, const GeoDataPlacemark *placemark ) { const GeoDataGeometry* geometry = placemark->geometry(); const GeoDataLineString* lineString = dynamic_cast( geometry ); QStringList blacklist = QStringList() << "" << "Route" << "Tessellated"; RouteSegment segment; bool isOutline = true; if ( !blacklist.contains( placemark->name() ) ) { if( lineString ) { Maneuver maneuver; maneuver.setInstructionText( placemark->name() ); maneuver.setPosition( lineString->at( 0 ) ); if (placemark->extendedData().contains(QStringLiteral("turnType"))) { QVariant turnType = placemark->extendedData().value(QStringLiteral("turnType")).value(); // The enum value is converted to/from an int in the QVariant // because only a limited set of data types can be serialized with QVariant's // toString() method (which is used to serialize / values) maneuver.setDirection( Maneuver::Direction( turnType.toInt() ) ); } if (placemark->extendedData().contains(QStringLiteral("roadName"))) { QVariant roadName = placemark->extendedData().value(QStringLiteral("roadName")).value(); maneuver.setRoadName( roadName.toString() ); } segment.setManeuver( maneuver ); isOutline = false; } } if ( lineString ) { segment.setPath( *lineString ); if ( isOutline ) { outline = segment; } else { segments.push_back( segment ); } } } AlternativeRoutesModel* RoutingManager::alternativeRoutesModel() { return &d->m_alternativeRoutesModel; } void RoutingManager::writeSettings() const { d->saveRoute( d->stateFile() ); } void RoutingManager::saveRoute( const QString &filename ) const { d->saveRoute( filename ); } void RoutingManager::loadRoute( const QString &filename ) { d->loadRoute( filename ); } RoutingProfile RoutingManager::defaultProfile( RoutingProfile::TransportType transportType ) const { RoutingProfile profile; RoutingProfilesModel::ProfileTemplate tpl = RoutingProfilesModel::CarFastestTemplate; switch ( transportType ) { case RoutingProfile::Motorcar: tpl = RoutingProfilesModel::CarFastestTemplate; profile.setName(QStringLiteral("Motorcar")); profile.setTransportType( RoutingProfile::Motorcar ); break; case RoutingProfile::Bicycle: tpl = RoutingProfilesModel::BicycleTemplate; profile.setName(QStringLiteral("Bicycle")); profile.setTransportType( RoutingProfile::Bicycle ); break; case RoutingProfile::Pedestrian: tpl = RoutingProfilesModel::PedestrianTemplate; profile.setName(QStringLiteral("Pedestrian")); profile.setTransportType( RoutingProfile::Pedestrian ); break; } for( RoutingRunnerPlugin* plugin: d->m_pluginManager->routingRunnerPlugins() ) { if ( plugin->supportsTemplate( tpl ) ) { profile.pluginSettings()[plugin->nameId()] = plugin->templateSettings( tpl ); } } return profile; } void RoutingManager::readSettings() { d->loadRoute( d->stateFile() ); } void RoutingManager::setGuidanceModeEnabled( bool enabled ) { if ( d->m_guidanceModeEnabled == enabled ) { return; } d->m_guidanceModeEnabled = enabled; if ( enabled ) { d->saveRoute( d->stateFile( "guidance.kml" ) ); if ( d->m_guidanceModeWarning ) { QString text = QLatin1String("

") + tr("Caution: Driving instructions may be incomplete or wrong.") + QLatin1Char(' ') + tr("Road construction, weather and other unforeseen variables can result in the suggested route not to be the most expedient or safest route to your destination.") + QLatin1Char(' ') + tr("Please use common sense while navigating.") + QLatin1String("

") + QLatin1String("

") + tr("The Marble development team wishes you a pleasant and safe journey.") + QLatin1String("

"); QPointer messageBox = new QMessageBox(QMessageBox::Information, tr("Guidance Mode"), text, QMessageBox::Ok); QCheckBox *showAgain = new QCheckBox( tr( "Show again" ) ); showAgain->setChecked( true ); showAgain->blockSignals( true ); // otherwise it'd close the dialog messageBox->addButton( showAgain, QMessageBox::ActionRole ); const bool smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; messageBox->resize( 380, smallScreen ? 400 : 240 ); messageBox->exec(); if ( !messageBox.isNull() ) { d->m_guidanceModeWarning = showAgain->isChecked(); } delete messageBox; } } else { d->loadRoute( d->stateFile( "guidance.kml" ) ); } PositionProviderPlugin* positionProvider = d->m_positionTracking->positionProviderPlugin(); if ( !positionProvider && enabled ) { QList plugins = d->m_pluginManager->positionProviderPlugins(); if ( plugins.size() > 0 ) { positionProvider = plugins.first()->newInstance(); } d->m_positionTracking->setPositionProviderPlugin( positionProvider ); d->m_shutdownPositionTracking = true; } else if ( positionProvider && !enabled && d->m_shutdownPositionTracking ) { d->m_shutdownPositionTracking = false; d->m_positionTracking->setPositionProviderPlugin( 0 ); } emit guidanceModeEnabledChanged( d->m_guidanceModeEnabled ); } void RoutingManagerPrivate::recalculateRoute( bool deviated ) { if ( m_guidanceModeEnabled && deviated ) { for ( int i=m_routeRequest.size()-3; i>=0; --i ) { if ( m_routeRequest.visited( i ) ) { m_routeRequest.remove( i ); } } if ( m_routeRequest.size() == 2 && m_routeRequest.visited( 0 ) && !m_routeRequest.visited( 1 ) ) { m_routeRequest.setPosition( 0, m_positionTracking->currentLocation(), QObject::tr( "Current Location" ) ); q->retrieveRoute(); } else if ( m_routeRequest.size() != 0 && !m_routeRequest.visited( m_routeRequest.size()-1 ) ) { m_routeRequest.insert( 0, m_positionTracking->currentLocation(), QObject::tr( "Current Location" ) ); q->retrieveRoute(); } } } void RoutingManager::reverseRoute() { d->m_routeRequest.reverse(); retrieveRoute(); } void RoutingManager::clearRoute() { d->m_routeRequest.clear(); retrieveRoute(); } void RoutingManager::setShowGuidanceModeStartupWarning( bool show ) { d->m_guidanceModeWarning = show; } bool RoutingManager::showGuidanceModeStartupWarning() const { return d->m_guidanceModeWarning; } void RoutingManager::setLastOpenPath( const QString &path ) { d->m_lastOpenPath = path; } QString RoutingManager::lastOpenPath() const { return d->m_lastOpenPath; } void RoutingManager::setLastSavePath( const QString &path ) { d->m_lastSavePath = path; } QString RoutingManager::lastSavePath() const { return d->m_lastSavePath; } void RoutingManager::setRouteColorStandard( const QColor& color ) { d->m_routeColorStandard = color; } QColor RoutingManager::routeColorStandard() const { return d->m_routeColorStandard; } void RoutingManager::setRouteColorHighlighted( const QColor& color ) { d->m_routeColorHighlighted = color; } QColor RoutingManager::routeColorHighlighted() const { return d->m_routeColorHighlighted; } void RoutingManager::setRouteColorAlternative( const QColor& color ) { d->m_routeColorAlternative = color; } QColor RoutingManager::routeColorAlternative() const { return d->m_routeColorAlternative; } bool RoutingManager::guidanceModeEnabled() const { return d->m_guidanceModeEnabled; } } // namespace Marble #include "moc_RoutingManager.cpp" diff --git a/src/lib/marble/routing/RoutingModel.cpp b/src/lib/marble/routing/RoutingModel.cpp index 3e3a93a7b..0041cbc1a 100644 --- a/src/lib/marble/routing/RoutingModel.cpp +++ b/src/lib/marble/routing/RoutingModel.cpp @@ -1,319 +1,318 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Dennis Nienhüser // #include "RoutingModel.h" #include "Planet.h" #include "PlanetFactory.h" #include "MarbleMath.h" #include "Route.h" #include "RouteRequest.h" #include "PositionTracking.h" -#include "MarbleModel.h" #include "MarbleGlobal.h" #include "GeoDataAccuracy.h" #include namespace Marble { class RoutingModelPrivate { public: enum RouteDeviation { Unknown, OnRoute, OffRoute }; explicit RoutingModelPrivate(PositionTracking *positionTracking, RouteRequest *request); Route m_route; PositionTracking *const m_positionTracking; RouteRequest* const m_request; QHash m_roleNames; RouteDeviation m_deviation; void updateViaPoints( const GeoDataCoordinates &position ); }; RoutingModelPrivate::RoutingModelPrivate(PositionTracking *positionTracking, RouteRequest *request) : m_positionTracking(positionTracking), m_request(request), m_deviation(Unknown) { // nothing to do } void RoutingModelPrivate::updateViaPoints( const GeoDataCoordinates &position ) { // Mark via points visited after approaching them in a range of 500m or less qreal const threshold = 500 / EARTH_RADIUS; for( int i=0; isize(); ++i ) { if ( !m_request->visited( i ) ) { if ( distanceSphere( position, m_request->at( i ) ) < threshold ) { m_request->setVisited( i, true ); } } } } -RoutingModel::RoutingModel( RouteRequest* request, MarbleModel *model, QObject *parent ) : +RoutingModel::RoutingModel(RouteRequest *request, PositionTracking *positionTracking, QObject *parent) : QAbstractListModel(parent), - d(new RoutingModelPrivate(model->positionTracking(), request)) + d(new RoutingModelPrivate(positionTracking, request)) { QObject::connect( d->m_positionTracking, SIGNAL(gpsLocation(GeoDataCoordinates,qreal)), this, SLOT(updatePosition(GeoDataCoordinates,qreal)) ); QHash roles; roles.insert( Qt::DisplayRole, "display" ); roles.insert( RoutingModel::TurnTypeIconRole, "turnTypeIcon" ); roles.insert( RoutingModel::LongitudeRole, "longitude" ); roles.insert( RoutingModel::LatitudeRole, "latitude" ); d->m_roleNames = roles; } RoutingModel::~RoutingModel() { delete d; } int RoutingModel::rowCount ( const QModelIndex &parent ) const { return parent.isValid() ? 0 : d->m_route.turnPoints().size(); } QVariant RoutingModel::headerData ( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal && role == Qt::DisplayRole && section == 0 ) { return QString( "Instruction" ); } return QAbstractListModel::headerData( section, orientation, role ); } QVariant RoutingModel::data ( const QModelIndex & index, int role ) const { if ( !index.isValid() ) { return QVariant(); } if ( index.row() < d->m_route.turnPoints().size() && index.column() == 0 ) { const RouteSegment &segment = d->m_route.at( index.row() ); switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return segment.maneuver().instructionText(); break; case Qt::DecorationRole: { bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; if ( segment.maneuver().hasWaypoint() ) { int const size = smallScreen ? 64 : 32; return d->m_request->pixmap( segment.maneuver().waypointIndex(), size, size/4 ); } else { QPixmap const pixmap = segment.maneuver().directionPixmap(); return smallScreen ? pixmap : pixmap.scaled( 32, 32 ); } } break; case Qt::SizeHintRole: { bool const smallScreen = MarbleGlobal::getInstance()->profiles() & MarbleGlobal::SmallScreen; int const size = smallScreen ? 64 : 32; return QSize( size, size ); } break; case RoutingModel::CoordinateRole: return QVariant::fromValue( segment.maneuver().position() ); break; case RoutingModel::LongitudeRole: return QVariant(segment.maneuver().position().longitude(GeoDataCoordinates::Degree)); break; case RoutingModel::LatitudeRole: return QVariant(segment.maneuver().position().latitude(GeoDataCoordinates::Degree)); break; case RoutingModel::TurnTypeIconRole: return segment.maneuver().directionPixmap(); break; default: return QVariant(); } } return QVariant(); } QHash RoutingModel::roleNames() const { return d->m_roleNames; } void RoutingModel::setRoute( const Route &route ) { d->m_route = route; d->m_deviation = RoutingModelPrivate::Unknown; beginResetModel(); endResetModel(); emit currentRouteChanged(); } void RoutingModel::exportGpx( QIODevice *device ) const { QString content = QLatin1String("\n" "\n" "\n \n " "Marble Virtual Globe\n \n\n" " \n Route\n"); bool hasAltitude = false; for ( int i=0; !hasAltitude && im_route.size(); ++i ) { hasAltitude = d->m_route.at( i ).maneuver().position().altitude() != 0.0; } for ( int i=0; im_route.size(); ++i ) { const Maneuver &maneuver = d->m_route.at( i ).maneuver(); qreal lon = maneuver.position().longitude( GeoDataCoordinates::Degree ); qreal lat = maneuver.position().latitude( GeoDataCoordinates::Degree ); QString const text = maneuver.instructionText(); content += QString( " \n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 ); content += QString( " %1\n").arg( text ); if ( hasAltitude ) { content += QString( " %1\n" ).arg( maneuver.position().altitude(), 0, 'f', 2 ); } content += QString( " \n" ); } content += QLatin1String(" \n" "\n Route\n \n"); GeoDataLineString points = d->m_route.path(); hasAltitude = false; for ( int i=0; !hasAltitude && i\n" ).arg( lat, 0, 'f', 7 ).arg( lon, 0, 'f', 7 ); if ( hasAltitude ) { content += QString( " %1\n" ).arg( point.altitude(), 0, 'f', 2 ); } content += QString( " \n" ); } content += QLatin1String(" \n \n" "\n"); device->write( content.toUtf8() ); } void RoutingModel::clear() { d->m_route = Route(); beginResetModel(); endResetModel(); emit currentRouteChanged(); } int RoutingModel::rightNeighbor( const GeoDataCoordinates &position, RouteRequest const *const route ) const { Q_ASSERT( route && "Must not pass a null route "); // Quick result for trivial cases if ( route->size() < 3 ) { return route->size() - 1; } // Generate an ordered list of all waypoints GeoDataLineString points = d->m_route.path(); QMap mapping; // Force first mapping point to match the route start mapping[0] = 0; // Calculate the mapping between waypoints and via points // Need two for loops to avoid getting stuck in local minima for ( int j=1; jsize()-1; ++j ) { qreal minDistance = -1.0; for ( int i=mapping[j-1]; iat(j) ); if (minDistance < 0.0 || distance < minDistance ) { mapping[j] = i; minDistance = distance; } } } // Determine waypoint with minimum distance to the provided position qreal minWaypointDistance = -1.0; int waypoint=0; for ( int i=0; isize()-1] = points.size()-1; // Determine neighbor based on the mapping QMap::const_iterator iter = mapping.constBegin(); for ( ; iter != mapping.constEnd(); ++iter ) { if ( iter.value() > waypoint ) { int index = iter.key(); Q_ASSERT( index >= 0 && index <= route->size() ); return index; } } return route->size()-1; } void RoutingModel::updatePosition( GeoDataCoordinates location, qreal speed ) { d->m_route.setPosition( location ); d->updateViaPoints( location ); const qreal planetRadius = PlanetFactory::construct("earth").radius(); qreal distance = planetRadius * distanceSphere( location, d->m_route.positionOnRoute() ); emit positionChanged(); qreal deviation = 0.0; if ( d->m_positionTracking && d->m_positionTracking->accuracy().vertical > 0.0 ) { deviation = qMax( d->m_positionTracking->accuracy().vertical, d->m_positionTracking->accuracy().horizontal ); } qreal const threshold = deviation + qBound(10.0, speed*10.0, 150.0); RoutingModelPrivate::RouteDeviation const deviated = distance < threshold ? RoutingModelPrivate::OnRoute : RoutingModelPrivate::OffRoute; if ( d->m_deviation != deviated ) { d->m_deviation = deviated; emit deviatedFromRoute( deviated == RoutingModelPrivate::OffRoute ); } } bool RoutingModel::deviatedFromRoute() const { return d->m_deviation == RoutingModelPrivate::OffRoute; } const Route & RoutingModel::route() const { return d->m_route; } } // namespace Marble #include "moc_RoutingModel.cpp" diff --git a/src/lib/marble/routing/RoutingModel.h b/src/lib/marble/routing/RoutingModel.h index 9dcf1d998..2c3d753f7 100644 --- a/src/lib/marble/routing/RoutingModel.h +++ b/src/lib/marble/routing/RoutingModel.h @@ -1,123 +1,123 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2010 Dennis Nienhüser // #ifndef MARBLE_ROUTINGMODEL_H #define MARBLE_ROUTINGMODEL_H #include "marble_export.h" #include class QIODevice; /** * A QAbstractItemModel that contains a list of routing instructions. * Each item represents a routing step in the way from source to * destination. Steps near the source come first, steps near the target * last. */ namespace Marble { class RoutingModelPrivate; class Route; class RouteRequest; -class MarbleModel; class GeoDataCoordinates; +class PositionTracking; class MARBLE_EXPORT RoutingModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY( bool deviatedFromRoute READ deviatedFromRoute NOTIFY deviatedFromRoute ) public: enum RoutingModelRoles { CoordinateRole = Qt::UserRole + 3, TurnTypeIconRole, LongitudeRole, LatitudeRole }; /** Constructor */ - explicit RoutingModel( RouteRequest* request, MarbleModel *model, QObject *parent = 0 ); + explicit RoutingModel(RouteRequest *request, PositionTracking *positionTracking, QObject *parent = 0); /** Destructor */ ~RoutingModel() override; // Model querying /** Overload of QAbstractListModel */ int rowCount ( const QModelIndex &parent = QModelIndex() ) const override; /** Overload of QAbstractListModel */ QVariant headerData ( int section, Qt::Orientation orientation, int role = Qt::DisplayRole ) const override; /** Overload of QAbstractListModel */ QVariant data ( const QModelIndex &index, int role = Qt::DisplayRole ) const override; /** Overload of QAbstractListModel */ QHash roleNames() const override; // Model data filling /** * Export waypoints and instructions in gpx format */ void exportGpx( QIODevice *device ) const; /** * Clear any data held in the model */ void clear(); /** * Maps points from the provided route request to waypoints in the model * according to their global minimal distance. Returns the right neighbor * (next route request item along the waypoints) of the provided position. * Provided route must not be null. * @return -1 If the provided route is empty, the index of the right * neighbor along the waypoints otherwise (result is a valid RouteRequest * index in that case) */ int rightNeighbor( const GeoDataCoordinates &position, RouteRequest const *const route ) const; /** * returns whether the gps location is on route */ bool deviatedFromRoute() const; const Route & route() const; public Q_SLOTS: /** * Old data in the model is discarded and a model reset is done */ void setRoute( const Route &route ); void updatePosition( GeoDataCoordinates, qreal ); Q_SIGNALS: /** * emits a signal regarding information about total time( seconds ) and distance( metres ) remaining to reach destination */ void positionChanged(); void deviatedFromRoute( bool deviated ); /** A different route was loaded */ void currentRouteChanged(); private: RoutingModelPrivate *const d; }; } // namespace Marble #endif