1
/***************************************************************************
2
qgsmaprender.cpp - class for rendering map layer set
5
copyright : (C) 2006 by Martin Dobias
6
email : wonder.sk at gmail dot com
7
***************************************************************************
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. *
14
***************************************************************************/
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"
35
#include <QDomDocument>
38
#include <QListIterator>
41
#include "qgslogger.h"
44
QgsMapRenderer::QgsMapRenderer()
46
mScaleCalculator = new QgsScaleCalculator;
47
mDistArea = new QgsDistanceArea;
52
// set default map units - we use WGS 84 thus use degrees
53
setMapUnits( QGis::Degrees );
55
mSize = QSize( 0, 0 );
57
mProjectionsEnabled = FALSE;
58
mDestCRS = new QgsCoordinateReferenceSystem( GEO_EPSG_CRS_ID, QgsCoordinateReferenceSystem::EpsgCrsId ); //WGS 84
60
mOutputUnits = QgsMapRenderer::Millimeters;
62
mLabelingEngine = NULL;
65
QgsMapRenderer::~QgsMapRenderer()
67
delete mScaleCalculator;
70
delete mLabelingEngine;
74
QgsRectangle QgsMapRenderer::extent() const
79
void QgsMapRenderer::updateScale()
81
mScale = mScaleCalculator->calculate( mExtent, mSize.width() );
84
bool QgsMapRenderer::setExtent( const QgsRectangle& extent )
86
//remember the previous extent
87
mLastExtent = mExtent;
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.
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).
100
if ( extent.width() > 0 &&
101
extent.height() > 0 &&
102
extent.width() < 1 &&
103
extent.height() < 1 )
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;
110
double xRange = extent.width() / xMean;
111
double yRange = extent.height() / yMean;
113
static const double minProportion = 1e-12;
114
if ( xRange < minProportion || yRange < minProportion )
119
if ( !extent.isEmpty() )
120
adjustExtentToSize();
126
void QgsMapRenderer::setOutputSize( QSize size, int dpi )
129
mScaleCalculator->setDpi( dpi );
130
adjustExtentToSize();
132
int QgsMapRenderer::outputDpi()
134
return mScaleCalculator->dpi();
136
QSize QgsMapRenderer::outputSize()
141
void QgsMapRenderer::adjustExtentToSize()
143
int myHeight = mSize.height();
144
int myWidth = mSize.width();
146
QgsMapToPixel newCoordXForm;
148
if ( !myWidth || !myHeight )
151
newCoordXForm.setParameters( 0, 0, 0, 0 );
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;
163
// calculate the actual extent of the mapCanvas
164
double dxmin, dxmax, dymin, dymax, whitespace;
166
if ( mapUnitsPerPixelY > mapUnitsPerPixelX )
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;
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;
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() );
189
mExtent.setXMinimum( dxmin );
190
mExtent.setXMaximum( dxmax );
191
mExtent.setYMinimum( dymin );
192
mExtent.setYMaximum( dymax );
197
QgsDebugMsg( QString( "Scale (assuming meters as map units) = 1:%1" ).arg( mScale ) );
199
newCoordXForm.setParameters( mMapUnitsPerPixel, dxmin, dymin, myHeight );
200
mRenderContext.setMapToPixel( newCoordXForm );
201
mRenderContext.setExtent( mExtent );
205
void QgsMapRenderer::render( QPainter* painter )
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;
212
QgsDebugMsg( "========== Rendering ==========" );
214
if ( mExtent.isEmpty() )
216
QgsDebugMsg( "empty extent... not rendering" );
225
QPaintDevice* thePaintDevice = painter->device();
226
if ( !thePaintDevice )
233
QgsCoordinateTransform* ct;
236
QgsDebugMsg( "Starting to render layer stack." );
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 );
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 )
255
scaleFactor = sceneDpi / 25.4;
257
double rasterScaleFactor = ( thePaintDevice->logicalDpiX() + thePaintDevice->logicalDpiY() ) / 2.0 / sceneDpi;
258
if ( mRenderContext.rasterScaleFactor() != rasterScaleFactor )
260
mRenderContext.setRasterScaleFactor( rasterScaleFactor );
261
mySameAsLastFlag = false;
263
if ( mRenderContext.scaleFactor() != scaleFactor )
265
mRenderContext.setScaleFactor( scaleFactor );
266
mySameAsLastFlag = false;
268
if ( mRenderContext.rendererScale() != mScale )
270
//add map scale to render context
271
mRenderContext.setRendererScale( mScale );
272
mySameAsLastFlag = false;
274
if ( mLastExtent != mExtent )
276
mLastExtent = mExtent;
277
mySameAsLastFlag = false;
280
mRenderContext.setLabelingEngine( mLabelingEngine );
281
if ( mLabelingEngine )
282
mLabelingEngine->init();
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 )
288
//clear the cache pixmap if we changed resolution / extent
289
QSettings mySettings;
290
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
292
QgsMapLayerRegistry::instance()->clearAllLayerCaches();
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 )
301
placeOverlays = true;
304
// render all layers in the stack, starting at the base
305
QListIterator<QString> li( mLayerSet );
310
while ( li.hasPrevious() )
312
if ( mRenderContext.renderingStopped() )
317
// Store the painter in case we need to swap it out for the
319
QPainter * mypContextPainter = mRenderContext.painter();
321
QString layerId = li.previous();
323
QgsDebugMsg( "Rendering at layer item " + layerId );
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" );
333
//emit drawingProgress(myRenderCounter++, mLayerSet.size());
334
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
338
QgsDebugMsg( "Layer not found in registry!" );
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() );
348
if ( !ml->hasScaleBasedVisibility() || ( ml->minimumScale() < mScale && mScale < ml->maximumScale() ) || mOverview )
350
connect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
353
// Now do the call to the layer that actually does
354
// the rendering work!
359
if ( hasCrsTransformEnabled() )
362
split = splitLayersExtent( ml, r1, r2 );
363
ct = new QgsCoordinateTransform( ml->srs(), *mDestCRS );
364
mRenderContext.setExtent( r1 );
371
mRenderContext.setCoordinateTransform( ct );
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;
379
if ( ml->type() == QgsMapLayer::RasterLayer && fabs( rasterScaleFactor - 1.0 ) > 0.000001 )
385
//create overlay objects for features within the view extent
386
if ( ml->type() == QgsMapLayer::VectorLayer && overlayManager )
388
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
391
QList<QgsVectorOverlay*> thisLayerOverlayList;
392
vl->vectorOverlays( thisLayerOverlayList );
394
QList<QgsVectorOverlay*>::iterator overlayIt = thisLayerOverlayList.begin();
395
for ( ; overlayIt != thisLayerOverlayList.end(); ++overlayIt )
397
if (( *overlayIt )->displayFlag() )
399
( *overlayIt )->createOverlayObjects( mRenderContext );
400
allOverlayList.push_back( *overlayIt );
404
overlayManager->addLayer( vl, thisLayerOverlayList );
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 )
412
QgsVectorLayer* vl = qobject_cast<QgsVectorLayer *>( ml );
413
if ( vl->isEditable() ||
414
( mRenderContext.labelingEngine() && mRenderContext.labelingEngine()->willUseLayer( vl ) ) )
416
ml->setCacheImage( 0 );
420
QSettings mySettings;
421
if ( ! split )//render caching does not yet cater for split extents
423
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
425
if ( !mySameAsLastFlag || ml->cacheImage() == 0 )
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 );
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() )
435
mypPainter->setRenderHint( QPainter::Antialiasing );
437
mRenderContext.setPainter( mypPainter );
439
else if ( mySameAsLastFlag )
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...
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 );
463
if ( !ml->draw( mRenderContext ) )
465
emit drawError( ml );
469
QgsDebugMsg( "Layer rendered without issues" );
474
mRenderContext.setExtent( r2 );
475
if ( !ml->draw( mRenderContext ) )
477
emit drawError( ml );
483
mRenderContext.setMapToPixel( bk_mapToPixel );
484
mRenderContext.painter()->restore();
487
if ( mySettings.value( "/qgis/enable_render_caching", false ).toBool() )
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() ) );
499
disconnect( ml, SIGNAL( drawingProgress( int, int ) ), this, SLOT( onDrawingProgress( int, int ) ) );
501
else // layer not visible due to scale
503
QgsDebugMsg( "Layer not rendered because it is not within the defined "
504
"visibility scale range" );
507
} // while (li.hasPrevious())
509
QgsDebugMsg( "Done rendering map layers" );
513
// render all labels for vector layers in the stack, starting at the base
515
while ( li.hasPrevious() )
517
if ( mRenderContext.renderingStopped() )
522
QString layerId = li.previous();
524
// TODO: emit drawingProgress((myRenderCounter++),zOrder.size());
525
QgsMapLayer *ml = QgsMapLayerRegistry::instance()->mapLayer( layerId );
527
if ( ml && ( ml->type() != QgsMapLayer::RasterLayer ) )
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() ) )
535
if ( hasCrsTransformEnabled() )
537
QgsRectangle r1 = mExtent;
538
split = splitLayersExtent( ml, r1, r2 );
539
ct = new QgsCoordinateTransform( ml->srs(), *mDestCRS );
540
mRenderContext.setExtent( r1 );
547
mRenderContext.setCoordinateTransform( ct );
549
ml->drawLabels( mRenderContext );
552
mRenderContext.setExtent( r2 );
553
ml->drawLabels( mRenderContext );
560
//find overlay positions and draw the vector overlays
561
if ( overlayManager && allOverlayList.size() > 0 )
563
overlayManager->findObjectPositions( mRenderContext, mScaleCalculator->mapUnits() );
564
//draw all the overlays
565
QList<QgsVectorOverlay*>::iterator allOverlayIt = allOverlayList.begin();
566
for ( ; allOverlayIt != allOverlayList.end(); ++allOverlayIt )
568
( *allOverlayIt )->drawOverlayObjects( mRenderContext );
570
overlayManager->removeLayers();
573
delete overlayManager;
574
// make sure progress bar arrives at 100%!
575
emit drawingProgress( 1, 1 );
577
if ( mLabelingEngine )
579
// set correct extent
580
mRenderContext.setExtent( mExtent );
581
mRenderContext.setCoordinateTransform( NULL );
583
mLabelingEngine->drawLabeling( mRenderContext );
584
mLabelingEngine->exit();
587
QgsDebugMsg( "Rendering completed in (seconds): " + QString( "%1" ).arg( renderTime.elapsed() / 1000.0 ) );
593
void QgsMapRenderer::setMapUnits( QGis::UnitType u )
595
mScaleCalculator->setMapUnits( u );
597
// Since the map units have changed, force a recalculation of the scale.
600
emit mapUnitsChanged();
603
QGis::UnitType QgsMapRenderer::mapUnits() const
605
return mScaleCalculator->mapUnits();
608
void QgsMapRenderer::onDrawingProgress( int current, int total )
610
// TODO: emit signal with progress
611
// QgsDebugMsg(QString("onDrawingProgress: %1 / %2").arg(current).arg(total));
617
void QgsMapRenderer::setProjectionsEnabled( bool enabled )
619
if ( mProjectionsEnabled != enabled )
621
mProjectionsEnabled = enabled;
622
QgsDebugMsg( "Adjusting DistArea projection on/off" );
623
mDistArea->setProjectionsEnabled( enabled );
625
emit hasCrsTransformEnabled( enabled );
629
bool QgsMapRenderer::hasCrsTransformEnabled()
631
return mProjectionsEnabled;
634
void QgsMapRenderer::setDestinationSrs( const QgsCoordinateReferenceSystem& srs )
636
QgsDebugMsg( "* Setting destCRS : = " + srs.toProj4() );
637
QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( srs.srsid() ) );
638
if ( *mDestCRS != srs )
640
QgsDebugMsg( "Setting DistArea CRS to " + QString::number( srs.srsid() ) );
641
mDistArea->setSourceCrs( srs.srsid() );
644
emit destinationSrsChanged();
648
const QgsCoordinateReferenceSystem& QgsMapRenderer::destinationSrs()
650
QgsDebugMsg( "* Returning destCRS" );
651
QgsDebugMsg( "* DestCRS.srsid() = " + QString::number( mDestCRS->srsid() ) );
652
QgsDebugMsg( "* DestCRS.proj4() = " + mDestCRS->toProj4() );
657
bool QgsMapRenderer::splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 )
661
if ( hasCrsTransformEnabled() )
665
QgsCoordinateTransform tr( layer->srs(), *mDestCRS );
668
// QgsLogger::debug<QgsRectangle>("Getting extent of canvas in layers CS. Canvas is ", extent, __FILE__, __FUNCTION__, __LINE__);
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;
677
if ( tr.sourceCrs().geographicFlag() )
679
// Note: ll = lower left point
680
// and ur = upper right point
681
QgsPoint ll = tr.transform( extent.xMinimum(), extent.yMinimum(),
682
QgsCoordinateTransform::ReverseTransform );
684
QgsPoint ur = tr.transform( extent.xMaximum(), extent.yMaximum(),
685
QgsCoordinateTransform::ReverseTransform );
687
if ( ll.x() > ur.x() )
689
extent.set( ll, QgsPoint( splitCoord, ur.y() ) );
690
r2.set( QgsPoint( -splitCoord, ll.y() ), ur );
693
else // no need to split
695
extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
698
else // can't cross 180
700
extent = tr.transformBoundingBox( extent, QgsCoordinateTransform::ReverseTransform );
703
catch ( QgsCsException &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 );
715
QgsRectangle QgsMapRenderer::layerExtentToOutputExtent( QgsMapLayer* theLayer, QgsRectangle extent )
717
if ( hasCrsTransformEnabled() )
721
QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
722
extent = tr.transformBoundingBox( extent );
724
catch ( QgsCsException &cse )
727
QgsDebugMsg( QString( "Transform error caught: " ).arg( cse.what() ) );
732
// leave extent unchanged
738
QgsPoint QgsMapRenderer::layerToMapCoordinates( QgsMapLayer* theLayer, QgsPoint point )
740
if ( hasCrsTransformEnabled() )
744
QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
745
point = tr.transform( point, QgsCoordinateTransform::ForwardTransform );
747
catch ( QgsCsException &cse )
750
QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
755
// leave point without transformation
760
QgsPoint QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsPoint point )
762
if ( hasCrsTransformEnabled() )
766
QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
767
point = tr.transform( point, QgsCoordinateTransform::ReverseTransform );
769
catch ( QgsCsException &cse )
771
QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
772
throw cse; //let client classes know there was a transformation error
777
// leave point without transformation
782
QgsRectangle QgsMapRenderer::mapToLayerCoordinates( QgsMapLayer* theLayer, QgsRectangle rect )
784
if ( hasCrsTransformEnabled() )
788
QgsCoordinateTransform tr( theLayer->srs(), *mDestCRS );
789
rect = tr.transform( rect, QgsCoordinateTransform::ReverseTransform );
791
catch ( QgsCsException &cse )
793
QgsDebugMsg( QString( "Transform error caught: %1" ).arg( cse.what() ) );
794
throw cse; //let client classes know there was a transformation error
801
void QgsMapRenderer::updateFullExtent()
803
QgsDebugMsg( "called." );
804
QgsMapLayerRegistry* registry = QgsMapLayerRegistry::instance();
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();
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() )
815
QgsMapLayer * lyr = registry->mapLayer( *it );
818
QgsDebugMsg( QString( "WARNING: layer '%1' not found in map layer registry!" ).arg( *it ) );
822
QgsDebugMsg( "Updating extent using " + lyr->name() );
823
QgsDebugMsg( "Input extent: " + lyr->extent().toString() );
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() );
829
QgsDebugMsg( "Output extent: " + extent.toString() );
830
mFullExtent.unionRect( extent );
836
if ( mFullExtent.width() == 0.0 || mFullExtent.height() == 0.0 )
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
842
if ( mFullExtent.xMinimum() == 0.0 && mFullExtent.xMaximum() == 0.0 &&
843
mFullExtent.yMinimum() == 0.0 && mFullExtent.yMaximum() == 0.0 )
845
mFullExtent.set( -1.0, -1.0, 1.0, 1.0 );
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 );
860
QgsDebugMsg( "Full extent: " + mFullExtent.toString() );
863
QgsRectangle QgsMapRenderer::fullExtent()
869
void QgsMapRenderer::setLayerSet( const QStringList& layers )
875
QStringList& QgsMapRenderer::layerSet()
880
QgsOverlayObjectPositionManager* QgsMapRenderer::overlayManagerFromSettings()
883
QString overlayAlgorithmQString = settings.value( "qgis/overlayPlacementAlgorithm", "Central point" ).toString();
885
QgsOverlayObjectPositionManager* result = 0;
887
if ( overlayAlgorithmQString != "Central point" )
889
QgsPALObjectPositionManager* palManager = new QgsPALObjectPositionManager();
890
if ( overlayAlgorithmQString == "Chain" )
892
palManager->setPlacementAlgorithm( "Chain" );
894
else if ( overlayAlgorithmQString == "Popmusic tabu chain" )
896
palManager->setPlacementAlgorithm( "Popmusic tabu chain" );
898
else if ( overlayAlgorithmQString == "Popmusic tabu" )
900
palManager->setPlacementAlgorithm( "Popmusic tabu" );
902
else if ( overlayAlgorithmQString == "Popmusic chain" )
904
palManager->setPlacementAlgorithm( "Popmusic chain" );
910
result = new QgsCentralPointPositionManager();
916
bool QgsMapRenderer::readXML( QDomNode & theNode )
918
QDomNode myNode = theNode.namedItem( "units" );
919
QDomElement element = myNode.toElement();
922
QGis::UnitType units;
923
if ( "meters" == element.text() )
925
units = QGis::Meters;
927
else if ( "feet" == element.text() )
931
else if ( "degrees" == element.text() )
933
units = QGis::Degrees;
935
else if ( "unknown" == element.text() )
937
units = QGis::UnknownUnit;
941
QgsDebugMsg( "Unknown map unit type " + element.text() );
942
units = QGis::Degrees;
944
setMapUnits( units );
949
QDomNode extentNode = theNode.namedItem( "extent" );
951
QDomNode xminNode = extentNode.namedItem( "xmin" );
952
QDomNode yminNode = extentNode.namedItem( "ymin" );
953
QDomNode xmaxNode = extentNode.namedItem( "xmax" );
954
QDomNode ymaxNode = extentNode.namedItem( "ymax" );
956
QDomElement exElement = xminNode.toElement();
957
double xmin = exElement.text().toDouble();
958
aoi.setXMinimum( xmin );
960
exElement = yminNode.toElement();
961
double ymin = exElement.text().toDouble();
962
aoi.setYMinimum( ymin );
964
exElement = xmaxNode.toElement();
965
double xmax = exElement.text().toDouble();
966
aoi.setXMaximum( xmax );
968
exElement = ymaxNode.toElement();
969
double ymax = exElement.text().toDouble();
970
aoi.setYMaximum( ymax );
974
// set projections flag
975
QDomNode projNode = theNode.namedItem( "projections" );
976
element = projNode.toElement();
977
setProjectionsEnabled( element.text().toInt() );
979
// set destination CRS
980
QgsCoordinateReferenceSystem srs;
981
QDomNode srsNode = theNode.namedItem( "destinationsrs" );
982
srs.readXML( srsNode );
983
setDestinationSrs( srs );
988
bool QgsMapRenderer::writeXML( QDomNode & theNode, QDomDocument & theDoc )
992
QDomElement unitsNode = theDoc.createElement( "units" );
993
theNode.appendChild( unitsNode );
997
switch ( mapUnits() )
1000
unitsString = "meters";
1003
unitsString = "feet";
1006
unitsString = "degrees";
1008
case QGis::UnknownUnit:
1010
unitsString = "unknown";
1013
QDomText unitsText = theDoc.createTextNode( unitsString );
1014
unitsNode.appendChild( unitsText );
1017
// Write current view extents
1018
QDomElement extentNode = theDoc.createElement( "extent" );
1019
theNode.appendChild( extentNode );
1021
QDomElement xMin = theDoc.createElement( "xmin" );
1022
QDomElement yMin = theDoc.createElement( "ymin" );
1023
QDomElement xMax = theDoc.createElement( "xmax" );
1024
QDomElement yMax = theDoc.createElement( "ymax" );
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' ) );
1032
xMin.appendChild( xMinText );
1033
yMin.appendChild( yMinText );
1034
xMax.appendChild( xMaxText );
1035
yMax.appendChild( yMaxText );
1037
extentNode.appendChild( xMin );
1038
extentNode.appendChild( yMin );
1039
extentNode.appendChild( xMax );
1040
extentNode.appendChild( yMax );
1042
// projections enabled
1043
QDomElement projNode = theDoc.createElement( "projections" );
1044
theNode.appendChild( projNode );
1046
QDomText projText = theDoc.createTextNode( QString::number( hasCrsTransformEnabled() ) );
1047
projNode.appendChild( projText );
1050
QDomElement srsNode = theDoc.createElement( "destinationsrs" );
1051
theNode.appendChild( srsNode );
1052
destinationSrs().writeXML( srsNode, theDoc );
1057
void QgsMapRenderer::setLabelingEngine( QgsLabelingEngineInterface* iface )
1059
if ( mLabelingEngine )
1060
delete mLabelingEngine;
1062
mLabelingEngine = iface;