I am trying to run this code and keep getting the error: "Classified objects: Layer error: Collection.geometry: Unable to perform this geometry operation. Please specify a non-zero error margin". I assume it's because I the composite I used has some masked elements but I am not sure. The pixel-based classification works but not the OBIA.
//Retrieve image collection var collection = ee.ImageCollection("COPERNICUS/S2_SR")
//Draw the outline and clip the area of interest var states = ee.FeatureCollection("TIGER/2016/Counties");
//Choose the state of interest var states_outline = ee.FeatureCollection('TIGER/2018/States');
//Choose state code https://www.bls.gov/respondents/mwr/electronic-data-interchange/appendix-d-usps-state-abbreviations-and-fips-codes.htm //Choose county names of interest var state = states.filter(ee.Filter.eq('STATEFP', '48')); var county1 = state.filter(ee.Filter.eq('NAME', 'Lipscomb')); var county2 = state.filter(ee.Filter.eq('NAME', 'Ochiltree')); var county3 = state.filter(ee.Filter.eq('NAME', 'Hemphill')); var county4 = state.filter(ee.Filter.eq('NAME', 'Roberts')); var county5 = state.filter(ee.Filter.eq('NAME','Hutchinson')); var county6 = state.filter(ee.Filter.eq('NAME','Hansford')); var county7 = state.filter(ee.Filter.eq('NAME','Carson')); var county8 = state.filter(ee.Filter.eq('NAME','Gray')); var county9 = state.filter(ee.Filter.eq('NAME','Wheeler')); var county10 = state.filter(ee.Filter.eq('NAME','Denton')); var counties = county1.merge(county2).merge(county3).merge(county4).merge(county5) .merge(county6).merge(county7).merge(county8).merge(county9).merge(county10);
//Apply criteria var images = collection.filterBounds(counties) .filterDate('2022-04-01', '2022-09-15') .filterMetadata('CLOUDY_PIXEL_PERCENTAGE','less_than', 5) .sort("CLOUD_COVERAGE_ASSESSMENT") .map(maskS2clouds) .median() .clip(counties);
//Add visual parameters to the map - bands: natural colors var imageVisParam = {bands:['B4', 'B3', 'B2'], min:[0.10,0.10,0.10], max:[0.5,0.5,0.5]} Map.addLayer(images,imageVisParam,'Counties_clip');
//Calculate the ndvi
var ndvi = images.expression( ' ((NIR - RED) / (NIR + RED))', { 'NIR': images.select('B8'), 'RED': images.select('B4'),
}).rename('nd');
print(ndvi);
images = images.addBands(ndvi) print(images)
//var ndvi = images.normalizedDifference(['B4', 'B8']).rename('NDVI')
//var threshndvi = ndvi.gte(-0.057).and(ndvi.lte(0.004)) var threshndvi = ndvi.gte(-0.057).and(ndvi.lte(0.1)) var ndviVis = {min:0,max:1,palette:['white','green']} Map.addLayer(ndvi, ndviVis, "ndvi_treshold")
// Create a binary mask var mask = threshndvi.eq(1);
// Update the composite mask with the mask. var maskedcomposite = images.updateMask(mask); Map.addLayer(maskedcomposite, imageVisParam, "masked composite",true)
//Pixel-based classification
var SampleSize = 256 // You can reset SampleSize to other number; // The max here should be 2500, since you have only two class and so each of them could be half of 5000
var landcover_labels = 'landcover'
//combination of feature collections var combinedRoads = roads.merge(roads2).merge(roads3).merge(roads4);
var combinedGeometry = geometry.merge(polygons_DL)
var geometryCollection = combinedGeometry.limit(10);
var GeometryArea = ee.FeatureCollection. randomPoints(geometryCollection, SampleSize).map(function(i){ return i.set({'landcover': 1})})
var Nonfracking = ee.FeatureCollection. randomPoints(nonfracking, SampleSize).map(function(i){ return i.set({'landcover': 2})})
var RoadArea = ee.FeatureCollection. randomPoints(combinedRoads, SampleSize).map(function(i){ return i.set({'landcover': 3})})
// Train the feature collection containing my training sites (polygons) var trainingpoints = GeometryArea.merge(RoadArea).merge(Nonfracking);
print(trainingpoints)
//var newfc = geometry.merge(nonfracking).merge(roads2) var bands = ['nd']; var training = maskedcomposite.select(bands).sampleRegions({ collection: trainingpoints, properties: ['landcover'], scale: 20, geometries:true });
print(training,'training')
// Add a random column and split the GCPs into training and validation set var gcp = training.randomColumn() print(gcp,'gcp')
// We take the recommended ratio of 70% training, 30% validation var trainingGcp = gcp.filter(ee.Filter.gt('random', 0.3)); var validationGcp = gcp.filter(ee.Filter.lte('random', 0.3));
var classifier = ee.Classifier.smileRandomForest(50).train(trainingGcp, landcover_labels, bands //features: trainingGcp, landcover_labels, bands //classProperty: 'landcover', //inputProperties: bands );
var classified = maskedcomposite.select(bands).classify(classifier);
Map.addLayer(classified, {min: 0, max: 1, palette: ['#1e82ff', '#1c8b08', '#88ff72','#88ff72','#c2b2bc','#fbff2a','#008800']}, 'classification');
//Object-based classification var seeds = ee.Algorithms.Image.Segmentation.seedGrid(36);
print(maskedcomposite,'MC')
var snic = ee.Algorithms.Image.Segmentation.SNIC({ image: images, compactness: 0, connectivity: 8, neighborhoodSize: 64, size: 3, seeds: seeds });
print(snic,"snic");
Map.addLayer(snic.randomVisualizer(),{}, 'snic');
var clusters_snic = snic.select("clusters"); Map.addLayer(clusters_snic.randomVisualizer(),{}, 'clusters_snic'); print(clusters_snic,"clusters_snic");
// Compute per-cluster stdDev. var stdDev = images.addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.stdDev(), 'clusters', 256) Map.addLayer(stdDev, {min:0, max:0.1}, 'StdDev', false)
// Area, Perimeter, Width and Height var area = ee.Image.pixelArea().addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256) Map.addLayer(area, {min:50000, max: 500000}, 'Cluster Area', false)
var minMax = clusters_snic.reduceNeighborhood(ee.Reducer.minMax(), ee.Kernel.square(1)); var perimeterPixels = minMax.select(0).neq(minMax.select(1)).rename('perimeter'); Map.addLayer(perimeterPixels, {min: 0, max: 1}, 'perimeterPixels');
var perimeter = perimeterPixels.addBands(clusters_snic) .reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256); Map.addLayer(perimeter, {min: 100, max: 400}, 'Perimeter size', false);
var sizes = ee.Image.pixelLonLat().addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.minMax(), 'clusters', 256) var width = sizes.select('longitude_max').subtract(sizes.select('longitude_min')).rename('width') var height = sizes.select('latitude_max').subtract(sizes.select('latitude_min')).rename('height') Map.addLayer(width, {min:0, max:0.02}, 'Cluster width', false) Map.addLayer(height, {min:0, max:0.02}, 'Cluster height', false)
var objectPropertiesImage = ee.Image.cat([ snic.select('nd_mean'), stdDev, area, perimeter, width, height ]).float();
print(objectPropertiesImage, 'opi')
var training_obia = objectPropertiesImage.addBands(classified.select('landcover')) .updateMask(seeds) .sample(geometry, 5) var classifier = ee.Classifier.smileRandomForest(10).train(training_obia, 'landcover') Map.addLayer(objectPropertiesImage.classify(classifier), {min:0, max:254}, 'Classified objects')
//Apply criteria
var images = collection.filterBounds(counties)
.filterDate('2022-04-01', '2022-09-15')
.filterMetadata('CLOUDY_PIXEL_PERCENTAGE','less_than', 5)
.sort("CLOUD_COVERAGE_ASSESSMENT")
.map(maskS2clouds)
.median()
.clip(counties);
//Add visual parameters to the map - bands: natural colors
var imageVisParam = {bands:['B4', 'B3', 'B2'], min:[0.10,0.10,0.10], max:[0.5,0.5,0.5]}
Map.addLayer(images,imageVisParam,'Counties_clip');
//Calculate the ndvi
var ndvi = images.expression(
' ((NIR - RED) / (NIR + RED))', {
'NIR': images.select('B8'),
'RED': images.select('B4'),
}).rename('nd');
print(ndvi);
images = images.addBands(ndvi)
print(images)
//var ndvi = images.normalizedDifference(['B4', 'B8']).rename('NDVI')
//var threshndvi = ndvi.gte(-0.057).and(ndvi.lte(0.004))
var threshndvi = ndvi.gte(-0.057).and(ndvi.lte(0.1))
var ndviVis = {min:0,max:1,palette:['white','green']}
Map.addLayer(ndvi, ndviVis, "ndvi_treshold")
// Create a binary mask
var mask = threshndvi.eq(1);
// Update the composite mask with the mask.
var maskedcomposite = images.updateMask(mask);
Map.addLayer(maskedcomposite, imageVisParam, "masked composite",true)
//Pixel-based classification
var SampleSize = 256
// You can reset SampleSize to other number;
// The max here should be 2500, since you have only two class and so each of them could be half of 5000
var landcover_labels = 'landcover'
//combination of feature collections
var combinedRoads = roads.merge(roads2).merge(roads3).merge(roads4);
var combinedGeometry = geometry.merge(polygons_DL)
var geometryCollection = combinedGeometry.limit(10);
var GeometryArea = ee.FeatureCollection.
randomPoints(geometryCollection, SampleSize).map(function(i){
return i.set({'landcover': 1})})
var Nonfracking = ee.FeatureCollection.
randomPoints(nonfracking, SampleSize).map(function(i){
return i.set({'landcover': 2})})
var RoadArea = ee.FeatureCollection.
randomPoints(combinedRoads, SampleSize).map(function(i){
return i.set({'landcover': 3})})
// Train the feature collection containing my training sites (polygons)
var trainingpoints = GeometryArea.merge(RoadArea).merge(Nonfracking);
print(trainingpoints)
//var newfc = geometry.merge(nonfracking).merge(roads2)
var bands = ['nd'];
var training = maskedcomposite.select(bands).sampleRegions({
collection: trainingpoints,
properties: ['landcover'],
scale: 20,
geometries:true
});
print(training,'training')
// Add a random column and split the GCPs into training and validation set
var gcp = training.randomColumn()
print(gcp,'gcp')
// We take the recommended ratio of 70% training, 30% validation
var trainingGcp = gcp.filter(ee.Filter.gt('random', 0.3));
var validationGcp = gcp.filter(ee.Filter.lte('random', 0.3));
var classifier = ee.Classifier.smileRandomForest(50).train(trainingGcp, landcover_labels, bands
//features: trainingGcp, landcover_labels, bands
//classProperty: 'landcover',
//inputProperties: bands
);
var classified = maskedcomposite.select(bands).classify(classifier);
Map.addLayer(classified,
{min: 0, max: 1, palette: ['#1e82ff', '#1c8b08', '#88ff72','#88ff72','#c2b2bc','#fbff2a','#008800']},
'classification');
//Object-based classification
var seeds = ee.Algorithms.Image.Segmentation.seedGrid(36);
print(maskedcomposite,'MC')
var snic = ee.Algorithms.Image.Segmentation.SNIC({
image: images,
compactness: 0,
connectivity: 8,
neighborhoodSize: 64,
size: 3,
seeds: seeds
});
print(snic,"snic");
Map.addLayer(snic.randomVisualizer(),{}, 'snic');
var clusters_snic = snic.select("clusters");
Map.addLayer(clusters_snic.randomVisualizer(),{}, 'clusters_snic');
print(clusters_snic,"clusters_snic");
// Compute per-cluster stdDev.
var stdDev = images.addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.stdDev(), 'clusters', 256)
Map.addLayer(stdDev, {min:0, max:0.1}, 'StdDev', false)
// Area, Perimeter, Width and Height
var area = ee.Image.pixelArea().addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256)
Map.addLayer(area, {min:50000, max: 500000}, 'Cluster Area', false)
var minMax = clusters_snic.reduceNeighborhood(ee.Reducer.minMax(), ee.Kernel.square(1));
var perimeterPixels = minMax.select(0).neq(minMax.select(1)).rename('perimeter');
Map.addLayer(perimeterPixels, {min: 0, max: 1}, 'perimeterPixels');
var perimeter = perimeterPixels.addBands(clusters_snic)
.reduceConnectedComponents(ee.Reducer.sum(), 'clusters', 256);
Map.addLayer(perimeter, {min: 100, max: 400}, 'Perimeter size', false);
var sizes = ee.Image.pixelLonLat().addBands(clusters_snic).reduceConnectedComponents(ee.Reducer.minMax(), 'clusters', 256)
var width = sizes.select('longitude_max').subtract(sizes.select('longitude_min')).rename('width')
var height = sizes.select('latitude_max').subtract(sizes.select('latitude_min')).rename('height')
Map.addLayer(width, {min:0, max:0.02}, 'Cluster width', false)
Map.addLayer(height, {min:0, max:0.02}, 'Cluster height', false)
var objectPropertiesImage = ee.Image.cat([
snic.select('nd_mean'),
stdDev,
area,
perimeter,
width,
height
]).float();
print(objectPropertiesImage, 'opi')
var training_obia = objectPropertiesImage.addBands(classified.select('landcover'))
.updateMask(seeds)
.sample(geometry, 5)
var classifier = ee.Classifier.smileRandomForest(10).train(training_obia, 'landcover')
Map.addLayer(objectPropertiesImage.classify(classifier), {min:0, max:254}, 'Classified objects')