QGIS Oriented Bounding Box – How to Get Manually Oriented Bounding Box of a Polygon in QGIS

qgisqgis-processing

I have polygons (blue) divided by lines (yellow) that are (more or less) parallel to the height contour lines. I want to calculate the height and width (i.e. bounding box) of the blue polygons in direction of the slope, i.e. perpendicular and parallel to the slope (represented by yellow line).

Judging from the name, the tool "oriented minimum bounding rectangle" sounded promising, but the orientation cannot be modified; it is always choosen in a way that minimizes the area of the polygon.

Is there a way to create a bounding box oriented in a specific direction, i.e. parallel to a line?

In below image, you see the "randomly" oriented bounding rectangles in magenta vs. sketched in dark green the result I would like (to ultimately calculate width and height of the polygon).
In below image, you see the "randomly" oriented bounding rectangles in magenta vs. sketched in dark green the result I would like (to ultimately calculate width and height of the polygon).

Best Answer

Credit to @geozelot for the concept

This expression is an implementation of geozelot's comment.

Breakdown of the expression
  • find the mean azimuth of all sequential pairs of vertices of all contour lines that intersect with each of the original polygons (perhaps median is a more appropriate statistic)
  • use that value to rotate the polygon
  • create an axis-aligned bounding box of the rotated polygon
  • rotate the bounding box back using the negative of the mean azimuth

To clarify, the rotation angle is calculated per polygon, it is not a global value.

Geometry by Expression tool settings

The expression can be used in the Geometry by Expression tool, using the polygon layer as the input layer, and Polygon as the Output geometry type.

enter image description here

Expression
with_variable(
    'center',                   
    centroid(bounds($geometry)),                         -- get the center point of the bouding box of the original polygon to use as the anchor point for rotation
    with_variable(
        'inter', 
        intersection(                                    -- get the intersection of the contour with the original polygon
            @geometry, 
            aggregate('contours', 'collect', @geometry)  -- change 'contours' for the name of your contours layer
        ),

        with_variable('nodes', nodes_to_points(@inter),
            
            with_variable('mean_azi',
                array_mean(                              -- get the mean azimuth of all the contour vertices that intersect with the original polygon
                    array_foreach(generate_series(1, num_geometries(@nodes)),
                        
                        azimuth(
                            geometry_n(@nodes, @element),
                            geometry_n(@nodes, @element - 1)
                        )
                    )
                ),
                
                rotate(                                  -- rotate the axis-aligned bounding box back to the mean azimuth of the intersecting contour
                    bounds(                              -- create the axis-aligned bounding box
                        rotate(                          -- rotate the polygon by the mean azimuth of the intersecting contour
                            $geometry,
                            degrees(@mean_azi),
                            @center                      -- rotate around the center of the bounding box of the original geometry
                        )
                    ),
                    degrees(-@mean_azi),                 -- negative angle to rotate back to starting angle
                    @center
                )
            )
        )
    )
)
Result

The red dotted outline is the generated bounding box, the solid blue outline is the oriented bounding box for comparison. The white points are the vertices used to obtain the mean azimuth of the contour segments.

enter image description here

Related Question