~ubuntu-branches/ubuntu/wily/qgis/wily

« back to all changes in this revision

Viewing changes to src/core/qgsmaprenderer.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Johan Van de Wauw
  • Date: 2010-07-11 20:23:24 UTC
  • mfrom: (3.1.4 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100711202324-5ktghxa7hracohmr
Tags: 1.4.0+12730-3ubuntu1
* Merge from Debian unstable (LP: #540941).
* Fix compilation issues with QT 4.7
* Add build-depends on libqt4-webkit-dev 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
    qgsmaprender.cpp  -  class for rendering map layer set
 
3
    ----------------------
 
4
    begin                : January 2006
 
5
    copyright            : (C) 2006 by Martin Dobias
 
6
    email                : wonder.sk at gmail dot com
 
7
 ***************************************************************************
 
8
 *                                                                         *
 
9
 *   This program is free software; you can redistribute it and/or modify  *
 
10
 *   it under the terms of the GNU General Public License as published by  *
 
11
 *   the Free Software Foundation; either version 2 of the License, or     *
 
12
 *   (at your option) any later version.                                   *
 
13
 *                                                                         *
 
14
 ***************************************************************************/
 
15
/* $Id$ */
 
16
 
 
17
#include <cmath>
 
18
#include <cfloat>
 
19
 
 
20
#include "qgscoordinatetransform.h"
 
21
#include "qgslogger.h"
 
22
#include "qgsmaprenderer.h"
 
23
#include "qgsscalecalculator.h"
 
24
#include "qgsmaptopixel.h"
 
25
#include "qgsmaplayer.h"
 
26
#include "qgsmaplayerregistry.h"
 
27
#include "qgsdistancearea.h"
 
28
#include "qgscentralpointpositionmanager.h"
 
29
#include "qgsoverlayobjectpositionmanager.h"
 
30
#include "qgspalobjectpositionmanager.h"
 
31
#include "qgsvectorlayer.h"
 
32
#include "qgsvectoroverlay.h"
 
33
 
 
34
 
 
35
#include <QDomDocument>
 
36
#include <QDomNode>
 
37
#include <QPainter>
 
38
#include <QListIterator>
 
39
#include <QSettings>
 
40
#include <QTime>
 
41
#include "qgslogger.h"
 
42
 
 
43
 
 
44
QgsMapRenderer::QgsMapRenderer()
 
45
{
 
46
  mScaleCalculator = new QgsScaleCalculator;
 
47
  mDistArea = new QgsDistanceArea;
 
48
 
 
49
  mDrawing = false;
 
50
  mOverview = false;
 
51
 
 
52
  // set default map units - we use WGS 84 thus use degrees
 
53
  setMapUnits( QGis::Degrees );
 
54
 
 
55
  mSize = QSize( 0, 0 );
 
56
 
 
57
  mProjectionsEnabled = FALSE;
 
58
  mDestCRS = new QgsCoordinateReferenceSystem( GEO_EPSG_CRS_ID, QgsCoordinateReferenceSystem::EpsgCrsId ); //WGS 84
 
59
 
 
60
  mOutputUnits = QgsMapRenderer::Millimeters;
 
61
 
 
62
  mLabelingEngine = NULL;
 
63
}
 
64
 
 
65
QgsMapRenderer::~QgsMapRenderer()
 
66
{
 
67
  delete mScaleCalculator;
 
68
  delete mDistArea;
 
69
  delete mDestCRS;
 
70
  delete mLabelingEngine;
 
71
}
 
72
 
 
73
 
 
74
QgsRectangle QgsMapRenderer::extent() const
 
75
{
 
76
  return mExtent;
 
77
}
 
78
 
 
79
void QgsMapRenderer::updateScale()
 
80
{
 
81
  mScale = mScaleCalculator->calculate( mExtent, mSize.width() );
 
82
}
 
83
 
 
84
bool QgsMapRenderer::setExtent( const QgsRectangle& extent )
 
85
{
 
86
  //remember the previous extent
 
87
  mLastExtent = mExtent;
 
88
 
 
89
  // Don't allow zooms where the current extent is so small that it
 
90
  // can't be accurately represented using a double (which is what
 
91
  // currentExtent uses). Excluding 0 avoids a divide by zero and an
 
92
  // infinite loop when rendering to a new canvas. Excluding extents
 
93
  // greater than 1 avoids doing unnecessary calculations.
 
94
 
 
95
  // The scheme is to compare the width against the mean x coordinate
 
96
  // (and height against mean y coordinate) and only allow zooms where
 
97
  // the ratio indicates that there is more than about 12 significant
 
98
  // figures (there are about 16 significant figures in a double).
 
99
 
 
100
  if ( extent.width()  > 0 &&
 
101
       extent.height() > 0 &&
 
102
       extent.width()  < 1 &&
 
103
       extent.height() < 1 )
 
104
  {
 
105
    // Use abs() on the extent to avoid the case where the extent is
 
106
    // symmetrical about 0.
 
107
    double xMean = ( fabs( extent.xMinimum() ) + fabs( extent.xMaximum() ) ) * 0.5;
 
108
    double yMean = ( fabs( extent.yMinimum() ) + fabs( extent.yMaximum() ) ) * 0.5;
 
109
 
 
110
    double xRange = extent.width() / xMean;
 
111
    double yRange = extent.height() / yMean;
 
112
 
 
113
    static const double minProportion = 1e-12;
 
114
    if ( xRange < minProportion || yRange < minProportion )
 
115
      return false;
 
116
  }
 
117
 
 
118
  mExtent = extent;
 
119
  if ( !extent.isEmpty() )
 
120
    adjustExtentToSize();
 
121
  return true;
 
122
}
 
123
 
 
124
 
 
125
 
 
126
void QgsMapRenderer::setOutputSize( QSize size, int dpi )
 
127
{
 
128
  mSize = size;
 
129
  mScaleCalculator->setDpi( dpi );
 
130
  adjustExtentToSize();
 
131
}
 
132
int QgsMapRenderer::outputDpi()
 
133
{
 
134
  return mScaleCalculator->dpi();
 
135
}
 
136
QSize QgsMapRenderer::outputSize()
 
137
{
 
138
  return mSize;
 
139
}
 
140
 
 
141
void QgsMapRenderer::adjustExtentToSize()
 
142
{
 
143
  int myHeight = mSize.height();
 
144
  int myWidth = mSize.width();
 
145
 
 
146
  QgsMapToPixel newCoordXForm;
 
147
 
 
148
  if ( !myWidth || !myHeight )
 
149
  {
 
150
    mScale = 1;
 
151
    newCoordXForm.setParameters( 0, 0, 0, 0 );
 
152
    return;
 
153
  }
 
154
 
 
155
  // calculate the translation and scaling parameters
 
156
  // mapUnitsPerPixel = map units per pixel
 
157
  double mapUnitsPerPixelY = static_cast<double>( mExtent.height() )
 
158
                             / static_cast<double>( myHeight );
 
159
  double mapUnitsPerPixelX = static_cast<double>( mExtent.width() )
 
160
                             / static_cast<double>( myWidth );
 
161
  mMapUnitsPerPixel = mapUnitsPerPixelY > mapUnitsPerPixelX ? mapUnitsPerPixelY : mapUnitsPerPixelX;
 
162
 
 
163
  // calculate the actual extent of the mapCanvas
 
164
  double dxmin, dxmax, dymin, dymax, whitespace;
 
165
 
 
166
  if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
 
167
  {
 
168
    dymin = mExtent.yMinimum();
 
169
    dymax = mExtent.yMaximum();
 
170
    whitespace = (( myWidth * mMapUnitsPerPixel ) - mExtent.width() ) * 0.5;
 
171
    dxmin = mExtent.xMinimum() - whitespace;
 
172
    dxmax = mExtent.xMaximum() + whitespace;
 
173
  }
 
174
  else
 
175
  {
 
176
    dxmin = mExtent.xMinimum();
 
177
    dxmax = mExtent.xMaximum();
 
178
    whitespace = (( myHeight * mMapUnitsPerPixel ) - mExtent.height() ) * 0.5;
 
179
    dymin = mExtent.yMinimum() - whitespace;
 
180
    dymax = mExtent.yMaximum() + whitespace;
 
181
  }
 
182
 
 
183
  QgsDebugMsg( QString( "Map units per pixel (x,y) : %1, %2\n" ).arg( mapUnitsPerPixelX ).arg( mapUnitsPerPixelY ) );
 
184
  QgsDebugMsg( QString( "Pixmap dimensions (x,y) : %1, %2\n" ).arg( myWidth ).arg( myHeight ) );
 
185
  QgsDebugMsg( QString( "Extent dimensions (x,y) : %1, %2\n" ).arg( mExtent.width() ).arg( mExtent.height() ) );
 
186
  QgsDebugMsg( mExtent.toString() );
 
187
 
 
188
  // update extent
 
189
  mExtent.setXMinimum( dxmin );
 
190
  mExtent.setXMaximum( dxmax );
 
191
  mExtent.setYMinimum( dymin );
 
192
  mExtent.setYMaximum( dymax );
 
193
 
 
194
  // update the scale
 
195
  updateScale();
 
196
 
 
197
  QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( mScale ) );
 
198
 
 
199
  newCoordXForm.setParameters( mMapUnitsPerPixel, dxmin, dymin, myHeight );
 
200
  mRenderContext.setMapToPixel( newCoordXForm );
 
201
  mRenderContext.setExtent( mExtent );
 
202
}
 
203
 
 
204
 
 
205
void QgsMapRenderer::render( QPainter* painter )
 
206
{
 
207
  //flag to see if the render context has changed
 
208
  //since the last time we rendered. If it hasnt changed we can
 
209
  //take some shortcuts with rendering
 
210
  bool mySameAsLastFlag = true;
 
211
 
 
212
  QgsDebugMsg( "========== Rendering ==========" );
 
213
 
 
214
  if ( mExtent.isEmpty() )
 
215
  {
 
216
    QgsDebugMsg( "empty extent... not rendering" );
 
217
    return;
 
218
  }
 
219
 
 
220
  if ( mDrawing )
 
221
  {
 
222
    return;
 
223
  }
 
224
 
 
225
  QPaintDevice* thePaintDevice = painter->device();
 
226
  if ( !thePaintDevice )
 
227
  {
 
228
    return;
 
229
  }
 
230
 
 
231
  mDrawing = true;
 
232
 
 
233
  QgsCoordinateTransform* ct;
 
234
 
 
235
#ifdef QGISDEBUG
 
236
  QgsDebugMsg( "Starting to render layer stack." );
 
237
  QTime renderTime;
 
238
  renderTime.start();
 
239
#endif
 
240
 
 
241
  mRenderContext.setDrawEditingInformation( !mOverview );
 
242
  mRenderContext.setPainter( painter );
 
243
  mRenderContext.setCoordinateTransform( 0 );
 
244
  //this flag is only for stopping during the current rendering progress,
 
245
  //so must be false at every new render operation
 
246
  mRenderContext.setRenderingStopped( false );
 
247
 
 
248
  //calculate scale factor
 
249
  //use the specified dpi and not those from the paint device
 
250
  //because sometimes QPainter units are in a local coord sys (e.g. in case of QGraphicsScene)
 
251
  double sceneDpi = mScaleCalculator->dpi();
 
252
  double scaleFactor = 1.0;
 
253
  if ( mOutputUnits == QgsMapRenderer::Millimeters )
 
254
  {
 
255
    scaleFactor = sceneDpi / 25.4;
 
256
  }
 
257
  double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
 
258
  if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
 
259
  {
 
260
    mRenderContext.setRasterScaleFactor( rasterScaleFactor );
 
261
    mySameAsLastFlag = false;
 
262
  }
 
263
  if ( mRenderContext.scaleFactor() != scaleFactor )
 
264
  {
 
265
    mRenderContext.setScaleFactor( scaleFactor );
 
266
    mySameAsLastFlag = false;
 
267
  }
 
268
  if ( mRenderContext.rendererScale() != mScale )
 
269
  {
 
270
    //add map scale to render context
 
271
    mRenderContext.setRendererScale( mScale );
 
272
    mySameAsLastFlag = false;
 
273
  }
 
274
  if ( mLastExtent != mExtent )
 
275
  {
 
276
    mLastExtent = mExtent;
 
277
    mySameAsLastFlag = false;
 
278
  }
 
279
 
 
280
  mRenderContext.setLabelingEngine( mLabelingEngine );
 
281
  if ( mLabelingEngine )
 
282
    mLabelingEngine->init();
 
283
 
 
284
  // know we know if this render is just a repeat of the last time, we
 
285
  // can clear caches if it has changed
 
286
  if ( !mySameAsLastFlag )
 
287
  {
 
288
    //clear the cache pixmap if we changed resolution / extent
 
289
    QSettings mySettings;
 
290
    if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
 
291
    {
 
292
      QgsMapLayerRegistry::instance()->clearAllLayerCaches();
 
293
    }
 
294
  }
 
295
 
 
296
  bool placeOverlays = false;
 
297
  QgsOverlayObjectPositionManager* overlayManager = overlayManagerFromSettings();
 
298
  QList<QgsVectorOverlay*> allOverlayList; //list of all overlays, used to draw them after layers have been rendered
 
299
  if ( overlayManager )
 
300
  {
 
301
    placeOverlays = true;
 
302
  }
 
303
 
 
304
  // render all layers in the stack, starting at the base
 
305
  QListIterator<QString> li( mLayerSet );
 
306
  li.toBack();
 
307
 
 
308
  QgsRectangle r1, r2;
 
309
 
 
310
  while ( li.hasPrevious() )
 
311
  {
 
312
    if ( mRenderContext.renderingStopped() )
 
313
    {
 
314
      break;
 
315
    }
 
316
 
 
317
    // Store the painter in case we need to swap it out for the
 
318
    // cache painter
 
319
    QPainter * mypContextPainter = mRenderContext.painter();
 
320
 
 
321
    QString layerId = li.previous();
 
322
 
 
323
    QgsDebugMsg( "Rendering at layer item " + layerId );
 
324
 
 
325
    // This call is supposed to cause the progress bar to
 
326
    // advance. However, it seems that updating the progress bar is
 
327
    // incompatible with having a QPainter active (the one that is
 
328
    // passed into this function), as Qt produces a number of errors
 
329
    // when try to do so. I'm (Gavin) not sure how to fix this, but
 
330
    // added these comments and debug statement to help others...
 
331
    QgsDebugMsg( "If there is a QPaintEngine error here, it is caused by an emit call" );
 
332
 
 
333
    //emit drawingProgress(myRenderCounter++, mLayerSet.size());
 
334
    QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
 
335
 
 
336
    if ( !ml )
 
337
    {
 
338
      QgsDebugMsg( "Layer not found in registry!" );
 
339
      continue;
 
340
    }
 
341
 
 
342
    QgsDebugMsg( "Rendering layer " + ml->name() );
 
343
    QgsDebugMsg( "  Layer minscale " + QString( "%1" ).arg( ml->minimumScale() ) );
 
344
    QgsDebugMsg( "  Layer maxscale " + QString( "%1" ).arg( ml->maximumScale() ) );
 
345
    QgsDebugMsg( "  Scale dep. visibility enabled? " + QString( "%1" ).arg( ml->hasScaleBasedVisibility() ) );
 
346
    QgsDebugMsg( "  Input extent: " + ml->extent().toString() );
 
347
 
 
348
    if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) || mOverview )
 
349
    {
 
350
      connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
 
351
 
 
352
      //
 
353
      // Now do the call to the layer that actually does
 
354
      // the rendering work!
 
355
      //
 
356
 
 
357
      bool split = false;
 
358
 
 
359
      if ( hasCrsTransformEnabled() )
 
360
      {
 
361
        r1 = mExtent;
 
362
        split = splitLayersExtent( ml, r1, r2 );
 
363
        ct = new QgsCoordinateTransform( ml->srs(), *mDestCRS );
 
364
        mRenderContext.setExtent( r1 );
 
365
      }
 
366
      else
 
367
      {
 
368
        ct = NULL;
 
369
      }
 
370
 
 
371
      mRenderContext.setCoordinateTransform( ct );
 
372
 
 
373
      //decide if we have to scale the raster
 
374
      //this is necessary in case QGraphicsScene is used
 
375
      bool scaleRaster = false;
 
376
      QgsMapToPixel rasterMapToPixel;
 
377
      QgsMapToPixel bk_mapToPixel;
 
378
 
 
379
      if ( ml->type() == QgsMapLayer::RasterLayer && fabs( rasterScaleFactor - 1.0 ) > 0.000001 )
 
380
      {
 
381
        scaleRaster = true;
 
382
      }
 
383
 
 
384
 
 
385
      //create overlay objects for features within the view extent
 
386
      if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager )
 
387
      {
 
388
        QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
 
389
        if ( vl )
 
390
        {
 
391
          QList<QgsVectorOverlay*> thisLayerOverlayList;
 
392
          vl->vectorOverlays( thisLayerOverlayList );
 
393
 
 
394
          QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin();
 
395
          for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt )
 
396
          {
 
397
            if (( *overlayIt )->displayFlag() )
 
398
            {
 
399
              ( *overlayIt )->createOverlayObjects( mRenderContext );
 
400
              allOverlayList.push_back( *overlayIt );
 
401
            }
 
402
          }
 
403
 
 
404
          overlayManager->addLayer( vl, thisLayerOverlayList );
 
405
        }
 
406
      }
 
407
 
 
408
      // Force render of layers that are being edited
 
409
      // or if there's a labeling engine that needs the layer to register features
 
410
      if ( ml->type() == QgsMapLayer::VectorLayer )
 
411
      {
 
412
        QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
 
413
        if ( vl->isEditable() ||
 
414
             ( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) )
 
415
        {
 
416
          ml->setCacheImage( 0 );
 
417
        }
 
418
      }
 
419
 
 
420
      QSettings mySettings;
 
421
      if ( ! split )//render caching does not yet cater for split extents
 
422
      {
 
423
        if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
 
424
        {
 
425
          if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
 
426
          {
 
427
            QgsDebugMsg( "\n\n\nCaching enabled but layer redraw forced by extent change or empty cache\n\n\n" );
 
428
            QImage * mypImage = new QImage( mRenderContext.painter()->device()->width(),
 
429
                                            mRenderContext.painter()->device()->height(), QImage::Format_ARGB32 );
 
430
            mypImage->fill( 0 );
 
431
            ml->setCacheImage( mypImage ); //no need to delete the old one, maplayer does it for you
 
432
            QPainter * mypPainter = new QPainter( ml->cacheImage() );
 
433
            if ( mySettings.value( "/qgis/enable_anti_aliasing", false ).toBool() )
 
434
            {
 
435
              mypPainter->setRenderHint( QPainter::Antialiasing );
 
436
            }
 
437
            mRenderContext.setPainter( mypPainter );
 
438
          }
 
439
          else if ( mySameAsLastFlag )
 
440
          {
 
441
            //draw from cached image
 
442
            QgsDebugMsg( "\n\n\nCaching enabled --- drawing layer from cached image\n\n\n" );
 
443
            mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
 
444
            disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
 
445
            //short circuit as there is nothing else to do...
 
446
            continue;
 
447
          }
 
448
        }
 
449
      }
 
450
 
 
451
      if ( scaleRaster )
 
452
      {
 
453
        bk_mapToPixel = mRenderContext.mapToPixel();
 
454
        rasterMapToPixel = mRenderContext.mapToPixel();
 
455
        rasterMapToPixel.setMapUnitsPerPixel( mRenderContext.mapToPixel().mapUnitsPerPixel() / rasterScaleFactor );
 
456
        rasterMapToPixel.setYMaximum( mSize.height() * rasterScaleFactor );
 
457
        mRenderContext.setMapToPixel( rasterMapToPixel );
 
458
        mRenderContext.painter()->save();
 
459
        mRenderContext.painter()->scale( 1.0 / rasterScaleFactor, 1.0 / rasterScaleFactor );
 
460
      }
 
461
 
 
462
 
 
463
      if ( !ml->draw( mRenderContext ) )
 
464
      {
 
465
        emit drawError( ml );
 
466
      }
 
467
      else
 
468
      {
 
469
        QgsDebugMsg( "Layer rendered without issues" );
 
470
      }
 
471
 
 
472
      if ( split )
 
473
      {
 
474
        mRenderContext.setExtent( r2 );
 
475
        if ( !ml->draw( mRenderContext ) )
 
476
        {
 
477
          emit drawError( ml );
 
478
        }
 
479
      }
 
480
 
 
481
      if ( scaleRaster )
 
482
      {
 
483
        mRenderContext.setMapToPixel( bk_mapToPixel );
 
484
        mRenderContext.painter()->restore();
 
485
      }
 
486
 
 
487
      if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
 
488
      {
 
489
        if ( !split )
 
490
        {
 
491
          // composite the cached image into our view and then clean up from caching
 
492
          // by reinstating the painter as it was swapped out for caching renders
 
493
          delete mRenderContext.painter();
 
494
          mRenderContext.setPainter( mypContextPainter );
 
495
          //draw from cached image that we created further up
 
496
          mypContextPainter->drawImage( 0, 0, *( ml->cacheImage() ) );
 
497
        }
 
498
      }
 
499
      disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
 
500
    }
 
501
    else // layer not visible due to scale
 
502
    {
 
503
      QgsDebugMsg( "Layer not rendered because it is not within the defined "
 
504
                   "visibility scale range" );
 
505
    }
 
506
 
 
507
  } // while (li.hasPrevious())
 
508
 
 
509
  QgsDebugMsg( "Done rendering map layers" );
 
510
 
 
511
  if ( !mOverview )
 
512
  {
 
513
    // render all labels for vector layers in the stack, starting at the base
 
514
    li.toBack();
 
515
    while ( li.hasPrevious() )
 
516
    {
 
517
      if ( mRenderContext.renderingStopped() )
 
518
      {
 
519
        break;
 
520
      }
 
521
 
 
522
      QString layerId = li.previous();
 
523
 
 
524
      // TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
 
525
      QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
 
526
 
 
527
      if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
 
528
      {
 
529
        // only make labels if the layer is visible
 
530
        // after scale dep viewing settings are checked
 
531
        if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) )
 
532
        {
 
533
          bool split = false;
 
534
 
 
535
          if ( hasCrsTransformEnabled() )
 
536
          {
 
537
            QgsRectangle r1 = mExtent;
 
538
            split = splitLayersExtent( ml, r1, r2 );
 
539
            ct = new QgsCoordinateTransform( ml->srs(), *mDestCRS );
 
540
            mRenderContext.setExtent( r1 );
 
541
          }
 
542
          else
 
543
          {
 
544
            ct = NULL;
 
545
          }
 
546
 
 
547
          mRenderContext.setCoordinateTransform( ct );
 
548
 
 
549
          ml->drawLabels( mRenderContext );
 
550
          if ( split )
 
551
          {
 
552
            mRenderContext.setExtent( r2 );
 
553
            ml->drawLabels( mRenderContext );
 
554
          }
 
555
        }
 
556
      }
 
557
    }
 
558
  } // if (!mOverview)
 
559
 
 
560
  //find overlay positions and draw the vector overlays
 
561
  if ( overlayManager && allOverlayList.size() > 0 )
 
562
  {
 
563
    overlayManager->findObjectPositions( mRenderContext, mScaleCalculator->mapUnits() );
 
564
    //draw all the overlays
 
565
    QList<QgsVectorOverlay*>::iterator allOverlayIt = allOverlayList.begin();
 
566
    for ( ; allOverlayIt != allOverlayList.end(); ++allOverlayIt )
 
567
    {
 
568
      ( *allOverlayIt )->drawOverlayObjects( mRenderContext );
 
569
    }
 
570
    overlayManager->removeLayers();
 
571
  }
 
572
 
 
573
  delete overlayManager;
 
574
  // make sure progress bar arrives at 100%!
 
575
  emit drawingProgress( 1, 1 );
 
576
 
 
577
  if ( mLabelingEngine )
 
578
  {
 
579
    // set correct extent
 
580
    mRenderContext.setExtent( mExtent );
 
581
    mRenderContext.setCoordinateTransform( NULL );
 
582
 
 
583
    mLabelingEngine->drawLabeling( mRenderContext );
 
584
    mLabelingEngine->exit();
 
585
  }
 
586
 
 
587
  QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
 
588
 
 
589
  mDrawing = false;
 
590
 
 
591
}
 
592
 
 
593
void QgsMapRenderer::setMapUnits( QGis::UnitType u )
 
594
{
 
595
  mScaleCalculator->setMapUnits( u );
 
596
 
 
597
  // Since the map units have changed, force a recalculation of the scale.
 
598
  updateScale();
 
599
 
 
600
  emit mapUnitsChanged();
 
601
}
 
602
 
 
603
QGis::UnitType QgsMapRenderer::mapUnits() const
 
604
{
 
605
  return mScaleCalculator->mapUnits();
 
606
}
 
607
 
 
608
void QgsMapRenderer::onDrawingProgress( int current, int total )
 
609
{
 
610
  // TODO: emit signal with progress
 
611
// QgsDebugMsg(QString("onDrawingProgress: %1 / %2").arg(current).arg(total));
 
612
  emit updateMap();
 
613
}
 
614
 
 
615
 
 
616
 
 
617
void QgsMapRenderer::setProjectionsEnabled( bool enabled )
 
618
{
 
619
  if ( mProjectionsEnabled != enabled )
 
620
  {
 
621
    mProjectionsEnabled = enabled;
 
622
    QgsDebugMsg( "Adjusting DistArea projection on/off" );
 
623
    mDistArea->setProjectionsEnabled( enabled );
 
624
    updateFullExtent();
 
625
    emit hasCrsTransformEnabled( enabled );
 
626
  }
 
627
}
 
628
 
 
629
bool QgsMapRenderer::hasCrsTransformEnabled()
 
630
{
 
631
  return mProjectionsEnabled;
 
632
}
 
633
 
 
634
void QgsMapRenderer::setDestinationSrs( const QgsCoordinateReferenceSystem& srs )
 
635
{
 
636
  QgsDebugMsg( "* Setting destCRS : = " + srs.toProj4() );
 
637
  QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( srs.srsid() ) );
 
638
  if ( *mDestCRS != srs )
 
639
  {
 
640
    QgsDebugMsg( "Setting DistArea CRS to " + QString::number( srs.srsid() ) );
 
641
    mDistArea->setSourceCrs( srs.srsid() );
 
642
    *mDestCRS = srs;
 
643
    updateFullExtent();
 
644
    emit destinationSrsChanged();
 
645
  }
 
646
}
 
647
 
 
648
const QgsCoordinateReferenceSystem& QgsMapRenderer::destinationSrs()
 
649
{
 
650
  QgsDebugMsg( "* Returning destCRS" );
 
651
  QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( mDestCRS->srsid() ) );
 
652
  QgsDebugMsg( "* DestCRS.proj4() = " + mDestCRS->toProj4() );
 
653
  return *mDestCRS;
 
654
}
 
655
 
 
656
 
 
657
bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 )
 
658
{
 
659
  bool split = false;
 
660
 
 
661
  if ( hasCrsTransformEnabled() )
 
662
  {
 
663
    try
 
664
    {
 
665
      QgsCoordinateTransform tr( layer->srs(), *mDestCRS );
 
666
 
 
667
#ifdef QGISDEBUG
 
668
      // QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
 
669
#endif
 
670
      // Split the extent into two if the source CRS is
 
671
      // geographic and the extent crosses the split in
 
672
      // geographic coordinates (usually +/- 180 degrees,
 
673
      // and is assumed to be so here), and draw each
 
674
      // extent separately.
 
675
      static const double splitCoord = 180.0;
 
676
 
 
677
      if ( tr.sourceCrs().geographicFlag() )
 
678
      {
 
679
        // Note: ll = lower left point
 
680
        //   and ur = upper right point
 
681
        QgsPoint ll = tr.transform( extent.xMinimum(), extent.yMinimum(),
 
682
                                    QgsCoordinateTransform::ReverseTransform );
 
683
 
 
684
        QgsPoint ur = tr.transform( extent.xMaximum(), extent.yMaximum(),
 
685
                                    QgsCoordinateTransform::ReverseTransform );
 
686
 
 
687
        if ( ll.x() > ur.x() )
 
688
        {
 
689
          extent.set( ll, QgsPoint( splitCoord, ur.y() ) );
 
690
          r2.set( QgsPoint( -splitCoord, ll.y() ), ur );
 
691
          split = true;
 
692
        }
 
693
        else // no need to split
 
694
        {
 
695
          extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
 
696
        }
 
697
      }
 
698
      else // can't cross 180
 
699
      {
 
700
        extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
 
701
      }
 
702
    }
 
703
    catch ( QgsCsException &cse )
 
704
    {
 
705
      Q_UNUSED( cse );
 
706
      QgsDebugMsg( "Transform error caught" );
 
707
      extent = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
 
708
      r2     = QgsRectangle( -DBL_MAX, -DBL_MAX, DBL_MAX, DBL_MAX );
 
709
    }
 
710
  }
 
711
  return split;
 
712
}
 
713
 
 
714
 
 
715
QgsRectangle QgsMapRenderer::layerExtentToOutputExtent( QgsMapLayer* theLayer, QgsRectangle extent )
 
716
{
 
717
  if ( hasCrsTransformEnabled() )
 
718
  {
 
719
    try
 
720
    {
 
721
      QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
 
722
      extent = tr.transformBoundingBox( extent );
 
723
    }
 
724
    catch ( QgsCsException &cse )
 
725
    {
 
726
      Q_UNUSED( cse );
 
727
      QgsDebugMsg( QString( "Transform error caught: " ).arg( cse.what() ) );
 
728
    }
 
729
  }
 
730
  else
 
731
  {
 
732
    // leave extent unchanged
 
733
  }
 
734
 
 
735
  return extent;
 
736
}
 
737
 
 
738
QgsPoint QgsMapRenderer::layerToMapCoordinates( QgsMapLayer* theLayer, QgsPoint point )
 
739
{
 
740
  if ( hasCrsTransformEnabled() )
 
741
  {
 
742
    try
 
743
    {
 
744
      QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
 
745
      point = tr.transform( point, QgsCoordinateTransform::ForwardTransform );
 
746
    }
 
747
    catch ( QgsCsException &cse )
 
748
    {
 
749
      Q_UNUSED( cse );
 
750
      QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
 
751
    }
 
752
  }
 
753
  else
 
754
  {
 
755
    // leave point without transformation
 
756
  }
 
757
  return point;
 
758
}
 
759
 
 
760
QgsPoint QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsPoint point )
 
761
{
 
762
  if ( hasCrsTransformEnabled() )
 
763
  {
 
764
    try
 
765
    {
 
766
      QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
 
767
      point = tr.transform( point, QgsCoordinateTransform::ReverseTransform );
 
768
    }
 
769
    catch ( QgsCsException &cse )
 
770
    {
 
771
      QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
 
772
      throw cse; //let client classes know there was a transformation error
 
773
    }
 
774
  }
 
775
  else
 
776
  {
 
777
    // leave point without transformation
 
778
  }
 
779
  return point;
 
780
}
 
781
 
 
782
QgsRectangle QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsRectangle rect )
 
783
{
 
784
  if ( hasCrsTransformEnabled() )
 
785
  {
 
786
    try
 
787
    {
 
788
      QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
 
789
      rect = tr.transform( rect, QgsCoordinateTransform::ReverseTransform );
 
790
    }
 
791
    catch ( QgsCsException &cse )
 
792
    {
 
793
      QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
 
794
      throw cse; //let client classes know there was a transformation error
 
795
    }
 
796
  }
 
797
  return rect;
 
798
}
 
799
 
 
800
 
 
801
void QgsMapRenderer::updateFullExtent()
 
802
{
 
803
  QgsDebugMsg( "called." );
 
804
  QgsMapLayerRegistry* registry = QgsMapLayerRegistry::instance();
 
805
 
 
806
  // reset the map canvas extent since the extent may now be smaller
 
807
  // We can't use a constructor since QgsRectangle normalizes the rectangle upon construction
 
808
  mFullExtent.setMinimal();
 
809
 
 
810
  // iterate through the map layers and test each layers extent
 
811
  // against the current min and max values
 
812
  QStringList::iterator it = mLayerSet.begin();
 
813
  while ( it != mLayerSet.end() )
 
814
  {
 
815
    QgsMapLayer * lyr = registry->mapLayer( *it );
 
816
    if ( lyr == NULL )
 
817
    {
 
818
      QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
 
819
    }
 
820
    else
 
821
    {
 
822
      QgsDebugMsg( "Updating extent using " + lyr->name() );
 
823
      QgsDebugMsg( "Input extent: " + lyr->extent().toString() );
 
824
 
 
825
      // Layer extents are stored in the coordinate system (CS) of the
 
826
      // layer. The extent must be projected to the canvas CS
 
827
      QgsRectangle extent = layerExtentToOutputExtent( lyr, lyr->extent() );
 
828
 
 
829
      QgsDebugMsg( "Output extent: " + extent.toString() );
 
830
      mFullExtent.unionRect( extent );
 
831
 
 
832
    }
 
833
    it++;
 
834
  }
 
835
 
 
836
  if ( mFullExtent.width() == 0.0 || mFullExtent.height() == 0.0 )
 
837
  {
 
838
    // If all of the features are at the one point, buffer the
 
839
    // rectangle a bit. If they are all at zero, do something a bit
 
840
    // more crude.
 
841
 
 
842
    if ( mFullExtent.xMinimum() == 0.0 && mFullExtent.xMaximum() == 0.0 &&
 
843
         mFullExtent.yMinimum() == 0.0 && mFullExtent.yMaximum() == 0.0 )
 
844
    {
 
845
      mFullExtent.set( -1.0, -1.0, 1.0, 1.0 );
 
846
    }
 
847
    else
 
848
    {
 
849
      const double padFactor = 1e-8;
 
850
      double widthPad = mFullExtent.xMinimum() * padFactor;
 
851
      double heightPad = mFullExtent.yMinimum() * padFactor;
 
852
      double xmin = mFullExtent.xMinimum() - widthPad;
 
853
      double xmax = mFullExtent.xMaximum() + widthPad;
 
854
      double ymin = mFullExtent.yMinimum() - heightPad;
 
855
      double ymax = mFullExtent.yMaximum() + heightPad;
 
856
      mFullExtent.set( xmin, ymin, xmax, ymax );
 
857
    }
 
858
  }
 
859
 
 
860
  QgsDebugMsg( "Full extent: " + mFullExtent.toString() );
 
861
}
 
862
 
 
863
QgsRectangle QgsMapRenderer::fullExtent()
 
864
{
 
865
  updateFullExtent();
 
866
  return mFullExtent;
 
867
}
 
868
 
 
869
void QgsMapRenderer::setLayerSet( const QStringList& layers )
 
870
{
 
871
  mLayerSet = layers;
 
872
  updateFullExtent();
 
873
}
 
874
 
 
875
QStringList& QgsMapRenderer::layerSet()
 
876
{
 
877
  return mLayerSet;
 
878
}
 
879
 
 
880
QgsOverlayObjectPositionManager* QgsMapRenderer::overlayManagerFromSettings()
 
881
{
 
882
  QSettings settings;
 
883
  QString overlayAlgorithmQString = settings.value( "qgis/overlayPlacementAlgorithm", "Central point" ).toString();
 
884
 
 
885
  QgsOverlayObjectPositionManager* result = 0;
 
886
 
 
887
  if ( overlayAlgorithmQString != "Central point" )
 
888
  {
 
889
    QgsPALObjectPositionManager* palManager = new QgsPALObjectPositionManager();
 
890
    if ( overlayAlgorithmQString == "Chain" )
 
891
    {
 
892
      palManager->setPlacementAlgorithm( "Chain" );
 
893
    }
 
894
    else if ( overlayAlgorithmQString == "Popmusic tabu chain" )
 
895
    {
 
896
      palManager->setPlacementAlgorithm( "Popmusic tabu chain" );
 
897
    }
 
898
    else if ( overlayAlgorithmQString == "Popmusic tabu" )
 
899
    {
 
900
      palManager->setPlacementAlgorithm( "Popmusic tabu" );
 
901
    }
 
902
    else if ( overlayAlgorithmQString == "Popmusic chain" )
 
903
    {
 
904
      palManager->setPlacementAlgorithm( "Popmusic chain" );
 
905
    }
 
906
    result = palManager;
 
907
  }
 
908
  else
 
909
  {
 
910
    result = new QgsCentralPointPositionManager();
 
911
  }
 
912
 
 
913
  return result;
 
914
}
 
915
 
 
916
bool QgsMapRenderer::readXML( QDomNode & theNode )
 
917
{
 
918
  QDomNode myNode = theNode.namedItem( "units" );
 
919
  QDomElement element = myNode.toElement();
 
920
 
 
921
  // set units
 
922
  QGis::UnitType units;
 
923
  if ( "meters" == element.text() )
 
924
  {
 
925
    units = QGis::Meters;
 
926
  }
 
927
  else if ( "feet" == element.text() )
 
928
  {
 
929
    units = QGis::Feet;
 
930
  }
 
931
  else if ( "degrees" == element.text() )
 
932
  {
 
933
    units = QGis::Degrees;
 
934
  }
 
935
  else if ( "unknown" == element.text() )
 
936
  {
 
937
    units = QGis::UnknownUnit;
 
938
  }
 
939
  else
 
940
  {
 
941
    QgsDebugMsg( "Unknown map unit type " + element.text() );
 
942
    units = QGis::Degrees;
 
943
  }
 
944
  setMapUnits( units );
 
945
 
 
946
 
 
947
  // set extent
 
948
  QgsRectangle aoi;
 
949
  QDomNode extentNode = theNode.namedItem( "extent" );
 
950
 
 
951
  QDomNode xminNode = extentNode.namedItem( "xmin" );
 
952
  QDomNode yminNode = extentNode.namedItem( "ymin" );
 
953
  QDomNode xmaxNode = extentNode.namedItem( "xmax" );
 
954
  QDomNode ymaxNode = extentNode.namedItem( "ymax" );
 
955
 
 
956
  QDomElement exElement = xminNode.toElement();
 
957
  double xmin = exElement.text().toDouble();
 
958
  aoi.setXMinimum( xmin );
 
959
 
 
960
  exElement = yminNode.toElement();
 
961
  double ymin = exElement.text().toDouble();
 
962
  aoi.setYMinimum( ymin );
 
963
 
 
964
  exElement = xmaxNode.toElement();
 
965
  double xmax = exElement.text().toDouble();
 
966
  aoi.setXMaximum( xmax );
 
967
 
 
968
  exElement = ymaxNode.toElement();
 
969
  double ymax = exElement.text().toDouble();
 
970
  aoi.setYMaximum( ymax );
 
971
 
 
972
  setExtent( aoi );
 
973
 
 
974
  // set projections flag
 
975
  QDomNode projNode = theNode.namedItem( "projections" );
 
976
  element = projNode.toElement();
 
977
  setProjectionsEnabled( element.text().toInt() );
 
978
 
 
979
  // set destination CRS
 
980
  QgsCoordinateReferenceSystem srs;
 
981
  QDomNode srsNode = theNode.namedItem( "destinationsrs" );
 
982
  srs.readXML( srsNode );
 
983
  setDestinationSrs( srs );
 
984
 
 
985
  return true;
 
986
}
 
987
 
 
988
bool QgsMapRenderer::writeXML( QDomNode & theNode, QDomDocument & theDoc )
 
989
{
 
990
  // units
 
991
 
 
992
  QDomElement unitsNode = theDoc.createElement( "units" );
 
993
  theNode.appendChild( unitsNode );
 
994
 
 
995
  QString unitsString;
 
996
 
 
997
  switch ( mapUnits() )
 
998
  {
 
999
    case QGis::Meters:
 
1000
      unitsString = "meters";
 
1001
      break;
 
1002
    case QGis::Feet:
 
1003
      unitsString = "feet";
 
1004
      break;
 
1005
    case QGis::Degrees:
 
1006
      unitsString = "degrees";
 
1007
      break;
 
1008
    case QGis::UnknownUnit:
 
1009
    default:
 
1010
      unitsString = "unknown";
 
1011
      break;
 
1012
  }
 
1013
  QDomText unitsText = theDoc.createTextNode( unitsString );
 
1014
  unitsNode.appendChild( unitsText );
 
1015
 
 
1016
 
 
1017
  // Write current view extents
 
1018
  QDomElement extentNode = theDoc.createElement( "extent" );
 
1019
  theNode.appendChild( extentNode );
 
1020
 
 
1021
  QDomElement xMin = theDoc.createElement( "xmin" );
 
1022
  QDomElement yMin = theDoc.createElement( "ymin" );
 
1023
  QDomElement xMax = theDoc.createElement( "xmax" );
 
1024
  QDomElement yMax = theDoc.createElement( "ymax" );
 
1025
 
 
1026
  QgsRectangle r = extent();
 
1027
  QDomText xMinText = theDoc.createTextNode( QString::number( r.xMinimum(), 'f' ) );
 
1028
  QDomText yMinText = theDoc.createTextNode( QString::number( r.yMinimum(), 'f' ) );
 
1029
  QDomText xMaxText = theDoc.createTextNode( QString::number( r.xMaximum(), 'f' ) );
 
1030
  QDomText yMaxText = theDoc.createTextNode( QString::number( r.yMaximum(), 'f' ) );
 
1031
 
 
1032
  xMin.appendChild( xMinText );
 
1033
  yMin.appendChild( yMinText );
 
1034
  xMax.appendChild( xMaxText );
 
1035
  yMax.appendChild( yMaxText );
 
1036
 
 
1037
  extentNode.appendChild( xMin );
 
1038
  extentNode.appendChild( yMin );
 
1039
  extentNode.appendChild( xMax );
 
1040
  extentNode.appendChild( yMax );
 
1041
 
 
1042
  // projections enabled
 
1043
  QDomElement projNode = theDoc.createElement( "projections" );
 
1044
  theNode.appendChild( projNode );
 
1045
 
 
1046
  QDomText projText = theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) );
 
1047
  projNode.appendChild( projText );
 
1048
 
 
1049
  // destination CRS
 
1050
  QDomElement srsNode = theDoc.createElement( "destinationsrs" );
 
1051
  theNode.appendChild( srsNode );
 
1052
  destinationSrs().writeXML( srsNode, theDoc );
 
1053
 
 
1054
  return true;
 
1055
}
 
1056
 
 
1057
void QgsMapRenderer::setLabelingEngine( QgsLabelingEngineInterface* iface )
 
1058
{
 
1059
  if ( mLabelingEngine )
 
1060
    delete mLabelingEngine;
 
1061
 
 
1062
  mLabelingEngine = iface;
 
1063
}