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.
void remove(int index)
Remove the element at the given position.
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)
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 May 17 2024 11:49:23 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.