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"