File indexing completed on 2025-01-05 03:59:17

0001 // SPDX-License-Identifier: LGPL-2.1-or-later
0002 //
0003 // SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de>
0004 // SPDX-FileCopyrightText: 2010 Thibaut Gridel <tgridel@free.fr>
0005 // SPDX-FileCopyrightText: 2011-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
0006 // SPDX-FileCopyrightText: 2014 Gábor Péterffy <peterffy95@gmail.com>
0007 //
0008 
0009 #include "GeometryLayer.h"
0010 
0011 // Qt
0012 #include <qmath.h>
0013 #include <QAbstractItemModel>
0014 #include <QModelIndex>
0015 
0016 // Marble
0017 #include "GeoDataLatLonAltBox.h"
0018 #include "GeoDataDocument.h"
0019 #include "GeoDataFolder.h"
0020 #include "GeoDataLineStyle.h"
0021 #include "GeoDataMultiTrack.h"
0022 #include "GeoDataObject.h"
0023 #include "GeoDataPlacemark.h"
0024 #include "GeoDataLinearRing.h"
0025 #include "GeoDataMultiGeometry.h"
0026 #include "GeoDataPolygon.h"
0027 #include "GeoDataBuilding.h"
0028 #include "GeoDataPolyStyle.h"
0029 #include "GeoDataStyle.h"
0030 #include "GeoDataIconStyle.h"
0031 #include "GeoDataStyleMap.h"
0032 #include "GeoDataTrack.h"
0033 #include "GeoDataFeature.h"
0034 #include "GeoPainter.h"
0035 #include "ViewportParams.h"
0036 #include "RenderState.h"
0037 #include "GeoGraphicsScene.h"
0038 #include "GeoGraphicsItem.h"
0039 #include "GeoLineStringGraphicsItem.h"
0040 #include "GeoPolygonGraphicsItem.h"
0041 #include "GeoTrackGraphicsItem.h"
0042 #include "GeoDataPhotoOverlay.h"
0043 #include "GeoDataScreenOverlay.h"
0044 #include "GeoPhotoGraphicsItem.h"
0045 #include "ScreenOverlayGraphicsItem.h"
0046 #include "TileId.h"
0047 #include "MarbleGraphicsItem.h"
0048 #include "MarblePlacemarkModel.h"
0049 #include "GeoDataTreeModel.h"
0050 #include "OsmPlacemarkData.h"
0051 #include "StyleBuilder.h"
0052 #include "AbstractGeoPolygonGraphicsItem.h"
0053 
0054 #include "digikam_debug.h"
0055 
0056 namespace Marble
0057 {
0058 class GeometryLayerPrivate
0059 {
0060 public:
0061     using OsmLineStringItems = QVector<GeoLineStringGraphicsItem *>;
0062     using Relations = QSet<const GeoDataRelation *>;
0063     typedef QHash<const GeoDataFeature *, Relations> FeatureRelationHash;
0064     using GeoGraphicItems = QVector<GeoGraphicsItem *>;
0065 
0066     struct PaintFragments {
0067         // Three lists for different z values
0068         // A z value of 0 is default and used by the majority of items, so sorting
0069         // can be avoided for it
0070         QVector<GeoGraphicsItem*> negative; // subways
0071         QVector<GeoGraphicsItem*> null;     // areas and roads
0072         QVector<GeoGraphicsItem*> positive; // buildings
0073     };
0074 
0075     explicit GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder);
0076 
0077     void createGraphicsItems(const GeoDataObject *object);
0078     void createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations);
0079     void createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark, const Relations &relations);
0080     void createGraphicsItemFromOverlay(const GeoDataOverlay *overlay);
0081     void removeGraphicsItems(const GeoDataFeature *feature);
0082     void updateTiledLineStrings(const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem);
0083     static void updateTiledLineStrings(OsmLineStringItems &lineStringItems);
0084     void clearCache();
0085     bool showRelation(const GeoDataRelation* relation) const;
0086     void updateRelationVisibility();
0087 
0088     const QAbstractItemModel *const m_model;
0089     const StyleBuilder *const m_styleBuilder;
0090     GeoGraphicsScene m_scene;
0091     QString m_runtimeTrace;
0092     QList<ScreenOverlayGraphicsItem*> m_screenOverlays;
0093 
0094     QHash<qint64, OsmLineStringItems> m_osmLineStringItems;
0095     int m_tileLevel;
0096     GeoGraphicsItem* m_lastFeatureAt;
0097 
0098     bool m_dirty;
0099     int m_cachedItemCount;
0100     QHash<QString, GeoGraphicItems> m_cachedPaintFragments;
0101     typedef QPair<QString, GeoGraphicsItem*> LayerItem;
0102     QList<LayerItem> m_cachedDefaultLayer;
0103     QDateTime m_cachedDateTime;
0104     GeoDataLatLonBox m_cachedLatLonBox;
0105     QSet<qint64> m_highlightedRouteRelations;
0106     GeoDataRelation::RelationTypes m_visibleRelationTypes;
0107     bool m_levelTagDebugModeEnabled;
0108     int m_debugLevelTag;
0109 };
0110 
0111 GeometryLayerPrivate::GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) :
0112     m_model(model),
0113     m_styleBuilder(styleBuilder),
0114     m_tileLevel(0),
0115     m_lastFeatureAt(nullptr),
0116     m_dirty(true),
0117     m_cachedItemCount(0),
0118     m_visibleRelationTypes(GeoDataRelation::RouteFerry),
0119     m_levelTagDebugModeEnabled(false),
0120     m_debugLevelTag(0)
0121 {
0122 }
0123 
0124 void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object)
0125 {
0126     FeatureRelationHash noRelations;
0127     createGraphicsItems(object, noRelations);
0128 }
0129 
0130 GeometryLayer::GeometryLayer(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) :
0131     d(new GeometryLayerPrivate(model, styleBuilder))
0132 {
0133     const GeoDataObject *object = static_cast<GeoDataObject*>(d->m_model->index(0, 0, QModelIndex()).internalPointer());
0134     if (object && object->parent()) {
0135         d->createGraphicsItems(object->parent());
0136     }
0137 
0138     connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
0139             this, SLOT(resetCacheData()));
0140     connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)),
0141             this, SLOT(addPlacemarks(QModelIndex,int,int)));
0142     connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
0143             this, SLOT(removePlacemarks(QModelIndex,int,int)));
0144     connect(model, SIGNAL(modelReset()),
0145             this, SLOT(resetCacheData()));
0146     connect(this, SIGNAL(highlightedPlacemarksChanged(QVector<GeoDataPlacemark*>)),
0147             &d->m_scene, SLOT(applyHighlight(QVector<GeoDataPlacemark*>)));
0148     connect(&d->m_scene, SIGNAL(repaintNeeded()),
0149             this, SIGNAL(repaintNeeded()));
0150 }
0151 
0152 GeometryLayer::~GeometryLayer()
0153 {
0154     delete d;
0155 }
0156 
0157 QStringList GeometryLayer::renderPosition() const
0158 {
0159     return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
0160 }
0161 
0162 
0163 bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport,
0164                            const QString& renderPos, GeoSceneLayer * layer)
0165 {
0166     Q_UNUSED(renderPos)
0167     Q_UNUSED(layer)
0168 
0169     painter->save();
0170 
0171     auto const & box = viewport->viewLatLonAltBox();
0172     bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05);
0173 
0174     if (d->m_cachedLatLonBox.isEmpty() || !isEqual) {
0175         d->m_dirty = true;
0176     }
0177 
0178     // update the items cache at least every second since the last request
0179     auto const now = QDateTime::currentDateTime();
0180     if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) {
0181         d->m_dirty = true;
0182     }
0183 
0184     if (d->m_dirty) {
0185         d->m_dirty = false;
0186 
0187         const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel());
0188         auto const items = d->m_scene.items(box, maxZoomLevel);
0189         d->m_cachedLatLonBox = box;
0190         d->m_cachedDateTime = now;
0191 
0192         d->m_cachedItemCount = items.size();
0193         d->m_cachedDefaultLayer.clear();
0194         d->m_cachedPaintFragments.clear();
0195         QHash<QString, GeometryLayerPrivate::PaintFragments> paintFragments;
0196         const QStringList &renderOrder = d->m_styleBuilder->renderOrder();
0197         QSet<QString> const knownLayers(renderOrder.constBegin(), renderOrder.constEnd());
0198         for (GeoGraphicsItem* item: items) {
0199             QStringList paintLayers = item->paintLayers();
0200             if (paintLayers.isEmpty()) {
0201                 qCDebug(DIGIKAM_MARBLE_LOG) << item << " provides no paint layers, so I force one onto it.";
0202                 paintLayers << QString();
0203             }
0204             for (const auto &layer: paintLayers) {
0205                 if (knownLayers.contains(layer)) {
0206                     GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer];
0207                     double const zValue = item->zValue();
0208                     // assign subway stations
0209                     if (zValue == 0.0) {
0210                         fragments.null << item;
0211                         // assign areas and streets
0212                     } else if (zValue < 0.0) {
0213                         fragments.negative << item;
0214                         // assign buildings
0215                     } else {
0216                         fragments.positive << item;
0217                     }
0218                 } else {
0219                     // assign symbols
0220                     d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item);
0221                     static QSet<QString> missingLayers;
0222                     if (!missingLayers.contains(layer)) {
0223                         qCDebug(DIGIKAM_MARBLE_LOG) << "Missing layer " << layer << ", in render order, will render it on top";
0224                         missingLayers << layer;
0225                     }
0226                 }
0227             }
0228         }
0229         // Sort each fragment by z-level
0230         for (const QString &layer: d->m_styleBuilder->renderOrder()) {
0231             GeometryLayerPrivate::PaintFragments & layerItems = paintFragments[layer];
0232             std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan);
0233             // The idea here is that layerItems.null has most items and does not need to be sorted by z-value
0234             // since they are all equal (=0). We do sort them by style pointer though for batch rendering
0235             std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan);
0236             std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan);
0237             auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size();
0238             d->m_cachedPaintFragments[layer].reserve(count);
0239             d->m_cachedPaintFragments[layer] << layerItems.negative;
0240             d->m_cachedPaintFragments[layer] << layerItems.null;
0241             d->m_cachedPaintFragments[layer] << layerItems.positive;
0242         }
0243     }
0244 
0245     for (const QString &layer: d->m_styleBuilder->renderOrder()) {
0246         auto & layerItems = d->m_cachedPaintFragments[layer];
0247         AbstractGeoPolygonGraphicsItem::s_previousStyle = nullptr;
0248         GeoLineStringGraphicsItem::s_previousStyle = nullptr;
0249         for (auto item: layerItems) {
0250             if (d->m_levelTagDebugModeEnabled) {
0251                 if (const auto placemark = geodata_cast<GeoDataPlacemark>(item->feature())) {
0252                     if (placemark->hasOsmData()) {
0253                         QHash<QString, QString>::const_iterator tagIter = placemark->osmData().findTag(QStringLiteral("level"));
0254                         if (tagIter != placemark->osmData().tagsEnd()) {
0255                             const int val = tagIter.value().toInt();
0256                             if (val != d->m_debugLevelTag) {
0257                                 continue;
0258                             }
0259                         }
0260                     }
0261                 }
0262             }
0263             item->paint(painter, viewport, layer, d->m_tileLevel);
0264         }
0265     }
0266 
0267     for (const auto & item: d->m_cachedDefaultLayer) {
0268         item.second->paint(painter, viewport, item.first, d->m_tileLevel);
0269     }
0270 
0271     for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) {
0272         item->paintEvent(painter, viewport);
0273     }
0274 
0275     painter->restore();
0276     d->m_runtimeTrace = QStringLiteral("Geometries: %1 Zoom: %2")
0277                         .arg(d->m_cachedItemCount)
0278                         .arg(d->m_tileLevel);
0279     return true;
0280 }
0281 
0282 RenderState GeometryLayer::renderState() const
0283 {
0284     return RenderState(QStringLiteral("GeoGraphicsScene"));
0285 }
0286 
0287 QString GeometryLayer::runtimeTrace() const
0288 {
0289     return d->m_runtimeTrace;
0290 }
0291 
0292 bool GeometryLayer::hasFeatureAt(const QPoint &curpos, const ViewportParams *viewport)
0293 {
0294     if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) {
0295         return true;
0296     }
0297 
0298     auto const renderOrder = d->m_styleBuilder->renderOrder();
0299     for (int i = renderOrder.size() - 1; i >= 0; --i) {
0300         auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
0301         for (auto item : layerItems) {
0302             if (item->contains(curpos, viewport)) {
0303                 d->m_lastFeatureAt = item;
0304                 return true;
0305             }
0306         }
0307     }
0308 
0309     return false;
0310 }
0311 
0312 void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations)
0313 {
0314     clearCache();
0315     if (auto document = geodata_cast<GeoDataDocument>(object)) {
0316         for (auto feature: document->featureList()) {
0317             if (auto relation = geodata_cast<GeoDataRelation>(feature)) {
0318                 relation->setVisible(showRelation(relation));
0319                 for (auto member: relation->members()) {
0320                     relations[member] << relation;
0321                 }
0322             }
0323         }
0324     }
0325     if (auto placemark = geodata_cast<GeoDataPlacemark>(object)) {
0326         createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark));
0327     } else if (const GeoDataOverlay* overlay = dynamic_cast<const GeoDataOverlay*>(object)) {
0328         createGraphicsItemFromOverlay(overlay);
0329     }
0330 
0331     // parse all child objects of the container
0332     if (const GeoDataContainer *container = dynamic_cast<const GeoDataContainer*>(object)) {
0333         int rowCount = container->size();
0334         for (int row = 0; row < rowCount; ++row) {
0335             createGraphicsItems(container->child(row), relations);
0336         }
0337     }
0338 }
0339 
0340 void GeometryLayerPrivate::updateTiledLineStrings(const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem)
0341 {
0342     if (!placemark->hasOsmData()) {
0343         return;
0344     }
0345     qint64 const osmId = placemark->osmData().oid();
0346     if (osmId <= 0) {
0347         return;
0348     }
0349     auto & lineStringItems = m_osmLineStringItems[osmId];
0350     lineStringItems << lineStringItem;
0351     updateTiledLineStrings(lineStringItems);
0352 }
0353 
0354 void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems)
0355 {
0356     GeoDataLineString merged;
0357     if (lineStringItems.size() > 1) {
0358         QVector<const GeoDataLineString*> lineStrings;
0359         for (auto item : lineStringItems) {
0360             lineStrings << item->lineString();
0361         }
0362         merged = GeoLineStringGraphicsItem::merge(lineStrings);
0363     }
0364 
0365     // If merging failed, reset all. Otherwise only the first one
0366     // gets the merge result and becomes visible.
0367     bool visible = true;
0368     for (auto item : lineStringItems) {
0369         item->setVisible(visible);
0370         if (visible) {
0371             item->setMergedLineString(merged);
0372             visible = merged.isEmpty();
0373         }
0374     }
0375 }
0376 
0377 void GeometryLayerPrivate::clearCache()
0378 {
0379     m_lastFeatureAt = nullptr;
0380     m_dirty = true;
0381     m_cachedDateTime = QDateTime();
0382     m_cachedItemCount = 0;
0383     m_cachedPaintFragments.clear();
0384     m_cachedDefaultLayer.clear();
0385     m_cachedLatLonBox = GeoDataLatLonBox();
0386 }
0387 
0388 inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const
0389 {
0390     return (m_visibleRelationTypes.testFlag(relation->relationType())
0391             || m_highlightedRouteRelations.contains(relation->osmData().oid()));
0392 }
0393 
0394 void GeometryLayerPrivate::updateRelationVisibility()
0395 {
0396     for (int i = 0; i < m_model->rowCount(); ++i) {
0397         QVariant const data = m_model->data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
0398         GeoDataObject *object = qvariant_cast<GeoDataObject*> (data);
0399         if (auto doc = geodata_cast<GeoDataDocument>(object)) {
0400             for (auto feature: doc->featureList()) {
0401                 if (auto relation = geodata_cast<GeoDataRelation>(feature)) {
0402                     relation->setVisible(showRelation(relation));
0403                 }
0404             }
0405         }
0406     }
0407     m_scene.resetStyle();
0408 }
0409 
0410 void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark, const Relations &relations)
0411 {
0412     if (!placemark->isGloballyVisible()) {
0413         return; // Reconsider this when visibility can be changed dynamically
0414     }
0415 
0416     GeoGraphicsItem *item = nullptr;
0417     if (const auto line = geodata_cast<GeoDataLineString>(object)) {
0418         auto lineStringItem = new GeoLineStringGraphicsItem(placemark, line);
0419         item = lineStringItem;
0420         updateTiledLineStrings(placemark, lineStringItem);
0421     } else if (const auto ring = geodata_cast<GeoDataLinearRing>(object)) {
0422         item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring);
0423     } else if (const auto poly = geodata_cast<GeoDataPolygon>(object)) {
0424         item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly);
0425         if (item->zValue() == 0) {
0426             item->setZValue(poly->renderOrder());
0427         }
0428     } else if (const auto building = geodata_cast<GeoDataBuilding>(object)) {
0429         item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, building);
0430     } else if (const auto multigeo = geodata_cast<GeoDataMultiGeometry>(object)) {
0431         int rowCount = multigeo->size();
0432         for (int row = 0; row < rowCount; ++row) {
0433             createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations);
0434         }
0435     } else if (const auto multitrack = geodata_cast<GeoDataMultiTrack>(object)) {
0436         int rowCount = multitrack->size();
0437         for (int row = 0; row < rowCount; ++row) {
0438             createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations);
0439         }
0440     } else if (const auto track = geodata_cast<GeoDataTrack>(object)) {
0441         item = new GeoTrackGraphicsItem(placemark, track);
0442     }
0443     if (!item) {
0444         return;
0445     }
0446     item->setRelations(relations);
0447     item->setStyleBuilder(m_styleBuilder);
0448     item->setVisible(item->visible() && placemark->isGloballyVisible());
0449     item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark));
0450     m_scene.addItem(item);
0451 }
0452 
0453 void GeometryLayerPrivate::createGraphicsItemFromOverlay(const GeoDataOverlay *overlay)
0454 {
0455     if (!overlay->isGloballyVisible()) {
0456         return; // Reconsider this when visibility can be changed dynamically
0457     }
0458 
0459     GeoGraphicsItem* item = nullptr;
0460     if (const auto photoOverlay = geodata_cast<GeoDataPhotoOverlay>(overlay)) {
0461         GeoPhotoGraphicsItem *photoItem = new GeoPhotoGraphicsItem(overlay);
0462         photoItem->setPoint(photoOverlay->point());
0463         item = photoItem;
0464     } else if (const auto screenOverlay = geodata_cast<GeoDataScreenOverlay>(overlay)) {
0465         ScreenOverlayGraphicsItem *screenItem = new ScreenOverlayGraphicsItem(screenOverlay);
0466         m_screenOverlays.push_back(screenItem);
0467     }
0468 
0469     if (item) {
0470         item->setStyleBuilder(m_styleBuilder);
0471         item->setVisible(overlay->isGloballyVisible());
0472         m_scene.addItem(item);
0473     }
0474 }
0475 
0476 void GeometryLayerPrivate::removeGraphicsItems(const GeoDataFeature *feature)
0477 {
0478     clearCache();
0479     if (const auto placemark = geodata_cast<GeoDataPlacemark>(feature)) {
0480         if (placemark->isGloballyVisible() &&
0481             geodata_cast<GeoDataLineString>(placemark->geometry()) &&
0482             placemark->hasOsmData() &&
0483             placemark->osmData().oid() > 0) {
0484             auto & items = m_osmLineStringItems[placemark->osmData().oid()];
0485             bool removed = false;
0486             for (auto item : items) {
0487                 if (item->feature() == feature) {
0488                     items.removeOne(item);
0489                     removed = true;
0490                     break;
0491                 }
0492             }
0493             Q_ASSERT(removed);
0494             updateTiledLineStrings(items);
0495         }
0496         m_scene.removeItem(feature);
0497     } else if (const auto container = dynamic_cast<const GeoDataContainer*>(feature)) {
0498         for (const GeoDataFeature *child: container->featureList()) {
0499             removeGraphicsItems(child);
0500         }
0501     } else if (geodata_cast<GeoDataScreenOverlay>(feature)) {
0502         for (ScreenOverlayGraphicsItem  *item: m_screenOverlays) {
0503             if (item->screenOverlay() == feature) {
0504                 m_screenOverlays.removeAll(item);
0505             }
0506         }
0507     }
0508 }
0509 
0510 void GeometryLayer::addPlacemarks(const QModelIndex& parent, int first, int last)
0511 {
0512     Q_ASSERT(first < d->m_model->rowCount(parent));
0513     Q_ASSERT(last < d->m_model->rowCount(parent));
0514     for (int i = first; i <= last; ++i) {
0515         QModelIndex index = d->m_model->index(i, 0, parent);
0516         Q_ASSERT(index.isValid());
0517         const GeoDataObject *object = qvariant_cast<GeoDataObject*>(index.data(MarblePlacemarkModel::ObjectPointerRole));
0518         Q_ASSERT(object);
0519         d->createGraphicsItems(object);
0520     }
0521     Q_EMIT repaintNeeded();
0522 
0523 }
0524 
0525 void GeometryLayer::removePlacemarks(const QModelIndex& parent, int first, int last)
0526 {
0527     Q_ASSERT(last < d->m_model->rowCount(parent));
0528     bool isRepaintNeeded = false;
0529     for (int i = first; i <= last; ++i) {
0530         QModelIndex index = d->m_model->index(i, 0, parent);
0531         Q_ASSERT(index.isValid());
0532         const GeoDataObject *object = qvariant_cast<GeoDataObject*>(index.data(MarblePlacemarkModel::ObjectPointerRole));
0533         const GeoDataFeature *feature = dynamic_cast<const GeoDataFeature*>(object);
0534         if (feature != nullptr) {
0535             d->removeGraphicsItems(feature);
0536             isRepaintNeeded = true;
0537         }
0538     }
0539     if (isRepaintNeeded) {
0540         Q_EMIT repaintNeeded();
0541     }
0542 
0543 }
0544 
0545 void GeometryLayer::resetCacheData()
0546 {
0547     d->clearCache();
0548     d->m_scene.clear();
0549     qDeleteAll(d->m_screenOverlays);
0550     d->m_screenOverlays.clear();
0551     d->m_osmLineStringItems.clear();
0552 
0553     const GeoDataObject *object = static_cast<GeoDataObject*>(d->m_model->index(0, 0, QModelIndex()).internalPointer());
0554     if (object && object->parent()) {
0555         d->createGraphicsItems(object->parent());
0556     }
0557     Q_EMIT repaintNeeded();
0558 }
0559 
0560 void GeometryLayer::setTileLevel(int tileLevel)
0561 {
0562     d->m_tileLevel = tileLevel;
0563 }
0564 
0565 QVector<const GeoDataFeature*> GeometryLayer::whichFeatureAt(const QPoint &curpos, const ViewportParams *viewport)
0566 {
0567     QVector<const GeoDataFeature*> result;
0568     auto const renderOrder = d->m_styleBuilder->renderOrder();
0569     QString const label = QStringLiteral("/label");
0570     QSet<GeoGraphicsItem*> checked;
0571     for (int i = renderOrder.size()-1; i >= 0; --i) {
0572         if (renderOrder[i].endsWith(label)) {
0573             continue;
0574         }
0575         auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]];
0576         for (auto j = layerItems.size()-1; j >= 0; --j) {
0577             auto const & layerItem = layerItems[j];
0578             if (!checked.contains(layerItem)) {
0579                 if (layerItem->contains(curpos, viewport)) {
0580                     result << layerItem->feature();
0581                 }
0582                 checked << layerItem;
0583             }
0584         }
0585     }
0586 
0587     return result;
0588 }
0589 
0590 void GeometryLayer::highlightRouteRelation(qint64 osmId, bool enabled)
0591 {
0592     if (enabled) {
0593         d->m_highlightedRouteRelations << osmId;
0594     } else {
0595         d->m_highlightedRouteRelations.remove(osmId);
0596     }
0597     d->updateRelationVisibility();
0598 }
0599 
0600 void GeometryLayer::setVisibleRelationTypes(GeoDataRelation::RelationTypes relationTypes)
0601 {
0602     if (relationTypes != d->m_visibleRelationTypes) {
0603         d->m_visibleRelationTypes = relationTypes;
0604         d->updateRelationVisibility();
0605     }
0606 }
0607 
0608 void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit)
0609 {
0610     GeoDataCoordinates clickedPoint(lon, lat, 0, unit);
0611     QVector<GeoDataPlacemark*> selectedPlacemarks;
0612 
0613     for (int i = 0; i < d->m_model->rowCount(); ++i) {
0614         QVariant const data = d->m_model->data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole);
0615         GeoDataObject *object = qvariant_cast<GeoDataObject*> (data);
0616         Q_ASSERT(object);
0617         if (const auto doc = geodata_cast<GeoDataDocument>(object)) {
0618             bool isHighlight = false;
0619 
0620             for (const GeoDataStyleMap &styleMap: doc->styleMaps()) {
0621                 if (styleMap.contains(QStringLiteral("highlight"))) {
0622                     isHighlight = true;
0623                     break;
0624                 }
0625             }
0626 
0627             /*
0628              * If a document doesn't specify any highlight
0629              * styleId in its style maps then there is no need
0630              * to further check that document for placemarks
0631              * which have been clicked because we won't
0632              * highlight them.
0633              */
0634             if (isHighlight) {
0635                 QVector<GeoDataFeature*>::Iterator iter = doc->begin();
0636                 QVector<GeoDataFeature*>::Iterator const end = doc->end();
0637 
0638                 for (; iter != end; ++iter) {
0639                     if (auto placemark = geodata_cast<GeoDataPlacemark>(*iter)) {
0640                         GeoDataPolygon *polygon = dynamic_cast<GeoDataPolygon*>(placemark->geometry());
0641                         if (polygon &&
0642                             polygon->contains(clickedPoint)) {
0643                             selectedPlacemarks.push_back(placemark);
0644                         }
0645 
0646                         if (auto linearRing = geodata_cast<GeoDataLinearRing>(placemark->geometry())) {
0647                             if (linearRing->contains(clickedPoint)) {
0648                                 selectedPlacemarks.push_back(placemark);
0649                             }
0650                         }
0651 
0652                         if (auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(placemark->geometry())) {
0653                             QVector<GeoDataGeometry*>::Iterator multiIter = multiGeometry->begin();
0654                             QVector<GeoDataGeometry*>::Iterator const multiEnd = multiGeometry->end();
0655 
0656                             for (; multiIter != multiEnd; ++multiIter) {
0657                                 GeoDataPolygon *poly = dynamic_cast<GeoDataPolygon*>(*multiIter);
0658 
0659                                 if (poly &&
0660                                     poly->contains(clickedPoint)) {
0661                                     selectedPlacemarks.push_back(placemark);
0662                                     break;
0663                                 }
0664 
0665                                 if (auto linearRing = geodata_cast<GeoDataLinearRing>(*multiIter)) {
0666                                     if (linearRing->contains(clickedPoint)) {
0667                                         selectedPlacemarks.push_back(placemark);
0668                                         break;
0669                                     }
0670                                 }
0671                             }
0672                         }
0673                     }
0674                 }
0675             }
0676         }
0677     }
0678 
0679     Q_EMIT highlightedPlacemarksChanged(selectedPlacemarks);
0680 }
0681 
0682 void GeometryLayer::setLevelTagDebugModeEnabled(bool enabled)
0683 {
0684     if (d->m_levelTagDebugModeEnabled != enabled) {
0685         d->m_levelTagDebugModeEnabled = enabled;
0686         Q_EMIT repaintNeeded();
0687     }
0688 }
0689 
0690 bool GeometryLayer::levelTagDebugModeEnabled() const
0691 {
0692     return d->m_levelTagDebugModeEnabled;
0693 }
0694 
0695 void GeometryLayer::setDebugLevelTag(int level)
0696 {
0697     if (d->m_debugLevelTag != level) {
0698         d->m_debugLevelTag = level;
0699         Q_EMIT repaintNeeded();
0700     }
0701 }
0702 
0703 int GeometryLayer::debugLevelTag() const
0704 {
0705     return d->m_debugLevelTag;
0706 }
0707 
0708 }
0709 
0710 #include "moc_GeometryLayer.cpp"