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

KDE's Doxygen guidelines are available online.