1

I followed the example in QGIS documentation for Network Analysis library to create a graph from a vector layer. When building the graph, the features of the line layer are added as arcs (edges) to the graph. I am looking for a way to assign the arcs with either the original feature ids or their attributes. I have tried two methods but none has worked so far.

Method 1

When building a graph a QgsArcProperter is used to assign costs to the arcs. The QgsArcProperter has a requiredAttributes method, which based on the documentation is called by QgsGraphDirector for fetching attributes from source layer.

When using the QgsDistanceArcProperter, that inherits its methods from the QgsArcProperter, the requiredAttributes method returns an empty list and the properties of the arcs return a list with the cost of the arc.

# build graph with length cost
# vl (a vector layer)
director = QgsLineVectorLayerDirector(vl, -1, '', '', '', 3)
properter = QgsDistanceArcProperter()
director.addProperter(properter)
builder = QgsGraphBuilder(crs)
pStart = QgsPoint(475869, 4203007)
tiedPoints = director.makeGraph(builder, [pStart])
graph = builder.graph()

properter.requiredAttributes()
# >>> returns []

# get the properties of an arc with 0 index
graph.arc(0).properties()
# >>> returns the cost of the arc e.g. [10.89]

When using a custom cost properter as shown at Properter to get travel time as cost for network analysis, then the requiredAttributes return the indices of the given columns. However, when accessing the properties of the arcs, only the cost is returned. From my understanding, the properties should return a list values of the attributes as specified by the requiredAtttibnutes method.

# custom cost properter 
class CustomCost(QgsArcProperter):
    def __init__(self, costColumIndex, defaultValue):
        QgsArcProperter.__init__(self)
        self.cost_column_index = costColumIndex
        self.default_value = defaultValue
    def property(self, distance, feature):
        cost = float(feature.attributes()[self.cost_column_index])
        if not cost or cost <= 0.0:
            return self.default_value
        else:
            return cost
    def requiredAttributes(self):
        return [0, self.cost_column_index]

# build graph but replace properter with custom cost
properter = CustomCost(13, 0.01)

properter.requiredAttributes()
# >>> returns [0, 13]

# get the properties of an arc with 0 index
graph.arc(0).properties()
# >>> returns the cost of the arc e.g. [10.89]

Method 2

I have also tried to loop through the polylines and the multi-polylines of the layer to reproduce a sequential index based on their segments. I store the information in a dictionary with arc indices as keys and feature ids as values. Every feature id is added twice as I am using a bidirectional graph.

indices = {}
idx = 0
for f in vl.getFeatures():
    if f.geometry().wkbType() == 2:
        for i in f.geometry().asPolyline()[1:]:
            indices[idx] = f.id()
            idx += 1
            indices[idx] = f.id()
            idx += 1
    elif f.geometry().wkbType() == 5:
        for pl in f.geometry().asMultiPolyline():
            for p in pl[1:]:
                indices[idx] = f.id()
                idx += 1
                indices[idx] = f.id()
                idx += 1

However, if tied points are used when building the graph then this solution does not work. If a point is tied to the graph, then the segment that is closest to it is split and therefore the sequence of the index breaks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.