I have a FeatureCollection of the Burmese state and region boundaries. As my study site only includes one state (the Ayeyarwady Region), I would like to mask my other data (mangrove cover, in this case) by this state.
I tried the following:
// Load state and region boundaries of Myanmar
var MMR_RegionBoundaries = ee.FeatureCollection('users/simonsesytze/aeo_research/MMR_region_boundaries')
// Select Ayeyarwady Region
var AyeyarwadyBoundary = ee.Feature(MMR_RegionBoundaries.select('Ayeyarwady'))
// Load Global Mangrove Watch coverage
var GMW_2016 = ee.FeatureCollection('users/simonsesytze/aeo_research/GMW_2016')
// Filter mangroves to Ayeyarwady Region
var GMW_Ayeyarwady = GMW_2016.filterBounds(AyeyarwadyBoundary)
// Display layer
Map.addLayer(GMW_Ayeyarwady, {}, 'Mangroves in Ayeyarwady Region');
However, this results in an error:
Mangroves in Ayeyarwady Region: Layer error: Feature, argument 'geometry': Invalid type.
Expected type: Geometry.
Actual type: FeatureCollection.
How can I extract a geometry from the FeatureCollection and use it to mask a layer to?
Try
var GMW_Ayeyarwady = GMW_2016.filterBounds(AyeyarwadyBoundary.geometry());
Related
As a newbie to the google earth engine, I have been trying something (https://code.earthengine.google.com/6f45059a59b75757c88ce2d3869fc9fd) following a NASA tutorial (https://www.youtube.com/watch?v=JFvxudueT_k&ab_channel=NASAVideo). My last line (line 60) shows image.filter is not a function, while the one in the tutorial (line 34) is working. I am not sure what happened and how to sort this out?
//creating a new variable 'image' from the L8 collection data imported
var image = ee.Image (L8_tier1 //the details in the data will represent that the band resolution is 30m
//the details in the data will represent that the band resolution is 30m
//.filterDate ("2019-07-01","2021-10-03") //for a specific date range. maybe good to remove it for the function.
//the details in the data will represent that the band resolution is 30m
//the details in the data will represent that the band resolution is 30m
//.filterDate ("2019-07-01","2021-10-03") //for a specific date range. maybe good to remove it for the function.
.filterBounds (ROI) //for the region of interest we are interested in
//.sort ("COLUD_COVER") //for sorting the data between the range with a cloud cover, the metadata property we are interested in. Other way to do this is using the function below.
//.first() //this will make the image choose the first image with the least amount of cloud cover for the area. Other way to do this is using the function below.
);
//print ("Hague and Rotterdam", image); //printing the image in the console
//console on the right hand side will explain everything from the data
//id will show the image deatils and date of the image, for this case 29th July 2019
//under the properties tab cloud cover can be found, this is the least we can get for this area during this period
// //vizualisation of the data in the map with true color rendering
// var trueColour = {
// bands:["SR_B4","SR_B3","SR_B2"],
// min: 5000,
// max: 12000
// };
// Map.centerObject (ROI, 12); //for the centering the area in the center of the map with required zoom level
// Map.addLayer (image, trueColour, "Hague and Rotterdam"); //for adding the image with the variable of bands we made and naming the image
//Alternate way
//Function to cloud mask from the qa_pixel band of Landsat 8 SR data. In this case bits 3 and 4 are clouds and cloud shadow respectively. This can be different for different image sets.
function maskL8sr(image) {
var cloudsBitMask = 1 << 3; //remember to check this with the source
var cloudshadowBitMask = 1 << 4; //remember to check this with the source
var qa = image.select ('qa_pixel'); //creating the new variable from the band of the source image
var mask = qa.bitwiseAnd(cloudsBitMask).eq(0) //making the cloud equal to zero to mask them out
.and(qa.bitwiseAnd(cloudshadowBitMask).eq(0)); //making the cloud shadow equal to zero to mask them out
return image.updateMask(mask).divide(10000)
.select("SR_B[0-9]*")
.copyProperties(image, ["system:time_start"]);
}
// print ("Hague and Rotterdam", image);// look into the console now. How many images the code have downloaded!!!
//filtering imagery for 2015 to 2021 summer date ranges
//creating joint filter and applying to image collection
var sum21 = ee.Filter.date ('2021-06-01','2021-09-30');
var sum20 = ee.Filter.date ('2020-06-01','2020-09-30');
var sum19 = ee.Filter.date ('2019-06-01','2019-09-30');
var sum18 = ee.Filter.date ('2018-06-01','2018-09-30');
var sum17 = ee.Filter.date ('2017-06-01','2017-09-30');
var sum16 = ee.Filter.date ('2016-06-01','2016-09-30');
var sum15 = ee.Filter.date ('2015-06-01','2015-09-30');
var SumFilter = ee.Filter.or(sum21, sum20, sum19, sum18, sum17, sum16, sum15);
var allsum = image.filter(SumFilter);
Filtering is an operation you can do on ImageCollections, not individual Images, because all filtering does is choose a subset of the images. Then, in your script, you have (with the comments removed):
var image = ee.Image (L8_tier1
.filterBounds (ROI)
);
The result of l8_tier1.filterBounds(ROI) is indeed an ImageCollection. But in this case, you have told the Earth Engine client that it should be treated as an Image, and it believed you. So, then, the last line
var allsum = image.filter(SumFilter);
fails with the error you saw because there is no filter() on ee.Image.
The script will successfully run if you change ee.Image(...) to ee.ImageCollection(...), or even better, remove the cast because it's not necessary — that is,
var image = L8_tier1.filterBounds(ROI);
You should probably also change the name of var image too, since it is confusing to call an ImageCollection by the name image. Naming things accurately helps avoid mistakes, while you are working on the code and also when others try to read it or build on it.
I have a script to download Sentinel-1 images from Google Earth Engine, which works perfectly over UK regions and other parts of Europe. However, when I try to run it for a region of Norway, the image returned is blurred. I think this is because within the ee.imagecollection some of the images have a different crs projection.
Hence, my question is how do I filter the images to remove images with the other crs? Here is an example of how it looks in Google Earth Engine:
Sentinel-1 image of area of Norway in Google Earth Engine
and here is how a print out of the image collection looks like in Google Earth Engine showing the two projections (see features 0 and 3 showing EPSG: 32632 and EPSG 32633):
Print out in Google Earth Engine of Norway image collection
My Google Earth Engine Script is included below. To replicate the problem replace the Norway geometry with a drawn polygon.
var year = 2021;
var region = 9;
var mth = 'October';
var mthno1 = 10;
var mthno2 = 11;
var endday1 = 18;
var endday2 = 18;
var geometry = ee.FeatureCollection("users/nfigbfr/Norway");
var s1c = ee.ImageCollection('COPERNICUS/S1_GRD')
.filterBounds(geometry)
.filterDate(year+'-'+mthno1+'-'+endday1,year+'-'+mthno2+'-'+endday2)
.filter(ee.Filter.eq('transmitterReceiverPolarisation', ['VV','VH']))
.filter(ee.Filter.eq('instrumentMode', 'IW'))
.map(function(image) {
var edge = image.lt(-30.0);
var maskedImage = image.mask().and(edge.not());
return image.updateMask(maskedImage);
});
print(s1c)
var img = s1c.mean();
print(img)
var img = img.addBands(img.select('VV').subtract(img.select('VH')).rename('Ratio'));
var img = img.select(['VV','VH','Ratio']).toFloat();
print(img);
var img_display = img.select(['VV','VH','Ratio']).clip(geometry);
Map.centerObject(geometry);
Map.addLayer(img_display, {min: -25, max: 0});
Export.image.toDrive({
image: img,
description: 'Norway_mean_'+mth+year,
folder: 'Sentinel_1',
crs: 'EPSG:32632',
scale: 10,
maxPixels: 1e13,
region: geometry
});
The crs is a property of individual bands, not the images. I also haven't been able to find out if/how we can access the band properties for filtering.
However, here is a workaround:
var target_crs = 'EPSG:32671'
var s1c = ee.ImageCollection('COPERNICUS/S1_GRD')
.filterBounds(point)
.filterDate(year+'-'+mthno1+'-'+endday1,year+'-'+mthno2+'-'+endday2)
.filter(ee.Filter.eq('transmitterReceiverPolarisation', ['VV','VH']))
.filter(ee.Filter.eq('instrumentMode', 'IW'))
.map(function(image) {
var edge = image.lt(-30.0);
var maskedImage = image.mask().and(edge.not());
return image.updateMask(maskedImage);
})
.map(function(img){
var crs = img.select(['VV']).projection().crs()
var myImageWithProperties = x.set({
crs: crs})
return ee.Image(myImageWithProperties)
;})
.filter(ee.Filter.eq('crs', target_crs));
I added a .map() function that grabs the projection code (EPSG) from the VV band and sets it as an image property. Then we can filter the collection based on this property.
I've tried this on Sentinel-2 and it works fine. Still curious if there is a simpler way, though.
PS: this question is better suited for https://gis.stackexchange.com
I am trying to produce some Landsat images which are intersecting large rivers. The output of the code is ImageID. However, when I run the code, it takes about 5 mins and shows "The service is currently unavailable" or "User memory limit exceeded". I guess too many images are selected and sorted. Please help. Any suggestions would be truly appreciated.
https://code.earthengine.google.com/1167e0c6656b0e99a345d15643a671b7
var table2 = ee.FeatureCollection("users/bo_wang1/Yukon_River");
//1. Display the shapefile into the interactive map
//Display the view to the center of the screen and scale the view
Map.centerObject(table2,10);
//Define styling and determine the color of the shapefile
var styling = {color: 'red', fillColor: '00000000'};
Map.addLayer(table.style(styling));
//2. Loading L8 image collection (TOA reflectance)
var l8_collection= ee.ImageCollection('LANDSAT/LC08/C01/T1_SR');
//3. Filter by time window
var x1= l8_collection.filterBounds(table2)
.filterDate('2019-05-01', '2019-09-30')
.sort('CLOUD_COVER');
print ('L8 2019 image collection:',x1);
print('# images', x1.size());
// extract the different rows and paths
var distinctRows = x1.distinct(['WRS_ROW']).aggregate_array('WRS_ROW');
var distinctPaths = x1.distinct(['WRS_PATH']).aggregate_array('WRS_PATH');
print(distinctRows, distinctPaths)
//Extract least cloudy L8 scene in each tile
var imagePerPath = distinctPaths.map(function(path){
var imagePerRow = distinctRows.map(function(row){
var images = x1.filter(ee.Filter.and(ee.Filter.eq('WRS_ROW', row), ee.Filter.eq('WRS_PATH', path)));
return images.sort('CLOUD_COVER').first();
});
return imagePerRow;
});
var leastCloud = ee.ImageCollection.fromImages(imagePerPath.flatten());
// print and add the geometries of the images to the map
Map.addLayer(ee.FeatureCollection(leastCloud.map(function(image){return image.geometry()})))
print('leastCloud',leastCloud);
//Get the number of images
var count = leastCloud.size();
print('Count:', count);
//Get and print property and ImageID
print(leastCloud.first().propertyNames());
var imageID = leastCloud.aggregate_array('LANDSAT_ID');
print(imageID);
//Export Landsat_ID to CSV
Export.table.toDrive({
collection: leastCloud,
description: 'Get_ImageID',
folder: 'Shapefile from GEE',
fileFormat: 'CSV',
selectors: ['LANDSAT_ID'],
});
All this gee is new for me.
I'm trying to flatten and export a table resulting from reduceRegions. The resulting json is a FeatureCollection but trying to .flatten() will thrown an error.
// Import WDPA dataset
var dataset = ee.FeatureCollection('WCMC/WDPA/current/polygons');
//var roi = dataset.filter(ee.Filter.eq('WDPAID', 33046)); // Cacheu
var roi = dataset.filter(ee.Filter.eq('PARENT_ISO', 'GNB')).select('WDPAID'); // all PA in GNB
// Import Global Forest Change dataset.
var dataset = ee.Image('UMD/hansen/global_forest_change_2019_v1_7').clip(roi);
// Subset the loss year layer; make units absolute (instead of years since 2000).
var treeLoss = dataset.select('lossyear').add(2000).selfMask();
// Display year of forest loss detection to the map.
Map.setOptions('SATELLITE');
Map.addLayer(treeLoss, {
min: 2001,
max: 2019,
palette: ['0D0887', '5B02A3', '9A179B', 'CB4678',
'EB7852', 'FBB32F', 'F0F921']
}, 'Tree loss year');
var forestloss = treeLoss.reduceRegions({
'collection': roi,
'reducer': ee.Reducer.frequencyHistogram(),
'scale': 100,
'crs': 'EPSG:5070'})
.select('histogram');
This went well with a single feature in my roi but but when I try to use a featurecollection and add a .flatten() at this point, I get an error
"the input collection must be a collection of collections but the
element ... was feature, which is not a collection."
print(forestloss, 'forestloss');
Map.setOptions('SATELLITE');
Map.centerObject(roi)
Map.addLayer(roi, {}, 'WDPA GB', true);
link to code.
Any help will be much appreciated.
[EDITED] works fine with a single feature but not with a collection of features
.flatten() does only one thing: convert a feature collection of feature collections into a feature collection of those collections. In your case, you have a feature collection (the output of reduceRegions) which contains plain features, but each of those features has a property which is a dictionary.
In order to convert that to multiple features (rows in your exported table), you need to first map over the collection to convert the dictionary to a collection of features, and then flatten the collection.
var forestLossCollection =
treeLoss.reduceRegions({
'collection': roi,
'reducer': ee.Reducer.frequencyHistogram(),
'scale': 100,
'crs': 'EPSG:5070'
})
.map(function (lossInRegionFeature) {
// Get the histogram dictionary from the feature produced by reduceRegions.
var forestLossDict = ee.Dictionary(lossInRegionFeature.get('histogram'));
// Make a FeatureCollection out of the dictionary.
return ee.FeatureCollection(
forestLossDict
.map(function (key, value) {
// Construct a feature from the dictionary entry.
return ee.Feature(null, {
'system:index': key,
'WDPAID': lossInRegionFeature.get('WDPAID'),
'year': key,
'loss': value});
})
// dict.map() returns a dictionary with newly computed values;
// we just want the values in a list, to make a collection of.
.values());
})
// Flatten the collection of collections returned by the map().
.flatten();
print(forestLossCollection);
Export.table.toDrive({
collection: forestLossCollection,
fileNamePrefix: 'forestLoss',
fileFormat: 'csv',
selectors: ['WDPAID', 'year', 'loss'],
});
https://code.earthengine.google.com/2bcfd3d34fed5255e25d5a553558de36
I have some code to export an LS8 image to Drive using GEE. Somehow, the resolution of the image seems to be lower (larger pixels) than what I am able to see on the browser. How can I increase the resolution? This is the code I´ve been using, with two different options.
I attempted to use .resample() as a solution but it produced a single band image that did not look good.
geometry2 = /* color: #57d64b */ee.Geometry.Polygon(
[[[-78.2812086867645, 2.3386717366200585],
[-77.56984394067075, 2.3729749945579948],
[-77.72227924340513, 2.776314152654142],
[-78.20842426293638, 2.725560942159387]]]);
function maskL8sr(image)
{
// Bits 3 and 5 are cloud shadow and cloud, respectively.
var cloudShadowBitMask = ee.Number(2).pow(3).int();
var cloudsBitMask = ee.Number(2).pow(5).int();
// Get the pixel QA band.
var qa = image.select('pixel_qa');
// Both flags should be set to zero, indicating clear conditions.
var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
.and(qa.bitwiseAnd(cloudsBitMask).eq(0));
// Return the masked image, scaled to TOA reflectance, without the QA bands.
return image.updateMask(mask).divide(10000)
.select("B[0-9]*")
.copyProperties(image, ["system:time_start"]);
}
// Map the function over one year of data.
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
.filterDate('2016-04-01', '2018-7-31')
.map(maskL8sr)
var composite = collection.median();
var VIS = {bands: ['B5', 'B6', 'B4'], min: 0, max: 0.47};
// Display the results.
Map.addLayer(composite,VIS ,"compt2");
var params ={
crs: 'EPSG:4326',
maxPixels: 1e12,
region:geometry2
}
Export.image(composite,'Guapi',params);
Export.image.toDrive({
image: composite,
description: 'Guapi',
scale: 30,
region: geometry2,
maxPixels: 1000000000
});