I would like to know if it is possible to automatically draw polygons patterns in a polygon.

With more details I would like to go from this : enter image description here

To this : enter image description here

The large rectangles are 6.90m*31.05m and can be reduce to 6.90m*1.15m. The spacing between each array is 11.5m, ideally I would like to be able to adapt this value.

I understand how to create the border, but not how to generate these polygons inside the first one. I think I am down to use the "model" and the PyQGIS parts of QGIS but I don't know where to begin.

I think I also have an optimisation issue about this problem.

  • A start in QGIS could be Create grid geoproccessing tool. Then figure out som way to delete some of the rectangles
    – BERA
    Mar 3 '21 at 10:01
  • 1
    There's a reason why proper PV-planning software costs a sh*tload of money. But yeah, if someone knew a solution, I'm in.
    – Erik
    Mar 3 '21 at 10:38

Well, I came across the issue again and had enough time on my hands, so here is a partly solution. I wasn't able to take maximum PV-table widths into account, as well as slight rotations which sometimes are used, based on the location.

This is a very long process, so I suggest you convert it to a model, which you may simply rerun using different parameters and areas.


  • a detailed polygon of your intended PV-area, taking into account trees, roads, creeks, etc
  • width of PV-modules, depth of PV-tables, minimum width of tables and space between tables
  • I strongly suggest to run the process using a m-based CRS


  1. Create a polygon grid based on the extent of your PV-area, using a grid-width of 1000 m, at grid-height equal to the depth of your PV-tables and a vertical displacement equal to the depth of your tables minus the space between your tables divided by two ((depth-space)/2)
  2. Each polygon gets assigned its own ID, now you need to extract every second polygon in order to create the space between the tables. Run extract by expression using $id % 2 = 1.
  3. clip the remaining polygons using your PV-area.
  4. Run multi-to-singlepart on the clipped polygons.
  5. Renew the existing ID-attribute of your polygons using $id.
  6. We need to save the x- and y- coordinates of the pole of inaccessibility of the polygons to the attribute table, since it is situated at the very center of the features and allows us to decide, which corner points are closest to the center and thus needed for creating the final PV-table feature. Run x(pole_of_inaccessibility($geometry,0.1)) in the field calculator in order to obtain the x-coordinate.
  7. Do the same for the y-coordinate: y(pole_of_inaccessibility($geometry,0.1))
  8. Extract the vertices of the polygons.
  9. Add the x-coordinate of each vertice to the attribute table using $x.
  10. Do the same for the y-coordinate ($y). These two steps could be omitted, but in my opinion it is a bit "safer" to create the following expressions while these values are stored.
  11. If you create a model, you need to add the depth of your tables to the attribute table (table_depth).
  12. Same goes for the module width (module_width).
  13. Now we go wild: Calculate the difference between the x-coordinate of the points and the pole of inaccessibility, while ingoring any points not situated at the top or bottom of their respective polygon. I called this field x_diff. This can be done using if(abs("y"-"y_pole")=("table-depth"/2),"x"-"x_pole",NULL). The if(...) checks, whether the point is situated exactly half the depth of your table north or south of the pole. If it is, you get the distance in m your point is east or west of the pole. Otherwise, you get returned NULL, which we need for the next step.
  14. Use extract by attributes running "x_diff" IS NOT NULL.
  15. Now we check which points are closest to their respective pole for both the points east and west of the pole: if("x_diff"<0,maximum("x_diff","id","x_diff"<0),minimum("x_diff","id","x_diff">0)). If your difference is negative, because your point is west of the pole, you get returned the maximum x-value for this polygon with a negative difference, otherwise you get the minumum x-value with a positive difference. I called this value shift.
  16. Based on difference and shift, we create new x-coordinates (x_new), if needed: if(x_diff != shift,x-(x_diff-shift),x). If the distance between point and pole is not the minimum distance, we substract the difference between distance and shift-value from the x-coordinate, otherwise we keep it.
  17. Naturally PV-modules have a fixed width, as we added them already to our attribute table. Now we use this width to check, if our total width of the table fits our module width, or if we need to shorten the table: if(x_new>x_pole,x_new-abs(((minimum("x_new","id")-x_new)%"module_width")),x_new). For each new x-coordinate east of the pole we substract the rest of the modulo-"module width" calculation to create a new-new x-coordinate (x_adapted).
  18. Run create points layer from table using the original y-coordinates and the adapted x-coordinates.
  19. Convert your points to path.
  20. Use extract by attribute to remove tables, which are not long enough (bounds_width($geometry)>@minimum_table-width)
  21. Convert your lines to polygons and save the outcome.

Image 1 shows an exemplary PV-area: enter image description here

Image 2 shows the same area after the workflow has been finished, using a table depth of 6.5 m and a spacing of 5.7 m: enter image description here

If you want to run this as a model, you might have to add a little additional workflow which grabs the width of your area of interest and returns it to your grid tool.

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.