Marble

Routing.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2011 Dennis Nienhüser <nienhueser@kde.org>
4//
5
6#include "Routing.h"
7
8#include <MarbleMap.h>
9#include <MarbleModel.h>
10#include "MarbleDirs.h"
11#include "routing/AlternativeRoutesModel.h"
12#include "routing/RoutingManager.h"
13#include "routing/RouteRequest.h"
14#include "routing/RoutingProfilesModel.h"
15#include <GeoDataLatLonAltBox.h>
16#include <GeoPainter.h>
17#include <routing/Route.h>
18#include <declarative/RouteRequestModel.h>
19#include <ViewportParams.h>
20#include <PositionTracking.h>
21
22#include <QDebug>
23#include <QQmlContext>
24#include <QOpenGLPaintDevice>
25#include <QSGGeometryNode>
26#include <QSGFlatColorMaterial>
27
28namespace Marble {
29
30class RoutingPrivate
31{
32public:
33 explicit RoutingPrivate(QObject * parent = nullptr);
34
35 MarbleMap* m_marbleMap;
37 QString m_routingProfile;
38 QQmlComponent * m_waypointDelegate;
39 QMap<int,QQuickItem*> m_waypointItems;
40 RouteRequestModel* m_routeRequestModel;
41 QObject * m_parent;
42 QVector<Placemark *> m_searchResultPlacemarks;
43 QMap<int, QQuickItem*> m_searchResultItems;
44};
45
46RoutingPrivate::RoutingPrivate(QObject *parent) :
47 m_marbleMap( nullptr ),
48 m_waypointDelegate( nullptr ),
49 m_routeRequestModel( new RouteRequestModel(parent) ),
50 m_parent( parent )
51{
52 // nothing to do
53}
54
55Routing::Routing( QQuickItem *parent) :
56 QQuickItem( parent ), d( new RoutingPrivate(this) )
57{
58 setFlag(ItemHasContents, true);
59 d->m_routeRequestModel->setRouting(this);
60 connect(d->m_routeRequestModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(updateWaypointItems()));
61 connect(d->m_routeRequestModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), this, SLOT(updateWaypointItems()));
62 connect(d->m_routeRequestModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(updateWaypointItems()));
63
64 emit routeRequestModelChanged(d->m_routeRequestModel);
65}
66
67Routing::~Routing()
68{
69 delete d;
70}
71
72QSGNode * Routing::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) {
73 if (!d->m_marbleMap) {
74 return nullptr;
75 }
76
77 QOpenGLPaintDevice paintDevice(QSize(width(), height()));
78 Marble::GeoPainter geoPainter(&paintDevice, d->m_marbleMap->viewport(), d->m_marbleMap->mapQuality());
79
80 RoutingManager const * const routingManager = d->m_marbleMap->model()->routingManager();
81 GeoDataLineString const & waypoints = routingManager->routingModel()->route().path();
82
83 if (waypoints.isEmpty()) {
84 return nullptr;
85 }
86
87 int const dpi = qMax(paintDevice.logicalDpiX(), paintDevice.logicalDpiY());
88 qreal const halfWidth = 0.5 * 2.5 * MM2M * M2IN * dpi;
89
90 QColor standardRouteColor = routingManager->state() == RoutingManager::Downloading ?
91 routingManager->routeColorStandard() :
92 routingManager->routeColorStandard().darker( 200 );
93
94 QVector<QPolygonF*> polygons;
95 geoPainter.polygonsFromLineString( waypoints, polygons);
96
97 if (!polygons.isEmpty()) {
98 delete oldNode;
99 oldNode = new QSGNode;
100 for(const QPolygonF* itPolygon: polygons) {
101 QPolygonF const & polygon = *itPolygon;
102 QVector<QVector2D> normals;
103 int segmentCount = itPolygon->size() - 1;
104 normals.reserve(segmentCount);
105 for(int i = 0; i < segmentCount; ++i) {
106 normals << QVector2D(polygon[i+1] - polygon[i]).normalized();
107 }
108 QSGGeometryNode* lineNode = new QSGGeometryNode;
109
110 QSGGeometry * lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount*4);
111 lineNodeGeo->setDrawingMode(GL_TRIANGLE_STRIP);
112 lineNodeGeo->allocate(segmentCount*4);
113
115 material->setColor(standardRouteColor);
116
117 lineNode->setGeometry(lineNodeGeo);
119 lineNode->setMaterial(material);
121
122 auto points = lineNodeGeo->vertexDataAsPoint2D();
123 int k = -1;
124 for(int i = 0; i < segmentCount; ++i) {
125 auto const & a = polygon[i];
126 auto const & b = polygon[i+1];
127 auto const & n = normals[i];
128 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x());
129 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x());
130 points[++k].set(b.x() - halfWidth * n.y(), b.y() + halfWidth * n.x());
131 points[++k].set(b.x() + halfWidth * n.y(), b.y() - halfWidth * n.x());
132 }
133
134 oldNode->appendChildNode(lineNode);
135 }
136 } else {
137 if (oldNode && oldNode->childCount() > 0) {
138 delete oldNode;
139 oldNode = new QSGNode;
140 }
141 }
142
143 qDeleteAll(polygons);
144 return oldNode;
145}
146
147QObject* Routing::waypointModel()
148{
149 return d->m_marbleMap ? d->m_marbleMap->model()->routingManager()->routingModel() : nullptr;
150}
151
152void Routing::setWaypointDelegate(QQmlComponent *waypointDelegate)
153{
154 if (d->m_waypointDelegate == waypointDelegate) {
155 return;
156 }
157
158 d->m_waypointDelegate = waypointDelegate;
159 emit waypointDelegateChanged(waypointDelegate);
160}
161
162void Routing::updateWaypointItems()
163{
164 if ( d->m_marbleMap && d->m_routeRequestModel ) {
165 for (int i = d->m_waypointItems.keys().size(); i < d->m_routeRequestModel->rowCount(); i++ ) {
166 QQmlContext * context = new QQmlContext( qmlContext( d->m_waypointDelegate ) );
167 QObject * component = d->m_waypointDelegate->create(context);
168 QQuickItem* item = qobject_cast<QQuickItem*>( component );
169 if ( item ) {
170 item->setParentItem( this );
171 item->setProperty("index", i);
172 d->m_waypointItems[i] = item;
173 } else {
174 delete component;
175 }
176 }
177
178 for (int i = d->m_waypointItems.keys().size()-1; i >= d->m_routeRequestModel->rowCount(); i--) {
179 QQuickItem* item = d->m_waypointItems[i];
180 item->setProperty("visible", QVariant(false) );
181 d->m_waypointItems.erase(d->m_waypointItems.find(i));
182 item->deleteLater();
183 }
184
185 QMap<int, QQuickItem*>::iterator iter = d->m_waypointItems.begin();
186 while ( iter != d->m_waypointItems.end() ) {
187 qreal x = 0;
188 qreal y = 0;
189 const qreal lon = d->m_routeRequestModel->data(d->m_routeRequestModel->index( iter.key() ), RouteRequestModel::LongitudeRole).toFloat();
190 const qreal lat = d->m_routeRequestModel->data(d->m_routeRequestModel->index( iter.key() ), RouteRequestModel::LatitudeRole).toFloat();
191 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon * DEG2RAD, lat * DEG2RAD, x, y);
192
193 QQuickItem * item = iter.value();
194 if ( item ) {
195 item->setVisible( visible );
196 if ( visible ) {
197 item->setProperty("xPos", QVariant(x));
198 item->setProperty("yPos", QVariant(y));
199 if (iter.key() == 0 && waypointCount() == 1) {
200 item->setProperty("type", QVariant(QStringLiteral("departure")));
201 }
202 else if (iter.key() == d->m_waypointItems.keys().size()-1) {
203 item->setProperty("type", QVariant(QStringLiteral("destination")));
204 }
205 else if (iter.key() > 0) {
206 item->setProperty("type", QVariant(QStringLiteral("waypoint")));
207 }
208 else {
209 item->setProperty("type", QVariant(QStringLiteral("departure")));
210 }
211 }
212 }
213 ++iter;
214 }
215 }
216}
217
218int Routing::addSearchResultPlacemark(Placemark *placemark)
219{
220 if ( d->m_marbleMap ) {
221 for (int i = 0; i < d->m_searchResultItems.size(); i++) {
222 if (d->m_searchResultPlacemarks[i]->placemark().coordinate() == placemark->placemark().coordinate()) {
223 return i;
224 }
225 }
226 Placemark * newPlacemark = new Placemark(this);
227 newPlacemark->setGeoDataPlacemark(placemark->placemark());
228 d->m_searchResultPlacemarks.push_back(newPlacemark);
229 }
230
231 updateSearchResultPlacemarks();
232 return d->m_searchResultPlacemarks.size()-1;
233}
234
235void Routing::clearSearchResultPlacemarks()
236{
237 for(Placemark* placemark: d->m_searchResultPlacemarks) {
238 placemark->deleteLater();
239 }
240 d->m_searchResultPlacemarks.clear();
241
242 for(QQuickItem* item: d->m_searchResultItems) {
243 item->deleteLater();
244 }
245 d->m_searchResultItems.clear();
246}
247
248void Routing::updateSearchResultPlacemarks()
249{
250 for (int i = d->m_searchResultItems.keys().size(); i < d->m_searchResultPlacemarks.size(); i++ ) {
251 QQmlContext * context = new QQmlContext( qmlContext( d->m_waypointDelegate ) );
252 QObject * component = d->m_waypointDelegate->create(context);
253 QQuickItem* item = qobject_cast<QQuickItem*>( component );
254 if ( item ) {
255 item->setParentItem( this );
256 item->setProperty("index", i);
257 item->setProperty("type", QVariant(QStringLiteral("searchResult")));
258 item->setProperty("placemark", QVariant::fromValue(d->m_searchResultPlacemarks[i]));
259 d->m_searchResultItems[i] = item;
260 } else {
261 delete component;
262 }
263 }
264
265 for (int i = d->m_searchResultItems.keys().size()-1; i >= d->m_searchResultPlacemarks.size(); i--) {
266 QQuickItem* item = d->m_searchResultItems[i];
267 item->setProperty("visible", QVariant(false) );
268 d->m_searchResultItems.erase(d->m_searchResultItems.find(i));
269 item->deleteLater();
270 }
271
272 for (int i = 0; i < d->m_searchResultItems.keys().size() && i < d->m_searchResultPlacemarks.size(); i++) {
273 qreal x = 0;
274 qreal y = 0;
275 const qreal lon = d->m_searchResultPlacemarks[i]->placemark().coordinate().longitude();
276 const qreal lat = d->m_searchResultPlacemarks[i]->placemark().coordinate().latitude();
277 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon, lat, x, y);
278
279 QQuickItem * item = d->m_searchResultItems[i];
280 if ( item ) {
281 item->setVisible( visible );
282 if ( visible ) {
283 item->setProperty("xPos", QVariant(x));
284 item->setProperty("yPos", QVariant(y));
285 }
286 }
287 }
288}
289
290void Routing::setMarbleMap( MarbleMap* marbleMap )
291{
292 d->m_marbleMap = marbleMap;
293
294 if ( d->m_marbleMap ) {
295 connect(d->m_marbleMap, SIGNAL(repaintNeeded(QRegion)), this, SLOT(update()));
296 RoutingManager* routingManager = d->m_marbleMap->model()->routingManager();
297 if (routingManager->profilesModel()->rowCount() == 0) {
298 routingManager->profilesModel()->loadDefaultProfiles();
299 routingManager->readSettings();
300 }
301
302 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)), this, SLOT(update()));
303 connect( routingManager, SIGNAL(routeRetrieved(GeoDataDocument*)), this, SLOT(update()));
304 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)),
305 this, SIGNAL(hasRouteChanged()) );
306 connect( routingModel(), SIGNAL(currentRouteChanged()),
307 this, SIGNAL(hasRouteChanged()) );
308 connect( routingManager, SIGNAL(stateChanged(RoutingManager::State)),
309 this, SIGNAL(hasWaypointsChanged()) );
310 connect( routingModel(), SIGNAL(currentRouteChanged()),
311 this, SIGNAL(hasWaypointsChanged()) );
312 connect( routingModel(), SIGNAL(currentRouteChanged()),
313 this, SLOT(update()) );
314 connect( d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
315 this, SLOT(updateWaypointItems()) );
316 connect( d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)),
317 this, SLOT(updateSearchResultPlacemarks()) );
318
319 emit routingModelChanged();
320
321 QList<Marble::RoutingProfile> profiles = routingManager->profilesModel()->profiles();
322 if ( profiles.size() == 4 ) {
323 /** @todo FIXME: Restrictive assumptions on available plugins and certain profile loading implementation */
324 d->m_profiles[QStringLiteral("Motorcar")] = profiles.at( 0 );
325 d->m_profiles[QStringLiteral("Bicycle")] = profiles.at( 2 );
326 d->m_profiles[QStringLiteral("Pedestrian")] = profiles.at( 3 );
327 } else {
328 qDebug() << "Unexpected size of default routing profiles: " << profiles.size();
329 }
330 }
331
332 emit marbleMapChanged();
333 emit routingProfileChanged();
334 emit hasRouteChanged();
335 emit hasWaypointsChanged();
336}
337
338MarbleMap *Routing::marbleMap()
339{
340 return d->m_marbleMap;
341}
342
343QString Routing::routingProfile() const
344{
345 return d->m_routingProfile;
346}
347
348void Routing::setRoutingProfile( const QString & profile )
349{
350 if ( d->m_routingProfile != profile ) {
351 d->m_routingProfile = profile;
352 if ( d->m_marbleMap ) {
353 d->m_marbleMap->model()->routingManager()->routeRequest()->setRoutingProfile( d->m_profiles[profile] );
354 }
355 emit routingProfileChanged();
356 }
357}
358
359bool Routing::hasRoute() const
360{
361 return d->m_marbleMap && !d->m_marbleMap->model()->routingManager()->routingModel()->route().path().isEmpty();
362}
363
364bool Routing::hasWaypoints() const
365{
366 return d->m_marbleMap && d->m_marbleMap->model()->routingManager()->routingModel()->rowCount() > 0;
367}
368
369RoutingModel *Routing::routingModel()
370{
371 return d->m_marbleMap == nullptr ? nullptr : d->m_marbleMap->model()->routingManager()->routingModel();
372}
373
374QQmlComponent *Routing::waypointDelegate() const
375{
376 return d->m_waypointDelegate;
377}
378
379int Routing::waypointCount() const
380{
381 return d->m_routeRequestModel ? d->m_routeRequestModel->rowCount() : 0;
382}
383
384RouteRequestModel *Routing::routeRequestModel()
385{
386 return d->m_routeRequestModel;
387}
388
389void Routing::addVia( qreal lon, qreal lat )
390{
391 if ( d->m_marbleMap ) {
392 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest();
393 request->addVia( Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) );
394 updateRoute();
395 }
396}
397
398void Routing::addViaAtIndex(int index, qreal lon, qreal lat)
399{
400 if ( d->m_marbleMap ) {
401 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest();
402 request->insert(index, Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree) );
403 updateRoute();
404 }
405}
406
407void Routing::addViaByPlacemark(Placemark *placemark)
408{
409 if (d->m_marbleMap && placemark) {
410 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest();
411 request->addVia(placemark->placemark());
412 updateRoute();
413 }
414}
415
416void Routing::addViaByPlacemarkAtIndex(int index, Placemark *placemark)
417{
418 if (d->m_marbleMap && placemark) {
419 Marble::RouteRequest * request = d->m_marbleMap->model()->routingManager()->routeRequest();
420 request->insert(index, placemark->placemark());
421 updateRoute();
422 }
423}
424
425void Routing::setVia( int index, qreal lon, qreal lat )
426{
427 if ( index < 0 || index > 200 || !d->m_marbleMap ) {
428 return;
429 }
430
431 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest();
432 Q_ASSERT( request );
433 if ( index < request->size() ) {
434 request->setPosition( index, Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) );
435 } else {
436 for ( int i=request->size(); i<index; ++i ) {
437 request->append( Marble::GeoDataCoordinates( 0.0, 0.0 ) );
438 }
439 request->append( Marble::GeoDataCoordinates( lon, lat, 0.0, Marble::GeoDataCoordinates::Degree ) );
440 }
441
442 updateRoute();
443}
444
445void Routing::removeVia( int index )
446{
447 if ( index < 0 || !d->m_marbleMap ) {
448 return;
449 }
450
451 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest();
452 if ( index < request->size() ) {
453 d->m_marbleMap->model()->routingManager()->routeRequest()->remove( index );
454 }
455
456 updateRoute();
457}
458
459void Routing::swapVias(int index1, int index2)
460{
461 if ( !d->m_marbleMap || !d->m_routeRequestModel ) {
462 return;
463 }
464
465 Marble::RouteRequest* request = d->m_marbleMap->model()->routingManager()->routeRequest();
466 request->swap(index1, index2);
467 updateRoute();
468 updateWaypointItems();
469}
470
471void Routing::reverseRoute()
472{
473 if ( d->m_marbleMap ) {
474 d->m_marbleMap->model()->routingManager()->reverseRoute();
475 }
476}
477
478void Routing::clearRoute()
479{
480 if ( d->m_marbleMap ) {
481 d->m_marbleMap->model()->routingManager()->clearRoute();
482 }
483}
484
485void Routing::updateRoute()
486{
487 if ( d->m_marbleMap ) {
488 d->m_marbleMap->model()->routingManager()->retrieveRoute();
489 }
490}
491
492void Routing::openRoute( const QString &fileName )
493{
494 if ( d->m_marbleMap ) {
495 Marble::RoutingManager * const routingManager = d->m_marbleMap->model()->routingManager();
496 /** @todo FIXME: replace the file:// prefix on QML side */
497 routingManager->clearRoute();
498 QString target = fileName.startsWith( QLatin1String( "file://" ) ) ? fileName.mid( 7 ) : fileName;
499 routingManager->loadRoute( target );
500 const Marble::GeoDataDocument *route = routingManager->alternativeRoutesModel()->currentRoute();
501 if ( route ) {
502 const Marble::GeoDataLineString* waypoints = Marble::AlternativeRoutesModel::waypoints( route );
503 if ( waypoints ) {
504 GeoDataCoordinates const center = waypoints->latLonAltBox().center();
505 GeoDataCoordinates::Unit const inDegree = GeoDataCoordinates::Degree;
506 d->m_marbleMap->centerOn( center.longitude(inDegree), center.latitude(inDegree) );
507 }
508 }
509 }
510}
511
512void Routing::saveRoute( const QString &fileName )
513{
514 if ( d->m_marbleMap ) {
515 /** @todo FIXME: replace the file:// prefix on QML side */
516 QString target = fileName.startsWith( QLatin1String( "file://" ) ) ? fileName.mid( 7 ) : fileName;
517 d->m_marbleMap->model()->routingManager()->saveRoute( target );
518 }
519}
520
521}
522
523#include "moc_Routing.cpp"
This file contains the headers for MarbleMap.
This file contains the headers for MarbleModel.
This file contains the headers for ViewportParams.
A 3d point representation.
A container for Features, Styles and in the future Schemas.
GeoDataCoordinates center() const override
returns the center of this box
A LineString that allows to store a contiguous set of line segments.
const GeoDataLatLonAltBox & latLonAltBox() const override
Returns the smallest latLonAltBox that contains the LineString.
A painter that allows to draw geometric primitives on the map.
Definition GeoPainter.h:89
Points to be included in a route.
int size() const
Number of points in the route.
void swap(int index1, int index2)
Swaps the given elements at the given positions.
void setPosition(int index, const GeoDataCoordinates &position, const QString &name=QString())
Change the value of the element at the given position.
void insert(int index, const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element at the given position.
void addVia(const GeoDataCoordinates &position)
Insert a via point.
void append(const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element to the end.
Delegates data retrieval and model updates to the appropriate routing provider.
void clearRoute()
Clear all via points.
void loadRoute(const QString &filename)
Opens the given filename (kml format) and loads the route contained in it.
AlternativeRoutesModel * alternativeRoutesModel()
Provides access to the model which contains a list of alternative routes.
void update(Part *part, const QByteArray &data, qint64 dataSize)
Binds a QML item to a specific geodetic location in screen coordinates.
QColor darker(int factor) const const
const_reference at(qsizetype i) const const
bool isEmpty() const const
void reserve(qsizetype size)
qsizetype size() const const
void deleteLater()
bool setProperty(const char *name, QVariant &&value)
void setParentItem(QQuickItem *parent)
void setVisible(bool)
void setGeometry(QSGGeometry *geometry)
void setColor(const QColor &color)
void allocate(int vertexCount, int indexCount)
const AttributeSet & defaultAttributes_Point2D()
void setDrawingMode(unsigned int mode)
Point2D * vertexDataAsPoint2D()
void setMaterial(QSGMaterial *material)
void appendChildNode(QSGNode *node)
int childCount() const const
void setFlag(Flag f, bool enabled)
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QTextStream & center(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QVariant fromValue(T &&value)
QVector2D normalized() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Sep 6 2024 11:58:09 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.