14

I'm working in a way to make hydrologic projects using QGIS and an Excel worksheet that I have. To do this, I want to extract some information of the lines, included in a vector layer, which represents a pipe section.

The information that I need to extract are:

  • ID Number
  • Length
  • X, Y start and end coordinates

I've found a way to capture this field using the "$length" and another algorithm for the X and Y coordinates, but for that I need to open the Attributes table, put the expressions in each attribute column and click to update fields.

Is there a way to when I draw a line these fields are filled in automatically? That is, I draw / edit a line (start editing or end node ) and when I open the attribute table the length fields and coordinates X , Y are filled and updated.

1
  • 1
    Not enterily sure if it is possible while in edit-mode as it is done in a temporary table. But you might have a look at actions. You can use them for example to run python code to fill in your calculations. Well you have to push an extra button to run it from the attribut table though. You can have a look at the possibilities there are and see if it fits your idea.
    – Matte
    Commented Apr 19, 2016 at 20:05

4 Answers 4

18

Interesting question! I'm not aware of any other way of achieving what you want, but using PyQGIS.

Read the code below. It has some texts in it: 'lines', 'length', 'startX', 'startY', 'endX', 'endY'. You can adjust those names in the script for it to work on your data. The first one is you layer name, whereas the rest corresponds to field names. I assume your line layer has those fields (after all, you want values to be written there).

Once you have adjusted your layer name and the names of the fields you want to be automatically updated, copy and paste the script into the QGIS Python console.

If everything goes well, you should be able to see that field values are automatically updated in two scenarios: 1) When new features are added, and 2) When geometries are modified.

# Initialize required variables
myLayer = QgsMapLayerRegistry.instance().mapLayersByName( 'lines' )[0]
lengthField = myLayer.fieldNameIndex( 'length' )
startXField = myLayer.fieldNameIndex( 'startX' )
startYField = myLayer.fieldNameIndex( 'startY' )
endXField = myLayer.fieldNameIndex( 'endX' )
endYField = myLayer.fieldNameIndex( 'endY' )

# Slot, updates field values
def updateFeatureAttrs( fId, geom=None ):
    f = myLayer.getFeatures( QgsFeatureRequest( fId ) ).next()    
    if not geom:
        geom = f.geometry() 
    myLayer.changeAttributeValue( fId, lengthField, geom.length() )
    myLayer.changeAttributeValue( fId, startXField, geom.vertexAt( 0 )[0] )
    myLayer.changeAttributeValue( fId, startYField, geom.vertexAt( 0 )[1] )
    myLayer.changeAttributeValue( fId, endXField, geom.asPolyline()[-1][0] )
    myLayer.changeAttributeValue( fId, endYField, geom.asPolyline()[-1][1] )

# Update feature attributes when new features are added or geometry changes
myLayer.featureAdded.connect( updateFeatureAttrs )
myLayer.geometryChanged.connect( updateFeatureAttrs )

This is how it works:

Automatic updates of fields in QGIS

If you have any problem while running the script, add a comment below this answer.

It might be handy for you to have this functionality already available when you open your QGIS project. If that's the case, tell me, I could post instructions to do that.


EDIT:

For this functionality to be available every time you open your QGIS project (i.e., a .qgs file containing, among others, your line layer) you need to follow these steps:

  1. Go to QGIS->Project->Project Properties->Macros, check the Python macros option, and replace the whole code with this one (adjust the values indicating your layer and field names):

    from qgis.core import QgsMapLayerRegistry, QgsFeatureRequest
    def openProject():    
        # Initialize required variables
        myLayer = QgsMapLayerRegistry.instance().mapLayersByName( 'lines' )[0]
    
        # Update feature attributes when new features are added or geometry changes
        myLayer.featureAdded.connect( updateFeatureAttrs )
        myLayer.geometryChanged.connect( updateFeatureAttrs )
    
    # Slot, updates field values
    def updateFeatureAttrs( fId, geom=None ):
        myLayer = QgsMapLayerRegistry.instance().mapLayersByName( 'lines' )[0]
        lengthField = myLayer.fieldNameIndex( 'length' )
        startXField = myLayer.fieldNameIndex( 'startX' )
        startYField = myLayer.fieldNameIndex( 'startY' )
        endXField = myLayer.fieldNameIndex( 'endX' )
        endYField = myLayer.fieldNameIndex( 'endY' )
        f = myLayer.getFeatures( QgsFeatureRequest( fId ) ).next()    
        if not geom:
            geom = f.geometry() 
        myLayer.changeAttributeValue( fId, lengthField, geom.length() )
        myLayer.changeAttributeValue( fId, startXField, geom.vertexAt( 0 )[0] )
        myLayer.changeAttributeValue( fId, startYField, geom.vertexAt( 0 )[1] )
        myLayer.changeAttributeValue( fId, endXField, geom.asPolyline()[-1][0] )
        myLayer.changeAttributeValue( fId, endYField, geom.asPolyline()[-1][1] )
    
    def saveProject():
        pass
    
    def closeProject():
        pass
    
  2. Make sure you enable macros on your project, this way: Settings->Options->General->Enable macros: Always.

  3. Save your QGIS project.

Now, every time you open the .qgs file you've just saved, the attributes of your line layer will be automatically updated when you add a new feature or modify a geometry (i.e., no need to copy anything into the QGIS Python Console anymore).


2nd EDIT:

I've just published a plugin called AutoFields to help people to solve this kind of problems. I even made a video showing how to solve your ploblem, you can watch it at:

https://vimeo.com/germap/autofields-geometric-properties

AutoFields documentation: http://geotux.tuxfamily.org/index.php/en/geo-blogs/item/333-autofields-plugin-for-qgis

10
  • 3
    This is exactly what I was looking for. Actually I was using your code to catch the X,Y coordinates, that I´ve found in your old answer in this post link , the same that you post now. I've already tested the automation and it works perfectly . I saved the Python code as a file " .pycl " . If you can explain to me how it can be available when I open my QGIS project will be great. Thank you by the help. Commented Apr 20, 2016 at 17:48
  • 2
    German, that was an awesome answer! Thanks! I learned a great deal from you with what you posted!
    – jbgramm
    Commented Apr 21, 2016 at 23:50
  • 2
    I'm actually working on a plugin that'll allow you to do exactly that. As I can devote to its development only my spare time, I cannot tell you an expected release date. In the meantime, you could run my 1st code snippet after you add the Tramo.shp, adjusting: myLayer = QgsMapLayerRegistry.instance().mapLayersByName( 'Tramo' )[0] If you happen to be really interested in my future plugin, please send me any suggestions you have, I'd appreciate that. You can contact me at GeoTux. Commented May 20, 2016 at 17:17
  • 1
    That will be great to me (and for a lot of people, of course), I´m already using the code with this ajustment that you suggested. I´ll wait anxiously your new plugin, and if I think in something useful I contact you. Thank you for everything. Commented May 20, 2016 at 18:06
  • 1
    @LeoNazareth, would you like to test the plugin? It's getting ready, but I'd like to perform some tests before releasing it. If you're willing to test it, please send me an e-mail. Commented Jul 7, 2016 at 16:27
12

If you only need these fields inside QGIS, you can use virtual fields. These allow using an expression (like $length) that depends on other values or the geometry.

Open the field calculator, add a new field with the name length, check the "Virtual Field" checkbox and enter $length as expression (or something else for the other fields).

These will however not be saved to the excel file.

If you want to keep an excel file in sync with an shp file for geometry and include derived fields in the excel file, there is a plugin called ShpSync that is aware of this concept and updates fields automatically when features are changed, added or deleted.

1
  • In fact , the answer below works exactly how I want to do , but this plugin you said will work for the next step of my project . Thank you for your help Commented Apr 20, 2016 at 17:55
11

For QGIS 3 go to the Layers Properties => Attributes Form => choose your field with geometry values (in example, area) => type $area in the Defaults value box and check Apply default value on update. This also could be usefull: $perimeter, $y, $x, $id

3
2

I would put the data in a database (PostGIS) and extract the data to QGIS with a (probably materialized) view.

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