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: 2011-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
6 //
7
8 #include "PlacemarkLayer.h"
9
10 #include <QPoint>
11
12 #include "MarbleDebug.h"
13 #include "AbstractProjection.h"
14 #include "GeoDataStyle.h"
15 #include "GeoPainter.h"
16 #include "GeoDataLatLonAltBox.h"
17 #include "ViewportParams.h"
18 #include "VisiblePlacemark.h"
19 #include "RenderState.h"
20 #include "osm/OsmPlacemarkData.h"
21
22 #define BATCH_RENDERING
23
24 using namespace Marble;
25
26 bool PlacemarkLayer::m_useXWorkaround = false;
27
PlacemarkLayer(QAbstractItemModel * placemarkModel,QItemSelectionModel * selectionModel,MarbleClock * clock,const StyleBuilder * styleBuilder,QObject * parent)28 PlacemarkLayer::PlacemarkLayer(QAbstractItemModel *placemarkModel,
29 QItemSelectionModel *selectionModel,
30 MarbleClock *clock, const StyleBuilder *styleBuilder,
31 QObject *parent ) :
32 QObject( parent ),
33 m_layout( placemarkModel, selectionModel, clock, styleBuilder ),
34 m_debugModeEnabled(false),
35 m_levelTagDebugModeEnabled(false),
36 m_tileLevel(0),
37 m_debugLevelTag(0)
38 {
39 m_useXWorkaround = testXBug();
40 mDebug() << "Use workaround: " << ( m_useXWorkaround ? "1" : "0" );
41
42 connect( &m_layout, SIGNAL(repaintNeeded()), SIGNAL(repaintNeeded()) );
43 }
44
~PlacemarkLayer()45 PlacemarkLayer::~PlacemarkLayer()
46 {
47 }
48
renderPosition() const49 QStringList PlacemarkLayer::renderPosition() const
50 {
51 return QStringList(QStringLiteral("PLACEMARKS"));
52 }
53
zValue() const54 qreal PlacemarkLayer::zValue() const
55 {
56 return 2.0;
57 }
58
render(GeoPainter * geoPainter,ViewportParams * viewport,const QString & renderPos,GeoSceneLayer * layer)59 bool PlacemarkLayer::render( GeoPainter *geoPainter, ViewportParams *viewport,
60 const QString &renderPos, GeoSceneLayer *layer )
61 {
62 Q_UNUSED( renderPos )
63 Q_UNUSED( layer )
64
65 QVector<VisiblePlacemark*> visiblePlacemarks = m_layout.generateLayout( viewport, m_tileLevel );
66 // draw placemarks less important first
67 QVector<VisiblePlacemark*>::const_iterator visit = visiblePlacemarks.constEnd();
68 QVector<VisiblePlacemark*>::const_iterator itEnd = visiblePlacemarks.constBegin();
69
70 QPainter *const painter = geoPainter;
71
72 bool const repeatableX = viewport->currentProjection()->repeatableX();
73 int const radius4 = 4 * viewport->radius();
74
75 #ifdef BATCH_RENDERING
76 QHash <QString, Fragment> hash;
77 #endif
78
79 while ( visit != itEnd ) {
80 --visit;
81
82 VisiblePlacemark *const mark = *visit;
83 if (m_levelTagDebugModeEnabled) {
84 if (mark->placemark()->hasOsmData()) {
85 QHash<QString, QString>::const_iterator tagIter = mark->placemark()->osmData().findTag(QStringLiteral("level"));
86 if (tagIter != mark->placemark()->osmData().tagsEnd()) {
87 const int val = tagIter.value().toInt();
88 if (val != m_debugLevelTag) {
89 continue;
90 }
91 }
92 }
93 }
94
95 // Intentionally converting positions from floating point to pixel aligned screen grid below
96 QRect labelRect( mark->labelRect().toRect() );
97 QPoint symbolPos( mark->symbolPosition().toPoint());
98
99 // when the map is such zoomed out that a given place
100 // appears many times, we draw one placemark at each
101 if (repeatableX) {
102 const int symbolX = symbolPos.x();
103 const int textX = labelRect.x();
104
105 for (int i = symbolX % radius4, width = viewport->width(); i <= width; i += radius4) {
106 labelRect.moveLeft(i - symbolX + textX);
107 symbolPos.setX(i);
108
109 if (!mark->symbolPixmap().isNull()) {
110 #ifdef BATCH_RENDERING
111 QRect symbolRect = mark->symbolPixmap().rect();
112 QPainter::PixmapFragment pixmapFragment = QPainter::PixmapFragment::create(QPointF(symbolPos+symbolRect.center()),QRectF(symbolRect));
113
114 auto iter = hash.find(mark->symbolId());
115 if (iter == hash.end()) {
116 Fragment fragment;
117 fragment.pixmap = mark->symbolPixmap();
118 fragment.fragments << pixmapFragment;
119 hash.insert(mark->symbolId(), fragment);
120 } else {
121 auto & fragment = iter.value();
122 fragment.fragments << pixmapFragment;
123 }
124 #else
125 painter->drawPixmap( symbolPos, mark->symbolPixmap() );
126 #endif
127 }
128 if (!mark->labelPixmap().isNull()) {
129 painter->drawPixmap( labelRect, mark->labelPixmap() );
130 }
131 }
132 } else { // simple case, one draw per placemark
133
134 if (!mark->symbolPixmap().isNull()) {
135 #ifdef BATCH_RENDERING
136 QRect symbolRect = mark->symbolPixmap().rect();
137 QPainter::PixmapFragment pixmapFragment = QPainter::PixmapFragment::create(QPointF(symbolPos+symbolRect.center()),QRectF(symbolRect));
138
139 auto iter = hash.find(mark->symbolId());
140 if (iter == hash.end()) {
141 Fragment fragment;
142 fragment.pixmap = mark->symbolPixmap();
143 fragment.fragments << pixmapFragment;
144 hash.insert(mark->symbolId(), fragment);
145 }
146 else {
147 auto & fragment = iter.value();
148 fragment.fragments << pixmapFragment;
149 }
150 #else
151 painter->drawPixmap( symbolPos, mark->symbolPixmap() );
152 #endif
153 }
154 if (!mark->labelPixmap().isNull()) {
155 painter->drawPixmap( labelRect, mark->labelPixmap() );
156 }
157 }
158 }
159
160 #ifdef BATCH_RENDERING
161 for (auto iter = hash.begin(), end = hash.end(); iter != end; ++iter) {
162 auto const & fragment = iter.value();
163 if (m_debugModeEnabled) {
164 QPixmap debugPixmap(fragment.pixmap.size());
165 QColor backgroundColor;
166 QString idStr = iter.key().section('/', -1);
167 if (idStr.length() > 2) {
168 idStr.remove("shop_");
169 backgroundColor = QColor(
170 (10 * (int)(idStr[0].toLatin1()))%255,
171 (10 * (int)(idStr[1].toLatin1()))%255,
172 (10 * (int)(idStr[2].toLatin1()))%255 );
173 }
174 else {
175 backgroundColor = QColor((quint64)(&iter.key()));
176 }
177 debugPixmap.fill(backgroundColor);
178 QPainter pixpainter;
179 pixpainter.begin(&debugPixmap);
180 pixpainter.drawPixmap(0, 0, fragment.pixmap);
181 pixpainter.end();
182 iter.value().pixmap = debugPixmap;
183 }
184 painter->drawPixmapFragments(fragment.fragments.data(), fragment.fragments.size(), fragment.pixmap);
185 }
186 #endif
187
188 if (m_debugModeEnabled) {
189 renderDebug(geoPainter, viewport, visiblePlacemarks);
190 }
191
192 return true;
193 }
194
renderState() const195 RenderState PlacemarkLayer::renderState() const
196 {
197 return RenderState(QStringLiteral("Placemarks"));
198 }
199
runtimeTrace() const200 QString PlacemarkLayer::runtimeTrace() const
201 {
202 return m_layout.runtimeTrace();
203 }
204
whichPlacemarkAt(const QPoint & pos)205 QVector<const GeoDataFeature *> PlacemarkLayer::whichPlacemarkAt( const QPoint &pos )
206 {
207 return m_layout.whichPlacemarkAt( pos );
208 }
209
hasPlacemarkAt(const QPoint & pos)210 bool PlacemarkLayer::hasPlacemarkAt(const QPoint &pos)
211 {
212 return m_layout.hasPlacemarkAt(pos);
213 }
214
isDebugModeEnabled() const215 bool PlacemarkLayer::isDebugModeEnabled() const
216 {
217 return m_debugModeEnabled;
218 }
219
setDebugModeEnabled(bool enabled)220 void PlacemarkLayer::setDebugModeEnabled(bool enabled)
221 {
222 m_debugModeEnabled = enabled;
223 }
224
setShowPlaces(bool show)225 void PlacemarkLayer::setShowPlaces( bool show )
226 {
227 m_layout.setShowPlaces( show );
228 }
229
setShowCities(bool show)230 void PlacemarkLayer::setShowCities( bool show )
231 {
232 m_layout.setShowCities( show );
233 }
234
setShowTerrain(bool show)235 void PlacemarkLayer::setShowTerrain( bool show )
236 {
237 m_layout.setShowTerrain( show );
238 }
239
setShowOtherPlaces(bool show)240 void PlacemarkLayer::setShowOtherPlaces( bool show )
241 {
242 m_layout.setShowOtherPlaces( show );
243 }
244
setShowLandingSites(bool show)245 void PlacemarkLayer::setShowLandingSites( bool show )
246 {
247 m_layout.setShowLandingSites( show );
248 }
249
setShowCraters(bool show)250 void PlacemarkLayer::setShowCraters( bool show )
251 {
252 m_layout.setShowCraters( show );
253 }
254
setShowMaria(bool show)255 void PlacemarkLayer::setShowMaria( bool show )
256 {
257 m_layout.setShowMaria( show );
258 }
259
requestStyleReset()260 void PlacemarkLayer::requestStyleReset()
261 {
262 m_layout.requestStyleReset();
263 }
264
setTileLevel(int tileLevel)265 void PlacemarkLayer::setTileLevel(int tileLevel)
266 {
267 m_tileLevel = tileLevel;
268 }
269
270
271 // Test if there a bug in the X server which makes
272 // text fully transparent if it gets written on
273 // QPixmaps that were initialized by filling them
274 // with Qt::transparent
275
testXBug()276 bool PlacemarkLayer::testXBug()
277 {
278 QString testchar( "K" );
279 QFont font( "Sans Serif", 10 );
280
281 int fontheight = QFontMetrics( font ).height();
282 int fontwidth = QFontMetrics( font ).width(testchar);
283 int fontascent = QFontMetrics( font ).ascent();
284
285 QPixmap pixmap( fontwidth, fontheight );
286 pixmap.fill( Qt::transparent );
287
288 QPainter textpainter;
289 textpainter.begin( &pixmap );
290 textpainter.setPen( QColor( 0, 0, 0, 255 ) );
291 textpainter.setFont( font );
292 textpainter.drawText( 0, fontascent, testchar );
293 textpainter.end();
294
295 QImage image = pixmap.toImage();
296
297 for ( int x = 0; x < fontwidth; ++x ) {
298 for ( int y = 0; y < fontheight; ++y ) {
299 if ( qAlpha( image.pixel( x, y ) ) > 0 )
300 return false;
301 }
302 }
303
304 return true;
305 }
306
renderDebug(GeoPainter * painter,ViewportParams * viewport,const QVector<VisiblePlacemark * > & placemarks) const307 void PlacemarkLayer::renderDebug(GeoPainter *painter, ViewportParams *viewport, const QVector<VisiblePlacemark *> &placemarks) const
308 {
309 painter->save();
310 painter->setFont(QFont(QStringLiteral("Sans Serif"), 7));
311 painter->setBrush(QBrush(Qt::NoBrush));
312 auto const latLonAltBox = viewport->viewLatLonAltBox();
313
314 using Placemarks = QSet<VisiblePlacemark *>;
315 Placemarks const hidden = Placemarks::fromList(m_layout.visiblePlacemarks()).subtract(Placemarks::fromList(placemarks.toList()));
316
317 for (auto placemark: hidden) {
318 bool const inside = latLonAltBox.contains(placemark->coordinates());
319 painter->setPen(QPen(QColor(inside ? Qt::red : Qt::darkYellow)));
320 painter->drawRect(placemark->boundingBox());
321 }
322
323 painter->setPen(QPen(QColor(Qt::blue)));
324 for (auto placemark: placemarks) {
325 painter->drawRect(placemark->boundingBox());
326 }
327
328 painter->setPen(QPen(QColor(Qt::green)));
329 for (auto placemark: placemarks) {
330 painter->drawRect(placemark->labelRect());
331 painter->drawRect(placemark->symbolRect());
332 }
333
334 auto const height = painter->fontMetrics().height();
335 painter->setPen(QPen(QColor(Qt::black)));
336 for (auto placemark: placemarks) {
337 QPoint position = placemark->symbolRect().bottomLeft().toPoint() + QPoint(0, qRound(0.8 * height));
338 auto const popularity = placemark->placemark()->popularity();
339 painter->drawText(position, QStringLiteral("p: %1").arg(popularity));
340 position -= QPoint(0, placemark->symbolRect().height() + height);
341 auto const zoomLevel = placemark->placemark()->zoomLevel();
342 painter->drawText(position, QStringLiteral("z: %1").arg(zoomLevel));
343 }
344
345 painter->restore();
346 }
347
setLevelTagDebugModeEnabled(bool enabled)348 void PlacemarkLayer::setLevelTagDebugModeEnabled(bool enabled)
349 {
350 if (m_levelTagDebugModeEnabled != enabled) {
351 m_levelTagDebugModeEnabled = enabled;
352 emit repaintNeeded();
353 }
354 }
355
levelTagDebugModeEnabled() const356 bool PlacemarkLayer::levelTagDebugModeEnabled() const
357 {
358 return m_levelTagDebugModeEnabled;
359 }
360
setDebugLevelTag(int level)361 void PlacemarkLayer::setDebugLevelTag(int level)
362 {
363 if (m_debugLevelTag != level) {
364 m_debugLevelTag = level;
365 emit repaintNeeded();
366 }
367 }
368
369 #include "moc_PlacemarkLayer.cpp"
370
371