2

Project QGIS have two layers: 'Points', 'Lines'.

Layer 'Points' have field 'id_point'.

Layer 'Lines' have fields 'begin' and 'dest'.

How to change feature attribute 'begin' and 'dest' in layer 'Lines' by start and end of line intercsect whith points from layer 'Points' on-the-fly?

I write some code.

from qgis.core import *
from qgis.gui import *
from qgis.utils import iface

allfeatures = None
index = QgsSpatialIndex()
indexMade = 0
refLayer = None

myLayer = QgsMapLayerRegistry.instance().mapLayersByName( 'Lines' )[0]
beginField = myLayer.fieldNameIndex( 'begin' )
destField = myLayer.fieldNameIndex( 'dest' )

def updateFeatureAttrs( fId, geom=None ):
    f = myLayer.getFeatures( QgsFeatureRequest( fId ) ).next()    
    if not geom:
        geom = f.geometry().asPolyline()
    myLayer.changeAttributeValue( fId, beginField, spJoin('Points','id_point','None',geom[0]))
    myLayer.changeAttributeValue( fId, destField, spJoin('Points','id_point','None',geom[-1]))

myLayer.featureAdded.connect( updateFeatureAttrs )
myLayer.geometryChanged.connect( updateFeatureAttrs )

def spJoin(layerName, refColumn, defaultValue, geom):
    if geom is None:
        return defaultValue

    global allfeatures
    global index
    global indexMade
    global refLayer

    if refLayer is None:
        for layer in iface.mapCanvas().layers():
            if layerName == layer.name():
                refLayer = layer
                break

    if indexMade == 0:
        index = QgsSpatialIndex()
        allAttrs = layer.pendingAllAttributesList()
        layer.select(allAttrs)
        allfeatures = {feature.id(): feature for (feature) in refLayer.getFeatures()}
        for f in allfeatures.values():
            index.insertFeature(f)
        indexMade = 1

    fid = None
    ids = index.nearestNeighbor(geom,0.00001)
    for id in ids:
        fid = id
        break # Only get the first match.
    if fid is not None:
        return allfeatures[fid].attribute(refColumn)

    return defaultValue

It work when i create new lines, but when change lines get this error:

Traceback (most recent call last):
              File "/home/xxx/Untitled-0.py", line 18, in updateFeatureAttrs
                myLayer.changeAttributeValue( fId, beginField, spJoin('Points','id_point','None',geom[0]))
            TypeError: 'QgsGeometry' object does not support indexing
  • Did you finally solve the problem? – Germán Carrillo Sep 5 '16 at 21:10
  • Not yet, but I use my code as is. – Wilhelm Beuve Sep 7 '16 at 11:46
1

TBH, your question is not very easy to read, but I'm familiar with the code, so let's give it a try.

I guess you just need to remove the if condition in your updateFeatureAttrs() slot.

def updateFeatureAttrs( fId, geom=None ):
    f = myLayer.getFeatures( QgsFeatureRequest( fId ) ).next()    
    geom = f.geometry().asPolyline()
    myLayer.changeAttributeValue( fId, beginField, spJoin('Points','id_point','None',geom[0]))
    myLayer.changeAttributeValue( fId, destField, spJoin('Points','id_point','None',geom[-1]))

When you change a geometry, geometryChanged SIGNAL sends a geometry as parameter, so it doesn't enter your if condition because the geometry (geom) does exist.

By the way, you might want to check the AutoFields plugin. With some Python function (you already have written the logic) defined in the QGIS Function Editor, you could achieve the same result, leaving the heavy part (SIGNAL/SLOTs connections that work across QGIS projects and sessions) to the plugin. To get an idea, have a look at this video example to see how to extract elevation values from a DEM for new (or modified) points.

If you happen to give AutoFields a try and make it work for your use case, please tell me, I'd like to show your example off.

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.