2

Using the field calculator one can update the geometry of a feature in dependence of one of its attributes. The process is described in Updating feature geometry from attribute fields in QGIS

My goal is to update rectangles depending on "width" and "height" attributes. I have the formula to do so. The formula works and I have saved it as a custom expression. And updating the geometry using the field calculator works like a charm as well. However it is quite tedious to

  1. Change the attribute.
  2. Open field calculator
  3. Select field
  4. Select the custom formula to update geometry depending on width or height
  5. Select update field

It is possible to link attribute fields (e.g. length of a line, area of a polygon etc.) to the geometry, such that they automatically update when the geometry is edited - see Automatically updating geometry attributes in QGIS without using Virtual Fields

Is there a way to switch this arround, so that the geometry automatically adapts, when i edit the width and height attributes without me having to manually perform the 5 steps above?

2
  • Using a virtual layer...?
    – Babel
    Commented Sep 8, 2022 at 21:15
  • Hmm could work, however i would prefer not to clutter up my project with yet another Layer. Currently looking to write a custom action using pyqgis, so the workflow to update geometry would be reduced to 1 step instead of 5. However the preferred solution remains an automatic geometry update...
    – Robert
    Commented Sep 9, 2022 at 14:16

1 Answer 1

2

I ended up writing a custom python Script to achieve what i was looking for:

from qgis.core import *
import math 

layer = qgis.utils.iface.activeLayer()

def addHeight(fromPoint, newHeight, angle):
    newX = fromPoint.x() - math.sin(math.radians(angle)) * newHeight 
    newY = fromPoint.y() + math.cos(math.radians(angle)) * newHeight 
    return QgsPointXY(newX,newY)

def addWidth(fromPoint, newWidth, angle):
    newX = fromPoint.x() + math.cos(math.radians(angle)) * newWidth 
    newY = fromPoint.y() + math.sin(math.radians(angle)) * newWidth 
    return QgsPointXY(newX,newY)

def createNewRectangle(oldGeom, newHeight, newWidth, angle): 
    lowerLeft = oldGeom[0][0]
    lowerRight = addWidth(lowerLeft, newWidth, angle)
    uperRight = addHeight(lowerRight, newHeight, angle)
    upperLeft = addHeight(lowerLeft, newHeight, angle)
    newGeom =  [] 
    newGeom.append(lowerLeft) 
    newGeom.append(lowerRight) 
    newGeom.append(uperRight) 
    newGeom.append(upperLeft) 
    return [newGeom]

def update(fid, idx, value):
    attribute = layer.attributeDisplayName(idx)
    print(f"fid = {fid}, attribute = {attribute}, value = {value}")
    feature = layer.getFeature(fid)

    ansicht_hoehe_neu = \
        (float(feature["seite_hoehe"]) \
        - feature["seitenrand_oben"] \
        - feature["seitenrand_unten"]) \
        * feature["massstab"] / 1000.0
    ansicht_breite_neu = \
        (float(feature["seite_breite"]) \
        - feature["seitenrand_links"] \
        - feature["seitenrand_rechts"] \
        - feature["breite_schriftfeld"]) \
        * feature["massstab"] / 1000.0
    winkel = feature["winkel"]

    oldRectangle = feature.geometry().asPolygon()
    newRectangle = createNewRectangle(oldRectangle, ansicht_hoehe_neu, ansicht_breite_neu, winkel) 
    
    if newRectangle != oldRectangle:
        newGeom = QgsGeometry.fromPolygonXY(newRectangle)
        layer.changeGeometry(fid, newGeom)
        
layer.attributeValueChanged.connect(update)

This script only works for rectangles. It basically connects attribute changes of the active layer (meaning the layer which is selected layer at the time the script is started) and connects attribute changes of that layer to the update-function.

It is assumed, that the layer has the following attributes (attribute names are in german - english translation is given in brackets)

  • seite_hoehe (page height)
  • seite_breite (page width)
  • seitenrand_links (page margin left)
  • seitenrand_rechts (page margin right)
  • seitenrand_oben (page margin top)
  • seitenrand_unten (page margin bottom)
  • massstab (scale)
  • winkel (angle)

The attributes are used to calculate the resulting width and height of the world view and then (using the angle) to create a new rectangle to represent said world view and assign that rectangle as geometry of the corresponding object.

All in all this is a very "plan-centric" approach. The attributes representing angle, page width etc. have the advantage, that they can be "reused" to control the page layout of every single plan using a layout with atlas-functionality and heavy us of data driven overrides.

So basically in the end multiple plans with varying scales, rotations, page width and heights can be created using a single vector layer and a corresponding layout with atlas functionality.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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