diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp index 8e907e674..17eb1fa57 100644 --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -1,726 +1,699 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2008-2009 Patrick Spendrin // Copyright 2010 Thibaut Gridel // Copyright 2011-2012 Bernhard Beschow // Copyright 2014 Gábor Péterffy // #include "GeometryLayer.h" // Marble #include "GeoDataLatLonAltBox.h" #include "GeoDataDocument.h" #include "GeoDataFolder.h" #include "GeoDataLineStyle.h" #include "GeoDataMultiTrack.h" #include "GeoDataObject.h" #include "GeoDataPlacemark.h" #include "GeoDataLinearRing.h" #include "GeoDataMultiGeometry.h" #include "GeoDataPolygon.h" #include "GeoDataPolyStyle.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataStyleMap.h" #include "GeoDataTrack.h" #include "GeoDataTypes.h" #include "GeoDataFeature.h" #include "MarbleDebug.h" #include "GeoPainter.h" #include "ViewportParams.h" #include "RenderState.h" #include "GeoGraphicsScene.h" #include "GeoGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoPolygonGraphicsItem.h" #include "GeoTrackGraphicsItem.h" #include "GeoDataPhotoOverlay.h" #include "GeoDataScreenOverlay.h" #include "GeoPhotoGraphicsItem.h" #include "ScreenOverlayGraphicsItem.h" #include "TileId.h" #include "MarbleGraphicsItem.h" #include "MarblePlacemarkModel.h" #include "GeoDataTreeModel.h" #include #include "StyleBuilder.h" #include "AbstractGeoPolygonGraphicsItem.h" #include "GeoLineStringGraphicsItem.h" #include "GeoDataRelation.h" // Qt #include #include #include namespace Marble { class GeometryLayerPrivate { public: typedef QVector OsmLineStringItems; typedef QSet Relations; typedef QHash FeatureRelationHash; + typedef QVector GeoGraphicItems; struct PaintFragments { // Three lists for different z values // A z value of 0 is default and used by the majority of items, so sorting // can be avoided for it QVector negative; // subways QVector null; // areas and roads QVector positive; // buildings }; explicit GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder); void createGraphicsItems(const GeoDataObject *object); void createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations); void createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark, const Relations &relations); void createGraphicsItemFromOverlay(const GeoDataOverlay *overlay); void removeGraphicsItems(const GeoDataFeature *feature); void updateTiledLineStrings(const GeoDataPlacemark *placemark, GeoLineStringGraphicsItem* lineStringItem); void updateTiledLineStrings(OsmLineStringItems &lineStringItems); void clearCache(); bool showRelation(const GeoDataRelation* relation) const; void updateRelationVisibility(); const QAbstractItemModel *const m_model; const StyleBuilder *const m_styleBuilder; GeoGraphicsScene m_scene; QString m_runtimeTrace; QList m_screenOverlays; QHash m_osmLineStringItems; int m_tileLevel; GeoGraphicsItem* m_lastFeatureAt; bool m_dirty; int m_cachedItemCount; - QHash m_cachedPaintFragments; + QHash m_cachedPaintFragments; typedef QPair LayerItem; QList m_cachedDefaultLayer; QDateTime m_cachedDateTime; GeoDataLatLonBox m_cachedLatLonBox; QSet m_highlightedRouteRelations; bool m_showPublicTransport; }; GeometryLayerPrivate::GeometryLayerPrivate(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) : m_model(model), m_styleBuilder(styleBuilder), m_tileLevel(0), m_lastFeatureAt(nullptr), m_dirty(true), m_cachedItemCount(0), m_showPublicTransport(false) { } void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object) { FeatureRelationHash noRelations; createGraphicsItems(object, noRelations); } GeometryLayer::GeometryLayer(const QAbstractItemModel *model, const StyleBuilder *styleBuilder) : d(new GeometryLayerPrivate(model, styleBuilder)) { const GeoDataObject *object = static_cast(d->m_model->index(0, 0, QModelIndex()).internalPointer()); if (object && object->parent()) { d->createGraphicsItems(object->parent()); } connect(model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(resetCacheData())); connect(model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(addPlacemarks(QModelIndex, int, int))); connect(model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(removePlacemarks(QModelIndex, int, int))); connect(model, SIGNAL(modelReset()), this, SLOT(resetCacheData())); connect(this, SIGNAL(highlightedPlacemarksChanged(QVector)), &d->m_scene, SLOT(applyHighlight(QVector))); connect(&d->m_scene, SIGNAL(repaintNeeded()), this, SIGNAL(repaintNeeded())); } GeometryLayer::~GeometryLayer() { delete d; } QStringList GeometryLayer::renderPosition() const { return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE")); } bool GeometryLayer::render(GeoPainter *painter, ViewportParams *viewport, const QString& renderPos, GeoSceneLayer * layer) { Q_UNUSED(renderPos) Q_UNUSED(layer) painter->save(); auto const & box = viewport->viewLatLonAltBox(); bool isEqual = GeoDataLatLonBox::fuzzyCompare(d->m_cachedLatLonBox, box, 0.05); if (d->m_cachedLatLonBox.isEmpty() || !isEqual) { d->m_dirty = true; } // update the items cache at least every second since the last request auto const now = QDateTime::currentDateTime(); if (!d->m_cachedDateTime.isValid() || d->m_cachedDateTime.msecsTo(now) > 1000) { d->m_dirty = true; } if (d->m_dirty) { d->m_dirty = false; const int maxZoomLevel = qMin(d->m_tileLevel, d->m_styleBuilder->maximumZoomLevel()); auto const items = d->m_scene.items(box, maxZoomLevel);; d->m_cachedLatLonBox = box; d->m_cachedDateTime = now; d->m_cachedItemCount = items.size(); d->m_cachedDefaultLayer.clear(); d->m_cachedPaintFragments.clear(); + QHash paintFragments; QSet const knownLayers = QSet::fromList(d->m_styleBuilder->renderOrder()); for (GeoGraphicsItem* item: items) { QStringList paintLayers = item->paintLayers(); if (paintLayers.isEmpty()) { mDebug() << item << " provides no paint layers, so I force one onto it."; paintLayers << QString(); } for (const auto &layer: paintLayers) { if (knownLayers.contains(layer)) { - GeometryLayerPrivate::PaintFragments & fragments = d->m_cachedPaintFragments[layer]; + GeometryLayerPrivate::PaintFragments &fragments = paintFragments[layer]; double const zValue = item->zValue(); // assign subway stations if (zValue == 0.0) { fragments.null << item; // assign areas and streets } else if (zValue < 0.0) { fragments.negative << item; // assign buildings } else { fragments.positive << item; } } else { // assign symbols d->m_cachedDefaultLayer << GeometryLayerPrivate::LayerItem(layer, item); static QSet missingLayers; if (!missingLayers.contains(layer)) { mDebug() << "Missing layer " << layer << ", in render order, will render it on top"; missingLayers << layer; } } } } // Sort each fragment by z-level for (const QString &layer: d->m_styleBuilder->renderOrder()) { - GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[layer]; + GeometryLayerPrivate::PaintFragments & layerItems = paintFragments[layer]; std::sort(layerItems.negative.begin(), layerItems.negative.end(), GeoGraphicsItem::zValueLessThan); // The idea here is that layerItems.null has most items and does not need to be sorted by z-value // since they are all equal (=0). We do sort them by style pointer though for batch rendering std::sort(layerItems.null.begin(), layerItems.null.end(), GeoGraphicsItem::styleLessThan); std::sort(layerItems.positive.begin(), layerItems.positive.end(), GeoGraphicsItem::zValueAndStyleLessThan); + auto const count = layerItems.negative.size() + layerItems.null.size() + layerItems.positive.size(); + d->m_cachedPaintFragments[layer].reserve(count); + d->m_cachedPaintFragments[layer] << layerItems.negative; + d->m_cachedPaintFragments[layer] << layerItems.null; + d->m_cachedPaintFragments[layer] << layerItems.positive; } } for (const QString &layer: d->m_styleBuilder->renderOrder()) { - GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[layer]; + auto & layerItems = d->m_cachedPaintFragments[layer]; AbstractGeoPolygonGraphicsItem::s_previousStyle = 0; GeoLineStringGraphicsItem::s_previousStyle = 0; - for (auto item: layerItems.negative) { - item->paint(painter, viewport, layer, d->m_tileLevel); - } - for (auto item: layerItems.null) { - item->paint(painter, viewport, layer, d->m_tileLevel); - } - for (auto item: layerItems.positive) { + for (auto item: layerItems) { item->paint(painter, viewport, layer, d->m_tileLevel); } } for (const auto & item: d->m_cachedDefaultLayer) { item.second->paint(painter, viewport, item.first, d->m_tileLevel); } for (ScreenOverlayGraphicsItem* item: d->m_screenOverlays) { item->paintEvent(painter, viewport); } painter->restore(); d->m_runtimeTrace = QStringLiteral("Geometries: %1 Zoom: %2") .arg(d->m_cachedItemCount) .arg(d->m_tileLevel); return true; } RenderState GeometryLayer::renderState() const { return RenderState(QStringLiteral("GeoGraphicsScene")); } QString GeometryLayer::runtimeTrace() const { return d->m_runtimeTrace; } bool GeometryLayer::hasFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { if (d->m_lastFeatureAt && d->m_lastFeatureAt->contains(curpos, viewport)) { return true; } auto const renderOrder = d->m_styleBuilder->renderOrder(); for (int i = renderOrder.size() - 1; i >= 0; --i) { - GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; - for (auto item : layerItems.positive) { - if (item->contains(curpos, viewport)) { - d->m_lastFeatureAt = item; - return true; - } - } - for (auto item : layerItems.null) { - if (item->contains(curpos, viewport)) { - d->m_lastFeatureAt = item; - return true; - } - } - for (auto item : layerItems.negative) { + auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; + for (auto item : layerItems) { if (item->contains(curpos, viewport)) { d->m_lastFeatureAt = item; return true; } } } return false; } void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object, FeatureRelationHash &relations) { clearCache(); if (object->nodeType() == GeoDataTypes::GeoDataDocumentType) { auto document = static_cast(object); for (auto feature: document->featureList()) { if (feature->nodeType() == GeoDataTypes::GeoDataRelationType) { auto relation = static_cast(feature); relation->setVisible(showRelation(relation)); for (auto member: relation->members()) { relations[member] << relation; } } } } if (object->nodeType() == GeoDataTypes::GeoDataPlacemarkType) { auto placemark = static_cast(object); createGraphicsItemFromGeometry(placemark->geometry(), placemark, relations.value(placemark)); } else if (const GeoDataOverlay* overlay = dynamic_cast(object)) { createGraphicsItemFromOverlay(overlay); } // parse all child objects of the container if (const GeoDataContainer *container = dynamic_cast(object)) { int rowCount = container->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItems(container->child(row), relations); } } } void GeometryLayerPrivate::updateTiledLineStrings(const GeoDataPlacemark* placemark, GeoLineStringGraphicsItem* lineStringItem) { if (!placemark->hasOsmData()) { return; } qint64 const osmId = placemark->osmData().oid(); if (osmId <= 0) { return; } auto & lineStringItems = m_osmLineStringItems[osmId]; lineStringItems << lineStringItem; updateTiledLineStrings(lineStringItems); } void GeometryLayerPrivate::updateTiledLineStrings(OsmLineStringItems &lineStringItems) { GeoDataLineString merged; if (lineStringItems.size() > 1) { QVector lineStrings; for (auto item : lineStringItems) { lineStrings << item->lineString(); } merged = GeoLineStringGraphicsItem::merge(lineStrings); } // If merging failed, reset all. Otherwise only the first one // gets the merge result and becomes visible. bool visible = true; for (auto item : lineStringItems) { item->setVisible(visible); if (visible) { item->setMergedLineString(merged); visible = merged.isEmpty(); } } } void GeometryLayerPrivate::clearCache() { m_lastFeatureAt = nullptr; m_dirty = true; m_cachedDateTime = QDateTime(); m_cachedItemCount = 0; m_cachedPaintFragments.clear(); m_cachedDefaultLayer.clear(); m_cachedLatLonBox = GeoDataLatLonBox(); } inline bool GeometryLayerPrivate::showRelation(const GeoDataRelation *relation) const { return (m_showPublicTransport && relation->relationType() >= GeoDataRelation::RouteTrain && relation->relationType() <= GeoDataRelation::RouteTrolleyBus) || m_highlightedRouteRelations.contains(relation->osmData().oid()); } void GeometryLayerPrivate::updateRelationVisibility() { for (int i = 0; i < m_model->rowCount(); ++i) { QVariant const data = m_model->data(m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); if (object->nodeType() == GeoDataTypes::GeoDataDocumentType) { GeoDataDocument* doc = static_cast(object); for (auto feature: doc->featureList()) { if (feature->nodeType() == GeoDataTypes::GeoDataRelationType) { auto relation = static_cast(feature); relation->setVisible(showRelation(relation)); } } } } m_scene.resetStyle(); } void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark, const Relations &relations) { if (!placemark->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem *item = 0; if (object->nodeType() == GeoDataTypes::GeoDataLineStringType) { const GeoDataLineString* line = static_cast(object); auto lineStringItem = new GeoLineStringGraphicsItem(placemark, line); item = lineStringItem; updateTiledLineStrings(placemark, lineStringItem); } else if (object->nodeType() == GeoDataTypes::GeoDataLinearRingType) { const GeoDataLinearRing *ring = static_cast(object); item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, ring); } else if (object->nodeType() == GeoDataTypes::GeoDataPolygonType) { const GeoDataPolygon *poly = static_cast(object); item = GeoPolygonGraphicsItem::createGraphicsItem(placemark, poly); if (item->zValue() == 0) { item->setZValue(poly->renderOrder()); } } else if (object->nodeType() == GeoDataTypes::GeoDataMultiGeometryType) { const GeoDataMultiGeometry *multigeo = static_cast(object); int rowCount = multigeo->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multigeo->child(row), placemark, relations); } } else if (object->nodeType() == GeoDataTypes::GeoDataMultiTrackType) { const GeoDataMultiTrack *multitrack = static_cast(object); int rowCount = multitrack->size(); for (int row = 0; row < rowCount; ++row) { createGraphicsItemFromGeometry(multitrack->child(row), placemark, relations); } } else if (object->nodeType() == GeoDataTypes::GeoDataTrackType) { const GeoDataTrack *track = static_cast(object); item = new GeoTrackGraphicsItem(placemark, track); } if (!item) { return; } item->setRelations(relations); item->setStyleBuilder(m_styleBuilder); item->setVisible(item->visible() && placemark->isGloballyVisible()); item->setMinZoomLevel(m_styleBuilder->minimumZoomLevel(*placemark)); m_scene.addItem(item); } void GeometryLayerPrivate::createGraphicsItemFromOverlay(const GeoDataOverlay *overlay) { if (!overlay->isGloballyVisible()) { return; // Reconsider this when visibility can be changed dynamically } GeoGraphicsItem* item = 0; if (overlay->nodeType() == GeoDataTypes::GeoDataPhotoOverlayType) { GeoDataPhotoOverlay const * photoOverlay = static_cast(overlay); GeoPhotoGraphicsItem *photoItem = new GeoPhotoGraphicsItem(overlay); photoItem->setPoint(photoOverlay->point()); item = photoItem; } else if (overlay->nodeType() == GeoDataTypes::GeoDataScreenOverlayType) { GeoDataScreenOverlay const * screenOverlay = static_cast(overlay); ScreenOverlayGraphicsItem *screenItem = new ScreenOverlayGraphicsItem(screenOverlay); m_screenOverlays.push_back(screenItem); } if (item) { item->setStyleBuilder(m_styleBuilder); item->setVisible(overlay->isGloballyVisible()); m_scene.addItem(item); } } void GeometryLayerPrivate::removeGraphicsItems(const GeoDataFeature *feature) { clearCache(); if (feature->nodeType() == GeoDataTypes::GeoDataPlacemarkType) { GeoDataPlacemark const * placemark = static_cast(feature); if (placemark->isGloballyVisible() && placemark->geometry()->nodeType() == GeoDataTypes::GeoDataLineStringType && placemark->hasOsmData() && placemark->osmData().oid() > 0) { auto & items = m_osmLineStringItems[placemark->osmData().oid()]; bool removed = false; for (auto item : items) { if (item->feature() == feature) { items.removeOne(item); removed = true; break; } } Q_ASSERT(removed); updateTiledLineStrings(items); } m_scene.removeItem(feature); } else if (feature->nodeType() == GeoDataTypes::GeoDataFolderType || feature->nodeType() == GeoDataTypes::GeoDataDocumentType) { const GeoDataContainer *container = static_cast(feature); for (const GeoDataFeature *child: container->featureList()) { removeGraphicsItems(child); } } else if (feature->nodeType() == GeoDataTypes::GeoDataScreenOverlayType) { for (ScreenOverlayGraphicsItem *item: m_screenOverlays) { if (item->screenOverlay() == feature) { m_screenOverlays.removeAll(item); } } } } void GeometryLayer::addPlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(first < d->m_model->rowCount(parent)); Q_ASSERT(last < d->m_model->rowCount(parent)); for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); Q_ASSERT(object); d->createGraphicsItems(object); } emit repaintNeeded(); } void GeometryLayer::removePlacemarks(const QModelIndex& parent, int first, int last) { Q_ASSERT(last < d->m_model->rowCount(parent)); bool isRepaintNeeded = false; for (int i = first; i <= last; ++i) { QModelIndex index = d->m_model->index(i, 0, parent); Q_ASSERT(index.isValid()); const GeoDataObject *object = qvariant_cast(index.data(MarblePlacemarkModel::ObjectPointerRole)); const GeoDataFeature *feature = dynamic_cast(object); if (feature != 0) { d->removeGraphicsItems(feature); isRepaintNeeded = true; } } if (isRepaintNeeded) { emit repaintNeeded(); } } void GeometryLayer::resetCacheData() { d->clearCache(); d->m_scene.clear(); qDeleteAll(d->m_screenOverlays); d->m_screenOverlays.clear(); d->m_osmLineStringItems.clear(); const GeoDataObject *object = static_cast(d->m_model->index(0, 0, QModelIndex()).internalPointer()); if (object && object->parent()) { d->createGraphicsItems(object->parent()); } emit repaintNeeded(); } void GeometryLayer::setTileLevel(int tileLevel) { d->m_tileLevel = tileLevel; } QVector GeometryLayer::whichFeatureAt(const QPoint &curpos, const ViewportParams *viewport) { QVector result; auto const renderOrder = d->m_styleBuilder->renderOrder(); QString const label = QStringLiteral("/label"); QSet checked; for (int i = renderOrder.size()-1; i >= 0; --i) { if (renderOrder[i].endsWith(label)) { continue; } - GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; - for (auto iter = layerItems.positive.crbegin(), end = layerItems.positive.crend(); iter != end; ++iter) { - if (!checked.contains(*iter)) { - if ((*iter)->contains(curpos, viewport)) { - result << (*iter)->feature(); - } - checked << *iter; - } - } - for (auto iter = layerItems.null.crbegin(), end = layerItems.null.crend(); iter != end; ++iter) { - if (!checked.contains(*iter)) { - if ((*iter)->contains(curpos, viewport)) { - result << (*iter)->feature(); - } - checked << *iter; - } - } - for (auto iter = layerItems.negative.crbegin(), end = layerItems.negative.crend(); iter != end; ++iter) { + auto & layerItems = d->m_cachedPaintFragments[renderOrder[i]]; + for (auto iter = layerItems.crbegin(), end = layerItems.crend(); iter != end; ++iter) { if (!checked.contains(*iter)) { if ((*iter)->contains(curpos, viewport)) { result << (*iter)->feature(); } checked << *iter; } } } return result; } void GeometryLayer::highlightRouteRelation(qint64 osmId, bool enabled) { if (enabled) { d->m_highlightedRouteRelations << osmId; } else { d->m_highlightedRouteRelations.remove(osmId); } d->updateRelationVisibility(); } void GeometryLayer::setShowPublicTransport(bool showPublicTransport) { if (showPublicTransport == d->m_showPublicTransport) { return; } d->m_showPublicTransport = showPublicTransport; d->updateRelationVisibility(); } void GeometryLayer::handleHighlight(qreal lon, qreal lat, GeoDataCoordinates::Unit unit) { GeoDataCoordinates clickedPoint(lon, lat, 0, unit); QVector selectedPlacemarks; for (int i = 0; i < d->m_model->rowCount(); ++i) { QVariant const data = d->m_model->data(d->m_model->index(i, 0), MarblePlacemarkModel::ObjectPointerRole); GeoDataObject *object = qvariant_cast (data); Q_ASSERT(object); if (object->nodeType() == GeoDataTypes::GeoDataDocumentType) { Q_ASSERT(dynamic_cast(object) != 0); GeoDataDocument* doc = static_cast(object); bool isHighlight = false; for (const GeoDataStyleMap &styleMap: doc->styleMaps()) { if (styleMap.contains(QStringLiteral("highlight"))) { isHighlight = true; break; } } /* * If a document doesn't specify any highlight * styleId in its style maps then there is no need * to further check that document for placemarks * which have been clicked because we won't * highlight them. */ if (isHighlight) { QVector::Iterator iter = doc->begin(); QVector::Iterator const end = doc->end(); for (; iter != end; ++iter) { if ((*iter)->nodeType() == GeoDataTypes::GeoDataPlacemarkType) { GeoDataPlacemark *placemark = static_cast(*iter); GeoDataPolygon *polygon = dynamic_cast(placemark->geometry()); GeoDataLineString *lineString = dynamic_cast(placemark->geometry()); GeoDataMultiGeometry *multiGeometry = dynamic_cast(placemark->geometry()); if (polygon && polygon->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } if (lineString && lineString->nodeType() == GeoDataTypes::GeoDataLinearRingType) { GeoDataLinearRing *linearRing = static_cast(lineString); if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); } } if (multiGeometry) { QVector::Iterator multiIter = multiGeometry->begin(); QVector::Iterator const multiEnd = multiGeometry->end(); for (; multiIter != multiEnd; ++multiIter) { GeoDataPolygon *poly = dynamic_cast(*multiIter); GeoDataLineString *linestring = dynamic_cast(*multiIter); if (poly && poly->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } if (linestring && linestring->nodeType() == GeoDataTypes::GeoDataLinearRingType) { GeoDataLinearRing *linearRing = static_cast(linestring); if (linearRing->contains(clickedPoint)) { selectedPlacemarks.push_back(placemark); break; } } } } } } } } } emit highlightedPlacemarksChanged(selectedPlacemarks); } } #include "moc_GeometryLayer.cpp"