Exemplo n.º 1
0
class CenterlineDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)

        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())
        self.rubberBandSelectedSegment = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedSegment.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedSegment.setWidth(2)

        self.defaultIndex = None
        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.snappingResultFirstEdge = None
        self.snappingResultSecondEdge = None
        self.defaultIndex = None

        self.rubberBandSelectedSegment.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetCenterline(self):
        self.step = 0
        self.snappingResultFirstEdge = None
        self.snappingResultSecondEdge = None
        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.segmentFinderTool.layers = None

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showButton("Centerline",
                                            "Select first edge",
                                            "Done",
                                            buttonCallback=self.done)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.firstEdgeFound)
        elif self.step == 1:
            candidates, candidateLayers, defaultIndex = self.listCandidateLayers(
            )
            if self.defaultIndex <> None and defaultIndex < len(
                    candidateLayers):
                defaultIndex = self.defaultIndex
            _, combobox = self.messageBarUtils.showCombobox(
                "Centerline", "Select second edge", candidates, defaultIndex)
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(
                partial(self.secondEdgeFound, candidateLayers, combobox))
        elif self.step == 2:
            self.doCenterline()

    def firstEdgeFound(self, result, pointClicked):
        self.snappingResultFirstEdge = result

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedSegment.addPoint(result.beforeVertex)
        self.rubberBandSelectedSegment.addPoint(result.afterVertex, True)
        self.rubberBandSelectedSegment.show()

        self.step = 1
        self.next()

    def secondEdgeFound(self, candidateLayers, combobox, result, pointClicked):
        index = combobox.currentIndex()
        self.defaultIndex = index
        self.outputLayer = candidateLayers[index]

        self.snappingResultSecondEdge = result

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        self.step = 2
        self.next()

    def listCandidateLayers(self, onlyEditable=True):
        candidates = []
        candidateLayers = []
        defaultIndex = 0
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            if (layer.type() == QgsMapLayer.VectorLayer
                    and layer.geometryType() == QGis.Line
                    and (not onlyEditable or layer.isEditable())):
                candidates.append(layer.name())
                candidateLayers.append(layer)
        sortedCandidates = sorted(enumerate(candidates), key=itemgetter(1))
        candidates = map(itemgetter(1), sortedCandidates)
        candidateLayers = map(lambda x: candidateLayers[x[0]],
                              sortedCandidates)
        for index in range(len(candidateLayers)):
            if candidateLayers[index] == self.layer:
                defaultIndex = index
        return candidates, candidateLayers, defaultIndex

    def doCenterline(self):
        self.messageBarUtils.showMessage("Centerline",
                                         "Running...",
                                         duration=0)
        try:
            qDebug("New Centerline")

            firstSegment = self._getSegment(self.snappingResultFirstEdge)
            secondSegment = self._getSegment(self.snappingResultSecondEdge)
            endSegments = self._getEndSegments(firstSegment, secondSegment)

            # middle of each end segments
            centerlineEndPoint1 = QgsPoint(
                (endSegments[0].vertexAt(0).x() +
                 endSegments[0].vertexAt(1).x()) / 2,
                (endSegments[0].vertexAt(0).y() +
                 endSegments[0].vertexAt(1).y()) / 2)
            centerlineEndPoint2 = QgsPoint(
                (endSegments[1].vertexAt(0).x() +
                 endSegments[1].vertexAt(1).x()) / 2,
                (endSegments[1].vertexAt(0).y() +
                 endSegments[1].vertexAt(1).y()) / 2)
            line = QgsGeometry.fromPolyline(
                [centerlineEndPoint1, centerlineEndPoint2])
            self.outputLayer.beginEditCommand("Centerline")
            feature = QgsFeature()
            feature.initAttributes(
                self.outputLayer.dataProvider().fields().count())
            feature.setGeometry(line)
            self.outputLayer.addFeature(feature)

            self.outputLayer.endEditCommand()

            self.iface.mapCanvas().refresh()

            self.messageBarUtils.showMessage(
                "Centerline",
                "The centerline was created successfully",
                duration=2)
        except Exception as e:
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Centerline",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.outputLayer.destroyEditCommand()
            self.done()

        self.resetCenterline()
        self.next()

    def _getEndSegments(self, firstSegment, secondSegment):
        candidates1 = (QgsGeometry.fromPolyline(
            [firstSegment.vertexAt(0),
             secondSegment.vertexAt(0)]),
                       QgsGeometry.fromPolyline([
                           firstSegment.vertexAt(1),
                           secondSegment.vertexAt(1)
                       ]))
        candidates2 = (QgsGeometry.fromPolyline(
            [firstSegment.vertexAt(0),
             secondSegment.vertexAt(1)]),
                       QgsGeometry.fromPolyline([
                           firstSegment.vertexAt(1),
                           secondSegment.vertexAt(0)
                       ]))
        if candidates1[0].intersects(candidates1[1]):
            return candidates2
        else:
            return candidates1

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = snappingResult.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv, av])

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.done()
                return

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)
Exemplo n.º 2
0
class CenterlineAdvancedDigitizingMode(QgsMapToolEmitPoint):
    def __init__(self, iface):
        self.iface = iface
        QgsMapToolEmitPoint.__init__(self, self.iface.mapCanvas())

        self.messageBarUtils = MessageBarUtils(iface)
        self.segmentFinderTool = SegmentFinderTool(self.iface.mapCanvas())

        # just needs to rubberbands (when selecting 2nd segment, replaced by selectedLine or
        # hidden)
        self.rubberBandSelectedSegment = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedSegment.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedSegment.setWidth(2)

        self.rubberBandSelectedLine = QgsRubberBand(self.iface.mapCanvas())
        self.rubberBandSelectedLine.setColor(QColor(255, 0, 0))
        self.rubberBandSelectedLine.setWidth(2)

        self.defaultIndex = None
        self.layer = None
        self.reset()

    def setLayer(self, layer):
        self.layer = layer

    def reset(self, clearMessages=True):
        self.step = 0
        self.snappingResults = []
        self.snappingPointsClicked = []
        self.subPolylines = []
        self.defaultIndex = None
        self.orderSelection = False

        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedLine.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        if clearMessages:
            self.messageBarUtils.removeAllMessages()

    def resetCenterline(self):
        self.step = 0
        self.snappingResults = []
        self.snappingPointsClicked = []
        self.subPolylines = []
        self.rubberBandSelectedSegment.reset(QGis.Line)
        self.rubberBandSelectedLine.reset(QGis.Line)

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

    def deactivate(self):
        self.reset()
        QgsMapToolEmitPoint.deactivate(self)

    def next(self):
        if self.step == 0:
            self.messageBarUtils.showButton(
                "Centerline",
                "Select starting segment of first line",
                "Done",
                buttonCallback=self.done)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 1:
            self.messageBarUtils.showMessage(
                "Centerline", "Select end segment of first line", duration=0)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 2:
            self.messageBarUtils.showMessage(
                "Centerline",
                "Select starting segment of second line",
                duration=0)
            _, candidateLayers, _ = self.listCandidateLayers(
                onlyEditable=False)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(self.edgeFound)
        elif self.step == 3:
            candidates, candidateLayers, defaultIndex = self.listCandidateLayers(
                onlyEditable=False)
            if self.defaultIndex <> None and defaultIndex < len(
                    candidateLayers):
                defaultIndex = self.defaultIndex
            _, combobox = self.messageBarUtils.showCombobox(
                "Centerline", "Select end segment of second line", candidates,
                defaultIndex)
            self.segmentFinderTool.layers = candidateLayers
            self.currentMapTool = self.segmentFinderTool
            self.segmentFinderTool.segmentFound.connect(
                partial(self.lastEdgeFound, candidateLayers, combobox))
        elif self.step == 4:
            self.doCenterline()

    def edgeFound(self, result, pointClicked):

        try:
            self.segmentFinderTool.segmentFound.disconnect()
        except Exception:
            pass

        self.segmentFinderTool.deactivate()

        if self.step == 0 or self.step == 2:
            self.snappingResults.append(result)
            self.snappingPointsClicked.append(pointClicked)

            self.rubberBandSelectedSegment.reset(QGis.Line)
            self.rubberBandSelectedSegment.addPoint(result.beforeVertex)
            self.rubberBandSelectedSegment.addPoint(result.afterVertex, True)
            self.rubberBandSelectedSegment.show()

            self.step += 1
        else:
            # step 1 or 3
            p = self._getSubPolyline(self.snappingResults[-1], result,
                                     self.snappingPointsClicked[0])
            if p:
                self.subPolylines.append(p)

                self.snappingResults.append(result)
                self.snappingPointsClicked.append(pointClicked)

                self.rubberBandSelectedSegment.reset(QGis.Line)
                self.rubberBandSelectedLine.reset(QGis.Line)

                if self.step == 1:
                    # don't draw when step is 3
                    polyline = QgsGeometry.fromPolyline(p)
                    self.rubberBandSelectedLine.setToGeometry(
                        polyline, self.snappingResults[0].layer)
                    self.rubberBandSelectedLine.show()

                self.step += 1
            else:
                # not valid choice for second segment: not on same layer or feature or subpolyline (if multipolyline)
                self.messageBarUtils.showMessage(
                    "Centerline",
                    "Invalid choice for second segment: Select a segment in the same part of the same geometry as the first",
                    QgsMessageBar.WARNING,
                    duration=5)

        self.next()

    def lastEdgeFound(self, candidateLayers, combobox, result, pointClicked):
        try:
            index = combobox.currentIndex()
            self.defaultIndex = index
            self.outputLayer = candidateLayers[index]
        except:
            # can happen in message with comobobox is closed by the user
            self.outputLayer = candidateLayers[self.defaultIndex]

        self.edgeFound(result, pointClicked)

    def listCandidateLayers(self, onlyEditable=True):
        candidates = []
        candidateLayers = []
        defaultIndex = 0
        for layer in QgsMapLayerRegistry.instance().mapLayers().values():
            if (layer.type() == QgsMapLayer.VectorLayer
                    and layer.geometryType() == QGis.Line
                    and (not onlyEditable or layer.isEditable())):
                candidates.append(layer.name())
                candidateLayers.append(layer)
        sortedCandidates = sorted(enumerate(candidates), key=itemgetter(1))
        candidates = map(itemgetter(1), sortedCandidates)
        candidateLayers = map(lambda x: candidateLayers[x[0]],
                              sortedCandidates)
        for index in range(len(candidateLayers)):
            if candidateLayers[index] == self.layer:
                defaultIndex = index
        return candidates, candidateLayers, defaultIndex

    def doCenterline(self):
        self.messageBarUtils.showMessage("Centerline",
                                         "Running...",
                                         duration=0)
        try:
            qDebug("New Centerline Advanced")

            p1 = self.subPolylines[0]
            p2 = self.subPolylines[1]

            if self._mustReverse(p1, p2):
                # reverse the node order of any of the 2
                p1 = list(reversed(p1))

            # both input geometries and output layer are in same CRS
            if len(p1) == len(p2):
                p = self._processSimple(p1, p2)
            else:
                # voronoi with post processing
                p = self._processVoronoiPolygons(p1, p2)

            if p:
                self.outputLayer.beginEditCommand("Centerline")

                fieldCount = self.outputLayer.dataProvider().fields().count()
                feature = QgsFeature()
                feature.initAttributes(fieldCount)
                feature.setGeometry(p)
                self.outputLayer.addFeature(feature)

                self.outputLayer.endEditCommand()
                self.iface.mapCanvas().refresh()

                self.messageBarUtils.showMessage(
                    "Centerline",
                    "The centerline was created successfully",
                    duration=2)
            else:
                self.messageBarUtils.showMessage("Centerline",
                                                 "No centerline was created",
                                                 QgsMessageBar.WARNING,
                                                 duration=2)

        except Exception as e:
            raise
            QgsMessageLog.logMessage(repr(e))
            self.messageBarUtils.showMessage(
                "Centerline",
                "There was an error performing this command. See QGIS Message log for details",
                QgsMessageBar.CRITICAL,
                duration=5)
            self.outputLayer.destroyEditCommand()
            self.done()

        self.resetCenterline()
        self.next()

    def _processSimple(self, p1, p2):
        #just iterate
        p = []
        for i in range(len(p1)):
            endSegment = [p1[i], p2[i]]
            # middle of each end segments
            centerlineEndPoint = QgsPoint(
                (endSegment[0].x() + endSegment[1].x()) / 2,
                (endSegment[0].y() + endSegment[1].y()) / 2)
            p.append(centerlineEndPoint)
        return QgsGeometry.fromPolyline(p)

    def _processVoronoiPolygons(self, p1, p2):
        tempDir = None
        try:
            # define a polygon formed by p1 and p2: add edge at both ends to
            # close it. P1 and p2 have already been ordered to face each other
            # in the same direction
            ring = p1[0:]
            ring.extend([p1[-1], p2[-1]])
            ring.extend(p2[-1::-1])
            ring.extend([p2[0], p1[0]])
            pipePolygon = QgsGeometry.fromPolygon([ring])
            end1 = QgsGeometry.fromPolyline([p1[-1], p2[-1]])
            end2 = QgsGeometry.fromPolyline([p2[0], p1[0]])

            # create temp shp
            tempDir = tempfile.mkdtemp()
            vl = QgsVectorLayer(
                "LineString?crs=%s" % self.outputLayer.crs().toWkt(), "temp",
                "memory")
            pr = vl.dataProvider()
            vl.startEditing()
            pr.addAttributes([QgsField("id", QVariant.Int, "Integer")])
            vl.commitChanges()

            vl.startEditing()
            for p in [p1, p2]:
                feature = QgsFeature()
                feature.initAttributes(1)
                feature.setGeometry(QgsGeometry.fromPolyline(p))
                vl.addFeature(feature)
            vl.commitChanges()

            qDebug("mem " + repr(vl.featureCount()))

            inputShp = os.path.join(tempDir, "input.shp")
            QgsVectorFileWriter.writeAsVectorFormat(vl, inputShp, "UTF-8",
                                                    vl.crs())

            #             vl=QgsVectorLayer("Polygon?crs=%s" % self.outputLayer.crs().toWkt(), "temp3", "memory")
            #             pr = vl.dataProvider()
            #             vl.startEditing()
            #             pr.addAttributes([QgsField("id",QVariant.Int,"Integer")])
            #             vl.commitChanges()
            #             vl.startEditing()
            #             feature=QgsFeature()
            #             feature.initAttributes(1)
            #             feature.setGeometry(pipePolygon)
            #             vl.addFeature(feature)
            #             vl.commitChanges()
            # QgsMapLayerRegistry.instance().addMapLayer(vl)

            # processing often fails randomly
            # try multiple times
            trials = 0
            success = False
            while trials < 3 and not success:
                try:
                    output = processing.runalg("qgis:densifygeometries",
                                               inputShp, 100, None)
                    output = processing.runalg("qgis:extractnodes",
                                               output["OUTPUT"], None)
                    output = processing.runalg("qgis:voronoipolygons",
                                               output["OUTPUT"], 0.001, None)
                    voronoi = QgsVectorLayer(output["OUTPUT"], "voronoi",
                                             "ogr")
                    #                     QgsMapLayerRegistry.instance().addMapLayer(voronoi, addToLegend=True)
                    #                     voronoi.setLayerTransparency(50)

                    output = processing.runalg("qgis:extractnodes", inputShp,
                                               None)
                    originalNodes = QgsVectorLayer(output["OUTPUT"],
                                                   "original", "ogr")

                    success = originalNodes.featureCount > 0 and voronoi.featureCount(
                    ) > 0
                except:
                    trials += 1

            if not success:
                return None

            qDebug("after processing")

            # 1. create a spatial index with all the voronoi polygons
            # 2. Get the lsit of edges from the vornoi polygons that are in the pipePolygon
            voronoiEdges = []
            voronoiEdgeInsideSet = set()
            spatialIndex = QgsSpatialIndex()
            for f in voronoi.getFeatures():
                spatialIndex.insertFeature(f)
                geometry = f.geometry()

                # voronoi polygons are single parts with one ring
                p = geometry.asPolygon()
                for edgeP in self._extractEdgesFromPolyline(p[0]):
                    edge = QgsGeometry.fromPolyline(edgeP)
                    if edge.within(pipePolygon):
                        key = self._keyFromEdge(edgeP)
                        if key not in voronoiEdgeInsideSet:
                            # prevent duplicates (voronoi polygons share a border)
                            voronoiEdges.append(edgeP)
                            voronoiEdgeInsideSet.add(key)

            voronoiPolygonMajorEdges = []
            qDebug(repr(originalNodes.featureCount()))
            for originalNode in originalNodes.getFeatures():
                geometry = originalNode.geometry()
                candidates = spatialIndex.intersects(geometry.boundingBox())
                if len(candidates) > 0:
                    for candidate in candidates:
                        fCandidate = voronoi.getFeatures(
                            QgsFeatureRequest(candidate)).next()
                        gCandidate = fCandidate.geometry()
                        if geometry.within(gCandidate):
                            p = gCandidate.asPolygon()
                            edges = self._extractEdgesFromPolyline(p[0])
                            for edge in edges:
                                key = self._keyFromEdge(edge)
                                if key not in voronoiEdgeInsideSet:
                                    # do not add the edge if already in the centerline
                                    #gEdge=QgsGeometry.fromPolyline(edge)
                                    #if gEdge.intersects(pipePolygon):
                                    voronoiPolygonMajorEdges.append(key)
                            break

            qDebug("after spatindex")

            # recursively combine voronoiedges into one big polyline
            voronoiEdges = QgsGeometry.fromMultiPolyline(voronoiEdges)
            centerlinePolyline = voronoiEdges.combine(
                voronoiEdges)  #self.union(voronoiEdges)

            if centerlinePolyline.isMultipart():
                lines = centerlinePolyline.asMultiPolyline()
            else:
                lines = [centerlinePolyline.asPolyline()]

            qDebug("after merge")

            # key template
            kT = "%3.11f"

            nodeToVertexIndex = {}
            nodeCount = {}
            for i in range(len(lines)):
                line = lines[i]
                for j in range(len(line)):
                    point = line[j]
                    key = (kT % point.x(), kT % point.y())
                    count = nodeCount.get(key, 0)
                    if j <> 0 and j <> len(line) - 1:
                        # each of those nodes is linked to 2 segments
                        count += 2
                    else:
                        count += 1
                    nodeCount[key] = count

                    nodeToVertexIndex[key] = (i, j)


#             for i in range(len(lines)):
#                 line=lines[i]
#                 for j in range(len(line)):
#                     point=line[j]
#                     key=(kT%point.x(),kT%point.y())
#                     qDebug(repr(key)+" "+repr(nodeCount[key]))

            for edgeKey in voronoiPolygonMajorEdges:
                p1x, p1y, p2x, p2y = edgeKey
                for key in [(kT % p1x, kT % p1y), (kT % p2x, kT % p2y)]:
                    if key == ('-76.87093621506', '42.15309787572'):
                        qDebug("found mine " + repr(nodeCount.get(key, 0)))
                    count = nodeCount.get(key, 0)
                    if count == 2:
                        # only counts if in the main segment (not the branches)
                        nodeCount[key] = count + 1

            qDebug("after indexing")

            # cleanup: only keep important vertices
            toKeep = set(filter(lambda x: nodeCount[x] > 2, nodeCount.keys()))
            toRemove = set(nodeToVertexIndex.keys()) - toKeep
            toRemoveIndices = map(lambda x: nodeToVertexIndex[x], toRemove)
            for lineIndex, pointIndex in sorted(toRemoveIndices,
                                                cmp=self._compareIndices,
                                                reverse=True):
                line = lines[lineIndex]
                del line[pointIndex]

            self._cleanupDegenerateSegments(lines)

            qDebug("after cleanup")

            return self.union(map(lambda x: QgsGeometry.fromPolyline(x),
                                  lines))
        finally:
            if tempDir:
                pass  #shutil.rmtree(tempDir, True)
            qDebug("after union")

    def union(self, lines):
        return reduce(lambda m, x: m.combine(x), lines[1:], lines[0])

    def _compareIndices(self, x, y):
        i1, j1 = x
        i2, j2 = y
        if i1 < i2:
            return -1
        elif i2 < i1:
            return 1
        else:
            if j1 < j2:
                return -1
            elif j2 < j1:
                return 1
            else:
                return 0

    def _keyFromEdge(self, edge):
        return (edge[0].x(), edge[0].y(), edge[1].x(), edge[1].y())

    def _cleanupDegenerateSegments(self, lines):
        linesToRemove = []
        for i in range(len(lines)):
            line = lines[i]
            if len(line) <= 1:
                linesToRemove.append(i)

        for i in reversed(linesToRemove):
            del lines[i]

        return lines

    def _extractEdgesFromPolyline(self, p):
        edges = []
        for i in range(len(p) - 1):
            edgeP = p[i:i + 2]
            edges.append(edgeP)
        return edges

    def _mustReverse(self, p1, p2):
        polyline1 = QgsGeometry.fromPolyline(p1)
        polyline2 = QgsGeometry.fromPolyline(p2)

        candidates1 = (QgsGeometry.fromPolyline([p1[0], p2[0]]),
                       QgsGeometry.fromPolyline([p1[-1], p2[-1]]))

        return ((candidates1[0].intersects(candidates1[1])
                 and not candidates1[0].equals(candidates1[1]))
                or polyline1.crosses(candidates1[0])
                or polyline1.crosses(candidates1[1])
                or polyline2.crosses(candidates1[0])
                or polyline2.crosses(candidates1[1]))

    def _getFeature(self, snappingResult):
        fid = snappingResult.snappedAtGeometry
        feature = QgsFeature()
        fiter = snappingResult.layer.getFeatures(QgsFeatureRequest(fid))
        if fiter.nextFeature(feature):
            return feature
        return None

    def _getSegment(self, snappingResult):
        feature = self._getFeature(snappingResult)
        geometry = feature.geometry()
        bv = geometry.vertexAt(snappingResult.beforeVertexNr)
        av = geometry.vertexAt(snappingResult.afterVertexNr)
        return QgsGeometry.fromPolyline([bv, av])

    def _getSubPolyline(self, snappingResult1, snappingResult2, pointClicked1):
        """
        computes the polyline defined by the 2 segments: all the segments between those 2
        will be included in the returned polyline. If Ctrl held while selecting the 2nd segment, 
        The side of the first segment the user clicked on determines which direction to go to add those segments
        """
        if (snappingResult1.layer.id() <> snappingResult2.layer.id()
                or snappingResult1.snappedAtGeometry <>
                snappingResult2.snappedAtGeometry):
            return None

        feature = self._getFeature(snappingResult1)
        geometry = feature.geometry()

        if geometry.isMultipart():
            mp = geometry.asMultipolyline()
            p, relative = vectorlayerutils.polylineWithVertexAtIndex(
                mp, snappingResult1.beforeVertexNr)
            _, relative2 = vectorlayerutils.polylineWithVertexAtIndex(
                mp, snappingResult2.beforeVertexNr)
            if relative <> relative2:
                # not on the same sub polyline
                return None
        else:
            p = geometry.asPolyline()
            relative = 0

        if self.orderSelection:
            bv = geometry.vertexAt(snappingResult1.beforeVertexNr)
            av = geometry.vertexAt(snappingResult1.afterVertexNr)

            # get the direction in which to add segments
            # vertex closest to point clicked is the top vertex: direction will be from
            # the other vertex to that one
            extendUtils = ExtendUtils(self.iface)
            vertexNrDirection = extendUtils.vertexIndexToMove(bv,
                                                              av,
                                                              snappingResult1,
                                                              pointClicked1,
                                                              mustExtend=False)

            # positive if index of vertices is incremented
            direction = vertexNrDirection == snappingResult1.afterVertexNr

            if not direction:
                # swap
                temp = snappingResult2
                snappingResult2 = snappingResult1
                snappingResult1 = temp

            if snappingResult2.afterVertexNr <= snappingResult1.beforeVertexNr:
                # wrap around
                nodes = p[snappingResult1.beforeVertexNr - relative:]
                nodes.extend(p[0:snappingResult2.afterVertexNr - relative + 1])
            else:
                nodes = p[snappingResult1.beforeVertexNr -
                          relative:snappingResult2.afterVertexNr - relative +
                          1]
        else:
            if snappingResult2.beforeVertexNr < snappingResult1.beforeVertexNr:
                # swap
                temp = snappingResult2
                snappingResult2 = snappingResult1
                snappingResult1 = temp
                direction = False
            else:
                direction = True

            nodes = p[snappingResult1.beforeVertexNr -
                      relative:snappingResult2.afterVertexNr - relative + 1]

        qDebug(repr(nodes))

        return nodes if direction else list(reversed(nodes))

    def done(self):
        self.reset()
        self.iface.mapCanvas().unsetMapTool(self)

    def enter(self):
        #ignore
        pass

    def canvasPressEvent(self, e):
        # use right button instead of clicking on button in message bar
        if e.button() == Qt.RightButton:
            if self.step == 0:
                self.done()
                return

        self.orderSelection = e.modifiers() == Qt.ControlModifier
        qDebug(repr(self.orderSelection))

        if self.currentMapTool:
            self.currentMapTool.canvasPressEvent(e)

    def canvasReleaseEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasReleaseEvent(e)

    def canvasMoveEvent(self, e):
        if self.currentMapTool:
            self.currentMapTool.canvasMoveEvent(e)

    def snap(self, pos):
        (_, result) = self.snapper.snapToBackgroundLayers(pos)
        return result