1 /***************************************************************************
2 qgsmaptoolreverseline.cpp - reverse a line geometry
3 ---------------------
4 begin : April 2018
5 copyright : (C) 2018 by Loïc Bartoletti
6 email : loic dot bartoletti at oslandia dot com
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
16 #include "qgsmaptoolreverseline.h"
17
18 #include "qgsfeatureiterator.h"
19 #include "qgsmapcanvas.h"
20 #include "qgsvertexmarker.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsgeometry.h"
23 #include "qgsrubberband.h"
24 #include "qgssnappingutils.h"
25 #include "qgstolerance.h"
26 #include "qgisapp.h"
27 #include "qgslinestring.h"
28 #include "qgsmultilinestring.h"
29 #include "qgsmapmouseevent.h"
30
31
QgsMapToolReverseLine(QgsMapCanvas * canvas)32 QgsMapToolReverseLine::QgsMapToolReverseLine( QgsMapCanvas *canvas )
33 : QgsMapToolEdit( canvas )
34 {
35 mToolName = tr( "Reverse line geometry" );
36 }
37
~QgsMapToolReverseLine()38 QgsMapToolReverseLine::~QgsMapToolReverseLine()
39 {
40 }
41
canvasMoveEvent(QgsMapMouseEvent * e)42 void QgsMapToolReverseLine::canvasMoveEvent( QgsMapMouseEvent *e )
43 {
44 Q_UNUSED( e )
45 //nothing to do
46 }
47
canvasPressEvent(QgsMapMouseEvent * e)48 void QgsMapToolReverseLine::canvasPressEvent( QgsMapMouseEvent *e )
49 {
50 mPressedFid = -1;
51 mPressedPartNum = -1;
52
53 QgsMapLayer *currentLayer = mCanvas->currentLayer();
54 if ( !currentLayer )
55 return;
56
57 vlayer = qobject_cast<QgsVectorLayer *>( currentLayer );
58 if ( !vlayer )
59 {
60 notifyNotVectorLayer();
61 return;
62 }
63
64 if ( !vlayer->isEditable() )
65 {
66 notifyNotEditableLayer();
67 return;
68 }
69
70 QgsGeometry geomPart = partUnderPoint( e->pos(), mPressedFid, mPressedPartNum );
71
72 if ( mPressedFid != -1 )
73 {
74 mRubberBand.reset( createRubberBand( vlayer->geometryType() ) );
75
76 mRubberBand->setToGeometry( geomPart, vlayer );
77 mRubberBand->show();
78 }
79
80 }
81
canvasReleaseEvent(QgsMapMouseEvent * e)82 void QgsMapToolReverseLine::canvasReleaseEvent( QgsMapMouseEvent *e )
83 {
84 Q_UNUSED( e )
85
86 if ( !vlayer || !vlayer->isEditable() )
87 {
88 return;
89 }
90
91 if ( mPressedFid == -1 )
92 return;
93
94 QgsFeature f;
95 vlayer->getFeatures( QgsFeatureRequest().setFilterFid( mPressedFid ) ).nextFeature( f );
96 QgsGeometry geom;
97
98 if ( f.hasGeometry() )
99 {
100 if ( f.geometry().isMultipart() )
101 {
102 std::unique_ptr<QgsMultiCurve> line_reversed( static_cast<QgsMultiCurve * >( f.geometry().constGet()->clone() ) );
103 std::unique_ptr<QgsCurve> line_part( line_reversed->curveN( mPressedPartNum )->clone() );
104 std::unique_ptr<QgsCurve> line_part_reversed( line_part->reversed() );
105 line_reversed->removeGeometry( mPressedPartNum );
106 line_reversed->insertGeometry( line_part_reversed.release(), mPressedPartNum );
107
108 geom = QgsGeometry( line_reversed.release() );
109
110 }
111 else
112 {
113
114 geom = QgsGeometry( static_cast< const QgsCurve * >( f.geometry().constGet() )->reversed() );
115
116 }
117
118 if ( !geom.isNull() )
119 {
120 vlayer->beginEditCommand( tr( "Reverse line" ) );
121 vlayer->changeGeometry( f.id(), geom );
122 vlayer->endEditCommand();
123 vlayer->triggerRepaint();
124 emit messageEmitted( tr( "Line reversed." ) );
125 }
126 else
127 {
128 emit messageEmitted( tr( "Couldn't reverse the selected part." ) );
129 }
130 }
131 mRubberBand.reset();
132 }
133
partUnderPoint(QPoint point,QgsFeatureId & fid,int & partNum)134 QgsGeometry QgsMapToolReverseLine::partUnderPoint( QPoint point, QgsFeatureId &fid, int &partNum )
135 {
136 QgsFeature f;
137 QgsGeometry geomPart;
138
139 switch ( vlayer->geometryType() )
140 {
141 case QgsWkbTypes::LineGeometry:
142 {
143 QgsPointLocator::Match match = mCanvas->snappingUtils()->snapToCurrentLayer( point, QgsPointLocator::Types( QgsPointLocator::Vertex | QgsPointLocator::Edge ) );
144 if ( !match.isValid() )
145 return geomPart;
146
147 int snapVertex = match.vertexIndex();
148 vlayer->getFeatures( QgsFeatureRequest().setFilterFid( match.featureId() ) ).nextFeature( f );
149 QgsGeometry g = f.geometry();
150 if ( !g.isMultipart() )
151 {
152 fid = match.featureId();
153 return g;
154 }
155 else if ( QgsWkbTypes::geometryType( g.wkbType() ) == QgsWkbTypes::LineGeometry )
156 {
157 QgsMultiPolylineXY mline = g.asMultiPolyline();
158 for ( int part = 0; part < mline.count(); part++ )
159 {
160 if ( snapVertex < mline[part].count() )
161 {
162 fid = match.featureId();
163 partNum = part;
164 return QgsGeometry::fromPolylineXY( mline[part] );
165 }
166 snapVertex -= mline[part].count();
167 }
168 }
169 break;
170 }
171 default:
172 {
173 break;
174 }
175 }
176 return geomPart;
177 }
178
deactivate()179 void QgsMapToolReverseLine::deactivate()
180 {
181 QgsMapTool::deactivate();
182 }
183
184