Marble

PlacemarkLayout.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2006-2007 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007-2008 Inge Wallin <ingwa@kde.org>
5// SPDX-FileCopyrightText: 2010-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6//
7
8#include "PlacemarkLayout.h"
9
10#include <QAbstractItemModel>
11#include <QList>
12#include <QPoint>
13#include <QVectorIterator>
14#include <QFont>
15#include <QFontMetrics>
16#include <QItemSelectionModel>
17#include <qmath.h>
18
19#include "GeoDataLatLonAltBox.h"
20#include "GeoDataLatLonBox.h"
21#include "GeoDataStyle.h"
22#include "GeoDataIconStyle.h"
23#include "GeoDataLabelStyle.h"
24#include "OsmPlacemarkData.h"
25
26#include "MarbleDebug.h"
27#include "MarbleGlobal.h"
28#include "PlacemarkLayer.h"
29#include "MarbleClock.h"
30#include "MarblePlacemarkModel.h"
31#include "MarbleDirs.h"
32#include "ViewportParams.h"
33#include "TileId.h"
34#include "TileCoordsPyramid.h"
35#include "VisiblePlacemark.h"
36#include "MathHelper.h"
37#include <StyleBuilder.h>
38
39namespace
40{ //Helper function that checks for available room for the label
41 bool hasRoomFor(const QVector<Marble::VisiblePlacemark*> & placemarks, const QRectF &boundingBox)
42 {
43 // Check if there is another label or symbol that overlaps.
46 beforeIt != beforeItEnd; ++beforeIt )
47 {
48 if ( boundingBox.intersects( (*beforeIt)->boundingBox() ) ) {
49 return false;
50 }
51 }
52 return true;
53 }
54}
55
56namespace Marble
57{
58
59QSet<GeoDataPlacemark::GeoDataVisualCategory> acceptedVisualCategories()
60{
62
63 visualCategories
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;
96
97 return visualCategories;
98}
99
100
102 QItemSelectionModel *selectionModel,
103 MarbleClock *clock,
104 const StyleBuilder *styleBuilder,
105 QObject* parent )
106 : QObject( parent ),
107 m_placemarkModel(placemarkModel),
108 m_selectionModel( selectionModel ),
109 m_clock( clock ),
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)
122{
123 Q_ASSERT(m_placemarkModel);
124
125 connect( m_selectionModel, SIGNAL( selectionChanged( QItemSelection,
127 this, SLOT(requestStyleReset()) );
128
129 connect( m_placemarkModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
130 this, SLOT(resetCacheData()) );
131 connect( m_placemarkModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
132 this, SLOT(addPlacemarks(QModelIndex,int,int)) );
133 connect( m_placemarkModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
134 this, SLOT(removePlacemarks(QModelIndex,int,int)) );
135 connect( m_placemarkModel, SIGNAL(modelReset()),
136 this, SLOT(resetCacheData()) );
137}
138
140{
141 styleReset();
142}
143
144void PlacemarkLayout::setShowPlaces( bool show )
145{
146 m_showPlaces = show;
147}
148
149void PlacemarkLayout::setShowCities( bool show )
150{
151 m_showCities = show;
152}
153
154void PlacemarkLayout::setShowTerrain( bool show )
155{
156 m_showTerrain = show;
157}
158
159void PlacemarkLayout::setShowOtherPlaces( bool show )
160{
161 m_showOtherPlaces = show;
162}
163
164void PlacemarkLayout::setShowLandingSites( bool show )
165{
166 m_showLandingSites = show;
167}
168
169void PlacemarkLayout::setShowCraters( bool show )
170{
171 m_showCraters = show;
172}
173
174void PlacemarkLayout::setShowMaria( bool show )
175{
176 m_showMaria = show;
177}
178
179void PlacemarkLayout::requestStyleReset()
180{
181 mDebug() << "Style reset requested.";
182 m_styleResetRequested = true;
183}
184
185void PlacemarkLayout::styleReset()
186{
187 clearCache();
188 m_maxLabelHeight = maxLabelHeight();
189 m_styleResetRequested = false;
190}
191
192void PlacemarkLayout::clearCache()
193{
194 m_paintOrder.clear();
195 m_lastPlacemarkAvailable = false;
196 m_lastPlacemarkLabelRect = QRectF();
197 m_lastPlacemarkSymbolRect = QRectF();
198 m_labelArea = 0;
199 qDeleteAll( m_visiblePlacemarks );
200 m_visiblePlacemarks.clear();
201}
202
204{
205 if ( m_styleResetRequested ) {
206 styleReset();
207 }
208
210
211 for( VisiblePlacemark* mark: m_paintOrder ) {
212 if ( mark->labelRect().contains( curpos ) || mark->symbolRect().contains( curpos ) ) {
213 ret.append( mark->placemark() );
214 }
215 }
216
217 return ret;
218}
219
220int PlacemarkLayout::maxLabelHeight()
221{
222 QFont const standardFont(QStringLiteral("Sans Serif"));
224}
225
226/// feed an internal QMap of placemarks with TileId as key when model changes
227void PlacemarkLayout::addPlacemarks( const QModelIndex& parent, int first, int last )
228{
231 for( int i=first; i<=last; ++i ) {
232 QModelIndex index = m_placemarkModel->index( i, 0, parent );
233 Q_ASSERT( index.isValid() );
235 if (const GeoDataPlacemark *placemark = geodata_cast<GeoDataPlacemark>(object)) {
236 const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark );
237 if ( !coordinates.isValid() ) {
238 continue;
239 }
240
241 if (placemark->hasOsmData()) {
242 qint64 const osmId = placemark->osmData().id();
243 if (osmId > 0) {
244 if (m_osmIds.contains(osmId)) {
245 continue; // placemark is already shown
246 }
247 m_osmIds << osmId;
248 }
249 }
250
251 int zoomLevel = placemark->zoomLevel();
252 TileId key = TileId::fromCoordinates( coordinates, zoomLevel );
253 m_placemarkCache[key].append( placemark );
254 }
255 }
256 emit repaintNeeded();
257}
258
259void PlacemarkLayout::removePlacemarks( const QModelIndex& parent, int first, int last )
260{
263 for( int i=first; i<=last; ++i ) {
264 QModelIndex index = m_placemarkModel->index( i, 0, parent );
265 Q_ASSERT( index.isValid() );
267 const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark );
268 if ( !coordinates.isValid() ) {
269 continue;
270 }
271
272 int zoomLevel = placemark->zoomLevel();
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();
279 if (osmId > 0) {
280 m_osmIds.remove(osmId);
281 }
282 }
283 }
284 emit repaintNeeded();
285}
286
287void PlacemarkLayout::resetCacheData()
288{
289 const int rowCount = m_placemarkModel->rowCount();
290
291 m_osmIds.clear();
292 m_placemarkCache.clear();
293 qDeleteAll(m_visiblePlacemarks);
294 m_visiblePlacemarks.clear();
295 requestStyleReset();
296 addPlacemarks( m_placemarkModel->index( 0, 0 ), 0, rowCount );
297 emit repaintNeeded();
298}
299
300QSet<TileId> PlacemarkLayout::visibleTiles(const ViewportParams &viewport, int zoomLevel)
301{
302 /*
303 * rely on m_placemarkCache to find the placemarks for the tiles which
304 * matter. The top level tiles have the more popular placemarks,
305 * the bottom level tiles have the smaller ones, and we only get the ones
306 * matching our latLonAltBox.
307 */
308
309 qreal north, south, east, west;
310 viewport.viewLatLonAltBox().boundaries(north, south, east, west);
313 if( west <= east ) {
314 geoRects << GeoDataLatLonBox(north, south, east, west);
315 } else {
316 geoRects << GeoDataLatLonBox(north, south, M_PI, west);
317 geoRects << GeoDataLatLonBox(north, south, east, -M_PI);
318 }
319
320 for (const GeoDataLatLonBox &geoRect : geoRects) {
321 TileId key;
322 QRect rect;
323
324 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.west(), north), zoomLevel);
325 rect.setLeft( key.x() );
326 rect.setTop( key.y() );
327
328 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.east(), south), zoomLevel);
329 rect.setRight( key.x() );
330 rect.setBottom( key.y() );
331
332 TileCoordsPyramid pyramid(0, zoomLevel );
333 pyramid.setBottomLevelCoords( rect );
334
335 for ( int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) {
336 QRect const coords = pyramid.coords( level );
337 int x1, y1, x2, y2;
338 coords.getCoords( &x1, &y1, &x2, &y2 );
339 for ( int x = x1; x <= x2; ++x ) {
340 for ( int y = y1; y <= y2; ++y ) {
341 TileId const tileId( 0, level, x, y );
342 tileIdSet.insert(tileId);
343 }
344 }
345 }
346 }
347
348 return tileIdSet;
349}
350
352{
353 m_runtimeTrace.clear();
354 if ( m_placemarkModel->rowCount() <= 0 ) {
355 clearCache();
357 }
358
359 if ( m_styleResetRequested ) {
360 styleReset();
361 }
362
363 if ( m_maxLabelHeight == 0 ) {
364 clearCache();
366 }
367
368 QList<const GeoDataPlacemark*> placemarkList;
370 do {
371 currentMaxLabelHeight = m_maxLabelHeight;
372 const int secnumber = viewport->height() / m_maxLabelHeight + 1;
373 m_rowsection.clear();
374 m_rowsection.resize(secnumber);
375
376 m_paintOrder.clear();
377 m_lastPlacemarkAvailable = false;
378 m_lastPlacemarkLabelRect = QRectF();
379 m_lastPlacemarkSymbolRect = QRectF();
380 m_labelArea = 0;
381
382 // First handle the selected placemarks as they have the highest priority.
383
384 const QModelIndexList selectedIndexes = m_selectionModel->selection().indexes();
385 auto const viewLatLonAltBox = viewport->viewLatLonAltBox();
386
387 for ( int i = 0; i < selectedIndexes.count(); ++i ) {
388 const QModelIndex index = selectedIndexes.at( i );
390 const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark );
391
392 if ( !coordinates.isValid() ) {
393 continue;
394 }
395
396 qreal x = 0;
397 qreal y = 0;
398
399 if ( !viewLatLonAltBox.contains( coordinates ) ||
400 ! viewport->screenCoordinates( coordinates, x, y ))
401 {
402 continue;
403 }
404
405 if( layoutPlacemark( placemark, coordinates, x, y, true) ) {
406 // Make sure not to draw more placemarks on the screen than
407 // specified by placemarksOnScreenLimit().
408 if ( placemarksOnScreenLimit( viewport->size() ) )
409 break;
410 }
411
412 }
413
414 // Now handle all other placemarks...
415
416 const QItemSelection selection = m_selectionModel->selection();
417
418 placemarkList.clear();
419 for (const TileId &tileId: visibleTiles(*viewport, tileLevel)) {
420 placemarkList += m_placemarkCache.value( tileId );
421 }
422 std::sort(placemarkList.begin(), placemarkList.end(), GeoDataPlacemark::placemarkLayoutOrderCompare);
423
424 for ( const GeoDataPlacemark *placemark: placemarkList ) {
425 const GeoDataCoordinates coordinates = placemarkIconCoordinates( placemark );
426 if ( !coordinates.isValid() ) {
427 continue;
428 }
429
430 int zoomLevel = placemark->zoomLevel();
431 if ( zoomLevel > 20 ) {
432 break;
433 }
434
435 qreal x = 0;
436 qreal y = 0;
437
438 if ( !viewLatLonAltBox.contains( coordinates ) ||
439 ! viewport->screenCoordinates( coordinates, x, y )) {
440 continue;
441 }
442
443 if ( !placemark->isGloballyVisible() ) {
444 continue;
445 }
446
447 const GeoDataPlacemark::GeoDataVisualCategory visualCategory = placemark->visualCategory();
448
449 // Skip city marks if we're not showing cities.
450 if ( !m_showCities
451 && visualCategory >= GeoDataPlacemark::SmallCity
452 && visualCategory <= GeoDataPlacemark::Nation )
453 continue;
454
455 // Skip terrain marks if we're not showing terrain.
456 if ( !m_showTerrain
457 && visualCategory >= GeoDataPlacemark::Mountain
458 && visualCategory <= GeoDataPlacemark::OtherTerrain )
459 continue;
460
461 // Skip other places if we're not showing other places.
462 if ( !m_showOtherPlaces
463 && visualCategory >= GeoDataPlacemark::GeographicPole
464 && visualCategory <= GeoDataPlacemark::Observatory )
465 continue;
466
467 // Skip landing sites if we're not showing landing sites.
468 if ( !m_showLandingSites
469 && visualCategory >= GeoDataPlacemark::MannedLandingSite
470 && visualCategory <= GeoDataPlacemark::UnmannedHardLandingSite )
471 continue;
472
473 // Skip craters if we're not showing craters.
474 if ( !m_showCraters
475 && visualCategory == GeoDataPlacemark::Crater )
476 continue;
477
478 // Skip maria if we're not showing maria.
479 if ( !m_showMaria
480 && visualCategory == GeoDataPlacemark::Mare )
481 continue;
482
483 if ( !m_showPlaces
484 && visualCategory >= GeoDataPlacemark::GeographicPole
485 && visualCategory <= GeoDataPlacemark::Observatory )
486 continue;
487
488 // We handled selected placemarks already, so we skip them here...
489 // Assuming that only a small amount of places is selected
490 // we check for the selected state after all other filters
491 bool isSelected = false;
492 for ( const QModelIndex &index: selection.indexes() ) {
494 if (mark == placemark ) {
495 isSelected = true;
496 break;
497 }
498 }
499 if ( isSelected )
500 continue;
501
502 if( layoutPlacemark( placemark, coordinates, x, y, isSelected ) ) {
503 // Make sure not to draw more placemarks on the screen than
504 // specified by placemarksOnScreenLimit().
505 if ( placemarksOnScreenLimit( viewport->size() ) )
506 break;
507 }
508 }
509
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;
516 }
517 }
518 for (auto placemark: outdated) {
519 delete m_visiblePlacemarks.take(placemark->placemark());
520 }
521 }
522 } while (currentMaxLabelHeight != m_maxLabelHeight);
523
524 m_runtimeTrace = QStringLiteral("Placemarks: %1 Drawn: %2").arg(placemarkList.count()).arg(m_paintOrder.size());
525 return m_paintOrder;
526}
527
528QString PlacemarkLayout::runtimeTrace() const
529{
530 return m_runtimeTrace;
531}
532
533QList<VisiblePlacemark *> PlacemarkLayout::visiblePlacemarks() const
534{
535 return m_visiblePlacemarks.values();
536}
537
538bool PlacemarkLayout::hasPlacemarkAt(const QPoint &pos)
539{
540 if ( m_styleResetRequested ) {
541 styleReset();
542 }
543
544 if (m_lastPlacemarkAvailable &&
545 (m_lastPlacemarkLabelRect.contains(pos) || m_lastPlacemarkSymbolRect.contains(pos))) {
546 return true;
547 }
548
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;
554 return true;
555 }
556 }
557
558 return false;
559}
560
561bool PlacemarkLayout::layoutPlacemark( const GeoDataPlacemark *placemark, const GeoDataCoordinates &coordinates, qreal x, qreal y, bool selected )
562{
563 // Find the corresponding visible placemark
564 VisiblePlacemark *mark = m_visiblePlacemarks.value( placemark );
565 if ( !mark ) {
566 // If there is no visible placemark yet for this index,
567 // create a new one...
568 StyleParameters parameters;
569 // @todo: Set / adjust to tile level
570 parameters.placemark = placemark;
571
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()) );
576 }
577 GeoDataStyle::ConstPtr style = mark->style();
578
579 // Choose Section
580
581 QPointF hotSpot = mark->hotSpot();
582 mark->setSelected(selected);
583 mark->setSymbolPosition(QPointF(x - hotSpot.x(), y - hotSpot.y()));
584
585 // Find out whether the area around the placemark is covered already.
586 // If there's not enough space free don't add a VisiblePlacemark here.
587
588 const QString labelText = placemark->displayName();
589 QRectF labelRect;
590 if (!labelText.isEmpty()) {
591 labelRect = roomForLabel(style, x, y, labelText, mark);
592 }
593 if (labelRect.isEmpty() && mark->symbolPixmap().isNull()) {
594 return false;
595 }
596 if (!mark->symbolPixmap().isNull() && !hasRoomForPixmap(y, mark)) {
597 return false;
598 }
599
600 mark->setLabelRect( labelRect );
601
602 // Add the current placemark to the matching row and its
603 // direct neighbors.
604 int idx = y / m_maxLabelHeight;
605 if ( idx - 1 >= 0 ) {
606 m_rowsection[ idx - 1 ].append( mark );
607 }
608 m_rowsection[ idx ].append( mark );
609 if ( idx + 1 < m_rowsection.size() ) {
610 m_rowsection[ idx + 1 ].append( mark );
611 }
612
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()));
618 return true;
619}
620
621GeoDataCoordinates PlacemarkLayout::placemarkIconCoordinates( const GeoDataPlacemark *placemark ) const
622{
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();
630 }
631 }
632
633 return coordinates;
634}
635
636QRectF PlacemarkLayout::roomForLabel( const GeoDataStyle::ConstPtr &style,
637 const qreal x, const qreal y,
638 const QString &labelText,
639 const VisiblePlacemark* placemark) const
640{
641 QFont labelFont = style->labelStyle().scaledFont();
643
644 int textWidth;
645 if ( style->labelStyle().glow() ) {
646 labelFont.setWeight( 75 ); // Needed to calculate the correct pixmap size;
647 textWidth = ( QFontMetrics( labelFont ).horizontalAdvance( labelText )
648 + qRound( 2 * s_labelOutlineWidth ) );
649 } else {
650 textWidth = ( QFontMetrics( labelFont ).horizontalAdvance( labelText ) );
651 }
652
653 const QVector<VisiblePlacemark*> currentsec = m_rowsection.at( y / m_maxLabelHeight );
654 QRectF const symbolRect = placemark->symbolRect();
655
656 if ( style->labelStyle().alignment() == GeoDataLabelStyle::Corner ) {
657 const int symbolWidth = style->iconStyle().scaledIcon().size().width();
658
659 // Check the four possible positions by going through all of them
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 :
664 y - textHeight;
665 const QRectF labelRect = QRectF( xPos, yPos, textWidth, textHeight );
666
667 if (hasRoomFor(currentsec, labelRect.united(symbolRect))) {
668 // claim the place immediately if it hasn't been used yet
669 return labelRect;
670 }
671 }
672 }
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 );
677
678 if (hasRoomFor(currentsec, labelRect.united(symbolRect))) {
679 // claim the place immediately if it hasn't been used yet
680 return labelRect;
681 }
682 }
683 else if (style->labelStyle().alignment() == GeoDataLabelStyle::Right)
684 {
685 const int symbolWidth = style->iconStyle().scaledIcon().width();
686 const qreal startY = y - textHeight/2;
687 const qreal xPos = x + symbolWidth / 2 + 1;
688
689 // Check up to seven vertical positions (center, +3, -3 from center)
690 for(int i=0; i<7; ++i)
691 {
692 const qreal increase = (i/2) * (textHeight + 1); //intentional integer arithmetics
693 const qreal direction = (i%2 == 0 ? 1 : -1);
694 const qreal yPos = startY + increase*direction;
695
696 const QRectF labelRect = QRectF(xPos, yPos, textWidth, textHeight);
697
698 if (hasRoomFor(currentsec, labelRect.united(symbolRect)))
699 {
700 return labelRect;
701 }
702 }
703 }
704
705 // At this point there is no space left for the rectangle anymore.
706 return QRectF();
707}
708
709bool PlacemarkLayout::hasRoomForPixmap(const qreal y, const VisiblePlacemark *placemark) const
710{
711 const QVector<VisiblePlacemark*> currentsec = m_rowsection.at(y / m_maxLabelHeight);
712 return hasRoomFor(currentsec, placemark->symbolRect());
713}
714
715bool PlacemarkLayout::placemarksOnScreenLimit( const QSize &screenSize ) const
716{
717 int ratio = ( m_labelArea * 100 ) / ( screenSize.width() * screenSize.height() );
718 return ratio >= 40;
719}
720
721}
722
723#include "moc_PlacemarkLayout.cpp"
This file contains the headers for ViewportParams.
A 3d point representation.
bool isGloballyVisible() const
Return whether this feature is visible or not in the context of its parenting.
int zoomLevel() const
Return the popularity index of the placemark.
a class representing a point of interest on the map
GeoDataVisualCategory
A categorization of a placemark as defined by ...FIXME.
GeoDataVisualCategory visualCategory() const
Return the symbol index of the placemark.
OsmPlacemarkData & osmData()
Quick, safe accessor to the placemark's OsmPlacemarkData stored within it's ExtendedData.
@ ObjectPointerRole
The pointer to a specific object.
QVector< VisiblePlacemark * > generateLayout(const ViewportParams *viewport, int tileLevel)
QVector< const GeoDataFeature * > whichPlacemarkAt(const QPoint &pos)
Returns a list of model indexes that are at position pos.
PlacemarkLayout(QAbstractItemModel *placemarkModel, QItemSelectionModel *selectionModel, MarbleClock *clock, const StyleBuilder *styleBuilder, QObject *parent=nullptr)
Creates a new place mark layout.
void addPlacemarks(const QModelIndex &index, int first, int last)
feed an internal QMap of placemarks with TileId as key when model changes
~PlacemarkLayout() override
Destroys the place mark painter.
A public class that controls what is visible in the viewport of a Marble map.
bool screenCoordinates(const qreal lon, const qreal lat, qreal &x, qreal &y) const
Get the screen coordinates corresponding to geographical coordinates in the map.
A class which represents the visible place marks on a map.
QStringView level(QStringView ifopt)
Binds a QML item to a specific geodetic location in screen coordinates.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
int height() const const
int horizontalAdvance(QChar ch) const const
QModelIndexList indexes() const const
const QItemSelection selection() const const
iterator begin()
void clear()
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
iterator end()
QVariant data(int role) const const
bool isValid() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
T qobject_cast(QObject *object)
qreal x() const const
qreal y() const const
void getCoords(int *x1, int *y1, int *x2, int *y2) const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
bool contains(const QPointF &point) const const
qreal height() const const
bool intersects(const QRectF &rectangle) const const
bool isEmpty() const const
QRectF united(const QRectF &rectangle) const const
qreal width() const const
void clear()
bool contains(const QSet< T > &other) const const
bool remove(const T &value)
int height() const const
int width() const const
QString arg(Args &&... args) const const
void clear()
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:17 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.