QGIS API Documentation  2.18.3-Las Palmas (77b8c3d)
qgscomposerpicture.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposerpicture.cpp
3  -------------------
4  begin : September 2005
5  copyright : (C) 2005 by Radim Blazek
6  email : radim.blazek@gmail.com
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 "qgscomposerpicture.h"
19 #include "qgscomposerutils.h"
20 #include "qgscomposermap.h"
21 #include "qgscomposition.h"
22 #include "qgsatlascomposition.h"
23 #include "qgsproject.h"
24 #include "qgsexpression.h"
25 #include "qgsvectorlayer.h"
26 #include "qgsmessagelog.h"
27 #include "qgsdatadefined.h"
29 #include "qgssymbollayerv2utils.h"
30 #include "qgssvgcache.h"
31 #include "qgslogger.h"
32 #include "qgsbearingutils.h"
33 #include "qgsmapsettings.h"
34 
35 #include <QDomDocument>
36 #include <QDomElement>
37 #include <QFileInfo>
38 #include <QImageReader>
39 #include <QPainter>
40 #include <QSvgRenderer>
41 #include <QNetworkRequest>
42 #include <QNetworkReply>
43 #include <QEventLoop>
44 #include <QCoreApplication>
45 
47  : QgsComposerItem( composition )
48  , mMode( Unknown )
49  , mPictureRotation( 0 )
50  , mRotationMap( nullptr )
51  , mNorthMode( GridNorth )
52  , mNorthOffset( 0.0 )
53  , mResizeMode( QgsComposerPicture::Zoom )
54  , mPictureAnchor( UpperLeft )
55  , mSvgFillColor( QColor( 255, 255, 255 ) )
56  , mSvgBorderColor( QColor( 0, 0, 0 ) )
57  , mSvgBorderWidth( 0.2 )
58  , mHasExpressionError( false )
59  , mLoadingSvg( false )
60 {
61  mPictureWidth = rect().width();
62  init();
63 }
64 
66  : QgsComposerItem( nullptr )
67  , mMode( Unknown )
68  , mPictureRotation( 0 )
69  , mRotationMap( nullptr )
70  , mNorthMode( GridNorth )
71  , mNorthOffset( 0.0 )
72  , mResizeMode( QgsComposerPicture::Zoom )
73  , mPictureAnchor( UpperLeft )
74  , mSvgFillColor( QColor( 255, 255, 255 ) )
75  , mSvgBorderColor( QColor( 0, 0, 0 ) )
76  , mSvgBorderWidth( 0.2 )
77  , mHasExpressionError( false )
78  , mLoadingSvg( false )
79 {
80  mPictureHeight = rect().height();
81  init();
82 }
83 
84 void QgsComposerPicture::init()
85 {
86  //default to no background
87  setBackgroundEnabled( false );
88 
89  //data defined strings
91 
92  //insert PictureSource data defined property (only required due to deprecated API elements,
93  //remove after 3.0
95 
96  //connect some signals
97 
98  //connect to atlas feature changing
99  //to update the picture source expression
100  connect( &mComposition->atlasComposition(), SIGNAL( featureChanged( QgsFeature* ) ), this, SLOT( refreshPicture() ) );
101 
102  //connect to composer print resolution changing
103  connect( mComposition, SIGNAL( printResolutionChanged() ), this, SLOT( recalculateSize() ) );
104 }
105 
107 {
108 
109 }
110 
111 void QgsComposerPicture::paint( QPainter* painter, const QStyleOptionGraphicsItem* itemStyle, QWidget* pWidget )
112 {
113  Q_UNUSED( itemStyle );
114  Q_UNUSED( pWidget );
115  if ( !painter )
116  {
117  return;
118  }
119  if ( !shouldDrawItem() )
120  {
121  return;
122  }
123 
124  drawBackground( painter );
125 
126  //int newDpi = ( painter->device()->logicalDpiX() + painter->device()->logicalDpiY() ) / 2;
127 
128  //picture resizing
129  if ( mMode != Unknown )
130  {
131  double boundRectWidthMM;
132  double boundRectHeightMM;
133  QRect imageRect;
134  if ( mResizeMode == QgsComposerPicture::Zoom || mResizeMode == QgsComposerPicture::ZoomResizeFrame )
135  {
136  boundRectWidthMM = mPictureWidth;
137  boundRectHeightMM = mPictureHeight;
138  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
139  }
140  else if ( mResizeMode == QgsComposerPicture::Stretch )
141  {
142  boundRectWidthMM = rect().width();
143  boundRectHeightMM = rect().height();
144  imageRect = QRect( 0, 0, mImage.width(), mImage.height() );
145  }
146  else if ( mResizeMode == QgsComposerPicture::Clip )
147  {
148  boundRectWidthMM = rect().width();
149  boundRectHeightMM = rect().height();
150  int imageRectWidthPixels = mImage.width();
151  int imageRectHeightPixels = mImage.height();
152  imageRect = clippedImageRect( boundRectWidthMM, boundRectHeightMM,
153  QSize( imageRectWidthPixels, imageRectHeightPixels ) );
154  }
155  else
156  {
157  boundRectWidthMM = rect().width();
158  boundRectHeightMM = rect().height();
159  imageRect = QRect( 0, 0, rect().width() * mComposition->printResolution() / 25.4,
160  rect().height() * mComposition->printResolution() / 25.4 );
161  }
162  painter->save();
163  //antialiasing on
164  painter->setRenderHint( QPainter::Antialiasing, true );
165 
166  //zoom mode - calculate anchor point and rotation
167  if ( mResizeMode == Zoom )
168  {
169  //TODO - allow placement modes with rotation set. for now, setting a rotation
170  //always places picture in center of frame
171  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
172  {
173  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
174  painter->rotate( mPictureRotation );
175  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
176  }
177  else
178  {
179  //shift painter to edge/middle of frame depending on placement
180  double diffX = rect().width() - boundRectWidthMM;
181  double diffY = rect().height() - boundRectHeightMM;
182 
183  double dX = 0;
184  double dY = 0;
185  switch ( mPictureAnchor )
186  {
187  case UpperLeft:
188  case MiddleLeft:
189  case LowerLeft:
190  //nothing to do
191  break;
192  case UpperMiddle:
193  case Middle:
194  case LowerMiddle:
195  dX = diffX / 2.0;
196  break;
197  case UpperRight:
198  case MiddleRight:
199  case LowerRight:
200  dX = diffX;
201  break;
202  }
203  switch ( mPictureAnchor )
204  {
205  case UpperLeft:
206  case UpperMiddle:
207  case UpperRight:
208  //nothing to do
209  break;
210  case MiddleLeft:
211  case Middle:
212  case MiddleRight:
213  dY = diffY / 2.0;
214  break;
215  case LowerLeft:
216  case LowerMiddle:
217  case LowerRight:
218  dY = diffY;
219  break;
220  }
221  painter->translate( dX, dY );
222  }
223  }
224  else if ( mResizeMode == ZoomResizeFrame )
225  {
226  if ( !qgsDoubleNear( mPictureRotation, 0.0 ) )
227  {
228  painter->translate( rect().width() / 2.0, rect().height() / 2.0 );
229  painter->rotate( mPictureRotation );
230  painter->translate( -boundRectWidthMM / 2.0, -boundRectHeightMM / 2.0 );
231  }
232  }
233 
234  if ( mMode == SVG )
235  {
236  mSVG.render( painter, QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ) );
237  }
238  else if ( mMode == RASTER )
239  {
240  painter->drawImage( QRectF( 0, 0, boundRectWidthMM, boundRectHeightMM ), mImage, imageRect );
241  }
242 
243  painter->restore();
244  }
245 
246  //frame and selection boxes
247  drawFrame( painter );
248  if ( isSelected() )
249  {
250  drawSelectionBoxes( painter );
251  }
252 }
253 
254 QRect QgsComposerPicture::clippedImageRect( double &boundRectWidthMM, double &boundRectHeightMM, QSize imageRectPixels )
255 {
256  int boundRectWidthPixels = boundRectWidthMM * mComposition->printResolution() / 25.4;
257  int boundRectHeightPixels = boundRectHeightMM * mComposition->printResolution() / 25.4;
258 
259  //update boundRectWidth/Height so that they exactly match pixel bounds
260  boundRectWidthMM = boundRectWidthPixels * 25.4 / mComposition->printResolution();
261  boundRectHeightMM = boundRectHeightPixels * 25.4 / mComposition->printResolution();
262 
263  //calculate part of image which fits in bounds
264  int leftClip = 0;
265  int topClip = 0;
266 
267  //calculate left crop
268  switch ( mPictureAnchor )
269  {
270  case UpperLeft:
271  case MiddleLeft:
272  case LowerLeft:
273  leftClip = 0;
274  break;
275  case UpperMiddle:
276  case Middle:
277  case LowerMiddle:
278  leftClip = ( imageRectPixels.width() - boundRectWidthPixels ) / 2;
279  break;
280  case UpperRight:
281  case MiddleRight:
282  case LowerRight:
283  leftClip = imageRectPixels.width() - boundRectWidthPixels;
284  break;
285  }
286 
287  //calculate top crop
288  switch ( mPictureAnchor )
289  {
290  case UpperLeft:
291  case UpperMiddle:
292  case UpperRight:
293  topClip = 0;
294  break;
295  case MiddleLeft:
296  case Middle:
297  case MiddleRight:
298  topClip = ( imageRectPixels.height() - boundRectHeightPixels ) / 2;
299  break;
300  case LowerLeft:
301  case LowerMiddle:
302  case LowerRight:
303  topClip = imageRectPixels.height() - boundRectHeightPixels;
304  break;
305  }
306 
307  return QRect( leftClip, topClip, boundRectWidthPixels, boundRectHeightPixels );
308 }
309 
311 {
312  setPicturePath( path );
313 }
314 
316 {
317  const QgsExpressionContext* evalContext = context;
319  if ( !evalContext )
320  {
321  scopedContext.reset( createExpressionContext() );
322  evalContext = scopedContext.data();
323  }
324 
325  QString source = mSourcePath;
326 
327  //data defined source set?
328  mHasExpressionError = false;
329  QVariant exprVal;
331  {
332  if ( dataDefinedEvaluate( QgsComposerObject::PictureSource, exprVal, *evalContext ) )
333  {
334  source = exprVal.toString().trimmed();
335  QgsDebugMsg( QString( "exprVal PictureSource:%1" ).arg( source ) );
336  }
337  else
338  {
339  mHasExpressionError = true;
340  source = QString();
341  QgsMessageLog::logMessage( tr( "Picture expression eval error" ) );
342  }
343  }
344 
345  loadPicture( source );
346 }
347 
348 void QgsComposerPicture::loadRemotePicture( const QString &url )
349 {
350  //remote location
351 
352  QgsNetworkContentFetcher fetcher;
353  //pause until HTML fetch
354  mLoaded = false;
355  fetcher.fetchContent( QUrl( url ) );
356  connect( &fetcher, SIGNAL( finished() ), this, SLOT( remotePictureLoaded() ) );
357 
358  while ( !mLoaded )
359  {
360  qApp->processEvents();
361  }
362 
363  QNetworkReply* reply = fetcher.reply();
364  if ( reply )
365  {
366  QImageReader imageReader( reply );
367  mImage = imageReader.read();
368  mMode = RASTER;
369  reply->deleteLater();
370  }
371  else
372  {
373  mMode = Unknown;
374  }
375 }
376 
377 void QgsComposerPicture::loadLocalPicture( const QString &path )
378 {
379  QFile pic;
380  pic.setFileName( path );
381 
382  if ( !pic.exists() )
383  {
384  mMode = Unknown;
385  }
386  else
387  {
388  QFileInfo sourceFileInfo( pic );
389  QString sourceFileSuffix = sourceFileInfo.suffix();
390  if ( sourceFileSuffix.compare( "svg", Qt::CaseInsensitive ) == 0 )
391  {
392  //try to open svg
393  const QByteArray &svgContent = QgsSvgCache::instance()->svgContent( pic.fileName(), rect().width(), mSvgFillColor, mSvgBorderColor, mSvgBorderWidth,
394  1.0, 1.0 );
395  mSVG.load( svgContent );
396  if ( mSVG.isValid() )
397  {
398  mMode = SVG;
399  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
400  mDefaultSvgSize.setWidth( viewBox.width() );
401  mDefaultSvgSize.setHeight( viewBox.height() );
402  }
403  else
404  {
405  mMode = Unknown;
406  }
407  }
408  else
409  {
410  //try to open raster with QImageReader
411  QImageReader imageReader( pic.fileName() );
412  if ( imageReader.read( &mImage ) )
413  {
414  mMode = RASTER;
415  }
416  else
417  {
418  mMode = Unknown;
419  }
420  }
421  }
422 
423 }
424 
425 void QgsComposerPicture::remotePictureLoaded()
426 {
427  mLoaded = true;
428 }
429 
430 void QgsComposerPicture::updateMapRotation()
431 {
432  if ( !mRotationMap )
433  return;
434 
435  // take map rotation
436  double rotation = mRotationMap->mapRotation();
437 
438  // handle true north
439  switch ( mNorthMode )
440  {
441  case GridNorth:
442  break; // nothing to do
443 
444  case TrueNorth:
445  {
446  QgsPoint center = mRotationMap->currentMapExtent()->center();
448 
449  try
450  {
451  double bearing = QgsBearingUtils::bearingTrueNorth( crs, center );
452  rotation += bearing;
453  }
454  catch ( QgsException& e )
455  {
456  Q_UNUSED( e );
457  QgsDebugMsg( QString( "Caught exception %1" ).arg( e.what() ) );
458  }
459  break;
460  }
461  }
462 
463  rotation += mNorthOffset;
464  setPictureRotation( rotation );
465 }
466 
467 void QgsComposerPicture::loadPicture( const QString &path )
468 {
469  if ( path.startsWith( "http" ) )
470  {
471  //remote location
472  loadRemotePicture( path );
473  }
474  else
475  {
476  //local location
477  loadLocalPicture( path );
478  }
479  if ( mMode != Unknown ) //make sure we start with a new QImage
480  {
481  recalculateSize();
482  }
483  else if ( mHasExpressionError || !( path.isEmpty() ) )
484  {
485  //trying to load an invalid file or bad expression, show cross picture
486  mMode = SVG;
487  QString badFile( ":/images/composer/missing_image.svg" );
488  mSVG.load( badFile );
489  if ( mSVG.isValid() )
490  {
491  mMode = SVG;
492  QRect viewBox = mSVG.viewBox(); //take width/height ratio from view box instead of default size
493  mDefaultSvgSize.setWidth( viewBox.width() );
494  mDefaultSvgSize.setHeight( viewBox.height() );
495  recalculateSize();
496  }
497  }
498 
499  emit itemChanged();
500 }
501 
502 QRectF QgsComposerPicture::boundedImageRect( double deviceWidth, double deviceHeight )
503 {
504  double imageToDeviceRatio;
505  if ( mImage.width() / deviceWidth > mImage.height() / deviceHeight )
506  {
507  imageToDeviceRatio = deviceWidth / mImage.width();
508  double height = imageToDeviceRatio * mImage.height();
509  return QRectF( 0, 0, deviceWidth, height );
510  }
511  else
512  {
513  imageToDeviceRatio = deviceHeight / mImage.height();
514  double width = imageToDeviceRatio * mImage.width();
515  return QRectF( 0, 0, width, deviceHeight );
516  }
517 }
518 
519 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
520 {
521  double imageToSvgRatio;
522  if ( deviceWidth / mDefaultSvgSize.width() > deviceHeight / mDefaultSvgSize.height() )
523  {
524  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
525  double width = mDefaultSvgSize.width() * imageToSvgRatio;
526  return QRectF( 0, 0, width, deviceHeight );
527  }
528  else
529  {
530  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
531  double height = mDefaultSvgSize.height() * imageToSvgRatio;
532  return QRectF( 0, 0, deviceWidth, height );
533  }
534 }
535 
536 QSizeF QgsComposerPicture::pictureSize()
537 {
538  if ( mMode == SVG )
539  {
540  return mDefaultSvgSize;
541  }
542  else if ( mMode == RASTER )
543  {
544  return QSizeF( mImage.width(), mImage.height() );
545  }
546  else
547  {
548  return QSizeF( 0, 0 );
549  }
550 }
551 
552 #if 0
553 QRectF QgsComposerPicture::boundedSVGRect( double deviceWidth, double deviceHeight )
554 {
555  double imageToSvgRatio;
556  if ( deviceWidth / mDefaultSvgSize.width() < deviceHeight / mDefaultSvgSize.height() )
557  {
558  imageToSvgRatio = deviceWidth / mDefaultSvgSize.width();
559  double height = mDefaultSvgSize.height() * imageToSvgRatio;
560  return QRectF( 0, 0, deviceWidth, height );
561  }
562  else
563  {
564  imageToSvgRatio = deviceHeight / mDefaultSvgSize.height();
565  double width = mDefaultSvgSize.width() * imageToSvgRatio;
566  return QRectF( 0, 0, width, deviceHeight );
567  }
568 }
569 #endif //0
570 
571 void QgsComposerPicture::setSceneRect( const QRectF& rectangle )
572 {
573  QSizeF currentPictureSize = pictureSize();
574 
575  if ( mResizeMode == QgsComposerPicture::Clip )
576  {
577  QgsComposerItem::setSceneRect( rectangle );
578  mPictureWidth = rectangle.width();
579  mPictureHeight = rectangle.height();
580  }
581  else
582  {
583  QRectF newRect = rectangle;
584 
585  if ( mResizeMode == ZoomResizeFrame && !rect().isEmpty() && !( currentPictureSize.isEmpty() ) )
586  {
587  QSizeF targetImageSize;
588  if ( qgsDoubleNear( mPictureRotation, 0.0 ) )
589  {
590  targetImageSize = currentPictureSize;
591  }
592  else
593  {
594  //calculate aspect ratio of bounds of rotated image
595  QTransform tr;
596  tr.rotate( mPictureRotation );
597  QRectF rotatedBounds = tr.mapRect( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ) );
598  targetImageSize = QSizeF( rotatedBounds.width(), rotatedBounds.height() );
599  }
600 
601  //if height has changed more than width, then fix width and set height correspondingly
602  //else, do the opposite
603  if ( qAbs( rect().width() - rectangle.width() ) <
604  qAbs( rect().height() - rectangle.height() ) )
605  {
606  newRect.setHeight( targetImageSize.height() * newRect.width() / targetImageSize.width() );
607  }
608  else
609  {
610  newRect.setWidth( targetImageSize.width() * newRect.height() / targetImageSize.height() );
611  }
612  }
613  else if ( mResizeMode == FrameToImageSize )
614  {
615  if ( !( currentPictureSize.isEmpty() ) )
616  {
617  newRect.setWidth( currentPictureSize.width() * 25.4 / mComposition->printResolution() );
618  newRect.setHeight( currentPictureSize.height() * 25.4 / mComposition->printResolution() );
619  }
620  }
621 
622  //find largest scaling of picture with this rotation which fits in item
623  if ( mResizeMode == Zoom || mResizeMode == ZoomResizeFrame )
624  {
625  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), newRect, mPictureRotation );
626  mPictureWidth = rotatedImageRect.width();
627  mPictureHeight = rotatedImageRect.height();
628  }
629  else
630  {
631  mPictureWidth = newRect.width();
632  mPictureHeight = newRect.height();
633  }
634 
636  emit itemChanged();
637  }
638 
639  if ( mMode == SVG && !mLoadingSvg )
640  {
641  mLoadingSvg = true;
642  refreshPicture();
643  mLoadingSvg = false;
644  }
645 }
646 
648 {
649  //kept for compatibility for QGIS2.0 api
650  setPictureRotation( r );
651 }
652 
654 {
655  double oldRotation = mPictureRotation;
656  mPictureRotation = r;
657 
658  if ( mResizeMode == Zoom )
659  {
660  //find largest scaling of picture with this rotation which fits in item
661  QSizeF currentPictureSize = pictureSize();
662  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), mPictureRotation );
663  mPictureWidth = rotatedImageRect.width();
664  mPictureHeight = rotatedImageRect.height();
665  update();
666  }
667  else if ( mResizeMode == ZoomResizeFrame )
668  {
669  QSizeF currentPictureSize = pictureSize();
670  QRectF oldRect = QRectF( pos().x(), pos().y(), rect().width(), rect().height() );
671 
672  //calculate actual size of image inside frame
673  QRectF rotatedImageRect = QgsComposerUtils::largestRotatedRectWithinBounds( QRectF( 0, 0, currentPictureSize.width(), currentPictureSize.height() ), rect(), oldRotation );
674 
675  //rotate image rect by new rotation and get bounding box
676  QTransform tr;
677  tr.rotate( mPictureRotation );
678  QRectF newRect = tr.mapRect( QRectF( 0, 0, rotatedImageRect.width(), rotatedImageRect.height() ) );
679 
680  //keep the center in the same location
681  newRect.moveCenter( oldRect.center() );
683  emit itemChanged();
684  }
685 
686  emit pictureRotationChanged( mPictureRotation );
687 }
688 
689 void QgsComposerPicture::setRotationMap( int composerMapId )
690 {
691  if ( !mComposition )
692  {
693  return;
694  }
695 
696  if ( composerMapId == -1 ) //disable rotation from map
697  {
698  disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
699  disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
700  mRotationMap = nullptr;
701  }
702 
703  const QgsComposerMap* map = mComposition->getComposerMapById( composerMapId );
704  if ( !map )
705  {
706  return;
707  }
708  if ( mRotationMap )
709  {
710  disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
711  disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
712  }
713  mPictureRotation = map->mapRotation();
714  connect( map, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
715  connect( map, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
716  mRotationMap = map;
717  updateMapRotation();
718  emit pictureRotationChanged( mPictureRotation );
719 }
720 
722 {
723  mResizeMode = mode;
725  || ( mode == QgsComposerPicture::Zoom && !qgsDoubleNear( mPictureRotation, 0.0 ) ) )
726  {
727  //call set scene rect to force item to resize to fit picture
728  recalculateSize();
729  }
730  update();
731 }
732 
734 {
735  //call set scene rect with current position/size, as this will trigger the
736  //picture item to recalculate its frame and image size
737  setSceneRect( QRectF( pos().x(), pos().y(), rect().width(), rect().height() ) );
738 }
739 
741 {
742  const QgsExpressionContext* evalContext = context;
744  if ( !evalContext )
745  {
746  scopedContext.reset( createExpressionContext() );
747  evalContext = scopedContext.data();
748  }
749 
751  {
752  refreshPicture( evalContext );
753  }
754 
755  QgsComposerItem::refreshDataDefinedProperty( property, evalContext );
756 }
757 
759 {
761  refreshPicture();
762 }
763 
765 {
767  refreshPicture();
768 }
769 
771 {
772  return picturePath();
773 }
774 
776 {
777  mSourcePath = path;
778  refreshPicture();
779 }
780 
782 {
783  return mSourcePath;
784 }
785 
787 {
788  if ( elem.isNull() )
789  {
790  return false;
791  }
792  QDomElement composerPictureElem = doc.createElement( "ComposerPicture" );
793  composerPictureElem.setAttribute( "file", QgsProject::instance()->writePath( mSourcePath ) );
794  composerPictureElem.setAttribute( "pictureWidth", QString::number( mPictureWidth ) );
795  composerPictureElem.setAttribute( "pictureHeight", QString::number( mPictureHeight ) );
796  composerPictureElem.setAttribute( "resizeMode", QString::number( static_cast< int >( mResizeMode ) ) );
797  composerPictureElem.setAttribute( "anchorPoint", QString::number( static_cast< int >( mPictureAnchor ) ) );
798  composerPictureElem.setAttribute( "svgFillColor", QgsSymbolLayerV2Utils::encodeColor( mSvgFillColor ) );
799  composerPictureElem.setAttribute( "svgBorderColor", QgsSymbolLayerV2Utils::encodeColor( mSvgBorderColor ) );
800  composerPictureElem.setAttribute( "svgBorderWidth", QString::number( mSvgBorderWidth ) );
801 
802  //rotation
803  composerPictureElem.setAttribute( "pictureRotation", QString::number( mPictureRotation ) );
804  if ( !mRotationMap )
805  {
806  composerPictureElem.setAttribute( "mapId", -1 );
807  }
808  else
809  {
810  composerPictureElem.setAttribute( "mapId", mRotationMap->id() );
811  }
812  composerPictureElem.setAttribute( "northMode", mNorthMode );
813  composerPictureElem.setAttribute( "northOffset", mNorthOffset );
814 
815  _writeXML( composerPictureElem, doc );
816  elem.appendChild( composerPictureElem );
817  return true;
818 }
819 
820 bool QgsComposerPicture::readXML( const QDomElement& itemElem, const QDomDocument& doc )
821 {
822  if ( itemElem.isNull() )
823  {
824  return false;
825  }
826 
827  mPictureWidth = itemElem.attribute( "pictureWidth", "10" ).toDouble();
828  mPictureHeight = itemElem.attribute( "pictureHeight", "10" ).toDouble();
829  mResizeMode = QgsComposerPicture::ResizeMode( itemElem.attribute( "resizeMode", "0" ).toInt() );
830  //when loading from xml, default to anchor point of middle to match pre 2.4 behaviour
831  mPictureAnchor = static_cast< QgsComposerItem::ItemPositionMode >( itemElem.attribute( "anchorPoint", QString::number( QgsComposerItem::Middle ) ).toInt() );
832 
833  mSvgFillColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "svgFillColor", QgsSymbolLayerV2Utils::encodeColor( QColor( 255, 255, 255 ) ) ) );
834  mSvgBorderColor = QgsSymbolLayerV2Utils::decodeColor( itemElem.attribute( "svgBorderColor", QgsSymbolLayerV2Utils::encodeColor( QColor( 0, 0, 0 ) ) ) );
835  mSvgBorderWidth = itemElem.attribute( "svgBorderWidth", "0.2" ).toDouble();
836 
837  QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
838  if ( !composerItemList.isEmpty() )
839  {
840  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
841 
842  if ( !qgsDoubleNear( composerItemElem.attribute( "rotation", "0" ).toDouble(), 0.0 ) )
843  {
844  //in versions prior to 2.1 picture rotation was stored in the rotation attribute
845  mPictureRotation = composerItemElem.attribute( "rotation", "0" ).toDouble();
846  }
847 
848  _readXML( composerItemElem, doc );
849  }
850 
851  mDefaultSvgSize = QSize( 0, 0 );
852 
853  if ( itemElem.hasAttribute( "sourceExpression" ) )
854  {
855  //update pre 2.5 picture expression to use data defined expression
856  QString sourceExpression = itemElem.attribute( "sourceExpression", "" );
857  QString useExpression = itemElem.attribute( "useExpression" );
858  bool expressionActive;
859  if ( useExpression.compare( "true", Qt::CaseInsensitive ) == 0 )
860  {
861  expressionActive = true;
862  }
863  else
864  {
865  expressionActive = false;
866  }
867 
868  setDataDefinedProperty( QgsComposerObject::PictureSource, expressionActive, true, sourceExpression, QString() );
869  }
870 
871  mSourcePath = QgsProject::instance()->readPath( itemElem.attribute( "file" ) );
872 
873  //picture rotation
874  if ( !qgsDoubleNear( itemElem.attribute( "pictureRotation", "0" ).toDouble(), 0.0 ) )
875  {
876  mPictureRotation = itemElem.attribute( "pictureRotation", "0" ).toDouble();
877  }
878 
879  //rotation map
880  mNorthMode = static_cast< NorthMode >( itemElem.attribute( "northMode", "0" ).toInt() );
881  mNorthOffset = itemElem.attribute( "northOffset", "0" ).toDouble();
882 
883  int rotationMapId = itemElem.attribute( "mapId", "-1" ).toInt();
884  if ( rotationMapId == -1 )
885  {
886  mRotationMap = nullptr;
887  }
888  else if ( mComposition )
889  {
890 
891  if ( mRotationMap )
892  {
893  disconnect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
894  disconnect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
895  }
896  mRotationMap = mComposition->getComposerMapById( rotationMapId );
897  connect( mRotationMap, SIGNAL( mapRotationChanged( double ) ), this, SLOT( updateMapRotation() ) );
898  connect( mRotationMap, SIGNAL( extentChanged() ), this, SLOT( updateMapRotation() ) );
899  }
900 
901  refreshPicture();
902 
903  emit itemChanged();
904  return true;
905 }
906 
908 {
909  if ( !mRotationMap )
910  {
911  return -1;
912  }
913  else
914  {
915  return mRotationMap->id();
916  }
917 }
918 
920 {
921  mNorthMode = mode;
922  updateMapRotation();
923 }
924 
926 {
927  mNorthOffset = offset;
928  updateMapRotation();
929 }
930 
932 {
933  mPictureAnchor = anchor;
934  update();
935 }
936 
938 {
939  mSvgFillColor = color;
940  refreshPicture();
941 }
942 
944 {
945  mSvgBorderColor = color;
946  refreshPicture();
947 }
948 
950 {
951  mSvgBorderWidth = width;
952  refreshPicture();
953 }
954 
956 {
958 }
959 
961 {
963 }
964 
965 bool QgsComposerPicture::imageSizeConsideringRotation( double& width, double& height ) const
966 {
967  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
969  return QgsComposerItem::imageSizeConsideringRotation( width, height, mPictureRotation );
971 }
972 
973 bool QgsComposerPicture::cornerPointOnRotatedAndScaledRect( double& x, double& y, double width, double height ) const
974 {
975  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
977  return QgsComposerItem::cornerPointOnRotatedAndScaledRect( x, y, width, height, mPictureRotation );
979 }
980 
981 void QgsComposerPicture::sizeChangedByRotation( double& width, double& height )
982 {
983  //kept for api compatibility with QGIS 2.0 - use mPictureRotation
985  return QgsComposerItem::sizeChangedByRotation( width, height, mPictureRotation );
987 }
QDomNodeList elementsByTagName(const QString &tagname) const
void setActive(bool active)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
qreal x() const
qreal y() const
Q_DECL_DEPRECATED bool imageSizeConsideringRotation(double &width, double &height, double rotation) const
Calculates width and hight of the picture (in mm) such that it fits into the item frame with the give...
void setHeight(int height)
int width() const
void setRenderHint(RenderHint hint, bool on)
QDomNode appendChild(const QDomNode &newChild)
void render(QPainter *painter)
QString attribute(const QString &name, const QString &defValue) const
Q_DECL_DEPRECATED double rotation() const
Returns the rotation used for drawing the picture within the composer item.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void itemChanged()
Emitted when the item changes.
bool writeXML(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom element.
QgsComposerPicture(QgsComposition *composition)
static QString encodeColor(const QColor &color)
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
Q_DECL_DEPRECATED bool imageSizeConsideringRotation(double &width, double &height) const
Calculates width and hight of the picture (in mm) such that it fits into the item frame with the give...
Mode mode() const
Returns the current picture mode (image format).
void setSvgBorderWidth(double width)
Sets the border width used for parameterized SVG files.
bool isValid() const
QMap< QgsComposerObject::DataDefinedProperty, QString > mDataDefinedNames
Map of data defined properties for the item to string name to use when exporting item to xml...
QString fileName() const
A item that forms part of a map composition.
#define Q_NOWARN_DEPRECATED_PUSH
Definition: qgis.h:515
void setFileName(const QString &name)
void save()
int height() const
QString expressionString() const
Returns the expression string of this QgsDataDefined.
virtual void drawFrame(QPainter *p)
Draw black frame around item.
QString picturePath() const
Returns the path of the source image.
QNetworkReply * reply()
Returns a reference to the network reply.
bool exists() const
void rotate(qreal angle)
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
double toDouble(bool *ok) const
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual QgsExpressionContext * createExpressionContext() const override
Creates an expression context relating to the item&#39;s current state.
QString tr(const char *sourceText, const char *disambiguation, int n)
QString readPath(QString filename) const
Turn filename read from the project file to an absolute path.
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
void update(const QRectF &rect)
DataDefinedProperty
Data defined properties for different item types.
A composer class that displays svg files or raster format (jpg, png, ...)
void recalculateSize()
Forces a recalculation of the picture&#39;s frame size.
void setSvgFillColor(const QColor &color)
Sets the fill color used for parameterized SVG files.
void setHeight(qreal height)
void reset(T *other)
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
static QgsSvgCache * instance()
Definition: qgssvgcache.cpp:97
QDomElement toElement() const
QgsDataDefined * dataDefinedProperty(const DataDefinedProperty property) const
Returns a reference to the data defined settings for one of the item&#39;s data defined properties...
bool isEmpty() const
QPointF pos() const
QString number(int n, int base)
void setPictureAnchor(QgsComposerItem::ItemPositionMode anchor)
Sets the picture&#39;s anchor point, which controls how it is placed within the picture item&#39;s frame...
bool isEmpty() const
QVariant property(const char *name) const
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint.
bool load(const QString &filename)
ResizeMode
Controls how pictures are scaled within the item&#39;s frame.
NorthMode
Method for syncing rotation to a map&#39;s North direction.
bool hasAttribute(const QString &name) const
HTTP network content fetcher.
virtual void drawSelectionBoxes(QPainter *p)
Draws additional graphics on selected items.
int printResolution() const
int width() const
void setAttribute(const QString &name, const QString &value)
bool isSelected() const
virtual void setResizeMode(ResizeMode mode)
Sets the resize mode used for drawing the picture within the item bounds.
const QgsCoordinateReferenceSystem & destinationCrs() const
returns CRS of destination coordinate reference system
int toInt(bool *ok, int base) const
void setWidth(int width)
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
bool isEmpty() const
QString trimmed() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void setNorthMode(NorthMode mode)
Sets the mode used to align the picture to a map&#39;s North.
static QRectF largestRotatedRectWithinBounds(const QRectF &originalRect, const QRectF &boundsRect, const double rotation)
Calculates the largest scaled version of originalRect which fits within boundsRect, when it is rotated by a specified amount.
static void logMessage(const QString &message, const QString &tag=QString::null, MessageLevel level=WARNING)
add a message to the instance (and create it if necessary)
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
Refreshes a data defined property for the item by reevaluating the property&#39;s value and redrawing the...
void setNorthOffset(double offset)
Sets the offset added to the picture&#39;s rotation from a map&#39;s North.
QPointF center() const
virtual void setUsePictureExpression(bool useExpression)
Sets whether the picture should use an expression based image source path.
const QByteArray & svgContent(const QString &file, double size, const QColor &fill, const QColor &outline, double outlineWidth, double widthScaleFactor, double rasterScaleFactor)
Get SVG content.
void deleteLater()
bool shouldDrawItem() const
Returns whether the item should be drawn in the current context.
virtual void setPictureExpression(const QString &expression)
Sets an expression to use for the picture source.
double mapRotation(QgsComposerObject::PropertyValueType valueType=QgsComposerObject::EvaluatedValue) const
Returns the rotation used for drawing the map within the composer item.
void setBackgroundEnabled(const bool drawBackground)
Set whether this item has a Background drawn around it or not.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
A class to represent a point.
Definition: qgspoint.h:117
Graphics scene for map printing.
void moveCenter(const QPointF &position)
Object representing map window.
T * data() const
Q_DECL_DEPRECATED QString pictureFile() const
Returns the path of the source image file.
Q_DECL_DEPRECATED bool cornerPointOnRotatedAndScaledRect(double &x, double &y, double width, double height) const
Calculates corner point after rotation and scaling.
bool dataDefinedEvaluate(const QgsComposerObject::DataDefinedProperty property, QVariant &expressionValue, const QgsExpressionContext &context=QgsExpressionContext()) const
Evaluate a data defined property and return the calculated value.
void pictureRotationChanged(double newRotation)
Is emitted on picture rotation change.
bool isNull() const
void restore()
#define Q_NOWARN_DEPRECATED_POP
Definition: qgis.h:516
int id() const
Get identification number.
QTransform & rotate(qreal angle, Qt::Axis axis)
Q_DECL_DEPRECATED void setPictureFile(const QString &path)
Sets the source file of the image (may be svg or a raster format).
QgsComposition * mComposition
QString what() const
Definition: qgsexception.h:36
Q_DECL_DEPRECATED bool cornerPointOnRotatedAndScaledRect(double &x, double &y, double width, double height, double rotation) const
Calculates corner point after rotation and scaling.
int width() const
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, QFlags< Qt::ImageConversionFlag > flags)
qreal width() const
void setSvgBorderColor(const QColor &color)
Sets the border color used for parameterized SVG files.
QString suffix() const
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
void setWidth(qreal width)
virtual void drawBackground(QPainter *p)
Draw background.
QImage read()
Q_DECL_DEPRECATED QString pictureExpression() const
Returns the expression the item is using for the picture source.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:382
Class for storing a coordinate reference system (CRS)
virtual void setSceneRect(const QRectF &rectangle)
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
int height() const
void setDataDefinedProperty(const DataDefinedProperty property, const bool active, const bool useExpression, const QString &expression, const QString &field)
Sets parameters for a data defined property for the item.
void translate(const QPointF &offset)
void setSceneRect(const QRectF &rectangle) override
Sets this items bound in scene coordinates such that 1 item size units corresponds to 1 scene size un...
bool isActive() const
qreal height() const
int height() const
QgsAtlasComposition & atlasComposition()
static QColor decodeColor(const QString &str)
virtual void setRotation(double r) override
Sets the picture rotation within the item bounds.
iterator insert(const Key &key, const T &value)
void setRotationMap(int composerMapId)
Sets the map object for rotation (by id).
void setPicturePath(const QString &path)
Sets the source path of the image (may be svg or a raster format).
QgsComposerItem(QgsComposition *composition, bool manageZValue=true)
Constructor.
virtual void setPictureRotation(double r)
Sets the picture rotation within the item bounds.
QDomElement createElement(const QString &tagName)
qreal height() const
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void fetchContent(const QUrl &url)
Fetches content from a remote URL and handles redirects.
int rotationMap() const
Returns the id of the rotation map.
int compare(const QString &other) const
static double bearingTrueNorth(const QgsCoordinateReferenceSystem &crs, const QgsPoint &point)
Returns the direction to true north from a specified point and for a specified coordinate reference s...
QString toString() const
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:217
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
Defines a qgis exception class.
Definition: qgsexception.h:25
void setExpressionString(const QString &expr)
Sets the expression for this QgsDataDefined.
bool isActive() const
QRect mapRect(const QRect &rectangle) const
qreal width() const
void refreshPicture(const QgsExpressionContext *context=nullptr)
Recalculates the source image (if using an expression for picture&#39;s source) and reloads and redraws t...
Q_DECL_DEPRECATED void sizeChangedByRotation(double &width, double &height, double rotation)
Calculates width / height of the bounding box of a rotated rectangle.
Q_DECL_DEPRECATED bool usePictureExpression() const
Returns whether the picture item is using an expression for the image source.
QDomNode at(int index) const
Q_DECL_DEPRECATED void sizeChangedByRotation(double &width, double &height)
Calculates width / height of the bounding box of a rotated rectangle.
QRectF rect() const