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)
127 this,
SLOT(requestStyleReset()) );
130 this,
SLOT(resetCacheData()) );
136 this,
SLOT(resetCacheData()) );
144void PlacemarkLayout::setShowPlaces(
bool show )
149void PlacemarkLayout::setShowCities(
bool show )
154void PlacemarkLayout::setShowTerrain(
bool show )
156 m_showTerrain = show;
159void PlacemarkLayout::setShowOtherPlaces(
bool show )
161 m_showOtherPlaces = show;
164void PlacemarkLayout::setShowLandingSites(
bool show )
166 m_showLandingSites = show;
169void PlacemarkLayout::setShowCraters(
bool show )
171 m_showCraters = show;
174void PlacemarkLayout::setShowMaria(
bool show )
179void PlacemarkLayout::requestStyleReset()
181 mDebug() <<
"Style reset requested.";
182 m_styleResetRequested =
true;
185void PlacemarkLayout::styleReset()
188 m_maxLabelHeight = maxLabelHeight();
189 m_styleResetRequested =
false;
192void PlacemarkLayout::clearCache()
194 m_paintOrder.clear();
195 m_lastPlacemarkAvailable =
false;
196 m_lastPlacemarkLabelRect =
QRectF();
197 m_lastPlacemarkSymbolRect =
QRectF();
200 m_visiblePlacemarks.clear();
205 if ( m_styleResetRequested ) {
212 if ( mark->labelRect().contains(
curpos ) || mark->symbolRect().contains(
curpos ) ) {
213 ret.append( mark->placemark() );
220int PlacemarkLayout::maxLabelHeight()
231 for(
int i=first;
i<=last; ++
i ) {
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();
259void PlacemarkLayout::removePlacemarks(
const QModelIndex& parent,
int first,
int last )
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()) {
284 emit repaintNeeded();
287void PlacemarkLayout::resetCacheData()
289 const int rowCount = m_placemarkModel->
rowCount();
292 m_placemarkCache.clear();
294 m_visiblePlacemarks.clear();
297 emit repaintNeeded();
300QSet<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);
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 );
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 ) {
372 const int secnumber = viewport->height() / m_maxLabelHeight + 1;
373 m_rowsection.clear();
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())) {
519 delete m_visiblePlacemarks.take(placemark->placemark());
524 m_runtimeTrace = QStringLiteral(
"Placemarks: %1 Drawn: %2").
arg(placemarkList.
count()).
arg(m_paintOrder.size());
528QString PlacemarkLayout::runtimeTrace()
const
530 return m_runtimeTrace;
535 return m_visiblePlacemarks.values();
538bool 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;
561bool 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 );
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();
616 m_labelArea += boundingBox.
width() * boundingBox.
height();
617 m_maxLabelHeight =
qMax(m_maxLabelHeight,
qCeil(boundingBox.
height()));
621GeoDataCoordinates 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();
636QRectF PlacemarkLayout::roomForLabel(
const GeoDataStyle::ConstPtr &style,
637 const qreal x,
const qreal y,
639 const VisiblePlacemark* placemark)
const
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 :
663 const qreal yPos = (
i%2 == 0 ) ? y :
673 else if ( style->labelStyle().alignment() == GeoDataLabelStyle::Center ) {
674 int const offsetY = style->iconStyle().scaledIcon().height() / 2.0;
683 else if (style->labelStyle().alignment() == GeoDataLabelStyle::Right)
685 const int symbolWidth = style->iconStyle().scaledIcon().width();
690 for(
int i=0;
i<7; ++
i)
693 const qreal direction = (
i%2 == 0 ? 1 : -1);
694 const qreal yPos =
startY + increase*direction;
709bool PlacemarkLayout::hasRoomForPixmap(
const qreal y,
const VisiblePlacemark *placemark)
const
715bool PlacemarkLayout::placemarksOnScreenLimit(
const QSize &screenSize )
const
717 int ratio = ( m_labelArea * 100 ) / ( screenSize.
width() * screenSize.
height() );
723#include "moc_PlacemarkLayout.cpp"
This file contains the headers for ViewportParams.
A 3d point representation.
bool isValid() const
Returns.
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 horizontalAdvance(QChar ch) const const
QModelIndexList indexes() const const
const QItemSelection selection() const const
const_iterator constBegin() const const
const_iterator constEnd() const const
qsizetype count() const const
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)
void getCoords(int *x1, int *y1, int *x2, int *y2) const const
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
bool contains(const QSet< T > &other) const const
bool remove(const T &value)
QString arg(Args &&... args) const const
bool isEmpty() const const