My solution was to convert the polygons to raster (snapped to the land cover map and with the same resolution) using a unique polygon ID field for raster value.
Then I converted this raster to points, and ended up with nearly 35,000 points, with attributes POINTID and GRID_CODE. POINTID is a unique cell identifier and GRID_CODE matches the original polygon ID. I joined the original polygon attribute table to the new points table using the GRID_CODE and polygon ID. This then gave me an attribute table like this for the points layer:
POINTID GRID_CODE cell_count cells_buil cells_arab cells_impr cells_brdl cells_semi
1 1 45 9 8 1 1 0 0
2 2 45 9 8 1 1 0 0
3 3 45 9 8 1 1 0 0
4 4 45 9 8 1 1 0 0
5 5 45 9 8 1 1 0 0
6 6 45 9 8 1 1 0 0
So I have the number of cells of each land cover class (fields 4-8) that I want to distribute among the points (cells) with the same GRID_CODE (e.g. in this case 45).
The rest I did in R.
I read this table into R and used the following code to create a land cover class field:
library(foreign)
points2<-read.dbf("C:\\temp\\housing_points.dbf")
# add landcover attribute field filled with value for builtup (16)
points2$lcover<-rep(16, nrow(points2))
# get list of polygon IDs without duplicate rows for looping through
id.list<-unique(points2$GRID_CODE)
#create empty dataframe
points.new<-points2[0,]
for (i in id.list){
# select all points belonging to each polygon, one polygon at a time
polygon.pnts<-points2[points2$GRID_CODE==i,]
# extract the number of cells of each land cover class within that polygon
cells.arab<-polygon.pnts[1,5]
cells.impr<-polygon.pnts[1,6]
cells.brdl<-polygon.pnts[1,7]
cells.semi<-polygon.pnts[1,8]
# create vector of new land cover classes (4 = arable, 5 = improved grassland, 2 = bleaf, 6 = semi-natural grassland)
# will use to replace land cover later on
new.lcover<-rep(4,cells.arab)
new.lcover<-append(new.lcover, rep(5,cells.impr))
new.lcover<-append(new.lcover, rep(2,cells.brdl))
new.lcover<-append(new.lcover, rep(6,cells.semi))
if (length(new.lcover) > 1) {
new.lcover<-sample(new.lcover)
} # shuffle the land cover values if more than one cell (point) being replaced
# get total number of cells (points) of new land cover classes to know how many random rows are needed
total.other<- sum(cells.arab, cells.impr, cells.brdl, cells.semi)
# pick out relevant number of random rows, saving just the POINT_ID
random.rows<-sample(polygon.pnts$POINTID, size = total.other)
# overwrite built up land cover class for selected rows with other land cover classes (column 9)
polygon.pnts[polygon.pnts$POINTID %in% random.rows,9]<- new.lcover
# join into one dataframe for exporting
points.new<-rbind(points.new, polygon.pnts)
}
write.csv(points.new, "c:\\temp\\points_new.csv")
This took seconds to run in R
I then joined this csv file back to my points layer based on POINTID and converted to raster using the lcover field as the value.
Hope this helps someone in the future!