QGIS API Documentation 3.43.0-Master (32433f7016e)
qgslayoutitemnodeitem.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgslayoutitemnodeitem.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
18#include "moc_qgslayoutitemnodeitem.cpp"
19#include "qgssymbol.h"
20#include "qgslayout.h"
21#include "qgsmarkersymbol.h"
23
24#include <limits>
25#include <cmath>
26#include <QStyleOptionGraphicsItem>
27
28void QgsLayoutNodesItem::setNodes( const QPolygonF &nodes )
29{
32 emit clipPathChanged();
33}
34
36{
37 return mCurrentRectangle;
38}
39
44
46 : QgsLayoutItem( layout )
47{
48 init();
49}
50
52 QgsLayout *layout )
53 : QgsLayoutItem( layout )
54{
55 init();
56
57 const QRectF boundingRect = polygon.boundingRect();
59
60 const QPointF topLeft = boundingRect.topLeft();
61 mPolygon = polygon.translated( -topLeft );
62}
63
64void QgsLayoutNodesItem::init()
65{
66 // no cache - the node based items cannot reliably determine their real bounds (e.g. due to mitred corners).
67 // this blocks use of the pixmap based cache for these
68 setCacheMode( QGraphicsItem::NoCache );
69 setBackgroundEnabled( false );
70 setFrameEnabled( false );
71
73}
74
76{
77 QPainter *painter = context.renderContext().painter();
78 painter->setPen( Qt::NoPen );
79 painter->setBrush( Qt::NoBrush );
80
81 context.renderContext().setForceVectorOutput( true );
83 _draw( context );
84
85 if ( mDrawNodes && layout()->renderContext().isPreviewRender() )
86 drawNodes( context );
87}
88
93
95 QPointF pt2 ) const
96{
97 return std::sqrt( std::pow( pt1.x() - pt2.x(), 2 ) + std::pow( pt1.y() - pt2.y(), 2 ) );
98}
99
101 const bool checkArea,
102 const double radius )
103{
104 const QPointF start = mapFromScene( pt );
105 double minDistance = std::numeric_limits<double>::max();
106 const double maxDistance = ( checkArea ) ? radius : minDistance;
107 bool rc = false;
108 int idx = -1;
109
110 for ( int i = 0; i != mPolygon.size(); i++ )
111 {
112 // get nodes of polyline
113 const QPointF pt1 = mPolygon.at( i );
114 QPointF pt2 = mPolygon.at( 0 );
115 if ( ( i + 1 ) != mPolygon.size() )
116 pt2 = mPolygon.at( i + 1 );
117
118 // compute line eq
119 const double coef = ( pt2.y() - pt1.y() ) / ( pt2.x() - pt1.x() );
120 const double b = pt1.y() - coef * pt1.x();
121
122 double distance = std::numeric_limits<double>::max();
123 if ( std::isinf( coef ) )
124 distance = std::fabs( pt1.x() - start.x() );
125 else
126 {
127 const double coef2 = ( -1 / coef );
128 const double b2 = start.y() - coef2 * start.x();
129
130 QPointF inter;
131 if ( std::isinf( coef2 ) )
132 {
133 distance = std::fabs( pt1.y() - start.y() );
134 inter.setX( start.x() );
135 inter.setY( pt1.y() );
136 }
137 else
138 {
139 const double interx = ( b - b2 ) / ( coef2 - coef );
140 const double intery = interx * coef2 + b2;
141 inter.setX( interx );
142 inter.setY( intery );
143 }
144
145 // check if intersection is within the line
146 const double length1 = computeDistance( inter, pt1 );
147 const double length2 = computeDistance( inter, pt2 );
148 const double length3 = computeDistance( pt1, pt2 );
149 const double length4 = length1 + length2;
150
151 if ( std::fabs( length3 - length4 ) < std::numeric_limits<float>::epsilon() )
152 distance = computeDistance( inter, start );
153 }
154
155 if ( distance < minDistance && distance < maxDistance )
156 {
157 minDistance = distance;
158 idx = i;
159 }
160 }
161
162 if ( idx >= 0 )
163 {
164 rc = _addNode( idx, start, maxDistance );
166 emit clipPathChanged();
167 }
168
169 return rc;
170}
171
172void QgsLayoutNodesItem::drawNodes( QgsLayoutItemRenderContext &context ) const
173{
174 context.renderContext().painter()->setRenderHint( QPainter::Antialiasing, false );
175
176 const double rectSize = 9.0 / context.viewScaleFactor();
177
178 QVariantMap properties;
179 properties.insert( QStringLiteral( "name" ), QStringLiteral( "cross" ) );
180 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "red" ) );
181
182 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
183 symbol->setSize( rectSize );
184 symbol->setAngle( 45 );
185
186 symbol->startRender( context.renderContext() );
187 for ( const QPointF pt : std::as_const( mPolygon ) )
188 symbol->renderPoint( pt * context.viewScaleFactor(), nullptr, context.renderContext() );
189 symbol->stopRender( context.renderContext() );
190
191 if ( mSelectedNode >= 0 && mSelectedNode < mPolygon.size() )
192 drawSelectedNode( context );
193}
194
195void QgsLayoutNodesItem::drawSelectedNode( QgsLayoutItemRenderContext &context ) const
196{
197 const double rectSize = 9.0 / context.viewScaleFactor();
198
199 QVariantMap properties;
200 properties.insert( QStringLiteral( "name" ), QStringLiteral( "square" ) );
201 properties.insert( QStringLiteral( "color" ), QStringLiteral( "0, 0, 0, 0" ) );
202 properties.insert( QStringLiteral( "color_border" ), QStringLiteral( "blue" ) );
203 properties.insert( QStringLiteral( "width_border" ), QStringLiteral( "4" ) );
204
205 std::unique_ptr<QgsMarkerSymbol> symbol = QgsMarkerSymbol::createSimple( properties );
206 symbol->setSize( rectSize );
207
208 symbol->startRender( context.renderContext() );
209 symbol->renderPoint( mPolygon.at( mSelectedNode ) * context.viewScaleFactor(), nullptr, context.renderContext() );
210 symbol->stopRender( context.renderContext() );
211}
212
214 const bool searchInRadius,
215 const double radius ) const
216{
217 const QPointF pt = mapFromScene( node );
218 double nearestDistance = std::numeric_limits<double>::max();
219 const double maxDistance = ( searchInRadius ) ? radius : nearestDistance;
220 double distance = 0;
221 int idx = -1;
222
223 int i = 0;
224 for ( const QPointF polyPt : std::as_const( mPolygon ) )
225 {
226 distance = computeDistance( pt, polyPt );
227 if ( distance < nearestDistance && distance < maxDistance )
228 {
229 nearestDistance = distance;
230 idx = i;
231 }
232 i++;
233 }
234
235 return idx;
236}
237
238bool QgsLayoutNodesItem::nodePosition( const int index, QPointF &position ) const
239{
240 bool rc( false );
241
242 if ( index >= 0 && index < mPolygon.size() )
243 {
244 position = mapToScene( mPolygon.at( index ) );
245 rc = true;
246 }
247
248 return rc;
249}
250
251bool QgsLayoutNodesItem::removeNode( const int index )
252{
253 const bool rc = _removeNode( index );
254 if ( rc )
255 {
257 emit clipPathChanged();
258 }
259 return rc;
260}
261
262bool QgsLayoutNodesItem::moveNode( const int index, QPointF pt )
263{
264 bool rc( false );
265
266 if ( index >= 0 && index < mPolygon.size() )
267 {
268 const QPointF nodeItem = mapFromScene( pt );
269 mPolygon.replace( index, nodeItem );
271 emit clipPathChanged();
272 rc = true;
273 }
274
275 return rc;
276}
277
278bool QgsLayoutNodesItem::readPropertiesFromElement( const QDomElement &itemElem,
279 const QDomDocument &, const QgsReadWriteContext &context )
280{
281 // restore style
282 const QDomElement styleSymbolElem = itemElem.firstChildElement( QStringLiteral( "symbol" ) );
283 if ( !styleSymbolElem.isNull() )
284 _readXmlStyle( styleSymbolElem, context );
285
286 // restore nodes
287 mPolygon.clear();
288 const QDomNodeList nodesList = itemElem.elementsByTagName( QStringLiteral( "node" ) );
289 for ( int i = 0; i < nodesList.size(); i++ )
290 {
291 const QDomElement nodeElem = nodesList.at( i ).toElement();
292 QPointF newPt;
293 newPt.setX( nodeElem.attribute( QStringLiteral( "x" ) ).toDouble() );
294 newPt.setY( nodeElem.attribute( QStringLiteral( "y" ) ).toDouble() );
295 mPolygon.append( newPt );
296 }
297
298 emit changed();
299 emit clipPathChanged();
300 return true;
301}
302
304{
305 // get the bounding rect for the polygon currently displayed
306 const QRectF boundingRect = mPolygon.boundingRect();
307
308 // compute x/y ratio
309 const float ratioX = !qgsDoubleNear( boundingRect.width(), 0.0 )
310 ? rect().width() / boundingRect.width() : 0;
311 const float ratioY = !qgsDoubleNear( boundingRect.height(), 0.0 )
312 ? rect().height() / boundingRect.height() : 0;
313
314 // scaling
315 QTransform trans;
316 trans = trans.scale( ratioX, ratioY );
317 mPolygon = trans.map( mPolygon );
318 emit clipPathChanged();
319}
320
322{
323 bool rc = false;
324
325 if ( index >= 0 && index < mPolygon.size() )
326 {
327 mSelectedNode = index;
328 rc = true;
329 }
330
331 return rc;
332}
333
335{
336 // set the new scene rectangle
337 const QRectF br = mPolygon.boundingRect();
338
339 const QPointF topLeft = mapToScene( br.topLeft() );
340 //will trigger updateBoundingRect if necessary
341 attemptSetSceneRect( QRectF( topLeft.x(), topLeft.y(), br.width(), br.height() ) );
342
343 // update polygon position
344 mPolygon.translate( -br.topLeft().x(), -br.topLeft().y() );
345}
346
348{
349 QRectF br = rect();
351 prepareGeometryChange();
353
354 // update
355 update();
356}
357
358bool QgsLayoutNodesItem::writePropertiesToElement( QDomElement &elem, QDomDocument &doc, const QgsReadWriteContext &context ) const
359{
360 // style
361 _writeXmlStyle( doc, elem, context );
362
363 // write nodes
364 QDomElement nodesElem = doc.createElement( QStringLiteral( "nodes" ) );
365 for ( const QPointF pt : std::as_const( mPolygon ) )
366 {
367 QDomElement nodeElem = doc.createElement( QStringLiteral( "node" ) );
368 nodeElem.setAttribute( QStringLiteral( "x" ), QString::number( pt.x() ) );
369 nodeElem.setAttribute( QStringLiteral( "y" ), QString::number( pt.y() ) );
370 nodesElem.appendChild( nodeElem );
371 }
372 elem.appendChild( nodesElem );
373
374 return true;
375}
Contains settings and helpers relating to a render of a QgsLayoutItem.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
double viewScaleFactor() const
Returns the current view zoom (scale factor).
Base class for graphical items within a QgsLayout.
virtual void setFrameEnabled(bool drawFrame)
Sets whether this item has a frame drawn around it or not.
@ FlagDisableSceneCaching
Item should not have QGraphicsItem caching enabled.
void sizePositionChanged()
Emitted when the item's size or position changes.
void clipPathChanged()
Emitted when the item's clipping path has changed.
QFlags< Flag > Flags
void attemptSetSceneRect(const QRectF &rect, bool includesFrame=false)
Attempts to update the item's position and size to match the passed rect in layout coordinates.
void setBackgroundEnabled(bool drawBackground)
Sets whether this item has a background drawn under it or not.
QgsLayoutItem::Flags itemFlags() const override
Returns the item's flags, which indicate how the item behaves.
virtual void updateBoundingRect()
Called when the bounding rect of the item should recalculated.
virtual bool _removeNode(int nodeIndex)=0
Method called in removeNode.
QPolygonF nodes() const
Returns the nodes the shape consists of.
double mMaxSymbolBleed
Max symbol bleed.
virtual void _writeXmlStyle(QDomDocument &doc, QDomElement &elmt, const QgsReadWriteContext &context) const =0
Method called in writeXml.
QRectF mCurrentRectangle
Current bounding rectangle of shape.
bool readPropertiesFromElement(const QDomElement &element, const QDomDocument &document, const QgsReadWriteContext &context) override
Sets item state from a DOM element.
bool setSelectedNode(int index)
Selects a node by index.
bool writePropertiesToElement(QDomElement &element, QDomDocument &document, const QgsReadWriteContext &context) const override
Stores item state within an XML DOM element.
void updateSceneRect()
Update the current scene rectangle for this item.
virtual void _readXmlStyle(const QDomElement &elmt, const QgsReadWriteContext &context)=0
Method called in readXml.
bool removeNode(int index)
Remove a node with specified index from the shape.
QRectF boundingRect() const override
double computeDistance(QPointF pt1, QPointF pt2) const
Compute an euclidean distance between 2 nodes.
bool nodePosition(int index, QPointF &position) const
Gets the position of a node in scene coordinates.
bool addNode(QPointF point, bool checkArea=true, double radius=10)
Add a node in current shape.
bool moveNode(int index, QPointF node)
Moves a node to a new position.
double estimatedFrameBleed() const override
Returns the estimated amount the item's frame bleeds outside the item's actual rectangle.
void draw(QgsLayoutItemRenderContext &context) override
Draws the item's contents using the specified item render context.
virtual void _draw(QgsLayoutItemRenderContext &context, const QStyleOptionGraphicsItem *itemStyle=nullptr)=0
Method called in paint.
QgsLayoutNodesItem(QgsLayout *layout)
Constructor for QgsLayoutNodesItem, attached to the specified layout.
virtual bool _addNode(int nodeIndex, QPointF newNode, double radius)=0
Method called in addNode.
void rescaleToFitBoundingBox()
Rescale the current shape according to the item's bounding box.
void setNodes(const QPolygonF &nodes)
Sets the nodes the shape consists of.
QPolygonF mPolygon
Shape's nodes.
int nodeAtPosition(QPointF point, bool searchInRadius=true, double radius=10) const
Search for the nearest node in the shape within a maximal area.
const QgsLayout * layout() const
Returns the layout the object is attached to.
void changed()
Emitted when the object's properties change.
Base class for layouts, which can contain items such as maps, labels, scalebars, etc.
Definition qgslayout.h:49
static std::unique_ptr< QgsMarkerSymbol > createSimple(const QVariantMap &properties)
Create a marker symbol with one symbol layer: SimpleMarker with specified properties.
A container for the context for various read/write operations on objects.
void setForceVectorOutput(bool force)
Sets whether rendering operations should use vector operations instead of any faster raster shortcuts...
QPainter * painter()
Returns the destination QPainter for the render operation.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6286