diff --git a/src/lib/marble/GeoGraphicsScene.cpp b/src/lib/marble/GeoGraphicsScene.cpp index e077f626a..7e3da0838 100644 --- a/src/lib/marble/GeoGraphicsScene.cpp +++ b/src/lib/marble/GeoGraphicsScene.cpp @@ -1,303 +1,271 @@ // // 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 2011 Konstantin Oblaukhov // #include "GeoGraphicsScene.h" #include "GeoDataFeature.h" #include "GeoDataLatLonAltBox.h" #include "GeoDataStyle.h" #include "GeoDataStyleMap.h" #include "GeoDataPlacemark.h" #include "GeoDataDocument.h" #include "GeoDataTypes.h" -#include "GeoDataRelation.h" #include "GeoGraphicsItem.h" #include "TileId.h" #include "TileCoordsPyramid.h" #include "MarbleDebug.h" #include #include namespace Marble { class GeoGraphicsScenePrivate { public: GeoGraphicsScene *q; explicit GeoGraphicsScenePrivate(GeoGraphicsScene *parent) : q(parent) { } ~GeoGraphicsScenePrivate() { q->clear(); } typedef QHash FeatureItemMap; QMap m_tiledItems; QHash m_features; - QSet m_relations; // Stores the items which have been clicked; QList m_selectedItems; GeoDataStyle::Ptr highlightStyle(const GeoDataDocument *document, const GeoDataStyleMap &styleMap); void selectItem( GeoGraphicsItem *item ); void applyHighlightStyle(GeoGraphicsItem *item, const GeoDataStyle::Ptr &style ); }; GeoDataStyle::Ptr GeoGraphicsScenePrivate::highlightStyle( const GeoDataDocument *document, const GeoDataStyleMap &styleMap ) { // @todo Consider QUrl parsing when external styles are suppported QString highlightStyleId = styleMap.value(QStringLiteral("highlight")); highlightStyleId.remove(QLatin1Char('#')); if ( !highlightStyleId.isEmpty() ) { GeoDataStyle::Ptr highlightStyle(new GeoDataStyle( *document->style(highlightStyleId) )); return highlightStyle; } else { return GeoDataStyle::Ptr(); } } void GeoGraphicsScenePrivate::selectItem( GeoGraphicsItem* item ) { m_selectedItems.append( item ); } void GeoGraphicsScenePrivate::applyHighlightStyle(GeoGraphicsItem* item, const GeoDataStyle::Ptr &highlightStyle ) { item->setHighlightStyle( highlightStyle ); item->setHighlighted( true ); } GeoGraphicsScene::GeoGraphicsScene( QObject* parent ): QObject( parent ), d( new GeoGraphicsScenePrivate(this) ) { } GeoGraphicsScene::~GeoGraphicsScene() { delete d; } QList< GeoGraphicsItem* > GeoGraphicsScene::items( const GeoDataLatLonBox &box, int zoomLevel ) const { if ( box.west() > box.east() ) { // Handle boxes crossing the IDL by splitting it into two separate boxes GeoDataLatLonBox left; left.setWest( -M_PI ); left.setEast( box.east() ); left.setNorth( box.north() ); left.setSouth( box.south() ); GeoDataLatLonBox right; right.setWest( box.west() ); right.setEast( M_PI ); right.setNorth( box.north() ); right.setSouth( box.south() ); return items(left, zoomLevel) + items(right, zoomLevel); } QList< GeoGraphicsItem* > result; QRect rect; qreal north, south, east, west; box.boundaries( north, south, east, west ); TileId key; key = TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel ); rect.setLeft( key.x() ); rect.setTop( key.y() ); key = TileId::fromCoordinates( GeoDataCoordinates(east, south, 0), zoomLevel ); rect.setRight( key.x() ); rect.setBottom( key.y() ); TileCoordsPyramid pyramid( 0, zoomLevel ); pyramid.setBottomLevelCoords( rect ); for ( int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) { QRect const coords = pyramid.coords( level ); int x1, y1, x2, y2; coords.getCoords( &x1, &y1, &x2, &y2 ); for ( int x = x1; x <= x2; ++x ) { bool const isBorderX = x == x1 || x == x2; for ( int y = y1; y <= y2; ++y ) { bool const isBorder = isBorderX || y == y1 || y == y2; const TileId tileId = TileId( 0, level, x, y ); foreach(GeoGraphicsItem *object, d->m_tiledItems.value( tileId )) { if (object->minZoomLevel() <= zoomLevel && object->visible()) { if (!isBorder || object->latLonAltBox().intersects(box)) { result.push_back(object); } } } } } } return result; } QList< GeoGraphicsItem* > GeoGraphicsScene::selectedItems() const { return d->m_selectedItems; } void GeoGraphicsScene::applyHighlight( const QVector< GeoDataPlacemark* > &selectedPlacemarks ) { /** * First set the items, which were selected previously, to * use normal style */ foreach ( GeoGraphicsItem *item, d->m_selectedItems ) { item->setHighlighted( false ); } // Also clear the list to store the new selected items d->m_selectedItems.clear(); /** * Process the placemark. which were under mouse * while clicking, and update corresponding graphics * items to use highlight style */ foreach( const GeoDataPlacemark *placemark, selectedPlacemarks ) { auto tileIter = d->m_features.find(placemark); if (tileIter != d->m_features.end()) { auto const & clickedItems = d->m_tiledItems[*tileIter]; auto iter = clickedItems.find(placemark); if (iter != clickedItems.end()) { GeoDataObject *parent = placemark->parent(); if ( parent ) { auto item = *iter; if ( parent->nodeType() == GeoDataTypes::GeoDataDocumentType ) { GeoDataDocument *doc = static_cast( parent ); QString styleUrl = placemark->styleUrl(); styleUrl.remove(QLatin1Char('#')); if ( !styleUrl.isEmpty() ) { GeoDataStyleMap const &styleMap = doc->styleMap( styleUrl ); GeoDataStyle::Ptr style = d->highlightStyle( doc, styleMap ); if ( style ) { d->selectItem( item ); d->applyHighlightStyle( item, style ); } } /** * If a placemark is using an inline style instead of a shared * style ( e.g in case when theme file specifies the colorMap * attribute ) then highlight it if any of the style maps have a * highlight styleId */ else { foreach ( const GeoDataStyleMap &styleMap, doc->styleMaps() ) { GeoDataStyle::Ptr style = d->highlightStyle( doc, styleMap ); if ( style ) { d->selectItem( item ); d->applyHighlightStyle( item, style ); break; } } } } } } } } emit repaintNeeded(); } void GeoGraphicsScene::removeItem( const GeoDataFeature* feature ) { auto tileIter = d->m_features.find(feature); if (tileIter != d->m_features.end()) { auto & tileList = d->m_tiledItems[*tileIter]; auto iter = tileList.find(feature); if (iter != tileList.end()) { auto item = iter.value(); d->m_features.erase(tileIter); tileList.erase(iter); delete item; } } } -void GeoGraphicsScene::removeRelation(const GeoDataRelation *relation) -{ - d->m_relations.remove(relation); -} - void GeoGraphicsScene::clear() { foreach(auto const &list, d->m_tiledItems.values()) { qDeleteAll(list.values()); } d->m_tiledItems.clear(); d->m_features.clear(); - d->m_relations.clear(); } void GeoGraphicsScene::addItem( GeoGraphicsItem* item ) { // Select zoom level so that the object fit in single tile int zoomLevel; qreal north, south, east, west; item->latLonAltBox().boundaries( north, south, east, west ); for(zoomLevel = item->minZoomLevel(); zoomLevel >= 0; zoomLevel--) { if( TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel ) == TileId::fromCoordinates( GeoDataCoordinates(east, south, 0), zoomLevel ) ) break; } const TileId key = TileId::fromCoordinates( GeoDataCoordinates(west, north, 0), zoomLevel ); // same as GeoDataCoordinates(east, south, 0), see above auto & tileList = d->m_tiledItems[key]; auto feature = item->feature(); tileList.insert(feature, item); d->m_features.insert(feature, key ); - for (auto relation: d->m_relations) { - for (auto member: relation->members()) { - if (member == feature) { - item->addRelation(relation); - break; - } - } - } -} - -void GeoGraphicsScene::addRelation(const GeoDataRelation *relation) -{ - d->m_relations << relation; - for (auto member: relation->members()) { - auto tileIter = d->m_features.find(member); - if (tileIter != d->m_features.end()) { - auto & tileList = d->m_tiledItems[*tileIter]; - auto iter = tileList.find(member); - if (iter != tileList.end()) { - auto item = iter.value(); - item->addRelation(relation); - } - } - } } } #include "moc_GeoGraphicsScene.cpp" diff --git a/src/lib/marble/GeoGraphicsScene.h b/src/lib/marble/GeoGraphicsScene.h index d5a9896cd..5011af6fe 100644 --- a/src/lib/marble/GeoGraphicsScene.h +++ b/src/lib/marble/GeoGraphicsScene.h @@ -1,93 +1,88 @@ // // 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 2010 Bastian Holst // Copyright 2011 Konstantin Oblaukhov // #ifndef MARBLE_GEOGRAPHICSSCENE_H #define MARBLE_GEOGRAPHICSSCENE_H #include "marble_export.h" #include "MarbleGlobal.h" #include #include namespace Marble { class GeoGraphicsItem; class GeoDataFeature; class GeoDataLatLonBox; class GeoGraphicsScenePrivate; class GeoDataPlacemark; -class GeoDataRelation; /** * @short This is the home of all GeoGraphicsItems to be shown on the map. */ class MARBLE_EXPORT GeoGraphicsScene : public QObject { Q_OBJECT public: /** * Creates a new instance of GeoGraphicsScene * @param parent the QObject parent of the Scene */ explicit GeoGraphicsScene( QObject *parent = 0 ); ~GeoGraphicsScene() override; /** * @brief Add an item to the GeoGraphicsScene * Adds the item @p item to the GeoGraphicsScene */ void addItem( GeoGraphicsItem *item ); - void addRelation(const GeoDataRelation* relation); - /** * @brief Remove all concerned items from the GeoGraphicsScene * Removes all items which are associated with @p object from the GeoGraphicsScene */ void removeItem( const GeoDataFeature *feature ); - void removeRelation(const GeoDataRelation* relation); - /** * @brief Remove all items from the GeoGraphicsScene */ void clear(); /** * @brief Get the list of items in the specified Box * * @param box The box around the items. * @param maxZoomLevel The max zoom level of tiling * @return The list of items in the specified box in no specific order. */ QList items( const GeoDataLatLonBox &box, int maxZoomLevel ) const; /** * @brief Get the list of items which belong to a placemark * that has been clicked. * @return Returns a list of selected Items */ QList selectedItems() const; public Q_SLOTS: void applyHighlight( const QVector& ); Q_SIGNALS: void repaintNeeded(); private: GeoGraphicsScenePrivate * const d; }; } #endif // MARBLE_GEOGRAPHICSSCENE_H diff --git a/src/lib/marble/graphicsview/GeoGraphicsItem.cpp b/src/lib/marble/graphicsview/GeoGraphicsItem.cpp index 11c22697b..7c3884d14 100644 --- a/src/lib/marble/graphicsview/GeoGraphicsItem.cpp +++ b/src/lib/marble/graphicsview/GeoGraphicsItem.cpp @@ -1,221 +1,211 @@ // // 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 2009 Bastian Holst // // Self #include "GeoGraphicsItem.h" #include "GeoGraphicsItem_p.h" #include "GeoDataTypes.h" #include "GeoDataPlacemark.h" // Qt #include "MarbleDebug.h" #include using namespace Marble; GeoGraphicsItem::GeoGraphicsItem( const GeoDataFeature *feature ) : d( new GeoGraphicsItemPrivate( feature ) ) { setFlag( ItemIsVisible, true ); } GeoGraphicsItem::~GeoGraphicsItem() { delete d; } bool GeoGraphicsItem::visible() const { return d->m_flags & ItemIsVisible; } void GeoGraphicsItem::setVisible( bool visible ) { setFlag( ItemIsVisible, visible ); } GeoGraphicsItem::GeoGraphicsItemFlags GeoGraphicsItem::flags() const { return d->m_flags; } void GeoGraphicsItem::setFlag( GeoGraphicsItemFlag flag, bool enabled ) { if( enabled ) { d->m_flags = d->m_flags | flag; } else { d->m_flags = d->m_flags & ~flag; } } void GeoGraphicsItem::setFlags( GeoGraphicsItemFlags flags ) { d->m_flags = flags; } const GeoDataFeature* GeoGraphicsItem::feature() const { return d->m_feature; } void GeoGraphicsItem::setHighlightStyle( const GeoDataStyle::ConstPtr &highlightStyle) { /** * Delete any previously set style * and assign the new style @highlightStyle */ d->m_highlightStyle = highlightStyle; } GeoDataStyle::ConstPtr GeoGraphicsItem::style() const { /** * m_isHighlight is set true when the item is * supposed to be colored highlighted */ if ( d->m_highlighted && d->m_highlightStyle ) { return d->m_highlightStyle; } if (!d->m_style) { if (d->m_feature->nodeType() == GeoDataTypes::GeoDataPlacemarkType) { const GeoDataPlacemark *placemark = static_cast(d->m_feature); auto styling = StyleParameters(placemark, d->m_renderContext.tileLevel()); for (auto relation: d->m_relations) { if (relation->isVisible()) { styling.relation = relation; break; } } d->m_style = d->m_styleBuilder->createStyle(styling); } else { d->m_style = d->m_feature->style(); } } return d->m_style; } void GeoGraphicsItem::setStyleBuilder(const StyleBuilder *styleBuilder) { d->m_styleBuilder = styleBuilder; } qreal GeoGraphicsItem::zValue() const { return d->m_zValue; } void GeoGraphicsItem::setZValue( qreal z ) { d->m_zValue = z; } void GeoGraphicsItem::setHighlighted( bool highlight ) { d->m_highlighted = highlight; } bool GeoGraphicsItem::isHighlighted() const { return d->m_highlighted; } QStringList GeoGraphicsItem::paintLayers() const { return d->m_paintLayers; } void GeoGraphicsItem::setPaintLayers(const QStringList &paintLayers) { d->m_paintLayers = paintLayers; } void GeoGraphicsItem::setRenderContext(const RenderContext &renderContext) { if (renderContext != d->m_renderContext) { d->m_renderContext = renderContext; d->m_style = GeoDataStyle::ConstPtr(); } } bool GeoGraphicsItem::contains(const QPoint &, const ViewportParams *) const { return false; } -void GeoGraphicsItem::addRelation(const GeoDataRelation *relation) +void GeoGraphicsItem::setRelations(const QSet &relations) { - d->m_relations << relation; - if (relation->isVisible()) { - d->m_style = GeoDataStyle::ConstPtr(); - } -} - -void GeoGraphicsItem::removeRelation(const GeoDataRelation *relation) -{ - d->m_relations.remove(relation); - if (relation->isVisible()) { - d->m_style = GeoDataStyle::ConstPtr(); - } + d->m_relations = relations; + d->m_style = GeoDataStyle::ConstPtr(); } int GeoGraphicsItem::minZoomLevel() const { return d->m_minZoomLevel; } void GeoGraphicsItem::setMinZoomLevel(int zoomLevel) { d->m_minZoomLevel = zoomLevel; } bool GeoGraphicsItem::zValueLessThan(GeoGraphicsItem *one, GeoGraphicsItem *two) { return one->d->m_zValue < two->d->m_zValue; } bool GeoGraphicsItem::styleLessThan(GeoGraphicsItem *one, GeoGraphicsItem *two) { return reinterpret_cast(one->d->m_style.data()) < reinterpret_cast(two->d->m_style.data()); } bool GeoGraphicsItem::zValueAndStyleLessThan(GeoGraphicsItem *one, GeoGraphicsItem *two) { if (one->d->m_zValue == two->d->m_zValue) { return reinterpret_cast(one->d->m_style.data()) < reinterpret_cast(two->d->m_style.data()); } return one->d->m_zValue < two->d->m_zValue; } bool RenderContext::operator==(const RenderContext &other) const { return m_tileLevel == other.m_tileLevel; } bool RenderContext::operator!=(const RenderContext &other) const { return !operator==(other); } int RenderContext::tileLevel() const { return m_tileLevel; } RenderContext::RenderContext(int tileLevel) : m_tileLevel(tileLevel) { // nothing to do } diff --git a/src/lib/marble/graphicsview/GeoGraphicsItem.h b/src/lib/marble/graphicsview/GeoGraphicsItem.h index cf965bd69..37e48ac84 100644 --- a/src/lib/marble/graphicsview/GeoGraphicsItem.h +++ b/src/lib/marble/graphicsview/GeoGraphicsItem.h @@ -1,172 +1,170 @@ // // 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 2009 Bastian Holst // Copyright 2009 Andrew Manson // #ifndef MARBLE_GEOGRAPHICSITEM_H #define MARBLE_GEOGRAPHICSITEM_H // Marble #include "marble_export.h" #include "GeoDataStyle.h" class QString; namespace Marble { class GeoDataFeature; class GeoDataLatLonAltBox; class GeoDataCoordinates; class GeoGraphicsItemPrivate; class GeoPainter; class StyleBuilder; class ViewportParams; class GeoDataRelation; class RenderContext { public: bool operator==(const RenderContext &other) const; bool operator!=(const RenderContext &other) const; explicit RenderContext(int tileLevel = -1); int tileLevel() const; private: int m_tileLevel; }; class MARBLE_EXPORT GeoGraphicsItem { public: explicit GeoGraphicsItem( const GeoDataFeature *feature ); virtual ~GeoGraphicsItem(); enum GeoGraphicsItemFlag { NoOptions = 0x0, ItemIsMovable = 0x1, ItemIsSelectable = 0x2, ItemIsVisible = 0x4 }; Q_DECLARE_FLAGS(GeoGraphicsItemFlags, GeoGraphicsItemFlag) bool visible() const; void setVisible( bool visible ); /** * Get the GeoGraphicItemFlags value that describes which flags are set on * this item. @see QFlags */ GeoGraphicsItemFlags flags() const; /** * Set or unset a single flag * @param enabled sets if the flag is to be set or unset */ void setFlag( GeoGraphicsItemFlag flag, bool enabled = true ); /** * Replace all of the current flags. * @param flags is the new value for this item's flags. */ void setFlags( GeoGraphicsItemFlags flags ); /** * Returns the minim zoom level on which item will be active. */ int minZoomLevel() const; /** * Sets the minimum zoom level */ void setMinZoomLevel( int zoomLevel ); /** * Returns the placemark for that item. */ const GeoDataFeature* feature() const; /** * Returns the bounding box covered by the item. */ virtual const GeoDataLatLonAltBox &latLonAltBox() const = 0; /** * Returns the style of item. */ GeoDataStyle::ConstPtr style() const; /** * Set the style for the item. */ void setStyleBuilder(const StyleBuilder *styleBuilder); /** * Set the style which will be used when * placemark is highlighted. * GeoGraphicsItem takes ownership of the * passed style and deletes it when appropriate. */ void setHighlightStyle( const GeoDataStyle::ConstPtr &highlightStyle ); /** * Returns the z value of the item */ qreal zValue() const; /** * Set the z value of the item */ void setZValue( qreal z ); static bool zValueLessThan(GeoGraphicsItem* one, GeoGraphicsItem* two); static bool styleLessThan(GeoGraphicsItem* one, GeoGraphicsItem* two); static bool zValueAndStyleLessThan(GeoGraphicsItem* one, GeoGraphicsItem* two); /** * Paints the item using the given GeoPainter. * * Note that depending on the projection and zoom level, the item may be visible more than once, * which is taken care of by GeoPainter. */ virtual void paint(GeoPainter *painter, const ViewportParams *viewport, const QString &layer, int tileZoomLevel) = 0; void setHighlighted( bool highlight ); bool isHighlighted() const; QStringList paintLayers() const; void setPaintLayers(const QStringList &paintLayers); void setRenderContext(const RenderContext &renderContext); /** * @brief contains Returns true if the item contains the given coordinates * @param coordinates * @param screenPosition * @return */ virtual bool contains(const QPoint &screenPosition, const ViewportParams *viewport) const; - void addRelation(const GeoDataRelation *relation); - - void removeRelation(const GeoDataRelation *relation); + void setRelations(const QSet &relations); protected: GeoGraphicsItemPrivate *const d; }; } // Namespace Marble Q_DECLARE_OPERATORS_FOR_FLAGS(Marble::GeoGraphicsItem::GeoGraphicsItemFlags) #endif diff --git a/src/lib/marble/layers/GeometryLayer.cpp b/src/lib/marble/layers/GeometryLayer.cpp index 379fbd609..458633189 100644 --- a/src/lib/marble/layers/GeometryLayer.cpp +++ b/src/lib/marble/layers/GeometryLayer.cpp @@ -1,643 +1,660 @@ // // 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; 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 createGraphicsItemFromGeometry(const GeoDataGeometry *object, const GeoDataPlacemark *placemark); + 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(); 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; typedef QPair LayerItem; QList m_cachedDefaultLayer; QDateTime m_cachedDateTime; GeoDataLatLonBox m_cachedLatLonBox; }; 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) { } +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(); QSet const knownLayers = QSet::fromList(d->m_styleBuilder->renderOrder()); foreach (GeoGraphicsItem* item, items) { QStringList paintLayers = item->paintLayers(); if (paintLayers.isEmpty()) { mDebug() << item << " provides no paint layers, so I force one onto it."; paintLayers << QString(); } foreach (const auto &layer, paintLayers) { if (knownLayers.contains(layer)) { GeometryLayerPrivate::PaintFragments & fragments = d->m_cachedPaintFragments[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 foreach (const QString &layer, d->m_styleBuilder->renderOrder()) { GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[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); } } foreach (const QString &layer, d->m_styleBuilder->renderOrder()) { GeometryLayerPrivate::PaintFragments & layerItems = d->m_cachedPaintFragments[layer]; AbstractGeoPolygonGraphicsItem::s_previousStyle = 0; GeoLineStringGraphicsItem::s_previousStyle = 0; foreach (auto item, layerItems.negative) { item->paint(painter, viewport, layer, d->m_tileLevel); } foreach (auto item, layerItems.null) { item->paint(painter, viewport, layer, d->m_tileLevel); } foreach (auto item, layerItems.positive) { item->paint(painter, viewport, layer, d->m_tileLevel); } } foreach (const auto & item, d->m_cachedDefaultLayer) { item.second->paint(painter, viewport, item.first, d->m_tileLevel); } foreach (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) { if (item->contains(curpos, viewport)) { d->m_lastFeatureAt = item; return true; } } } return false; } -void GeometryLayerPrivate::createGraphicsItems(const GeoDataObject *object) +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); + for (auto member: relation->members()) { + relations[member] << relation; + } + } + } + } if (object->nodeType() == GeoDataTypes::GeoDataPlacemarkType) { auto placemark = static_cast(object); - createGraphicsItemFromGeometry(placemark->geometry(), placemark); - } else if (object->nodeType() == GeoDataTypes::GeoDataRelationType) { - m_scene.addRelation(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)); + 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(); } -void GeometryLayerPrivate::createGraphicsItemFromGeometry(const GeoDataGeometry* object, const GeoDataPlacemark *placemark) +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); + 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); + 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::GeoDataRelationType) { - m_scene.removeRelation(static_cast(feature)); } else if (feature->nodeType() == GeoDataTypes::GeoDataFolderType || feature->nodeType() == GeoDataTypes::GeoDataDocumentType) { const GeoDataContainer *container = static_cast(feature); foreach (const GeoDataFeature *child, container->featureList()) { removeGraphicsItems(child); } } else if (feature->nodeType() == GeoDataTypes::GeoDataScreenOverlayType) { foreach (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(); 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)) { result << item->feature(); } } for (auto item : layerItems.null) { if (item->contains(curpos, viewport)) { result << item->feature(); } } for (auto item : layerItems.negative) { if (item->contains(curpos, viewport)) { result << item->feature(); } } } return result; } 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; foreach (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"