Modifying polygons to be more rectangular using PyQGIS

Next code uses analytical geometry to change each polygon quasi rectangular in rectangular and it could be used:

layer = iface.activeLayer()

feats = [ feat for feat in layer.getFeatures() ]

n = len(feats)

crs = layer.crs()
epsg = crs.postgisSrid()

uri = "Polygon?crs=epsg:" + str(epsg) + "&field=id:integer""&index=yes"

mem_layer = QgsVectorLayer(uri,
                           'rectangle',
                           'memory')

prov = mem_layer.dataProvider()

for feature in feats:

    geom = feature.geometry()

    xmin, ymin, xmax, ymax = geom.boundingBox().toRectF().getCoords()

    points = feature.geometry().asPolygon()[0]

    for i in range(len(points)-1):
        if points[i][1] == ymax and points[i+1][1] < points[i][1]:
            idx = i
        if points[i][1] == ymax and points[i-1][1] < points[i][1]:
            idx = i-1

    rectangle = []

    #x,y coordinates of first point
    x1 = points[idx][0] 
    y1 = points[idx][1]

    rectangle.append(QgsPoint(x1,y1))

    #x,y coordinates of second point
    x2 = points[idx+1][0] 
    y2 = points[idx+1][1]

    rectangle.append(QgsPoint(x2,y2))

    #slope for first line
    m1 = (y2 - y1) / (x2 - x1)

    #intercept at origin for first line
    int1 = y1 - m1 * x1

    #slope for second line
    m2 = m1

    #x,y coordinates of third point
    x3 = points[idx+2][0] 
    y3 = points[idx+2][1]

    #intercept at origin for second line
    int2 = y3 - m2 * x3

    #first perpendicular
    m3 = -1/m1

    #intercept at origin for second line
    int3 = y2 - m3 * x2

    #intersect point
    x4 = (int3 - int2)/(m2 - m3)
    y4 = m3*x4 + int3

    rectangle.append(QgsPoint(x4, y4))

    #second perpendicular
    m4 = -1/m1

    #intercept at origin for second perpendicular
    int4 = y1 - m4 * x1

    #intersect point
    x5 = (int4 - int2)/(m2 - m4)
    y5 = m4*x5 + int4

    rectangle.extend([QgsPoint(x5, y5),QgsPoint(x1, y1)])

    polygon = []

    polygon.append(rectangle)

    geom = QgsGeometry.fromPolygon(polygon)

    feat = QgsFeature()

    feat.setAttributes([i])
    feat.setGeometry(geom)
    prov.addFeatures( [feat] )

QgsMapLayerRegistry.instance().addMapLayer(mem_layer)

I tried it out with shapefile of next image:

enter image description here

After running the code at the Python Console of QGIS I got:

enter image description here


You can try to use the following approach that changes the geometry based on the bounding box and the angle of the first digitized edge. You can of course alter the angle with the one from the longest edge or something. Only works well when your polygons are near rectangular already (as it looks like).

Input in the console in Qgis and the layer needs to be selected and editable:

import shapely

from shapely import affinity

from shapely.wkb import loads

layer = qgis.utils.iface.activeLayer()

for feature in layer.getFeatures():

    azimuth = feature.geometry().vertexAt(0).azimuth(feature.geometry().vertexAt(1))

    bbox = QgsGeometry.fromRect(feature.geometry().boundingBox())

    input = loads(bbox.asWkb())

    shape = shapely.geometry.asShape(input)

    rotated = affinity.rotate(shape, azimuth-90.0)

    new_geom = QgsGeometry.fromWkt(rotated.wkt)

    layer.changeGeometry(feature.id(),new_geom)

Tags:

Pyqgis