3

I am trying to split polygons into equally sized pieces using a grid. The polygons are all image segments derived from 2.4 meter satellite imagery. So each polygon is a grouping of 2.4m x 2.4m squares. I want to produce a QGIS processing tool to simplify my current workflow of individually running the "Create Grid" and "Split with Lines" QGIS processing tools. The workflow should also edit the selected polygon in the existing layer, not create a new layer.

Example input polygon (with the polygon to be split selected):enter image description here

Expected output from processing tool: enter image description here

from qgis.core import *
from qgis.gui import *
import qgis.utils
import processing
import re
from PyQt5.QtCore import *

#the spacing of the cut grid
cell_size = 2.4

#get current layer, selection, CRS from QGIS map canvas
layer = qgis.utils.iface.activeLayer()
selected = layer.selectedFeatures()
crs = qgis.utils.iface.activeLayer().crs().authid()

for lay in selected:
    #get the ID of the feature we've selected in QGIS map canvas
    id = lay.id
    print("working on: " + str(id))

    #get the extent of the selected feature
    x_max, y_max, x_min, y_min = re.split(":|,",  lay.geometry().boundingBox().toString().replace(" ", ""))
    extent = x_min + "," + x_max + "," + y_min + "," + y_max
    print(extent)

    #create our grid in memory
    grid_parameters = {"TYPE":  1,
                                    "EXTENT": extent,
                                    "HSPACING": cell_size,
                                    "VSPACING":cell_size,
                                    "HOVERLAY": 0,
                                    "VOVERLAY": 0,
                                    "CRS": crs,
                                    "OUTPUT": "memory_grid"}

    grid = processing.run("qgis:creategrid",  grid_parameters)

    #split the selected feature, store the results in memory
    split_parameters = {"INPUT": layer.getFeature(id),
                                    "LINES": grid["OUTPUT"],
                                    "OUTPUT": "memory_split"}

    split = processing.run("qgis:splitwithlines", split_parameters)

    #delete the original feature,  add the split results to the original layer

with edit(layer):
    layer.deleteFeature(id)
    layer.addFeatures(split["OUTPUT"])

When I run this code I get an error on line 39 (split parameters dictionary) that reads: "TypeError: QgsVectorLayer.getFeature: argument 1 has unexpected type 'builtin_function_or_method"

I have tried several different approaches, including directly feeding my selection, the entire layer, layer.getFeatures(), etc. but nothing has been an acceptable input for the Split with Lines tool.

4
  • What do you mean 'automate'? Automate for all features in a layer or for all shapefiles in a folder? Commented Apr 19, 2018 at 0:13
  • I clarified "automate" in my post above.
    – rosswin
    Commented Apr 19, 2018 at 2:05
  • To get rid of "unexpected type 'builtin_function_or_method" error, you should replace id = lay.id with id = lay.id() Commented Apr 19, 2018 at 2:11
  • Good catch! I am now getting "TypeError: reportError() missing 1 required positional argument: 'fatal error'" in the same location as before.
    – rosswin
    Commented Apr 19, 2018 at 2:16

2 Answers 2

3

If you don't need a script, "Create Grid" and "Intersection" tool are enough to solve your problem.

"Create Grid" tool: For CRS, select your layer CRS. (In this case, CRS must be projected, not geographic)

enter image description here

Result of "Create Grid":

enter image description here

"Intersection" tool:

enter image description here

Final Result:

enter image description here

Output Attribute Table: ('Segment_x' comes from polygons and expresses that which segment/polygon the grid is in)

enter image description here

If pixel size is 2.4, result will be as you need.

2
  • Thanks for taking a look. This is essentially the approach I am currently using and attempting to implement in the script. However, I am performing this workflow 1000s of times, and hand-entering the parameters and executing the workflow is tedious. Also, I would like the approach to edit the existing layer, not produce a new layer.
    – rosswin
    Commented Apr 19, 2018 at 2:03
  • Have you tried using the Graphical Modeler to automate your process?
    – csk
    Commented Apr 23, 2018 at 19:09
2

Final, working code. I mimplemented this in QGIS 2.18.19 since there is more material on PyQGIS and it still suits my workflows.

from qgis.core import *
from qgis.gui import *
import qgis.utils
import processing
import re
from PyQt4.QtCore import *

cell_size = 2.4

layer = qgis.utils.iface.activeLayer()
selected = layer.selectedFeatures()
crs = qgis.utils.iface.activeLayer().crs().authid()

ids = []
x_maxs = []
x_mins = []
y_maxs = []
y_mins = []

for lay in selected:
    id = lay.id()
    ids.append(id)
    print("working on: " + str(id))

    x_min, y_min, x_max, y_max = re.split(":|,",  lay.geometry().boundingBox().toString().replace(" ", ""))
    x_mins.append(float(x_min))
    x_maxs.append(float(x_max))
    y_mins.append(float(y_min))
    y_maxs.append(float(y_max))

extent = str(min(x_mins)) + "," + str(max(x_maxs)) + "," + str(min(y_mins)) + "," + str(max(y_maxs))
print(extent)

grid_parameters = {"TYPE":  1,
                                "EXTENT": extent,
                                "HSPACING": cell_size,
                                "VSPACING":cell_size,
                                #"HOVERLAY": 0,
                                #"VOVERLAY": 0,
                                "CRS": crs,
                                "OUTPUT": None}

grid = processing.runalg("qgis:creategrid",  grid_parameters)

intersection_parameters = {"INPUT":  layer,
                                           "INPUT2": grid["OUTPUT"],
                                            "IGNORE_NULL": True,
                                            "OUTPUT": None}

intersect = processing.runalg("qgis:intersection", intersection_parameters)

mem_intersect = QgsVectorLayer(intersect["OUTPUT"], "lyr_intersect", "ogr")

fields_to_delete = []
fieldnames = set(["left", "right",  "top", "bottom"])
for field in mem_intersect.fields():
    if field.name() in fieldnames:
        fields_to_delete.append(mem_intersect.fieldNameIndex(field.name()))

mem_intersect.dataProvider().deleteAttributes(fields_to_delete)
mem_intersect.updateFields()

features = []
for feat in mem_intersect.getFeatures():
    features.append(feat)

with edit(layer):
        layer.deleteFeatures(ids)
        layer.dataProvider().addFeatures(features)

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.