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 "routing/AlternativeRoutesModel.h"
9#include "routing/RouteRequest.h"
10#include "routing/RoutingManager.h"
11#include "routing/RoutingProfilesModel.h"
12#include <GeoDataLatLonAltBox.h>
13#include <GeoPainter.h>
14#include <MarbleMap.h>
15#include <MarbleModel.h>
16#include <PositionTracking.h>
17#include <ViewportParams.h>
18#include <declarative/RouteRequestModel.h>
19#include <routing/Route.h>
20
21#include <QDebug>
22#include <QOpenGLPaintDevice>
23#include <QQmlContext>
24#include <QSGFlatColorMaterial>
25#include <QSGGeometryNode>
26
27namespace Marble
28{
29
30class RoutingPrivate
31{
32public:
33 explicit RoutingPrivate(QObject *parent = nullptr);
34
35 MarbleMap *m_marbleMap;
37 Routing::RoutingProfile m_routingProfile;
38 QQmlComponent *m_waypointDelegate;
39 QMap<int, QQuickItem *> m_waypointItems;
40 RouteRequestModel *m_routeRequestModel;
41 QObject *m_parent;
42 QList<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)
57 , d(new RoutingPrivate(this))
58{
59 setFlag(ItemHasContents, true);
60 d->m_routeRequestModel->setRouting(this);
61 connect(d->m_routeRequestModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(updateWaypointItems()));
62 connect(d->m_routeRequestModel, SIGNAL(rowsMoved(QModelIndex, int, int, QModelIndex, int)), this, SLOT(updateWaypointItems()));
63 connect(d->m_routeRequestModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(updateWaypointItems()));
64}
65
66Routing::~Routing()
67{
68 delete d;
69}
70
71QSGNode *Routing::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
72{
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 =
91 routingManager->state() == RoutingManager::Downloading ? routingManager->routeColorStandard() : routingManager->routeColorStandard().darker(200);
92
93 QList<QPolygonF *> polygons;
94 geoPainter.polygonsFromLineString(waypoints, polygons);
95
96 if (!polygons.isEmpty()) {
97 delete oldNode;
98 oldNode = new QSGNode;
99 for (const QPolygonF *itPolygon : std::as_const(polygons)) {
100 QPolygonF const &polygon = *itPolygon;
101 QList<QVector2D> normals;
102 int segmentCount = itPolygon->size() - 1;
103 normals.reserve(segmentCount);
104 for (int i = 0; i < segmentCount; ++i) {
105 normals << QVector2D(polygon[i + 1] - polygon[i]).normalized();
106 }
107 auto lineNode = new QSGGeometryNode;
108
109 auto lineNodeGeo = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), segmentCount * 4);
110 lineNodeGeo->setDrawingMode(GL_TRIANGLE_STRIP);
111 lineNodeGeo->allocate(segmentCount * 4);
112
113 auto material = new QSGFlatColorMaterial;
114 material->setColor(standardRouteColor);
115
116 lineNode->setGeometry(lineNodeGeo);
117 lineNode->setFlag(QSGNode::OwnsGeometry);
118 lineNode->setMaterial(material);
119 lineNode->setFlag(QSGNode::OwnsMaterial);
120
121 auto points = lineNodeGeo->vertexDataAsPoint2D();
122 int k = -1;
123 for (int i = 0; i < segmentCount; ++i) {
124 auto const &a = polygon[i];
125 auto const &b = polygon[i + 1];
126 auto const &n = normals[i];
127 points[++k].set(a.x() - halfWidth * n.y(), a.y() + halfWidth * n.x());
128 points[++k].set(a.x() + halfWidth * n.y(), a.y() - halfWidth * n.x());
129 points[++k].set(b.x() - halfWidth * n.y(), b.y() + halfWidth * n.x());
130 points[++k].set(b.x() + halfWidth * n.y(), b.y() - halfWidth * n.x());
131 }
132
133 oldNode->appendChildNode(lineNode);
134 }
135 } else {
136 if (oldNode && oldNode->childCount() > 0) {
137 delete oldNode;
138 oldNode = new QSGNode;
139 }
140 }
141
142 qDeleteAll(polygons);
143 return oldNode;
144}
145
146QObject *Routing::waypointModel()
147{
148 return d->m_marbleMap ? d->m_marbleMap->model()->routingManager()->routingModel() : nullptr;
149}
150
151void Routing::setWaypointDelegate(QQmlComponent *waypointDelegate)
152{
153 if (d->m_waypointDelegate == waypointDelegate) {
154 return;
155 }
156
157 d->m_waypointDelegate = waypointDelegate;
158 Q_EMIT waypointDelegateChanged(waypointDelegate);
159}
160
161void Routing::updateWaypointItems()
162{
163 if (d->m_marbleMap && d->m_routeRequestModel) {
164 for (int i = d->m_waypointItems.keys().size(); i < d->m_routeRequestModel->rowCount(); i++) {
165 auto context = new QQmlContext(qmlContext(d->m_waypointDelegate));
166 QObject *component = d->m_waypointDelegate->create(context);
167 QQuickItem *item = qobject_cast<QQuickItem *>(component);
168 if (item) {
169 item->setParentItem(this);
170 item->setProperty("index", i);
171 d->m_waypointItems[i] = item;
172 } else {
173 delete component;
174 }
175 }
176
177 for (int i = d->m_waypointItems.keys().size() - 1; i >= d->m_routeRequestModel->rowCount(); i--) {
178 QQuickItem *item = d->m_waypointItems[i];
179 item->setProperty("visible", QVariant(false));
180 d->m_waypointItems.erase(d->m_waypointItems.find(i));
181 item->deleteLater();
182 }
183
184 QMap<int, QQuickItem *>::iterator iter = d->m_waypointItems.begin();
185 while (iter != d->m_waypointItems.end()) {
186 qreal x = 0;
187 qreal y = 0;
188 const qreal lon = d->m_routeRequestModel->data(d->m_routeRequestModel->index(iter.key()), RouteRequestModel::LongitudeRole).toFloat();
189 const qreal lat = d->m_routeRequestModel->data(d->m_routeRequestModel->index(iter.key()), RouteRequestModel::LatitudeRole).toFloat();
190 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon * DEG2RAD, lat * DEG2RAD, x, y);
191
192 QQuickItem *item = iter.value();
193 if (item) {
194 item->setVisible(visible);
195 if (visible) {
196 item->setProperty("xPos", QVariant(x));
197 item->setProperty("yPos", QVariant(y));
198 if (iter.key() == 0 && waypointCount() == 1) {
199 item->setProperty("type", QVariant(QStringLiteral("departure")));
200 } else if (iter.key() == d->m_waypointItems.keys().size() - 1) {
201 item->setProperty("type", QVariant(QStringLiteral("destination")));
202 } else if (iter.key() > 0) {
203 item->setProperty("type", QVariant(QStringLiteral("waypoint")));
204 } else {
205 item->setProperty("type", QVariant(QStringLiteral("departure")));
206 }
207 }
208 }
209 ++iter;
210 }
211 }
212}
213
214int Routing::addSearchResultPlacemark(Placemark *placemark)
215{
216 if (d->m_marbleMap) {
217 for (int i = 0; i < d->m_searchResultItems.size(); i++) {
218 if (d->m_searchResultPlacemarks[i]->placemark().coordinate() == placemark->placemark().coordinate()) {
219 return i;
220 }
221 }
222 auto newPlacemark = new Placemark(this);
223 newPlacemark->setGeoDataPlacemark(placemark->placemark());
224 d->m_searchResultPlacemarks.push_back(newPlacemark);
225 }
226
227 updateSearchResultPlacemarks();
228 return d->m_searchResultPlacemarks.size() - 1;
229}
230
231void Routing::clearSearchResultPlacemarks()
232{
233 for (Placemark *placemark : std::as_const(d->m_searchResultPlacemarks)) {
234 placemark->deleteLater();
235 }
236 d->m_searchResultPlacemarks.clear();
237
238 for (QQuickItem *item : std::as_const(d->m_searchResultItems)) {
239 item->deleteLater();
240 }
241 d->m_searchResultItems.clear();
242}
243
244void Routing::updateSearchResultPlacemarks()
245{
246 for (int i = d->m_searchResultItems.keys().size(); i < d->m_searchResultPlacemarks.size(); i++) {
247 auto context = new QQmlContext(qmlContext(d->m_waypointDelegate));
248 QObject *component = d->m_waypointDelegate->create(context);
249 QQuickItem *item = qobject_cast<QQuickItem *>(component);
250 if (item) {
251 item->setParentItem(this);
252 item->setProperty("index", i);
253 item->setProperty("type", QVariant(QStringLiteral("searchResult")));
254 item->setProperty("placemark", QVariant::fromValue(d->m_searchResultPlacemarks[i]));
255 d->m_searchResultItems[i] = item;
256 } else {
257 delete component;
258 }
259 }
260
261 for (int i = d->m_searchResultItems.keys().size() - 1; i >= d->m_searchResultPlacemarks.size(); i--) {
262 QQuickItem *item = d->m_searchResultItems[i];
263 item->setProperty("visible", QVariant(false));
264 d->m_searchResultItems.erase(d->m_searchResultItems.find(i));
265 item->deleteLater();
266 }
267
268 for (int i = 0; i < d->m_searchResultItems.keys().size() && i < d->m_searchResultPlacemarks.size(); i++) {
269 qreal x = 0;
270 qreal y = 0;
271 const qreal lon = d->m_searchResultPlacemarks[i]->placemark().coordinate().longitude();
272 const qreal lat = d->m_searchResultPlacemarks[i]->placemark().coordinate().latitude();
273 const bool visible = d->m_marbleMap->viewport()->screenCoordinates(lon, lat, x, y);
274
275 QQuickItem *item = d->m_searchResultItems[i];
276 if (item) {
277 item->setVisible(visible);
278 if (visible) {
279 item->setProperty("xPos", QVariant(x));
280 item->setProperty("yPos", QVariant(y));
281 }
282 }
283 }
284}
285
286void Routing::setMarbleMap(MarbleMap *marbleMap)
287{
288 d->m_marbleMap = marbleMap;
289
290 if (d->m_marbleMap) {
291 connect(d->m_marbleMap, SIGNAL(repaintNeeded(QRegion)), this, SLOT(update()));
292 RoutingManager *routingManager = d->m_marbleMap->model()->routingManager();
293 if (routingManager->profilesModel()->rowCount() == 0) {
294 routingManager->profilesModel()->loadDefaultProfiles();
295 routingManager->readSettings();
296 }
297
298 connect(routingManager, SIGNAL(stateChanged(RoutingManager::State)), this, SLOT(update()));
299 connect(routingManager, SIGNAL(routeRetrieved(GeoDataDocument *)), this, SLOT(update()));
300 connect(routingManager, SIGNAL(stateChanged(RoutingManager::State)), this, SIGNAL(hasRouteChanged()));
301 connect(routingModel(), SIGNAL(currentRouteChanged()), this, SIGNAL(hasRouteChanged()));
302 connect(routingManager, SIGNAL(stateChanged(RoutingManager::State)), this, SIGNAL(hasWaypointsChanged()));
303 connect(routingModel(), SIGNAL(currentRouteChanged()), this, SIGNAL(hasWaypointsChanged()));
304 connect(routingModel(), SIGNAL(currentRouteChanged()), this, SLOT(update()));
305 connect(d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), this, SLOT(updateWaypointItems()));
306 connect(d->m_marbleMap, SIGNAL(visibleLatLonAltBoxChanged(GeoDataLatLonAltBox)), this, SLOT(updateSearchResultPlacemarks()));
307
308 Q_EMIT routingModelChanged();
309
310 QList<Marble::RoutingProfile> profiles = routingManager->profilesModel()->profiles();
311 if (profiles.size() == 4) {
312 /** @todo FIXME: Restrictive assumptions on available plugins and certain profile loading implementation */
313 d->m_profiles[Motorcar] = profiles.at(0);
314 d->m_profiles[Bicycle] = profiles.at(2);
315 d->m_profiles[Pedestrian] = profiles.at(3);
316 } else {
317 qDebug() << "Unexpected size of default routing profiles: " << profiles.size();
318 }
319 }
320
321 Q_EMIT marbleMapChanged();
322 Q_EMIT routingProfileChanged();
323 Q_EMIT hasRouteChanged();
324 Q_EMIT hasWaypointsChanged();
325}
326
327MarbleMap *Routing::marbleMap()
328{
329 return d->m_marbleMap;
330}
331
332Routing::RoutingProfile Routing::routingProfile() const
333{
334 return d->m_routingProfile;
335}
336
337void Routing::setRoutingProfile(Routing::RoutingProfile profile)
338{
339 if (d->m_routingProfile != profile) {
340 d->m_routingProfile = profile;
341 if (d->m_marbleMap) {
342 d->m_marbleMap->model()->routingManager()->routeRequest()->setRoutingProfile(d->m_profiles[profile]);
343 }
344 Q_EMIT routingProfileChanged();
345 }
346}
347
348bool Routing::hasRoute() const
349{
350 if (d->m_marbleMap)
351 qWarning() << d->m_marbleMap << !d->m_marbleMap->model()->routingManager()->routingModel()->route().path().isEmpty();
352 return d->m_marbleMap && !d->m_marbleMap->model()->routingManager()->routingModel()->route().path().isEmpty();
353}
354
355bool Routing::hasWaypoints() const
356{
357 return d->m_marbleMap && d->m_marbleMap->model()->routingManager()->routingModel()->rowCount() > 0;
358}
359
360RoutingModel *Routing::routingModel()
361{
362 return d->m_marbleMap == nullptr ? nullptr : d->m_marbleMap->model()->routingManager()->routingModel();
363}
364
365QQmlComponent *Routing::waypointDelegate() const
366{
367 return d->m_waypointDelegate;
368}
369
370int Routing::waypointCount() const
371{
372 return d->m_routeRequestModel ? d->m_routeRequestModel->rowCount() : 0;
373}
374
375RouteRequestModel *Routing::routeRequestModel() const
376{
377 return d->m_routeRequestModel;
378}
379
380AlternativeRoutesModel *Routing::alternativeRoutesModel() const
381{
382 if (d->m_marbleMap) {
383 Marble::RoutingManager *const routingManager = d->m_marbleMap->model()->routingManager();
384 return routingManager->alternativeRoutesModel();
385 }
386 return nullptr;
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(QLatin1StringView("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(QLatin1StringView("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:86
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 setColor(const QColor &color)
const AttributeSet & defaultAttributes_Point2D()
void appendChildNode(QSGNode *node)
int childCount() 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 Nov 8 2024 12:02:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.