PlacemarkLayout.cpp 22.4 KB
Newer Older
Torsten Rahn's avatar
 
Torsten Rahn committed
1
//
2
// This file is part of the Marble Virtual Globe.
Torsten Rahn's avatar
 
Torsten Rahn 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>
Torsten Rahn's avatar
 
Torsten Rahn committed
10
11
//

Patrick Spendrin's avatar
Patrick Spendrin committed
12
#include "PlacemarkLayout.h"
Torsten Rahn's avatar
 
Torsten Rahn committed
13
14
15
16
17
18

#include <QtCore/QAbstractItemModel>
#include <QtCore/QList>
#include <QtCore/QPoint>
#include <QtCore/QVector>
#include <QtCore/QVectorIterator>
Torsten Rahn's avatar
   
Torsten Rahn committed
19
#include <QtGui/QFont>
Torsten Rahn's avatar
 
Torsten Rahn committed
20
21
#include <QtGui/QItemSelectionModel>
#include <QtGui/QPainter>
22
#include <QtGui/QSortFilterProxyModel>
Torsten Rahn's avatar
 
Torsten Rahn committed
23

Torsten Rahn's avatar
Torsten Rahn committed
24
25
#include "GeoSceneDocument.h"
#include "GeoSceneMap.h"
26
27
#include "GeoDataPlacemark.h"
#include "GeoDataStyle.h"
28
#include "GeoDataTypes.h"
Torsten Rahn's avatar
   
Torsten Rahn committed
29

30
#include "MarbleDebug.h"
Torsten Rahn's avatar
 
Torsten Rahn committed
31
#include "global.h"
Patrick Spendrin's avatar
Patrick Spendrin committed
32
#include "PlacemarkPainter.h"
33
#include "MarblePlacemarkModel.h"
Torsten Rahn's avatar
 
Torsten Rahn committed
34
35
#include "MarbleDirs.h"
#include "ViewParams.h"
Jens-Michael Hoffmann's avatar
Jens-Michael Hoffmann committed
36
#include "ViewportParams.h"
37
38
#include "TileId.h"
#include "TileCoordsPyramid.h"
39
#include "AbstractProjection.h"
Patrick Spendrin's avatar
Patrick Spendrin committed
40
#include "VisiblePlacemark.h"
41
#include "MathHelper.h"
42

43
44
using namespace Marble;

45
46
PlacemarkLayout::PlacemarkLayout( QAbstractItemModel  *placemarkModel,
                                  QItemSelectionModel *selectionModel,
47
                                  QObject* parent )
48
    : QObject( parent ),
49
      m_placemarkModel( new QSortFilterProxyModel ),
50
      m_selectionModel( selectionModel ),
Patrick Spendrin's avatar
Patrick Spendrin committed
51
      m_placemarkPainter( 0 ),
52
      m_maxLabelHeight( 0 ),
53
      m_styleResetRequested( true )
Torsten Rahn's avatar
 
Torsten Rahn committed
54
{
55
56
57
58
59
60
61
62
63
64
    m_placemarkModel->setSourceModel( placemarkModel );
    m_placemarkModel->setDynamicSortFilter( true );
    m_placemarkModel->setSortRole( MarblePlacemarkModel::PopularityIndexRole );
    m_placemarkModel->sort( 0, Qt::DescendingOrder );

    connect( m_selectionModel,  SIGNAL( selectionChanged( QItemSelection,
                                                           QItemSelection) ),
             this,               SLOT( requestStyleReset() ) );

//  Old weightfilter array. Still here
65
// to be able to compare performance
Torsten Rahn's avatar
   
Torsten Rahn committed
66
/*
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
    m_weightfilter
        << 9999
        << 4200
        << 3900
        << 3600

        << 3300
        << 3000
        << 2700
        << 2400

        << 2100
        << 1800
        << 1500
        << 1200

        << 900
        << 400
        << 200
        << 0;
Torsten Rahn's avatar
   
Torsten Rahn committed
87
*/
Torsten Rahn's avatar
 
Torsten Rahn committed
88

89
90
91
92
93
94
// lower radius limit, level
    m_weightfilter  
        << 49300    // 0
        << 40300    // 1
        << 32300    // 2
        << 25300    // 3
Torsten Rahn's avatar
 
Torsten Rahn committed
95

96
97
98
99
        << 19300    // 4
        << 14300    // 5
        << 10300    // 6
        << 7300     // 7
Torsten Rahn's avatar
 
Torsten Rahn committed
100

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
        << 5300     // 8
        << 3300     // 9
        << 2400     // 10
        << 1800     // 11

        << 1200     // 12
        << 800      // 13
        << 300      // 14
        << 250      // 15

        << 200      // 16
        << 150      // 17
        << 100      // 18
        << 50       // 19

        << 0;       // 20
Torsten Rahn's avatar
   
Torsten Rahn committed
117

Patrick Spendrin's avatar
Patrick Spendrin committed
118
    m_placemarkPainter =  new PlacemarkPainter( this );
Torsten Rahn's avatar
 
Torsten Rahn committed
119
120
}

Patrick Spendrin's avatar
Patrick Spendrin committed
121
PlacemarkLayout::~PlacemarkLayout()
Torsten Rahn's avatar
 
Torsten Rahn committed
122
{
Torsten Rahn's avatar
   
Torsten Rahn committed
123
    styleReset();
Torsten Rahn's avatar
 
Torsten Rahn committed
124
125
}

Patrick Spendrin's avatar
Patrick Spendrin committed
126
void PlacemarkLayout::requestStyleReset()
Torsten Rahn's avatar
   
Torsten Rahn committed
127
{
Patrick Spendrin's avatar
Patrick Spendrin committed
128
    mDebug() << "Style reset requested.";
Torsten Rahn's avatar
   
Torsten Rahn committed
129
130
131
    m_styleResetRequested = true;
}

Patrick Spendrin's avatar
Patrick Spendrin committed
132
void PlacemarkLayout::styleReset()
Torsten Rahn's avatar
 
Torsten Rahn committed
133
134
{
    m_paintOrder.clear();
Patrick Spendrin's avatar
Patrick Spendrin committed
135
136
    qDeleteAll( m_visiblePlacemarks );
    m_visiblePlacemarks.clear();
Thibaut Gridel's avatar
Thibaut Gridel committed
137
    m_maxLabelHeight = maxLabelHeight();
Torsten Rahn's avatar
 
Torsten Rahn committed
138
139
}

140
QVector<const GeoDataPlacemark*> PlacemarkLayout::whichPlacemarkAt( const QPoint& curpos )
Torsten Rahn's avatar
 
Torsten Rahn committed
141
{
142
    if ( m_styleResetRequested ) {
143
144
145
        styleReset();
    }

146
    QVector<const GeoDataPlacemark*> ret;
Torsten Rahn's avatar
 
Torsten Rahn committed
147

Patrick Spendrin's avatar
Patrick Spendrin committed
148
149
    QVector<VisiblePlacemark*>::ConstIterator  it;
    QVector<VisiblePlacemark*>::ConstIterator  itEnd = m_paintOrder.constEnd();
Torsten Rahn's avatar
 
Torsten Rahn committed
150
    for ( it = m_paintOrder.constBegin();
151
          it != itEnd; ++it )
Torsten Rahn's avatar
 
Torsten Rahn committed
152
    {
Patrick Spendrin's avatar
Patrick Spendrin committed
153
        const VisiblePlacemark  *mark = *it; // no cast
Torsten Rahn's avatar
 
Torsten Rahn committed
154
155

        if ( mark->labelRect().contains( curpos )
Torsten Rahn's avatar
   
Torsten Rahn committed
156
             || QRect( mark->symbolPosition(), mark->symbolPixmap().size() ).contains( curpos ) ) {
157
            ret.append( mark->placemark() );
Torsten Rahn's avatar
 
Torsten Rahn committed
158
159
160
161
162
163
        }
    }

    return ret;
}

164
int PlacemarkLayout::maxLabelHeight() const
Torsten Rahn's avatar
   
Torsten Rahn committed
165
{
Patrick Spendrin's avatar
Patrick Spendrin committed
166
    mDebug() << "Detecting maxLabelHeight ...";
Torsten Rahn's avatar
   
Torsten Rahn committed
167
168
169

    int maxLabelHeight = 0;

170
    const QModelIndexList selectedIndexes = m_selectionModel->selection().indexes();
Torsten Rahn's avatar
   
Torsten Rahn committed
171
172
173

    for ( int i = 0; i < selectedIndexes.count(); ++i ) {
        const QModelIndex index = selectedIndexes.at( i );
174
175
        GeoDataPlacemark *placemark = dynamic_cast<GeoDataPlacemark*>(qvariant_cast<GeoDataObject*>(index.data( MarblePlacemarkModel::ObjectPointerRole ) ));
        GeoDataStyle* style = placemark->style();
176
        QFont labelFont = style->labelStyle().font();
Torsten Rahn's avatar
   
Torsten Rahn committed
177
178
179
180
181
        int textHeight = QFontMetrics( labelFont ).height();
        if ( textHeight > maxLabelHeight )
            maxLabelHeight = textHeight; 
    }

182
183
    for ( int i = 0; i < m_placemarkModel->rowCount(); ++i ) {
        QModelIndex index = m_placemarkModel->index( i, 0 );
184
        GeoDataPlacemark *placemark = dynamic_cast<GeoDataPlacemark*>(qvariant_cast<GeoDataObject*>(index.data( MarblePlacemarkModel::ObjectPointerRole ) ));
185
186
187
188
189
190
191
        if ( placemark ) {
            GeoDataStyle* style = placemark->style();
            QFont labelFont = style->labelStyle().font();
            int textHeight = QFontMetrics( labelFont ).height();
            if ( textHeight > maxLabelHeight )
                maxLabelHeight = textHeight;
        }
Torsten Rahn's avatar
   
Torsten Rahn committed
192
193
    }

Patrick Spendrin's avatar
Patrick Spendrin committed
194
    //mDebug() <<"Detected maxLabelHeight: " << maxLabelHeight;
Torsten Rahn's avatar
   
Torsten Rahn committed
195
196
197
    return maxLabelHeight;
}

198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Calculate a "TileId" from a Placemark containing coordinates and popularity
// The popularity will lead to a tile level, i.e. popularity 1 (most popular)
// goes to topmost tile level.
// Then for a given popularity, calculate which tile matches the coordinates
// In the tiling, every level has 4 times more tiles than previous level
// In the end, one placemark belongs to one Tile only.
TileId PlacemarkLayout::placemarkToTileId( const GeoDataCoordinates& coords, int popularity ) const
{
    if ( popularity < 0 ) {
        return TileId();
    }
    int maxLat = 90000000;
    int maxLon = 180000000;
    int lat = coords.latitude( GeoDataCoordinates::Degree ) * 1000000;
    int lon = coords.longitude( GeoDataCoordinates::Degree ) * 1000000;
    int deltaLat, deltaLon;
    int x = 0;
    int y = 0;
    for( int i=0; i<popularity; ++i ) {
        deltaLat = maxLat >> i;
        if( lat < ( maxLat - deltaLat )) {
            y += 1<<(popularity-i-1);
            lat += deltaLat;
        }
        deltaLon = maxLon >> i;
        if( lon >= ( maxLon - deltaLon )) {
            x += 1<<(popularity-i-1);
        } else {
            lon += deltaLon;
        }
    }
    return TileId("Placemark", popularity, x, y);
}

void PlacemarkLayout::setCacheData()
{
    const int rowCount = m_placemarkModel->rowCount();

236
    m_paintOrder.clear();
237
238
    qDeleteAll( m_visiblePlacemarks );
    m_visiblePlacemarks.clear();
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
    m_placemarkCache.clear();
    for ( int i = 0; i != rowCount; ++i )
    {
        const QModelIndex& index = m_placemarkModel->index( i, 0 );
        if( !index.isValid() ) {
            mDebug() << "invalid index!!!";
            continue;
        }

        GeoDataPlacemark *placemark = static_cast<GeoDataPlacemark*>(qvariant_cast<GeoDataObject*>(index.data( MarblePlacemarkModel::ObjectPointerRole ) ));
        GeoDataGeometry *geometry = placemark->geometry();
        if( geometry->nodeType() != GeoDataTypes::GeoDataPointType ) {
            continue;
        }

        int popularity = (20 - placemark->popularityIndex())/2;
        TileId key = placemarkToTileId( placemark->coordinate(), popularity );
        m_placemarkCache[key].append( placemark );
    }
}

Patrick Spendrin's avatar
Patrick Spendrin committed
260
void PlacemarkLayout::paintPlaceFolder( QPainter   *painter,
261
                                        ViewParams *viewParams )
Torsten Rahn's avatar
 
Torsten Rahn committed
262
{
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
    // earth
    bool showPlaces, showCities, showTerrain, showOtherPlaces;

    viewParams->propertyValue( "places", showPlaces );
    viewParams->propertyValue( "cities", showCities );
    viewParams->propertyValue( "terrain", showTerrain );
    viewParams->propertyValue( "otherplaces", showOtherPlaces );
    
    // other planets
    bool showLandingSites, showCraters, showMaria;

    viewParams->propertyValue( "landingsites", showLandingSites );
    viewParams->propertyValue( "craters", showCraters );
    viewParams->propertyValue( "maria", showMaria );

    if ( !showPlaces && !showCities && !showTerrain && !showOtherPlaces &&
         !showLandingSites && !showCraters && !showMaria )
        return;

282
283
    if ( m_placemarkModel->rowCount() <= 0 )
        return;
284

Inge Wallin's avatar
Inge Wallin committed
285
    // const int imgwidth  = viewParams->canvasImage()->width();
286
    const int imgheight = viewParams->canvasImagePtr()->height();
Torsten Rahn's avatar
   
Torsten Rahn committed
287

288
    if ( m_styleResetRequested ) {
Torsten Rahn's avatar
   
Torsten Rahn committed
289
290
291
        m_styleResetRequested = false;
        styleReset();
    }
292
293
294
295

    if ( m_maxLabelHeight == 0 ) {
        return;
    }
Inge Wallin's avatar
Inge Wallin committed
296
    const int   secnumber         = imgheight / m_maxLabelHeight + 1;
297

298
    //Quaternion  inversePlanetAxis = viewParams->m_planetAxis.inverse();
Torsten Rahn's avatar
 
Torsten Rahn committed
299

Patrick Spendrin's avatar
Patrick Spendrin committed
300
    QVector< QVector< VisiblePlacemark* > >  rowsection;
Torsten Rahn's avatar
 
Torsten Rahn committed
301
    for ( int i = 0; i < secnumber; i++)
Patrick Spendrin's avatar
Patrick Spendrin committed
302
        rowsection.append( QVector<VisiblePlacemark*>( ) );
Torsten Rahn's avatar
 
Torsten Rahn committed
303
304
305
306

    m_paintOrder.clear();

    int labelnum = 0;
Torsten Rahn's avatar
   
Torsten Rahn committed
307
308
    qreal x = 0;
    qreal y = 0;
Torsten Rahn's avatar
 
Torsten Rahn committed
309

Torsten Rahn's avatar
Torsten Rahn committed
310
311
    GeoDataLatLonAltBox latLonAltBox = viewParams->viewport()->viewLatLonAltBox();

312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
    int popularity = 0;
    while ( m_weightfilter.at( popularity ) > viewParams->radius() ) {
        ++popularity;
    }
    popularity = (20 - popularity)/2;

    /**
     * rely on m_placemarkCache to find the placemarks for the tiles which
     * matter. The top level tiles have the more popular placemarks,
     * the bottom level tiles have the smaller ones, and we only get the ones
     * matching our latLonAltBox.
     */

    QRect rect;
    qreal north, south, east, west;
    latLonAltBox.boundaries(north, south, east, west);
    TileId key;

    key = placemarkToTileId( GeoDataCoordinates(west, north, 0), popularity);
    rect.setLeft( key.x() );
    rect.setTop( key.y() );

    key = placemarkToTileId( GeoDataCoordinates(east, south, 0), popularity);
    rect.setRight( key.x() );
    rect.setBottom( key.y() );

    TileCoordsPyramid pyramid(0, popularity );
    pyramid.setBottomLevelCoords( rect );

    QList<GeoDataPlacemark*> placemarkList;
    for ( int level = pyramid.topLevel(); level <= pyramid.bottomLevel(); ++level ) {
        QRect const coords = pyramid.coords( level );
        int x1, y1, x2, y2;
        coords.getCoords( &x1, &y1, &x2, &y2 );
        for ( int x = x1; x <= x2; ++x ) {
            for ( int y = y1; y <= y2; ++y ) {
                TileId const tileId( "Placemark", level, x, y );
                placemarkList += m_placemarkCache.value(tileId);
            }
        }
    }


Torsten Rahn's avatar
 
Torsten Rahn committed
355
    /**
Inge Wallin's avatar
Inge Wallin committed
356
     * First handle the selected placemarks, as they have the highest priority.
Torsten Rahn's avatar
 
Torsten Rahn committed
357
358
     */

359
    const QModelIndexList selectedIndexes = m_selectionModel->selection().indexes();
Torsten Rahn's avatar
 
Torsten Rahn committed
360
361
362

    for ( int i = 0; i < selectedIndexes.count(); ++i ) {
        const QModelIndex index = selectedIndexes.at( i );
363
364
        GeoDataPlacemark *placemark = dynamic_cast<GeoDataPlacemark*>(qvariant_cast<GeoDataObject*>(index.data( MarblePlacemarkModel::ObjectPointerRole ) ));
        GeoDataGeometry *geometry = placemark->geometry();
365
        if( !dynamic_cast<GeoDataPoint*>(geometry) ) {
366
367
368
            continue;
        }

369
        GeoDataCoordinates geopoint = placemark->coordinate();
Torsten Rahn's avatar
Torsten Rahn committed
370
371

        if ( !latLonAltBox.contains( geopoint ) ||
372
             ! viewParams->currentProjection()->screenCoordinates( geopoint, viewParams->viewport(), x, y ))
Torsten Rahn's avatar
Torsten Rahn committed
373
            {
374
                delete m_visiblePlacemarks.take( placemark );
Torsten Rahn's avatar
Torsten Rahn committed
375
376
                continue;
            }
Torsten Rahn's avatar
 
Torsten Rahn committed
377
378
379
380
381

        // ----------------------------------------------------------------
        // End of checks. Here the actual layouting starts.
        int textWidth = 0;

Inge Wallin's avatar
Inge Wallin committed
382
        // Find the corresponding visible placemark
383
        VisiblePlacemark *mark = m_visiblePlacemarks.value( placemark );
Torsten Rahn's avatar
 
Torsten Rahn committed
384

385
        GeoDataStyle* style = placemark->style();
Torsten Rahn's avatar
   
Torsten Rahn committed
386

Torsten Rahn's avatar
 
Torsten Rahn committed
387
388
389
390
391
        // Specify font properties
        if ( mark ) {
            textWidth = mark->labelRect().width();
        }
        else {
392
            QFont labelFont = style->labelStyle().font();
Torsten Rahn's avatar
   
Torsten Rahn committed
393
394
            labelFont.setWeight( 75 ); // Needed to calculate the correct pixmap size; 

395
            textWidth = ( QFontMetrics( labelFont ).width( placemark->name() )
Torsten Rahn's avatar
   
Torsten Rahn committed
396
                  + (int)( 2 * s_labelOutlineWidth ) );
Torsten Rahn's avatar
 
Torsten Rahn committed
397
398
399
        }

        // Choose Section
Patrick Spendrin's avatar
Patrick Spendrin committed
400
        const QVector<VisiblePlacemark*> currentsec = rowsection.at( y / m_maxLabelHeight );
Torsten Rahn's avatar
 
Torsten Rahn committed
401
402

        // Find out whether the area around the placemark is covered already.
Patrick Spendrin's avatar
Patrick Spendrin committed
403
        // If there's not enough space free don't add a VisiblePlacemark here.
Torsten Rahn's avatar
 
Torsten Rahn committed
404

Torsten Rahn's avatar
   
Torsten Rahn committed
405
        QRect labelRect = roomForLabel( style, currentsec, x, y, textWidth );
406
407
        if ( labelRect.isNull() )
            continue;
Torsten Rahn's avatar
 
Torsten Rahn committed
408
409

        // Make sure not to draw more placemarks on the screen than 
Patrick Spendrin's avatar
Patrick Spendrin committed
410
        // specified by placemarksOnScreenLimit().
Torsten Rahn's avatar
 
Torsten Rahn committed
411
412

        ++labelnum;
Patrick Spendrin's avatar
Patrick Spendrin committed
413
        if ( labelnum >= placemarksOnScreenLimit() )
Torsten Rahn's avatar
 
Torsten Rahn committed
414
            break;
Inge Wallin's avatar
Inge Wallin committed
415
416
417
        if ( !mark ) {
            // If there is no visible placemark yet for this index,
            // create a new one...
418
            mark = new VisiblePlacemark( placemark );
Torsten Rahn's avatar
 
Torsten Rahn committed
419

420
            m_visiblePlacemarks.insert( placemark, mark );
Torsten Rahn's avatar
 
Torsten Rahn committed
421
422
423
        }

        // Finally save the label position on the map.
424
        QPointF hotSpot = style->iconStyle().hotSpot();
Torsten Rahn's avatar
   
Torsten Rahn committed
425
426
427

        mark->setSymbolPosition( QPoint( x - (int)( hotSpot.x() ),
                                         y - (int)( hotSpot.y() ) ) );
Torsten Rahn's avatar
 
Torsten Rahn committed
428
429
        mark->setLabelRect( labelRect );

Inge Wallin's avatar
Inge Wallin committed
430
        // Add the current placemark to the matching row and its
Torsten Rahn's avatar
 
Torsten Rahn committed
431
        // direct neighbors.
Torsten Rahn's avatar
   
Torsten Rahn committed
432
        int idx = y / m_maxLabelHeight;
Torsten Rahn's avatar
 
Torsten Rahn committed
433
434
435
436
437
438
439
440
441
442
        if ( idx - 1 >= 0 )
            rowsection[ idx - 1 ].append( mark );
        rowsection[ idx ].append( mark );
        if ( idx + 1 < secnumber )
            rowsection[ idx + 1 ].append( mark );

        m_paintOrder.append( mark );
    }

    /**
Inge Wallin's avatar
Inge Wallin committed
443
     * Now handle all other placemarks...
Torsten Rahn's avatar
 
Torsten Rahn committed
444
     */
445
    const QItemSelection selection = m_selectionModel->selection();
Torsten Rahn's avatar
 
Torsten Rahn committed
446

447
    const int rowCount = placemarkList.count();
448

449
    for ( int i = 0; i != rowCount; ++i )
Torsten Rahn's avatar
 
Torsten Rahn committed
450
    {
451
        GeoDataPlacemark *placemark = placemarkList.at(i);
452
        GeoDataGeometry *geometry = placemark->geometry();
453
        if( !dynamic_cast<GeoDataPoint*>(geometry) ) {
454
455
456
            continue;
        }

457
        int popularityIndex = placemark->popularityIndex();
Torsten Rahn's avatar
 
Torsten Rahn committed
458

459
        
460
        if ( popularityIndex < 1 ) {
461
            break;
Torsten Rahn's avatar
   
Torsten Rahn committed
462
463
        }

Torsten Rahn's avatar
 
Torsten Rahn committed
464
        // Skip the places that are too small.
465
        if ( m_weightfilter.at( popularityIndex ) > viewParams->radius() ) {
466
            break;
Torsten Rahn's avatar
 
Torsten Rahn committed
467
468
        }

469
        GeoDataCoordinates geopoint = placemark->coordinate();
Torsten Rahn's avatar
Torsten Rahn committed
470
471

        if ( !latLonAltBox.contains( geopoint ) ||
472
             ! viewParams->currentProjection()->screenCoordinates( geopoint, viewParams->viewport(), x, y ))
Torsten Rahn's avatar
Torsten Rahn committed
473
            {
474
                delete m_visiblePlacemarks.take( placemark );
Torsten Rahn's avatar
Torsten Rahn committed
475
476
                continue;
            }
Torsten Rahn's avatar
 
Torsten Rahn committed
477

478
479
        if ( !placemark->isVisible() )
        {
480
            delete m_visiblePlacemarks.take( placemark );
481
482
483
            continue;
        }

484
        const int visualCategory  = placemark->visualCategory();
Torsten Rahn's avatar
 
Torsten Rahn committed
485
486

        // Skip city marks if we're not showing cities.
Torsten Rahn's avatar
   
Torsten Rahn committed
487
        if ( !showCities
488
             && ( visualCategory > 2 && visualCategory < 20 ) )
Torsten Rahn's avatar
 
Torsten Rahn committed
489
490
491
            continue;

        // Skip terrain marks if we're not showing terrain.
Torsten Rahn's avatar
   
Torsten Rahn committed
492
        if ( !showTerrain
493
494
             && (    visualCategory >= (int)(GeoDataFeature::Mountain) ) 
                  && visualCategory <= (int)(GeoDataFeature::OtherTerrain) )
Torsten Rahn's avatar
   
Torsten Rahn committed
495
496
            continue;

497
        // Skip other places if we're not showing other places.
Torsten Rahn's avatar
   
Torsten Rahn committed
498
        if ( !showOtherPlaces
499
             && (    visualCategory >= (int)(GeoDataFeature::GeographicPole) ) 
Patrick Spendrin's avatar
Patrick Spendrin committed
500
                  && visualCategory <= (int)(GeoDataFeature::Observatory) )
Torsten Rahn's avatar
 
Torsten Rahn committed
501
502
            continue;

503
        // Skip landing sites if we're not showing landing sites.
Torsten Rahn's avatar
Torsten Rahn committed
504
505
506
507
508
        if ( !showLandingSites
             && (    visualCategory >= (int)(GeoDataFeature::MannedLandingSite) ) 
                  && visualCategory <= (int)(GeoDataFeature::UnmannedHardLandingSite) )
            continue;

509
510
511
512
513
514
515
516
517
        // Skip craters if we're not showing craters.
        if ( !showCraters
             && (    visualCategory == (int)(GeoDataFeature::Crater) ) )
            continue;

        // Skip maria if we're not showing maria.
        if ( !showMaria
             && (    visualCategory == (int)(GeoDataFeature::Mare) ) )
            continue;
Torsten Rahn's avatar
   
Torsten Rahn committed
518

Torsten Rahn's avatar
 
Torsten Rahn committed
519
        /**
Inge Wallin's avatar
Inge Wallin committed
520
         * We handled selected placemarks already, so we skip them here...
521
         * Assuming that only a small amount of places is selected
522
         * we check for the selected state after all other filters
Torsten Rahn's avatar
 
Torsten Rahn committed
523
         */
524
525
526
527
528
529
530
531
        bool isSelected = false;
        foreach ( QModelIndex index, selection.indexes() ) {
            GeoDataPlacemark *mark = dynamic_cast<GeoDataPlacemark*>(qvariant_cast<GeoDataObject*>(index.data( MarblePlacemarkModel::ObjectPointerRole ) ));
            if (mark == placemark ) {
                isSelected = true;
                break;
            }
        }
Torsten Rahn's avatar
 
Torsten Rahn committed
532
533
534
535
536
537
538
        if ( isSelected )
            continue;

        // ----------------------------------------------------------------
        // End of checks. Here the actual layouting starts.
        int textWidth = 0;

Inge Wallin's avatar
Inge Wallin committed
539
        // Find the corresponding visible placemark
540
        VisiblePlacemark *mark = m_visiblePlacemarks.value( placemark );
541
        GeoDataStyle* style = placemark->style();
Torsten Rahn's avatar
 
Torsten Rahn committed
542
543
544
545
546
547

        // Specify font properties
        if ( mark ) {
            textWidth = mark->labelRect().width();
        }
        else {
548
            QFont labelFont = style->labelStyle().font();
549
            textWidth = ( QFontMetrics( labelFont ).width( placemark->name() ) );
Torsten Rahn's avatar
 
Torsten Rahn committed
550
551
552
        }

        // Choose Section
Patrick Spendrin's avatar
Patrick Spendrin committed
553
        const QVector<VisiblePlacemark*> currentsec = rowsection.at( y / m_maxLabelHeight );
Torsten Rahn's avatar
 
Torsten Rahn committed
554

555
         // Find out whether the area around the placemark is covered already.
Patrick Spendrin's avatar
Patrick Spendrin committed
556
        // If there's not enough space free don't add a VisiblePlacemark here.
Torsten Rahn's avatar
 
Torsten Rahn committed
557

Torsten Rahn's avatar
   
Torsten Rahn committed
558
        QRect labelRect = roomForLabel( style, currentsec, x, y, textWidth );
559
560
        if ( labelRect.isNull() )
            continue;
Torsten Rahn's avatar
 
Torsten Rahn committed
561
562

        // Make sure not to draw more placemarks on the screen than 
Patrick Spendrin's avatar
Patrick Spendrin committed
563
        // specified by placemarksOnScreenLimit().
Torsten Rahn's avatar
 
Torsten Rahn committed
564
565

        ++labelnum;
Patrick Spendrin's avatar
Patrick Spendrin committed
566
        if ( labelnum >= placemarksOnScreenLimit() )
Torsten Rahn's avatar
 
Torsten Rahn committed
567
568
            break;

Inge Wallin's avatar
Inge Wallin committed
569
570
571
        if ( !mark ) {
            // If there is no visible placemark yet for this index,
            // create a new one...
572
573
            mark = new VisiblePlacemark( placemark );
            m_visiblePlacemarks.insert( placemark, mark );
Torsten Rahn's avatar
 
Torsten Rahn committed
574
575
576
        }

        // Finally save the label position on the map.
577
        QPointF hotSpot = style->iconStyle().hotSpot();
Torsten Rahn's avatar
   
Torsten Rahn committed
578
579
580

        mark->setSymbolPosition( QPoint( x - (int)( hotSpot.x() ),
                                         y - (int)( hotSpot.y() ) ) );
Torsten Rahn's avatar
 
Torsten Rahn committed
581
582
583
584
        mark->setLabelRect( labelRect );

        // Add the current placemark to the matching row and it's
        // direct neighbors.
Torsten Rahn's avatar
   
Torsten Rahn committed
585
        int idx = y / m_maxLabelHeight;
Torsten Rahn's avatar
 
Torsten Rahn committed
586
587
588
589
590
591
592
593
        if ( idx - 1 >= 0 )
            rowsection[ idx - 1 ].append( mark );
        rowsection[ idx ].append( mark );
        if ( idx + 1 < secnumber )
            rowsection[ idx + 1 ].append( mark );

        m_paintOrder.append( mark );
    }
Torsten Rahn's avatar
Torsten Rahn committed
594
595
596
597
    if ( viewParams->mapTheme() )
    {
        QColor labelColor = viewParams->mapTheme()->map()->labelColor();

Patrick Spendrin's avatar
Patrick Spendrin committed
598
        m_placemarkPainter->setDefaultLabelColor( labelColor );
Torsten Rahn's avatar
Torsten Rahn committed
599
600
601

    }

Patrick Spendrin's avatar
Patrick Spendrin committed
602
    m_placemarkPainter->drawPlacemarks( painter, m_paintOrder, selection, 
603
                                        viewParams->viewport() );
Torsten Rahn's avatar
 
Torsten Rahn committed
604
605
}

Patrick Spendrin's avatar
Patrick Spendrin committed
606
607
QRect PlacemarkLayout::roomForLabel( GeoDataStyle * style,
                                      const QVector<VisiblePlacemark*> &currentsec,
Torsten Rahn's avatar
 
Torsten Rahn committed
608
                                      const int x, const int y,
Torsten Rahn's avatar
   
Torsten Rahn committed
609
                                      const int textWidth )
Torsten Rahn's avatar
 
Torsten Rahn committed
610
611
{
    bool  isRoom      = false;
Torsten Rahn's avatar
   
Torsten Rahn committed
612

613
    int symbolwidth = style->iconStyle().icon().width();
Torsten Rahn's avatar
   
Torsten Rahn committed
614

615
    QFont labelFont = style->labelStyle().font();
Torsten Rahn's avatar
   
Torsten Rahn committed
616
    int textHeight = QFontMetrics( labelFont ).height();
Patrick Spendrin's avatar
Patrick Spendrin committed
617
//    mDebug() << textHeight;
Torsten Rahn's avatar
 
Torsten Rahn committed
618

619
    if ( style->labelStyle().alignment() == GeoDataLabelStyle::Corner ) {
Torsten Rahn's avatar
   
Torsten Rahn committed
620
621
        int  xpos = symbolwidth / 2 + x + 1;
        int  ypos = 0;
Torsten Rahn's avatar
 
Torsten Rahn committed
622

Torsten Rahn's avatar
   
Torsten Rahn committed
623
        // Check the four possible positions by going through all of them
Torsten Rahn's avatar
 
Torsten Rahn committed
624
 
Torsten Rahn's avatar
   
Torsten Rahn committed
625
626
627
628
629
630
631
632
633
634
635
        QRect  labelRect( xpos, ypos, textWidth, textHeight );
    
        while ( xpos >= x - textWidth - symbolwidth - 1 ) {
            ypos = y;

            while ( ypos >= y - textHeight ) {

                isRoom = true;
                labelRect.moveTo( xpos, ypos );

                // Check if there is another label or symbol that overlaps.
Patrick Spendrin's avatar
Patrick Spendrin committed
636
637
                QVector<VisiblePlacemark*>::const_iterator beforeItEnd = currentsec.constEnd();
                for ( QVector<VisiblePlacemark*>::ConstIterator beforeIt = currentsec.constBegin();
638
639
                      beforeIt != beforeItEnd;
                      ++beforeIt )
Torsten Rahn's avatar
   
Torsten Rahn committed
640
                {
641
                    if ( labelRect.intersects( (*beforeIt)->labelRect()) ) {
Torsten Rahn's avatar
   
Torsten Rahn committed
642
643
644
645
                        isRoom = false;
                        break;
                    }
                }
Torsten Rahn's avatar
 
Torsten Rahn committed
646

Torsten Rahn's avatar
   
Torsten Rahn committed
647
648
649
                if ( isRoom ) {
                    // claim the place immediately if it hasn't been used yet 
                    return labelRect;
Torsten Rahn's avatar
 
Torsten Rahn committed
650
651
                }

Torsten Rahn's avatar
   
Torsten Rahn committed
652
                ypos -= textHeight;
Torsten Rahn's avatar
 
Torsten Rahn committed
653
654
            }

Torsten Rahn's avatar
   
Torsten Rahn committed
655
656
657
            xpos -= ( symbolwidth + textWidth + 2 );
        }
    }
658
    else if ( style->labelStyle().alignment() == GeoDataLabelStyle::Center ) {
Torsten Rahn's avatar
   
Torsten Rahn committed
659
        isRoom = true;
Inge Wallin's avatar
Inge Wallin committed
660
661
        QRect  labelRect( x - textWidth / 2, y - textHeight / 2, 
                          textWidth, textHeight );
Torsten Rahn's avatar
   
Torsten Rahn committed
662
663

        // Check if there is another label or symbol that overlaps.
Patrick Spendrin's avatar
Patrick Spendrin committed
664
665
        QVector<VisiblePlacemark*>::const_iterator beforeItEnd = currentsec.constEnd();
        for ( QVector<VisiblePlacemark*>::ConstIterator beforeIt = currentsec.constBegin();
666
              beforeIt != beforeItEnd; ++beforeIt )
Torsten Rahn's avatar
   
Torsten Rahn committed
667
        {
668
            if ( labelRect.intersects( (*beforeIt)->labelRect() ) ) {
Torsten Rahn's avatar
   
Torsten Rahn committed
669
670
671
                isRoom = false;
                break;
            }
Torsten Rahn's avatar
 
Torsten Rahn committed
672
673
        }

Torsten Rahn's avatar
   
Torsten Rahn committed
674
675
676
677
        if ( isRoom ) {
            // claim the place immediately if it hasn't been used yet 
            return labelRect;
        }
Torsten Rahn's avatar
 
Torsten Rahn committed
678
679
680
681
682
683
    }

    return QRect(); // At this point there is no space left 
                    // for the rectangle anymore.
}

Patrick Spendrin's avatar
Patrick Spendrin committed
684
int PlacemarkLayout::placemarksOnScreenLimit() const
685
{
Torsten Rahn's avatar
   
Torsten Rahn committed
686
687
688
689
    // For now we just return 100.
    // Later on once we focus on decent high dpi print quality
    // we should replace this static value by a dynamic value
    // that takes the area that gets displayed into account.
690
691
692
    return 100;
}

Patrick Spendrin's avatar
Patrick Spendrin committed
693
#include "PlacemarkLayout.moc"