I don't follow how you want this to work. What are the classes you want in your classification? Nevertheless, the reason for your error is this:
var label = 'Relative Height'
var sample = img.select(bands).sampleRegions({
collection: points,
properties: [label],
scale: 30
})
The points
collection doesn't contain any Relative Height
property. Even if it did, I don't think this quite makes sense.
You could maybe try something like the below. Note that I've never used the GEDI data, and have very limited knowledge of it. So take this approach with a grain of salt. I'd very much appreciate someone with more knowledge on this to jump in and comment.
I'm extracting reference data from the GEDI collection, create Landsat 8/Sentinel 1 composites for the corresponding month, and sample it for training data. A classifier is trained, then finally applied to a composite for a date range of your choosing.
var aoi = ee.Geometry.Polygon([[
[-52.992228448173776, 5.3639624330339855],
[-52.992228448173776, 5.216962579558439],
[-52.898158013603464, 5.216962579558439],
[-52.898158013603464, 5.3639624330339855]
]]).buffer(1e4)
var heightBand = 'rh95'
var gedi = ee.ImageCollection('LARSE/GEDI/GEDI02_A_002_MONTHLY')
.filterBounds(aoi)
.map(function (image) {
return image
.updateMask(image.select('quality_flag').eq(1))
.updateMask(image.select('degrade_flag').eq(0))
.select(heightBand)
})
var referenceData = gedi
.map(toReferenceData)
.filterMetadata('count', 'not_equals', 0)
var trainingData = referenceData
.map(toTrainingData)
.flatten()
var composite = toComposite('2021-01-01', '2022-01-01')
var classifier = ee.Classifier.smileRandomForest(25)
.train(trainingData, heightBand, composite.bandNames())
.setOutputMode('REGRESSION')
var classification = composite.classify(classifier)
print('trainingData', trainingData)
print('classifier', classifier.explain())
Map.addLayer(referenceData.flatten(), null, 'reference data', false)
Map.addLayer(gedi.mosaic().clip(aoi), {bands: heightBand, min: 0, max: 40, palette: '#fffdcd, #e1cd73, #aaac20, #5f920c, #187328, #144b2a, #172313'}, heightBand)
Map.addLayer(composite, {bands: 'red,green,blue', min: 0, max: 2000, gamma: 1.6}, 'optical')
Map.addLayer(composite, {bands: 'VV,VH,ratio_VV_VH', min: [-20, -25, 3], max: [0, -5, 14]}, 'sar')
Map.addLayer(classification, {min: 0, max: 40, palette: '#fffdcd, #e1cd73, #aaac20, #5f920c, #187328, #144b2a, #172313'}, 'height')
Map.centerObject(aoi, 12)
function toReferenceData(gedi) {
var referenceData = gedi
.sample({
region: aoi,
scale : 25,
numPixels : 200000,
geometries: true
})
.map(function (feature) {
// Can only use ints as classes
return feature.set('rh99', feature.getNumber('rh99').round())
})
return referenceData
.set({
year: gedi.getNumber('year'),
month: gedi.getNumber('month'),
count: referenceData.size()
})
}
function toTrainingData(referenceData) {
var year = referenceData.getNumber('year')
var month = referenceData.getNumber('month')
var fromDate = ee.Date.fromYMD(year, month, 1)
var toDate = fromDate.advance(1, 'month')
var composite = toComposite(fromDate, toDate)
return composite.sampleRegions({
collection: referenceData,
properties : [heightBand],
scale : 30
})
}
function toComposite(fromDate, toDate) {
return toOpticalComposite(fromDate, toDate)
.addBands(toSarComposite(fromDate, toDate))
}
function toSarComposite(fromDate, toDate) {
var composite = ee.ImageCollection('COPERNICUS/S1_GRD_FLOAT')
.filter(ee.Filter.and(
ee.Filter.bounds(aoi),
ee.Filter.date(fromDate, toDate),
ee.Filter.eq('instrumentMode', 'IW'),
ee.Filter.listContains('transmitterReceiverPolarisation', 'VV'),
ee.Filter.listContains('transmitterReceiverPolarisation', 'VH')
))
.map(function (image) {
return maskBorder(
toDb(
toGamma0(image)
)
).select(['VV', 'VH'])
})
.median()
return composite
.addBands(
composite.select('VV').divide(composite.select('VH')).rename('ratio_VV_VH')
)
function toGamma0(image) {
var gamma0 = image.expression('i/(cos(angle * pi / 180))', {
'i': image.select(['VV', 'VH']),
'angle': image.select('angle'),
'pi': Math.PI
})
return image.addBands(gamma0, null, true)
}
function toDb(image) {
return image.addBands(
image.select(['VV', 'VH']).log10().multiply(10), null, true
)
}
function maskBorder(image) {
var angle = image.select('angle')
return image
.updateMask(
angle.gt(31).and(angle.lt(45))
)
}
}
function toOpticalComposite(fromDate, toDate) {
return ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
.filterDate(fromDate, toDate)
.filterBounds(aoi)
.map(function (image) {
var qa = image.select('QA_PIXEL')
var cloudShadow = bitwiseExtract(qa, 4)
var snow = bitwiseExtract(qa, 5).rename('snow')
var cloud = bitwiseExtract(qa, 6).not().rename('cloud')
return image
.updateMask(
cloudShadow.not()
.and(snow.not())
.and(cloud.not())
)
.select(
['SR_B2', 'SR_B3', 'SR_B4', 'SR_B5', 'SR_B6', 'SR_B7'],
['blue', 'green', 'red', 'nir', 'swir1', 'swir2']
)
.multiply(2.75e-05)
.add(-0.2)
.multiply(10000)
.int16()
})
.median()
}
function bitwiseExtract(value, fromBit, toBit) {
if (toBit === undefined)
toBit = fromBit
var maskSize = ee.Number(1).add(toBit).subtract(fromBit)
var mask = ee.Number(1).leftShift(maskSize).subtract(1)
return value.rightShift(fromBit).bitwiseAnd(mask)
}
https://code.earthengine.google.com/52f8f4ee6d2ab759e4f0fd1acb1ea2a9
Best Answer
maybe it helps to specify ''setOutputMode'' to "regression" as done here https://doi.org/10.3390/rs10081167