QGIS API Documentation  2.99.0-Master (3450a9f)
qgscomposermapoverview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposermapoverview.cpp
3  --------------------
4  begin : July 2014
5  copyright : (C) 2014 by Nyall Dawson
6  email : nyall dot dawson at gmail dot 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 "qgscomposermapoverview.h"
19 #include "qgscomposermap.h"
20 #include "qgscomposition.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgssymbol.h"
23 #include "qgsmapsettings.h"
24 #include "qgspainting.h"
25 #include "qgscomposerutils.h"
26 #include "qgscsexception.h"
27 
28 #include <QPainter>
29 
31  : QgsComposerMapItem( name, map )
32  , mFrameMapId( -1 )
33  , mFrameSymbol( nullptr )
34  , mBlendMode( QPainter::CompositionMode_SourceOver )
35  , mInverted( false )
36  , mCentered( false )
37 {
38  createDefaultFrameSymbol();
39 }
40 
42  : QgsComposerMapItem( QString(), nullptr )
43  , mFrameMapId( -1 )
44  , mFrameSymbol( nullptr )
45  , mBlendMode( QPainter::CompositionMode_SourceOver )
46  , mInverted( false )
47  , mCentered( false )
48 {
49 }
50 
51 void QgsComposerMapOverview::createDefaultFrameSymbol()
52 {
53  delete mFrameSymbol;
54  QgsStringMap properties;
55  properties.insert( QStringLiteral( "color" ), QStringLiteral( "255,0,0,255" ) );
56  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
57  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
58  mFrameSymbol = QgsFillSymbol::createSimple( properties );
59  mFrameSymbol->setAlpha( 0.3 );
60 }
61 
63 {
64  delete mFrameSymbol;
65 }
66 
67 void QgsComposerMapOverview::draw( QPainter *painter )
68 {
69  if ( !mEnabled || mFrameMapId == -1 || !mComposerMap || !mComposerMap->composition() )
70  {
71  return;
72  }
73  if ( !painter )
74  {
75  return;
76  }
77 
78  const QgsComposerMap *overviewFrameMap = mComposerMap->composition()->getComposerMapById( mFrameMapId );
79  if ( !overviewFrameMap )
80  {
81  return;
82  }
83 
84  //get polygon for other overview frame map's extent (use visibleExtentPolygon as it accounts for map rotation)
85  QPolygonF otherExtent = overviewFrameMap->visibleExtentPolygon();
86  if ( overviewFrameMap->crs() !=
87  mComposerMap->crs() )
88  {
89  QgsGeometry g = QgsGeometry::fromQPolygonF( otherExtent );
90 
91  // reproject extent
92  QgsCoordinateTransform ct( overviewFrameMap->crs(),
93  mComposerMap->crs() );
94  g = g.densifyByCount( 20 );
95  try
96  {
97  g.transform( ct );
98  }
99  catch ( QgsCsException & )
100  {
101  }
102 
103  otherExtent = g.asQPolygonF();
104  }
105 
106  //get current map's extent as a QPolygonF
107  QPolygonF thisExtent = mComposerMap->visibleExtentPolygon();
108  //intersect the two
109  QPolygonF intersectExtent = thisExtent.intersected( otherExtent );
110 
111  //setup painter scaling to dots so that raster symbology is drawn to scale
112  double dotsPerMM = painter->device()->logicalDpiX() / 25.4;
113 
114  //setup render context
116  context.setForceVectorOutput( true );
117  QgsExpressionContext expressionContext = createExpressionContext();
118  context.setExpressionContext( expressionContext );
119 
120  painter->save();
121  painter->setCompositionMode( mBlendMode );
122  painter->translate( mComposerMap->mXOffset, mComposerMap->mYOffset );
123  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
124  painter->setRenderHint( QPainter::Antialiasing );
125 
126  mFrameSymbol->startRender( context );
127 
128  //construct a polygon corresponding to the intersecting map extent
129  //need to scale line to dots, rather then mm, since the painter has been scaled to dots
130  QTransform mapTransform;
131  QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, dotsPerMM * mComposerMap->rect().width(), dotsPerMM * mComposerMap->rect().height() ) );
132 
133  //workaround QT Bug #21329
134  thisRectPoly.pop_back();
135  thisExtent.pop_back();
136 
137  //create transform from map coordinates to painter coordinates
138  QTransform::quadToQuad( thisExtent, thisRectPoly, mapTransform );
139  QPolygonF intersectPolygon;
140  intersectPolygon = mapTransform.map( intersectExtent );
141 
142  QList<QPolygonF> rings; //empty list
143  if ( !mInverted )
144  {
145  //Render the intersecting map extent
146  mFrameSymbol->renderPolygon( intersectPolygon, &rings, nullptr, context );
147  }
148  else
149  {
150  //We are inverting the overview frame (ie, shading outside the intersecting extent)
151  //Construct a polygon corresponding to the overview map extent
152  QPolygonF outerPolygon;
153  outerPolygon << QPointF( 0, 0 )
154  << QPointF( mComposerMap->rect().width() * dotsPerMM, 0 )
155  << QPointF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM )
156  << QPointF( 0, mComposerMap->rect().height() * dotsPerMM )
157  << QPointF( 0, 0 );
158 
159  //Intersecting extent is an inner ring for the shaded area
160  rings.append( intersectPolygon );
161  mFrameSymbol->renderPolygon( outerPolygon, &rings, nullptr, context );
162  }
163 
164  mFrameSymbol->stopRender( context );
165  painter->restore();
166 }
167 
168 bool QgsComposerMapOverview::writeXml( QDomElement &elem, QDomDocument &doc ) const
169 {
170  if ( elem.isNull() )
171  {
172  return false;
173  }
174 
175  //overview map frame
176  QDomElement overviewFrameElem = doc.createElement( QStringLiteral( "ComposerMapOverview" ) );
177 
178  overviewFrameElem.setAttribute( QStringLiteral( "frameMap" ), mFrameMapId );
179  overviewFrameElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
180  overviewFrameElem.setAttribute( QStringLiteral( "inverted" ), mInverted );
181  overviewFrameElem.setAttribute( QStringLiteral( "centered" ), mCentered );
182 
183  QDomElement frameStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mFrameSymbol, doc );
184  overviewFrameElem.appendChild( frameStyleElem );
185 
186  bool ok = QgsComposerMapItem::writeXml( overviewFrameElem, doc );
187  elem.appendChild( overviewFrameElem );
188  return ok;
189 }
190 
191 bool QgsComposerMapOverview::readXml( const QDomElement &itemElem, const QDomDocument &doc )
192 {
193  Q_UNUSED( doc );
194  if ( itemElem.isNull() )
195  {
196  return false;
197  }
198 
199  bool ok = QgsComposerMapItem::readXml( itemElem, doc );
200 
201  setFrameMap( itemElem.attribute( QStringLiteral( "frameMap" ), QStringLiteral( "-1" ) ).toInt() );
202  mBlendMode = QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) );
203  mInverted = ( itemElem.attribute( QStringLiteral( "inverted" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
204  mCentered = ( itemElem.attribute( QStringLiteral( "centered" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
205 
206  QDomElement frameStyleElem = itemElem.firstChildElement( QStringLiteral( "symbol" ) );
207  if ( !frameStyleElem.isNull() )
208  {
209  delete mFrameSymbol;
210  mFrameSymbol = QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( frameStyleElem );
211  }
212  return ok;
213 }
214 
216 {
217  return mBlendMode != QPainter::CompositionMode_SourceOver;
218 }
219 
220 void QgsComposerMapOverview::setFrameMap( const int mapId )
221 {
222  if ( mFrameMapId == mapId )
223  {
224  //no change
225  return;
226  }
227 
228  //disconnect old map
229  if ( mFrameMapId != -1 && mComposerMap && mComposerMap->composition() )
230  {
231  const QgsComposerMap *map = mComposerMap->composition()->getComposerMapById( mFrameMapId );
232  if ( map )
233  {
235  }
236  }
237  mFrameMapId = mapId;
238  //connect to new map signals
239  connectSignals();
240 }
241 
243 {
244  if ( !mComposerMap )
245  {
246  return;
247  }
248 
249  if ( mFrameMapId != -1 && mComposerMap->composition() )
250  {
251  const QgsComposerMap *map = mComposerMap->composition()->getComposerMapById( mFrameMapId );
252  if ( map )
253  {
255  }
256  }
257 }
258 
260 {
261  delete mFrameSymbol;
262  mFrameSymbol = symbol;
263 }
264 
265 void QgsComposerMapOverview::setBlendMode( const QPainter::CompositionMode blendMode )
266 {
267  mBlendMode = blendMode;
268 }
269 
271 {
272  mInverted = inverted;
273 }
274 
276 {
277  mCentered = centered;
279 }
280 
282 {
283  if ( !mComposerMap )
284  {
285  return;
286  }
287 
288  //if using overview centering, update the map's extent
289  if ( mComposerMap->composition() && mCentered && mFrameMapId != -1 )
290  {
292 
293  const QgsComposerMap *overviewFrameMap = mComposerMap->composition()->getComposerMapById( mFrameMapId );
294  if ( !overviewFrameMap )
295  {
296  //redraw map so that overview gets updated
297  mComposerMap->update();
298  return;
299  }
300  QgsRectangle otherExtent = *overviewFrameMap->currentMapExtent();
301 
302  QgsPoint center = otherExtent.center();
303  QgsRectangle movedExtent( center.x() - extent.width() / 2,
304  center.y() - extent.height() / 2,
305  center.x() - extent.width() / 2 + extent.width(),
306  center.y() - extent.height() / 2 + extent.height() );
307  *mComposerMap->currentMapExtent() = movedExtent;
308 
309  //trigger a recalculation of data defined extents, scale and rotation, since that
310  //may override the map centering
312 
313  //must invalidate cache so that map gets redrawn
314  mComposerMap->cache();
315  }
316 
317  //repaint map so that overview gets updated
318  mComposerMap->update();
319 }
320 
321 
322 //
323 // QgsComposerMapOverviewStack
324 //
325 
327  : QgsComposerMapItemStack( map )
328 {
329 
330 }
331 
333 {
335 }
336 
337 void QgsComposerMapOverviewStack::removeOverview( const QString &overviewId )
338 {
340 }
341 
342 void QgsComposerMapOverviewStack::moveOverviewUp( const QString &overviewId )
343 {
345 }
346 
347 void QgsComposerMapOverviewStack::moveOverviewDown( const QString &overviewId )
348 {
350 }
351 
353 {
355  return dynamic_cast<const QgsComposerMapOverview *>( item );
356 }
357 
359 {
361  return dynamic_cast<QgsComposerMapOverview *>( item );
362 }
363 
365 {
367  return dynamic_cast<QgsComposerMapOverview *>( item );
368 }
369 
371 {
372  QgsComposerMapItem *item = mItems.at( idx );
374  return *overview;
375 }
376 
377 QList<QgsComposerMapOverview *> QgsComposerMapOverviewStack::asList() const
378 {
379  QList< QgsComposerMapOverview * > list;
380  QList< QgsComposerMapItem * >::const_iterator it = mItems.begin();
381  for ( ; it != mItems.end(); ++it )
382  {
383  QgsComposerMapOverview *overview = dynamic_cast<QgsComposerMapOverview *>( *it );
384  if ( overview )
385  {
386  list.append( overview );
387  }
388  }
389  return list;
390 }
391 
392 bool QgsComposerMapOverviewStack::readXml( const QDomElement &elem, const QDomDocument &doc )
393 {
394  removeItems();
395 
396  //read overview stack
397  QDomNodeList mapOverviewNodeList = elem.elementsByTagName( QStringLiteral( "ComposerMapOverview" ) );
398  for ( int i = 0; i < mapOverviewNodeList.size(); ++i )
399  {
400  QDomElement mapOverviewElem = mapOverviewNodeList.at( i ).toElement();
401  QgsComposerMapOverview *mapOverview = new QgsComposerMapOverview( mapOverviewElem.attribute( QStringLiteral( "name" ) ), mComposerMap );
402  mapOverview->readXml( mapOverviewElem, doc );
403  mItems.append( mapOverview );
404  }
405 
406  return true;
407 }
QgsComposerMapItem * item(const QString &itemId) const
Returns a reference to an item within the stack.
void connectSignals()
Reconnects signals for overview map, so that overview correctly follows changes to source map&#39;s exten...
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
void setForceVectorOutput(bool force)
A rectangle specified with double values.
Definition: qgsrectangle.h:38
double y
Definition: qgspoint.h:42
QgsPoint center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:146
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets overview state from a DOM document.
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
virtual bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets map item state from a DOM document.
virtual QgsExpressionContext createExpressionContext() const
Creates an expression context relating to the objects&#39; current state.
void addItem(QgsComposerMapItem *item)
Adds a new map item to the stack and takes ownership of the item.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc)
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used for drawing the overview extent.
void setAlpha(qreal alpha)
Set alpha transparency 1 for opaque, 0 for invisible.
Definition: qgssymbol.h:231
void addOverview(QgsComposerMapOverview *overview)
Adds a new map overview to the stack and takes ownership of the overview.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation...
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1052
QgsComposerMapItem(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapItem.
void cache()
Create cache image.
void moveOverviewDown(const QString &overviewId)
Moves an overview down the stack, causing it to be rendered below other overviews.
QList< QgsComposerMapItem *> mItems
static BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:66
virtual bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores map item state in DOM element.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:79
const QgsComposerMapOverview * constOverview(const QString &overviewId) const
Returns a const reference to an overview within the stack.
static QPainter::CompositionMode getCompositionMode(BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:6
QMap< QString, QString > QgsStringMap
Definition: qgis.h:344
An item which is drawn inside a QgsComposerMap, e.g., a grid or map overview.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:378
bool readXml(const QDomElement &elem, const QDomDocument &doc) override
Sets the overview stack&#39;s state from a DOM document.
An individual overview which is drawn above the map content in a QgsComposerMap, and shows the extent...
bool mEnabled
True if item is to be displayed on map.
void renderPolygon(const QPolygonF &points, QList< QPolygonF > *rings, const QgsFeature *f, QgsRenderContext &context, int layer=-1, bool selected=false)
Definition: qgssymbol.cpp:1710
QgsComposerMapOverview & operator[](int idx)
Returns a reference to an overview within the stack.
void removeItem(const QString &itemId)
Removes an item from the stack and deletes the corresponding QgsComposerMapItem.
const QgsComposerMapItem * constItem(const QString &itemId) const
Returns a const reference to an item within the stack.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:118
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void moveItemUp(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
void setCentered(const bool centered)
Sets whether the extent of the map is forced to center on the overview.
void removeOverview(const QString &overviewId)
Removes an overview from the stack and deletes the corresponding QgsComposerMapOverview.
QgsComposerMapOverview(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapOverview.
QgsComposerMap * mComposerMap
Associated composer map.
A class to represent a point.
Definition: qgspoint.h:37
bool usesAdvancedEffects() const override
Returns true if the item is drawn using advanced effects, such as blend modes.
Object representing map window.
void moveItemDown(const QString &itemId)
Moves an item up the stack, causing it to be rendered above other items.
void draw(QPainter *painter) override
Draws an overview.
QgsComposerMapOverview * overview(const QString &overviewId) const
Returns a reference to an overview within the stack.
QgsComposition * mComposition
Contains information about the context of a rendering operation.
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
void overviewExtentChanged()
Handles recentering of the map and redrawing of the map&#39;s overview.
const QgsComposition * composition() const
Returns the composition the item is attached to.
void moveOverviewUp(const QString &overviewId)
Moves an overview up the stack, causing it to be rendered above other overviews.
QList< QgsComposerMapOverview *> asList() const
Returns a list of QgsComposerMapOverviews contained by the stack.
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
A collection of map items which are drawn above the map content in a QgsComposerMap.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
Class for doing transforms between two map coordinate systems.
void setInverted(const bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
int transform(const QgsCoordinateTransform &ct)
Transform this geometry as described by CoordinateTransform ct.
void setFrameMap(const int mapId)
Sets overview frame map.
Custom exception class for Coordinate Reference System related exceptions.
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores overview state in DOM element.
void extentChanged()
void removeItems()
Clears the item stack and deletes all QgsComposerMapItems contained by the stack. ...
QgsComposerMapOverviewStack(QgsComposerMap *map)
Constructor for QgsComposerMapOverviewStack.
static QgsRenderContext createRenderContextForComposition(QgsComposition *composition, QPainter *painter)
Creates a render context suitable for the specified composition and painter destination.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:399
const QgsRectangle * currentMapExtent() const
Returns a pointer to the current map extent, which is either the original user specified extent or th...
void setBlendMode(const QPainter::CompositionMode blendMode)
Sets the blending mode used for drawing the overview.
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:125
QgsComposerMap * mComposerMap
double x
Definition: qgspoint.h:41