Skip to content

Commit e1d481f

Browse files
committedFeb 13, 2018
various fixes to the offset map tool
code has been refactored, hopefully things are clearer now fix #17367 fix #14751
1 parent ca0cb9b commit e1d481f

File tree

2 files changed

+217
-135
lines changed

2 files changed

+217
-135
lines changed
 

‎src/app/qgsmaptooloffsetcurve.cpp

Lines changed: 159 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
* *
1414
***************************************************************************/
1515

16+
#include <QGraphicsProxyWidget>
17+
#include <QMouseEvent>
18+
#include <QGridLayout>
19+
#include <QLabel>
20+
1621
#include "qgsdoublespinbox.h"
1722
#include "qgsfeatureiterator.h"
1823
#include "qgsmaptooloffsetcurve.h"
@@ -21,33 +26,29 @@
2126
#include "qgsrubberband.h"
2227
#include "qgssnappingutils.h"
2328
#include "qgsvectorlayer.h"
24-
#include "qgsvertexmarker.h"
29+
#include "qgssnapindicator.h"
2530
#include "qgssnappingconfig.h"
2631
#include "qgssettings.h"
2732
#include "qgisapp.h"
2833

29-
#include <QGraphicsProxyWidget>
30-
#include <QMouseEvent>
3134
#include "qgisapp.h"
3235

3336
QgsMapToolOffsetCurve::QgsMapToolOffsetCurve( QgsMapCanvas *canvas )
3437
: QgsMapToolEdit( canvas )
38+
, mSnapIndicator( qgis::make_unique< QgsSnapIndicator >( canvas ) )
3539
{
3640
}
3741

3842
QgsMapToolOffsetCurve::~QgsMapToolOffsetCurve()
3943
{
40-
deleteRubberBandAndGeometry();
41-
deleteDistanceWidget();
42-
delete mSnapVertexMarker;
44+
cancel();
4345
}
4446

4547
void QgsMapToolOffsetCurve::keyPressEvent( QKeyEvent *e )
4648
{
4749
if ( e && e->key() == Qt::Key_Escape && !e->isAutoRepeat() )
4850
{
49-
deleteRubberBandAndGeometry();
50-
deleteDistanceWidget();
51+
cancel();
5152
}
5253
else
5354
{
@@ -58,23 +59,11 @@ void QgsMapToolOffsetCurve::keyPressEvent( QKeyEvent *e )
5859

5960
void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
6061
{
61-
if ( !mCanvas )
62-
{
63-
return;
64-
}
65-
66-
QgsVectorLayer *layer = currentVectorLayer();
67-
if ( !layer )
68-
{
69-
deleteRubberBandAndGeometry();
70-
notifyNotVectorLayer();
71-
return;
72-
}
62+
mCtrlHeldOnFirstClick = false;
7363

7464
if ( e->button() == Qt::RightButton )
7565
{
76-
deleteRubberBandAndGeometry();
77-
deleteDistanceWidget();
66+
cancel();
7867
return;
7968
}
8069

@@ -83,7 +72,6 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
8372
// first click, get feature to modify
8473
deleteRubberBandAndGeometry();
8574
mGeometryModified = false;
86-
mCtrlWasHeldOnFeatureSelection = false;
8775

8876
QgsSnappingUtils *snapping = mCanvas->snappingUtils();
8977

@@ -106,50 +94,53 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
10694

10795
if ( match.hasEdge() && match.layer() )
10896
{
109-
mSourceLayerId = match.layer()->id();
97+
mLayer = match.layer();
11098
QgsFeature fet;
11199
if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) )
112100
{
113-
mCtrlWasHeldOnFeatureSelection = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed
101+
mCtrlHeldOnFirstClick = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed
114102
prepareGeometry( match.layer(), match, fet );
115103
mRubberBand = createRubberBand();
116104
if ( mRubberBand )
117105
{
118-
mRubberBand->setToGeometry( mManipulatedGeometry, layer );
106+
mRubberBand->setToGeometry( mManipulatedGeometry, match.layer() );
119107
}
120108
mModifiedFeature = fet.id();
121-
createDistanceWidget();
109+
createUserInputWidget();
122110
}
123111
}
124112

125113
if ( mOriginalGeometry.isNull() )
126114
{
127115
emit messageEmitted( tr( "Could not find a nearby feature in any vector layer." ) );
116+
cancel();
117+
notifyNotVectorLayer();
128118
}
129119
}
130120
else
131121
{
132122
// second click - apply changes
133-
applyOffset( e->modifiers() & Qt::ControlModifier );
123+
double offset = calculateOffset( e->snapPoint() );
124+
applyOffset( offset, e->modifiers() );
134125
}
135126
}
136127

137-
void QgsMapToolOffsetCurve::applyOffset( bool forceCopy )
128+
void QgsMapToolOffsetCurve::applyOffset( const double &offset, const Qt::KeyboardModifiers &modifiers )
138129
{
139-
QgsVectorLayer *layer = currentVectorLayer();
140-
if ( !layer )
130+
if ( !mLayer || offset == 0.0 )
141131
{
142-
deleteRubberBandAndGeometry();
132+
cancel();
143133
notifyNotVectorLayer();
144134
return;
145135
}
146136

137+
updateGeometryAndRubberBand( offset );
138+
147139
// no modification
148140
if ( !mGeometryModified )
149141
{
150-
deleteRubberBandAndGeometry();
151-
layer->destroyEditCommand();
152-
deleteDistanceWidget();
142+
mLayer->destroyEditCommand();
143+
cancel();
153144
return;
154145
}
155146

@@ -207,110 +198,99 @@ void QgsMapToolOffsetCurve::applyOffset( bool forceCopy )
207198
mModifiedGeometry = geometry;
208199
}
209200

210-
layer->beginEditCommand( tr( "Offset curve" ) );
201+
mLayer->beginEditCommand( tr( "Offset curve" ) );
211202

212203
bool editOk;
213-
if ( mSourceLayerId == layer->id() && !mCtrlWasHeldOnFeatureSelection && !forceCopy )
204+
if ( !mCtrlHeldOnFirstClick && !modifiers.testFlag( Qt::ControlModifier ) )
214205
{
215-
editOk = layer->changeGeometry( mModifiedFeature, mModifiedGeometry );
206+
editOk = mLayer->changeGeometry( mModifiedFeature, mModifiedGeometry );
216207
}
217208
else
218209
{
219210
QgsFeature f;
220211
f.setGeometry( mModifiedGeometry );
221212

222213
//add empty values for all fields (allows inserting attribute values via the feature form in the same session)
223-
QgsAttributes attrs( layer->fields().count() );
224-
const QgsFields &fields = layer->fields();
214+
QgsAttributes attrs( mLayer->fields().count() );
215+
const QgsFields &fields = mLayer->fields();
225216
for ( int idx = 0; idx < fields.count(); ++idx )
226217
{
227218
attrs[idx] = QVariant();
228219
}
229220
f.setAttributes( attrs );
230-
editOk = layer->addFeature( f );
221+
editOk = mLayer->addFeature( f );
231222
}
232223

233224
if ( editOk )
234225
{
235-
layer->endEditCommand();
226+
mLayer->endEditCommand();
236227
}
237228
else
238229
{
239-
layer->destroyEditCommand();
230+
mLayer->destroyEditCommand();
231+
emit messageEmitted( "Could not apply offset", Qgis::Critical );
240232
}
241233

242234
deleteRubberBandAndGeometry();
243-
deleteDistanceWidget();
244-
delete mSnapVertexMarker;
245-
mSnapVertexMarker = nullptr;
246-
mCtrlWasHeldOnFeatureSelection = false;
247-
layer->triggerRepaint();
235+
deleteUserInputWidget();
236+
mLayer->triggerRepaint();
237+
mLayer = nullptr;
248238
}
249239

250-
void QgsMapToolOffsetCurve::placeOffsetCurveToValue()
240+
void QgsMapToolOffsetCurve::cancel()
251241
{
252-
setOffsetForRubberBand( mDistanceWidget->value() );
242+
deleteUserInputWidget();
243+
deleteRubberBandAndGeometry();
244+
mLayer = nullptr;
253245
}
254246

255-
void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
247+
double QgsMapToolOffsetCurve::calculateOffset( QgsPointXY mapPoint )
256248
{
257-
delete mSnapVertexMarker;
258-
mSnapVertexMarker = nullptr;
259-
260-
if ( mOriginalGeometry.isNull() || !mRubberBand )
249+
double offset = 0.0;
250+
if ( mLayer )
261251
{
262-
return;
252+
//get offset from current position rectangular to feature
253+
QgsPointXY layerCoords = toLayerCoordinates( mLayer, mapPoint );
254+
255+
QgsPointXY minDistPoint;
256+
int beforeVertex;
257+
int leftOf = 0;
258+
259+
offset = std::sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
260+
offset = leftOf < 0 ? offset : -offset;
263261
}
262+
return offset;
263+
}
264264

265-
QgsVectorLayer *layer = currentVectorLayer();
266-
if ( !layer )
265+
void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
266+
{
267+
if ( mOriginalGeometry.isNull() || !mRubberBand )
267268
{
268269
return;
269270
}
270271

271-
272272
mGeometryModified = true;
273273

274-
//get offset from current position rectangular to feature
275-
QgsPointXY layerCoords = toLayerCoordinates( layer, e->pos() );
274+
QgsPointXY mapPoint = e->snapPoint();
275+
mSnapIndicator->setMatch( e->mapPointMatch() );
276276

277-
//snap cursor to background layers
278-
QgsPointLocator::Match m = mCanvas->snappingUtils()->snapToMap( e->pos() );
279-
if ( m.isValid() )
280-
{
281-
if ( ( m.layer() && m.layer()->id() != mSourceLayerId ) || m.featureId() != mModifiedFeature )
282-
{
283-
layerCoords = toLayerCoordinates( layer, m.point() );
284-
mSnapVertexMarker = new QgsVertexMarker( mCanvas );
285-
mSnapVertexMarker->setIconType( QgsVertexMarker::ICON_CROSS );
286-
mSnapVertexMarker->setColor( Qt::green );
287-
mSnapVertexMarker->setPenWidth( 1 );
288-
mSnapVertexMarker->setCenter( m.point() );
289-
}
290-
}
291-
292-
QgsPointXY minDistPoint;
293-
int beforeVertex;
294-
int leftOf = 0;
295-
double offset = std::sqrt( mOriginalGeometry.closestSegmentWithContext( layerCoords, minDistPoint, beforeVertex, &leftOf ) );
277+
double offset = calculateOffset( mapPoint );
296278
if ( offset == 0.0 )
297279
{
298280
return;
299281
}
300-
offset = leftOf < 0 ? offset : -offset;
301282

302-
303-
if ( mDistanceWidget )
283+
if ( mUserInputWidget )
304284
{
305-
// this will also set the rubber band
306-
mDistanceWidget->setValue( offset );
307-
mDistanceWidget->setFocus( Qt::TabFocusReason );
308-
}
309-
else
310-
{
311-
//create offset geometry using geos
312-
setOffsetForRubberBand( offset );
285+
disconnect( mUserInputWidget, &QgsOffsetUserWidget::offsetChanged, this, &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
286+
mUserInputWidget->setOffset( offset );
287+
connect( mUserInputWidget, &QgsOffsetUserWidget::offsetChanged, this, &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
288+
mUserInputWidget->setFocus( Qt::TabFocusReason );
289+
mUserInputWidget->editor()->selectAll();
313290
}
291+
292+
//create offset geometry using geos
293+
updateGeometryAndRubberBand( offset );
314294
}
315295

316296
void QgsMapToolOffsetCurve::prepareGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature )
@@ -374,38 +354,34 @@ void QgsMapToolOffsetCurve::prepareGeometry( QgsVectorLayer *vl, const QgsPointL
374354
}
375355
}
376356

377-
void QgsMapToolOffsetCurve::createDistanceWidget()
357+
void QgsMapToolOffsetCurve::createUserInputWidget()
378358
{
379359
if ( !mCanvas )
380360
{
381361
return;
382362
}
383363

384-
deleteDistanceWidget();
385-
386-
mDistanceWidget = new QgsDoubleSpinBox();
387-
mDistanceWidget->setMinimum( -99999999 );
388-
mDistanceWidget->setMaximum( 99999999 );
389-
mDistanceWidget->setDecimals( 6 );
390-
mDistanceWidget->setPrefix( tr( "Offset: " ) );
391-
mDistanceWidget->setClearValue( 0.0 );
392-
QgisApp::instance()->addUserInputWidget( mDistanceWidget );
364+
deleteUserInputWidget();
365+
mUserInputWidget = new QgsOffsetUserWidget();
366+
QgisApp::instance()->addUserInputWidget( mUserInputWidget );
367+
mUserInputWidget->setFocus( Qt::TabFocusReason );
393368

394-
mDistanceWidget->setFocus( Qt::TabFocusReason );
395-
396-
connect( mDistanceWidget, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsMapToolOffsetCurve::placeOffsetCurveToValue );
397-
connect( mDistanceWidget, &QAbstractSpinBox::editingFinished, this, [ = ] { applyOffset(); } );
369+
connect( mUserInputWidget, &QgsOffsetUserWidget::offsetChanged, this, &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
370+
connect( mUserInputWidget, &QgsOffsetUserWidget::offsetEditingFinished, this, &QgsMapToolOffsetCurve::applyOffset );
371+
connect( mUserInputWidget, &QgsOffsetUserWidget::offsetEditingCanceled, this, &QgsMapToolOffsetCurve::cancel );
398372
}
399373

400-
void QgsMapToolOffsetCurve::deleteDistanceWidget()
374+
void QgsMapToolOffsetCurve::deleteUserInputWidget()
401375
{
402-
if ( mDistanceWidget )
376+
if ( mUserInputWidget )
403377
{
404-
disconnect( mDistanceWidget, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsMapToolOffsetCurve::placeOffsetCurveToValue );
405-
mDistanceWidget->releaseKeyboard();
406-
mDistanceWidget->deleteLater();
378+
disconnect( mUserInputWidget, &QgsOffsetUserWidget::offsetChanged, this, &QgsMapToolOffsetCurve::updateGeometryAndRubberBand );
379+
disconnect( mUserInputWidget, &QgsOffsetUserWidget::offsetEditingFinished, this, &QgsMapToolOffsetCurve::applyOffset );
380+
disconnect( mUserInputWidget, &QgsOffsetUserWidget::offsetEditingCanceled, this, &QgsMapToolOffsetCurve::cancel );
381+
mUserInputWidget->releaseKeyboard();
382+
mUserInputWidget->deleteLater();
407383
}
408-
mDistanceWidget = nullptr;
384+
mUserInputWidget = nullptr;
409385
}
410386

411387
void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry()
@@ -416,15 +392,14 @@ void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry()
416392
mRubberBand = nullptr;
417393
}
418394

419-
void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset )
395+
void QgsMapToolOffsetCurve::updateGeometryAndRubberBand( double offset )
420396
{
421397
if ( !mRubberBand || mOriginalGeometry.isNull() )
422398
{
423399
return;
424400
}
425401

426-
QgsVectorLayer *sourceLayer = dynamic_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( mSourceLayerId ) );
427-
if ( !sourceLayer )
402+
if ( !mLayer )
428403
{
429404
return;
430405
}
@@ -447,17 +422,81 @@ void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset )
447422
if ( !offsetGeom )
448423
{
449424
deleteRubberBandAndGeometry();
450-
deleteDistanceWidget();
451-
delete mSnapVertexMarker;
452-
mSnapVertexMarker = nullptr;
453-
mCtrlWasHeldOnFeatureSelection = false;
425+
deleteUserInputWidget();
426+
mLayer = nullptr;
454427
mGeometryModified = false;
455-
deleteDistanceWidget();
456428
emit messageEmitted( tr( "Creating offset geometry failed: %1" ).arg( offsetGeom.lastError() ), Qgis::Critical );
457429
}
458430
else
459431
{
460432
mModifiedGeometry = offsetGeom;
461-
mRubberBand->setToGeometry( mModifiedGeometry, sourceLayer );
433+
mRubberBand->setToGeometry( mModifiedGeometry, mLayer );
434+
}
435+
}
436+
437+
438+
// ******************
439+
// Offset User Widget
440+
441+
QgsOffsetUserWidget::QgsOffsetUserWidget( QWidget *parent )
442+
: QWidget( parent )
443+
{
444+
mLayout = new QGridLayout( this );
445+
mLayout->setContentsMargins( 0, 0, 0, 0 );
446+
//mLayout->setAlignment( Qt::AlignLeft );
447+
setLayout( mLayout );
448+
449+
QLabel *lbl = new QLabel( tr( "Offset" ), this );
450+
lbl->setAlignment( Qt::AlignRight | Qt::AlignCenter );
451+
mLayout->addWidget( lbl, 0, 0 );
452+
453+
mOffsetSpinBox = new QgsDoubleSpinBox();
454+
mOffsetSpinBox->setMinimum( -99999999 );
455+
mOffsetSpinBox->setMaximum( 99999999 );
456+
mOffsetSpinBox->setDecimals( 6 );
457+
mOffsetSpinBox->setClearValue( 0.0 );
458+
mOffsetSpinBox->setShowClearButton( false );
459+
mLayout->addWidget( mOffsetSpinBox, 0, 1 );
460+
461+
// connect signals
462+
mOffsetSpinBox->installEventFilter( this );
463+
connect( mOffsetSpinBox, static_cast < void ( QgsDoubleSpinBox::* )( double ) > ( &QgsDoubleSpinBox::valueChanged ), this, &QgsOffsetUserWidget::offsetSpinBoxValueChanged );
464+
465+
// config focus
466+
setFocusProxy( mOffsetSpinBox );
467+
}
468+
469+
void QgsOffsetUserWidget::setOffset( double offset )
470+
{
471+
mOffsetSpinBox->setValue( offset );
472+
}
473+
474+
double QgsOffsetUserWidget::offset()
475+
{
476+
return mOffsetSpinBox->value();
477+
}
478+
479+
bool QgsOffsetUserWidget::eventFilter( QObject *obj, QEvent *ev )
480+
{
481+
if ( obj == mOffsetSpinBox && ev->type() == QEvent::KeyPress )
482+
{
483+
QKeyEvent *event = static_cast<QKeyEvent *>( ev );
484+
if ( event->key() == Qt::Key_Escape )
485+
{
486+
emit offsetEditingCanceled();
487+
return true;
488+
}
489+
if ( event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return )
490+
{
491+
emit offsetEditingFinished( offset(), event->modifiers() );
492+
return true;
493+
}
462494
}
495+
496+
return false;
497+
}
498+
499+
void QgsOffsetUserWidget::offsetSpinBoxValueChanged( double offset )
500+
{
501+
emit offsetChanged( offset );
463502
}

‎src/app/qgsmaptooloffsetcurve.h

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,42 @@
2020
#include "qgsgeometry.h"
2121
#include "qgis_app.h"
2222

23-
class QgsVertexMarker;
23+
class QGridLayout;
24+
25+
class QgsSnapIndicator;
2426
class QgsDoubleSpinBox;
2527
class QGraphicsProxyWidget;
2628

29+
class APP_EXPORT QgsOffsetUserWidget : public QWidget
30+
{
31+
Q_OBJECT
32+
33+
public:
34+
35+
explicit QgsOffsetUserWidget( QWidget *parent = nullptr );
36+
37+
void setOffset( double offset );
38+
39+
double offset();
40+
41+
QgsDoubleSpinBox *editor() {return mOffsetSpinBox;}
42+
43+
signals:
44+
void offsetChanged( double offset );
45+
void offsetEditingFinished( double offset, const Qt::KeyboardModifiers &modifiers );
46+
void offsetEditingCanceled();
47+
48+
protected:
49+
bool eventFilter( QObject *obj, QEvent *ev ) override;
50+
51+
private slots:
52+
void offsetSpinBoxValueChanged( double offset );
53+
54+
private:
55+
QGridLayout *mLayout = nullptr;
56+
QgsDoubleSpinBox *mOffsetSpinBox = nullptr;
57+
};
58+
2759
class APP_EXPORT QgsMapToolOffsetCurve: public QgsMapToolEdit
2860
{
2961
Q_OBJECT
@@ -36,15 +68,23 @@ class APP_EXPORT QgsMapToolOffsetCurve: public QgsMapToolEdit
3668
void canvasMoveEvent( QgsMapMouseEvent *e ) override;
3769

3870
private slots:
39-
//! Places curve offset to value entered in the spin box
40-
void placeOffsetCurveToValue();
71+
//! Places curve offset from the mouse position or from the value entered in the spin box
72+
void updateGeometryAndRubberBand( double offset );
4173

4274
//! Apply the offset either from the spin box or from the mouse event
43-
void applyOffset( bool forceCopy = false );
75+
void applyOffset( const double &offset, const Qt::KeyboardModifiers &modifiers );
76+
77+
void cancel();
4478

4579
private:
4680
//! Rubberband that shows the position of the offset curve
4781
QgsRubberBand *mRubberBand = nullptr;
82+
//! Snapping indicators
83+
std::unique_ptr<QgsSnapIndicator> mSnapIndicator;
84+
85+
//! The layer maniuplated
86+
QgsVectorLayer *mLayer = nullptr;
87+
4888
//! Geometry to manipulate
4989
QgsGeometry mOriginalGeometry;
5090
//! Geometry being manipulated
@@ -53,25 +93,28 @@ class APP_EXPORT QgsMapToolOffsetCurve: public QgsMapToolEdit
5393
QgsGeometry mModifiedGeometry;
5494
//! ID of manipulated feature
5595
QgsFeatureId mModifiedFeature = -1;
56-
//! Layer ID of source layer
57-
QString mSourceLayerId;
96+
bool mMultiPartGeometry = false;
97+
int mModifiedPart = 0;
98+
5899
//! Internal flag to distinguish move from click
59100
bool mGeometryModified = false;
101+
60102
//! Shows current distance value and allows numerical editing
61-
QgsDoubleSpinBox *mDistanceWidget = nullptr;
62-
//! Marker to show the cursor was snapped to another location
63-
QgsVertexMarker *mSnapVertexMarker = nullptr;
103+
QgsOffsetUserWidget *mUserInputWidget = nullptr;
104+
64105
//! Forces geometry copy (no modification of geometry in current layer)
65-
bool mCtrlWasHeldOnFeatureSelection = false;
66-
bool mMultiPartGeometry = false;
67-
int mModifiedPart = 0;
106+
bool mCtrlHeldOnFirstClick = false;
107+
108+
double calculateOffset( QgsPointXY mapPoint );
109+
110+
void createUserInputWidget();
111+
void deleteUserInputWidget();
68112

69113
void prepareGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature );
70114

71115
void deleteRubberBandAndGeometry();
72-
void createDistanceWidget();
73-
void deleteDistanceWidget();
74-
void setOffsetForRubberBand( double offset );
116+
117+
75118
//! Creates a linestring from the polygon ring containing the snapped vertex. Caller takes ownership of the created object
76119
QgsGeometry linestringFromPolygon( const QgsGeometry &featureGeom, int vertex );
77120
//! Returns a single line from a multiline (or does nothing if geometry is already a single line). Deletes the input geometry

0 commit comments

Comments
 (0)
Please sign in to comment.