QGIS API Documentation  2.18.3-Las Palmas (77b8c3d)
qgscomposernodesitem.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscomposernodesitem.cpp
3  begin : March 2016
4  copyright : (C) 2016 Paul Blottiere, Oslandia
5  email : paul dot blottiere at oslandia dot com
6  ***************************************************************************/
7 
8 /***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
16 
17 #include "qgscomposernodesitem.h"
18 #include "qgscomposition.h"
19 #include "qgscomposerutils.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgssymbolv2.h"
22 #include <limits>
23 #include <math.h>
24 
26  QgsComposition* c )
27  : QgsComposerItem( c )
28  , mTagName( tagName )
29  , mSelectedNode( -1 )
30  , mDrawNodes( false )
31 {
32 }
33 
35  QPolygonF polygon,
36  QgsComposition* c )
37  : QgsComposerItem( c )
38  , mTagName( tagName )
39  , mSelectedNode( -1 )
40  , mDrawNodes( false )
41 {
42  const QRectF boundingRect = polygon.boundingRect();
43  setSceneRect( boundingRect );
44 
45  const QPointF topLeft = boundingRect.topLeft();
46  mPolygon = polygon.translated( -topLeft );
47 }
48 
50 {
51 }
52 
54  const QPointF &pt2 ) const
55 {
56  return sqrt( pow( pt1.x() - pt2.x(), 2 ) + pow( pt1.y() - pt2.y(), 2 ) );
57 }
58 
60  const bool checkArea,
61  const double radius )
62 {
63  const QPointF start = mapFromScene( pt );
64  double minDistance = std::numeric_limits<double>::max();
65  double maxDistance = ( checkArea ) ? radius : minDistance;
66  bool rc = false;
67  int idx = -1;
68 
69  for ( int i = 0; i != mPolygon.size(); i++ )
70  {
71  // get nodes of polyline
72  const QPointF pt1 = mPolygon.at( i );
73  QPointF pt2 = mPolygon.first();
74  if (( i + 1 ) != mPolygon.size() )
75  pt2 = mPolygon.at( i + 1 );
76 
77  // compute line eq
78  const double coef = ( pt2.y() - pt1.y() ) / ( pt2.x() - pt1.x() );
79  const double b = pt1.y() - coef * pt1.x();
80 
81  double distance = std::numeric_limits<double>::max();
82  if ( qIsInf( coef ) )
83  distance = qAbs( pt1.x() - start.x() );
84  else
85  {
86  const double coef2 = ( -1 / coef );
87  const double b2 = start.y() - coef2 * start.x();
88 
89  QPointF inter;
90  if ( qIsInf( coef2 ) )
91  {
92  distance = qAbs( pt1.y() - start.y() );
93  inter.setX( start.x() );
94  inter.setY( pt1.y() );
95  }
96  else
97  {
98  const double interx = ( b - b2 ) / ( coef2 - coef );
99  const double intery = interx * coef2 + b2;
100  inter.setX( interx );
101  inter.setY( intery );
102  }
103 
104  // check if intersection is within the line
105  const double length1 = computeDistance( inter, pt1 );
106  const double length2 = computeDistance( inter, pt2 );
107  const double length3 = computeDistance( pt1, pt2 );
108  const double length4 = length1 + length2;
109 
110  if ( qAbs( length3 - length4 ) < std::numeric_limits<float>::epsilon() )
111  distance = computeDistance( inter, start );
112  }
113 
114  if ( distance < minDistance && distance < maxDistance )
115  {
116  minDistance = distance;
117  idx = i;
118  }
119  }
120 
121  if ( idx >= 0 )
122  {
123  rc = _addNode( idx, start, maxDistance );
124  updateSceneRect();
125  }
126 
127  return rc;
128 }
129 
130 void QgsComposerNodesItem::drawNodes( QPainter *painter ) const
131 {
132  double rectSize = 3.0 / horizontalViewScaleFactor();
133 
134  QgsStringMap properties;
135  properties.insert( "name", "cross" );
136  properties.insert( "color_border", "red" );
137 
139  symbol.reset( QgsMarkerSymbolV2::createSimple( properties ) );
140  symbol.data()->setSize( rectSize );
141  symbol.data()->setAngle( 45 );
142 
144  ms.setOutputDpi( painter->device()->logicalDpiX() );
145 
147  context.setPainter( painter );
148  context.setForceVectorOutput( true );
149 
150  QScopedPointer<QgsExpressionContext> expressionContext;
151  expressionContext.reset( createExpressionContext() );
152  context.setExpressionContext( *expressionContext.data() );
153 
154  symbol.data()->startRender( context );
155 
156  Q_FOREACH ( QPointF pt, mPolygon )
157  symbol.data()->renderPoint( pt, nullptr, context );
158 
159  symbol.data()->stopRender( context );
160 
161  if ( mSelectedNode >= 0 && mSelectedNode < mPolygon.size() )
162  drawSelectedNode( painter );
163 }
164 
165 void QgsComposerNodesItem::drawSelectedNode( QPainter *painter ) const
166 {
167  double rectSize = 3.0 / horizontalViewScaleFactor();
168 
169  QgsStringMap properties;
170  properties.insert( "name", "square" );
171  properties.insert( "color", "0, 0, 0, 0" );
172  properties.insert( "color_border", "blue" );
173  properties.insert( "width_border", "4" );
174 
176  symbol.reset( QgsMarkerSymbolV2::createSimple( properties ) );
177  symbol.data()->setSize( rectSize );
178 
180  ms.setOutputDpi( painter->device()->logicalDpiX() );
181 
183  context.setPainter( painter );
184  context.setForceVectorOutput( true );
185 
186  QScopedPointer<QgsExpressionContext> expressionContext;
187  expressionContext.reset( createExpressionContext() );
188  context.setExpressionContext( *expressionContext.data() );
189 
190  symbol.data()->startRender( context );
191  symbol.data()->renderPoint( mPolygon.at( mSelectedNode ), nullptr, context );
192  symbol.data()->stopRender( context );
193 }
194 
196  const QStyleOptionGraphicsItem* itemStyle,
197  QWidget* pWidget )
198 {
199  Q_UNUSED( itemStyle );
200  Q_UNUSED( pWidget );
201 
202  if ( !painter )
203  return;
204 
205  painter->save();
206  painter->setPen( Qt::NoPen );
207  painter->setBrush( Qt::NoBrush );
208  painter->setRenderHint( QPainter::Antialiasing, true );
209 
211  _draw( painter );
212 
213  if ( mDrawNodes && composition()->plotStyle() == QgsComposition::Preview )
214  drawNodes( painter );
215 
216  painter->restore();
217 }
218 
220  const bool searchInRadius,
221  const double radius )
222 {
223  const QPointF pt = mapFromScene( node );
224  double nearestDistance = std::numeric_limits<double>::max();
225  double maxDistance = ( searchInRadius ) ? radius : nearestDistance;
226  double distance = 0;
227  int idx = -1;
228 
230  for ( ; it != mPolygon.constEnd(); ++it )
231  {
232  distance = computeDistance( pt, *it );
233  if ( distance < nearestDistance && distance < maxDistance )
234  {
235  nearestDistance = distance;
236  idx = it - mPolygon.constBegin();
237  }
238  }
239 
240  return idx;
241 }
242 
244 {
245  bool rc( false );
246 
247  if ( index >= 0 && index < mPolygon.size() )
248  {
249  position = mapToScene( mPolygon.at( index ) );
250  rc = true;
251  }
252 
253  return rc;
254 }
255 
257 {
258  bool rc = _removeNode( index );
259  if ( rc )
260  updateSceneRect();
261  return rc;
262 }
263 
264 bool QgsComposerNodesItem::moveNode( const int index, const QPointF &pt )
265 {
266  bool rc( false );
267 
268  if ( index >= 0 && index < mPolygon.size() )
269  {
270  QPointF nodeItem = mapFromScene( pt );
271  mPolygon.replace( index, nodeItem );
272  updateSceneRect();
273 
274  rc = true;
275  }
276 
277  return rc;
278 }
279 
281  const QDomDocument& doc )
282 {
283  // restore general composer item properties
284  const QDomNodeList composerItemList = itemElem.elementsByTagName( "ComposerItem" );
285  if ( !composerItemList.isEmpty() )
286  {
287  QDomElement composerItemElem = composerItemList.at( 0 ).toElement();
288 
289  if ( !qgsDoubleNear( composerItemElem.attribute( "rotation", "0" ).toDouble(), 0.0 ) )
290  setItemRotation( composerItemElem.attribute( "rotation", "0" ).toDouble() );
291 
292  _readXML( composerItemElem, doc );
293  }
294 
295  // restore style
296  QDomElement styleSymbolElem = itemElem.firstChildElement( "symbol" );
297  if ( !styleSymbolElem.isNull() )
298  _readXMLStyle( styleSymbolElem );
299 
300  // restore nodes
301  mPolygon.clear();
302  QDomNodeList nodesList = itemElem.elementsByTagName( "node" );
303  for ( int i = 0; i < nodesList.size(); i++ )
304  {
305  QDomElement nodeElem = nodesList.at( i ).toElement();
306  QPointF newPt;
307  newPt.setX( nodeElem.attribute( "x" ).toDouble() );
308  newPt.setY( nodeElem.attribute( "y" ).toDouble() );
309  mPolygon.append( newPt );
310  }
311 
312  emit itemChanged();
313  return true;
314 }
315 
317 {
318  // get the bounding rect for the polygon currently displayed
320 
321  // compute x/y ratio
322  const float ratioX = rect().width() / boundingRect.width();
323  const float ratioY = rect().height() / boundingRect.height();
324 
325  // scaling
326  QTransform trans;
327  trans = trans.scale( ratioX, ratioY );
328  mPolygon = trans.map( mPolygon );
329 }
330 
332 {
333  bool rc = false;
334 
335  if ( index >= 0 && index < mPolygon.size() )
336  {
337  mSelectedNode = index;
338  rc = true;
339  }
340 
341  return rc;
342 }
343 
345 {
346  // set the new scene rectangle
347  const QRectF br = mPolygon.boundingRect();
348 
349  const QPointF topLeft = mapToScene( br.topLeft() );
350  setSceneRect( QRectF( topLeft.x(), topLeft.y(), br.width(), br.height() ) );
351 
352  // update polygon position
353  mPolygon.translate( -br.topLeft().x(), -br.topLeft().y() );
354 
355  // update
357  update();
358  emit itemChanged();
359 }
360 
362 {
363  QDomElement composerPolygonElem = doc.createElement( mTagName );
364 
365  // style
366  _writeXMLStyle( doc, composerPolygonElem );
367 
368  // write nodes
369  QDomElement nodesElem = doc.createElement( "nodes" );
370  Q_FOREACH ( QPointF pt, mPolygon )
371  {
372  QDomElement nodeElem = doc.createElement( "node" );
373  nodeElem.setAttribute( "x", QString::number( pt.x() ) );
374  nodeElem.setAttribute( "y", QString::number( pt.y() ) );
375  nodesElem.appendChild( nodeElem );
376  }
377  composerPolygonElem.appendChild( nodesElem );
378 
379  elem.appendChild( composerPolygonElem );
380 
381  return _writeXML( composerPolygonElem, doc );
382 }
bool moveNode(const int index, const QPointF &node)
Move a node to a new position.
void setForceVectorOutput(bool force)
QDomNodeList elementsByTagName(const QString &tagname) const
static unsigned index
static QgsMarkerSymbolV2 * createSimple(const QgsStringMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
QPolygonF mPolygon
Storage meaning for shape&#39;s nodes.
bool removeNode(const int index)
Remove a node from the shape.
int nodeAtPosition(QPointF node, const bool searchInRadius=true, const double radius=10)
Search the nearest node in shape within a maximal area.
void setRenderHint(RenderHint hint, bool on)
virtual void _readXMLStyle(const QDomElement &elmt)=0
Method called in readXML.
QDomNode appendChild(const QDomNode &newChild)
void append(const T &value)
QString attribute(const QString &name, const QString &defValue) const
virtual void _writeXMLStyle(QDomDocument &doc, QDomElement &elmt) const =0
Method called in writeXML.
void itemChanged()
Emitted when the item changes.
QPoint map(const QPoint &point) const
const QgsMapSettings & mapSettings() const
Return setting of QGIS map canvas.
virtual bool _removeNode(const int nodeIndex)=0
Method called in removeNode.
const_iterator constEnd() const
A item that forms part of a map composition.
QPolygonF translated(qreal dx, qreal dy) const
void setOutputDpi(double dpi)
Set DPI used for conversion between real world units (e.g. mm) and pixels.
void save()
T & first()
double toDouble(bool *ok) const
virtual QgsExpressionContext * createExpressionContext() const override
Creates an expression context relating to the item&#39;s current state.
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)
bool readXML(const QDomElement &itemElem, const QDomDocument &doc) override
Sets state from Dom document.
void reset(T *other)
The QgsMapSettings class contains configuration for rendering of the map.
bool _readXML(const QDomElement &itemElem, const QDomDocument &doc)
Reads parameter that are not subclass specific in document.
QDomElement toElement() const
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
bool isEmpty() const
virtual QRectF boundingRect() const
void clear()
void updateSceneRect()
Update the current scene rectangle for this item.
QString number(int n, int base)
qreal x() const
qreal y() const
double horizontalViewScaleFactor() const
Returns the zoom factor of the graphics view.
QTransform & scale(qreal sx, qreal sy)
bool addNode(const QPointF &pt, const bool checkArea=true, const double radius=10)
Add a node in current shape.
void setPen(const QColor &color)
void setAttribute(const QString &name, const QString &value)
const QgsComposition * composition() const
Returns the composition the item is attached to.
bool nodePosition(const int index, QPointF &position)
Gets the position of a node in scene coordinate.
double computeDistance(const QPointF &pt1, const QPointF &pt2) const
Compute an euclidian distance between 2 nodes.
QPointF topLeft() const
void paint(QPainter *painter, const QStyleOptionGraphicsItem *itemStyle, QWidget *pWidget) override
Reimplementation of QCanvasItem::paint - draw on canvas.
QPointF mapFromScene(const QPointF &point) const
QPaintDevice * device() const
virtual bool _addNode(const int nodeIndex, const QPointF &newNode, const double radius)=0
Method called in addNode.
void translate(qreal dx, qreal dy)
void setBrush(const QBrush &brush)
void setPainter(QPainter *p)
bool setSelectedNode(const int index)
Select a node.
virtual void _draw(QPainter *painter)=0
Method called in paint.
void rescaleToFitBoundingBox()
Rescale the current shape according to the boudning box.
void prepareGeometryChange()
Graphics scene for map printing.
int logicalDpiX() const
T * data() const
bool isNull() const
void restore()
QgsComposition * mComposition
QgsComposerNodesItem(QString mTagName, QgsComposition *c)
Constructor.
const T & at(int i) const
const_iterator constBegin() const
Contains information about the context of a rendering operation.
QRectF boundingRect() const
QPointF mapToScene(const QPointF &point) const
qreal width() const
bool _writeXML(QDomElement &itemElem, QDomDocument &doc) const
Writes parameter that are not subclass specific in document.
virtual void setItemRotation(const double r, const bool adjustPosition=false)
Sets the item rotation.
~QgsComposerNodesItem()
Destructor.
static QgsRenderContext fromMapSettings(const QgsMapSettings &mapSettings)
create initialized QgsRenderContext instance from given QgsMapSettings
void setX(qreal x)
void setY(qreal y)
QDomElement firstChildElement(const QString &tagName) const
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...
void replace(int i, const T &value)
qreal height() const
iterator insert(const Key &key, const T &value)
bool writeXML(QDomElement &elem, QDomDocument &doc) const override
Stores state in Dom element.
int size() const
QDomElement createElement(const QString &tagName)
int size() const
void setExpressionContext(const QgsExpressionContext &context)
Sets the expression context.
QDomNode at(int index) const
QRectF rect() const