Marble

GeometryLayer.cpp
1 // SPDX-License-Identifier: LGPL-2.1-or-later
2 //
3 // SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <[email protected]>
4 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <[email protected]>
5 // SPDX-FileCopyrightText: 2011-2012 Bernhard Beschow <[email protected]>
6 // SPDX-FileCopyrightText: 2014 Gábor Péterffy <[email protected]>
7 //
8 
9 #include "GeometryLayer.h"
10 
11 // Marble
12 #include "GeoDataLatLonAltBox.h"
13 #include "GeoDataDocument.h"
14 #include "GeoDataFolder.h"
15 #include "GeoDataLineStyle.h"
16 #include "GeoDataMultiTrack.h"
17 #include "GeoDataObject.h"
18 #include "GeoDataPlacemark.h"
19 #include "GeoDataLinearRing.h"
20 #include "GeoDataMultiGeometry.h"
21 #include "GeoDataPolygon.h"
22 #include "GeoDataBuilding.h"
23 #include "GeoDataPolyStyle.h"
24 #include "GeoDataStyle.h"
25 #include "GeoDataIconStyle.h"
26 #include "GeoDataStyleMap.h"
27 #include "GeoDataTrack.h"
28 #include "GeoDataFeature.h"
29 #include "MarbleDebug.h"
30 #include "GeoPainter.h"
31 #include "ViewportParams.h"
32 #include "RenderState.h"
33 #include "GeoGraphicsScene.h"
34 #include "GeoGraphicsItem.h"
35 #include "GeoLineStringGraphicsItem.h"
36 #include "GeoPolygonGraphicsItem.h"
37 #include "GeoTrackGraphicsItem.h"
38 #include "GeoDataPhotoOverlay.h"
39 #include "GeoDataScreenOverlay.h"
40 #include "GeoPhotoGraphicsItem.h"
41 #include "ScreenOverlayGraphicsItem.h"
42 #include "TileId.h"
43 #include "MarbleGraphicsItem.h"
44 #include "MarblePlacemarkModel.h"
45 #include "GeoDataTreeModel.h"
46 #include <OsmPlacemarkData.h>
47 #include "StyleBuilder.h"
48 #include "AbstractGeoPolygonGraphicsItem.h"
49 
50 // Qt
51 #include <qmath.h>
52 #include <QAbstractItemModel>
53 #include <QModelIndex>
54 
55 namespace Marble
56 {
57 class GeometryLayerPrivate
58 {
59 public:
60  using OsmLineStringItems = QVector<GeoLineStringGraphicsItem *>;
61  using Relations = QSet<const GeoDataRelation *>;
62  typedef QHash<const GeoDataFeature *, Relations> FeatureRelationHash;
63  using GeoGraphicItems = QVector<GeoGraphicsItem *>;
64 
65  struct PaintFragments {
66  // Three lists for different z values
67  // A z value of 0 is default and used by the majority of items, so sorting
68  // can be avoided for it
69  QVector<GeoGraphicsItem*> negative; // subways
70  QVector<GeoGraphicsItem*> null; // areas and roads
71  QVector<GeoGraphicsItem*> positive; // buildings
72  };
73 
74  explicit GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder);
75 
76  void createGraphicsItems(const GeoDataObject *object);
77  void createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations);
78  void createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark, const Relations &relations);
79  void createGraphicsItemFromOverlay(const GeoDataOverlay *overlay);
80  void removeGraphicsItems(const GeoDataFeature *feature);
81  void updateTiledLineStrings(const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem);
82  static void updateTiledLineStrings(OsmLineStringItems &lineStringItems);
83  void clearCache();
84  bool showRelation(const GeoDataRelation* relation) const;
85  void updateRelationVisibility();
86 
87  const QAbstractItemModel *const m_model;
88  const StyleBuilder *const m_styleBuilder;
89  GeoGraphicsScene m_scene;
90  QString m_runtimeTrace;
91  QList<ScreenOverlayGraphicsItem*> m_screenOverlays;
92 
93  QHash<qint64, OsmLineStringItems> m_osmLineStringItems;
94  int m_tileLevel;
95  GeoGraphicsItem* m_lastFeatureAt;
96 
97  bool m_dirty;
98  int m_cachedItemCount;
99  QHash<QString, GeoGraphicItems> m_cachedPaintFragments;
100  typedef QPair<QString, GeoGraphicsItem*> LayerItem;
101  QList<LayerItem> m_cachedDefaultLayer;
102  QDateTime m_cachedDateTime;
103  GeoDataLatLonBox m_cachedLatLonBox;
104  QSet<qint64> m_highlightedRouteRelations;
105  GeoDataRelation::RelationTypes m_visibleRelationTypes;
106  bool m_levelTagDebugModeEnabled;
107  int m_debugLevelTag;
108 };
109 
110 GeometryLayerPrivate::GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) :
111  m_model(model),
112  m_styleBuilder(styleBuilder),
113  m_tileLevel(0),
114  m_lastFeatureAt(nullptr),
115  m_dirty(true),
116  m_cachedItemCount(0),
117  m_visibleRelationTypes(GeoDataRelation::RouteFerry),
118  m_levelTagDebugModeEnabled(false),
119  m_debugLevelTag(0)
120 {
121 }
122 
123 void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object)
124 {
125  FeatureRelationHash noRelations;
126  createGraphicsItems(object, noRelations);
127 }
128 
129 GeometryLayer::GeometryLayer(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) :
130  d(new GeometryLayerPrivate(model, styleBuilder))
131 {
132  const GeoDataObject *object = static_cast<GeoDataObject*>(d->m_model->index(0, 0, QModelIndex()).internalPointer());
133  if (object && object->parent()) {
134  d->createGraphicsItems(object->parent());
135  }
136 
137  connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
138  this, SLOT(resetCacheData()));
139  connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
140  this, SLOT(addPlacemarks(QModelIndex,int,int)));
141  connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
142  this, SLOT(removePlacemarks(QModelIndex,int,int)));
143  connect(model, SIGNAL(modelReset()),
144  this, SLOT(resetCacheData()));
145  connect(this, SIGNAL(highlightedPlacemarksChanged(QVector<GeoDataPlacemark*>)),
146  &d->m_scene, SLOT(applyHighlight(QVector<GeoDataPlacemark*>)));
147  connect(&d->m_scene, SIGNAL(repaintNeeded()),
148  this, SIGNAL(repaintNeeded()));
149 }
150 
151 GeometryLayer::~GeometryLayer()
152 {
153  delete d;
154 }
155 
156 QStringList GeometryLayer::renderPosition() const
157 {
158  return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
159 }
160 
161 
162 bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport,
163  const QString& renderPos, GeoSceneLayer * layer)
164 {
165  Q_UNUSED(renderPos)
166  Q_UNUSED(layer)
167 
168  painter->save();
169 
170  auto const & box = viewport->viewLatLonAltBox();
171  bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05);
172 
173  if (d->m_cachedLatLonBox.isEmpty() || !isEqual) {
174  d->m_dirty = true;
175  }
176 
177  // update the items cache at least every second since the last request
178  auto const now = QDateTime::currentDateTime();
179  if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) {
180  d->m_dirty = true;
181  }
182 
183  if (d->m_dirty) {
184  d->m_dirty = false;
185 
186  const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel());
187  auto const items = d->m_scene.items(box, maxZoomLevel);
188  d->m_cachedLatLonBox = box;
189  d->m_cachedDateTime = now;
190 
191  d->m_cachedItemCount = items.size();
192  d->m_cachedDefaultLayer.clear();
193  d->m_cachedPaintFragments.clear();
195  const QStringList &renderOrder = d->m_styleBuilder->renderOrder();
196  QSet<QString> const knownLayers(renderOrder.constBegin(), renderOrder.constEnd());
197  for (GeoGraphicsItem* item: items) {
198  QStringList paintLayers = item->paintLayers();
199  if (paintLayers.isEmpty()) {
200  mDebug() << item << " provides no paint layers, so I force one onto it.";
201  paintLayers << QString();
202  }
203  for (const auto &layer: paintLayers) {
204  if (knownLayers.contains(layer)) {
205  GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer];
206  double const zValue = item->zValue();
207  // assign subway stations
208  if (zValue == 0.0) {
209  fragments.null << item;
210  // assign areas and streets
211  } else if (zValue < 0.0) {
212  fragments.negative << item;
213  // assign buildings
214  } else {
215  fragments.positive << item;
216  }
217  } else {
218  // assign symbols
219  d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item);
220  static QSet<QString> missingLayers;
221  if (!missingLayers.contains(layer)) {
222  mDebug() << "Missing layer " << layer << ", in render order, will render it on top";
223  missingLayers << layer;
224  }
225  }
226  }
227  }
228  // Sort each fragment by z-level
229  for (const QString &layer: d->m_styleBuilder->renderOrder()) {
230  GeometryLayerPrivate::PaintFragments & layerItems = paintFragments[layer];
231  std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan);
232  // The idea here is that layerItems.null has most items and does not need to be sorted by z-value
233  // since they are all equal (=0). We do sort them by style pointer though for batch rendering
234  std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan);
235  std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan);
236  auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size();
237  d->m_cachedPaintFragments[layer].reserve(count);
238  d->m_cachedPaintFragments[layer] << layerItems.negative;
239  d->m_cachedPaintFragments[layer] << layerItems.null;
240  d->m_cachedPaintFragments[layer] << layerItems.positive;
241  }
242  }
243 
244  for (const QString &layer: d->m_styleBuilder->renderOrder()) {
245  auto & layerItems = d->m_cachedPaintFragments[layer];
246  AbstractGeoPolygonGraphicsItem::s_previousStyle = nullptr;
247  GeoLineStringGraphicsItem::s_previousStyle = nullptr;
248  for (auto item: layerItems) {
249  if (d->m_levelTagDebugModeEnabled) {
250  if (const auto placemark = geodata_cast<GeoDataPlacemark>(item->feature())) {
251  if (placemark->hasOsmData()) {
252  QHash<QString, QString>::const_iterator tagIter = placemark->osmData().findTag(QStringLiteral("level"));
253  if (tagIter != placemark->osmData().tagsEnd()) {
254  const int val = tagIter.value().toInt();
255  if (val != d->m_debugLevelTag) {
256  continue;
257  }
258  }
259  }
260  }
261  }
262  item->paint(painter, viewport, layer, d->m_tileLevel);
263  }
264  }
265 
266  for (const auto & item: d->m_cachedDefaultLayer) {
267  item.second->paint(painter, viewport, item.first, d->m_tileLevel);
268  }
269 
270  for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) {
271  item->paintEvent(painter, viewport);
272  }
273 
274  painter->restore();
275  d->m_runtimeTrace = QStringLiteral("Geometries: %1 Zoom: %2")
276  .arg(d->m_cachedItemCount)
277  .arg(d->m_tileLevel);
278  return true;
279 }
280 
281 RenderState GeometryLayer::renderState() const
282 {
283  return RenderState(QStringLiteral("GeoGraphicsScene"));
284 }
285 
286 QString GeometryLayer::runtimeTrace() const
287 {
288  return d->m_runtimeTrace;
289 }
290 
291 bool GeometryLayer::hasFeatureAt(const QPoint &curpos, const ViewportParams *viewport)
292 {
293  if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) {
294  return true;
295  }
296 
297  auto const renderOrder = d->m_styleBuilder->renderOrder();
298  for (int i = renderOrder.size() - 1; i >= 0; --i) {
299  auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
300  for (auto item : layerItems) {
301  if (item->contains(curpos, viewport)) {
302  d->m_lastFeatureAt = item;
303  return true;
304  }
305  }
306  }
307 
308  return false;
309 }
310 
311 void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations)
312 {
313  clearCache();
314  if (auto document = geodata_cast<GeoDataDocument>(object)) {
315  for (auto feature: document->featureList()) {
316  if (auto relation = geodata_cast<GeoDataRelation>(feature)) {
317  relation->setVisible(showRelation(relation));
318  for (auto member: relation->members()) {
319  relations[member] << relation;
320  }
321  }
322  }
323  }
324  if (auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
325  createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark));
326  } else if (const GeoDataOverlay* overlay = dynamic_cast<const GeoDataOverlay*>(object)) {
327  createGraphicsItemFromOverlay(overlay);
328  }
329 
330  // parse all child objects of the container
331  if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer*>(object)) {
332  int rowCount = container->size();
333  for (int row = 0; row < rowCount; ++row) {
334  createGraphicsItems(container->child(row), relations);
335  }
336  }
337 }
338 
339 void GeometryLayerPrivate::updateTiledLineStrings(const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem)
340 {
341  if (!placemark->hasOsmData()) {
342  return;
343  }
344  qint64 const osmId = placemark->osmData().oid();
345  if (osmId <= 0) {
346  return;
347  }
348  auto & lineStringItems = m_osmLineStringItems[osmId];
349  lineStringItems << lineStringItem;
350  updateTiledLineStrings(lineStringItems);
351 }
352 
353 void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems)
354 {
355  GeoDataLineString merged;
356  if (lineStringItems.size() > 1) {
358  for (auto item : lineStringItems) {
359  lineStrings << item->lineString();
360  }
361  merged = GeoLineStringGraphicsItem::merge(lineStrings);
362  }
363 
364  // If merging failed, reset all. Otherwise only the first one
365  // gets the merge result and becomes visible.
366  bool visible = true;
367  for (auto item : lineStringItems) {
368  item->setVisible(visible);
369  if (visible) {
370  item->setMergedLineString(merged);
371  visible = merged.isEmpty();
372  }
373  }
374 }
375 
376 void GeometryLayerPrivate::clearCache()
377 {
378  m_lastFeatureAt = nullptr;
379  m_dirty = true;
380  m_cachedDateTime = QDateTime();
381  m_cachedItemCount = 0;
382  m_cachedPaintFragments.clear();
383  m_cachedDefaultLayer.clear();
384  m_cachedLatLonBox = GeoDataLatLonBox();
385 }
386 
387 inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const
388 {
389  return (m_visibleRelationTypes.testFlag(relation->relationType())
390  || m_highlightedRouteRelations.contains(relation->osmData().oid()));
391 }
392 
393 void GeometryLayerPrivate::updateRelationVisibility()
394 {
395  for (int i = 0; i < m_model->rowCount(); ++i) {
396  QVariant const data = m_model->data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
397  GeoDataObject *object = qvariant_cast<GeoDataObject*> (data);
398  if (auto doc = geodata_cast<GeoDataDocument>(object)) {
399  for (auto feature: doc->featureList()) {
400  if (auto relation = geodata_cast<GeoDataRelation>(feature)) {
401  relation->setVisible(showRelation(relation));
402  }
403  }
404  }
405  }
406  m_scene.resetStyle();
407 }
408 
409 void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark, const Relations &relations)
410 {
411  if (!placemark->isGloballyVisible()) {
412  return; // Reconsider this when visibility can be changed dynamically
413  }
414 
415  GeoGraphicsItem *item = nullptr;
416  if (const auto line = geodata_cast<GeoDataLineString>(object)) {
417  auto lineStringItem = new GeoLineStringGraphicsItem(placemark, line);
418  item = lineStringItem;
419  updateTiledLineStrings(placemark, lineStringItem);
420  } else if (const auto ring = geodata_cast<GeoDataLinearRing>(object)) {
421  item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring);
422  } else if (const auto poly = geodata_cast<GeoDataPolygon>(object)) {
423  item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly);
424  if (item->zValue() == 0) {
425  item->setZValue(poly->renderOrder());
426  }
427  } else if (const auto building = geodata_cast<GeoDataBuilding>(object)) {
428  item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building);
429  } else if (const auto multigeo = geodata_cast<GeoDataMultiGeometry>(object)) {
430  int rowCount = multigeo->size();
431  for (int row = 0; row < rowCount; ++row) {
432  createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations);
433  }
434  } else if (const auto multitrack = geodata_cast<GeoDataMultiTrack>(object)) {
435  int rowCount = multitrack->size();
436  for (int row = 0; row < rowCount; ++row) {
437  createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations);
438  }
439  } else if (const auto track = geodata_cast<GeoDataTrack>(object)) {
440  item = new GeoTrackGraphicsItem(placemark, track);
441  }
442  if (!item) {
443  return;
444  }
445  item->setRelations(relations);
446  item->setStyleBuilder(m_styleBuilder);
447  item->setVisible(item->visible() && placemark->isGloballyVisible());
448  item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark));
449  m_scene.addItem(item);
450 }
451 
452 void GeometryLayerPrivate::createGraphicsItemFromOverlay(const GeoDataOverlay *overlay)
453 {
454  if (!overlay->isGloballyVisible()) {
455  return; // Reconsider this when visibility can be changed dynamically
456  }
457 
458  GeoGraphicsItem* item = nullptr;
459  if (const auto photoOverlay = geodata_cast<GeoDataPhotoOverlay>(overlay)) {
460  GeoPhotoGraphicsItem *photoItem = new GeoPhotoGraphicsItem(overlay);
461  photoItem->setPoint(photoOverlay->point());
462  item = photoItem;
463  } else if (const auto screenOverlay = geodata_cast<GeoDataScreenOverlay>(overlay)) {
464  ScreenOverlayGraphicsItem *screenItem = new ScreenOverlayGraphicsItem(screenOverlay);
465  m_screenOverlays.push_back(screenItem);
466  }
467 
468  if (item) {
469  item->setStyleBuilder(m_styleBuilder);
470  item->setVisible(overlay->isGloballyVisible());
471  m_scene.addItem(item);
472  }
473 }
474 
475 void GeometryLayerPrivate::removeGraphicsItems(const GeoDataFeature *feature)
476 {
477  clearCache();
478  if (const auto placemark = geodata_cast<GeoDataPlacemark>(feature)) {
479  if (placemark->isGloballyVisible() &&
480  geodata_cast<GeoDataLineString>(placemark->geometry()) &&
481  placemark->hasOsmData() &&
482  placemark->osmData().oid() > 0) {
483  auto & items = m_osmLineStringItems[placemark->osmData().oid()];
484  bool removed = false;
485  for (auto item : items) {
486  if (item->feature() == feature) {
487  items.removeOne(item);
488  removed = true;
489  break;
490  }
491  }
492  Q_ASSERT(removed);
493  updateTiledLineStrings(items);
494  }
495  m_scene.removeItem(feature);
496  } else if (const auto container = dynamic_cast<const GeoDataContainer*>(feature)) {
497  for (const GeoDataFeature *child: container->featureList()) {
498  removeGraphicsItems(child);
499  }
500  } else if (geodata_cast<GeoDataScreenOverlay>(feature)) {
501  for (ScreenOverlayGraphicsItem *item: m_screenOverlays) {
502  if (item->screenOverlay() == feature) {
503  m_screenOverlays.removeAll(item);
504  }
505  }
506  }
507 }
508 
509 void GeometryLayer::addPlacemarks(const QModelIndex& parent, int first, int last)
510 {
511  Q_ASSERT(first < d->m_model->rowCount(parent));
512  Q_ASSERT(last < d->m_model->rowCount(parent));
513  for (int i = first; i <= last; ++i) {
514  QModelIndex index = d->m_model->index(i, 0, parent);
515  Q_ASSERT(index.isValid());
516  const GeoDataObject *object = qvariant_cast<GeoDataObject*>(index.data(MarblePlacemarkModel::ObjectPointerRole));
517  Q_ASSERT(object);
518  d->createGraphicsItems(object);
519  }
520  emit repaintNeeded();
521 
522 }
523 
524 void GeometryLayer::removePlacemarks(const QModelIndex& parent, int first, int last)
525 {
526  Q_ASSERT(last < d->m_model->rowCount(parent));
527  bool isRepaintNeeded = false;
528  for (int i = first; i <= last; ++i) {
529  QModelIndex index = d->m_model->index(i, 0, parent);
530  Q_ASSERT(index.isValid());
531  const GeoDataObject *object = qvariant_cast<GeoDataObject*>(index.data(MarblePlacemarkModel::ObjectPointerRole));
532  const GeoDataFeature *feature = dynamic_cast<const GeoDataFeature*>(object);
533  if (feature != nullptr) {
534  d->removeGraphicsItems(feature);
535  isRepaintNeeded = true;
536  }
537  }
538  if (isRepaintNeeded) {
539  emit repaintNeeded();
540  }
541 
542 }
543 
544 void GeometryLayer::resetCacheData()
545 {
546  d->clearCache();
547  d->m_scene.clear();
548  qDeleteAll(d->m_screenOverlays);
549  d->m_screenOverlays.clear();
550  d->m_osmLineStringItems.clear();
551 
552  const GeoDataObject *object = static_cast<GeoDataObject*>(d->m_model->index(0, 0, QModelIndex()).internalPointer());
553  if (object && object->parent()) {
554  d->createGraphicsItems(object->parent());
555  }
556  emit repaintNeeded();
557 }
558 
559 void GeometryLayer::setTileLevel(int tileLevel)
560 {
561  d->m_tileLevel = tileLevel;
562 }
563 
564 QVector<const GeoDataFeature*> GeometryLayer::whichFeatureAt(const QPoint &curpos, const ViewportParams *viewport)
565 {
567  auto const renderOrder = d->m_styleBuilder->renderOrder();
568  QString const label = QStringLiteral("/label");
569  QSet<GeoGraphicsItem*> checked;
570  for (int i = renderOrder.size()-1; i >= 0; --i) {
571  if (renderOrder[i].endsWith(label)) {
572  continue;
573  }
574  auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
575  for (auto j = layerItems.size()-1; j >= 0; --j) {
576  auto const & layerItem = layerItems[j];
577  if (!checked.contains(layerItem)) {
578  if (layerItem->contains(curpos, viewport)) {
579  result << layerItem->feature();
580  }
581  checked << layerItem;
582  }
583  }
584  }
585 
586  return result;
587 }
588 
589 void GeometryLayer::highlightRouteRelation(qint64 osmId, bool enabled)
590 {
591  if (enabled) {
592  d->m_highlightedRouteRelations << osmId;
593  } else {
594  d->m_highlightedRouteRelations.remove(osmId);
595  }
596  d->updateRelationVisibility();
597 }
598 
599 void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes)
600 {
601  if (relationTypes != d->m_visibleRelationTypes) {
602  d->m_visibleRelationTypes = relationTypes;
603  d->updateRelationVisibility();
604  }
605 }
606 
607 void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
608 {
609  GeoDataCoordinates clickedPoint(lon, lat, 0, unit);
610  QVector<GeoDataPlacemark*> selectedPlacemarks;
611 
612  for (int i = 0; i < d->m_model->rowCount(); ++i) {
613  QVariant const data = d->m_model->data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
614  GeoDataObject *object = qvariant_cast<GeoDataObject*> (data);
615  Q_ASSERT(object);
616  if (const auto doc = geodata_cast<GeoDataDocument>(object)) {
617  bool isHighlight = false;
618 
619  for (const GeoDataStyleMap &styleMap: doc->styleMaps()) {
620  if (styleMap.contains(QStringLiteral("highlight"))) {
621  isHighlight = true;
622  break;
623  }
624  }
625 
626  /*
627  * If a document doesn't specify any highlight
628  * styleId in its style maps then there is no need
629  * to further check that document for placemarks
630  * which have been clicked because we won't
631  * highlight them.
632  */
633  if (isHighlight) {
635  QVector<GeoDataFeature*>::Iterator const end = doc->end();
636 
637  for (; iter != end; ++iter) {
638  if (auto placemark = geodata_cast<GeoDataPlacemark>(*iter)) {
639  GeoDataPolygon *polygon = dynamic_cast<GeoDataPolygon*>(placemark->geometry());
640  if (polygon &&
641  polygon->contains(clickedPoint)) {
642  selectedPlacemarks.push_back(placemark);
643  }
644 
645  if (auto linearRing = geodata_cast<GeoDataLinearRing>(placemark->geometry())) {
646  if (linearRing->contains(clickedPoint)) {
647  selectedPlacemarks.push_back(placemark);
648  }
649  }
650 
651  if (auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) {
652  QVector<GeoDataGeometry*>::Iterator multiIter = multiGeometry->begin();
653  QVector<GeoDataGeometry*>::Iterator const multiEnd = multiGeometry->end();
654 
655  for (; multiIter != multiEnd; ++multiIter) {
656  GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>(*multiIter);
657 
658  if (poly &&
659  poly->contains(clickedPoint)) {
660  selectedPlacemarks.push_back(placemark);
661  break;
662  }
663 
664  if (auto linearRing = geodata_cast<GeoDataLinearRing>(*multiIter)) {
665  if (linearRing->contains(clickedPoint)) {
666  selectedPlacemarks.push_back(placemark);
667  break;
668  }
669  }
670  }
671  }
672  }
673  }
674  }
675  }
676  }
677 
678  emit highlightedPlacemarksChanged(selectedPlacemarks);
679 }
680 
681 void GeometryLayer::setLevelTagDebugModeEnabled(bool enabled)
682 {
683  if (d->m_levelTagDebugModeEnabled != enabled) {
684  d->m_levelTagDebugModeEnabled = enabled;
685  emit repaintNeeded();
686  }
687 }
688 
689 bool GeometryLayer::levelTagDebugModeEnabled() const
690 {
691  return d->m_levelTagDebugModeEnabled;
692 }
693 
694 void GeometryLayer::setDebugLevelTag(int level)
695 {
696  if (d->m_debugLevelTag != level) {
697  d->m_debugLevelTag = level;
698  emit repaintNeeded();
699  }
700 }
701 
702 int GeometryLayer::debugLevelTag() const
703 {
704  return d->m_debugLevelTag;
705 }
706 
707 }
708 
709 #include "moc_GeometryLayer.cpp"
const T value(const Key &key) const const
QVector::iterator begin()
QDateTime currentDateTime()
void push_back(const T &value)
QStringView level(QStringView ifopt)
QList::const_iterator constBegin() const const
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void remove(int i)
@ ObjectPointerRole
The pointer to a specific object.
QVariant data(int role) const const
int size() const const
void applyHighlight(const QVector< GeoDataPlacemark * > &)
static bool fuzzyCompare(const GeoDataLatLonBox &lhs, const GeoDataLatLonBox &rhs, const qreal factor=0.01)
Indicates whether two bounding boxes are roughly equal.
bool isEmpty() const const
Binds a QML item to a specific geodetic location in screen coordinates.
bool isValid() const const
bool contains(const T &value) const const
QString label(StandardShortcut id)
QVector::iterator end()
QList::const_iterator constEnd() const const
Unit
enum used constructor to specify the units used
const QList< QKeySequence > & end()
QDebug mDebug()
a function to replace qDebug() in Marble library code
Definition: MarbleDebug.cpp:31
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Oct 2 2023 03:52:08 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.