Example #1
0
    def loadData(self, resultFile, chunkId):
        """ Load data to the map """
        
        if isMimeTypeVector(self.mimeType, True) != None:                 
            # Memory layer:
            geometryTypes = ["Point","LineString","Polygon","Unknown", "NoGeometry"]
            vlayer = QgsVectorLayer(resultFile, "chunk", "ogr")

            if self.__bFirstChunk:    
                self.__bFirstChunk = False
                self.__geometryType = geometryTypes[vlayer.geometryType()]
                self.__bGeomMulti = vlayer.wkbType() in [4,5,6,11,12,13]
                self.__memoryLayer = QgsVectorLayer(self.__geometryType,"Streamed data","memory")
                self.__memoryLayer.dataProvider().addAttributes(vlayer.pendingFields().values())
                self.__memoryLayer.updateFieldMap()            

            provider = vlayer.dataProvider()
            allAttrs = provider.attributeIndexes()
            vlayer.select(allAttrs)  
            
            # Visualize temporal geometries during the downloading process
            # Don't add temporal geometries if last chunk
            if self.DEBUG: print "Loaded chunkId:",chunkId           
            res = self.__memoryLayer.dataProvider().addFeatures( [feat for feat in vlayer] )
            self.__deliveredChunks += 1      
            
            if not self.allChunksDelivered():
                inFeat = QgsFeature()
                inGeom = QgsGeometry()
                self.createTempGeometry(chunkId, self.__geometryType)
                while provider.nextFeature( inFeat ):
                    inGeom = inFeat.geometry()
                    featList = self.extractAsSingle(self.__geometryType, inGeom) if self.__bGeomMulti else [inGeom]
                    for geom in featList:
                        self.addTempGeometry(chunkId, self.__geometryType, geom)  
            else:
                self.finishLoading()
                                
        # Raster data
        elif isMimeTypeRaster(self.mimeType, True) != None:
            # We can directly attach the new layer
            if self.__bFirstChunk:    
                self.__bFirstChunk = False
                self.__groupIndex = self.__legend.addGroup("Streamed-raster")
                
            rLayer = QgsRasterLayer(resultFile, "raster_"+str(chunkId))
            bLoaded = QgsMapLayerRegistry.instance().addMapLayer(rLayer)
            self.stretchRaster(rLayer)
            self.__legend.moveLayer(rLayer, self.__groupIndex + 1)
            
            self.__deliveredChunks += 1
            
            if self.allChunksDelivered():
                self.finishLoading()
Example #2
0
    def loadData(self, resultFile, chunkId):
        """ Load data to the map """
        
        if isMimeTypeVector(self.mimeType, True) != None:                 
            # Memory layer:
            geometryTypes = ["Point","LineString","Polygon","Unknown", "NoGeometry"]
            vlayer = QgsVectorLayer(resultFile, "chunk", "ogr")

            if self.__bFirstChunk:    
                self.__bFirstChunk = False
                self.__geometryType = geometryTypes[vlayer.geometryType()]
                self.__bGeomMulti = vlayer.wkbType() in [4,5,6,11,12,13]
                self.__memoryLayer = QgsVectorLayer(self.__geometryType,"Streamed data","memory")
                self.__memoryLayer.dataProvider().addAttributes(vlayer.pendingFields().values())
                self.__memoryLayer.updateFieldMap()            

            provider = vlayer.dataProvider()
            allAttrs = provider.attributeIndexes()
            vlayer.select(allAttrs)  
            
            # Visualize temporal geometries during the downloading process
            # Don't add temporal geometries if last chunk
            if self.DEBUG: print "Loaded chunkId:",chunkId           
            res = self.__memoryLayer.dataProvider().addFeatures( [feat for feat in vlayer] )
            self.__deliveredChunks += 1      
            
            if not self.allChunksDelivered():
                inFeat = QgsFeature()
                inGeom = QgsGeometry()
                self.createTempGeometry(chunkId, self.__geometryType)
                while provider.nextFeature( inFeat ):
                    inGeom = inFeat.geometry()
                    featList = self.extractAsSingle(self.__geometryType, inGeom) if self.__bGeomMulti else [inGeom]
                    for geom in featList:
                        self.addTempGeometry(chunkId, self.__geometryType, geom)  
            else:
                self.finishLoading()
                                
        # Raster data
        elif isMimeTypeRaster(self.mimeType, True) != None:
            # We can directly attach the new layer
            if self.__bFirstChunk:    
                self.__bFirstChunk = False
                self.__groupIndex = self.__legend.addGroup("Streamed-raster")
                
            rLayer = QgsRasterLayer(resultFile, "raster_"+str(chunkId))
            bLoaded = QgsMapLayerRegistry.instance().addMapLayer(rLayer)
            self.stretchRaster(rLayer)
            self.__legend.moveLayer(rLayer, self.__groupIndex + 1)
            
            self.__deliveredChunks += 1
            
            if self.allChunksDelivered():
                self.finishLoading()
Example #3
0
    def test_ValidFeature(self):
        myPath = os.path.join(unitTestDataPath(), 'points.shp')
        myLayer = QgsVectorLayer(myPath, 'Points', 'ogr')
        provider = myLayer.dataProvider()
        allAttrs = provider.attributeIndexes()
        myLayer.select(allAttrs)

        feat = QgsFeature()
        myLayer.nextFeature(feat)
        myValidValue = feat.isValid()
        myMessage = '\nExpected: %s\nGot: %s' % ("True", myValidValue)
        assert myValidValue == True, myMessage
Example #4
0
def main():
  QgsApplication.setPrefixPath('/usr/local', True)
  QgsApplication.initQgis()
  layer = QgsVectorLayer('/path/to/layer.shp', 'layer', 'ogr')
  provider = layer.dataProvider()
  layer.select(provider.attributeIndexes())
  values = []
  for feat in layer:
    values.append(feat.attributeMap()[1].toDouble()[0])
  print "Quantile: ", quantile(values, 5)
  print "Equal Interval: ", equal(values, 5)
  print "R's Pretty: ", pretty(values, 5)
  print "Standard Deviation: ", std_dev(values, 5)
  print "Natural Breaks (Jenks): ", jenks(values, classes=5)
  print "Sampled Jenks: ", jenks_sample(values, classes=5)
Example #5
0
    def test_Attributes(self):
        myPath = os.path.join(unitTestDataPath(), 'lines.shp')
        myLayer = QgsVectorLayer(myPath, 'Lines', 'ogr')
        provider = myLayer.dataProvider()
        allAttrs = provider.attributeIndexes()
        myLayer.select(allAttrs)
        feat = QgsFeature()
        myLayer.nextFeature(feat)

        myAttributes = feat.attributes()
        myExpectedAttributes = [ QVariant("Highway"), QVariant(1) ]

        # Only for printing purposes
        myAttributeDict = [
            str(myAttributes[0].toString()),
            int(myAttributes[1].toString()) ]
        myExpectedAttributes = [ "Highway",  1 ]
        myMessage = '\nExpected: %s\nGot: %s' % (myExpectedAttributes,
            myAttributes)

        assert myAttributes == myExpectedAttributes, myMessage
class TestQgsNullSymbolRenderer(unittest.TestCase):

    def setUp(self):
        self.iface = get_iface()
        myShpFile = os.path.join(TEST_DATA_DIR, 'polys.shp')
        self.layer = QgsVectorLayer(myShpFile, 'Polys', 'ogr')
        QgsProject.instance().addMapLayer(self.layer)

        self.renderer = QgsNullSymbolRenderer()
        self.layer.setRenderer(self.renderer)

        rendered_layers = [self.layer]
        self.mapsettings = self.iface.mapCanvas().mapSettings()
        self.mapsettings.setOutputSize(QSize(400, 400))
        self.mapsettings.setOutputDpi(96)
        self.mapsettings.setExtent(QgsRectangle(-163, 22, -70, 52))
        self.mapsettings.setLayers(rendered_layers)

    def tearDown(self):
        QgsProject.instance().removeAllMapLayers()

    def testRender(self):
        # test no features are rendered
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlPathPrefix('null_renderer')
        renderchecker.setControlName('expected_nullrenderer_render')
        result = renderchecker.runTest('nullrenderer_render')
        assert result

    def testSelected(self):
        # select a feature and render
        self.layer.select([1, 2, 3])
        renderchecker = QgsMultiRenderChecker()
        renderchecker.setMapSettings(self.mapsettings)
        renderchecker.setControlPathPrefix('null_renderer')
        renderchecker.setControlName('expected_nullrenderer_selected')
        result = renderchecker.runTest('nullrenderer_selected')
        assert result
Example #7
0
    def _test_copy_selected(self,
                            input_layer_name,
                            output_layer_name,
                            select_id=1):
        print("### ", input_layer_name, output_layer_name)
        gpkg = get_test_file_copy_path('insert_features_to_layer_test.gpkg')

        input_layer_path = "{}|layername={}".format(gpkg, input_layer_name)
        output_layer_path = "{}|layername={}".format(gpkg, output_layer_name)
        input_layer = QgsVectorLayer(input_layer_path, 'layer name', 'ogr')
        self.assertTrue(input_layer.isValid())
        output_layer = QgsVectorLayer(output_layer_path, 'layer name', 'ogr')
        self.assertTrue(output_layer.isValid())
        QgsProject.instance().addMapLayers([input_layer, output_layer])

        input_layer.select(select_id)  # fid=1

        res = processing.run(
            "etl_load:appendfeaturestolayer", {
                'SOURCE_LAYER':
                QgsProcessingFeatureSourceDefinition(input_layer_path, True),
                'SOURCE_FIELD':
                None,
                'TARGET_LAYER':
                output_layer,
                'TARGET_FIELD':
                None,
                'ACTION_ON_DUPLICATE':
                0
            })  # No action

        self.assertIsNone(
            res[UPDATED_COUNT]
        )  # These are None because ACTION_ON_DUPLICATE is None
        self.assertIsNone(res[SKIPPED_COUNT])

        return res
Example #8
0
    def testClipping(self):
        """Test that we can clip geometries using other geometries."""
        myMemoryLayer = QgsVectorLayer(
            ('LineString?crs=epsg:4326&field=name:string(20)&index=yes'),
            'clip-in',
            'memory')

        assert myMemoryLayer is not None, 'Provider not initialised'
        myProvider = myMemoryLayer.dataProvider()
        assert myProvider is not None

        myFeature1 = QgsFeature()
        myFeature1.setGeometry(QgsGeometry.fromPolyline([
            QgsPoint(10,10),
            QgsPoint(20,10),
            QgsPoint(30,10),
            QgsPoint(40,10),
            ]
        ))
        myFeature1.setAttributes([QVariant('Johny')])

        myFeature2 = QgsFeature()
        myFeature2.setGeometry(QgsGeometry.fromPolyline([
            QgsPoint(10,10),
            QgsPoint(20,20),
            QgsPoint(30,30),
            QgsPoint(40,40),
            ]
        ))
        myFeature2.setAttributes([QVariant('Be')])

        myFeature3 = QgsFeature()
        myFeature3.setGeometry(QgsGeometry.fromPolyline([
            QgsPoint(10,10),
            QgsPoint(10,20),
            QgsPoint(10,30),
            QgsPoint(10,40),
            ]
        ))

        myFeature3.setAttributes([QVariant('Good')])

        myResult, myFeatures = myProvider.addFeatures(
            [myFeature1, myFeature2, myFeature3])
        assert myResult == True
        assert len(myFeatures) == 3

        myClipPolygon = QgsGeometry.fromPolygon([[
            QgsPoint(20,20),
            QgsPoint(20,30),
            QgsPoint(30,30),
            QgsPoint(30,20),
            QgsPoint(20,20),
            ]]
        )
        print 'Clip: %s' % myClipPolygon.exportToWkt()
        writeShape(myMemoryLayer, 'clipGeometryBefore.shp')
        myMemoryLayer.select(myProvider.attributeIndexes())
        myFeatures = []
        myFeature = QgsFeature()
        while myMemoryLayer.nextFeature(myFeature):
            myGeometry = myFeature.geometry()
            if myGeometry.intersects(myClipPolygon):
                # Adds nodes where the clip and the line intersec
                myCombinedGeometry = myGeometry.combine(myClipPolygon)
                # Gives you the areas inside the clip
                mySymmetricalGeometry = myGeometry.symDifference(
                    myCombinedGeometry)
                # Gives you areas outside the clip area
                # myDifferenceGeometry = myCombinedGeometry.difference(
                #    myClipPolygon)
                #print 'Original: %s' % myGeometry.exportToWkt()
                #print 'Combined: %s' % myCombinedGeometry.exportToWkt()
                #print 'Difference: %s' % myDifferenceGeometry.exportToWkt()
                print 'Symmetrical: %s' % mySymmetricalGeometry.exportToWkt()

                myExpectedWkt = 'LINESTRING(20.0 20.0, 30.0 30.0)'
                # There should only be one feature that intersects this clip
                # poly so this assertion should work.
                self.assertEqual(myExpectedWkt,
                                 mySymmetricalGeometry.exportToWkt())

                myNewFeature = QgsFeature()
                myNewFeature.setAttributes(myFeature.attributes())
                myNewFeature.setGeometry(mySymmetricalGeometry)
                myFeatures.append(myNewFeature)

        myNewMemoryLayer = QgsVectorLayer(
            ('LineString?crs=epsg:4326&field=name:string(20)&index=yes'),
            'clip-out',
            'memory')
        myNewProvider = myNewMemoryLayer.dataProvider()
        myResult, myFeatures = myNewProvider.addFeatures(myFeatures)
        self.assertTrue(myResult)
        self.assertEqual(len(myFeatures), 1)

        writeShape(myNewMemoryLayer, 'clipGeometryAfter.shp')
Example #9
0
    def route(self):
        try:

            import urllib.request
            import json
            origin_dest = []
            featurelist = []
            if self.dlg.inputpoint.isChecked():
                vp_layer = self.dlg.point.currentLayer()
                countfeat = vp_layer.featureCount()

            else:
                vp_layer = iface.activeLayer()
                vp_layer.commitChanges()
                countfeat = vp_layer.featureCount()

            result = processing.run(
                "native:addautoincrementalfield", {
                    'INPUT': vp_layer,
                    'FIELD_NAME': 'id',
                    'START': 1,
                    'GROUP_FIELDS': [],
                    'SORT_EXPRESSION': '\"id\"',
                    'SORT_ASCENDING': True,
                    'SORT_NULLS_FIRST': False,
                    'OUTPUT': 'memory:{0}'.format(self.dlg.route_id.text())
                })
            QgsProject.instance().removeMapLayer(vp_layer)
            vp_layer = result['OUTPUT']
            QgsProject.instance().addMapLayer(vp_layer)
            features = vp_layer.getFeatures()
            points = []
            pointdist = []
            waypoints = []
            # if vp_layer.featureCount() == 2:
            for feature in vp_layer.getFeatures():
                point = feature.geometry().asPoint()
                xpoint = point.x()
                ypoint = point.y()
                Qpoint = QgsPointXY(xpoint, ypoint)
                points.append(Qpoint)
            distcheck = 0

            if self.dlg.direction.currentText() == 'Start->End':
                # for i in points:
                # distance = QgsDistanceArea()
                # Qpoint1 = i
                # for j in points:
                #     Qpoint2 = j
                #     dist = distance.measureLine(Qpoint1, Qpoint2)
                #     pointdist.append(dist)
                #     if dist > distcheck:
                #         distcheck = dist

                self.origin = points[0]
                self.destination = points[countfeat - 1]
                # print('End->Start', self.origin, self.destination)
            elif self.dlg.direction.currentText() == 'End->Start':
                # for i in points:
                #     distance = QgsDistanceArea()
                #     Qpoint1 = i
                #     for j in points:
                #         Qpoint2 = j
                #         dist = distance.measureLine(Qpoint1, Qpoint2)
                #         pointdist.append(dist)
                #         if dist > distcheck:
                #             distcheck = dist
                self.origin = points[countfeat - 1]
                self.destination = points[0]
                # print('Start->End', self.origin, self.destination)
            # print(vp_layer.featureCount())
            if vp_layer.featureCount() > 3:
                for i in range(countfeat - 1):
                    if i != 0 and i != countfeat - 1:
                        if self.dlg.direction.currentText() == 'Start->End':
                            if len(waypoints) < 1:
                                waypoints.append('optimize:true|via:' +
                                                 str(points[i].y()) + ',' +
                                                 str(points[i].x()))
                            else:
                                waypoints.append('via:' + str(points[i].y()) +
                                                 ',' + str(points[i].x()))
                        elif self.dlg.direction.currentText() == 'End->Start':
                            if len(waypoints) < 1:
                                waypoints.append(
                                    'optimize:true|via:' +
                                    str(points[countfeat - i].y()) + ',' +
                                    str(points[countfeat - i].x()))
                            else:
                                waypoints.append(
                                    'via:' + str(points[countfeat - i].y()) +
                                    ',' + str(points[countfeat - i].x()))
                print('|'.join(waypoints))
            elif vp_layer.featureCount() == 3:
                for i in points:
                    if i != self.origin and i != self.destination:
                        waypoints.append('optimize:true|via:' + str(i.y()) +
                                         ',' + str(i.x()))
            else:
                pass
            # print(waypoints[0], 'waypoints')

            # print(distcheck, 'dist')
            # print(pointdist, 'pointdist')
            # print(origin.x(),origin.y(), destination.x(),destination.y(), 'origin_dest')

            # vp_layer.select(origin)
            # vp_layer.select(destination)

            for feature in vp_layer.getFeatures():
                geometry = feature.geometry()
                origin_dest.append(
                    {geometry.asPoint().y(),
                     geometry.asPoint().x()})
            # print(origin_dest)
            endpoint = 'https://maps.googleapis.com/maps/api/directions/json?'
            APIkey = self.dlg.api.text()
            # mode = 'driving'
            origin_str = self.origin.y(), self.origin.x()
            destination_str = self.destination.y(), self.destination.x()
            # departure_time = (self.dlg.timeEdit.time().hour()*3600 + self.dlg.timeEdit.time().minute()*60+ self.dlg.timeEdit.time().second())
            # print(departure_time)
            import time
            import datetime
            # departure = self.totimestamp(self.dlg.timeEdit.dateTime())
            if self.dlg.nowtime.isChecked():
                departure = 'now'
            else:
                departure = self.dlg.timeEdit.dateTime().toSecsSinceEpoch()
            print(departure)
            if vp_layer.featureCount() > 3:

                if self.dlg.avoid.currentText() == 'None':
                    nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&mode={6}&model={7}&key={8}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), '|'.join(waypoints), departure,
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)
                else:
                    nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&avoid={6}&mode={7}&model={8}&key={9}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), '|'.join(waypoints), departure,
                        self.dlg.avoid.currentText(),
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)
            elif vp_layer.featureCount() == 3:
                if self.dlg.avoid.currentText() == 'None':
                    nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&mode={6}&model={7}&key={8}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), waypoints[0], departure,
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)
                else:
                    nav_request = 'origin={0},{1}&destination={2},{3}&waypoints={4}&departure_time={5}&avoid={6}&mode={7}&model={8}&key={9}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), waypoints[0], departure,
                        self.dlg.avoid.currentText(),
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)

            else:
                if self.dlg.avoid.currentText() == 'None':
                    nav_request = 'origin={0},{1}&destination={2},{3}&departure_time={4}&mode={5}&model={6}&key={7}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), departure,
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)
                    # print(nav_request)
                else:
                    nav_request = 'origin={0},{1}&destination={2},{3}&departure_time{4}&avoid={5}&mode={6}&model={7}&key={8}'.format(
                        self.origin.y(), self.origin.x(), self.destination.y(),
                        self.destination.x(), departure,
                        self.dlg.avoid.currentText(),
                        self.dlg.mode.currentText(),
                        self.dlg.model.currentText(), api_key)
            request = endpoint + nav_request
            print(request)
            response = urllib.request.urlopen(request).read()
            directions = json.loads(response)
            keys = directions.keys()
            # print(keys)
            # print(directions['error_message'], directions['routes'])
            routes = directions['routes']
            legs = routes[0]['legs']
            line = routes[0]['overview_polyline']
            # print(routes)
            points = polyline.decode(line['points'])
            self.route_layer = QgsVectorLayer(
                "Point?crs=EPSG:4326&field=route_id:String(100)&field=distance:String(100)&field=time:String(100)&field=ascending/descending:String(100)&field=departure_time:String(100)&field=roads_to_avoid:String(100)&field=traffic_model:String(100)",
                "route_points", "memory")
            provider = self.route_layer.dataProvider()
            # QgsProject.instance().addMapLayer(self.route_layer)
            if len(legs[0]['duration']['text'].split(' ')) == 2:
                duration1 = legs[0]['duration']['text'].split(' ')
                print(duration1, 'dur')
                duration = duration1[0]
            else:
                duration1 = legs[0]['duration']['text'].split(' ')
                print(duration1, 'dur1')
                duration = str((int(duration1[0]) * 60) + int(duration1[2]))

            self.route_layer.startEditing()

            route_attrib = [
                self.dlg.route_id.text(),
                legs[0]['distance']['text'].split(' ')[0], duration,
                self.dlg.direction.currentText(),
                (self.dlg.timeEdit.dateTime()),
                self.dlg.avoid.currentText(),
                self.dlg.model.currentText()
            ]
            # print((self.dlg.timeEdit.time()))
            # print(route_attrib)
            for i in points:
                outelem = QgsFeature(self.route_layer.fields())
                outelem.setGeometry(
                    QgsGeometry.fromPointXY(QgsPointXY(i[1], i[0])))
                outelem.setFields(self.route_layer.fields())
                outelem.setAttributes(route_attrib)
                featurelist.append(outelem)
                # print(outelem)
            self.route_layer.dataProvider().addFeatures(featurelist)
            self.route_layer.commitChanges()

            result = processing.run(
                "qgis:pointstopath", {
                    'INPUT': self.route_layer,
                    'ORDER_FIELD': 'route_id',
                    'GROUP_FIELD': None,
                    'DATE_FORMAT': '',
                    'OUTPUT': 'memory:'
                })

            if not self.dlg.checkBox.isChecked():
                route = QgsVectorLayer(
                    "Linestring?crs=EPSG:4326&field=route_id:String(100)&field=distance(km):String(100)&field=time(min):String(100)&field=ascending/descending:String(100)&field=departure_time:String(100)&field=duration_in_traffic(min):String(100)&field=roads_to_avoid:String(100)&field=traffic_model:String(100)&field=no_of_nodes:String(100)",
                    "route", "memory")
            else:
                # print(self.dlg.route_id.text(), route_attrib2)
                route = self.dlg.layer.currentLayer()
            fields = route.dataProvider().fields()
            field_name = [field.name() for field in fields]

            # line_layer = QgsVectorLayer(result['OUTPUT'], 'route')
            featurelist2 = []

            # if self.dlg.mode.currentText() not in ['walking', 'bicycling', 'transit']:
            if 'duration_in_traffic' in legs[0].keys():
                if len(legs[0]['duration']['text'].split(' ')) == 2:
                    duration1 = legs[0]['duration_in_traffic']['text'].split(
                        ' ')
                    print(duration1, 'dur12')
                    duration_in_traffic = duration1[0]
                else:
                    duration1 = legs[0]['duration_in_traffic']['text'].split(
                        ' ')
                    print(duration1, 'dur13')
                    duration_in_traffic = str((int(duration1[0]) * 60) +
                                              int(duration1[2]))
                if 'fid' in field_name:
                    route_attrib2 = [
                        route.featureCount(),
                        self.dlg.route_id.text(),
                        legs[0]['distance']['text'].split(' ')[0], duration,
                        self.dlg.direction.currentText(),
                        (self.dlg.timeEdit.dateTime()), duration_in_traffic,
                        self.dlg.avoid.currentText(),
                        self.dlg.model.currentText(),
                        str(self.route_layer.featureCount())
                    ]
                else:
                    route_attrib2 = [
                        self.dlg.route_id.text(),
                        legs[0]['distance']['text'].split(' ')[0], duration,
                        self.dlg.direction.currentText(),
                        (self.dlg.timeEdit.dateTime()), duration_in_traffic,
                        self.dlg.avoid.currentText(),
                        self.dlg.model.currentText(),
                        str(self.route_layer.featureCount())
                    ]
            else:
                if 'fid' in field_name:
                    route_attrib2 = [
                        route.featureCount(),
                        self.dlg.route_id.text(),
                        legs[0]['distance']['text'].split(' ')[0], duration,
                        self.dlg.direction.currentText(),
                        (self.dlg.timeEdit.dateTime()), 'None',
                        self.dlg.avoid.currentText(),
                        self.dlg.model.currentText(),
                        str(self.route_layer.featureCount())
                    ]
                else:
                    route_attrib2 = [
                        self.dlg.route_id.text(),
                        legs[0]['distance']['text'].split(' ')[0], duration,
                        self.dlg.direction.currentText(),
                        (self.dlg.timeEdit.dateTime()), duration_in_traffic,
                        self.dlg.avoid.currentText(),
                        self.dlg.model.currentText(),
                        str(self.route_layer.featureCount())
                    ]

                # print(field_name)
                # if 'fid' in field_name:
                #     # route.startEditing()
                #     # print(route.dataProvider().fieldNameIndex('fid'))
                #     # route.dataProvider().deleteAttributes([route.dataProvider().fieldNameIndex('fid')])
                #     # route.updateFields()
                #     # field_name = [field.name() for field in fields]
                #     # print(field_name)
                #
                #     route_attrib2 = [route.featureCount(), self.dlg.route_id.text(), legs[0]['distance']['text'].split(' ')[0], duration, self.dlg.direction.currentText(),(self.dlg.timeEdit.dateTime()),'None',self.dlg.avoid.currentText(), self.dlg.model.currentText(), str(self.route_layer.featureCount())]
                # else:
                #      pass
            provider = route.dataProvider()
            route.startEditing()

            request = QgsFeatureRequest()
            # set order by field
            clause = QgsFeatureRequest.OrderByClause('route_id',
                                                     ascending=False)
            orderby = QgsFeatureRequest.OrderBy([clause])
            request.setOrderBy(orderby)
            fields = route.dataProvider().fields()
            field_name = [field.name() for field in fields]
            for feature in result['OUTPUT'].getFeatures():
                outelem = QgsFeature(route.fields())
                outelem.setGeometry(feature.geometry())
                outelem.setFields(route.fields())
                if 'fid' not in field_name:
                    outelem.setAttributes(route_attrib2)
                else:
                    for index, field in enumerate(field_name):
                        if field != 'fid':
                            print(field, route_attrib2[index])
                            outelem[field] = route_attrib2[index]
                featurelist2.append(outelem)

            route.dataProvider().addFeatures(featurelist2)

            # route.updateFeature(feature)
            route.commitChanges()
            # QgsProject.instance().addMapLayer(route)
            if route.featureCount() == 1:
                file_path = os.path.abspath(
                    os.path.join(os.path.dirname(__file__), "route_style.qml"))
                route.loadNamedStyle(file_path)
            else:

                # result_del = processing.run("qgis:deletecolumn", {
                #     'INPUT': route,
                #     'COLUMN': ['gid'], 'OUTPUT': 'memory:'})
                # QgsProject.instance().addMapLayer(result_del['OUTPUT'])

                # result = processing.run("native:addautoincrementalfield",
                #                         {'INPUT': result_del['OUTPUT'],
                #                          'FIELD_NAME': 'gid', 'START': 1, 'GROUP_FIELDS': [],
                #                          'SORT_EXPRESSION': '',
                #                          'SORT_ASCENDING': False, 'SORT_NULLS_FIRST': False, 'OUTPUT': 'memory:route'})
                #
                # QgsProject.instance().removeMapLayer(route)
                # route = result['OUTPUT']
                QgsProject.instance().addMapLayer(route)
                # provide file name index and field's unique values
                # fni = route.dataProvider().fieldNameIndex('route_id')
                # unique_values = route.uniqueValues(fni)
                # fni2 = route.dataProvider().fieldNameIndex('route_id')
                # unique_values2 = route.uniqueValues(fni2)
                # unique_values2 = sorted(unique_values2)
                unique_values2 = []
                unique_values = []
                request = QgsFeatureRequest()
                # set order by field
                clause = QgsFeatureRequest.OrderByClause('route_id',
                                                         ascending=False)
                orderby = QgsFeatureRequest.OrderBy([clause])
                request.setOrderBy(orderby)
                for feature in route.getFeatures():
                    attrib = feature.attributes()
                    unique_values2.append(attrib[
                        route.dataProvider().fieldNameIndex('route_id')])
                    unique_values.append(attrib[
                        route.dataProvider().fieldNameIndex('route_id')])
                from random import randrange
                # fill categories
                categories = []
                # print(unique_values)
                # unique_values = sorted(unique_values)
                print(unique_values, unique_values2)
                for index, unique_value in enumerate(unique_values):
                    # initialize the default symbol for this geometry type
                    # symbol = QgsSymbol.defaultSymbol(route.geometryType())
                    # symbol = QgsSymbol.Symbol().setShape(QgsSimpleMarkerSymbolLayerBase.Star
                    # symbol.appendSymbolLayer(symbol_layer)
                    # configure a symbol layer
                    # sym = route.renderer().symbol()
                    # double headed
                    symbol = QgsSymbol.defaultSymbol(route.geometryType())
                    # double headed
                    sym_layer = QgsArrowSymbolLayer.create({
                        "arrow_width":
                        "1",
                        "arrow_start_width":
                        "1",
                        "head_length":
                        "1.5",
                        "head_thickness":
                        "1.5",
                        "head_type":
                        "0",
                        "arrow_type":
                        "0",
                        "is_curved":
                        "0",
                    })
                    fill_sym = QgsFillSymbol.createSimple({
                        "color":
                        '%d, %d, %d' % (randrange(0, 256), randrange(
                            0, 256), randrange(0, 256))
                    })
                    sym_layer.setSubSymbol(fill_sym)
                    symbol.changeSymbolLayer(0, sym_layer)
                    # layer_style = {}
                    # layer_style['color'] = '%d, %d, %d' % (randrange(0, 256), randrange(0, 256), randrange(0, 256))
                    # layer_style['outline'] = '#FF0000'
                    # # layer_style['width'] = '7.6'
                    # symbol_layer = QgsSimpleFillSymbolLayer.create(layer_style)

                    # replace default symbol layer with the configured one
                    # if symbol_layer is not None:
                    #     symbol.changeSymbolLayer(0, symbol_layer)
                    #     symbol.setWidth(0.66)

                    # create renderer object
                    category = QgsRendererCategory(unique_value, symbol,
                                                   str(unique_values2[index]))
                    # entry for the list of category items
                    categories.append(category)

                # create renderer object
                renderer = QgsCategorizedSymbolRenderer('route_id', categories)

                # assign the created renderer to the layer
                if renderer is not None:
                    route.setRenderer(renderer)
                route.triggerRepaint()

                ltl = QgsProject.instance().layerTreeRoot().findLayer(
                    route.id())
                ltm = iface.layerTreeView()
                # ltm.sortItems(0, Qt.AscendingOrder)
                # view = iface.layerTreeView()
                # ltm.model().AllowNodeReorder()
                index_newfeat = ltm.model().index(0, 0)
                node = ltm.model().index2node(index_newfeat)
                nodes = ltm.model().layerLegendNodes(node)
                legendNodes = ltm.model().layerLegendNodes(ltl)
                legend_dict = {}
                legend_dict[node.name()] = legendNodes
                # print(legend_dict)
                ltm.setSortingEnabled(True)
                ltm.sortByColumn(0, Qt.DescendingOrder)

                for index, ln in enumerate(legendNodes):
                    if index + 1 != route.featureCount():
                        ln.setData(Qt.Unchecked, Qt.CheckStateRole)
                # index_newfeat = ltm.model().index(route.featureCount()-1, 0)
                # print(index_newfeat)
                # node = ltm.model().index2node(index_newfeat)
                # print(node)
                # nodes = ltm.model().layerLegendNodes(node)
                # # layer_and_nodes[n.name()] = nodes
                # # print(layer_and_nodes)
                # # legend_get = ltm.model().index2legendNode(nodes)
                # print(nodes)

                # print(index, ln)
                # print(index, ltm.model().legendRootIndex(ln), ltm.model().legendNode2index(ln), ln, index_newfeat)
                # if index+1 != int(self.dlg.route_id.text()):

                # ln.setData(Qt.Checked, Qt.CheckStateRole)

            if not self.dlg.checkBox.isChecked():
                if self.dlg.output.text() != '':
                    path = self.dlg.output.text()
                    QgsVectorFileWriter.writeAsVectorFormat(
                        route, path, 'UTF-8', route.crs(), 'ESRI Shapefile')
                    # layer = QgsProject.instance().layerTreeRoot().findLayer(route.id())
                    # print(layer.name())
                    output = self.dlg.output.text().split('/')
                    route_path = QgsVectorLayer(
                        path, output[len(output) - 1].split('.')[0])
                    QgsProject.instance().addMapLayer(route_path)
                else:
                    QgsProject.instance().addMapLayer(route)
            listselect = []
            for index, feature in enumerate(route.getFeatures()):
                if index + 1 == route.featureCount():
                    listselect.append(feature.id())
            route.select(listselect)
            iface.actionZoomToSelected().trigger()

        except Exception as e:
            alert = QMessageBox()
            alert.setWindowTitle('Alert')

            if self.dlg.mode.currentText() in [
                    'walking', 'bicycling', 'transit'
            ]:
                alert.setText(
                    str(e) + '\nRoute not available for selected mode.')
            else:
                alert.setText(str(e))
            result = alert.exec_()
            print(e)
Example #10
0
class Geonet:
    """QGIS Plugin Implementation."""
    def __init__(self, iface):
        """Constructor.

      :param iface: An interface instance that will be passed to this class
          which provides the hook by which you can manipulate the QGIS
          application at run time.
      :type iface: QgsInterface
      """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(self.plugin_dir, 'i18n',
                                   'Geonet_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Geonet')

        # Check if plugin was started the first time in current QGIS session
        # Must be set in initGui() to survive plugin reloads
        self.first_start = None

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

      We implement this ourselves since we do not inherit QObject.

      :param message: String for translation.
      :type message: str, QString

      :returns: Translated version of message.
      :rtype: QString
      """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('Geonet', message)

    def add_action(self,
                   icon_path,
                   text,
                   callback,
                   enabled_flag=True,
                   add_to_menu=True,
                   add_to_toolbar=True,
                   status_tip=None,
                   whats_this=None,
                   parent=None):
        """Add a toolbar icon to the toolbar.

      :param icon_path: Path to the icon for this action. Can be a resource
          path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
      :type icon_path: str

      :param text: Text that should be shown in menu items for this action.
      :type text: str

      :param callback: Function to be called when the action is triggered.
      :type callback: function

      :param enabled_flag: A flag indicating if the action should be enabled
          by default. Defaults to True.
      :type enabled_flag: bool

      :param add_to_menu: Flag indicating whether the action should also
          be added to the menu. Defaults to True.
      :type add_to_menu: bool

      :param add_to_toolbar: Flag indicating whether the action should also
          be added to the toolbar. Defaults to True.
      :type add_to_toolbar: bool

      :param status_tip: Optional text to show in a popup when mouse pointer
          hovers over the action.
      :type status_tip: str

      :param parent: Parent widget for the new action. Defaults None.
      :type parent: QWidget

      :param whats_this: Optional text to show in the status bar when the
          mouse pointer hovers over the action.

      :returns: The action that was created. Note that the action is also
          added to self.actions list.
      :rtype: QAction
      """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            # Adds plugin icon to Plugins toolbar
            self.iface.addToolBarIcon(action)

        if add_to_menu:
            self.iface.addPluginToMenu(self.menu, action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        self.add_action('D:/Code/QGIS_Plugin/geonet/feature.png',
                        text=self.tr(u'Import feaure'),
                        callback=self.import_feature,
                        parent=self.iface.mainWindow())

        self.add_action('D:/Code/QGIS_Plugin/geonet/dangle.png',
                        text=self.tr(u'Clean dangle nodes'),
                        callback=self.dangle_clean,
                        parent=self.iface.mainWindow())

        self.add_action('D:/Code/QGIS_Plugin/geonet/pseudo.png',
                        text=self.tr(u'Clean pseudo nodes'),
                        callback=self.pseudo_clean,
                        parent=self.iface.mainWindow())

        self.add_action('D:/Code/QGIS_Plugin/geonet/graph.png',
                        text=self.tr(u'Build graph'),
                        callback=self.graph_build,
                        parent=self.iface.mainWindow())

        self.add_action('D:/Code/QGIS_Plugin/geonet/shortpath.png',
                        text=self.tr(u'Shortest Path'),
                        callback=self.shortest_path,
                        parent=self.iface.mainWindow())

        # init params
        self.resDlg = GeonetTableView()
        self.model = QStandardItemModel()
        self.resDlg.tView.verticalHeader().hide()
        self.resDlg.tView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.resDlg.tView.setSelectionMode(QAbstractItemView.SingleSelection)
        self.resDlg.tView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.resDlg.tView.clicked.connect(self._zoom_to_feat)

        self.canvas = self.iface.mapCanvas()
        self.corrSet = set()
        self.errSet = set()
        self.errFeatMap = defaultdict(set)
        self.errPointList = []
        self.newFeatMap = {}
        self._id = 0
        self.iNode = []
        self.featLayer = None
        self.g = None
        self.tool = PointTool(self.iface.mapCanvas())
        self.tool.trigger.connect(self._add_node_selected)
        self.iface.mapCanvas().setMapTool(self.tool)

        # will be set False in run()
        self.first_start = True

    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginMenu(self.tr(u'&Geonet'), action)
            self.iface.removeToolBarIcon(action)

    def import_feature(self):
        """Run method that performs all the real work"""

        # Create the dialog with elements (after translation) and keep reference
        # Only create GUI ONCE in callback, so that it will only load when the plugin is started
        if self.first_start == True:
            self.first_start = False
            self.dlg = FeatInputDialog()

        self.dlg.mLayerComboBox.clear()
        self.errFeatMap.clear()
        self.newFeatMap.clear()
        layers = list(QgsProject.instance().mapLayers().values())
        for layer in layers:
            if layer.type() == layer.VectorLayer:
                self.dlg.mLayerComboBox.addItem(layer.name())

        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            self.featLayer = layers[self.dlg.mLayerComboBox.currentIndex()]
            lineFeatIter = self.featLayer.getFeatures()
            # Find pseudo points
            list(map(self._map_point_to_feat, lineFeatIter))

        self.tool.activate()

    def dangle_clean(self):
        if self.featLayer is None:
            QMessageBox.warning(None, "Warning",
                                "Please choose a feature first!")
            return

        self.model.setHorizontalHeaderLabels(
            ["Point_X", "Point_Y", "Feature ID"])
        self.dangleList = []
        for (point, feat_ids) in self.errFeatMap.items():
            if len(feat_ids) == 1:
                # Pseudo node
                self.dangleList.extend(map(lambda x: (point, x), feat_ids))

        self._render_table(self.dangleList)
        self._render_err_layer("dangle", [x for (x, _) in self.dangleList])
        self.resDlg.show()

        result = self.resDlg.exec_()
        if result:
            pass

        self.model.clear()

    def pseudo_clean(self):
        if self.featLayer is None:
            QMessageBox.warning(None, "Warning",
                                "Please choose a feature first!")
            return

        self.model.setHorizontalHeaderLabels(
            ["Point_X", "Point_Y", "Feature ID"])
        self.pseduoList = []
        for (point, feat_ids) in self.errFeatMap.items():
            if len(feat_ids) == 2:
                # Pseudo node
                self.pseduoList.extend(map(lambda x: (point, x), feat_ids))

        self._render_table(self.pseduoList)
        self._render_err_layer("pseudo", [x for (x, _) in self.pseduoList])
        self.resDlg.show()

        result = self.resDlg.exec_()
        if result:
            self.pBar = WaitProgressDialog()
            self.thread = WorkThread(net=self)
            cancelButton = QPushButton("Cancel")
            self.pBar.setCancelButton(cancelButton)
            cancelButton.clicked.connect(self.thread.terminate)
            cancelButton.setGeometry(100, 100, 100, 100)
            self.pBar.show()
            self.thread.start()

            # self._render_corr_layer("pseudo", feat_set.values())

        self.model.clear()

    def graph_build(self):
        if self.featLayer is None:
            QMessageBox.warning(None, "Warning",
                                "Please choose a feature first!")
            return
        self.g = nx.Graph()

        featIter = self.featLayer.getFeatures()
        self.node_map = {}
        self.node_set = set()
        self.weighted_edges = []
        self.node_id = 0
        list(map(self._map_point_to_edge, featIter))
        self.g.add_weighted_edges_from(self.weighted_edges)

        self._render_node_layer(self.g.nodes())
        # self.tool.activate()

    def shortest_path(self):

        if self.g is None:
            QMessageBox.warning(None, "Warning",
                                "Please build the graph first!")
            return
        if len(self.iNode) > 1:
            startNode, endNode = self.iNode[0].geometry().asPoint(
            ), self.iNode[1].geometry().asPoint()
            _route = nx.shortest_path(self.g, startNode, endNode)
            self.iNode.clear()
            self.featLayer.removeSelection()
            self.canvas.refresh()
            self._render_route_layer(_route)
            # self.tool.deactivate()

    def _clean_work_by_thread(self):
        print("----- Clean work start -----")
        # Union find feature set
        featCount = len(self.newFeatMap)
        _father = [i for i in range(0, featCount)]
        _rank = [0] * featCount

        def _find(x):
            if _father[x] != x:
                _father[x] = _find(_father[x])
            return _father[x]

        def _union(x, y):
            _x = _find(x)
            _y = _find(y)
            if _x == _y:
                return
            if _rank[_x] < _rank[_y]:
                _father[_x] = _y
            elif _rank[_x] > _rank[_y]:
                _father[_y] = _x
            else:
                _father[_y] = _x
                _rank[x] += 1

        for (_, feat_ids) in self.errFeatMap.items():
            feat_ids = list(feat_ids)
            for feat_id in feat_ids[1:]:
                _union(feat_id, feat_ids[0])
        feat_set = defaultdict(list)
        for idx, feat in enumerate(_father):
            feat_set[feat].append(idx)
        print('----- Render work start -----')
        self._render_corr_layer("pseudo", feat_set.values())

    def _map_point_to_feat(self, feat):
        geom = feat.geometry()
        # Map features as point to id
        if geom.isMultipart():
            for line in geom.asGeometryCollection():
                new_feat = QgsFeature(self._id)
                new_feat.setGeometry(line)
                self._id += 1
                startPoint, endPoint = line.asPolyline()[0], line.asPolyline(
                )[-1]
                self.newFeatMap[new_feat.id()] = new_feat
                self.errFeatMap[startPoint].add(new_feat.id())
                self.errFeatMap[endPoint].add(new_feat.id())
        else:
            new_feat = QgsFeature(self._id)
            new_feat.setGeometry(geom)
            self._id += 1
            startPoint, endPoint = geom.asPolyline()[0], geom.asPolyline()[-1]
            self.newFeatMap[new_feat.id()] = new_feat
            self.errFeatMap[startPoint].add(new_feat.id())
            self.errFeatMap[endPoint].add(new_feat.id())

    def _map_point_to_edge(self, feat):
        geom = feat.geometry()
        weighted_edges = []
        if geom.isMultipart():
            for line in geom.asGeometryCollection():
                startPoint, endPoint = line.asPolyline()[0], line.asPolyline(
                )[-1]
                startNode = QgsFeature()
                startNode.setGeometry(QgsGeometry.fromPointXY(startPoint))
                # self.node_id += 1
                endNode = QgsFeature()
                endNode.setGeometry(QgsGeometry.fromPointXY(endPoint))
                # self.node_id += 1
                self.weighted_edges.append(
                    (startPoint, endPoint, line.length()))
        else:
            startPoint, endPoint = geom.asPolyline()[0], geom.asPolyline()[-1]
            startNode = QgsFeature(self.node_id)
            startNode.setGeometry(QgsGeometry.fromPointXY(startPoint))
            self.node_id += 1
            endNode = QgsFeature(self.node_id)
            endNode.setGeometry(QgsGeometry.fromPointXY(endPoint))
            self.node_id += 1
            self.weighted_edges.append((startNode, endNode, geom.length()))

    def _zoom_to_feat(self, item):
        # Clear previous selection
        for layer in self.canvas.layers():
            if layer.type() == layer.VectorLayer:
                layer.removeSelection()
        self.canvas.refresh()

        # Get id of feature selected
        feat_id = self.model.item(item.row(), 2).data(0)
        self.featLayer.select(int(feat_id))

        # Zoom canvas
        self.canvas.zoomToSelected(self.featLayer)
        self.canvas.refresh()

    def _render_table(self, list):
        for (idx, err) in enumerate(list):
            self.model.setItem(idx, 0, QStandardItem(str(err[0].x())))
            self.model.setItem(idx, 1, QStandardItem(str(err[0].y())))
            self.model.setItem(idx, 2, QStandardItem(str(err[1])))
            self.resDlg.tView.setModel(self.model)
            # self.resDlg.tView.selectionModel().currentRowChanged.connect(self.zoom_to_feature)
            w = self.resDlg.tView.width()
            self.resDlg.tView.setColumnWidth(0, w / 3 - 1)
            self.resDlg.tView.setColumnWidth(1, w / 3 - 1)
            self.resDlg.tView.setColumnWidth(2, w - 2 * w / 3 - 2)

    def _render_err_layer(self, name, _list):
        self.errLayer = QgsVectorLayer(
            "Point?crs=" + self.featLayer.crs().authid(),
            self.featLayer.name() + "_" + name + "_Nodes", "memory")
        errPr = self.errLayer.dataProvider()
        self.errLayer.startEditing()

        feats = map(self._gen_point_feats, _list)
        errPr.addFeatures(feats)
        self.errLayer.commitChanges()
        self._add_layer(self.errLayer)

    def _render_corr_layer(self, name, _list):
        # Init correct layer
        self.corrLayer = QgsVectorLayer(
            "LineString?crs=" + self.featLayer.crs().authid(),
            self.featLayer.name() + "_" + name + "_Correct", "memory")
        corrPr = self.corrLayer.dataProvider()
        self.corrLayer.startEditing()

        # Generate new features
        feats = map(self._gen_corr_feats, _list)
        corrPr.addFeatures(feats)
        self.corrLayer.commitChanges()

        # self._add_layer(self.corrLayer)

    def _render_route_layer(self, _list):
        self.routeLayer = QgsVectorLayer(
            "LineString?crs=" + self.featLayer.crs().authid(),
            self.featLayer.name() + "_Route", "memory")
        routePr = self.routeLayer.dataProvider()
        self.routeLayer.startEditing()
        _symbol = QgsLineSymbol()
        _symbol.appendSymbolLayer(QgsArrowSymbolLayer())
        self.routeLayer.renderer().setSymbol(_symbol)
        for idx in range(len(_list) - 1):
            route_seg = [_list[idx], _list[idx + 1]]
            feat = QgsFeature()
            feat.setGeometry(QgsGeometry.fromPolylineXY(route_seg))
            routePr.addFeatures([feat])
        self.routeLayer.commitChanges()

        self._add_layer(self.routeLayer)
        # self.canvas.refresh()

    def _render_node_layer(self, _list):
        self.nodeLayer = QgsVectorLayer(
            "Point?crs=" + self.featLayer.crs().authid(),
            self.featLayer.name() + "_Graph_Vertex", "memory")
        nodePr = self.nodeLayer.dataProvider()
        self.nodeLayer.startEditing()

        # Generate new features
        feats = map(self._gen_point_feats, _list)
        # feats = _list
        nodePr.addFeatures(feats)
        self.nodeLayer.commitChanges()
        self._add_layer(self.nodeLayer)

    def _add_layer(self, layer):
        QgsProject.instance().addMapLayer(layer)

    def _add_node_selected(self, node):
        self.iNode.append(node)
        self.nodeLayer.select([x.id() for x in self.iNode])

    def _gen_point_feats(self, p):
        feat = QgsFeature()
        feat.setGeometry(QgsGeometry.fromPointXY(p))
        return feat

    def _gen_corr_feats(self, feat_ids):
        new_feat = QgsFeature()
        geom = new_feat.geometry()

        # Combine features
        for idx, feat_id in enumerate(feat_ids):
            feat = self.newFeatMap[feat_id]
            if idx == 0:
                new_feat.setGeometry(feat.geometry())
                geom = new_feat.geometry()
            else:
                geom = geom.combine(feat.geometry())
        new_feat.setGeometry(geom)
        return new_feat

    def _add_corr_layer(self):
        QgsProject.instance().addMapLayer(self.corrLayer)