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

« back to all changes in this revision

Viewing changes to src/core/composer/qgscomposermap.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
                         qgscomposermap.cpp
 
3
                             -------------------
 
4
    begin                : January 2005
 
5
    copyright            : (C) 2005 by Radim Blazek
 
6
    email                : blazek@itc.it
 
7
 ***************************************************************************/
 
8
 
 
9
/***************************************************************************
 
10
 *                                                                         *
 
11
 *   This program is free software; you can redistribute it and/or modify  *
 
12
 *   it under the terms of the GNU General Public License as published by  *
 
13
 *   the Free Software Foundation; either version 2 of the License, or     *
 
14
 *   (at your option) any later version.                                   *
 
15
 *                                                                         *
 
16
 ***************************************************************************/
 
17
 
 
18
#include "qgscomposermap.h"
 
19
 
 
20
#include "qgscoordinatetransform.h"
 
21
#include "qgslogger.h"
 
22
#include "qgsmaprenderer.h"
 
23
#include "qgsmaplayer.h"
 
24
#include "qgsmaplayerregistry.h"
 
25
#include "qgsmaptopixel.h"
 
26
#include "qgsproject.h"
 
27
#include "qgsmaprenderer.h"
 
28
#include "qgsrasterlayer.h"
 
29
#include "qgsrendercontext.h"
 
30
#include "qgsscalecalculator.h"
 
31
#include "qgsvectorlayer.h"
 
32
 
 
33
#include "qgslabel.h"
 
34
#include "qgslabelattributes.h"
 
35
 
 
36
#include <QGraphicsScene>
 
37
#include <QPainter>
 
38
#include <QSettings>
 
39
#include <iostream>
 
40
#include <cmath>
 
41
 
 
42
QgsComposerMap::QgsComposerMap( QgsComposition *composition, int x, int y, int width, int height )
 
43
    : QgsComposerItem( x, y, width, height, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
 
44
    mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
 
45
    mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 )
 
46
{
 
47
  mComposition = composition;
 
48
  mId = mComposition->composerMapItems().size();
 
49
  mMapRenderer = mComposition->mapRenderer();
 
50
  mPreviewMode = QgsComposerMap::Rectangle;
 
51
  mCurrentRectangle = rect();
 
52
 
 
53
  // Cache
 
54
  mCacheUpdated = false;
 
55
  mDrawing = false;
 
56
 
 
57
  //Offset
 
58
  mXOffset = 0.0;
 
59
  mYOffset = 0.0;
 
60
 
 
61
  connectUpdateSlot();
 
62
 
 
63
  //calculate mExtent based on width/height ratio and map canvas extent
 
64
  if ( mMapRenderer )
 
65
  {
 
66
    mExtent = mMapRenderer->extent();
 
67
  }
 
68
  setSceneRect( QRectF( x, y, width, height ) );
 
69
  setToolTip( tr( "Map %1" ).arg( mId ) );
 
70
  mGridPen.setCapStyle( Qt::FlatCap );
 
71
}
 
72
 
 
73
QgsComposerMap::QgsComposerMap( QgsComposition *composition )
 
74
    : QgsComposerItem( 0, 0, 10, 10, composition ), mKeepLayerSet( false ), mGridEnabled( false ), mGridStyle( Solid ), \
 
75
    mGridIntervalX( 0.0 ), mGridIntervalY( 0.0 ), mGridOffsetX( 0.0 ), mGridOffsetY( 0.0 ), mGridAnnotationPrecision( 3 ), mShowGridAnnotation( false ), \
 
76
    mGridAnnotationPosition( OutsideMapFrame ), mAnnotationFrameDistance( 1.0 ), mGridAnnotationDirection( Horizontal ), mCrossLength( 3 )
 
77
{
 
78
  //Offset
 
79
  mXOffset = 0.0;
 
80
  mYOffset = 0.0;
 
81
 
 
82
  connectUpdateSlot();
 
83
 
 
84
  mComposition = composition;
 
85
  mMapRenderer = mComposition->mapRenderer();
 
86
  mId = mComposition->composerMapItems().size();
 
87
  mPreviewMode = QgsComposerMap::Rectangle;
 
88
  mCurrentRectangle = rect();
 
89
 
 
90
  setToolTip( tr( "Map %1" ).arg( mId ) );
 
91
  mGridPen.setCapStyle( Qt::FlatCap );
 
92
}
 
93
 
 
94
QgsComposerMap::~QgsComposerMap()
 
95
{
 
96
}
 
97
 
 
98
/* This function is called by paint() and cache() to render the map.  It does not override any functions
 
99
from QGraphicsItem. */
 
100
void QgsComposerMap::draw( QPainter *painter, const QgsRectangle& extent, const QSize& size, int dpi )
 
101
{
 
102
  if ( !painter )
 
103
  {
 
104
    return;
 
105
  }
 
106
 
 
107
  if ( !mMapRenderer )
 
108
  {
 
109
    return;
 
110
  }
 
111
 
 
112
  QgsMapRenderer theMapRenderer;
 
113
  theMapRenderer.setExtent( extent );
 
114
  theMapRenderer.setOutputSize( size, dpi );
 
115
 
 
116
  //use stored layer set or read current set from main canvas
 
117
  if ( mKeepLayerSet )
 
118
  {
 
119
    theMapRenderer.setLayerSet( mLayerSet );
 
120
  }
 
121
  else
 
122
  {
 
123
    theMapRenderer.setLayerSet( mMapRenderer->layerSet() );
 
124
  }
 
125
  theMapRenderer.setProjectionsEnabled( mMapRenderer->hasCrsTransformEnabled() );
 
126
  theMapRenderer.setDestinationSrs( mMapRenderer->destinationSrs() );
 
127
 
 
128
  //set antialiasing if enabled in options
 
129
  QSettings settings;
 
130
  if ( settings.value( "/qgis/enable_anti_aliasing", false ).toBool() )
 
131
  {
 
132
    painter->setRenderHint( QPainter::Antialiasing );
 
133
  }
 
134
 
 
135
  QgsRenderContext* theRendererContext = theMapRenderer.rendererContext();
 
136
  if ( theRendererContext )
 
137
  {
 
138
    theRendererContext->setDrawEditingInformation( false );
 
139
    theRendererContext->setRenderingStopped( false );
 
140
  }
 
141
 
 
142
  //force composer map scale for scale dependent visibility
 
143
  double bk_scale = theMapRenderer.scale();
 
144
  theMapRenderer.setScale( scale() );
 
145
 
 
146
  //layer caching (as QImages) cannot be done for composer prints
 
147
  QSettings s;
 
148
  bool bkLayerCaching = s.value( "/qgis/enable_render_caching", false ).toBool();
 
149
  s.setValue( "/qgis/enable_render_caching", false );
 
150
 
 
151
  theMapRenderer.render( painter );
 
152
  s.setValue( "/qgis/enable_render_caching", bkLayerCaching );
 
153
 
 
154
  theMapRenderer.setScale( bk_scale );
 
155
}
 
156
 
 
157
void QgsComposerMap::cache( void )
 
158
{
 
159
  if ( mPreviewMode == Rectangle )
 
160
  {
 
161
    return;
 
162
  }
 
163
 
 
164
  if ( mDrawing )
 
165
  {
 
166
    return;
 
167
  }
 
168
 
 
169
  mDrawing = true;
 
170
 
 
171
  //in case of rotation, we need to request a larger rectangle and create a larger cache image
 
172
  QgsRectangle requestExtent;
 
173
  requestedExtent( requestExtent );
 
174
 
 
175
  double horizontalVScaleFactor = horizontalViewScaleFactor();
 
176
  if ( horizontalVScaleFactor < 0 )
 
177
  {
 
178
    horizontalVScaleFactor = mLastValidViewScaleFactor;
 
179
  }
 
180
 
 
181
  int w = requestExtent.width() * mapUnitsToMM() * horizontalVScaleFactor;
 
182
  int h = requestExtent.height() * mapUnitsToMM() * horizontalVScaleFactor;
 
183
 
 
184
  if ( w > 5000 ) //limit size of image for better performance
 
185
  {
 
186
    w = 5000;
 
187
  }
 
188
 
 
189
  if ( h > 5000 )
 
190
  {
 
191
    h = 5000;
 
192
  }
 
193
 
 
194
  mCacheImage = QImage( w, h,  QImage::Format_ARGB32 );
 
195
  mCacheImage.fill( brush().color().rgb() ); //consider the item background brush
 
196
  double mapUnitsPerPixel = mExtent.width() / w;
 
197
 
 
198
  // WARNING: ymax in QgsMapToPixel is device height!!!
 
199
  QgsMapToPixel transform( mapUnitsPerPixel, h, requestExtent.yMinimum(), requestExtent.xMinimum() );
 
200
 
 
201
  QPainter p( &mCacheImage );
 
202
 
 
203
  draw( &p, requestExtent, QSize( w, h ), mCacheImage.logicalDpiX() );
 
204
  p.end();
 
205
  mCacheUpdated = true;
 
206
 
 
207
  mDrawing = false;
 
208
}
 
209
 
 
210
void QgsComposerMap::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
 
211
{
 
212
  if ( !mComposition || !painter )
 
213
  {
 
214
    return;
 
215
  }
 
216
 
 
217
  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
 
218
  painter->save();
 
219
  painter->setClipRect( thisPaintRect );
 
220
 
 
221
  drawBackground( painter );
 
222
 
 
223
  if ( mComposition->plotStyle() == QgsComposition::Preview && mPreviewMode == Rectangle )
 
224
  {
 
225
    QFont messageFont( "", 12 );
 
226
    painter->setFont( messageFont );
 
227
    painter->setPen( QColor( 0, 0, 0 ) );
 
228
    painter->drawText( thisPaintRect, tr( "Map will be printed here" ) );
 
229
  }
 
230
  else if ( mComposition->plotStyle() == QgsComposition::Preview )
 
231
  {
 
232
    //draw cached pixmap. This function does not call cache() any more because
 
233
    //Qt 4.4.0 and 4.4.1 have problems with recursive paintings
 
234
    //QgsComposerMap::cache() and QgsComposerMap::update() need to be called by
 
235
    //client functions
 
236
 
 
237
    QgsRectangle requestRectangle;
 
238
    requestedExtent( requestRectangle );
 
239
    double horizontalVScaleFactor = horizontalViewScaleFactor();
 
240
    if ( horizontalVScaleFactor < 0 )
 
241
    {
 
242
      horizontalVScaleFactor = mLastValidViewScaleFactor;
 
243
    }
 
244
 
 
245
    double imagePixelWidth = mExtent.width() / requestRectangle.width() * mCacheImage.width() ; //how many pixels of the image are for the map extent?
 
246
    double scale = rect().width() / imagePixelWidth;
 
247
    QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
 
248
 
 
249
    //shift such that rotation point is at 0/0 point in the coordinate system
 
250
    double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
 
251
    double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
 
252
 
 
253
    //shift such that top left point of the extent at point 0/0 in item coordinate system
 
254
    double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
 
255
    double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
 
256
 
 
257
    painter->save();
 
258
    //painter->scale( scale, scale );
 
259
    painter->translate( mXOffset, mYOffset );
 
260
    painter->translate( xTopLeftShift, yTopLeftShift );
 
261
    painter->rotate( mRotation );
 
262
    painter->translate( xShiftMM, -yShiftMM );
 
263
    painter->scale( scale, scale );
 
264
    painter->drawImage( 0, 0, mCacheImage );
 
265
    painter->restore();
 
266
  }
 
267
  else if ( mComposition->plotStyle() == QgsComposition::Print ||
 
268
            mComposition->plotStyle() == QgsComposition::Postscript )
 
269
  {
 
270
    if ( mDrawing )
 
271
    {
 
272
      return;
 
273
    }
 
274
 
 
275
    mDrawing = true;
 
276
    QPaintDevice* thePaintDevice = painter->device();
 
277
    if ( !thePaintDevice )
 
278
    {
 
279
      return;
 
280
    }
 
281
 
 
282
    QgsRectangle requestRectangle;
 
283
    requestedExtent( requestRectangle );
 
284
 
 
285
    QSize theSize( requestRectangle.width() * mapUnitsToMM(), requestRectangle.height() * mapUnitsToMM() );
 
286
    QgsPoint rotationPoint = QgsPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
 
287
 
 
288
    //shift such that rotation point is at 0/0 point in the coordinate system
 
289
    double yShiftMM = ( requestRectangle.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
 
290
    double xShiftMM = ( requestRectangle.xMinimum() - rotationPoint.x() ) * mapUnitsToMM();
 
291
 
 
292
    //shift such that top left point of the extent at point 0/0 in item coordinate system
 
293
    double xTopLeftShift = ( rotationPoint.x() - mExtent.xMinimum() ) * mapUnitsToMM();
 
294
    double yTopLeftShift = ( mExtent.yMaximum() - rotationPoint.y() ) * mapUnitsToMM();
 
295
    painter->save();
 
296
    painter->translate( mXOffset, mYOffset );
 
297
    painter->translate( xTopLeftShift, yTopLeftShift );
 
298
    painter->rotate( mRotation );
 
299
    painter->translate( xShiftMM, -yShiftMM );
 
300
    draw( painter, requestRectangle, theSize, 25.4 ); //scene coordinates seem to be in mm
 
301
    painter->restore();
 
302
 
 
303
    mDrawing = false;
 
304
  }
 
305
 
 
306
  painter->setClipRect( thisPaintRect , Qt::NoClip );
 
307
 
 
308
  if ( mGridEnabled )
 
309
  {
 
310
    drawGrid( painter );
 
311
  }
 
312
  drawFrame( painter );
 
313
  if ( isSelected() )
 
314
  {
 
315
    drawSelectionBoxes( painter );
 
316
  }
 
317
 
 
318
 
 
319
  painter->restore();
 
320
}
 
321
 
 
322
void QgsComposerMap::updateCachedImage( void )
 
323
{
 
324
  syncLayerSet(); //layer list may have changed
 
325
  mCacheUpdated = false;
 
326
  cache();
 
327
  QGraphicsRectItem::update();
 
328
}
 
329
 
 
330
void QgsComposerMap::renderModeUpdateCachedImage()
 
331
{
 
332
  if ( mPreviewMode == Render )
 
333
  {
 
334
    updateCachedImage();
 
335
  }
 
336
}
 
337
 
 
338
void QgsComposerMap::setCacheUpdated( bool u )
 
339
{
 
340
  mCacheUpdated = u;
 
341
}
 
342
 
 
343
double QgsComposerMap::scale() const
 
344
{
 
345
  QgsScaleCalculator calculator;
 
346
  calculator.setMapUnits( mMapRenderer->mapUnits() );
 
347
  calculator.setDpi( 25.4 );  //QGraphicsView units are mm
 
348
  return calculator.calculate( mExtent, rect().width() );
 
349
}
 
350
 
 
351
void QgsComposerMap::resize( double dx, double dy )
 
352
{
 
353
  //setRect
 
354
  QRectF currentRect = rect();
 
355
  QRectF newSceneRect = QRectF( transform().dx(), transform().dy(), currentRect.width() + dx, currentRect.height() + dy );
 
356
  setSceneRect( newSceneRect );
 
357
}
 
358
 
 
359
void QgsComposerMap::moveContent( double dx, double dy )
 
360
{
 
361
  if ( !mDrawing )
 
362
  {
 
363
    transformShift( dx, dy );
 
364
    mExtent.setXMinimum( mExtent.xMinimum() + dx );
 
365
    mExtent.setXMaximum( mExtent.xMaximum() + dx );
 
366
    mExtent.setYMinimum( mExtent.yMinimum() + dy );
 
367
    mExtent.setYMaximum( mExtent.yMaximum() + dy );
 
368
    emit extentChanged();
 
369
    cache();
 
370
    update();
 
371
  }
 
372
}
 
373
 
 
374
void QgsComposerMap::zoomContent( int delta, double x, double y )
 
375
{
 
376
  if ( mDrawing )
 
377
  {
 
378
    return;
 
379
  }
 
380
 
 
381
  QSettings settings;
 
382
 
 
383
  //read zoom mode
 
384
  //0: zoom, 1: zoom and recenter, 2: zoom to cursor, 3: nothing
 
385
  int zoomMode = settings.value( "/qgis/wheel_action", 0 ).toInt();
 
386
  if ( zoomMode == 3 ) //do nothing
 
387
  {
 
388
    return;
 
389
  }
 
390
 
 
391
  double zoomFactor = settings.value( "/qgis/zoom_factor", 2.0 ).toDouble();
 
392
 
 
393
  //find out new center point
 
394
  double centerX = ( mExtent.xMaximum() + mExtent.xMinimum() ) / 2;
 
395
  double centerY = ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2;
 
396
 
 
397
  if ( zoomMode != 0 )
 
398
  {
 
399
    //find out map coordinates of mouse position
 
400
    double mapMouseX = mExtent.xMinimum() + ( x / rect().width() ) * ( mExtent.xMaximum() - mExtent.xMinimum() );
 
401
    double mapMouseY = mExtent.yMinimum() + ( 1 - ( y / rect().height() ) ) * ( mExtent.yMaximum() - mExtent.yMinimum() );
 
402
    if ( zoomMode == 1 ) //zoom and recenter
 
403
    {
 
404
      centerX = mapMouseX;
 
405
      centerY = mapMouseY;
 
406
    }
 
407
    else if ( zoomMode == 2 ) //zoom to cursor
 
408
    {
 
409
      centerX = mapMouseX + ( centerX - mapMouseX ) * ( 1.0 / zoomFactor );
 
410
      centerY = mapMouseY + ( centerY - mapMouseY ) * ( 1.0 / zoomFactor );
 
411
    }
 
412
  }
 
413
 
 
414
  double newIntervalX, newIntervalY;
 
415
 
 
416
  if ( delta > 0 )
 
417
  {
 
418
    newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) / zoomFactor;
 
419
    newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) / zoomFactor;
 
420
  }
 
421
  else if ( delta < 0 )
 
422
  {
 
423
    newIntervalX = ( mExtent.xMaximum() - mExtent.xMinimum() ) * zoomFactor;
 
424
    newIntervalY = ( mExtent.yMaximum() - mExtent.yMinimum() ) * zoomFactor;
 
425
  }
 
426
  else //no need to zoom
 
427
  {
 
428
    return;
 
429
  }
 
430
 
 
431
  mExtent.setXMaximum( centerX + newIntervalX / 2 );
 
432
  mExtent.setXMinimum( centerX - newIntervalX / 2 );
 
433
  mExtent.setYMaximum( centerY + newIntervalY / 2 );
 
434
  mExtent.setYMinimum( centerY - newIntervalY / 2 );
 
435
 
 
436
  emit extentChanged();
 
437
  cache();
 
438
  update();
 
439
}
 
440
 
 
441
void QgsComposerMap::setSceneRect( const QRectF& rectangle )
 
442
{
 
443
  double w = rectangle.width();
 
444
  double h = rectangle.height();
 
445
  //prepareGeometryChange();
 
446
 
 
447
  QgsComposerItem::setSceneRect( rectangle );
 
448
 
 
449
  //QGraphicsRectItem::update();
 
450
  double newHeight = mExtent.width() * h / w ;
 
451
  mExtent = QgsRectangle( mExtent.xMinimum(), mExtent.yMinimum(), mExtent.xMaximum(), mExtent.yMinimum() + newHeight );
 
452
  mCacheUpdated = false;
 
453
  emit extentChanged();
 
454
  if ( mPreviewMode != Rectangle )
 
455
  {
 
456
    cache();
 
457
  }
 
458
  updateBoundingRect();
 
459
  update();
 
460
}
 
461
 
 
462
void QgsComposerMap::setNewExtent( const QgsRectangle& extent )
 
463
{
 
464
  if ( mExtent == extent )
 
465
  {
 
466
    return;
 
467
  }
 
468
  mExtent = extent;
 
469
 
 
470
  //adjust height
 
471
  QRectF currentRect = rect();
 
472
 
 
473
  double newHeight = currentRect.width() * extent.height() / extent.width();
 
474
 
 
475
  setSceneRect( QRectF( transform().dx(), transform().dy(), currentRect.width(), newHeight ) );
 
476
}
 
477
 
 
478
void QgsComposerMap::setNewScale( double scaleDenominator )
 
479
{
 
480
  double currentScaleDenominator = scale();
 
481
 
 
482
  if ( scaleDenominator == currentScaleDenominator )
 
483
  {
 
484
    return;
 
485
  }
 
486
 
 
487
  double scaleRatio = scaleDenominator / currentScaleDenominator;
 
488
  mExtent.scale( scaleRatio );
 
489
  mCacheUpdated = false;
 
490
  emit extentChanged();
 
491
  cache();
 
492
  update();
 
493
}
 
494
 
 
495
void QgsComposerMap::setOffset( double xOffset, double yOffset )
 
496
{
 
497
  mXOffset = xOffset;
 
498
  mYOffset = yOffset;
 
499
}
 
500
 
 
501
void QgsComposerMap::setMapRotation( double r )
 
502
{
 
503
  setRotation( r );
 
504
  emit rotationChanged( r );
 
505
}
 
506
 
 
507
bool QgsComposerMap::containsWMSLayer() const
 
508
{
 
509
  if ( !mMapRenderer )
 
510
  {
 
511
    return false;
 
512
  }
 
513
 
 
514
  QStringList layers = mMapRenderer->layerSet();
 
515
 
 
516
  QStringList::const_iterator layer_it = layers.constBegin();
 
517
  QgsMapLayer* currentLayer = 0;
 
518
 
 
519
  for ( ; layer_it != layers.constEnd(); ++layer_it )
 
520
  {
 
521
    currentLayer = QgsMapLayerRegistry::instance()->mapLayer( *layer_it );
 
522
    if ( currentLayer )
 
523
    {
 
524
      QgsRasterLayer* currentRasterLayer = qobject_cast<QgsRasterLayer *>( currentLayer );
 
525
      if ( currentRasterLayer )
 
526
      {
 
527
        const QgsRasterDataProvider* rasterProvider = 0;
 
528
        if (( rasterProvider = currentRasterLayer->dataProvider() ) )
 
529
        {
 
530
          if ( rasterProvider->name() == "wms" )
 
531
          {
 
532
            return true;
 
533
          }
 
534
        }
 
535
      }
 
536
    }
 
537
  }
 
538
  return false;
 
539
}
 
540
 
 
541
void QgsComposerMap::connectUpdateSlot()
 
542
{
 
543
  //connect signal from layer registry to update in case of new or deleted layers
 
544
  QgsMapLayerRegistry* layerRegistry = QgsMapLayerRegistry::instance();
 
545
  if ( layerRegistry )
 
546
  {
 
547
    connect( layerRegistry, SIGNAL( layerWillBeRemoved( QString ) ), this, SLOT( updateCachedImage() ) );
 
548
    connect( layerRegistry, SIGNAL( layerWasAdded( QgsMapLayer* ) ), this, SLOT( updateCachedImage() ) );
 
549
  }
 
550
}
 
551
 
 
552
bool QgsComposerMap::writeXML( QDomElement& elem, QDomDocument & doc ) const
 
553
{
 
554
  if ( elem.isNull() )
 
555
  {
 
556
    return false;
 
557
  }
 
558
 
 
559
  QDomElement composerMapElem = doc.createElement( "ComposerMap" );
 
560
  composerMapElem.setAttribute( "id", mId );
 
561
 
 
562
  //previewMode
 
563
  if ( mPreviewMode == Cache )
 
564
  {
 
565
    composerMapElem.setAttribute( "previewMode", "Cache" );
 
566
  }
 
567
  else if ( mPreviewMode == Render )
 
568
  {
 
569
    composerMapElem.setAttribute( "previewMode", "Render" );
 
570
  }
 
571
  else //rectangle
 
572
  {
 
573
    composerMapElem.setAttribute( "previewMode", "Rectangle" );
 
574
  }
 
575
 
 
576
  if ( mKeepLayerSet )
 
577
  {
 
578
    composerMapElem.setAttribute( "keepLayerSet", "true" );
 
579
  }
 
580
  else
 
581
  {
 
582
    composerMapElem.setAttribute( "keepLayerSet", "false" );
 
583
  }
 
584
 
 
585
  //extent
 
586
  QDomElement extentElem = doc.createElement( "Extent" );
 
587
  extentElem.setAttribute( "xmin", QString::number( mExtent.xMinimum() ) );
 
588
  extentElem.setAttribute( "xmax", QString::number( mExtent.xMaximum() ) );
 
589
  extentElem.setAttribute( "ymin", QString::number( mExtent.yMinimum() ) );
 
590
  extentElem.setAttribute( "ymax", QString::number( mExtent.yMaximum() ) );
 
591
  composerMapElem.appendChild( extentElem );
 
592
 
 
593
  //layer set
 
594
  QDomElement layerSetElem = doc.createElement( "LayerSet" );
 
595
  QStringList::const_iterator layerIt = mLayerSet.constBegin();
 
596
  for ( ; layerIt != mLayerSet.constEnd(); ++layerIt )
 
597
  {
 
598
    QDomElement layerElem = doc.createElement( "Layer" );
 
599
    QDomText layerIdText = doc.createTextNode( *layerIt );
 
600
    layerElem.appendChild( layerIdText );
 
601
    layerSetElem.appendChild( layerElem );
 
602
  }
 
603
  composerMapElem.appendChild( layerSetElem );
 
604
 
 
605
  //grid
 
606
  QDomElement gridElem = doc.createElement( "Grid" );
 
607
  gridElem.setAttribute( "show", mGridEnabled );
 
608
  gridElem.setAttribute( "gridStyle", mGridStyle );
 
609
  gridElem.setAttribute( "intervalX", mGridIntervalX );
 
610
  gridElem.setAttribute( "intervalY", mGridIntervalY );
 
611
  gridElem.setAttribute( "offsetX", mGridOffsetX );
 
612
  gridElem.setAttribute( "offsetY", mGridOffsetY );
 
613
  gridElem.setAttribute( "penWidth", mGridPen.widthF() );
 
614
  gridElem.setAttribute( "penColorRed", mGridPen.color().red() );
 
615
  gridElem.setAttribute( "penColorGreen", mGridPen.color().green() );
 
616
  gridElem.setAttribute( "penColorBlue", mGridPen.color().blue() );
 
617
  gridElem.setAttribute( "crossLength", mCrossLength );
 
618
 
 
619
  //grid annotation
 
620
  QDomElement annotationElem = doc.createElement( "Annotation" );
 
621
  annotationElem.setAttribute( "show", mShowGridAnnotation );
 
622
  annotationElem.setAttribute( "position", mGridAnnotationPosition );
 
623
  annotationElem.setAttribute( "frameDistance", mAnnotationFrameDistance );
 
624
  annotationElem.setAttribute( "direction", mGridAnnotationDirection );
 
625
  annotationElem.setAttribute( "font", mGridAnnotationFont.toString() );
 
626
  annotationElem.setAttribute( "precision", mGridAnnotationPrecision );
 
627
 
 
628
  gridElem.appendChild( annotationElem );
 
629
  composerMapElem.appendChild( gridElem );
 
630
 
 
631
  elem.appendChild( composerMapElem );
 
632
  return _writeXML( composerMapElem, doc );
 
633
}
 
634
 
 
635
bool QgsComposerMap::readXML( const QDomElement& itemElem, const QDomDocument& doc )
 
636
{
 
637
  if ( itemElem.isNull() )
 
638
  {
 
639
    return false;
 
640
  }
 
641
 
 
642
  QString idRead = itemElem.attribute( "id", "not found" );
 
643
  if ( idRead != "not found" )
 
644
  {
 
645
    mId = idRead.toInt();
 
646
  }
 
647
  mPreviewMode = Rectangle;
 
648
 
 
649
  //previewMode
 
650
  QString previewMode = itemElem.attribute( "previewMode" );
 
651
  if ( previewMode == "Cache" )
 
652
  {
 
653
    mPreviewMode = Cache;
 
654
  }
 
655
  else if ( previewMode == "Render" )
 
656
  {
 
657
    mPreviewMode = Render;
 
658
  }
 
659
  else
 
660
  {
 
661
    mPreviewMode = Rectangle;
 
662
  }
 
663
 
 
664
  //extent
 
665
  QDomNodeList extentNodeList = itemElem.elementsByTagName( "Extent" );
 
666
  if ( extentNodeList.size() > 0 )
 
667
  {
 
668
    QDomElement extentElem = extentNodeList.at( 0 ).toElement();
 
669
    double xmin, xmax, ymin, ymax;
 
670
    xmin = extentElem.attribute( "xmin" ).toDouble();
 
671
    xmax = extentElem.attribute( "xmax" ).toDouble();
 
672
    ymin = extentElem.attribute( "ymin" ).toDouble();
 
673
    ymax = extentElem.attribute( "ymax" ).toDouble();
 
674
 
 
675
    mExtent = QgsRectangle( xmin, ymin, xmax, ymax );
 
676
  }
 
677
 
 
678
  //mKeepLayerSet flag
 
679
  QString keepLayerSetFlag = itemElem.attribute( "keepLayerSet" );
 
680
  if ( keepLayerSetFlag.compare( "true", Qt::CaseInsensitive ) == 0 )
 
681
  {
 
682
    mKeepLayerSet = true;
 
683
  }
 
684
  else
 
685
  {
 
686
    mKeepLayerSet = false;
 
687
  }
 
688
 
 
689
  //mLayerSet
 
690
  QDomNodeList layerSetNodeList = itemElem.elementsByTagName( "LayerSet" );
 
691
  QStringList layerSet;
 
692
  if ( layerSetNodeList.size() > 0 )
 
693
  {
 
694
    QDomElement layerSetElem = layerSetNodeList.at( 0 ).toElement();
 
695
    QDomNodeList layerIdNodeList = layerSetElem.elementsByTagName( "Layer" );
 
696
    for ( int i = 0; i < layerIdNodeList.size(); ++i )
 
697
    {
 
698
      layerSet << layerIdNodeList.at( i ).toElement().text();
 
699
    }
 
700
  }
 
701
  mLayerSet = layerSet;
 
702
 
 
703
  mDrawing = false;
 
704
  mNumCachedLayers = 0;
 
705
  mCacheUpdated = false;
 
706
 
 
707
  //grid
 
708
  QDomNodeList gridNodeList = itemElem.elementsByTagName( "Grid" );
 
709
  if ( gridNodeList.size() > 0 )
 
710
  {
 
711
    QDomElement gridElem = gridNodeList.at( 0 ).toElement();
 
712
    mGridEnabled = ( gridElem.attribute( "show", "0" ) != "0" );
 
713
    mGridStyle = QgsComposerMap::GridStyle( gridElem.attribute( "gridStyle", "0" ).toInt() );
 
714
    mGridIntervalX = gridElem.attribute( "intervalX", "0" ).toDouble();
 
715
    mGridIntervalY = gridElem.attribute( "intervalY", "0" ).toDouble();
 
716
    mGridOffsetX = gridElem.attribute( "offsetX", "0" ).toDouble();
 
717
    mGridOffsetY = gridElem.attribute( "offsetY", "0" ).toDouble();
 
718
    mGridPen.setWidthF( gridElem.attribute( "penWidth", "0" ).toDouble() );
 
719
    mGridPen.setColor( QColor( gridElem.attribute( "penColorRed", "0" ).toInt(), \
 
720
                               gridElem.attribute( "penColorGreen", "0" ).toInt(), \
 
721
                               gridElem.attribute( "penColorBlue", "0" ).toInt() ) );
 
722
    mCrossLength = gridElem.attribute( "crossLength", "3" ).toDouble();
 
723
 
 
724
    QDomNodeList annotationNodeList = gridElem.elementsByTagName( "Annotation" );
 
725
    if ( annotationNodeList.size() > 0 )
 
726
    {
 
727
      QDomElement annotationElem = annotationNodeList.at( 0 ).toElement();
 
728
      mShowGridAnnotation = ( annotationElem.attribute( "show", "0" ) != "0" );
 
729
      mGridAnnotationPosition = QgsComposerMap::GridAnnotationPosition( annotationElem.attribute( "position", "0" ).toInt() );
 
730
      mAnnotationFrameDistance = annotationElem.attribute( "frameDistance", "0" ).toDouble();
 
731
      mGridAnnotationDirection = QgsComposerMap::GridAnnotationDirection( annotationElem.attribute( "direction", "0" ).toInt() );
 
732
      mGridAnnotationFont.fromString( annotationElem.attribute( "font", "" ) );
 
733
      mGridAnnotationPrecision = annotationElem.attribute( "precision", "3" ).toInt();
 
734
    }
 
735
  }
 
736
 
 
737
  //restore general composer item properties
 
738
  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
 
739
  if ( composerItemList.size() > 0 )
 
740
  {
 
741
    QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
 
742
    _readXML( composerItemElem, doc );
 
743
  }
 
744
 
 
745
  updateBoundingRect();
 
746
  return true;
 
747
}
 
748
 
 
749
void QgsComposerMap::storeCurrentLayerSet()
 
750
{
 
751
  if ( mMapRenderer )
 
752
  {
 
753
    mLayerSet = mMapRenderer->layerSet();
 
754
  }
 
755
}
 
756
 
 
757
void QgsComposerMap::syncLayerSet()
 
758
{
 
759
  if ( mLayerSet.size() < 1 && !mMapRenderer )
 
760
  {
 
761
    return;
 
762
  }
 
763
 
 
764
  QStringList currentLayerSet = mMapRenderer->layerSet();
 
765
  for ( int i = mLayerSet.size() - 1; i >= 0; --i )
 
766
  {
 
767
    if ( !currentLayerSet.contains( mLayerSet.at( i ) ) )
 
768
    {
 
769
      mLayerSet.removeAt( i );
 
770
    }
 
771
  }
 
772
}
 
773
 
 
774
void QgsComposerMap::drawGrid( QPainter* p )
 
775
{
 
776
  p->setPen( mGridPen );
 
777
 
 
778
  QList< QPair< double, QLineF > > verticalLines;
 
779
  yGridLines( verticalLines );
 
780
  QList< QPair< double, QLineF > >::const_iterator vIt = verticalLines.constBegin();
 
781
  QList< QPair< double, QLineF > > horizontalLines;
 
782
  xGridLines( horizontalLines );
 
783
  QList< QPair< double, QLineF > >::const_iterator hIt = horizontalLines.constBegin();
 
784
 
 
785
  QRectF thisPaintRect = QRectF( 0, 0, QGraphicsRectItem::rect().width(), QGraphicsRectItem::rect().height() );
 
786
  p->setClipRect( thisPaintRect );
 
787
 
 
788
  //simpler approach: draw vertical lines first, then horizontal ones
 
789
  if ( mGridStyle == QgsComposerMap::Solid )
 
790
  {
 
791
    for ( ; vIt != verticalLines.constEnd(); ++vIt )
 
792
    {
 
793
      p->drawLine( vIt->second );
 
794
    }
 
795
 
 
796
    for ( ; hIt != horizontalLines.constEnd(); ++hIt )
 
797
    {
 
798
      p->drawLine( hIt->second );
 
799
    }
 
800
  }
 
801
  else //cross
 
802
  {
 
803
    QPointF intersectionPoint, crossEnd1, crossEnd2;
 
804
    for ( ; vIt != verticalLines.constEnd(); ++vIt )
 
805
    {
 
806
      //start mark
 
807
      crossEnd1 = pointOnLineWithDistance( vIt->second.p1(), vIt->second.p2(), mCrossLength );
 
808
      p->drawLine( vIt->second.p1(), crossEnd1 );
 
809
 
 
810
      //test for intersection with every horizontal line
 
811
      hIt = horizontalLines.constBegin();
 
812
      for ( ; hIt != horizontalLines.constEnd(); ++hIt )
 
813
      {
 
814
        if ( hIt->second.intersect( vIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
 
815
        {
 
816
          crossEnd1 = pointOnLineWithDistance( intersectionPoint, vIt->second.p1(), mCrossLength );
 
817
          crossEnd2 = pointOnLineWithDistance( intersectionPoint, vIt->second.p2(), mCrossLength );
 
818
          p->drawLine( crossEnd1, crossEnd2 );
 
819
        }
 
820
      }
 
821
      //end mark
 
822
      QPointF crossEnd2 = pointOnLineWithDistance( vIt->second.p2(), vIt->second.p1(), mCrossLength );
 
823
      p->drawLine( vIt->second.p2(), crossEnd2 );
 
824
    }
 
825
 
 
826
    hIt = horizontalLines.constBegin();
 
827
    for ( ; hIt != horizontalLines.constEnd(); ++hIt )
 
828
    {
 
829
      //start mark
 
830
      crossEnd1 = pointOnLineWithDistance( hIt->second.p1(), hIt->second.p2(), mCrossLength );
 
831
      p->drawLine( hIt->second.p1(), crossEnd1 );
 
832
 
 
833
      vIt = verticalLines.constBegin();
 
834
      for ( ; vIt != verticalLines.constEnd(); ++vIt )
 
835
      {
 
836
        if ( vIt->second.intersect( hIt->second, &intersectionPoint ) == QLineF::BoundedIntersection )
 
837
        {
 
838
          crossEnd1 = pointOnLineWithDistance( intersectionPoint, hIt->second.p1(), mCrossLength );
 
839
          crossEnd2 = pointOnLineWithDistance( intersectionPoint, hIt->second.p2(), mCrossLength );
 
840
          p->drawLine( crossEnd1, crossEnd2 );
 
841
        }
 
842
      }
 
843
      //end mark
 
844
      crossEnd1 = pointOnLineWithDistance( hIt->second.p2(), hIt->second.p1(), mCrossLength );
 
845
      p->drawLine( hIt->second.p2(), crossEnd1 );
 
846
    }
 
847
 
 
848
 
 
849
  }
 
850
 
 
851
  p->setClipRect( thisPaintRect , Qt::NoClip );
 
852
 
 
853
  if ( mShowGridAnnotation )
 
854
  {
 
855
    drawCoordinateAnnotations( p, horizontalLines, verticalLines );
 
856
  }
 
857
}
 
858
 
 
859
void QgsComposerMap::drawCoordinateAnnotations( QPainter* p, const QList< QPair< double, QLineF > >& hLines, const QList< QPair< double, QLineF > >& vLines )
 
860
{
 
861
  if ( !p )
 
862
  {
 
863
    return;
 
864
  }
 
865
 
 
866
 
 
867
  QString currentAnnotationString;
 
868
  QList< QPair< double, QLineF > >::const_iterator it = hLines.constBegin();
 
869
  for ( ; it != hLines.constEnd(); ++it )
 
870
  {
 
871
    currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
 
872
    drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
 
873
    drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
 
874
  }
 
875
 
 
876
  it = vLines.constBegin();
 
877
  for ( ; it != vLines.constEnd(); ++it )
 
878
  {
 
879
    currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
 
880
    drawCoordinateAnnotation( p, it->second.p1(), currentAnnotationString );
 
881
    drawCoordinateAnnotation( p, it->second.p2(), currentAnnotationString );
 
882
  }
 
883
}
 
884
 
 
885
void QgsComposerMap::drawCoordinateAnnotation( QPainter* p, const QPointF& pos, QString annotationString )
 
886
{
 
887
  Border frameBorder = borderForLineCoord( pos );
 
888
  double textWidth = textWidthMillimeters( mGridAnnotationFont, annotationString );
 
889
  double textHeight = fontAscentMillimeters( mGridAnnotationFont );
 
890
  double xpos = pos.x();
 
891
  double ypos = pos.y();
 
892
  int rotation = 0;
 
893
 
 
894
  if ( frameBorder == Left )
 
895
  {
 
896
 
 
897
    if ( mGridAnnotationPosition == InsideMapFrame )
 
898
    {
 
899
      if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
 
900
      {
 
901
        xpos -= textHeight + mAnnotationFrameDistance;
 
902
        ypos += textWidth / 2.0;
 
903
        rotation = 270;
 
904
      }
 
905
      else
 
906
      {
 
907
        xpos += mAnnotationFrameDistance;
 
908
        ypos += textHeight / 2.0;
 
909
      }
 
910
    }
 
911
    else //Outside map frame
 
912
    {
 
913
      if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
 
914
      {
 
915
        xpos -= mAnnotationFrameDistance;
 
916
        ypos += textWidth / 2.0;
 
917
        rotation = 270;
 
918
      }
 
919
      else
 
920
      {
 
921
        xpos -= textWidth + mAnnotationFrameDistance;
 
922
        ypos += textHeight / 2.0;
 
923
      }
 
924
    }
 
925
 
 
926
  }
 
927
  else if ( frameBorder == Right )
 
928
  {
 
929
    if ( mGridAnnotationPosition == InsideMapFrame )
 
930
    {
 
931
      if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
 
932
      {
 
933
        xpos -= mAnnotationFrameDistance;
 
934
        ypos += textWidth / 2.0;
 
935
        rotation = 270;
 
936
      }
 
937
      else //Horizontal
 
938
      {
 
939
        xpos -= textWidth + mAnnotationFrameDistance;
 
940
        ypos += textHeight / 2.0;
 
941
      }
 
942
    }
 
943
    else //OutsideMapFrame
 
944
    {
 
945
      if ( mGridAnnotationDirection == Vertical || mGridAnnotationDirection == BoundaryDirection )
 
946
      {
 
947
        xpos += textHeight + mAnnotationFrameDistance;
 
948
        ypos += textWidth / 2.0;
 
949
        rotation = 270;
 
950
      }
 
951
      else //Horizontal
 
952
      {
 
953
        xpos += mAnnotationFrameDistance;
 
954
        ypos += textHeight / 2.0;
 
955
      }
 
956
    }
 
957
  }
 
958
  else if ( frameBorder == Bottom )
 
959
  {
 
960
    if ( mGridAnnotationPosition == InsideMapFrame )
 
961
    {
 
962
      if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
 
963
      {
 
964
        ypos -= mAnnotationFrameDistance;
 
965
        xpos -= textWidth / 2.0;
 
966
      }
 
967
      else //Vertical
 
968
      {
 
969
        xpos += textHeight / 2.0;
 
970
        ypos -= mAnnotationFrameDistance;
 
971
        rotation = 270;
 
972
      }
 
973
    }
 
974
    else //OutsideMapFrame
 
975
    {
 
976
      if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
 
977
      {
 
978
        ypos += mAnnotationFrameDistance + textHeight;
 
979
        xpos -= textWidth / 2.0;
 
980
      }
 
981
      else //Vertical
 
982
      {
 
983
        xpos += textHeight / 2.0;
 
984
        ypos += textWidth + mAnnotationFrameDistance;
 
985
        rotation = 270;
 
986
      }
 
987
    }
 
988
  }
 
989
  else //Top
 
990
  {
 
991
    if ( mGridAnnotationPosition == InsideMapFrame )
 
992
    {
 
993
      if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
 
994
      {
 
995
        xpos -= textWidth / 2.0;
 
996
        ypos += textHeight + mAnnotationFrameDistance;
 
997
      }
 
998
      else //Vertical
 
999
      {
 
1000
        xpos += textHeight / 2.0;
 
1001
        ypos += textWidth + mAnnotationFrameDistance;
 
1002
        rotation = 270;
 
1003
      }
 
1004
    }
 
1005
    else //OutsideMapFrame
 
1006
    {
 
1007
      if ( mGridAnnotationDirection == Horizontal || mGridAnnotationDirection == BoundaryDirection )
 
1008
      {
 
1009
        xpos -= textWidth / 2.0;
 
1010
        ypos -= mAnnotationFrameDistance;
 
1011
      }
 
1012
      else //Vertical
 
1013
      {
 
1014
        xpos += textHeight / 2.0;
 
1015
        ypos -= mAnnotationFrameDistance;
 
1016
        rotation = 270;
 
1017
      }
 
1018
    }
 
1019
  }
 
1020
 
 
1021
  drawAnnotation( p, QPointF( xpos, ypos ), rotation, annotationString );
 
1022
}
 
1023
 
 
1024
void QgsComposerMap::drawAnnotation( QPainter* p, const QPointF& pos, int rotation, const QString& annotationText )
 
1025
{
 
1026
  p->save();
 
1027
  p->translate( pos );
 
1028
  p->rotate( rotation );
 
1029
  p->setPen( QColor( 0, 0, 0 ) );
 
1030
  drawText( p, 0, 0, annotationText, mGridAnnotationFont );
 
1031
  p->restore();
 
1032
}
 
1033
 
 
1034
int QgsComposerMap::xGridLines( QList< QPair< double, QLineF > >& lines ) const
 
1035
{
 
1036
  lines.clear();
 
1037
  if ( mGridIntervalY <= 0.0 )
 
1038
  {
 
1039
    return 1;
 
1040
  }
 
1041
 
 
1042
  QPolygonF mapPolygon = transformedMapPolygon();
 
1043
  QRectF mapBoundingRect = mapPolygon.boundingRect();
 
1044
  double currentLevel = ( int )(( mapBoundingRect.top() - mGridOffsetY ) / mGridIntervalY + 1.0 ) * mGridIntervalY + mGridOffsetY;
 
1045
 
 
1046
  if ( mRotation <= 0.0 )
 
1047
  {
 
1048
    //no rotation. Do it 'the easy way'
 
1049
 
 
1050
    double yCanvasCoord;
 
1051
 
 
1052
    while ( currentLevel <= mapBoundingRect.bottom() )
 
1053
    {
 
1054
      yCanvasCoord = rect().height() * ( 1 - ( currentLevel - mapBoundingRect.top() ) / mapBoundingRect.height() );
 
1055
      lines.push_back( qMakePair( currentLevel, QLineF( 0, yCanvasCoord, rect().width(), yCanvasCoord ) ) );
 
1056
      currentLevel += mGridIntervalY;
 
1057
    }
 
1058
  }
 
1059
 
 
1060
  //the four border lines
 
1061
  QVector<QLineF> borderLines;
 
1062
  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
 
1063
  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
 
1064
  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
 
1065
  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
 
1066
 
 
1067
  QList<QPointF> intersectionList; //intersects between border lines and grid lines
 
1068
 
 
1069
  while ( currentLevel <= mapBoundingRect.bottom() )
 
1070
  {
 
1071
    intersectionList.clear();
 
1072
    QLineF gridLine( mapBoundingRect.left(), currentLevel, mapBoundingRect.right(), currentLevel );
 
1073
 
 
1074
    QVector<QLineF>::const_iterator it = borderLines.constBegin();
 
1075
    for ( ; it != borderLines.constEnd(); ++it )
 
1076
    {
 
1077
      QPointF intersectionPoint;
 
1078
      if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
 
1079
      {
 
1080
        intersectionList.push_back( intersectionPoint );
 
1081
        if ( intersectionList.size() >= 2 )
 
1082
        {
 
1083
          break; //we already have two intersections, skip further tests
 
1084
        }
 
1085
      }
 
1086
    }
 
1087
 
 
1088
    if ( intersectionList.size() >= 2 )
 
1089
    {
 
1090
      lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
 
1091
    }
 
1092
    currentLevel += mGridIntervalY;
 
1093
  }
 
1094
 
 
1095
 
 
1096
  return 0;
 
1097
}
 
1098
 
 
1099
int QgsComposerMap::yGridLines( QList< QPair< double, QLineF > >& lines ) const
 
1100
{
 
1101
  lines.clear();
 
1102
  if ( mGridIntervalX <= 0.0 )
 
1103
  {
 
1104
    return 1;
 
1105
  }
 
1106
 
 
1107
  QPolygonF mapPolygon = transformedMapPolygon();
 
1108
  QRectF mapBoundingRect = mapPolygon.boundingRect();
 
1109
  double currentLevel = ( int )(( mapBoundingRect.left() - mGridOffsetX ) / mGridIntervalX + 1.0 ) * mGridIntervalX + mGridOffsetX;
 
1110
 
 
1111
  if ( mRotation <= 0.0 )
 
1112
  {
 
1113
    //no rotation. Do it 'the easy way'
 
1114
    double xCanvasCoord;
 
1115
 
 
1116
    while ( currentLevel <= mapBoundingRect.right() )
 
1117
    {
 
1118
      xCanvasCoord = rect().width() * ( currentLevel - mapBoundingRect.left() ) / mapBoundingRect.width();
 
1119
      lines.push_back( qMakePair( currentLevel, QLineF( xCanvasCoord, 0, xCanvasCoord, rect().height() ) ) );
 
1120
      currentLevel += mGridIntervalX;
 
1121
    }
 
1122
  }
 
1123
 
 
1124
  //the four border lines
 
1125
  QVector<QLineF> borderLines;
 
1126
  borderLines << QLineF( mapPolygon.at( 0 ), mapPolygon.at( 1 ) );
 
1127
  borderLines << QLineF( mapPolygon.at( 1 ), mapPolygon.at( 2 ) );
 
1128
  borderLines << QLineF( mapPolygon.at( 2 ), mapPolygon.at( 3 ) );
 
1129
  borderLines << QLineF( mapPolygon.at( 3 ), mapPolygon.at( 0 ) );
 
1130
 
 
1131
  QList<QPointF> intersectionList; //intersects between border lines and grid lines
 
1132
 
 
1133
  while ( currentLevel <= mapBoundingRect.right() )
 
1134
  {
 
1135
    intersectionList.clear();
 
1136
    QLineF gridLine( currentLevel, mapBoundingRect.bottom(), currentLevel, mapBoundingRect.top() );
 
1137
 
 
1138
    QVector<QLineF>::const_iterator it = borderLines.constBegin();
 
1139
    for ( ; it != borderLines.constEnd(); ++it )
 
1140
    {
 
1141
      QPointF intersectionPoint;
 
1142
      if ( it->intersect( gridLine, &intersectionPoint ) == QLineF::BoundedIntersection )
 
1143
      {
 
1144
        intersectionList.push_back( intersectionPoint );
 
1145
        if ( intersectionList.size() >= 2 )
 
1146
        {
 
1147
          break; //we already have two intersections, skip further tests
 
1148
        }
 
1149
      }
 
1150
    }
 
1151
 
 
1152
    if ( intersectionList.size() >= 2 )
 
1153
    {
 
1154
      lines.push_back( qMakePair( currentLevel, QLineF( mapToItemCoords( intersectionList.at( 0 ) ), mapToItemCoords( intersectionList.at( 1 ) ) ) ) );
 
1155
    }
 
1156
    currentLevel += mGridIntervalX;
 
1157
  }
 
1158
 
 
1159
  return 0;
 
1160
}
 
1161
 
 
1162
void QgsComposerMap::setGridPenWidth( double w )
 
1163
{
 
1164
  mGridPen.setWidthF( w );
 
1165
}
 
1166
 
 
1167
void QgsComposerMap::setGridPenColor( const QColor& c )
 
1168
{
 
1169
  mGridPen.setColor( c );
 
1170
}
 
1171
 
 
1172
QRectF QgsComposerMap::boundingRect() const
 
1173
{
 
1174
  return mCurrentRectangle;
 
1175
}
 
1176
 
 
1177
void QgsComposerMap::updateBoundingRect()
 
1178
{
 
1179
  QRectF rectangle = rect();
 
1180
  double extension = maxExtension();
 
1181
  rectangle.setLeft( rectangle.left() - extension );
 
1182
  rectangle.setRight( rectangle.right() + extension );
 
1183
  rectangle.setTop( rectangle.top() - extension );
 
1184
  rectangle.setBottom( rectangle.bottom() + extension );
 
1185
  if ( rectangle != mCurrentRectangle )
 
1186
  {
 
1187
    prepareGeometryChange();
 
1188
    mCurrentRectangle = rectangle;
 
1189
  }
 
1190
}
 
1191
 
 
1192
QgsRectangle QgsComposerMap::transformedExtent() const
 
1193
{
 
1194
  double dx = mXOffset;
 
1195
  double dy = mYOffset;
 
1196
  transformShift( dx, dy );
 
1197
  return QgsRectangle( mExtent.xMinimum() - dx, mExtent.yMinimum() - dy, mExtent.xMaximum() - dx, mExtent.yMaximum() - dy );
 
1198
}
 
1199
 
 
1200
QPolygonF QgsComposerMap::transformedMapPolygon() const
 
1201
{
 
1202
  double dx = mXOffset;
 
1203
  double dy = mYOffset;
 
1204
  //qWarning("offset");
 
1205
  //qWarning(QString::number(dx).toLocal8Bit().data());
 
1206
  //qWarning(QString::number(dy).toLocal8Bit().data());
 
1207
  transformShift( dx, dy );
 
1208
  //qWarning("transformed:");
 
1209
  //qWarning(QString::number(dx).toLocal8Bit().data());
 
1210
  //qWarning(QString::number(dy).toLocal8Bit().data());
 
1211
  QPolygonF poly;
 
1212
  mapPolygon( poly );
 
1213
  poly.translate( -dx, -dy );
 
1214
  return poly;
 
1215
}
 
1216
 
 
1217
double QgsComposerMap::maxExtension() const
 
1218
{
 
1219
  if ( !mGridEnabled || !mShowGridAnnotation || mGridAnnotationPosition != OutsideMapFrame )
 
1220
  {
 
1221
    return 0;
 
1222
  }
 
1223
 
 
1224
  QList< QPair< double, QLineF > > xLines;
 
1225
  QList< QPair< double, QLineF > > yLines;
 
1226
 
 
1227
  if ( xGridLines( xLines ) != 0 )
 
1228
  {
 
1229
    return 0;
 
1230
  }
 
1231
 
 
1232
  if ( yGridLines( yLines ) != 0 )
 
1233
  {
 
1234
    return 0;
 
1235
  }
 
1236
 
 
1237
  double maxExtension = 0;
 
1238
  double currentExtension = 0;
 
1239
  QString currentAnnotationString;
 
1240
 
 
1241
  QList< QPair< double, QLineF > >::const_iterator it = xLines.constBegin();
 
1242
  for ( ; it != xLines.constEnd(); ++it )
 
1243
  {
 
1244
    currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
 
1245
    currentExtension = std::max( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
 
1246
    maxExtension = std::max( maxExtension, currentExtension );
 
1247
  }
 
1248
 
 
1249
  it = yLines.constBegin();
 
1250
  for ( ; it != yLines.constEnd(); ++it )
 
1251
  {
 
1252
    currentAnnotationString = QString::number( it->first, 'f', mGridAnnotationPrecision );
 
1253
    currentExtension = std::max( textWidthMillimeters( mGridAnnotationFont, currentAnnotationString ), fontAscentMillimeters( mGridAnnotationFont ) );
 
1254
    maxExtension = std::max( maxExtension, currentExtension );
 
1255
  }
 
1256
 
 
1257
  return maxExtension + mAnnotationFrameDistance;
 
1258
}
 
1259
 
 
1260
void QgsComposerMap::mapPolygon( QPolygonF& poly ) const
 
1261
{
 
1262
  poly.clear();
 
1263
  if ( mRotation == 0 )
 
1264
  {
 
1265
    poly << QPointF( mExtent.xMinimum(), mExtent.yMaximum() );
 
1266
    poly << QPointF( mExtent.xMaximum(), mExtent.yMaximum() );
 
1267
    poly << QPointF( mExtent.xMaximum(), mExtent.yMinimum() );
 
1268
    poly << QPointF( mExtent.xMinimum(), mExtent.yMinimum() );
 
1269
    return;
 
1270
  }
 
1271
 
 
1272
  //there is rotation
 
1273
  QgsPoint rotationPoint(( mExtent.xMaximum() + mExtent.xMinimum() ) / 2.0, ( mExtent.yMaximum() + mExtent.yMinimum() ) / 2.0 );
 
1274
  double dx, dy; //x-, y- shift from rotation point to corner point
 
1275
 
 
1276
  //top left point
 
1277
  dx = rotationPoint.x() - mExtent.xMinimum();
 
1278
  dy = rotationPoint.y() - mExtent.yMaximum();
 
1279
  rotate( mRotation, dx, dy );
 
1280
  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
 
1281
 
 
1282
  //top right point
 
1283
  dx = rotationPoint.x() - mExtent.xMaximum();
 
1284
  dy = rotationPoint.y() - mExtent.yMaximum();
 
1285
  rotate( mRotation, dx, dy );
 
1286
  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
 
1287
 
 
1288
  //bottom right point
 
1289
  dx = rotationPoint.x() - mExtent.xMaximum();
 
1290
  dy = rotationPoint.y() - mExtent.yMinimum();
 
1291
  rotate( mRotation, dx, dy );
 
1292
  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
 
1293
 
 
1294
  //bottom left point
 
1295
  dx = rotationPoint.x() - mExtent.xMinimum();
 
1296
  dy = rotationPoint.y() - mExtent.yMinimum();
 
1297
  rotate( mRotation, dx, dy );
 
1298
  poly << QPointF( rotationPoint.x() + dx, rotationPoint.y() + dy );
 
1299
}
 
1300
 
 
1301
void QgsComposerMap::requestedExtent( QgsRectangle& extent ) const
 
1302
{
 
1303
  if ( mRotation == 0 )
 
1304
  {
 
1305
    extent = mExtent;
 
1306
    return;
 
1307
  }
 
1308
 
 
1309
  QPolygonF poly;
 
1310
  mapPolygon( poly );
 
1311
  QRectF bRect = poly.boundingRect();
 
1312
  extent.setXMinimum( bRect.left() );
 
1313
  extent.setXMaximum( bRect.right() );
 
1314
  extent.setYMinimum( bRect.top() );
 
1315
  extent.setYMaximum( bRect.bottom() );
 
1316
  return;
 
1317
}
 
1318
 
 
1319
double QgsComposerMap::mapUnitsToMM() const
 
1320
{
 
1321
  double extentWidth = mExtent.width();
 
1322
  if ( extentWidth <= 0 )
 
1323
  {
 
1324
    return 1;
 
1325
  }
 
1326
  return rect().width() / extentWidth;
 
1327
}
 
1328
 
 
1329
void QgsComposerMap::transformShift( double& xShift, double& yShift ) const
 
1330
{
 
1331
  double mmToMapUnits = 1.0 / mapUnitsToMM();
 
1332
  double dxScaled = xShift * mmToMapUnits;
 
1333
  double dyScaled = - yShift * mmToMapUnits;
 
1334
 
 
1335
  rotate( mRotation, dxScaled, dyScaled );
 
1336
 
 
1337
  xShift = dxScaled;
 
1338
  yShift = dyScaled;
 
1339
}
 
1340
 
 
1341
QPointF QgsComposerMap::mapToItemCoords( const QPointF& mapCoords ) const
 
1342
{
 
1343
  QPolygonF mapPoly = transformedMapPolygon();
 
1344
  if ( mapPoly.size() < 1 )
 
1345
  {
 
1346
    return QPointF( 0, 0 );
 
1347
  }
 
1348
 
 
1349
  QgsRectangle tExtent = transformedExtent();
 
1350
  QgsPoint rotationPoint(( tExtent.xMaximum() + tExtent.xMinimum() ) / 2.0, ( tExtent.yMaximum() + tExtent.yMinimum() ) / 2.0 );
 
1351
  double dx = mapCoords.x() - rotationPoint.x();
 
1352
  double dy = mapCoords.y() - rotationPoint.y();
 
1353
  rotate( -mRotation, dx, dy );
 
1354
  QgsPoint backRotatedCoords( rotationPoint.x() + dx, rotationPoint.y() + dy );
 
1355
 
 
1356
  QgsRectangle unrotatedExtent = transformedExtent();
 
1357
  double xItem = rect().width() * ( backRotatedCoords.x() - unrotatedExtent.xMinimum() ) / unrotatedExtent.width();
 
1358
  double yItem = rect().height() * ( 1 - ( backRotatedCoords.y() - unrotatedExtent.yMinimum() ) / unrotatedExtent.height() );
 
1359
  return QPointF( xItem, yItem );
 
1360
}
 
1361
 
 
1362
QgsComposerMap::Border QgsComposerMap::borderForLineCoord( const QPointF& p ) const
 
1363
{
 
1364
  if ( p.x() <= pen().widthF() )
 
1365
  {
 
1366
    return Left;
 
1367
  }
 
1368
  else if ( p.x() >= ( rect().width() - pen().widthF() ) )
 
1369
  {
 
1370
    return Right;
 
1371
  }
 
1372
  else if ( p.y() <= pen().widthF() )
 
1373
  {
 
1374
    return Top;
 
1375
  }
 
1376
  else
 
1377
  {
 
1378
    return Bottom;
 
1379
  }
 
1380
}