QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsmaptoolcapturerubberband.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmaptoolcapturerubberband.cpp - map tool for capturing points, lines, polygons
3 ---------------------
4 begin : January 2022
5 copyright : (C) Denis Rouzaud
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
19#include "qgsnurbscurve.h"
20#include "qgsrubberband.h"
23
25
26
27QgsMapToolCaptureRubberBand::QgsMapToolCaptureRubberBand( QgsMapCanvas *mapCanvas, Qgis::GeometryType geomType )
28 : QgsGeometryRubberBand( mapCanvas, geomType )
29 , mControlPolygonRubberBand( std::make_unique<QgsRubberBand>( mapCanvas, Qgis::GeometryType::Line ) )
30{
31 setVertexDrawingEnabled( false );
32
33 // Style control polygon rubberband for NURBS visualization
34 mControlPolygonRubberBand->setColor( QColor( 100, 100, 100, 150 ) );
35 mControlPolygonRubberBand->setWidth( 1 );
36 mControlPolygonRubberBand->setLineStyle( Qt::DashLine );
37 mControlPolygonRubberBand->setVisible( false );
38}
39
40QgsMapToolCaptureRubberBand::~QgsMapToolCaptureRubberBand() = default;
41
42QgsCurve *QgsMapToolCaptureRubberBand::curve()
43{
44 if ( mPoints.empty() )
45 return nullptr;
46
47 switch ( mStringType )
48 {
50 return new QgsLineString( mPoints );
51 break;
53 if ( mPoints.size() != 3 )
54 return nullptr;
55 return new QgsCircularString( mPoints[0], mPoints[1], mPoints[2] );
56 break;
58 if ( mPoints.size() < 4 )
59 return nullptr;
60 return createNurbsCurve();
61 break;
62 default:
63 return nullptr;
64 }
65}
66
67bool QgsMapToolCaptureRubberBand::curveIsComplete() const
68{
69 return ( mStringType == Qgis::WkbType::LineString && mPoints.size() > 1 ) || ( mStringType == Qgis::WkbType::CircularString && mPoints.size() > 2 );
70}
71
72void QgsMapToolCaptureRubberBand::reset( Qgis::GeometryType geomType, Qgis::WkbType stringType, const QgsPoint &firstPolygonPoint )
73{
74 if ( !( geomType == Qgis::GeometryType::Line || geomType == Qgis::GeometryType::Polygon ) )
75 return;
76
77 mPoints.clear();
78 mWeights.clear();
79 mFirstPolygonPoint = firstPolygonPoint;
80 setStringType( stringType );
81 setRubberBandGeometryType( geomType );
82}
83
84void QgsMapToolCaptureRubberBand::setRubberBandGeometryType( Qgis::GeometryType geomType )
85{
87 updateCurve();
88}
89
90void QgsMapToolCaptureRubberBand::addPoint( const QgsPoint &point, bool doUpdate )
91{
92 if ( mPoints.size() == 0 )
93 {
94 mPoints.append( point );
95 mWeights.append( 1.0 );
96 }
97
98 mPoints.append( point );
99 mWeights.append( 1.0 );
100
101 if ( doUpdate )
102 updateCurve();
103}
104
105void QgsMapToolCaptureRubberBand::movePoint( const QgsPoint &point )
106{
107 if ( mPoints.size() > 0 )
108 mPoints.last() = point;
109
110 updateCurve();
111}
112
113void QgsMapToolCaptureRubberBand::movePoint( int index, const QgsPoint &point )
114{
115 if ( mPoints.size() > 0 && mPoints.size() > index )
116 mPoints[index] = point;
117
118 updateCurve();
119}
120
121int QgsMapToolCaptureRubberBand::pointsCount() const
122{
123 return mPoints.size();
124}
125
126Qgis::WkbType QgsMapToolCaptureRubberBand::stringType() const
127{
128 return mStringType;
129}
130
131void QgsMapToolCaptureRubberBand::setStringType( Qgis::WkbType type )
132{
133 if ( ( type != Qgis::WkbType::CircularString && type != Qgis::WkbType::LineString && type != Qgis::WkbType::NurbsCurve ) || type == mStringType )
134 return;
135
136 mStringType = type;
137 if ( type == Qgis::WkbType::LineString && mPoints.size() == 3 )
138 {
139 mPoints.removeAt( 1 );
140 }
141
142 setVertexDrawingEnabled( type == Qgis::WkbType::CircularString || type == Qgis::WkbType::NurbsCurve );
143 updateCurve();
144}
145
146QgsPoint QgsMapToolCaptureRubberBand::lastPoint() const
147{
148 if ( mPoints.empty() )
149 return QgsPoint();
150
151 return mPoints.last();
152}
153
154QgsPoint QgsMapToolCaptureRubberBand::pointFromEnd( int posFromEnd ) const
155{
156 if ( posFromEnd < mPoints.size() )
157 return mPoints.at( mPoints.size() - 1 - posFromEnd );
158 else
159 return QgsPoint();
160}
161
162void QgsMapToolCaptureRubberBand::removeLastPoint()
163{
164 if ( mPoints.size() > 1 )
165 {
166 mPoints.removeLast();
167 if ( !mWeights.isEmpty() )
168 mWeights.removeLast();
169 }
170
171 updateCurve();
172}
173
174void QgsMapToolCaptureRubberBand::setGeometry( QgsAbstractGeometry *geom )
175{
177}
178
179void QgsMapToolCaptureRubberBand::updateCurve()
180{
181 std::unique_ptr<QgsCurve> curve;
182 switch ( mStringType )
183 {
185 curve.reset( createLinearString() );
186 break;
188 curve.reset( createCircularString() );
189 break;
191 curve.reset( createNurbsCurve() );
192 break;
193 default:
194 return;
195 break;
196 }
197
198 if ( geometryType() == Qgis::GeometryType::Polygon )
199 {
200 auto geom = std::make_unique<QgsCurvePolygon>();
201 geom->setExteriorRing( curve.release() );
202 setGeometry( geom.release() );
203 }
204 else
205 {
206 setGeometry( curve.release() );
207 }
208
209 // Update control polygon for NURBS visualization
210 updateControlPolygon();
211}
212
213QgsCurve *QgsMapToolCaptureRubberBand::createLinearString()
214{
215 auto curve = std::make_unique<QgsLineString>();
216 if ( geometryType() == Qgis::GeometryType::Polygon )
217 {
218 QgsPointSequence points = mPoints;
219 points.prepend( mFirstPolygonPoint );
220 curve->setPoints( points );
221 }
222 else
223 curve->setPoints( mPoints );
224
225 return curve.release();
226}
227
228QgsCurve *QgsMapToolCaptureRubberBand::createCircularString()
229{
230 auto curve = std::make_unique<QgsCircularString>();
231 curve->setPoints( mPoints );
232 if ( geometryType() == Qgis::GeometryType::Polygon )
233 {
234 // add a linear string to close the polygon
235 auto polygonCurve = std::make_unique<QgsCompoundCurve>();
236 polygonCurve->addVertex( mFirstPolygonPoint );
237 if ( !mPoints.empty() )
238 polygonCurve->addVertex( mPoints.first() );
239 polygonCurve->addCurve( curve.release() );
240 return polygonCurve.release();
241 }
242 else
243 return curve.release();
244}
245
246QgsCurve *QgsMapToolCaptureRubberBand::createNurbsCurve()
247{
248 // Use control points from mPoints
249 QgsPointSequence controlPoints = mPoints;
250 if ( geometryType() == Qgis::GeometryType::Polygon )
251 {
252 controlPoints.prepend( mFirstPolygonPoint );
253 // For closed curves, the last control point must equal the first
254 controlPoints.append( mFirstPolygonPoint );
255 }
256
257 // Get degree from settings
259 const int n = controlPoints.size();
260
261 // Adapt degree if not enough control points
262 if ( n < degree + 1 )
263 {
264 // Try lower degrees, minimum is 1 (linear)
265 degree = std::max( 1, n - 1 );
266 if ( n < 2 )
267 {
268 return new QgsLineString( controlPoints );
269 }
270 }
271
272 // Generate uniform clamped knot vector using the factorized utility
273 QVector<double> knots = QgsNurbsCurve::generateUniformKnots( n, degree );
274
275 // Use stored weights, or default to 1.0
276 QVector<double> weights;
277 if ( geometryType() == Qgis::GeometryType::Polygon )
278 {
279 // For polygon, prepend weight 1.0 for mFirstPolygonPoint
280 weights.append( 1.0 );
281 weights.append( mWeights );
282 // Closing point weight should match the first point weight
283 weights.append( 1.0 );
284 }
285 else
286 {
287 weights = mWeights;
288 }
289 // Ensure we have the right number of weights
290 while ( weights.size() < n )
291 weights.append( 1.0 );
292 weights.resize( n );
293
294 auto curve = std::make_unique<QgsNurbsCurve>( controlPoints, degree, knots, weights );
295
296 if ( geometryType() == Qgis::GeometryType::Polygon )
297 {
298 // For polygons, we need to close the curve
299 auto polygonCurve = std::make_unique<QgsCompoundCurve>();
300 polygonCurve->addCurve( curve.release() );
301 return polygonCurve.release();
302 }
303 else
304 return curve.release();
305}
306
307void QgsMapToolCaptureRubberBand::updateControlPolygon()
308{
309 if ( !mControlPolygonRubberBand )
310 return;
311
312 // Only show control polygon for NURBS curves
313 if ( !QgsWkbTypes::isNurbsType( mStringType ) || mPoints.size() < 2 )
314 {
315 mControlPolygonRubberBand->reset( Qgis::GeometryType::Line );
316 mControlPolygonRubberBand->setVisible( false );
317 return;
318 }
319
320 mControlPolygonRubberBand->reset( Qgis::GeometryType::Line );
321
322 // Add control points to form the control polygon
323 QgsPointSequence controlPoints = mPoints;
324 if ( geometryType() == Qgis::GeometryType::Polygon && !mFirstPolygonPoint.isEmpty() )
325 {
326 controlPoints.prepend( mFirstPolygonPoint );
327 }
328
329 for ( const QgsPoint &pt : std::as_const( controlPoints ) )
330 {
331 mControlPolygonRubberBand->addPoint( QgsPointXY( pt ) );
332 }
333
334 mControlPolygonRubberBand->setVisible( true );
335}
336
337double QgsMapToolCaptureRubberBand::weight( int index ) const
338{
339 if ( index < 0 || index >= mWeights.size() )
340 return 1.0;
341 return mWeights[index];
342}
343
344bool QgsMapToolCaptureRubberBand::setWeight( int index, double weight )
345{
346 if ( index < 0 || index >= mWeights.size() )
347 return false;
348 if ( weight <= 0.0 )
349 return false;
350
351 mWeights[index] = weight;
352 updateCurve();
353 return true;
354}
355
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:62
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ LineString
LineString.
Definition qgis.h:297
@ NurbsCurve
NurbsCurve.
Definition qgis.h:311
@ CircularString
CircularString.
Definition qgis.h:304
Abstract base class for all geometries.
virtual void clear()=0
Clears the geometry, ie reset it to a null geometry.
Circular string geometry type.
Abstract base class for curved geometry type.
Definition qgscurve.h:36
A rubberband class for QgsAbstractGeometry (considering curved geometries).
void setGeometryType(Qgis::GeometryType geometryType)
Sets which geometry is handled by the rubber band, polygon or line.
virtual void setGeometry(QgsAbstractGeometry *geom)
Sets geometry (takes ownership). Geometry is expected to be in map coordinates.
Line string geometry type, with support for z-dimension and m-values.
Map canvas is a class for displaying all GIS data types on a canvas.
static QVector< double > generateUniformKnots(int numControlPoints, int degree)
Generates a uniform clamped knot vector for a NURBS curve.
Represents a 2D point.
Definition qgspointxy.h:62
Point geometry type, with support for z-dimension and m-values.
Definition qgspoint.h:53
Responsible for drawing transient features (e.g.
static const QgsSettingsEntryInteger * settingsDigitizingNurbsDegree
Settings entry digitizing NURBS curve degree.
static Q_INVOKABLE bool isNurbsType(Qgis::WkbType type)
Returns true if the WKB type is a NURBS curve type.
QVector< QgsPoint > QgsPointSequence