diff --git a/src/lib/marble/PlacemarkLayout.cpp b/src/lib/marble/PlacemarkLayout.cpp index 59f675092..665813719 100644 --- a/src/lib/marble/PlacemarkLayout.cpp +++ b/src/lib/marble/PlacemarkLayout.cpp @@ -1,712 +1,720 @@ // // 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 2006-2007 Torsten Rahn // Copyright 2007-2008 Inge Wallin // Copyright 2010-2012 Bernhard Beschow // #include "PlacemarkLayout.h" #include #include #include #include #include #include #include #include #include "GeoDataLatLonAltBox.h" #include "GeoDataPlacemark.h" #include "GeoDataStyle.h" #include "GeoDataIconStyle.h" #include "GeoDataLabelStyle.h" #include "GeoDataTypes.h" #include "OsmPlacemarkData.h" #include "MarbleDebug.h" #include "MarbleGlobal.h" #include "PlacemarkLayer.h" #include "MarbleClock.h" #include "MarblePlacemarkModel.h" #include "MarbleDirs.h" #include "ViewportParams.h" #include "TileId.h" #include "TileCoordsPyramid.h" #include "VisiblePlacemark.h" #include "MathHelper.h" #include namespace { //Helper function that checks for available room for the label bool hasRoomFor(const QVector & placemarks, const QRectF &boundingBox) { // Check if there is another label or symbol that overlaps. QVector::const_iterator beforeItEnd = placemarks.constEnd(); for ( QVector::ConstIterator beforeIt = placemarks.constBegin(); beforeIt != beforeItEnd; ++beforeIt ) { if ( boundingBox.intersects( (*beforeIt)->boundingBox() ) ) { return false; } } return true; } } namespace Marble { QSet acceptedVisualCategories() { QSet visualCategories; visualCategories << GeoDataPlacemark::SmallCity << GeoDataPlacemark::SmallCountyCapital << GeoDataPlacemark::SmallStateCapital << GeoDataPlacemark::SmallNationCapital << GeoDataPlacemark::MediumCity << GeoDataPlacemark::MediumCountyCapital << GeoDataPlacemark::MediumStateCapital << GeoDataPlacemark::MediumNationCapital << GeoDataPlacemark::BigCity << GeoDataPlacemark::BigCountyCapital << GeoDataPlacemark::BigStateCapital << GeoDataPlacemark::BigNationCapital << GeoDataPlacemark::LargeCity << GeoDataPlacemark::LargeCountyCapital << GeoDataPlacemark::LargeStateCapital << GeoDataPlacemark::LargeNationCapital << GeoDataPlacemark::Nation << GeoDataPlacemark::Mountain << GeoDataPlacemark::Volcano << GeoDataPlacemark::Mons << GeoDataPlacemark::Valley << GeoDataPlacemark::Continent << GeoDataPlacemark::Ocean << GeoDataPlacemark::OtherTerrain << GeoDataPlacemark::Crater << GeoDataPlacemark::Mare << GeoDataPlacemark::GeographicPole << GeoDataPlacemark::MagneticPole << GeoDataPlacemark::ShipWreck << GeoDataPlacemark::PlaceSuburb << GeoDataPlacemark::PlaceHamlet << GeoDataPlacemark::PlaceLocality; return visualCategories; } PlacemarkLayout::PlacemarkLayout( QAbstractItemModel *placemarkModel, QItemSelectionModel *selectionModel, MarbleClock *clock, const StyleBuilder *styleBuilder, QObject* parent ) : QObject( parent ), m_placemarkModel(placemarkModel), m_selectionModel( selectionModel ), m_clock( clock ), m_acceptedVisualCategories( acceptedVisualCategories() ), m_showPlaces( false ), m_showCities( false ), m_showTerrain( false ), m_showOtherPlaces( false ), m_showLandingSites( false ), m_showCraters( false ), m_showMaria( false ), m_maxLabelHeight(maxLabelHeight()), m_styleResetRequested( true ), m_styleBuilder(styleBuilder), m_lastPlacemarkAt(nullptr) { Q_ASSERT(m_placemarkModel); connect( m_selectionModel, SIGNAL( selectionChanged( QItemSelection, QItemSelection) ), this, SLOT(requestStyleReset()) ); connect( m_placemarkModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(resetCacheData()) ); connect( m_placemarkModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(addPlacemarks(QModelIndex,int,int)) ); connect( m_placemarkModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(removePlacemarks(QModelIndex,int,int)) ); connect( m_placemarkModel, SIGNAL(modelReset()), this, SLOT(resetCacheData()) ); } PlacemarkLayout::~PlacemarkLayout() { styleReset(); } void PlacemarkLayout::setShowPlaces( bool show ) { m_showPlaces = show; } void PlacemarkLayout::setShowCities( bool show ) { m_showCities = show; } void PlacemarkLayout::setShowTerrain( bool show ) { m_showTerrain = show; } void PlacemarkLayout::setShowOtherPlaces( bool show ) { m_showOtherPlaces = show; } void PlacemarkLayout::setShowLandingSites( bool show ) { m_showLandingSites = show; } void PlacemarkLayout::setShowCraters( bool show ) { m_showCraters = show; } void PlacemarkLayout::setShowMaria( bool show ) { m_showMaria = show; } void PlacemarkLayout::requestStyleReset() { mDebug() << "Style reset requested."; m_styleResetRequested = true; } void PlacemarkLayout::styleReset() +{ + clearCache(); + m_maxLabelHeight = maxLabelHeight(); + m_styleResetRequested = false; +} + +void PlacemarkLayout::clearCache() { m_paintOrder.clear(); m_lastPlacemarkAt = nullptr; m_labelArea = 0; qDeleteAll( m_visiblePlacemarks ); m_visiblePlacemarks.clear(); - m_maxLabelHeight = maxLabelHeight(); - m_styleResetRequested = false; } QVector PlacemarkLayout::whichPlacemarkAt( const QPoint& curpos ) { if ( m_styleResetRequested ) { styleReset(); } QVector ret; foreach( VisiblePlacemark* mark, m_paintOrder ) { if ( mark->labelRect().contains( curpos ) || mark->symbolRect().contains( curpos ) ) { ret.append( mark->placemark() ); } } return ret; } int PlacemarkLayout::maxLabelHeight() const { QFont const standardFont(QStringLiteral("Sans Serif")); return QFontMetrics(standardFont).height(); } /// feed an internal QMap of placemarks with TileId as key when model changes void PlacemarkLayout::addPlacemarks( const QModelIndex& parent, int first, int last ) { Q_ASSERT( first < m_placemarkModel->rowCount() ); Q_ASSERT( last < m_placemarkModel->rowCount() ); for( int i=first; i<=last; ++i ) { QModelIndex index = m_placemarkModel->index( i, 0, parent ); Q_ASSERT( index.isValid() ); const GeoDataPlacemark *placemark = static_cast(qvariant_cast(index.data( MarblePlacemarkModel::ObjectPointerRole ) )); const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark ); if ( !coordinates.isValid() ) { continue; } if (placemark->hasOsmData()) { qint64 const osmId = placemark->osmData().id(); if (osmId > 0) { if (m_osmIds.contains(osmId)) { continue; // placemark is already shown } m_osmIds << osmId; } } int zoomLevel = placemark->zoomLevel(); TileId key = TileId::fromCoordinates( coordinates, zoomLevel ); m_placemarkCache[key].append( placemark ); } emit repaintNeeded(); } void PlacemarkLayout::removePlacemarks( const QModelIndex& parent, int first, int last ) { Q_ASSERT( first < m_placemarkModel->rowCount() ); Q_ASSERT( last < m_placemarkModel->rowCount() ); for( int i=first; i<=last; ++i ) { QModelIndex index = m_placemarkModel->index( i, 0, parent ); Q_ASSERT( index.isValid() ); const GeoDataPlacemark *placemark = static_cast(qvariant_cast( index.data( MarblePlacemarkModel::ObjectPointerRole ) )); const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark ); if ( !coordinates.isValid() ) { continue; } int zoomLevel = placemark->zoomLevel(); TileId key = TileId::fromCoordinates( coordinates, zoomLevel ); delete m_visiblePlacemarks[placemark]; m_visiblePlacemarks.remove(placemark); m_placemarkCache[key].removeAll( placemark ); if (placemark->hasOsmData()) { qint64 const osmId = placemark->osmData().id(); if (osmId > 0) { m_osmIds.remove(osmId); } } } emit repaintNeeded(); } void PlacemarkLayout::resetCacheData() { const int rowCount = m_placemarkModel->rowCount(); m_osmIds.clear(); m_placemarkCache.clear(); qDeleteAll(m_visiblePlacemarks); m_visiblePlacemarks.clear(); requestStyleReset(); addPlacemarks( m_placemarkModel->index( 0, 0 ), 0, rowCount ); emit repaintNeeded(); } QSet PlacemarkLayout::visibleTiles( const ViewportParams *viewport, int zoomLevel ) const { /* * rely on m_placemarkCache to find the placemarks for the tiles which * matter. The top level tiles have the more popular placemarks, * the bottom level tiles have the smaller ones, and we only get the ones * matching our latLonAltBox. */ qreal north, south, east, west; viewport->viewLatLonAltBox().boundaries(north, south, east, west); QSet tileIdSet; QVector geoRects; if( west <= east ) { geoRects << QRectF(west, north, east - west, south - north); } else { geoRects << QRectF(west, north, M_PI - west, south - north); geoRects << QRectF(-M_PI, north, east + M_PI, south - north); } foreach( const QRectF &geoRect, geoRects ) { TileId key; QRect rect; key = TileId::fromCoordinates( GeoDataCoordinates(geoRect.left(), north, 0), zoomLevel); rect.setLeft( key.x() ); rect.setTop( key.y() ); key = TileId::fromCoordinates( GeoDataCoordinates(geoRect.right(), 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 ) { for ( int y = y1; y <= y2; ++y ) { TileId const tileId( 0, level, x, y ); tileIdSet.insert(tileId); } } } } return tileIdSet; } QVector PlacemarkLayout::generateLayout( const ViewportParams *viewport, int tileLevel ) { m_runtimeTrace.clear(); - if ( m_placemarkModel->rowCount() <= 0 ) + if ( m_placemarkModel->rowCount() <= 0 ) { + clearCache(); return QVector(); + } if ( m_styleResetRequested ) { styleReset(); } if ( m_maxLabelHeight == 0 ) { + clearCache(); return QVector(); } QList placemarkList; auto currentMaxLabelHeight = m_maxLabelHeight; do { currentMaxLabelHeight = m_maxLabelHeight; const int secnumber = viewport->height() / m_maxLabelHeight + 1; m_rowsection.clear(); m_rowsection.resize(secnumber); m_paintOrder.clear(); m_lastPlacemarkAt = nullptr; m_labelArea = 0; // First handle the selected placemarks as they have the highest priority. const QModelIndexList selectedIndexes = m_selectionModel->selection().indexes(); auto const viewLatLonAltBox = viewport->viewLatLonAltBox(); for ( int i = 0; i < selectedIndexes.count(); ++i ) { const QModelIndex index = selectedIndexes.at( i ); const GeoDataPlacemark *placemark = static_cast(qvariant_cast(index.data( MarblePlacemarkModel::ObjectPointerRole ) )); const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark ); if ( !coordinates.isValid() ) { continue; } qreal x = 0; qreal y = 0; if ( !viewLatLonAltBox.contains( coordinates ) || ! viewport->screenCoordinates( coordinates, x, y )) { continue; } if( layoutPlacemark( placemark, coordinates, x, y, true) ) { // Make sure not to draw more placemarks on the screen than // specified by placemarksOnScreenLimit(). if ( placemarksOnScreenLimit( viewport->size() ) ) break; } } // Now handle all other placemarks... const QItemSelection selection = m_selectionModel->selection(); placemarkList.clear(); foreach ( const TileId &tileId, visibleTiles( viewport, tileLevel ) ) { placemarkList += m_placemarkCache.value( tileId ); } std::sort(placemarkList.begin(), placemarkList.end(), GeoDataPlacemark::placemarkLayoutOrderCompare); foreach ( const GeoDataPlacemark *placemark, placemarkList ) { const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark ); if ( !coordinates.isValid() ) { continue; } int zoomLevel = placemark->zoomLevel(); if ( zoomLevel > 20 ) { break; } qreal x = 0; qreal y = 0; if ( !viewLatLonAltBox.contains( coordinates ) || ! viewport->screenCoordinates( coordinates, x, y )) { continue; } if ( !placemark->isGloballyVisible() ) { continue; } const GeoDataPlacemark::GeoDataVisualCategory visualCategory = placemark->visualCategory(); // Skip city marks if we're not showing cities. if ( !m_showCities && visualCategory >= GeoDataPlacemark::SmallCity && visualCategory <= GeoDataPlacemark::Nation ) continue; // Skip terrain marks if we're not showing terrain. if ( !m_showTerrain && visualCategory >= GeoDataPlacemark::Mountain && visualCategory <= GeoDataPlacemark::OtherTerrain ) continue; // Skip other places if we're not showing other places. if ( !m_showOtherPlaces && visualCategory >= GeoDataPlacemark::GeographicPole && visualCategory <= GeoDataPlacemark::Observatory ) continue; // Skip landing sites if we're not showing landing sites. if ( !m_showLandingSites && visualCategory >= GeoDataPlacemark::MannedLandingSite && visualCategory <= GeoDataPlacemark::UnmannedHardLandingSite ) continue; // Skip craters if we're not showing craters. if ( !m_showCraters && visualCategory == GeoDataPlacemark::Crater ) continue; // Skip maria if we're not showing maria. if ( !m_showMaria && visualCategory == GeoDataPlacemark::Mare ) continue; if ( !m_showPlaces && visualCategory >= GeoDataPlacemark::GeographicPole && visualCategory <= GeoDataPlacemark::Observatory ) continue; // We handled selected placemarks already, so we skip them here... // Assuming that only a small amount of places is selected // we check for the selected state after all other filters bool isSelected = false; foreach ( const QModelIndex &index, selection.indexes() ) { const GeoDataPlacemark *mark = static_cast(qvariant_cast(index.data( MarblePlacemarkModel::ObjectPointerRole ) )); if (mark == placemark ) { isSelected = true; break; } } if ( isSelected ) continue; if( layoutPlacemark( placemark, coordinates, x, y, isSelected ) ) { // Make sure not to draw more placemarks on the screen than // specified by placemarksOnScreenLimit(). if ( placemarksOnScreenLimit( viewport->size() ) ) break; } } if (m_visiblePlacemarks.size() > qMax(100, 4 * m_paintOrder.size())) { auto const extendedBox = viewLatLonAltBox.scaled(2.0, 2.0); QVector outdated; for (auto placemark: m_visiblePlacemarks) { if (!extendedBox.contains(placemark->coordinates())) { outdated << placemark; } } for (auto placemark: outdated) { delete m_visiblePlacemarks.take(placemark->placemark()); } } } while (currentMaxLabelHeight != m_maxLabelHeight); m_runtimeTrace = QStringLiteral("Placemarks: %1 Drawn: %2").arg(placemarkList.count()).arg(m_paintOrder.size()); return m_paintOrder; } QString PlacemarkLayout::runtimeTrace() const { return m_runtimeTrace; } QList PlacemarkLayout::visiblePlacemarks() const { return m_visiblePlacemarks.values(); } bool PlacemarkLayout::hasPlacemarkAt(const QPoint &pos) { if ( m_styleResetRequested ) { styleReset(); } if (m_lastPlacemarkAt && (m_lastPlacemarkAt->labelRect().contains(pos) || m_lastPlacemarkAt->symbolRect().contains(pos))) { return true; } foreach(VisiblePlacemark* mark, m_paintOrder) { if (mark->labelRect().contains(pos) || mark->symbolRect().contains(pos)) { m_lastPlacemarkAt = mark; return true; } } return false; } bool PlacemarkLayout::layoutPlacemark( const GeoDataPlacemark *placemark, const GeoDataCoordinates &coordinates, qreal x, qreal y, bool selected ) { // Find the corresponding visible placemark VisiblePlacemark *mark = m_visiblePlacemarks.value( placemark ); if ( !mark ) { // If there is no visible placemark yet for this index, // create a new one... StyleParameters parameters; // @todo: Set / adjust to tile level parameters.placemark = placemark; auto style = m_styleBuilder->createStyle(parameters); mark = new VisiblePlacemark(placemark, coordinates, style); m_visiblePlacemarks.insert( placemark, mark ); connect( mark, SIGNAL(updateNeeded()), this, SIGNAL(repaintNeeded()) ); } GeoDataStyle::ConstPtr style = mark->style(); // Choose Section QPointF hotSpot = mark->hotSpot(); mark->setSelected(selected); mark->setSymbolPosition(QPointF(x - hotSpot.x(), y - hotSpot.y())); // Find out whether the area around the placemark is covered already. // If there's not enough space free don't add a VisiblePlacemark here. const QString labelText = placemark->displayName(); QRectF labelRect; if (!labelText.isEmpty()) { labelRect = roomForLabel(style, x, y, labelText, mark); } if (labelRect.isEmpty() && mark->symbolPixmap().isNull()) { return false; } if (!mark->symbolPixmap().isNull() && !hasRoomForPixmap(y, mark)) { return false; } mark->setLabelRect( labelRect ); // Add the current placemark to the matching row and its // direct neighbors. int idx = y / m_maxLabelHeight; if ( idx - 1 >= 0 ) { m_rowsection[ idx - 1 ].append( mark ); } m_rowsection[ idx ].append( mark ); if ( idx + 1 < m_rowsection.size() ) { m_rowsection[ idx + 1 ].append( mark ); } m_paintOrder.append( mark ); QRectF const boundingBox = mark->boundingBox(); Q_ASSERT(!boundingBox.isEmpty()); m_labelArea += boundingBox.width() * boundingBox.height(); m_maxLabelHeight = qMax(m_maxLabelHeight, qCeil(boundingBox.height())); return true; } GeoDataCoordinates PlacemarkLayout::placemarkIconCoordinates( const GeoDataPlacemark *placemark ) const { GeoDataCoordinates coordinates = placemark->coordinate( m_clock->dateTime()); if (!m_acceptedVisualCategories.contains(placemark->visualCategory())) { StyleParameters parameters; parameters.placemark = placemark; auto style = m_styleBuilder->createStyle(parameters); if (style->iconStyle().scaledIcon().isNull()) { return GeoDataCoordinates(); } } return coordinates; } QRectF PlacemarkLayout::roomForLabel( const GeoDataStyle::ConstPtr &style, const qreal x, const qreal y, const QString &labelText, const VisiblePlacemark* placemark) const { QFont labelFont = style->labelStyle().scaledFont(); int textHeight = QFontMetrics( labelFont ).height(); int textWidth; if ( style->labelStyle().glow() ) { labelFont.setWeight( 75 ); // Needed to calculate the correct pixmap size; textWidth = ( QFontMetrics( labelFont ).width( labelText ) + qRound( 2 * s_labelOutlineWidth ) ); } else { textWidth = ( QFontMetrics( labelFont ).width( labelText ) ); } const QVector currentsec = m_rowsection.at( y / m_maxLabelHeight ); QRectF const symbolRect = placemark->symbolRect(); if ( style->labelStyle().alignment() == GeoDataLabelStyle::Corner ) { const int symbolWidth = style->iconStyle().scaledIcon().size().width(); // Check the four possible positions by going through all of them for( int i=0; i<4; ++i ) { const qreal xPos = ( i/2 == 0 ) ? x + symbolWidth / 2 + 1 : x - symbolWidth / 2 - 1 - textWidth; const qreal yPos = ( i%2 == 0 ) ? y : y - textHeight; const QRectF labelRect = QRectF( xPos, yPos, textWidth, textHeight ); if (hasRoomFor(currentsec, labelRect.united(symbolRect))) { // claim the place immediately if it hasn't been used yet return labelRect; } } } else if ( style->labelStyle().alignment() == GeoDataLabelStyle::Center ) { int const offsetY = style->iconStyle().scaledIcon().height() / 2.0; QRectF labelRect = QRectF( x - textWidth / 2, y - offsetY - textHeight, textWidth, textHeight ); if (hasRoomFor(currentsec, labelRect.united(symbolRect))) { // claim the place immediately if it hasn't been used yet return labelRect; } } else if (style->labelStyle().alignment() == GeoDataLabelStyle::Right) { const int symbolWidth = style->iconStyle().scaledIcon().width(); const qreal startY = y - textHeight/2; const qreal xPos = x + symbolWidth / 2 + 1; // Check up to seven vertical positions (center, +3, -3 from center) for(int i=0; i<7; ++i) { const qreal increase = (i/2) * (textHeight + 1); //intentional integer arithmetics const qreal direction = (i%2 == 0 ? 1 : -1); const qreal yPos = startY + increase*direction; const QRectF labelRect = QRectF(xPos, yPos, textWidth, textHeight); if (hasRoomFor(currentsec, labelRect.united(symbolRect))) { return labelRect; } } } // At this point there is no space left for the rectangle anymore. return QRectF(); } bool PlacemarkLayout::hasRoomForPixmap(const qreal y, const VisiblePlacemark *placemark) const { const QVector currentsec = m_rowsection.at(y / m_maxLabelHeight); return hasRoomFor(currentsec, placemark->symbolRect()); } bool PlacemarkLayout::placemarksOnScreenLimit( const QSize &screenSize ) const { int ratio = ( m_labelArea * 100 ) / ( screenSize.width() * screenSize.height() ); return ratio >= 40; } } #include "moc_PlacemarkLayout.cpp" diff --git a/src/lib/marble/PlacemarkLayout.h b/src/lib/marble/PlacemarkLayout.h index d48c4591d..7a410691c 100644 --- a/src/lib/marble/PlacemarkLayout.h +++ b/src/lib/marble/PlacemarkLayout.h @@ -1,174 +1,175 @@ // // 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 2006-2007 Torsten Rahn // Copyright 2007 Inge Wallin // Copyright 2010-2012 Bernhard Beschow // // // PlacemarkLayout is responsible for drawing the Placemarks on the map // #ifndef MARBLE_PLACEMARKLAYOUT_H #define MARBLE_PLACEMARKLAYOUT_H #include #include #include #include #include #include "GeoDataPlacemark.h" #include class QAbstractItemModel; class QItemSelectionModel; class QPoint; class QModelIndex; namespace Marble { class GeoDataCoordinates; class GeoPainter; class MarbleClock; class PlacemarkPainter; class TileId; class VisiblePlacemark; class ViewportParams; class StyleBuilder; /** * Layouts the place marks with a passed QPainter. */ class PlacemarkLayout : public QObject { Q_OBJECT public: /** * Creates a new place mark layout. */ PlacemarkLayout( QAbstractItemModel *placemarkModel, QItemSelectionModel *selectionModel, MarbleClock *clock, const StyleBuilder* styleBuilder, QObject *parent = 0 ); /** * Destroys the place mark painter. */ ~PlacemarkLayout() override; /** * @reimp */ QVector generateLayout(const ViewportParams *viewport , int tileLevel); /** * Returns a list of model indexes that are at position @p pos. */ QVector whichPlacemarkAt( const QPoint &pos ); QString runtimeTrace() const; QList visiblePlacemarks() const; bool hasPlacemarkAt(const QPoint &pos); public Q_SLOTS: // earth void setShowPlaces( bool show ); void setShowCities( bool show ); void setShowTerrain( bool show ); void setShowOtherPlaces( bool show ); // other planets void setShowLandingSites( bool show ); void setShowCraters( bool show ); void setShowMaria( bool show ); void requestStyleReset(); void addPlacemarks( const QModelIndex& index, int first, int last ); void removePlacemarks( const QModelIndex& index, int first, int last ); void resetCacheData(); Q_SIGNALS: void repaintNeeded(); private: /** * Returns a the maximum height of all possible labels. * WARNING: This is a really slow method as it traverses all placemarks * to check the labelheight. * FIXME: Once a StyleManager that manages all styles has been implemented * just traverse all existing styles. */ int maxLabelHeight() const; void styleReset(); + void clearCache(); QSet visibleTiles( const ViewportParams *viewport, int tileLevel ) const; bool layoutPlacemark(const GeoDataPlacemark *placemark, const GeoDataCoordinates &coordinates, qreal x, qreal y, bool selected ); /** * Returns the coordinates at which an icon should be drawn for the @p placemark. * @p ok is set to true if the coordinates are valid and should be used for drawing, * it it set to false otherwise. */ GeoDataCoordinates placemarkIconCoordinates( const GeoDataPlacemark *placemark ) const; QRectF roomForLabel(const GeoDataStyle::ConstPtr &style, const qreal x, const qreal y, const QString &labelText , const VisiblePlacemark *placemark) const; bool hasRoomForPixmap(const qreal y, const VisiblePlacemark *placemark) const; bool placemarksOnScreenLimit( const QSize &screenSize ) const; private: Q_DISABLE_COPY( PlacemarkLayout ) QAbstractItemModel* m_placemarkModel; QItemSelectionModel *const m_selectionModel; MarbleClock *const m_clock; QVector m_paintOrder; QString m_runtimeTrace; int m_labelArea; QHash m_visiblePlacemarks; QVector< QVector< VisiblePlacemark* > > m_rowsection; /// map providing the list of placemark belonging in TileId as key QMap > m_placemarkCache; QSet m_osmIds; const QSet m_acceptedVisualCategories; // earth bool m_showPlaces; bool m_showCities; bool m_showTerrain; bool m_showOtherPlaces; // other planets bool m_showLandingSites; bool m_showCraters; bool m_showMaria; int m_maxLabelHeight; bool m_styleResetRequested; const StyleBuilder* m_styleBuilder; VisiblePlacemark* m_lastPlacemarkAt; }; } #endif