Marble

GeometryLayer.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de>
4// SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr>
5// SPDX-FileCopyrightText: 2011-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6// SPDX-FileCopyrightText: 2014 Gábor Péterffy <peterffy95@gmail.com>
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
55namespace Marble
56{
57class GeometryLayerPrivate
58{
59public:
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
110GeometryLayerPrivate::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
123void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object)
124{
125 FeatureRelationHash noRelations;
126 createGraphicsItems(object, noRelations);
127}
128
129GeometryLayer::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
151GeometryLayer::~GeometryLayer()
152{
153 delete d;
154}
155
156QStringList GeometryLayer::renderPosition() const
157{
158 return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
159}
160
161
162bool 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
281RenderState GeometryLayer::renderState() const
282{
283 return RenderState(QStringLiteral("GeoGraphicsScene"));
284}
285
286QString GeometryLayer::runtimeTrace() const
287{
288 return d->m_runtimeTrace;
289}
290
291bool 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
311void 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
339void 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
353void 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
376void 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
387inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const
388{
389 return (m_visibleRelationTypes.testFlag(relation->relationType())
390 || m_highlightedRouteRelations.contains(relation->osmData().oid()));
391}
392
393void 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
409void 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
452void 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
475void 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
509void 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
524void 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
544void 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
559void GeometryLayer::setTileLevel(int tileLevel)
560{
561 d->m_tileLevel = tileLevel;
562}
563
564QVector<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");
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
589void 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
599void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes)
600{
601 if (relationTypes != d->m_visibleRelationTypes) {
602 d->m_visibleRelationTypes = relationTypes;
603 d->updateRelationVisibility();
604 }
605}
606
607void 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
681void GeometryLayer::setLevelTagDebugModeEnabled(bool enabled)
682{
683 if (d->m_levelTagDebugModeEnabled != enabled) {
684 d->m_levelTagDebugModeEnabled = enabled;
685 emit repaintNeeded();
686 }
687}
688
689bool GeometryLayer::levelTagDebugModeEnabled() const
690{
691 return d->m_levelTagDebugModeEnabled;
692}
693
694void GeometryLayer::setDebugLevelTag(int level)
695{
696 if (d->m_debugLevelTag != level) {
697 d->m_debugLevelTag = level;
698 emit repaintNeeded();
699 }
700}
701
702int GeometryLayer::debugLevelTag() const
703{
704 return d->m_debugLevelTag;
705}
706
707}
708
709#include "moc_GeometryLayer.cpp"
This file contains the headers for ViewportParams.
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
QStringView level(QStringView ifopt)
QString label(StandardShortcut id)
Binds a QML item to a specific geodetic location in screen coordinates.
QDateTime currentDateTime()
iterator begin()
const_iterator constBegin() const const
const_iterator constEnd() const const
iterator end()
bool isEmpty() const const
void push_back(parameter_type value)
qsizetype size() const const
QVariant data(int role) const const
bool isValid() const const
bool contains(const QSet< T > &other) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void * data()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:57 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.