QGIS API Documentation  2.99.0-Master (c558d51)
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 
26 #include <QPainter>
27 
29  : QgsComposerMapItem( name, map )
30  , mFrameMapId( -1 )
31  , mFrameSymbol( nullptr )
32  , mBlendMode( QPainter::CompositionMode_SourceOver )
33  , mInverted( false )
34  , mCentered( false )
35 {
36  createDefaultFrameSymbol();
37 }
38 
40  : QgsComposerMapItem( QString(), nullptr )
41  , mFrameMapId( -1 )
42  , mFrameSymbol( nullptr )
43  , mBlendMode( QPainter::CompositionMode_SourceOver )
44  , mInverted( false )
45  , mCentered( false )
46 {
47 }
48 
49 void QgsComposerMapOverview::createDefaultFrameSymbol()
50 {
51  delete mFrameSymbol;
52  QgsStringMap properties;
53  properties.insert( QStringLiteral( "color" ), QStringLiteral( "255,0,0,255" ) );
54  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
55  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
56  mFrameSymbol = QgsFillSymbol::createSimple( properties );
57  mFrameSymbol->setAlpha( 0.3 );
58 }
59 
61 {
62  delete mFrameSymbol;
63 }
64 
65 void QgsComposerMapOverview::draw( QPainter *painter )
66 {
67  if ( !mEnabled || mFrameMapId == -1 || !mComposerMap || !mComposerMap->composition() )
68  {
69  return;
70  }
71  if ( !painter )
72  {
73  return;
74  }
75 
76  const QgsComposerMap* overviewFrameMap = mComposerMap->composition()->getComposerMapById( mFrameMapId );
77  if ( !overviewFrameMap )
78  {
79  return;
80  }
81 
82  //get polygon for other overview frame map's extent (use visibleExtentPolygon as it accounts for map rotation)
83  QPolygonF otherExtent = overviewFrameMap->visibleExtentPolygon();
84 
85  //get current map's extent as a QPolygonF
86  QPolygonF thisExtent = mComposerMap->visibleExtentPolygon();
87  //intersect the two
88  QPolygonF intersectExtent = thisExtent.intersected( otherExtent );
89 
90  //setup painter scaling to dots so that raster symbology is drawn to scale
91  double dotsPerMM = painter->device()->logicalDpiX() / 25.4;
92 
93  //setup render context
95  //context units should be in dots
96  ms.setOutputSize( QSizeF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM ).toSize() );
98  ms.setOutputDpi( painter->device()->logicalDpiX() );
100  context.setForceVectorOutput( true );
101  context.setPainter( painter );
102  QgsExpressionContext expressionContext = createExpressionContext();
103  context.setExpressionContext( expressionContext );
104 
105  painter->save();
106  painter->setCompositionMode( mBlendMode );
107  painter->translate( mComposerMap->mXOffset, mComposerMap->mYOffset );
108  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
109  painter->setRenderHint( QPainter::Antialiasing );
110 
111  mFrameSymbol->startRender( context );
112 
113  //construct a polygon corresponding to the intersecting map extent
114  //need to scale line to dots, rather then mm, since the painter has been scaled to dots
115  QTransform mapTransform;
116  QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, dotsPerMM * mComposerMap->rect().width(), dotsPerMM * mComposerMap->rect().height() ) );
117 
118  //workaround QT Bug #21329
119  thisRectPoly.pop_back();
120  thisExtent.pop_back();
121 
122  //create transform from map coordinates to painter coordinates
123  QTransform::quadToQuad( thisExtent, thisRectPoly, mapTransform );
124  QPolygonF intersectPolygon;
125  intersectPolygon = mapTransform.map( intersectExtent );
126 
127  QList<QPolygonF> rings; //empty list
128  if ( !mInverted )
129  {
130  //Render the intersecting map extent
131  mFrameSymbol->renderPolygon( intersectPolygon, &rings, nullptr, context );
132  }
133  else
134  {
135  //We are inverting the overview frame (ie, shading outside the intersecting extent)
136  //Construct a polygon corresponding to the overview map extent
137  QPolygonF outerPolygon;
138  outerPolygon << QPointF( 0, 0 )
139  << QPointF( mComposerMap->rect().width() * dotsPerMM, 0 )
140  << QPointF( mComposerMap->rect().width() * dotsPerMM, mComposerMap->rect().height() * dotsPerMM )
141  << QPointF( 0, mComposerMap->rect().height() * dotsPerMM )
142  << QPointF( 0, 0 );
143 
144  //Intersecting extent is an inner ring for the shaded area
145  rings.append( intersectPolygon );
146  mFrameSymbol->renderPolygon( outerPolygon, &rings, nullptr, context );
147  }
148 
149  mFrameSymbol->stopRender( context );
150  painter->restore();
151 }
152 
153 bool QgsComposerMapOverview::writeXml( QDomElement &elem, QDomDocument &doc ) const
154 {
155  if ( elem.isNull() )
156  {
157  return false;
158  }
159 
160  //overview map frame
161  QDomElement overviewFrameElem = doc.createElement( QStringLiteral( "ComposerMapOverview" ) );
162 
163  overviewFrameElem.setAttribute( QStringLiteral( "frameMap" ), mFrameMapId );
164  overviewFrameElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
165  overviewFrameElem.setAttribute( QStringLiteral( "inverted" ), mInverted );
166  overviewFrameElem.setAttribute( QStringLiteral( "centered" ), mCentered );
167 
168  QDomElement frameStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mFrameSymbol, doc );
169  overviewFrameElem.appendChild( frameStyleElem );
170 
171  bool ok = QgsComposerMapItem::writeXml( overviewFrameElem, doc );
172  elem.appendChild( overviewFrameElem );
173  return ok;
174 }
175 
176 bool QgsComposerMapOverview::readXml( const QDomElement &itemElem, const QDomDocument &doc )
177 {
178  Q_UNUSED( doc );
179  if ( itemElem.isNull() )
180  {
181  return false;
182  }
183 
184  bool ok = QgsComposerMapItem::readXml( itemElem, doc );
185 
186  setFrameMap( itemElem.attribute( QStringLiteral( "frameMap" ), QStringLiteral( "-1" ) ).toInt() );
187  mBlendMode = QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) );
188  mInverted = ( itemElem.attribute( QStringLiteral( "inverted" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
189  mCentered = ( itemElem.attribute( QStringLiteral( "centered" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
190 
191  QDomElement frameStyleElem = itemElem.firstChildElement( QStringLiteral( "symbol" ) );
192  if ( !frameStyleElem.isNull() )
193  {
194  delete mFrameSymbol;
195  mFrameSymbol = QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( frameStyleElem );
196  }
197  return ok;
198 }
199 
201 {
202  return mBlendMode != QPainter::CompositionMode_SourceOver;
203 }
204 
205 void QgsComposerMapOverview::setFrameMap( const int mapId )
206 {
207  if ( mFrameMapId == mapId )
208  {
209  //no change
210  return;
211  }
212 
213  //disconnect old map
214  if ( mFrameMapId != -1 && mComposerMap && mComposerMap->composition() )
215  {
216  const QgsComposerMap* map = mComposerMap->composition()->getComposerMapById( mFrameMapId );
217  if ( map )
218  {
219  QObject::disconnect( map, SIGNAL( extentChanged() ), this, SLOT( overviewExtentChanged() ) );
220  }
221  }
222  mFrameMapId = mapId;
223  //connect to new map signals
224  connectSignals();
225 }
226 
228 {
229  if ( !mComposerMap )
230  {
231  return;
232  }
233 
234  if ( mFrameMapId != -1 && mComposerMap->composition() )
235  {
236  const QgsComposerMap* map = mComposerMap->composition()->getComposerMapById( mFrameMapId );
237  if ( map )
238  {
239  QObject::connect( map, SIGNAL( extentChanged() ), this, SLOT( overviewExtentChanged() ) );
240  }
241  }
242 }
243 
245 {
246  delete mFrameSymbol;
247  mFrameSymbol = symbol;
248 }
249 
250 void QgsComposerMapOverview::setBlendMode( const QPainter::CompositionMode blendMode )
251 {
252  mBlendMode = blendMode;
253 }
254 
256 {
257  mInverted = inverted;
258 }
259 
261 {
262  mCentered = centered;
264 }
265 
267 {
268  if ( !mComposerMap )
269  {
270  return;
271  }
272 
273  //if using overview centering, update the map's extent
274  if ( mComposerMap->composition() && mCentered && mFrameMapId != -1 )
275  {
277 
278  const QgsComposerMap* overviewFrameMap = mComposerMap->composition()->getComposerMapById( mFrameMapId );
279  if ( !overviewFrameMap )
280  {
281  //redraw map so that overview gets updated
282  mComposerMap->update();
283  return;
284  }
285  QgsRectangle otherExtent = *overviewFrameMap->currentMapExtent();
286 
287  QgsPoint center = otherExtent.center();
288  QgsRectangle movedExtent( center.x() - extent.width() / 2,
289  center.y() - extent.height() / 2,
290  center.x() - extent.width() / 2 + extent.width(),
291  center.y() - extent.height() / 2 + extent.height() );
292  *mComposerMap->currentMapExtent() = movedExtent;
293 
294  //trigger a recalculation of data defined extents, scale and rotation, since that
295  //may override the map centering
297 
298  //must invalidate cache so that map gets redrawn
299  mComposerMap->cache();
300  }
301 
302  //repaint map so that overview gets updated
303  mComposerMap->update();
304 }
305 
306 
307 //
308 // QgsComposerMapOverviewStack
309 //
310 
312  : QgsComposerMapItemStack( map )
313 {
314 
315 }
316 
318 {
319 
320 }
321 
323 {
325 }
326 
327 void QgsComposerMapOverviewStack::removeOverview( const QString &overviewId )
328 {
330 }
331 
332 void QgsComposerMapOverviewStack::moveOverviewUp( const QString &overviewId )
333 {
335 }
336 
337 void QgsComposerMapOverviewStack::moveOverviewDown( const QString &overviewId )
338 {
340 }
341 
343 {
345  return dynamic_cast<const QgsComposerMapOverview*>( item );
346 }
347 
349 {
351  return dynamic_cast<QgsComposerMapOverview*>( item );
352 }
353 
355 {
357  return dynamic_cast<QgsComposerMapOverview*>( item );
358 }
359 
361 {
362  QgsComposerMapItem* item = mItems.at( idx );
364  return *overview;
365 }
366 
367 QList<QgsComposerMapOverview *> QgsComposerMapOverviewStack::asList() const
368 {
369  QList< QgsComposerMapOverview* > list;
370  QList< QgsComposerMapItem* >::const_iterator it = mItems.begin();
371  for ( ; it != mItems.end(); ++it )
372  {
373  QgsComposerMapOverview* overview = dynamic_cast<QgsComposerMapOverview*>( *it );
374  if ( overview )
375  {
376  list.append( overview );
377  }
378  }
379  return list;
380 }
381 
382 bool QgsComposerMapOverviewStack::readXml( const QDomElement &elem, const QDomDocument &doc )
383 {
384  removeItems();
385 
386  //read overview stack
387  QDomNodeList mapOverviewNodeList = elem.elementsByTagName( QStringLiteral( "ComposerMapOverview" ) );
388  for ( int i = 0; i < mapOverviewNodeList.size(); ++i )
389  {
390  QDomElement mapOverviewElem = mapOverviewNodeList.at( i ).toElement();
391  QgsComposerMapOverview* mapOverview = new QgsComposerMapOverview( mapOverviewElem.attribute( QStringLiteral( "name" ) ), mComposerMap );
392  mapOverview->readXml( mapOverviewElem, doc );
393  mItems.append( mapOverview );
394  }
395 
396  return true;
397 }
void connectSignals()
Reconnects signals for overview map, so that overview correctly follows changes to source map&#39;s exten...
void setForceVectorOutput(bool force)
static unsigned index
A rectangle specified with double values.
Definition: qgsrectangle.h:35
void setExtent(const QgsRectangle &rect, bool magnified=true)
Set coordinates of the rectangle which should be rendered.
double y
Definition: qgspoint.h:116
bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets overview state from a DOM document.
virtual bool readXml(const QDomElement &itemElem, const QDomDocument &doc) override
Sets map item state from a DOM document.
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:228
void addOverview(QgsComposerMapOverview *overview)
Adds a new map overview to the stack and takes ownership of the overview.
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1066
QgsComposerMapItem(const QString &name, QgsComposerMap *map)
Constructor for QgsComposerMapItem.
void cache()
Create cache image.
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
void moveOverviewDown(const QString &overviewId)
Moves an overview down the stack, causing it to be rendered below other overviews.
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.
void setOutputDpi(double dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
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:328
An item which is drawn inside a QgsComposerMap, eg a grid or map overview.
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:387
The QgsMapSettings class contains configuration for rendering of the map.
const QgsComposerMapItem * constItem(const QString &itemId) const
Returns a const reference to an item within the stack.
bool readXml(const QDomElement &elem, const QDomDocument &doc) override
Sets the overview stack&#39;s state from a DOM document.
void setOutputSize(QSize size)
Set the size of the resulting map image.
An individual overview which is drawn above the map content in a QgsComposerMap, and shows the extent...
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
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:1732
QgsComposerMapOverview & operator[](int idx)
Returns a reference to an overview within the stack.
const QgsComposition * composition() const
Returns the composition the item is attached to.
void removeItem(const QString &itemId)
Removes an item from the stack and deletes the corresponding QgsComposerMapItem.
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 setPainter(QPainter *p)
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.
virtual QgsExpressionContext createExpressionContext() const
Creates an expression context relating to the objects&#39; current state.
QgsComposerMap * mComposerMap
Associated composer map.
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation...
const QgsComposerMapOverview * constOverview(const QString &overviewId) const
Returns a const reference to an overview within the stack.
A class to represent a point.
Definition: qgspoint.h:111
QgsComposerMapItem * item(const QString &itemId) const
Returns a reference to an item within the stack.
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.
QList< QgsComposerMapItem * > mItems
Contains information about the context of a rendering operation.
virtual void refreshDataDefinedProperty(const QgsComposerObject::DataDefinedProperty property=QgsComposerObject::AllProperties, const QgsExpressionContext *context=nullptr) override
QgsComposerMapOverview * overview(const QString &overviewId) const
Returns a reference to an overview within the stack.
void overviewExtentChanged()
Handles recentering of the map and redrawing of the map&#39;s overview.
void moveOverviewUp(const QString &overviewId)
Moves an overview up the stack, causing it to be rendered above other overviews.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
A collection of map items which are drawn above the map content in a QgsComposerMap.
QList< QgsComposerMapOverview * > asList() const
Returns a list of QgsComposerMapOverviews contained by the stack.
void setInverted(const bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
void setFrameMap(const int mapId)
Sets overview frame map.
bool writeXml(QDomElement &elem, QDomDocument &doc) const override
Stores overview state in DOM element.
void removeItems()
Clears the item stack and deletes all QgsComposerMapItems contained by the stack. ...
const QgsComposerMap * getComposerMapById(const int id) const
Returns the composer map with specified id.
double width() const
Width of the rectangle.
Definition: qgsrectangle.h:211
QgsComposerMapOverviewStack(QgsComposerMap *map)
Constructor for QgsComposerMapOverviewStack.
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:408
QgsPoint center() const
Center point of the rectangle.
Definition: qgsrectangle.h:221
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.
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
double height() const
Height of the rectangle.
Definition: qgsrectangle.h:216
QgsComposerMap * mComposerMap
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
double x
Definition: qgspoint.h:115