8 #include "PlacemarkLayout.h"
10 #include <QAbstractItemModel>
13 #include <QVectorIterator>
15 #include <QFontMetrics>
16 #include <QItemSelectionModel>
19 #include "GeoDataLatLonAltBox.h"
20 #include "GeoDataLatLonBox.h"
21 #include "GeoDataStyle.h"
22 #include "GeoDataIconStyle.h"
23 #include "GeoDataLabelStyle.h"
24 #include "OsmPlacemarkData.h"
26 #include "MarbleDebug.h"
27 #include "MarbleGlobal.h"
28 #include "PlacemarkLayer.h"
29 #include "MarbleClock.h"
30 #include "MarblePlacemarkModel.h"
31 #include "MarbleDirs.h"
34 #include "TileCoordsPyramid.h"
35 #include "VisiblePlacemark.h"
36 #include "MathHelper.h"
37 #include <StyleBuilder.h>
46 beforeIt != beforeItEnd; ++beforeIt )
48 if ( boundingBox.
intersects( (*beforeIt)->boundingBox() ) ) {
64 << GeoDataPlacemark::SmallCity
65 << GeoDataPlacemark::SmallCountyCapital
66 << GeoDataPlacemark::SmallStateCapital
67 << GeoDataPlacemark::SmallNationCapital
68 << GeoDataPlacemark::MediumCity
69 << GeoDataPlacemark::MediumCountyCapital
70 << GeoDataPlacemark::MediumStateCapital
71 << GeoDataPlacemark::MediumNationCapital
72 << GeoDataPlacemark::BigCity
73 << GeoDataPlacemark::BigCountyCapital
74 << GeoDataPlacemark::BigStateCapital
75 << GeoDataPlacemark::BigNationCapital
76 << GeoDataPlacemark::LargeCity
77 << GeoDataPlacemark::LargeCountyCapital
78 << GeoDataPlacemark::LargeStateCapital
79 << GeoDataPlacemark::LargeNationCapital
80 << GeoDataPlacemark::Nation
81 << GeoDataPlacemark::Mountain
82 << GeoDataPlacemark::Volcano
83 << GeoDataPlacemark::Mons
84 << GeoDataPlacemark::Valley
85 << GeoDataPlacemark::Continent
86 << GeoDataPlacemark::Ocean
87 << GeoDataPlacemark::OtherTerrain
88 << GeoDataPlacemark::Crater
89 << GeoDataPlacemark::Mare
90 << GeoDataPlacemark::GeographicPole
91 << GeoDataPlacemark::MagneticPole
92 << GeoDataPlacemark::ShipWreck
93 << GeoDataPlacemark::PlaceSuburb
94 << GeoDataPlacemark::PlaceHamlet
95 << GeoDataPlacemark::PlaceLocality;
97 return visualCategories;
104 const StyleBuilder *styleBuilder,
107 m_placemarkModel(placemarkModel),
108 m_selectionModel( selectionModel ),
110 m_acceptedVisualCategories( acceptedVisualCategories() ),
111 m_showPlaces( false ),
112 m_showCities( false ),
113 m_showTerrain( false ),
114 m_showOtherPlaces( false ),
115 m_showLandingSites( false ),
116 m_showCraters( false ),
117 m_showMaria( false ),
118 m_maxLabelHeight(maxLabelHeight()),
119 m_styleResetRequested( true ),
120 m_styleBuilder(styleBuilder),
121 m_lastPlacemarkAvailable(false)
123 Q_ASSERT(m_placemarkModel);
127 this, SLOT(requestStyleReset()) );
130 this, SLOT(resetCacheData()) );
134 this, SLOT(removePlacemarks(
QModelIndex,
int,
int)) );
135 connect( m_placemarkModel, SIGNAL(modelReset()),
136 this, SLOT(resetCacheData()) );
144 void PlacemarkLayout::setShowPlaces(
bool show )
149 void PlacemarkLayout::setShowCities(
bool show )
154 void PlacemarkLayout::setShowTerrain(
bool show )
156 m_showTerrain = show;
159 void PlacemarkLayout::setShowOtherPlaces(
bool show )
161 m_showOtherPlaces = show;
164 void PlacemarkLayout::setShowLandingSites(
bool show )
166 m_showLandingSites = show;
169 void PlacemarkLayout::setShowCraters(
bool show )
171 m_showCraters = show;
174 void PlacemarkLayout::setShowMaria(
bool show )
179 void PlacemarkLayout::requestStyleReset()
181 mDebug() <<
"Style reset requested.";
182 m_styleResetRequested =
true;
185 void PlacemarkLayout::styleReset()
188 m_maxLabelHeight = maxLabelHeight();
189 m_styleResetRequested =
false;
192 void PlacemarkLayout::clearCache()
194 m_paintOrder.clear();
195 m_lastPlacemarkAvailable =
false;
196 m_lastPlacemarkLabelRect =
QRectF();
197 m_lastPlacemarkSymbolRect =
QRectF();
199 qDeleteAll( m_visiblePlacemarks );
200 m_visiblePlacemarks.clear();
205 if ( m_styleResetRequested ) {
212 if ( mark->labelRect().contains( curpos ) || mark->symbolRect().contains( curpos ) ) {
213 ret.
append( mark->placemark() );
220 int PlacemarkLayout::maxLabelHeight()
222 QFont const standardFont(QStringLiteral(
"Sans Serif"));
229 Q_ASSERT( first < m_placemarkModel->rowCount() );
230 Q_ASSERT( last < m_placemarkModel->rowCount() );
231 for(
int i=first; i<=last; ++i ) {
235 if (
const GeoDataPlacemark *placemark = geodata_cast<GeoDataPlacemark>(
object)) {
237 if ( !coordinates.
isValid() ) {
241 if (placemark->hasOsmData()) {
242 qint64
const osmId = placemark->osmData().id();
251 int zoomLevel = placemark->zoomLevel();
252 TileId key = TileId::fromCoordinates( coordinates, zoomLevel );
253 m_placemarkCache[key].append( placemark );
256 emit repaintNeeded();
259 void PlacemarkLayout::removePlacemarks(
const QModelIndex& parent,
int first,
int last )
261 Q_ASSERT( first < m_placemarkModel->rowCount() );
262 Q_ASSERT( last < m_placemarkModel->rowCount() );
263 for(
int i=first; i<=last; ++i ) {
268 if ( !coordinates.
isValid() ) {
273 TileId key = TileId::fromCoordinates( coordinates, zoomLevel );
274 delete m_visiblePlacemarks[placemark];
275 m_visiblePlacemarks.remove(placemark);
276 m_placemarkCache[key].removeAll( placemark );
277 if (placemark->hasOsmData()) {
278 qint64
const osmId = placemark->
osmData().id();
284 emit repaintNeeded();
287 void PlacemarkLayout::resetCacheData()
289 const int rowCount = m_placemarkModel->
rowCount();
292 m_placemarkCache.clear();
293 qDeleteAll(m_visiblePlacemarks);
294 m_visiblePlacemarks.clear();
297 emit repaintNeeded();
300 QSet<TileId> PlacemarkLayout::visibleTiles(
const ViewportParams &viewport,
int zoomLevel)
309 qreal north, south, east, west;
310 viewport.viewLatLonAltBox().boundaries(north, south, east, west);
314 geoRects << GeoDataLatLonBox(north, south, east, west);
316 geoRects << GeoDataLatLonBox(north, south, M_PI, west);
317 geoRects << GeoDataLatLonBox(north, south, east, -M_PI);
320 for (
const GeoDataLatLonBox &geoRect : geoRects) {
324 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.west(), north), zoomLevel);
328 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.east(), south), zoomLevel);
332 TileCoordsPyramid pyramid(0, zoomLevel );
333 pyramid.setBottomLevelCoords( rect );
335 for (
int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) {
336 QRect const coords = pyramid.coords( level );
339 for (
int x = x1; x <= x2; ++x ) {
340 for (
int y = y1; y <= y2; ++y ) {
341 TileId
const tileId( 0, level, x, y );
353 m_runtimeTrace.
clear();
354 if ( m_placemarkModel->
rowCount() <= 0 ) {
359 if ( m_styleResetRequested ) {
363 if ( m_maxLabelHeight == 0 ) {
369 int currentMaxLabelHeight;
371 currentMaxLabelHeight = m_maxLabelHeight;
372 const int secnumber = viewport->height() / m_maxLabelHeight + 1;
373 m_rowsection.clear();
374 m_rowsection.resize(secnumber);
376 m_paintOrder.clear();
377 m_lastPlacemarkAvailable =
false;
378 m_lastPlacemarkLabelRect =
QRectF();
379 m_lastPlacemarkSymbolRect =
QRectF();
384 const QModelIndexList selectedIndexes = m_selectionModel->
selection().
indexes();
385 auto const viewLatLonAltBox = viewport->viewLatLonAltBox();
387 for (
int i = 0; i < selectedIndexes.count(); ++i ) {
392 if ( !coordinates.
isValid() ) {
399 if ( !viewLatLonAltBox.contains( coordinates ) ||
405 if( layoutPlacemark( placemark, coordinates, x, y,
true) ) {
408 if ( placemarksOnScreenLimit( viewport->size() ) )
418 placemarkList.
clear();
419 for (
const TileId &tileId: visibleTiles(*viewport, tileLevel)) {
420 placemarkList += m_placemarkCache.value( tileId );
422 std::sort(placemarkList.
begin(), placemarkList.
end(), GeoDataPlacemark::placemarkLayoutOrderCompare);
426 if ( !coordinates.
isValid() ) {
431 if ( zoomLevel > 20 ) {
438 if ( !viewLatLonAltBox.contains( coordinates ) ||
451 && visualCategory >= GeoDataPlacemark::SmallCity
452 && visualCategory <= GeoDataPlacemark::Nation )
457 && visualCategory >= GeoDataPlacemark::Mountain
458 && visualCategory <= GeoDataPlacemark::OtherTerrain )
462 if ( !m_showOtherPlaces
463 && visualCategory >= GeoDataPlacemark::GeographicPole
464 && visualCategory <= GeoDataPlacemark::Observatory )
468 if ( !m_showLandingSites
469 && visualCategory >= GeoDataPlacemark::MannedLandingSite
470 && visualCategory <= GeoDataPlacemark::UnmannedHardLandingSite )
475 && visualCategory == GeoDataPlacemark::Crater )
480 && visualCategory == GeoDataPlacemark::Mare )
484 && visualCategory >= GeoDataPlacemark::GeographicPole
485 && visualCategory <= GeoDataPlacemark::Observatory )
491 bool isSelected =
false;
494 if (mark == placemark ) {
502 if( layoutPlacemark( placemark, coordinates, x, y, isSelected ) ) {
505 if ( placemarksOnScreenLimit( viewport->size() ) )
510 if (m_visiblePlacemarks.size() > qMax(100, 4 * m_paintOrder.size())) {
511 auto const extendedBox = viewLatLonAltBox.scaled(2.0, 2.0);
513 for (
auto placemark: m_visiblePlacemarks) {
514 if (!extendedBox.contains(placemark->coordinates())) {
515 outdated << placemark;
518 for (
auto placemark: outdated) {
519 delete m_visiblePlacemarks.take(placemark->placemark());
522 }
while (currentMaxLabelHeight != m_maxLabelHeight);
524 m_runtimeTrace = QStringLiteral(
"Placemarks: %1 Drawn: %2").
arg(placemarkList.
count()).
arg(m_paintOrder.size());
528 QString PlacemarkLayout::runtimeTrace()
const
530 return m_runtimeTrace;
535 return m_visiblePlacemarks.values();
538 bool PlacemarkLayout::hasPlacemarkAt(
const QPoint &pos)
540 if ( m_styleResetRequested ) {
544 if (m_lastPlacemarkAvailable &&
545 (m_lastPlacemarkLabelRect.
contains(pos) || m_lastPlacemarkSymbolRect.
contains(pos))) {
549 for(VisiblePlacemark* mark: m_paintOrder) {
550 if (mark->labelRect().contains(pos) || mark->symbolRect().contains(pos)) {
551 m_lastPlacemarkLabelRect = mark->labelRect();
552 m_lastPlacemarkSymbolRect = mark->symbolRect();
553 m_lastPlacemarkAvailable =
true;
561 bool PlacemarkLayout::layoutPlacemark(
const GeoDataPlacemark *placemark,
const GeoDataCoordinates &coordinates, qreal x, qreal y,
bool selected )
564 VisiblePlacemark *mark = m_visiblePlacemarks.value( placemark );
568 StyleParameters parameters;
570 parameters.placemark = placemark;
572 auto style = m_styleBuilder->createStyle(parameters);
573 mark =
new VisiblePlacemark(placemark, coordinates, style);
574 m_visiblePlacemarks.insert( placemark, mark );
575 connect( mark, SIGNAL(updateNeeded()),
this, SIGNAL(repaintNeeded()) );
577 GeoDataStyle::ConstPtr style = mark->style();
581 QPointF hotSpot = mark->hotSpot();
582 mark->setSelected(selected);
583 mark->setSymbolPosition(
QPointF(x - hotSpot.
x(), y - hotSpot.
y()));
588 const QString labelText = placemark->displayName();
591 labelRect = roomForLabel(style, x, y, labelText, mark);
593 if (labelRect.
isEmpty() && mark->symbolPixmap().isNull()) {
596 if (!mark->symbolPixmap().isNull() && !hasRoomForPixmap(y, mark)) {
600 mark->setLabelRect( labelRect );
604 int idx = y / m_maxLabelHeight;
605 if ( idx - 1 >= 0 ) {
606 m_rowsection[ idx - 1 ].append( mark );
608 m_rowsection[ idx ].append( mark );
609 if ( idx + 1 < m_rowsection.size() ) {
610 m_rowsection[ idx + 1 ].append( mark );
613 m_paintOrder.append( mark );
614 QRectF const boundingBox = mark->boundingBox();
615 Q_ASSERT(!boundingBox.
isEmpty());
616 m_labelArea += boundingBox.
width() * boundingBox.
height();
617 m_maxLabelHeight = qMax(m_maxLabelHeight, qCeil(boundingBox.
height()));
621 GeoDataCoordinates PlacemarkLayout::placemarkIconCoordinates(
const GeoDataPlacemark *placemark )
const
623 GeoDataCoordinates coordinates = placemark->coordinate( m_clock->dateTime());
624 if (!m_acceptedVisualCategories.
contains(placemark->visualCategory())) {
625 StyleParameters parameters;
626 parameters.placemark = placemark;
627 auto style = m_styleBuilder->createStyle(parameters);
628 if (style->iconStyle().scaledIcon().isNull()) {
629 return GeoDataCoordinates();
636 QRectF PlacemarkLayout::roomForLabel(
const GeoDataStyle::ConstPtr &style,
637 const qreal x,
const qreal y,
639 const VisiblePlacemark* placemark)
const
641 QFont labelFont = style->labelStyle().scaledFont();
645 if ( style->labelStyle().glow() ) {
648 + qRound( 2 * s_labelOutlineWidth ) );
654 QRectF const symbolRect = placemark->symbolRect();
656 if ( style->labelStyle().alignment() == GeoDataLabelStyle::Corner ) {
657 const int symbolWidth = style->iconStyle().scaledIcon().
size().
width();
660 for(
int i=0; i<4; ++i ) {
661 const qreal xPos = ( i/2 == 0 ) ? x + symbolWidth / 2 + 1 :
662 x - symbolWidth / 2 - 1 - textWidth;
663 const qreal yPos = ( i%2 == 0 ) ? y :
665 const QRectF labelRect =
QRectF( xPos, yPos, textWidth, textHeight );
667 if (hasRoomFor(currentsec, labelRect.
united(symbolRect))) {
673 else if ( style->labelStyle().alignment() == GeoDataLabelStyle::Center ) {
674 int const offsetY = style->iconStyle().scaledIcon().
height() / 2.0;
675 QRectF labelRect =
QRectF( x - textWidth / 2, y - offsetY - textHeight,
676 textWidth, textHeight );
678 if (hasRoomFor(currentsec, labelRect.
united(symbolRect))) {
683 else if (style->labelStyle().alignment() == GeoDataLabelStyle::Right)
685 const int symbolWidth = style->iconStyle().scaledIcon().
width();
686 const qreal startY = y - textHeight/2;
687 const qreal xPos = x + symbolWidth / 2 + 1;
690 for(
int i=0; i<7; ++i)
692 const qreal increase = (i/2) * (textHeight + 1);
693 const qreal direction = (i%2 == 0 ? 1 : -1);
694 const qreal yPos = startY + increase*direction;
696 const QRectF labelRect =
QRectF(xPos, yPos, textWidth, textHeight);
698 if (hasRoomFor(currentsec, labelRect.
united(symbolRect)))
709 bool PlacemarkLayout::hasRoomForPixmap(
const qreal y,
const VisiblePlacemark *placemark)
const
712 return hasRoomFor(currentsec, placemark->symbolRect());
715 bool PlacemarkLayout::placemarksOnScreenLimit(
const QSize &screenSize )
const
717 int ratio = ( m_labelArea * 100 ) / ( screenSize.
width() * screenSize.
height() );
723 #include "moc_PlacemarkLayout.cpp"