I use this code to ~randomly place a grid of sample plots for forest inventory:
import numpy as np
from itertools import product
polylayer = iface.activeLayer() #Highlight polygon layer in layer tree
npoints = 8 #Change
pointlayer = QgsVectorLayer('Point?crs=epsg:3006', 'point' , 'memory') #Change epsg
prov = pointlayer.dataProvider()
for poly in polylayer.getFeatures():
geom = poly.geometry()
bbox = poly.geometry().boundingBox()
xmin, xmax, ymin, ymax = bbox.xMinimum(),bbox.xMaximum(),bbox.yMinimum(),bbox.yMaximum()
success = 0
while success == 0:
featlist = []
spacing = ((geom.area()/npoints)**0.5)*np.random.uniform(0.7,1.3) #Adjust random range. If code runs forever increase range.
nspacesx = np.ceil((xmax-xmin)/spacing)
nspacesy = np.ceil((ymax-ymin)/spacing)
randomstart = [xmin-spacing*np.random.random(),ymin-spacing*np.random.random()]
xlist=[randomstart[0]+(x*spacing) for x in range(int(nspacesx)+1)]
ylist=[randomstart[1]+(y*spacing) for y in range(int(nspacesy)+1)]
for x,y in product(xlist,ylist):
feat = QgsFeature()
feat.setGeometry(QgsPoint(x,y))
featlist.append(feat)
points_inside = [1 if f.geometry().intersects(geom.buffer(-5,10)) else 0 for f in featlist] #I use negative buffer to prevent points to end up near polygon edge. You need to adjust -5 m or remove the buffering.
#print(sum(points_inside))
if sum(points_inside)==npoints:
featlist = [p for p,i in zip(featlist, points_inside) if i==1]
prov.addFeatures(featlist)
success = 1
QgsProject.instance().addMapLayer(pointlayer)