Marble

Routing.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2011 Dennis Nienhüser <[email protected]>
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 
28 namespace Marble {
29 
30 class RoutingPrivate
31 {
32 public:
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 
46 RoutingPrivate::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 
55 Routing::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 
67 Routing::~Routing()
68 {
69  delete d;
70 }
71 
72 QSGNode * 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);
118  lineNode->setFlag(QSGNode::OwnsGeometry);
119  lineNode->setMaterial(material);
120  lineNode->setFlag(QSGNode::OwnsMaterial);
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 
147 QObject* Routing::waypointModel()
148 {
149  return d->m_marbleMap ? d->m_marbleMap->model()->routingManager()->routingModel() : nullptr;
150 }
151 
152 void 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 
162 void 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 
218 int 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 
235 void 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 
248 void 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 
290 void 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 
338 MarbleMap *Routing::marbleMap()
339 {
340  return d->m_marbleMap;
341 }
342 
343 QString Routing::routingProfile() const
344 {
345  return d->m_routingProfile;
346 }
347 
348 void 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 
359 bool Routing::hasRoute() const
360 {
361  return d->m_marbleMap && !d->m_marbleMap->model()->routingManager()->routingModel()->route().path().isEmpty();
362 }
363 
364 bool Routing::hasWaypoints() const
365 {
366  return d->m_marbleMap && d->m_marbleMap->model()->routingManager()->routingModel()->rowCount() > 0;
367 }
368 
369 RoutingModel *Routing::routingModel()
370 {
371  return d->m_marbleMap == nullptr ? nullptr : d->m_marbleMap->model()->routingManager()->routingModel();
372 }
373 
374 QQmlComponent *Routing::waypointDelegate() const
375 {
376  return d->m_waypointDelegate;
377 }
378 
379 int Routing::waypointCount() const
380 {
381  return d->m_routeRequestModel ? d->m_routeRequestModel->rowCount() : 0;
382 }
383 
384 RouteRequestModel *Routing::routeRequestModel()
385 {
386  return d->m_routeRequestModel;
387 }
388 
389 void 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 
398 void 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 
407 void 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 
416 void 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 
425 void 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 
445 void 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 
459 void 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 
471 void Routing::reverseRoute()
472 {
473  if ( d->m_marbleMap ) {
474  d->m_marbleMap->model()->routingManager()->reverseRoute();
475  }
476 }
477 
478 void Routing::clearRoute()
479 {
480  if ( d->m_marbleMap ) {
481  d->m_marbleMap->model()->routingManager()->clearRoute();
482  }
483 }
484 
485 void Routing::updateRoute()
486 {
487  if ( d->m_marbleMap ) {
488  d->m_marbleMap->model()->routingManager()->retrieveRoute();
489  }
490 }
491 
492 void 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 
512 void 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"
A 3d point representation.
bool isEmpty() const const
void setDrawingMode(unsigned int mode)
Delegates data retrieval and model updates to the appropriate routing provider.
QVariant fromValue(const T &value)
const T value(const Key &key, const T &defaultValue) const const
QMap::iterator begin()
void appendChildNode(QSGNode *node)
int childCount() const const
void setGeometry(QSGGeometry *geometry)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const QSGGeometry::AttributeSet & defaultAttributes_Point2D()
int size() const const
void deleteLater()
void setParentItem(QQuickItem *parent)
AlternativeRoutesModel * alternativeRoutesModel()
Provides access to the model which contains a list of alternative routes.
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.
void setPosition(int index, const GeoDataCoordinates &position, const QString &name=QString())
Change the value of the element at the given position.
const T & at(int i) const const
A container for Features, Styles and in the future Schemas.
void remove(int index)
Remove the element at the given position.
Binds a QML item to a specific geodetic location in screen coordinates.
void loadRoute(const QString &filename)
Opens the given filename (kml format) and loads the route contained in it.
void swap(int index1, int index2)
Swaps the given elements at the given positions.
A painter that allows to draw geometric primitives on the map.
Definition: GeoPainter.h:88
void reserve(int size)
GeoDataCoordinates center() const override
returns the center of this box
void setMaterial(QSGMaterial *material)
const Key key(const T &value, const Key &defaultKey) const const
QColor darker(int factor) const const
void append(const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element to the end.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
void setColor(const QColor &color)
bool setProperty(const char *name, const QVariant &value)
void addVia(const GeoDataCoordinates &position)
Insert a via point.
Unit
enum used constructor to specify the units used
void update(Part *part, const QByteArray &data, qint64 dataSize)
QTextStream & center(QTextStream &stream)
int size() const const
void allocate(int vertexCount, int indexCount)
void setVisible(bool)
Points to be included in a route.
Definition: RouteRequest.h:26
QVector2D normalized() const const
QSGGeometry::Point2D * vertexDataAsPoint2D()
QString mid(int position, int n) const const
void insert(int index, const GeoDataCoordinates &coordinates, const QString &name=QString())
Add the given element at the given position.
void clearRoute()
Clear all via points.
int size() const
Number of points in the route.
void setFlag(QSGNode::Flag f, bool enabled)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Sep 21 2023 04:12:28 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.