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