1

So I have a polygon in QGIS (3.18) and it will always be a rectangle (I create it using minimum bounding geometry, oriented rectangle) I want to buffer the width out using some math to get the total width to a number divisible by a number input by the user, and I want to do the same to the height but the buffer amount will need to be different to get it divisible by the same number. What is the best way to go about this?

This is for a plugin so I need to do this in PyQGIS.

EDIT: the end goal here is to divide this rectangle up into a grid of squares (side length of whatever the user inputs), and the squares should fill the rectangle up without any partial squares. If there is an even better way to get to this end goal then great. But I figure I will need to get the dimensions of the rectangle correct first.

here is the code to create the rectangle:

        newconvexhulllayer = processing.run("qgis:minimumboundinggeometry", {
          'INPUT': self.poly_layer,
          'TYPE': 1,
          'OUTPUT': 'memory:'})

        QgsProject.instance().addMapLayer(newconvexhulllayer['OUTPUT'])

I don't know where to begin for code for the buffer since I need to buffer the width and height by a different amount. I'm not worried about the math I just need to know how to buffer the width and height differently in qgis.

3
  • 1
    What do you want exactly ? Can you provide an example ? If I understand correctly, if you have a rectangle 100 x 20, and the user want to divide it by 5, you want to know the "buffer" distance, in this example 20 for the width and 4 for the height ? May 10 at 15:55
  • lets say its 99 x 23, and the user input number is 20. then I will need to buffer the width out to get it to 100, and I will need to buffer the height out to get it to 40.
    – tbob
    May 10 at 16:02
  • Coding questions are expected to contain code. What have you tried, and where are you stuck?
    – Vince
    May 10 at 16:45

1 Answer 1

1

Here a PyQGIS solution for 1 feature :

from functools import lru_cache

@lru_cache(maxsize=6)
def orientation_choices(num_coords: int, axis: str = "x") -> list:
    if axis == "x":
        if num_coords == 4:  # diagonal rectangle
            return ["L", "L", "R", "R"]
        elif num_coords == 2:  # aligned with North rectangle
            return ["L", "R"]
        elif num_coords == 3:  # diamond rectangle
            return ["L", "0", "R"]

    if axis == "y":
        if num_coords == 4:  # diagonal rectangle
            return ["B", "B", "T", "T"]
        elif num_coords == 2:  # aligned with North rectangle
            return ["B", "T"]
        elif num_coords == 3:  # diamond rectangle
            return ["B", "0", "T"]


def which_rect_corner(vertices: list, vertex: QgsPoint) -> str:
    x_coords, y_coords = zip(*set((v.x(), v.y()) for v in vertices))
    x_coords = sorted(x_coords)
    y_coords = sorted(y_coords)
    pos_x_vertex = x_coords.index(vertex.x())
    pos_y_vertex = y_coords.index(vertex.y())
    choices_x = orientation_choices(len(x_coords), "x")
    choices_y = orientation_choices(len(y_coords), "y")
    return f'{choices_y[pos_y_vertex]}{choices_x[pos_x_vertex]}'


# replace here the variables with true values
rect_feature_index = 1
rect_width = 99
rect_height = 23
user_input = 20

# replace layer by the vector layer output of the QGIS alg
layer = iface.activeLayer()
current_rect_geom = layer.getFeature(rect_feature_index).geometry()


new_rect_width = (rect_width // user_input + [1, 0][rect_width % user_input == 0]) * user_input
new_rect_height = (rect_height // user_input + [1, 0][rect_width % user_input == 0]) * user_input

x_coord_delta = (new_rect_width - rect_width) / 2
y_coord_delta = (new_rect_height - rect_height) / 2

corners = [
    (
        vertex,
        which_rect_corner(current_rect_geom.vertices(), vertex)
    )
    for vertex in current_rect_geom.vertices()
]

for i, (vertex, corner) in enumerate(corners):
    vertex_x, vertex_y = vertex.x(), vertex.y()
    if corner == "BL":  # Bottom Left
        vertex_x -= x_coord_delta
        vertex_y -= y_coord_delta
    elif corner == "TL":  # Top Left
        vertex_x -= x_coord_delta
        vertex_y += y_coord_delta
    elif corner == "TR":  # Top Right
        vertex_x += x_coord_delta
        vertex_y += y_coord_delta
    elif corner == "BR":  # Bottom Right
        vertex_x += x_coord_delta
        vertex_y -= y_coord_delta
    elif corner == "B0":
        vertex_y -= y_coord_delta
    elif corner == "T0":
        vertex_y += y_coord_delta
    elif corner == "0L":
        vertex_x -= x_coord_delta
    elif corner == "0R":
        vertex_x += x_coord_delta

    # modify the vertex
    current_rect_geom.moveVertex(vertex_x, vertex_y, i)

with edit(layer):
    layer.changeGeometry(rect_feature_index, current_rect_geom)
4
  • Thanks so much for this. Seems that all works except for 2 corners have the x value go the wrong way and I can't seem to fix it. My work is pulling me a different direction for now so I'll have to try to trouble shoot this later. The example rectangle I'm working with for my data is sitting at about a 45 degree angle, so its close to a diamond shape, and the corners that have an issue are the very top one and the bottom one if that makes sense , the right most corner and left most are fine.
    – tbob
    May 11 at 22:13
  • I didn't think about diamond shape rectangles, answer edited. May 12 at 7:27
  • 1
    Thanks so much for this, but actually this one seems to not produce any change from the original geometry. here is the geometry I am using: <QgsGeometry: Polygon ((-497098.89258654043078423 -982100.14251322357449681, -520808.41675865603610873 -967862.51037820673082024, -544775.87417017761617899 -1007774.83606928959488869, -521066.34999806201085448 -1022012.46820430643856525, -497098.89258654043078423 -982100.14251322357449681))> I am in crs=esri:102008
    – tbob
    May 12 at 14:53
  • 1
    I fixed a little thing but it doesn't resolve the problem. For your geometry, the top and bottom vertices change side after the "buffer", so I think that's for this reason it doesn't work, the addition of coordinates isn't in the good way. But I don't know how to fix that. May 12 at 19:21

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.