File indexing completed on 2024-05-05 03:49:20
0001 // SPDX-License-Identifier: LGPL-2.1-or-later 0002 // 0003 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org> 0004 // 0005 0006 #include "Routing.h" 0007 0008 #include <MarbleMap.h> 0009 #include <MarbleModel.h> 0010 #include "MarbleDirs.h" 0011 #include "routing/AlternativeRoutesModel.h" 0012 #include "routing/RoutingManager.h" 0013 #include "routing/RouteRequest.h" 0014 #include "routing/RoutingProfilesModel.h" 0015 #include <GeoDataLatLonAltBox.h> 0016 #include <GeoPainter.h> 0017 #include <routing/Route.h> 0018 #include <declarative/RouteRequestModel.h> 0019 #include <ViewportParams.h> 0020 #include <PositionTracking.h> 0021 0022 #include <QDebug> 0023 #include <QQmlContext> 0024 #include <QOpenGLPaintDevice> 0025 #include <QSGGeometryNode> 0026 #include <QSGFlatColorMaterial> 0027 0028 namespace Marble { 0029 0030 class RoutingPrivate 0031 { 0032 public: 0033 explicit RoutingPrivate(QObject * parent = nullptr); 0034 0035 MarbleMap* m_marbleMap; 0036 QMap<QString, Marble::RoutingProfile> m_profiles; 0037 QString m_routingProfile; 0038 QQmlComponent * m_waypointDelegate; 0039 QMap<int,QQuickItem*> m_waypointItems; 0040 RouteRequestModel* m_routeRequestModel; 0041 QObject * m_parent; 0042 QVector<Placemark *> m_searchResultPlacemarks; 0043 QMap<int, QQuickItem*> m_searchResultItems; 0044 }; 0045 0046 RoutingPrivate::RoutingPrivate(QObject *parent) : 0047 m_marbleMap( nullptr ), 0048 m_waypointDelegate( nullptr ), 0049 m_routeRequestModel( new RouteRequestModel(parent) ), 0050 m_parent( parent ) 0051 { 0052 // nothing to do 0053 } 0054 0055 Routing::Routing( QQuickItem *parent) : 0056 QQuickItem( parent ), d( new RoutingPrivate(this) ) 0057 { 0058 setFlag(ItemHasContents, true); 0059 d->m_routeRequestModel->setRouting(this); 0060 connect(d->m_routeRequestModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateWaypointItems())); 0061 connect(d->m_routeRequestModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateWaypointItems())); 0062 connect(d->m_routeRequestModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateWaypointItems())); 0063 0064 emit routeRequestModelChanged(d->m_routeRequestModel); 0065 } 0066 0067 Routing::~Routing() 0068 { 0069 delete d; 0070 } 0071 0072 QSGNode * Routing::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) { 0073 if (!d->m_marbleMap) { 0074 return nullptr; 0075 } 0076 0077 QOpenGLPaintDevice paintDevice(QSize(width(), height())); 0078 Marble::GeoPainter geoPainter(&paintDevice, d->m_marbleMap->viewport(), d->m_marbleMap->mapQuality()); 0079 0080 RoutingManager const * const routingManager = d->m_marbleMap->model()->routingManager(); 0081 GeoDataLineString const & waypoints = routingManager->routingModel()->route().path(); 0082 0083 if (waypoints.isEmpty()) { 0084 return nullptr; 0085 } 0086 0087 int const dpi = qMax(paintDevice.logicalDpiX(), paintDevice.logicalDpiY()); 0088 qreal const halfWidth = 0.5 * 2.5 * MM2M * M2IN * dpi; 0089 0090 QColor standardRouteColor = routingManager->state() == RoutingManager::Downloading ? 0091 routingManager->routeColorStandard() : 0092 routingManager->routeColorStandard().darker( 200 ); 0093 0094 QVector<QPolygonF*> polygons; 0095 geoPainter.polygonsFromLineString( waypoints, polygons); 0096 0097 if (!polygons.isEmpty()) { 0098 delete oldNode; 0099 oldNode = new QSGNode; 0100 for(const QPolygonF* itPolygon: polygons) { 0101 QPolygonF const & polygon = *itPolygon; 0102 QVector<QVector2D> normals; 0103 int segmentCount = itPolygon->size() - 1; 0104 normals.reserve(segmentCount); 0105 for(int i = 0; i < segmentCount; ++i) { 0106 normals << QVector2D(polygon[i+1] - polygon[i]).normalized(); 0107 } 0108 QSGGeometryNode* lineNode = new QSGGeometryNode; 0109 0110 QSGGeometry * lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount*4); 0111 lineNodeGeo->setDrawingMode(GL_TRIANGLE_STRIP); 0112 lineNodeGeo->allocate(segmentCount*4); 0113 0114 QSGFlatColorMaterial *material = new QSGFlatColorMaterial; 0115 material->setColor(standardRouteColor); 0116 0117 lineNode->setGeometry(lineNodeGeo); 0118 lineNode->setFlag(QSGNode::OwnsGeometry); 0119 lineNode->setMaterial(material); 0120 lineNode->setFlag(QSGNode::OwnsMaterial); 0121 0122 auto points = lineNodeGeo->vertexDataAsPoint2D(); 0123 int k = -1; 0124 for(int i = 0; i < segmentCount; ++i) { 0125 auto const & a = polygon[i]; 0126 auto const & b = polygon[i+1]; 0127 auto const & n = normals[i]; 0128 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x()); 0129 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x()); 0130 points[++k].set(b.x() - halfWidth * n.y(), b.y() + halfWidth * n.x()); 0131 points[++k].set(b.x() + halfWidth * n.y(), b.y() - halfWidth * n.x()); 0132 } 0133 0134 oldNode->appendChildNode(lineNode); 0135 } 0136 } else { 0137 if (oldNode && oldNode->childCount() > 0) { 0138 delete oldNode; 0139 oldNode = new QSGNode; 0140 } 0141 } 0142 0143 qDeleteAll(polygons); 0144 return oldNode; 0145 } 0146 0147 QObject* Routing::waypointModel() 0148 { 0149 return d->m_marbleMap ? d->m_marbleMap->model()->routingManager()->routingModel() : nullptr; 0150 } 0151 0152 void Routing::setWaypointDelegate(QQmlComponent *waypointDelegate) 0153 { 0154 if (d->m_waypointDelegate == waypointDelegate) { 0155 return; 0156 } 0157 0158 d->m_waypointDelegate = waypointDelegate; 0159 emit waypointDelegateChanged(waypointDelegate); 0160 } 0161 0162 void Routing::updateWaypointItems() 0163 { 0164 if ( d->m_marbleMap && d->m_routeRequestModel ) { 0165 for (int i = d->m_waypointItems.keys().size(); i < d->m_routeRequestModel->rowCount(); i++ ) { 0166 QQmlContext * context = new QQmlContext( qmlContext( d->m_waypointDelegate ) ); 0167 QObject * component = d->m_waypointDelegate->create(context); 0168 QQuickItem* item = qobject_cast<QQuickItem*>( component ); 0169 if ( item ) { 0170 item->setParentItem( this ); 0171 item->setProperty("index", i); 0172 d->m_waypointItems[i] = item; 0173 } else { 0174 delete component; 0175 } 0176 } 0177 0178 for (int i = d->m_waypointItems.keys().size()-1; i >= d->m_routeRequestModel->rowCount(); i--) { 0179 QQuickItem* item = d->m_waypointItems[i]; 0180 item->setProperty("visible", QVariant(false) ); 0181 d->m_waypointItems.erase(d->m_waypointItems.find(i)); 0182 item->deleteLater(); 0183 } 0184 0185 QMap<int, QQuickItem*>::iterator iter = d->m_waypointItems.begin(); 0186 while ( iter != d->m_waypointItems.end() ) { 0187 qreal x = 0; 0188 qreal y = 0; 0189 const qreal lon = d->m_routeRequestModel->data(d->m_routeRequestModel->index( iter.key() ), RouteRequestModel::LongitudeRole).toFloat(); 0190 const qreal lat = d->m_routeRequestModel->data(d->m_routeRequestModel->index( iter.key() ), RouteRequestModel::LatitudeRole).toFloat(); 0191 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon * DEG2RAD, lat * DEG2RAD, x, y); 0192 0193 QQuickItem * item = iter.value(); 0194 if ( item ) { 0195 item->setVisible( visible ); 0196 if ( visible ) { 0197 item->setProperty("xPos", QVariant(x)); 0198 item->setProperty("yPos", QVariant(y)); 0199 if (iter.key() == 0 && waypointCount() == 1) { 0200 item->setProperty("type", QVariant(QStringLiteral("departure"))); 0201 } 0202 else if (iter.key() == d->m_waypointItems.keys().size()-1) { 0203 item->setProperty("type", QVariant(QStringLiteral("destination"))); 0204 } 0205 else if (iter.key() > 0) { 0206 item->setProperty("type", QVariant(QStringLiteral("waypoint"))); 0207 } 0208 else { 0209 item->setProperty("type", QVariant(QStringLiteral("departure"))); 0210 } 0211 } 0212 } 0213 ++iter; 0214 } 0215 } 0216 } 0217 0218 int Routing::addSearchResultPlacemark(Placemark *placemark) 0219 { 0220 if ( d->m_marbleMap ) { 0221 for (int i = 0; i < d->m_searchResultItems.size(); i++) { 0222 if (d->m_searchResultPlacemarks[i]->placemark().coordinate() == placemark->placemark().coordinate()) { 0223 return i; 0224 } 0225 } 0226 Placemark * newPlacemark = new Placemark(this); 0227 newPlacemark->setGeoDataPlacemark(placemark->placemark()); 0228 d->m_searchResultPlacemarks.push_back(newPlacemark); 0229 } 0230 0231 updateSearchResultPlacemarks(); 0232 return d->m_searchResultPlacemarks.size()-1; 0233 } 0234 0235 void Routing::clearSearchResultPlacemarks() 0236 { 0237 for(Placemark* placemark: d->m_searchResultPlacemarks) { 0238 placemark->deleteLater(); 0239 } 0240 d->m_searchResultPlacemarks.clear(); 0241 0242 for(QQuickItem* item: d->m_searchResultItems) { 0243 item->deleteLater(); 0244 } 0245 d->m_searchResultItems.clear(); 0246 } 0247 0248 void Routing::updateSearchResultPlacemarks() 0249 { 0250 for (int i = d->m_searchResultItems.keys().size(); i < d->m_searchResultPlacemarks.size(); i++ ) { 0251 QQmlContext * context = new QQmlContext( qmlContext( d->m_waypointDelegate ) ); 0252 QObject * component = d->m_waypointDelegate->create(context); 0253 QQuickItem* item = qobject_cast<QQuickItem*>( component ); 0254 if ( item ) { 0255 item->setParentItem( this ); 0256 item->setProperty("index", i); 0257 item->setProperty("type", QVariant(QStringLiteral("searchResult"))); 0258 item->setProperty("placemark", QVariant::fromValue(d->m_searchResultPlacemarks[i])); 0259 d->m_searchResultItems[i] = item; 0260 } else { 0261 delete component; 0262 } 0263 } 0264 0265 for (int i = d->m_searchResultItems.keys().size()-1; i >= d->m_searchResultPlacemarks.size(); i--) { 0266 QQuickItem* item = d->m_searchResultItems[i]; 0267 item->setProperty("visible", QVariant(false) ); 0268 d->m_searchResultItems.erase(d->m_searchResultItems.find(i)); 0269 item->deleteLater(); 0270 } 0271 0272 for (int i = 0; i < d->m_searchResultItems.keys().size() && i < d->m_searchResultPlacemarks.size(); i++) { 0273 qreal x = 0; 0274 qreal y = 0; 0275 const qreal lon = d->m_searchResultPlacemarks[i]->placemark().coordinate().longitude(); 0276 const qreal lat = d->m_searchResultPlacemarks[i]->placemark().coordinate().latitude(); 0277 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon, lat, x, y); 0278 0279 QQuickItem * item = d->m_searchResultItems[i]; 0280 if ( item ) { 0281 item->setVisible( visible ); 0282 if ( visible ) { 0283 item->setProperty("xPos", QVariant(x)); 0284 item->setProperty("yPos", QVariant(y)); 0285 } 0286 } 0287 } 0288 } 0289 0290 void Routing::setMarbleMap( MarbleMap* marbleMap ) 0291 { 0292 d->m_marbleMap = marbleMap; 0293 0294 if ( d->m_marbleMap ) { 0295 connect(d->m_marbleMap, SIGNAL(repaintNeeded(QRegion)), this, SLOT(update())); 0296 RoutingManager* routingManager = d->m_marbleMap->model()->routingManager(); 0297 if (routingManager->profilesModel()->rowCount() == 0) { 0298 routingManager->profilesModel()->loadDefaultProfiles(); 0299 routingManager->readSettings(); 0300 } 0301 0302 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)), this, SLOT(update())); 0303 connect( routingManager, SIGNAL(routeRetrieved(GeoDataDocument*)), this, SLOT(update())); 0304 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)), 0305 this, SIGNAL(hasRouteChanged()) ); 0306 connect( routingModel(), SIGNAL(currentRouteChanged()), 0307 this, SIGNAL(hasRouteChanged()) ); 0308 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)), 0309 this, SIGNAL(hasWaypointsChanged()) ); 0310 connect( routingModel(), SIGNAL(currentRouteChanged()), 0311 this, SIGNAL(hasWaypointsChanged()) ); 0312 connect( routingModel(), SIGNAL(currentRouteChanged()), 0313 this, SLOT(update()) ); 0314 connect( d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), 0315 this, SLOT(updateWaypointItems()) ); 0316 connect( d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), 0317 this, SLOT(updateSearchResultPlacemarks()) ); 0318 0319 emit routingModelChanged(); 0320 0321 QList<Marble::RoutingProfile> profiles = routingManager->profilesModel()->profiles(); 0322 if ( profiles.size() == 4 ) { 0323 /** @todo FIXME: Restrictive assumptions on available plugins and certain profile loading implementation */ 0324 d->m_profiles[QStringLiteral("Motorcar")] = profiles.at( 0 ); 0325 d->m_profiles[QStringLiteral("Bicycle")] = profiles.at( 2 ); 0326 d->m_profiles[QStringLiteral("Pedestrian")] = profiles.at( 3 ); 0327 } else { 0328 qDebug() << "Unexpected size of default routing profiles: " << profiles.size(); 0329 } 0330 } 0331 0332 emit marbleMapChanged(); 0333 emit routingProfileChanged(); 0334 emit hasRouteChanged(); 0335 emit hasWaypointsChanged(); 0336 } 0337 0338 MarbleMap *Routing::marbleMap() 0339 { 0340 return d->m_marbleMap; 0341 } 0342 0343 QString Routing::routingProfile() const 0344 { 0345 return d->m_routingProfile; 0346 } 0347 0348 void Routing::setRoutingProfile( const QString & profile ) 0349 { 0350 if ( d->m_routingProfile != profile ) { 0351 d->m_routingProfile = profile; 0352 if ( d->m_marbleMap ) { 0353 d->m_marbleMap->model()->routingManager()->routeRequest()->setRoutingProfile( d->m_profiles[profile] ); 0354 } 0355 emit routingProfileChanged(); 0356 } 0357 } 0358 0359 bool Routing::hasRoute() const 0360 { 0361 return d->m_marbleMap && !d->m_marbleMap->model()->routingManager()->routingModel()->route().path().isEmpty(); 0362 } 0363 0364 bool Routing::hasWaypoints() const 0365 { 0366 return d->m_marbleMap && d->m_marbleMap->model()->routingManager()->routingModel()->rowCount() > 0; 0367 } 0368 0369 RoutingModel *Routing::routingModel() 0370 { 0371 return d->m_marbleMap == nullptr ? nullptr : d->m_marbleMap->model()->routingManager()->routingModel(); 0372 } 0373 0374 QQmlComponent *Routing::waypointDelegate() const 0375 { 0376 return d->m_waypointDelegate; 0377 } 0378 0379 int Routing::waypointCount() const 0380 { 0381 return d->m_routeRequestModel ? d->m_routeRequestModel->rowCount() : 0; 0382 } 0383 0384 RouteRequestModel *Routing::routeRequestModel() 0385 { 0386 return d->m_routeRequestModel; 0387 } 0388 0389 void Routing::addVia( qreal lon, qreal lat ) 0390 { 0391 if ( d->m_marbleMap ) { 0392 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0393 request->addVia( Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) ); 0394 updateRoute(); 0395 } 0396 } 0397 0398 void Routing::addViaAtIndex(int index, qreal lon, qreal lat) 0399 { 0400 if ( d->m_marbleMap ) { 0401 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0402 request->insert(index, Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree) ); 0403 updateRoute(); 0404 } 0405 } 0406 0407 void Routing::addViaByPlacemark(Placemark *placemark) 0408 { 0409 if (d->m_marbleMap && placemark) { 0410 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0411 request->addVia(placemark->placemark()); 0412 updateRoute(); 0413 } 0414 } 0415 0416 void Routing::addViaByPlacemarkAtIndex(int index, Placemark *placemark) 0417 { 0418 if (d->m_marbleMap && placemark) { 0419 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0420 request->insert(index, placemark->placemark()); 0421 updateRoute(); 0422 } 0423 } 0424 0425 void Routing::setVia( int index, qreal lon, qreal lat ) 0426 { 0427 if ( index < 0 || index > 200 || !d->m_marbleMap ) { 0428 return; 0429 } 0430 0431 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0432 Q_ASSERT( request ); 0433 if ( index < request->size() ) { 0434 request->setPosition( index, Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) ); 0435 } else { 0436 for ( int i=request->size(); i<index; ++i ) { 0437 request->append( Marble::GeoDataCoordinates( 0.0, 0.0 ) ); 0438 } 0439 request->append( Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) ); 0440 } 0441 0442 updateRoute(); 0443 } 0444 0445 void Routing::removeVia( int index ) 0446 { 0447 if ( index < 0 || !d->m_marbleMap ) { 0448 return; 0449 } 0450 0451 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0452 if ( index < request->size() ) { 0453 d->m_marbleMap->model()->routingManager()->routeRequest()->remove( index ); 0454 } 0455 0456 updateRoute(); 0457 } 0458 0459 void Routing::swapVias(int index1, int index2) 0460 { 0461 if ( !d->m_marbleMap || !d->m_routeRequestModel ) { 0462 return; 0463 } 0464 0465 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest(); 0466 request->swap(index1, index2); 0467 updateRoute(); 0468 updateWaypointItems(); 0469 } 0470 0471 void Routing::reverseRoute() 0472 { 0473 if ( d->m_marbleMap ) { 0474 d->m_marbleMap->model()->routingManager()->reverseRoute(); 0475 } 0476 } 0477 0478 void Routing::clearRoute() 0479 { 0480 if ( d->m_marbleMap ) { 0481 d->m_marbleMap->model()->routingManager()->clearRoute(); 0482 } 0483 } 0484 0485 void Routing::updateRoute() 0486 { 0487 if ( d->m_marbleMap ) { 0488 d->m_marbleMap->model()->routingManager()->retrieveRoute(); 0489 } 0490 } 0491 0492 void Routing::openRoute( const QString &fileName ) 0493 { 0494 if ( d->m_marbleMap ) { 0495 Marble::RoutingManager * const routingManager = d->m_marbleMap->model()->routingManager(); 0496 /** @todo FIXME: replace the file:// prefix on QML side */ 0497 routingManager->clearRoute(); 0498 QString target = fileName.startsWith( QLatin1String( "file://" ) ) ? fileName.mid( 7 ) : fileName; 0499 routingManager->loadRoute( target ); 0500 const Marble::GeoDataDocument *route = routingManager->alternativeRoutesModel()->currentRoute(); 0501 if ( route ) { 0502 const Marble::GeoDataLineString* waypoints = Marble::AlternativeRoutesModel::waypoints( route ); 0503 if ( waypoints ) { 0504 GeoDataCoordinates const center = waypoints->latLonAltBox().center(); 0505 GeoDataCoordinates::Unit const inDegree = GeoDataCoordinates::Degree; 0506 d->m_marbleMap->centerOn( center.longitude(inDegree), center.latitude(inDegree) ); 0507 } 0508 } 0509 } 0510 } 0511 0512 void Routing::saveRoute( const QString &fileName ) 0513 { 0514 if ( d->m_marbleMap ) { 0515 /** @todo FIXME: replace the file:// prefix on QML side */ 0516 QString target = fileName.startsWith( QLatin1String( "file://" ) ) ? fileName.mid( 7 ) : fileName; 0517 d->m_marbleMap->model()->routingManager()->saveRoute( target ); 0518 } 0519 } 0520 0521 } 0522 0523 #include "moc_Routing.cpp"