PlacemarkLayer.cpp 10.4 KB
Newer Older
Inge Wallin's avatar
Inge Wallin committed
1
//
2
// This file is part of the Marble Virtual Globe.
Inge Wallin's avatar
Inge Wallin committed
3
4
5
6
7
//
// This program is free software licensed under the GNU LGPL. You can
// find a copy of this license in LICENSE.txt in the top directory of
// the source code.
//
8
9
// Copyright 2006-2007 Torsten Rahn <tackat@kde.org>
// Copyright 2007-2008 Inge Wallin  <ingwa@kde.org>
10
// Copyright 2011-2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
11
//
12

13
#include "PlacemarkLayer.h"
Torsten Rahn's avatar
 
Torsten Rahn committed
14

15
16
#include <QPoint>
#include <QPainter>
Inge Wallin's avatar
Inge Wallin committed
17

18
#include "MarbleDebug.h"
Jens-Michael Hoffmann's avatar
Jens-Michael Hoffmann committed
19
#include "AbstractProjection.h"
20
#include "GeoDataStyle.h"
21
#include "GeoPainter.h"
22
#include "GeoDataPlacemark.h"
23
#include "GeoDataLatLonAltBox.h"
Jens-Michael Hoffmann's avatar
Jens-Michael Hoffmann committed
24
#include "ViewportParams.h"
Patrick Spendrin's avatar
Patrick Spendrin committed
25
#include "VisiblePlacemark.h"
26
#include "RenderState.h"
Torsten Rahn's avatar
 
Torsten Rahn committed
27

28
29
#define BATCH_RENDERING

30
31
using namespace Marble;

32
33
bool PlacemarkLayer::m_useXWorkaround = false;

34
PlacemarkLayer::PlacemarkLayer(QAbstractItemModel *placemarkModel,
35
                                QItemSelectionModel *selectionModel,
36
                                MarbleClock *clock, const StyleBuilder *styleBuilder,
37
38
                                QObject *parent ) :
    QObject( parent ),
39
40
    m_layout( placemarkModel, selectionModel, clock, styleBuilder ),
    m_debugModeEnabled(false)
Inge Wallin's avatar
Inge Wallin committed
41
{
42
    m_useXWorkaround = testXBug();
Patrick Spendrin's avatar
Patrick Spendrin committed
43
    mDebug() << "Use workaround: " << ( m_useXWorkaround ? "1" : "0" );
Torsten Rahn's avatar
Torsten Rahn committed
44

45
    connect( &m_layout, SIGNAL(repaintNeeded()), SIGNAL(repaintNeeded()) );
Torsten Rahn's avatar
 
Torsten Rahn committed
46
47
}

48
PlacemarkLayer::~PlacemarkLayer()
49
50
51
{
}

52
QStringList PlacemarkLayer::renderPosition() const
53
{
54
    return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
Torsten Rahn's avatar
Torsten Rahn committed
55
56
}

57
qreal PlacemarkLayer::zValue() const
58
{
59
60
    return 2.0;
}
61

62
63
64
65
66
67
68
bool PlacemarkLayer::render( GeoPainter *geoPainter, ViewportParams *viewport,
                               const QString &renderPos, GeoSceneLayer *layer )
{
    Q_UNUSED( renderPos )
    Q_UNUSED( layer )

    QVector<VisiblePlacemark*> visiblePlacemarks = m_layout.generateLayout( viewport );
69
70
71
72
    // draw placemarks less important first
    QVector<VisiblePlacemark*>::const_iterator visit = visiblePlacemarks.constEnd();
    QVector<VisiblePlacemark*>::const_iterator itEnd = visiblePlacemarks.constBegin();

73
    QPainter *const painter = geoPainter;
74

75
76
    bool const repeatableX = viewport->currentProjection()->repeatableX();
    int const radius4 = 4 * viewport->radius();
77
78
79
80
81

#ifdef BATCH_RENDERING
    QHash <QString, Fragment> hash;
#endif

82
83
    while ( visit != itEnd ) {
        --visit;
84
85

        VisiblePlacemark *const mark = *visit;
86

87
        // Intentionally converting positions from floating point to pixel aligned screen grid below
88
        QRect labelRect( mark->labelRect().toRect() );
89
90
        QPoint symbolPos( mark->symbolPosition().toPoint());
        struct Fragment fragment;
91
92
93

        // when the map is such zoomed out that a given place
        // appears many times, we draw one placemark at each
94
        if (repeatableX) {
95
96
97
            const int symbolX = mark->symbolPosition().x();
            const int textX =   mark->labelRect().x();

98
99
100
            for (int i = symbolX % radius4, width = viewport->width(); i <= width; i += radius4) {
                labelRect.moveLeft(i - symbolX + textX);
                symbolPos.setX(i);
101

102
103
104
105
106
107
108
109
110
                if (!mark->symbolPixmap().isNull()) {
#ifdef BATCH_RENDERING
                    QRect symbolRect = mark->symbolPixmap().rect();
                    QPainter::PixmapFragment pixmapFragment = QPainter::PixmapFragment::create(QPointF(symbolPos+symbolRect.center()),QRectF(symbolRect));

                    QHash<QString, Fragment>::iterator i = hash.find(mark->symbolId());
                    if (i == hash.end()) {
                        fragment.count = 1;
                        fragment.pixmap = mark->symbolPixmap();
111
                        fragment.symbolId = mark->symbolId();
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
                    }
                    else {
                        fragment = i.value();
                        ++fragment.count;
                    }
                    fragment.fragments.append(pixmapFragment);
                    hash.insert(mark->symbolId(), fragment);
#else
                    painter->drawPixmap( symbolPos, mark->symbolPixmap() );
#endif
                }
                if (!mark->labelPixmap().isNull()) {
                    painter->drawPixmap( labelRect, mark->labelPixmap() );
                }
            }
        } else { // simple case, one draw per placemark

            if (!mark->symbolPixmap().isNull()) {
#ifdef BATCH_RENDERING
                QRect symbolRect = mark->symbolPixmap().rect();
                QPainter::PixmapFragment pixmapFragment = QPainter::PixmapFragment::create(QPointF(symbolPos+symbolRect.center()),QRectF(symbolRect));

                QHash<QString, Fragment>::iterator i = hash.find(mark->symbolId());
                if (i == hash.end()) {
                    fragment.count = 1;
                    fragment.pixmap = mark->symbolPixmap();
138
                    fragment.symbolId = mark->symbolId();
139
140
141
142
143
144
145
146
                }
                else {
                    fragment = i.value();
                    ++fragment.count;
                }
                fragment.fragments.append(pixmapFragment);
                hash.insert(mark->symbolId(), fragment);
#else
147
                painter->drawPixmap( symbolPos, mark->symbolPixmap() );
148
149
150
#endif
            }
            if (!mark->labelPixmap().isNull()) {
151
152
153
                painter->drawPixmap( labelRect, mark->labelPixmap() );
            }
        }
154
    }
155

156
157
158
#ifdef BATCH_RENDERING
    for (auto& val : hash.values()) {
        if (m_debugModeEnabled) {
159
//            qDebug() << "Batches" << val.symbolId << val.count;
160
            QPixmap debugPixmap(val.pixmap.size());
161
162
163
164
165
166
167
168
169
170
171
172
173
            QColor backgroundColor;
            QString idStr = val.symbolId.section('/', -1);
            if (idStr.length() > 2) {
              idStr.remove("shop_");
              backgroundColor = QColor(
                          (10 * (int)(idStr[0].toLatin1()))%255,
                          (10 * (int)(idStr[1].toLatin1()))%255,
                          (10 * (int)(idStr[2].toLatin1()))%255 );
            }
            else {
              backgroundColor = QColor((quint64)(&val.symbolId));
            }
            debugPixmap.fill(backgroundColor);
174
175
176
177
178
179
180
181
182
183
            QPainter pixpainter;
            pixpainter.begin(&debugPixmap);
            pixpainter.drawPixmap(0, 0, val.pixmap);
            pixpainter.end();
            val.pixmap = debugPixmap;
        }
        painter->drawPixmapFragments(val.fragments.data(), val.count, val.pixmap);
    }
#endif

184
185
186
187
    if (m_debugModeEnabled) {
        renderDebug(geoPainter, viewport, visiblePlacemarks);
    }

188
189
190
    return true;
}

191
192
RenderState PlacemarkLayer::renderState() const
{
193
    return RenderState(QStringLiteral("Placemarks"));
194
195
}

196
197
198
199
200
QString PlacemarkLayer::runtimeTrace() const
{
    return m_layout.runtimeTrace();
}

Gábor Péterffy's avatar
Gábor Péterffy committed
201
QVector<const GeoDataFeature *> PlacemarkLayer::whichPlacemarkAt( const QPoint &pos )
202
203
204
205
{
    return m_layout.whichPlacemarkAt( pos );
}

206
207
208
209
210
211
212
213
214
215
bool PlacemarkLayer::isDebugModeEnabled() const
{
    return m_debugModeEnabled;
}

void PlacemarkLayer::setDebugModeEnabled(bool enabled)
{
    m_debugModeEnabled = enabled;
}

216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
void PlacemarkLayer::setShowPlaces( bool show )
{
    m_layout.setShowPlaces( show );
}

void PlacemarkLayer::setShowCities( bool show )
{
    m_layout.setShowCities( show );
}

void PlacemarkLayer::setShowTerrain( bool show )
{
    m_layout.setShowTerrain( show );
}

void PlacemarkLayer::setShowOtherPlaces( bool show )
{
    m_layout.setShowOtherPlaces( show );
}

void PlacemarkLayer::setShowLandingSites( bool show )
{
    m_layout.setShowLandingSites( show );
}

void PlacemarkLayer::setShowCraters( bool show )
{
    m_layout.setShowCraters( show );
}

void PlacemarkLayer::setShowMaria( bool show )
{
    m_layout.setShowMaria( show );
}

void PlacemarkLayer::requestStyleReset()
{
    m_layout.requestStyleReset();
254
}
255

Torsten Rahn's avatar
   
Torsten Rahn committed
256

257
258
259
// Test if there a bug in the X server which makes
// text fully transparent if it gets written on
// QPixmaps that were initialized by filling them
260
// with Qt::transparent
261

262
bool PlacemarkLayer::testXBug()
Inge Wallin's avatar
Inge Wallin committed
263
264
265
{
    QString  testchar( "K" );
    QFont    font( "Sans Serif", 10 );
Torsten Rahn's avatar
   
Torsten Rahn committed
266

Inge Wallin's avatar
Inge Wallin committed
267
268
269
    int fontheight = QFontMetrics( font ).height();
    int fontwidth  = QFontMetrics( font ).width(testchar);
    int fontascent = QFontMetrics( font ).ascent();
Torsten Rahn's avatar
   
Torsten Rahn committed
270

Inge Wallin's avatar
Inge Wallin committed
271
272
    QPixmap  pixmap( fontwidth, fontheight );
    pixmap.fill( Qt::transparent );
Torsten Rahn's avatar
   
Torsten Rahn committed
273

Inge Wallin's avatar
Inge Wallin committed
274
275
276
277
278
279
    QPainter textpainter;
    textpainter.begin( &pixmap );
    textpainter.setPen( QColor( 0, 0, 0, 255 ) );
    textpainter.setFont( font );
    textpainter.drawText( 0, fontascent, testchar );
    textpainter.end();
Torsten Rahn's avatar
   
Torsten Rahn committed
280

Inge Wallin's avatar
Inge Wallin committed
281
    QImage image = pixmap.toImage();
Torsten Rahn's avatar
   
Torsten Rahn committed
282

Torsten Rahn's avatar
   
Torsten Rahn committed
283
284
    for ( int x = 0; x < fontwidth; ++x ) {
        for ( int y = 0; y < fontheight; ++y ) {
285
            if ( qAlpha( image.pixel( x, y ) ) > 0 )
Inge Wallin's avatar
Inge Wallin committed
286
287
                return false;
        }
288
    }
Torsten Rahn's avatar
   
Torsten Rahn committed
289

Inge Wallin's avatar
Inge Wallin committed
290
    return true;
Torsten Rahn's avatar
   
Torsten Rahn committed
291
}
Torsten Rahn's avatar
   
Torsten Rahn committed
292

293
294
295
void PlacemarkLayer::renderDebug(GeoPainter *painter, ViewportParams *viewport, const QVector<VisiblePlacemark *> &placemarks)
{
    painter->save();
296
    painter->setFont(QFont(QStringLiteral("Sans Serif"), 7));
297
    painter->setBrush(QBrush(Qt::NoBrush));
298
    auto const latLonAltBox = viewport->viewLatLonAltBox();
299
300
301
302
303

    typedef QSet<VisiblePlacemark*> Placemarks;
    Placemarks const hidden = Placemarks::fromList(m_layout.visiblePlacemarks()).subtract(Placemarks::fromList(placemarks.toList()));

    for (auto placemark: hidden) {
304
305
        bool const inside = latLonAltBox.contains(placemark->coordinates());
        painter->setPen(QPen(QColor(inside ? Qt::red : Qt::darkYellow)));
306
307
308
309
310
311
312
313
314
315
316
317
        painter->drawRect(placemark->boundingBox());
    }

    painter->setPen(QPen(QColor(Qt::blue)));
    for (auto placemark: placemarks) {
        painter->drawRect(placemark->boundingBox());
    }

    painter->setPen(QPen(QColor(Qt::green)));
    for (auto placemark: placemarks) {
        painter->drawRect(placemark->labelRect());
        painter->drawRect(placemark->symbolRect());
318
319
320
321
322
323
324
325
326
327
328
    }

    auto const height = painter->fontMetrics().height();
    painter->setPen(QPen(QColor(Qt::black)));
    for (auto placemark: placemarks) {
        QPoint position = placemark->symbolRect().bottomLeft().toPoint() + QPoint(0, qRound(0.8 * height));
        auto const popularity = placemark->placemark()->popularity();
        painter->drawText(position, QStringLiteral("p: %1").arg(popularity));
        position -= QPoint(0, placemark->symbolRect().height() + height);
        auto const zoomLevel = placemark->placemark()->zoomLevel();
        painter->drawText(position, QStringLiteral("z: %1").arg(zoomLevel));
329
330
331
332
333
    }

    painter->restore();
}

334
#include "moc_PlacemarkLayer.cpp"
Torsten Rahn's avatar
   
Torsten Rahn committed
335