Skip to content

Commit 2c65dbc

Browse files
authoredNov 29, 2017
[offset curve tool] fix two serious issues (#5760)
- broken polygon support (fixes #15222) - lost of parts when using tool with mutlipart features
1 parent e0290a9 commit 2c65dbc

File tree

4 files changed

+111
-122
lines changed

4 files changed

+111
-122
lines changed
 

‎src/app/qgisapp.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11404,7 +11404,6 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
1140411404
mActionDeleteRing->setEnabled( false );
1140511405
mActionDeletePart->setEnabled( false );
1140611406
mActionReshapeFeatures->setEnabled( false );
11407-
mActionOffsetCurve->setEnabled( false );
1140811407
mActionSplitFeatures->setEnabled( false );
1140911408
mActionSplitParts->setEnabled( false );
1141011409
mActionMergeFeatures->setEnabled( false );
@@ -11627,7 +11626,7 @@ void QgisApp::activateDeactivateLayerRelatedActions( QgsMapLayer *layer )
1162711626
mActionSplitParts->setEnabled( isEditable && canChangeGeometry && isMultiPart );
1162811627
mActionSimplifyFeature->setEnabled( isEditable && canChangeGeometry );
1162911628
mActionDeleteRing->setEnabled( isEditable && canChangeGeometry );
11630-
mActionOffsetCurve->setEnabled( false );
11629+
mActionOffsetCurve->setEnabled( isEditable && canAddFeatures && canChangeAttributes );
1163111630
}
1163211631
else if ( vlayer->geometryType() == QgsWkbTypes::NullGeometry )
1163311632
{

‎src/app/qgsmaptooloffsetcurve.cpp

Lines changed: 105 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
9393
// setup new settings (temporary)
9494
QgsSettings settings;
9595
config.setEnabled( true );
96-
config.setMode( QgsSnappingConfig::AllLayers );
96+
config.setMode( QgsSnappingConfig::ActiveLayer );
9797
config.setType( QgsSnappingConfig::Segment );
9898
config.setTolerance( settings.value( QStringLiteral( "qgis/digitizing/search_radius_vertex_edit" ), 10 ).toDouble() );
9999
config.setUnits( static_cast<QgsTolerance::UnitType>( settings.value( QStringLiteral( "qgis/digitizing/search_radius_vertex_edit_unit" ), QgsTolerance::Pixels ).toInt() ) );
@@ -111,11 +111,11 @@ void QgsMapToolOffsetCurve::canvasReleaseEvent( QgsMapMouseEvent *e )
111111
if ( match.layer()->getFeatures( QgsFeatureRequest( match.featureId() ) ).nextFeature( fet ) )
112112
{
113113
mCtrlWasHeldOnFeatureSelection = ( e->modifiers() & Qt::ControlModifier ); //no geometry modification if ctrl is pressed
114-
mOriginalGeometry = createOriginGeometry( match.layer(), match, fet );
114+
prepareGeometry( match.layer(), match, fet );
115115
mRubberBand = createRubberBand();
116116
if ( mRubberBand )
117117
{
118-
mRubberBand->setToGeometry( mOriginalGeometry, layer );
118+
mRubberBand->setToGeometry( mManipulatedGeometry, layer );
119119
}
120120
mModifiedFeature = fet.id();
121121
createDistanceWidget();
@@ -155,7 +155,56 @@ void QgsMapToolOffsetCurve::applyOffset( bool forceCopy )
155155

156156
if ( mMultiPartGeometry )
157157
{
158-
mModifiedGeometry.convertToMultiType();
158+
QgsGeometry geometry;
159+
int partIndex = 0;
160+
QgsWkbTypes::Type geomType = mOriginalGeometry.wkbType();
161+
if ( QgsWkbTypes::geometryType( geomType ) == QgsWkbTypes::LineGeometry )
162+
{
163+
QgsMultiPolylineXY newMultiLine;
164+
QgsMultiPolylineXY multiLine = mOriginalGeometry.asMultiPolyline();
165+
QgsMultiPolylineXY::const_iterator it = multiLine.constBegin();
166+
for ( ; it != multiLine.constEnd(); ++it )
167+
{
168+
if ( partIndex == mModifiedPart )
169+
{
170+
newMultiLine.append( mModifiedGeometry.asPolyline() );
171+
}
172+
else
173+
{
174+
newMultiLine.append( *it );
175+
}
176+
partIndex++;
177+
}
178+
geometry = QgsGeometry::fromMultiPolylineXY( newMultiLine );
179+
}
180+
else
181+
{
182+
QgsMultiPolygonXY newMultiPoly;
183+
QgsMultiPolygonXY multiPoly = mOriginalGeometry.asMultiPolygon();
184+
QgsMultiPolygonXY::const_iterator multiPolyIt = multiPoly.constBegin();
185+
for ( ; multiPolyIt != multiPoly.constEnd(); ++multiPolyIt )
186+
{
187+
if ( partIndex == mModifiedPart )
188+
{
189+
if ( mModifiedGeometry.isMultipart() )
190+
{
191+
newMultiPoly += mModifiedGeometry.asMultiPolygon();
192+
}
193+
else
194+
{
195+
newMultiPoly.append( mModifiedGeometry.asPolygon() );
196+
}
197+
}
198+
else
199+
{
200+
newMultiPoly.append( *multiPolyIt );
201+
}
202+
partIndex++;
203+
}
204+
geometry = QgsGeometry::fromMultiPolygonXY( newMultiPoly );
205+
}
206+
geometry.convertToMultiType();
207+
mModifiedGeometry = geometry;
159208
}
160209

161210
layer->beginEditCommand( tr( "Offset curve" ) );
@@ -248,73 +297,79 @@ void QgsMapToolOffsetCurve::canvasMoveEvent( QgsMapMouseEvent *e )
248297
{
249298
return;
250299
}
251-
300+
offset = leftOf < 0 ? offset : -offset;
252301

253302

254303
if ( mDistanceWidget )
255304
{
256305
// this will also set the rubber band
257-
mDistanceWidget->setValue( leftOf < 0 ? offset : -offset );
306+
mDistanceWidget->setValue( offset );
258307
mDistanceWidget->setFocus( Qt::TabFocusReason );
259308
}
260309
else
261310
{
262311
//create offset geometry using geos
263-
setOffsetForRubberBand( leftOf < 0 ? offset : -offset );
312+
setOffsetForRubberBand( offset );
264313
}
265314
}
266315

267-
QgsGeometry QgsMapToolOffsetCurve::createOriginGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature )
316+
void QgsMapToolOffsetCurve::prepareGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature )
268317
{
269318
if ( !vl )
270319
{
271-
return QgsGeometry();
320+
return;
272321
}
273322

323+
mOriginalGeometry = QgsGeometry();
324+
mManipulatedGeometry = QgsGeometry();
274325
mMultiPartGeometry = false;
275-
//assign feature part by vertex number (snap to vertex) or by before vertex number (snap to segment)
276-
int partVertexNr = match.vertexIndex();
326+
mModifiedPart = 0;
277327

278-
if ( vl == currentVectorLayer() && !mCtrlWasHeldOnFeatureSelection )
328+
//assign feature part by vertex number (snap to vertex) or by before vertex number (snap to segment)
329+
QgsGeometry geom = snappedFeature.geometry();
330+
if ( geom.isNull() )
279331
{
280-
//don't consider selected geometries, only the snap result
281-
return convertToSingleLine( snappedFeature.geometry(), partVertexNr, mMultiPartGeometry );
332+
return;
282333
}
283-
else //snapped to a background layer
334+
mOriginalGeometry = geom;
335+
336+
QgsWkbTypes::Type geomType = geom.wkbType();
337+
if ( QgsWkbTypes::geometryType( geomType ) == QgsWkbTypes::LineGeometry )
284338
{
285-
//if source layer is polygon / multipolygon, create a linestring from the snapped ring
286-
if ( vl->geometryType() == QgsWkbTypes::PolygonGeometry )
339+
if ( !geom.isMultipart() )
287340
{
288-
//make linestring from polygon ring and return this geometry
289-
return linestringFromPolygon( snappedFeature.geometry(), partVertexNr );
341+
mManipulatedGeometry = geom;
290342
}
343+
else
344+
{
345+
mMultiPartGeometry = true;
346+
347+
int vertex = match.vertexIndex();
348+
QgsVertexId vertexId;
349+
geom.vertexIdFromVertexNr( vertex, vertexId );
350+
mModifiedPart = vertexId.part;
291351

292-
//for background layers, try to merge selected entries together if snapped feature is contained in selection
293-
const QgsFeatureIds &selection = vl->selectedFeatureIds();
294-
if ( selection.empty() || !selection.contains( match.featureId() ) )
352+
QgsMultiPolylineXY multiLine = geom.asMultiPolyline();
353+
mManipulatedGeometry = QgsGeometry::fromPolylineXY( multiLine.at( mModifiedPart ) );
354+
}
355+
}
356+
else if ( QgsWkbTypes::geometryType( geomType ) == QgsWkbTypes::PolygonGeometry )
357+
{
358+
if ( !geom.isMultipart() )
295359
{
296-
return convertToSingleLine( snappedFeature.geometry(), partVertexNr, mMultiPartGeometry );
360+
mManipulatedGeometry = geom;
297361
}
298362
else
299363
{
300-
//merge together if several features
301-
QgsFeatureList selectedFeatures = vl->selectedFeatures();
302-
QgsFeatureList::iterator selIt = selectedFeatures.begin();
303-
QgsGeometry geom = selIt->geometry();
304-
++selIt;
305-
for ( ; selIt != selectedFeatures.end(); ++selIt )
306-
{
307-
geom = geom.combine( selIt->geometry() );
308-
}
364+
mMultiPartGeometry = true;
309365

310-
//if multitype, return only the snapped to geometry
311-
if ( geom.isMultipart() )
312-
{
313-
return convertToSingleLine( snappedFeature.geometry(),
314-
match.vertexIndex(), mMultiPartGeometry );
315-
}
366+
int vertex = match.vertexIndex();
367+
QgsVertexId vertexId;
368+
geom.vertexIdFromVertexNr( vertex, vertexId );
369+
mModifiedPart = vertexId.part;
316370

317-
return geom;
371+
QgsMultiPolygonXY multiPoly = geom.asMultiPolygon();
372+
mManipulatedGeometry = QgsGeometry::fromPolygonXY( multiPoly.at( mModifiedPart ) );
318373
}
319374
}
320375
}
@@ -356,6 +411,7 @@ void QgsMapToolOffsetCurve::deleteDistanceWidget()
356411
void QgsMapToolOffsetCurve::deleteRubberBandAndGeometry()
357412
{
358413
mOriginalGeometry.set( nullptr );
414+
mManipulatedGeometry.set( nullptr );
359415
delete mRubberBand;
360416
mRubberBand = nullptr;
361417
}
@@ -378,7 +434,16 @@ void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset )
378434
int quadSegments = s.value( QStringLiteral( "/qgis/digitizing/offset_quad_seg" ), 8 ).toInt();
379435
double miterLimit = s.value( QStringLiteral( "/qgis/digitizing/offset_miter_limit" ), 5.0 ).toDouble();
380436

381-
QgsGeometry offsetGeom = mOriginalGeometry.offsetCurve( offset, quadSegments, joinStyle, miterLimit );
437+
QgsGeometry offsetGeom;
438+
if ( QgsWkbTypes::geometryType( mOriginalGeometry.wkbType() ) == QgsWkbTypes::LineGeometry )
439+
{
440+
offsetGeom = mManipulatedGeometry.offsetCurve( offset, quadSegments, joinStyle, miterLimit );
441+
}
442+
else
443+
{
444+
offsetGeom = mManipulatedGeometry.buffer( offset, quadSegments, QgsGeometry::CapRound, joinStyle, miterLimit );
445+
}
446+
382447
if ( !offsetGeom )
383448
{
384449
deleteRubberBandAndGeometry();
@@ -396,80 +461,3 @@ void QgsMapToolOffsetCurve::setOffsetForRubberBand( double offset )
396461
mRubberBand->setToGeometry( mModifiedGeometry, sourceLayer );
397462
}
398463
}
399-
400-
QgsGeometry QgsMapToolOffsetCurve::linestringFromPolygon( const QgsGeometry &featureGeom, int vertex )
401-
{
402-
if ( featureGeom.isNull() )
403-
{
404-
return QgsGeometry();
405-
}
406-
407-
QgsWkbTypes::Type geomType = featureGeom.wkbType();
408-
int currentVertex = 0;
409-
QgsMultiPolygonXY multiPoly;
410-
411-
if ( geomType == QgsWkbTypes::Polygon || geomType == QgsWkbTypes::Polygon25D )
412-
{
413-
QgsPolygonXY polygon = featureGeom.asPolygon();
414-
multiPoly.append( polygon );
415-
}
416-
else if ( geomType == QgsWkbTypes::MultiPolygon || geomType == QgsWkbTypes::MultiPolygon25D )
417-
{
418-
//iterate all polygons / rings
419-
multiPoly = featureGeom.asMultiPolygon();
420-
}
421-
else
422-
{
423-
return QgsGeometry();
424-
}
425-
426-
QgsMultiPolygonXY::const_iterator multiPolyIt = multiPoly.constBegin();
427-
for ( ; multiPolyIt != multiPoly.constEnd(); ++multiPolyIt )
428-
{
429-
QgsPolygonXY::const_iterator polyIt = multiPolyIt->constBegin();
430-
for ( ; polyIt != multiPolyIt->constEnd(); ++polyIt )
431-
{
432-
currentVertex += polyIt->size();
433-
if ( vertex < currentVertex )
434-
{
435-
//found, return ring
436-
return QgsGeometry::fromPolylineXY( *polyIt );
437-
}
438-
}
439-
}
440-
441-
return QgsGeometry();
442-
}
443-
444-
445-
QgsGeometry QgsMapToolOffsetCurve::convertToSingleLine( const QgsGeometry &geom, int vertex, bool &isMulti )
446-
{
447-
if ( geom.isNull() )
448-
{
449-
return QgsGeometry();
450-
}
451-
452-
isMulti = false;
453-
QgsWkbTypes::Type geomType = geom.wkbType();
454-
if ( geomType == QgsWkbTypes::LineString || geomType == QgsWkbTypes::LineString25D )
455-
{
456-
return geom;
457-
}
458-
else if ( geomType == QgsWkbTypes::MultiLineString || geomType == QgsWkbTypes::MultiLineString25D )
459-
{
460-
//search vertex
461-
isMulti = true;
462-
int currentVertex = 0;
463-
QgsMultiPolylineXY multiLine = geom.asMultiPolyline();
464-
QgsMultiPolylineXY::const_iterator it = multiLine.constBegin();
465-
for ( ; it != multiLine.constEnd(); ++it )
466-
{
467-
currentVertex += it->size();
468-
if ( vertex < currentVertex )
469-
{
470-
return QgsGeometry::fromPolylineXY( *it );
471-
}
472-
}
473-
}
474-
return QgsGeometry();
475-
}

‎src/app/qgsmaptooloffsetcurve.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class APP_EXPORT QgsMapToolOffsetCurve: public QgsMapToolEdit
4747
QgsRubberBand *mRubberBand = nullptr;
4848
//! Geometry to manipulate
4949
QgsGeometry mOriginalGeometry;
50+
//! Geometry being manipulated
51+
QgsGeometry mManipulatedGeometry;
5052
//! Geometry after manipulation
5153
QgsGeometry mModifiedGeometry;
5254
//! ID of manipulated feature
@@ -62,17 +64,18 @@ class APP_EXPORT QgsMapToolOffsetCurve: public QgsMapToolEdit
6264
//! Forces geometry copy (no modification of geometry in current layer)
6365
bool mCtrlWasHeldOnFeatureSelection = false;
6466
bool mMultiPartGeometry = false;
67+
int mModifiedPart = 0;
6568

69+
void prepareGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature );
6670

6771
void deleteRubberBandAndGeometry();
68-
QgsGeometry createOriginGeometry( QgsVectorLayer *vl, const QgsPointLocator::Match &match, QgsFeature &snappedFeature );
6972
void createDistanceWidget();
7073
void deleteDistanceWidget();
7174
void setOffsetForRubberBand( double offset );
7275
//! Creates a linestring from the polygon ring containing the snapped vertex. Caller takes ownership of the created object
7376
QgsGeometry linestringFromPolygon( const QgsGeometry &featureGeom, int vertex );
7477
//! Returns a single line from a multiline (or does nothing if geometry is already a single line). Deletes the input geometry
75-
QgsGeometry convertToSingleLine( const QgsGeometry &geom, int vertex, bool &isMulti );
78+
QgsGeometry convertToSingleLine( const QgsGeometry &geom, int vertex );
7679
};
7780

7881
#endif // QGSMAPTOOLOFFSETCURVE_H

‎src/gui/qgsrubberband.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,6 @@ void QgsRubberBand::addGeometry( const QgsGeometry &geometry, const QgsCoordinat
333333
case QgsWkbTypes::MultiPolygon:
334334
case QgsWkbTypes::MultiPolygon25D:
335335
{
336-
337336
const QgsMultiPolygonXY multipoly = geom.asMultiPolygon();
338337
for ( const QgsPolygonXY &poly : multipoly )
339338
{

0 commit comments

Comments
 (0)
Please sign in to comment.