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 <QFont>
12#include <QFontMetrics>
13#include <QItemSelectionModel>
14#include <QList>
15#include <QPoint>
16#include <QVectorIterator>
17#include <qmath.h>
18
19#include "GeoDataIconStyle.h"
20#include "GeoDataLabelStyle.h"
21#include "GeoDataLatLonAltBox.h"
22#include "GeoDataLatLonBox.h"
23#include "GeoDataStyle.h"
24#include "OsmPlacemarkData.h"
25
26#include "MarbleClock.h"
27#include "MarbleDebug.h"
28#include "MarbleDirs.h"
29#include "MarbleGlobal.h"
30#include "MarblePlacemarkModel.h"
31#include "MathHelper.h"
32#include "PlacemarkLayer.h"
33#include "TileCoordsPyramid.h"
34#include "TileId.h"
35#include "ViewportParams.h"
36#include "VisiblePlacemark.h"
37#include <StyleBuilder.h>
38
39namespace
40{ // Helper function that checks for available room for the label
41bool hasRoomFor(const QList<Marble::VisiblePlacemark *> &placemarks, const QRectF &boundingBox)
42{
43 // Check if there is another label or symbol that overlaps.
45 for (QList<Marble::VisiblePlacemark *>::ConstIterator beforeIt = placemarks.constBegin(); beforeIt != beforeItEnd; ++beforeIt) {
46 if (boundingBox.intersects((*beforeIt)->boundingBox())) {
47 return false;
48 }
49 }
50 return true;
51}
52}
53
54namespace Marble
55{
56
57QSet<GeoDataPlacemark::GeoDataVisualCategory> acceptedVisualCategories()
58{
60
61 visualCategories << GeoDataPlacemark::SmallCity << GeoDataPlacemark::SmallCountyCapital << GeoDataPlacemark::SmallStateCapital
62 << GeoDataPlacemark::SmallNationCapital << GeoDataPlacemark::MediumCity << GeoDataPlacemark::MediumCountyCapital
63 << GeoDataPlacemark::MediumStateCapital << GeoDataPlacemark::MediumNationCapital << GeoDataPlacemark::BigCity
64 << GeoDataPlacemark::BigCountyCapital << GeoDataPlacemark::BigStateCapital << GeoDataPlacemark::BigNationCapital
65 << GeoDataPlacemark::LargeCity << GeoDataPlacemark::LargeCountyCapital << GeoDataPlacemark::LargeStateCapital
66 << GeoDataPlacemark::LargeNationCapital << GeoDataPlacemark::Nation << GeoDataPlacemark::Mountain << GeoDataPlacemark::Volcano
67 << GeoDataPlacemark::Mons << GeoDataPlacemark::Valley << GeoDataPlacemark::Continent << GeoDataPlacemark::Ocean
68 << GeoDataPlacemark::OtherTerrain << GeoDataPlacemark::Crater << GeoDataPlacemark::Mare << GeoDataPlacemark::GeographicPole
69 << GeoDataPlacemark::MagneticPole << GeoDataPlacemark::ShipWreck << GeoDataPlacemark::PlaceSuburb << GeoDataPlacemark::PlaceHamlet
70 << GeoDataPlacemark::PlaceLocality;
71
72 return visualCategories;
73}
74
76 QItemSelectionModel *selectionModel,
77 MarbleClock *clock,
78 const StyleBuilder *styleBuilder,
79 QObject *parent)
80 : QObject(parent)
81 , m_placemarkModel(placemarkModel)
82 , m_selectionModel(selectionModel)
83 , m_clock(clock)
84 , m_acceptedVisualCategories(acceptedVisualCategories())
85 , m_showPlaces(false)
86 , m_showCities(false)
87 , m_showTerrain(false)
88 , m_showOtherPlaces(false)
89 , m_showLandingSites(false)
90 , m_showCraters(false)
91 , m_showMaria(false)
92 , m_maxLabelHeight(maxLabelHeight())
93 , m_styleResetRequested(true)
94 , m_styleBuilder(styleBuilder)
95 , m_lastPlacemarkAvailable(false)
96{
97 Q_ASSERT(m_placemarkModel);
98
99 connect(m_selectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), this, SLOT(requestStyleReset()));
100
101 connect(m_placemarkModel, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(resetCacheData()));
102 connect(m_placemarkModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(addPlacemarks(QModelIndex, int, int)));
103 connect(m_placemarkModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(removePlacemarks(QModelIndex, int, int)));
104 connect(m_placemarkModel, SIGNAL(modelReset()), this, SLOT(resetCacheData()));
105}
106
108{
109 styleReset();
110}
111
112void PlacemarkLayout::setShowPlaces(bool show)
113{
114 m_showPlaces = show;
115}
116
117void PlacemarkLayout::setShowCities(bool show)
118{
119 m_showCities = show;
120}
121
122void PlacemarkLayout::setShowTerrain(bool show)
123{
124 m_showTerrain = show;
125}
126
127void PlacemarkLayout::setShowOtherPlaces(bool show)
128{
129 m_showOtherPlaces = show;
130}
131
132void PlacemarkLayout::setShowLandingSites(bool show)
133{
134 m_showLandingSites = show;
135}
136
137void PlacemarkLayout::setShowCraters(bool show)
138{
139 m_showCraters = show;
140}
141
142void PlacemarkLayout::setShowMaria(bool show)
143{
144 m_showMaria = show;
145}
146
147void PlacemarkLayout::requestStyleReset()
148{
149 mDebug() << "Style reset requested.";
150 m_styleResetRequested = true;
151}
152
153void PlacemarkLayout::styleReset()
154{
155 clearCache();
156 m_maxLabelHeight = maxLabelHeight();
157 m_styleResetRequested = false;
158}
159
160void PlacemarkLayout::clearCache()
161{
162 m_paintOrder.clear();
163 m_lastPlacemarkAvailable = false;
164 m_lastPlacemarkLabelRect = QRectF();
165 m_lastPlacemarkSymbolRect = QRectF();
166 m_labelArea = 0;
167 qDeleteAll(m_visiblePlacemarks);
168 m_visiblePlacemarks.clear();
169}
170
172{
173 if (m_styleResetRequested) {
174 styleReset();
175 }
176
178
179 for (VisiblePlacemark *mark : std::as_const(m_paintOrder)) {
180 if (mark->labelRect().contains(curpos) || mark->symbolRect().contains(curpos)) {
181 ret.append(mark->placemark());
182 }
183 }
184
185 return ret;
186}
187
188int PlacemarkLayout::maxLabelHeight()
189{
190 QFont const standardFont(QStringLiteral("Sans Serif"));
191 return QFontMetrics(standardFont).height();
192}
193
194/// feed an internal QMap of placemarks with TileId as key when model changes
195void PlacemarkLayout::addPlacemarks(const QModelIndex &parent, int first, int last)
196{
197 Q_ASSERT(first < m_placemarkModel->rowCount());
198 Q_ASSERT(last < m_placemarkModel->rowCount());
199 for (int i = first; i <= last; ++i) {
200 QModelIndex index = m_placemarkModel->index(i, 0, parent);
201 Q_ASSERT(index.isValid());
202 auto const object = qvariant_cast<GeoDataObject *>(index.data(MarblePlacemarkModel::ObjectPointerRole));
203 if (const GeoDataPlacemark *placemark = geodata_cast<GeoDataPlacemark>(object)) {
204 const GeoDataCoordinates coordinates = placemarkIconCoordinates(placemark);
205 if (!coordinates.isValid()) {
206 continue;
207 }
208
209 if (placemark->hasOsmData()) {
210 qint64 const osmId = placemark->osmData().id();
211 if (osmId > 0) {
212 if (m_osmIds.contains(osmId)) {
213 continue; // placemark is already shown
214 }
215 m_osmIds << osmId;
216 }
217 }
218
219 int zoomLevel = placemark->zoomLevel();
220 TileId key = TileId::fromCoordinates(coordinates, zoomLevel);
221 m_placemarkCache[key].append(placemark);
222 }
223 }
224 Q_EMIT repaintNeeded();
225}
226
227void PlacemarkLayout::removePlacemarks(const QModelIndex &parent, int first, int last)
228{
229 Q_ASSERT(first < m_placemarkModel->rowCount());
230 Q_ASSERT(last < m_placemarkModel->rowCount());
231 for (int i = first; i <= last; ++i) {
232 QModelIndex index = m_placemarkModel->index(i, 0, parent);
233 Q_ASSERT(index.isValid());
234 const GeoDataPlacemark *placemark =
235 static_cast<GeoDataPlacemark *>(qvariant_cast<GeoDataObject *>(index.data(MarblePlacemarkModel::ObjectPointerRole)));
236 const GeoDataCoordinates coordinates = placemarkIconCoordinates(placemark);
237 if (!coordinates.isValid()) {
238 continue;
239 }
240
241 int zoomLevel = placemark->zoomLevel();
242 TileId key = TileId::fromCoordinates(coordinates, zoomLevel);
243 delete m_visiblePlacemarks[placemark];
244 m_visiblePlacemarks.remove(placemark);
245 m_placemarkCache[key].removeAll(placemark);
246 if (placemark->hasOsmData()) {
247 qint64 const osmId = placemark->osmData().id();
248 if (osmId > 0) {
249 m_osmIds.remove(osmId);
250 }
251 }
252 }
253 Q_EMIT repaintNeeded();
254}
255
256void PlacemarkLayout::resetCacheData()
257{
258 const int rowCount = m_placemarkModel->rowCount();
259
260 m_osmIds.clear();
261 m_placemarkCache.clear();
262 qDeleteAll(m_visiblePlacemarks);
263 m_visiblePlacemarks.clear();
264 requestStyleReset();
265 addPlacemarks(m_placemarkModel->index(0, 0), 0, rowCount);
266 Q_EMIT repaintNeeded();
267}
268
269QSet<TileId> PlacemarkLayout::visibleTiles(const ViewportParams &viewport, int zoomLevel)
270{
271 /*
272 * rely on m_placemarkCache to find the placemarks for the tiles which
273 * matter. The top level tiles have the more popular placemarks,
274 * the bottom level tiles have the smaller ones, and we only get the ones
275 * matching our latLonAltBox.
276 */
277
278 qreal north, south, east, west;
279 viewport.viewLatLonAltBox().boundaries(north, south, east, west);
280 QSet<TileId> tileIdSet;
282 if (west <= east) {
283 geoRects << GeoDataLatLonBox(north, south, east, west);
284 } else {
285 geoRects << GeoDataLatLonBox(north, south, M_PI, west);
286 geoRects << GeoDataLatLonBox(north, south, east, -M_PI);
287 }
288
289 for (const GeoDataLatLonBox &geoRect : std::as_const(geoRects)) {
290 TileId key;
291 QRect rect;
292
293 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.west(), north), zoomLevel);
294 rect.setLeft(key.x());
295 rect.setTop(key.y());
296
297 key = TileId::fromCoordinates(GeoDataCoordinates(geoRect.east(), south), zoomLevel);
298 rect.setRight(key.x());
299 rect.setBottom(key.y());
300
301 TileCoordsPyramid pyramid(0, zoomLevel);
302 pyramid.setBottomLevelCoords(rect);
303
304 for (int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level) {
305 QRect const coords = pyramid.coords(level);
306 int x1, y1, x2, y2;
307 coords.getCoords(&x1, &y1, &x2, &y2);
308 for (int x = x1; x <= x2; ++x) {
309 for (int y = y1; y <= y2; ++y) {
310 TileId const tileId(0, level, x, y);
311 tileIdSet.insert(tileId);
312 }
313 }
314 }
315 }
316
317 return tileIdSet;
318}
319
321{
322 m_runtimeTrace.clear();
323 if (m_placemarkModel->rowCount() <= 0) {
324 clearCache();
325 return {};
326 }
327
328 if (m_styleResetRequested) {
329 styleReset();
330 }
331
332 if (m_maxLabelHeight == 0) {
333 clearCache();
334 return {};
335 }
336
338 int currentMaxLabelHeight;
339 do {
340 currentMaxLabelHeight = m_maxLabelHeight;
341 const int secnumber = viewport->height() / m_maxLabelHeight + 1;
342 m_rowsection.clear();
343 m_rowsection.resize(secnumber);
344
345 m_paintOrder.clear();
346 m_lastPlacemarkAvailable = false;
347 m_lastPlacemarkLabelRect = QRectF();
348 m_lastPlacemarkSymbolRect = QRectF();
349 m_labelArea = 0;
350
351 // First handle the selected placemarks as they have the highest priority.
352
353 const QModelIndexList selectedIndexes = m_selectionModel->selection().indexes();
354 auto const viewLatLonAltBox = viewport->viewLatLonAltBox();
355
356 for (int i = 0; i < selectedIndexes.count(); ++i) {
357 const QModelIndex index = selectedIndexes.at(i);
358 const GeoDataPlacemark *placemark =
359 static_cast<GeoDataPlacemark *>(qvariant_cast<GeoDataObject *>(index.data(MarblePlacemarkModel::ObjectPointerRole)));
360 const GeoDataCoordinates coordinates = placemarkIconCoordinates(placemark);
361
362 if (!coordinates.isValid()) {
363 continue;
364 }
365
366 qreal x = 0;
367 qreal y = 0;
368
369 if (!viewLatLonAltBox.contains(coordinates) || !viewport->screenCoordinates(coordinates, x, y)) {
370 continue;
371 }
372
373 if (layoutPlacemark(placemark, coordinates, x, y, true)) {
374 // Make sure not to draw more placemarks on the screen than
375 // specified by placemarksOnScreenLimit().
376 if (placemarksOnScreenLimit(viewport->size()))
377 break;
378 }
379 }
380
381 // Now handle all other placemarks...
382
383 const QItemSelection selection = m_selectionModel->selection();
384
385 placemarkList.clear();
386 for (const TileId &tileId : visibleTiles(*viewport, tileLevel)) {
387 placemarkList += m_placemarkCache.value(tileId);
388 }
389 std::sort(placemarkList.begin(), placemarkList.end(), GeoDataPlacemark::placemarkLayoutOrderCompare);
390
391 for (const GeoDataPlacemark *placemark : std::as_const(placemarkList)) {
392 const GeoDataCoordinates coordinates = placemarkIconCoordinates(placemark);
393 if (!coordinates.isValid()) {
394 continue;
395 }
396
397 int zoomLevel = placemark->zoomLevel();
398 if (zoomLevel > 20) {
399 break;
400 }
401
402 qreal x = 0;
403 qreal y = 0;
404
405 if (!viewLatLonAltBox.contains(coordinates) || !viewport->screenCoordinates(coordinates, x, y)) {
406 continue;
407 }
408
409 if (!placemark->isGloballyVisible()) {
410 continue;
411 }
412
413 const GeoDataPlacemark::GeoDataVisualCategory visualCategory = placemark->visualCategory();
414
415 // Skip city marks if we're not showing cities.
416 if (!m_showCities && visualCategory >= GeoDataPlacemark::SmallCity && visualCategory <= GeoDataPlacemark::Nation)
417 continue;
418
419 // Skip terrain marks if we're not showing terrain.
420 if (!m_showTerrain && visualCategory >= GeoDataPlacemark::Mountain && visualCategory <= GeoDataPlacemark::OtherTerrain)
421 continue;
422
423 // Skip other places if we're not showing other places.
424 if (!m_showOtherPlaces && visualCategory >= GeoDataPlacemark::GeographicPole && visualCategory <= GeoDataPlacemark::Observatory)
425 continue;
426
427 // Skip landing sites if we're not showing landing sites.
428 if (!m_showLandingSites && visualCategory >= GeoDataPlacemark::MannedLandingSite && visualCategory <= GeoDataPlacemark::UnmannedHardLandingSite)
429 continue;
430
431 // Skip craters if we're not showing craters.
432 if (!m_showCraters && visualCategory == GeoDataPlacemark::Crater)
433 continue;
434
435 // Skip maria if we're not showing maria.
436 if (!m_showMaria && visualCategory == GeoDataPlacemark::Mare)
437 continue;
438
439 if (!m_showPlaces && visualCategory >= GeoDataPlacemark::GeographicPole && visualCategory <= GeoDataPlacemark::Observatory)
440 continue;
441
442 // We handled selected placemarks already, so we skip them here...
443 // Assuming that only a small amount of places is selected
444 // we check for the selected state after all other filters
445 bool isSelected = false;
446 for (const QModelIndex &index : selection.indexes()) {
447 const GeoDataPlacemark *mark =
448 static_cast<GeoDataPlacemark *>(qvariant_cast<GeoDataObject *>(index.data(MarblePlacemarkModel::ObjectPointerRole)));
449 if (mark == placemark) {
450 isSelected = true;
451 break;
452 }
453 }
454 if (isSelected)
455 continue;
456
457 if (layoutPlacemark(placemark, coordinates, x, y, isSelected)) {
458 // Make sure not to draw more placemarks on the screen than
459 // specified by placemarksOnScreenLimit().
460 if (placemarksOnScreenLimit(viewport->size()))
461 break;
462 }
463 }
464
465 if (m_visiblePlacemarks.size() > qMax(100, 4 * m_paintOrder.size())) {
466 auto const extendedBox = viewLatLonAltBox.scaled(2.0, 2.0);
468 for (auto placemark : std::as_const(m_visiblePlacemarks)) {
469 if (!extendedBox.contains(placemark->coordinates())) {
470 outdated << placemark;
471 }
472 }
473 for (auto placemark : std::as_const(outdated)) {
474 delete m_visiblePlacemarks.take(placemark->placemark());
475 }
476 }
477 } while (currentMaxLabelHeight != m_maxLabelHeight);
478
479 m_runtimeTrace = QStringLiteral("Placemarks: %1 Drawn: %2").arg(placemarkList.count()).arg(m_paintOrder.size());
480 return m_paintOrder;
481}
482
483QString PlacemarkLayout::runtimeTrace() const
484{
485 return m_runtimeTrace;
486}
487
488QList<VisiblePlacemark *> PlacemarkLayout::visiblePlacemarks() const
489{
490 return m_visiblePlacemarks.values();
491}
492
493bool PlacemarkLayout::hasPlacemarkAt(const QPoint &pos)
494{
495 if (m_styleResetRequested) {
496 styleReset();
497 }
498
499 if (m_lastPlacemarkAvailable && (m_lastPlacemarkLabelRect.contains(pos) || m_lastPlacemarkSymbolRect.contains(pos))) {
500 return true;
501 }
502
503 for (VisiblePlacemark *mark : std::as_const(m_paintOrder)) {
504 if (mark->labelRect().contains(pos) || mark->symbolRect().contains(pos)) {
505 m_lastPlacemarkLabelRect = mark->labelRect();
506 m_lastPlacemarkSymbolRect = mark->symbolRect();
507 m_lastPlacemarkAvailable = true;
508 return true;
509 }
510 }
511
512 return false;
513}
514
515bool PlacemarkLayout::layoutPlacemark(const GeoDataPlacemark *placemark, const GeoDataCoordinates &coordinates, qreal x, qreal y, bool selected)
516{
517 // Find the corresponding visible placemark
518 VisiblePlacemark *mark = m_visiblePlacemarks.value(placemark);
519 if (!mark) {
520 // If there is no visible placemark yet for this index,
521 // create a new one...
522 StyleParameters parameters;
523 // @todo: Set / adjust to tile level
524 parameters.placemark = placemark;
525
526 auto style = m_styleBuilder->createStyle(parameters);
527 mark = new VisiblePlacemark(placemark, coordinates, style);
528 m_visiblePlacemarks.insert(placemark, mark);
529 connect(mark, SIGNAL(updateNeeded()), this, SIGNAL(repaintNeeded()));
530 }
531 GeoDataStyle::ConstPtr style = mark->style();
532
533 // Choose Section
534
535 QPointF hotSpot = mark->hotSpot();
536 mark->setSelected(selected);
537 mark->setSymbolPosition(QPointF(x - hotSpot.x(), y - hotSpot.y()));
538
539 // Find out whether the area around the placemark is covered already.
540 // If there's not enough space free don't add a VisiblePlacemark here.
541
542 const QString labelText = placemark->displayName();
543 QRectF labelRect;
544 if (!labelText.isEmpty()) {
545 labelRect = roomForLabel(style, x, y, labelText, mark);
546 }
547 if (labelRect.isEmpty() && mark->symbolPixmap().isNull()) {
548 return false;
549 }
550 if (!mark->symbolPixmap().isNull() && !hasRoomForPixmap(y, mark)) {
551 return false;
552 }
553
554 mark->setLabelRect(labelRect);
555
556 // Add the current placemark to the matching row and its
557 // direct neighbors.
558 int idx = y / m_maxLabelHeight;
559 if (idx - 1 >= 0) {
560 m_rowsection[idx - 1].append(mark);
561 }
562 m_rowsection[idx].append(mark);
563 if (idx + 1 < m_rowsection.size()) {
564 m_rowsection[idx + 1].append(mark);
565 }
566
567 m_paintOrder.append(mark);
568 QRectF const boundingBox = mark->boundingBox();
569 Q_ASSERT(!boundingBox.isEmpty());
570 m_labelArea += boundingBox.width() * boundingBox.height();
571 m_maxLabelHeight = qMax(m_maxLabelHeight, qCeil(boundingBox.height()));
572 return true;
573}
574
575GeoDataCoordinates PlacemarkLayout::placemarkIconCoordinates(const GeoDataPlacemark *placemark) const
576{
577 GeoDataCoordinates coordinates = placemark->coordinate(m_clock->dateTime());
578 if (!m_acceptedVisualCategories.contains(placemark->visualCategory())) {
579 StyleParameters parameters;
580 parameters.placemark = placemark;
581 auto style = m_styleBuilder->createStyle(parameters);
582 if (style->iconStyle().scaledIcon().isNull()) {
583 return {};
584 }
585 }
586
587 return coordinates;
588}
589
590QRectF PlacemarkLayout::roomForLabel(const GeoDataStyle::ConstPtr &style,
591 const qreal x,
592 const qreal y,
593 const QString &labelText,
594 const VisiblePlacemark *placemark) const
595{
596 QFont labelFont = style->labelStyle().scaledFont();
597 int textHeight = QFontMetrics(labelFont).height();
598
599 int textWidth;
600 if (style->labelStyle().glow()) {
601 labelFont.setWeight(QFont::Bold); // Needed to calculate the correct pixmap size;
602 textWidth = (QFontMetrics(labelFont).horizontalAdvance(labelText) + qRound(2 * s_labelOutlineWidth));
603 } else {
604 textWidth = (QFontMetrics(labelFont).horizontalAdvance(labelText));
605 }
606
607 const QList<VisiblePlacemark *> currentsec = m_rowsection.at(y / m_maxLabelHeight);
608 QRectF const symbolRect = placemark->symbolRect();
609
610 if (style->labelStyle().alignment() == GeoDataLabelStyle::Corner) {
611 const int symbolWidth = style->iconStyle().scaledIcon().size().width();
612
613 // Check the four possible positions by going through all of them
614 for (int i = 0; i < 4; ++i) {
615 const qreal xPos = (i / 2 == 0) ? x + symbolWidth / 2 + 1 : x - symbolWidth / 2 - 1 - textWidth;
616 const qreal yPos = (i % 2 == 0) ? y : y - textHeight;
617 const QRectF labelRect = QRectF(xPos, yPos, textWidth, textHeight);
618
619 if (hasRoomFor(currentsec, labelRect.united(symbolRect))) {
620 // claim the place immediately if it hasn't been used yet
621 return labelRect;
622 }
623 }
624 } else if (style->labelStyle().alignment() == GeoDataLabelStyle::Center) {
625 int const offsetY = style->iconStyle().scaledIcon().height() / 2.0;
626 QRectF labelRect = QRectF(x - textWidth / 2, y - offsetY - textHeight, textWidth, textHeight);
627
628 if (hasRoomFor(currentsec, labelRect.united(symbolRect))) {
629 // claim the place immediately if it hasn't been used yet
630 return labelRect;
631 }
632 } else if (style->labelStyle().alignment() == GeoDataLabelStyle::Right) {
633 const int symbolWidth = style->iconStyle().scaledIcon().width();
634 const qreal startY = y - textHeight / 2;
635 const qreal xPos = x + symbolWidth / 2 + 1;
636
637 // Check up to seven vertical positions (center, +3, -3 from center)
638 for (int i = 0; i < 7; ++i) {
639 const qreal increase = (i / 2) * (textHeight + 1); // intentional integer arithmetics
640 const qreal direction = (i % 2 == 0 ? 1 : -1);
641 const qreal yPos = startY + increase * direction;
642
643 const QRectF labelRect = QRectF(xPos, yPos, textWidth, textHeight);
644
645 if (hasRoomFor(currentsec, labelRect.united(symbolRect))) {
646 return labelRect;
647 }
648 }
649 }
650
651 // At this point there is no space left for the rectangle anymore.
652 return {};
653}
654
655bool PlacemarkLayout::hasRoomForPixmap(const qreal y, const VisiblePlacemark *placemark) const
656{
657 const QList<VisiblePlacemark *> currentsec = m_rowsection.at(y / m_maxLabelHeight);
658 return hasRoomFor(currentsec, placemark->symbolRect());
659}
660
661bool PlacemarkLayout::placemarksOnScreenLimit(const QSize &screenSize) const
662{
663 int ratio = (m_labelArea * 100) / (screenSize.width() * screenSize.height());
664 return ratio >= 40;
665}
666
667}
668
669#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.
QList< 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.
QList< VisiblePlacemark * > generateLayout(const ViewportParams *viewport, int tileLevel)
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.
Binds a QML item to a specific geodetic location in screen coordinates.
T * geodata_cast(GeoDataObject *node)
Returns the given node cast to type T if the node was instantiated as type T; otherwise returns 0.
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const=0
virtual int rowCount(const QModelIndex &parent) const const=0
void setWeight(Weight weight)
int height() const const
int horizontalAdvance(QChar ch) const const
QModelIndexList indexes() const const
const QItemSelection selection() const const
void append(QList< T > &&value)
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
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
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
iterator insert(const T &value)
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 Mon Nov 4 2024 16:37:03 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.