QGIS API Documentation 3.99.0-Master (357b655ed83)
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(
56 mPoints[0],
57 mPoints[1],
58 mPoints[2]
59 );
60 break;
62 if ( mPoints.size() < 4 )
63 return nullptr;
64 return createNurbsCurve();
65 break;
66 default:
67 return nullptr;
68 }
69}
70
71bool QgsMapToolCaptureRubberBand::curveIsComplete() const
72{
73 return ( mStringType == Qgis::WkbType::LineString && mPoints.size() > 1 )
74 || ( mStringType == Qgis::WkbType::CircularString && mPoints.size() > 2 );
75}
76
77void QgsMapToolCaptureRubberBand::reset( Qgis::GeometryType geomType, Qgis::WkbType stringType, const QgsPoint &firstPolygonPoint )
78{
79 if ( !( geomType == Qgis::GeometryType::Line || geomType == Qgis::GeometryType::Polygon ) )
80 return;
81
82 mPoints.clear();
83 mWeights.clear();
84 mFirstPolygonPoint = firstPolygonPoint;
85 setStringType( stringType );
86 setRubberBandGeometryType( geomType );
87}
88
89void QgsMapToolCaptureRubberBand::setRubberBandGeometryType( Qgis::GeometryType geomType )
90{
92 updateCurve();
93}
94
95void QgsMapToolCaptureRubberBand::addPoint( const QgsPoint &point, bool doUpdate )
96{
97 if ( mPoints.size() == 0 )
98 {
99 mPoints.append( point );
100 mWeights.append( 1.0 );
101 }
102
103 mPoints.append( point );
104 mWeights.append( 1.0 );
105
106 if ( doUpdate )
107 updateCurve();
108}
109
110void QgsMapToolCaptureRubberBand::movePoint( const QgsPoint &point )
111{
112 if ( mPoints.size() > 0 )
113 mPoints.last() = point;
114
115 updateCurve();
116}
117
118void QgsMapToolCaptureRubberBand::movePoint( int index, const QgsPoint &point )
119{
120 if ( mPoints.size() > 0 && mPoints.size() > index )
121 mPoints[index] = point;
122
123 updateCurve();
124}
125
126int QgsMapToolCaptureRubberBand::pointsCount() const
127{
128 return mPoints.size();
129}
130
131Qgis::WkbType QgsMapToolCaptureRubberBand::stringType() const
132{
133 return mStringType;
134}
135
136void QgsMapToolCaptureRubberBand::setStringType( Qgis::WkbType type )
137{
138 if ( ( type != Qgis::WkbType::CircularString && type != Qgis::WkbType::LineString && type != Qgis::WkbType::NurbsCurve ) || type == mStringType )
139 return;
140
141 mStringType = type;
142 if ( type == Qgis::WkbType::LineString && mPoints.size() == 3 )
143 {
144 mPoints.removeAt( 1 );
145 }
146
147 setVertexDrawingEnabled( type == Qgis::WkbType::CircularString || type == Qgis::WkbType::NurbsCurve );
148 updateCurve();
149}
150
151QgsPoint QgsMapToolCaptureRubberBand::lastPoint() const
152{
153 if ( mPoints.empty() )
154 return QgsPoint();
155
156 return mPoints.last();
157}
158
159QgsPoint QgsMapToolCaptureRubberBand::pointFromEnd( int posFromEnd ) const
160{
161 if ( posFromEnd < mPoints.size() )
162 return mPoints.at( mPoints.size() - 1 - posFromEnd );
163 else
164 return QgsPoint();
165}
166
167void QgsMapToolCaptureRubberBand::removeLastPoint()
168{
169 if ( mPoints.size() > 1 )
170 {
171 mPoints.removeLast();
172 if ( !mWeights.isEmpty() )
173 mWeights.removeLast();
174 }
175
176 updateCurve();
177}
178
179void QgsMapToolCaptureRubberBand::setGeometry( QgsAbstractGeometry *geom )
180{
182}
183
184void QgsMapToolCaptureRubberBand::updateCurve()
185{
186 std::unique_ptr<QgsCurve> curve;
187 switch ( mStringType )
188 {
190 curve.reset( createLinearString() );
191 break;
193 curve.reset( createCircularString() );
194 break;
196 curve.reset( createNurbsCurve() );
197 break;
198 default:
199 return;
200 break;
201 }
202
203 if ( geometryType() == Qgis::GeometryType::Polygon )
204 {
205 auto geom = std::make_unique<QgsCurvePolygon>();
206 geom->setExteriorRing( curve.release() );
207 setGeometry( geom.release() );
208 }
209 else
210 {
211 setGeometry( curve.release() );
212 }
213
214 // Update control polygon for NURBS visualization
215 updateControlPolygon();
216}
217
218QgsCurve *QgsMapToolCaptureRubberBand::createLinearString()
219{
220 auto curve = std::make_unique<QgsLineString>();
221 if ( geometryType() == Qgis::GeometryType::Polygon )
222 {
223 QgsPointSequence points = mPoints;
224 points.prepend( mFirstPolygonPoint );
225 curve->setPoints( points );
226 }
227 else
228 curve->setPoints( mPoints );
229
230 return curve.release();
231}
232
233QgsCurve *QgsMapToolCaptureRubberBand::createCircularString()
234{
235 auto curve = std::make_unique<QgsCircularString>();
236 curve->setPoints( mPoints );
237 if ( geometryType() == Qgis::GeometryType::Polygon )
238 {
239 // add a linear string to close the polygon
240 auto polygonCurve = std::make_unique<QgsCompoundCurve>();
241 polygonCurve->addVertex( mFirstPolygonPoint );
242 if ( !mPoints.empty() )
243 polygonCurve->addVertex( mPoints.first() );
244 polygonCurve->addCurve( curve.release() );
245 return polygonCurve.release();
246 }
247 else
248 return curve.release();
249}
250
251QgsCurve *QgsMapToolCaptureRubberBand::createNurbsCurve()
252{
253 // Use control points from mPoints
254 QgsPointSequence controlPoints = mPoints;
255 if ( geometryType() == Qgis::GeometryType::Polygon )
256 {
257 controlPoints.prepend( mFirstPolygonPoint );
258 // For closed curves, the last control point must equal the first
259 controlPoints.append( mFirstPolygonPoint );
260 }
261
262 // Get degree from settings
264 const int n = controlPoints.size();
265
266 // Adapt degree if not enough control points
267 if ( n < degree + 1 )
268 {
269 // Try lower degrees, minimum is 1 (linear)
270 degree = std::max( 1, n - 1 );
271 if ( n < 2 )
272 {
273 return new QgsLineString( controlPoints );
274 }
275 }
276
277 // Generate uniform clamped knot vector using the factorized utility
278 QVector<double> knots = QgsNurbsCurve::generateUniformKnots( n, degree );
279
280 // Use stored weights, or default to 1.0
281 QVector<double> weights;
282 if ( geometryType() == Qgis::GeometryType::Polygon )
283 {
284 // For polygon, prepend weight 1.0 for mFirstPolygonPoint
285 weights.append( 1.0 );
286 weights.append( mWeights );
287 // Closing point weight should match the first point weight
288 weights.append( 1.0 );
289 }
290 else
291 {
292 weights = mWeights;
293 }
294 // Ensure we have the right number of weights
295 while ( weights.size() < n )
296 weights.append( 1.0 );
297 weights.resize( n );
298
299 auto curve = std::make_unique<QgsNurbsCurve>( controlPoints, degree, knots, weights );
300
301 if ( geometryType() == Qgis::GeometryType::Polygon )
302 {
303 // For polygons, we need to close the curve
304 auto polygonCurve = std::make_unique<QgsCompoundCurve>();
305 polygonCurve->addCurve( curve.release() );
306 return polygonCurve.release();
307 }
308 else
309 return curve.release();
310}
311
312void QgsMapToolCaptureRubberBand::updateControlPolygon()
313{
314 if ( !mControlPolygonRubberBand )
315 return;
316
317 // Only show control polygon for NURBS curves
318 if ( !QgsWkbTypes::isNurbsType( mStringType ) || mPoints.size() < 2 )
319 {
320 mControlPolygonRubberBand->reset( Qgis::GeometryType::Line );
321 mControlPolygonRubberBand->setVisible( false );
322 return;
323 }
324
325 mControlPolygonRubberBand->reset( Qgis::GeometryType::Line );
326
327 // Add control points to form the control polygon
328 QgsPointSequence controlPoints = mPoints;
329 if ( geometryType() == Qgis::GeometryType::Polygon && !mFirstPolygonPoint.isEmpty() )
330 {
331 controlPoints.prepend( mFirstPolygonPoint );
332 }
333
334 for ( const QgsPoint &pt : std::as_const( controlPoints ) )
335 {
336 mControlPolygonRubberBand->addPoint( QgsPointXY( pt ) );
337 }
338
339 mControlPolygonRubberBand->setVisible( true );
340}
341
342double QgsMapToolCaptureRubberBand::weight( int index ) const
343{
344 if ( index < 0 || index >= mWeights.size() )
345 return 1.0;
346 return mWeights[index];
347}
348
349bool QgsMapToolCaptureRubberBand::setWeight( int index, double weight )
350{
351 if ( index < 0 || index >= mWeights.size() )
352 return false;
353 if ( weight <= 0.0 )
354 return false;
355
356 mWeights[index] = weight;
357 updateCurve();
358 return true;
359}
360
Provides global constants and enumerations for use throughout the application.
Definition qgis.h:59
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:365
@ Line
Lines.
Definition qgis.h:367
@ Polygon
Polygons.
Definition qgis.h:368
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:280
@ LineString
LineString.
Definition qgis.h:283
@ NurbsCurve
NurbsCurve.
Definition qgis.h:297
@ CircularString
CircularString.
Definition qgis.h:290
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