QGIS API Documentation  3.4.15-Madeira (e83d02e274)
qgslayoutitemmapoverview.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgslayoutitemmapoverview.cpp
3  --------------------
4  begin : October 2017
5  copyright : (C) 2017 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 
19 #include "qgslayoutitemmap.h"
20 #include "qgslayout.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgssymbol.h"
23 #include "qgsmapsettings.h"
24 #include "qgspainting.h"
25 #include "qgspathresolver.h"
26 #include "qgsreadwritecontext.h"
27 #include "qgslayoututils.h"
28 #include "qgsexception.h"
29 
30 #include <QPainter>
31 
33  : QgsLayoutItemMapItem( name, map )
34 {
35  createDefaultFrameSymbol();
36 }
37 
38 void QgsLayoutItemMapOverview::createDefaultFrameSymbol()
39 {
40  QgsStringMap properties;
41  properties.insert( QStringLiteral( "color" ), QStringLiteral( "255,0,0,75" ) );
42  properties.insert( QStringLiteral( "style" ), QStringLiteral( "solid" ) );
43  properties.insert( QStringLiteral( "style_border" ), QStringLiteral( "no" ) );
44  mFrameSymbol.reset( QgsFillSymbol::createSimple( properties ) );
45 }
46 
47 void QgsLayoutItemMapOverview::draw( QPainter *painter )
48 {
49  if ( !mEnabled || !mFrameMap || !mMap || !mMap->layout() )
50  {
51  return;
52  }
53  if ( !painter )
54  {
55  return;
56  }
57 
58  const QgsLayoutItemMap *overviewFrameMap = linkedMap();
59  if ( !overviewFrameMap )
60  {
61  return;
62  }
63 
64  //get polygon for other overview frame map's extent (use visibleExtentPolygon as it accounts for map rotation)
65  QPolygonF otherExtent = overviewFrameMap->visibleExtentPolygon();
66  if ( overviewFrameMap->crs() !=
67  mMap->crs() )
68  {
69  QgsGeometry g = QgsGeometry::fromQPolygonF( otherExtent );
70 
71  // reproject extent
72  QgsCoordinateTransform ct( overviewFrameMap->crs(),
73  mMap->crs(), mLayout->project() );
74  g = g.densifyByCount( 20 );
75  try
76  {
77  g.transform( ct );
78  }
79  catch ( QgsCsException & )
80  {
81  }
82 
83  otherExtent = g.asQPolygonF();
84  }
85 
86  //get current map's extent as a QPolygonF
87  QPolygonF thisExtent = mMap->visibleExtentPolygon();
88  //intersect the two
89  QPolygonF intersectExtent = thisExtent.intersected( otherExtent );
90 
91  //setup painter scaling to dots so that raster symbology is drawn to scale
92  double dotsPerMM = painter->device()->logicalDpiX() / 25.4;
93 
94  //setup render context
96  context.setForceVectorOutput( true );
97  QgsExpressionContext expressionContext = createExpressionContext();
98  context.setExpressionContext( expressionContext );
99 
100  painter->save();
101  painter->setCompositionMode( mBlendMode );
102  painter->translate( mMap->mXOffset, mMap->mYOffset );
103  painter->scale( 1 / dotsPerMM, 1 / dotsPerMM ); // scale painter from mm to dots
104  painter->setRenderHint( QPainter::Antialiasing );
105 
106  mFrameSymbol->startRender( context );
107 
108  //construct a polygon corresponding to the intersecting map extent
109  //need to scale line to dots, rather then mm, since the painter has been scaled to dots
110  QTransform mapTransform;
111  QPolygonF thisRectPoly = QPolygonF( QRectF( 0, 0, dotsPerMM * mMap->rect().width(), dotsPerMM * mMap->rect().height() ) );
112 
113  //workaround QT Bug #21329
114  thisRectPoly.pop_back();
115  thisExtent.pop_back();
116 
117  //create transform from map coordinates to painter coordinates
118  QTransform::quadToQuad( thisExtent, thisRectPoly, mapTransform );
119  QPolygonF intersectPolygon;
120  intersectPolygon = mapTransform.map( intersectExtent );
121 
122  QList<QPolygonF> rings; //empty list
123  if ( !mInverted )
124  {
125  //Render the intersecting map extent
126  mFrameSymbol->renderPolygon( intersectPolygon, &rings, nullptr, context );
127  }
128  else
129  {
130  //We are inverting the overview frame (ie, shading outside the intersecting extent)
131  //Construct a polygon corresponding to the overview map extent
132  QPolygonF outerPolygon;
133  outerPolygon << QPointF( 0, 0 )
134  << QPointF( mMap->rect().width() * dotsPerMM, 0 )
135  << QPointF( mMap->rect().width() * dotsPerMM, mMap->rect().height() * dotsPerMM )
136  << QPointF( 0, mMap->rect().height() * dotsPerMM )
137  << QPointF( 0, 0 );
138 
139  //Intersecting extent is an inner ring for the shaded area
140  rings.append( intersectPolygon );
141  mFrameSymbol->renderPolygon( outerPolygon, &rings, nullptr, context );
142  }
143 
144  mFrameSymbol->stopRender( context );
145  painter->restore();
146 }
147 
148 bool QgsLayoutItemMapOverview::writeXml( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
149 {
150  if ( elem.isNull() )
151  {
152  return false;
153  }
154 
155  //overview map frame
156  QDomElement overviewFrameElem = doc.createElement( QStringLiteral( "ComposerMapOverview" ) );
157 
158  overviewFrameElem.setAttribute( QStringLiteral( "frameMap" ), mFrameMap ? mFrameMap ->uuid() : QString() );
159  overviewFrameElem.setAttribute( QStringLiteral( "blendMode" ), QgsPainting::getBlendModeEnum( mBlendMode ) );
160  overviewFrameElem.setAttribute( QStringLiteral( "inverted" ), mInverted );
161  overviewFrameElem.setAttribute( QStringLiteral( "centered" ), mCentered );
162 
163  QDomElement frameStyleElem = QgsSymbolLayerUtils::saveSymbol( QString(), mFrameSymbol.get(), doc, context );
164  overviewFrameElem.appendChild( frameStyleElem );
165 
166  bool ok = QgsLayoutItemMapItem::writeXml( overviewFrameElem, doc, context );
167  elem.appendChild( overviewFrameElem );
168  return ok;
169 }
170 
171 bool QgsLayoutItemMapOverview::readXml( const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context )
172 {
173  Q_UNUSED( doc );
174  if ( itemElem.isNull() )
175  {
176  return false;
177  }
178 
179  bool ok = QgsLayoutItemMapItem::readXml( itemElem, doc, context );
180 
181  mFrameMapUuid = itemElem.attribute( QStringLiteral( "frameMap" ) );
182  setLinkedMap( nullptr );
183 
184  mBlendMode = QgsPainting::getCompositionMode( static_cast< QgsPainting::BlendMode >( itemElem.attribute( QStringLiteral( "blendMode" ), QStringLiteral( "0" ) ).toUInt() ) );
185  mInverted = ( itemElem.attribute( QStringLiteral( "inverted" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
186  mCentered = ( itemElem.attribute( QStringLiteral( "centered" ), QStringLiteral( "0" ) ) != QLatin1String( "0" ) );
187 
188  QDomElement frameStyleElem = itemElem.firstChildElement( QStringLiteral( "symbol" ) );
189  if ( !frameStyleElem.isNull() )
190  {
191  mFrameSymbol.reset( QgsSymbolLayerUtils::loadSymbol<QgsFillSymbol>( frameStyleElem, context ) );
192  }
193  return ok;
194 }
195 
197 {
198  if ( !mFrameMapUuid.isEmpty() )
199  {
200  setLinkedMap( qobject_cast< QgsLayoutItemMap * >( mLayout->itemByUuid( mFrameMapUuid, true ) ) );
201  }
202 }
203 
205 {
206  return mBlendMode != QPainter::CompositionMode_SourceOver;
207 }
208 
210 {
211  if ( mFrameMap == map )
212  {
213  //no change
214  return;
215  }
216 
217  //disconnect old map
218  if ( mFrameMap )
219  {
222  }
223  mFrameMap = map;
224  //connect to new map signals
225  connectSignals();
226  mMap->update();
227 }
228 
230 {
231  return mFrameMap;
232 }
233 
235 {
236  if ( !mMap )
237  {
238  return;
239  }
240 
241  if ( mFrameMap )
242  {
245  }
246 }
247 
249 {
250  mFrameSymbol.reset( symbol );
251 }
252 
254 {
255  return mFrameSymbol.get();
256 }
257 
259 {
260  return mFrameSymbol.get();
261 }
262 
263 void QgsLayoutItemMapOverview::setBlendMode( const QPainter::CompositionMode blendMode )
264 {
265  mBlendMode = blendMode;
266 }
267 
269 {
270  mInverted = inverted;
271 }
272 
274 {
275  mCentered = centered;
277 }
278 
280 {
281  if ( !mMap )
282  {
283  return;
284  }
285 
286  //if using overview centering, update the map's extent
287  if ( mMap->layout() && mCentered && mFrameMap )
288  {
289  QgsRectangle extent = mMap->extent();
290  QgsRectangle otherExtent = mFrameMap->extent();
291 
292  QgsPointXY center = otherExtent.center();
293  QgsRectangle movedExtent( center.x() - extent.width() / 2,
294  center.y() - extent.height() / 2,
295  center.x() - extent.width() / 2 + extent.width(),
296  center.y() - extent.height() / 2 + extent.height() );
297  mMap->setExtent( movedExtent );
298 
299  //must invalidate cache so that map gets redrawn
301  }
302 
303  //repaint map so that overview gets updated
304  mMap->update();
305 }
306 
307 
308 //
309 // QgsLayoutItemMapOverviewStack
310 //
311 
314 {
315 
316 }
317 
319 {
321 }
322 
323 void QgsLayoutItemMapOverviewStack::removeOverview( const QString &overviewId )
324 {
326 }
327 
328 void QgsLayoutItemMapOverviewStack::moveOverviewUp( const QString &overviewId )
329 {
331 }
332 
333 void QgsLayoutItemMapOverviewStack::moveOverviewDown( const QString &overviewId )
334 {
336 }
337 
339 {
341  return dynamic_cast<QgsLayoutItemMapOverview *>( item );
342 }
343 
345 {
347  return dynamic_cast<QgsLayoutItemMapOverview *>( item );
348 }
349 
351 {
352  QgsLayoutItemMapItem *item = mItems.at( idx );
354  return *overview;
355 }
356 
357 QList<QgsLayoutItemMapOverview *> QgsLayoutItemMapOverviewStack::asList() const
358 {
359  QList< QgsLayoutItemMapOverview * > list;
360  QList< QgsLayoutItemMapItem * >::const_iterator it = mItems.begin();
361  for ( ; it != mItems.end(); ++it )
362  {
364  if ( overview )
365  {
366  list.append( overview );
367  }
368  }
369  return list;
370 }
371 
372 bool QgsLayoutItemMapOverviewStack::readXml( const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context )
373 {
374  removeItems();
375 
376  //read overview stack
377  QDomNodeList mapOverviewNodeList = elem.elementsByTagName( QStringLiteral( "ComposerMapOverview" ) );
378  for ( int i = 0; i < mapOverviewNodeList.size(); ++i )
379  {
380  QDomElement mapOverviewElem = mapOverviewNodeList.at( i ).toElement();
381  QgsLayoutItemMapOverview *mapOverview = new QgsLayoutItemMapOverview( mapOverviewElem.attribute( QStringLiteral( "name" ) ), mMap );
382  mapOverview->readXml( mapOverviewElem, doc, context );
383  mItems.append( mapOverview );
384  }
385 
386  return true;
387 }
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
The class is used as a container of context for various read/write operations on other objects...
void setLinkedMap(QgsLayoutItemMap *map)
Sets the map to show the overview extent of.
A rectangle specified with double values.
Definition: qgsrectangle.h:40
QgsExpressionContext createExpressionContext() const override
Creates an expression context relating to the objects&#39; current state.
void moveOverviewUp(const QString &overviewId)
Moves an overview with matching overviewId up the stack, causing it to be rendered above other overvi...
void setFrameSymbol(QgsFillSymbol *symbol)
Sets the fill symbol used for drawing the overview extent.
An individual overview which is drawn above the map content in a QgsLayoutItemMap, and shows the extent of another QgsLayoutItemMap.
QgsLayoutItemMapItem * item(const QString &itemId) const
Returns a reference to an item which matching itemId within the stack.
OperationResult transform(const QgsCoordinateTransform &ct, QgsCoordinateTransform::TransformDirection direction=QgsCoordinateTransform::ForwardTransform, bool transformZ=false) SIP_THROW(QgsCsException)
Transforms this geometry as described by the coordinate transform ct.
void setInverted(bool inverted)
Sets whether the overview frame is inverted, ie, whether the shaded area is drawn outside the extent ...
void setExtent(const QgsRectangle &extent)
Sets a new extent for the map.
QgsLayoutItemMapOverview(const QString &name, QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapOverview.
double y
Definition: qgspointxy.h:48
An item which is drawn inside a QgsLayoutItemMap, e.g., a grid or map overview.
static QgsFillSymbol * createSimple(const QgsStringMap &properties)
Create a fill symbol with one symbol layer: SimpleFill with specified properties. ...
Definition: qgssymbol.cpp:1150
A class to represent a 2D point.
Definition: qgspointxy.h:43
void extentChanged()
Is emitted when the map&#39;s extent changes.
void draw(QPainter *painter) override
Draws the item on to a destination painter.
static QgsPainting::BlendMode getBlendModeEnum(QPainter::CompositionMode blendMode)
Returns a BlendMode corresponding to a QPainter::CompositionMode.
Definition: qgspainting.cpp:80
QgsLayoutItemMapOverview * overview(const QString &overviewId) const
Returns a reference to an overview with matching overviewId within the stack.
A collection of map items which are drawn above the map content in a QgsLayoutItemMap.
const QgsLayoutItemMap * map() const
Returns the layout item map for the item.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
bool readXml(const QDomElement &itemElem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets the map item state from a DOM document, where element is the DOM node corresponding to a &#39;Layout...
static QPainter::CompositionMode getCompositionMode(QgsPainting::BlendMode blendMode)
Returns a QPainter::CompositionMode corresponding to a BlendMode.
Definition: qgspainting.cpp:20
void invalidateCache() override
void finalizeRestoreFromXml() override
Called after all pending items have been restored from XML.
QgsFillSymbol * frameSymbol()
Returns the fill symbol used for drawing the overview extent.
void setBlendMode(QPainter::CompositionMode mode)
Sets the blending mode used for drawing the overview.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:577
void connectSignals()
Reconnects signals for overview map, so that overview correctly follows changes to source map&#39;s exten...
void removeItems()
Clears the item stack and deletes all QgsLayoutItemMapItems contained by the stack.
QgsLayoutItemMapOverview & operator[](int index)
Returns a reference to an overview at the specified index within the stack.
void addOverview(QgsLayoutItemMapOverview *overview)
Adds a new map overview to the stack and takes ownership of the overview.
QgsLayoutItemMap * mMap
Associated map.
virtual bool writeXml(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const
Stores map item state in a DOM element, where element is the DOM element corresponding to a &#39;LayoutMa...
bool mEnabled
True if item is to be displayed on map.
Layout graphical items for displaying a map.
bool centered() const
Returns whether the extent of the map is forced to center on the overview.
QPainter::CompositionMode blendMode() const
Retrieves the blending mode used for drawing the overview.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void addItem(QgsLayoutItemMapItem *item)
Adds a new map item to the stack and takes ownership of the item.
static QgsRenderContext createRenderContextForLayout(QgsLayout *layout, QPainter *painter, double dpi=-1)
Creates a render context suitable for the specified layout and painter destination.
QPointer< QgsLayout > mLayout
QgsPointXY center() const
Returns the center point of the rectangle.
Definition: qgsrectangle.h:229
void moveOverviewDown(const QString &overviewId)
Moves an overview with matching overviewId down the stack, causing it to be rendered below other over...
QgsCoordinateReferenceSystem crs() const
Returns coordinate reference system used for rendering the map.
bool readXml(const QDomElement &elem, const QDomDocument &doc, const QgsReadWriteContext &context) override
Sets the item stack&#39;s state from a DOM document, where element is a DOM node corresponding to a &#39;Layo...
void moveItemUp(const QString &itemId)
Moves an item which matching itemId up the stack, causing it to be rendered above other items...
void setCentered(bool centered)
Sets whether the extent of the map is forced to center on the overview.
double x
Definition: qgspointxy.h:47
void mapRotationChanged(double newRotation)
Is emitted when the map&#39;s rotation changes.
QgsLayoutItemMapOverviewStack(QgsLayoutItemMap *map)
Constructor for QgsLayoutItemMapOverviewStack, attached to the specified map.
virtual bool readXml(const QDomElement &element, const QDomDocument &doc, const QgsReadWriteContext &context)
Sets the map item state from a DOM document, where element is the DOM node corresponding to a &#39;Layout...
QgsGeometry densifyByCount(int extraNodesPerSegment) const
Returns a copy of the geometry which has been densified by adding the specified number of extra nodes...
QgsRectangle extent() const
Returns the current map extent.
void removeOverview(const QString &overviewId)
Removes an overview with matching overviewId from the stack and deletes the corresponding QgsLayoutIt...
Contains information about the context of a rendering operation.
void overviewExtentChanged()
Handles recentering of the map and redrawing of the map&#39;s overview.
bool usesAdvancedEffects() const override
Returns true if the item is drawn using advanced effects, such as blend modes.
static QgsGeometry fromQPolygonF(const QPolygonF &polygon)
Construct geometry from a QPolygonF.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Class for doing transforms between two map coordinate systems.
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgssymbol.h:1003
void removeItem(const QString &itemId)
Removes an item which matching itemId from the stack and deletes the corresponding QgsLayoutItemMapIt...
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
const QgsLayout * layout() const
Returns the layout the object is attached to.
void moveItemDown(const QString &itemId)
Moves an item which matching itemId up the stack, causing it to be rendered above other items...
QList< QgsLayoutItemMapItem * > mItems
QList< QgsLayoutItemMapOverview * > asList() const
Returns a list of QgsLayoutItemMapOverviews contained by the stack.
double width() const
Returns the width of the rectangle.
Definition: qgsrectangle.h:201
bool inverted() const
Returns whether the overview frame is inverted, ie, whether the shaded area is drawn outside the exte...
QPolygonF visibleExtentPolygon() const
Returns a polygon representing the current visible map extent, considering map extents and rotation...
bool writeXml(QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context) const override
Stores map item state in a DOM element, where element is the DOM element corresponding to a &#39;LayoutMa...
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
double height() const
Returns the height of the rectangle.
Definition: qgsrectangle.h:208
QgsLayoutItemMap * linkedMap()
Returns the source map to show the overview extent of.