Land-use mapping with Sentinel 1 & 2

Yeah! mapping with a 10m spatial resolution.

Both new Sentinel-1 and Sentinel-2 satellites take measurements of the earth with a very high spatial and good temporal resolution. These data are very useful for mapping landuse and landuse change. This tutorial show how simple algorithms can be used to identify areas with water, dense vegetation, settlements and rice paddies.

1. Import the Sentinel-1 and Sentinel-2 ImageCollection.

2. Define the geographic and temporal domain.

```// Define period
var startdate = ee.Date.fromYMD(2014,1,1);
var enddate = ee.Date.fromYMD(2016,12,1);

// Define geograpic domain
var Ca = ee.FeatureCollection('ft:1T7GmJ0tJCu4QwclY5qiG9EGBFgNhhNjttq8F7gQm');
Map.centerObject(Ca,8);
```

3. Filter the data.

```// filter s2 data&lt;/pre&gt;
var Sentinel2 = s2.filterBounds(Ca)
.filterDate(startdate, enddate)
.filterBounds(Ca);

// filter s1 data
var Sentinel1 =  ee.ImageCollection('COPERNICUS/S1_GRD')
.filterBounds(Ca)
.filterDate(startdate, enddate)
.select('VV');
```

4. remove the clouds from Sentinel-2

```// cloud function to remove clouds
var cloudfunction_ST2 = function(image){
//use add the cloud likelihood band to the image
//get pixels above the threshold
var cloud01 = quality.gt(0);
//create a mask from high likelihood pixels
//mask those pixels from the image
};

// remove the clouds
var ST2_nocloud = Sentinel2.map(cloudfunction_ST2);
```

5. Calculate the NDBI, NDVI and NDWI from the median of Sentinel-2

```// take the median
var st2median = ST2_nocloud.median();

// the normalized difference bare index
var ndbi = st2median.normalizedDifference(['B12', 'B8']);

// the normalized difference vegetation index
var ndvi = st2median.normalizedDifference(['B8', 'B4']);

// the normalize difference water index
var ndwi = st2median.normalizedDifference(['B3', 'B8']);
```

6. Set the threshold for the indices.

```// define thresholds
var bareThreshold = -0.32
var vegetationThreshold = 0.65
var waterThreshold = 0.2
```

7. Add the different layers to the canvas.

```// show the urban area
var ndbi_th = ndbi.gt(bareThreshold)
var ndbi_viz = {palette:&quot;111101&quot;};

// show the water areas
var ndwi_th = ndwi.gt(waterThreshold)
var ndwi_viz = {palette:&quot;24069b&quot;};

// show the forests
var ndvi_th = ndvi.gt(vegetationThreshold)
var ndvi_viz = {palette:&quot;006b0c&quot;};
```

8. Compare the wet from the dry conditions using a reducer function on the sentinel-1 images.

```// create a map of the wet and dry conditions from sentinel-1
var wet = Sentinel1.reduce(ee.Reducer.percentile([10]))
var dry = Sentinel1.reduce(ee.Reducer.percentile([90]))
```

9. Identify the rice paddies which are inundated in the wet season, but dry in the dry season.

```// calculate the difference between wet and dry conditions
```

10. Mountains can be identified as rice paddies, so we remove all slopes greater than 2 degrees.

```// remove the mountains from the data
var hydrosheds = ee.Image('WWF/HydroSHEDS/03VFDEM');
var terrain = ee.Algorithms.Terrain(hydrosheds);
var slope = terrain.select('slope');

// remove all slopes greater then 2 degrees
```

11. Also add the paddies to the map based on the threshold of -8.

```// set the paddy threshold

// select areas smaller than the threshold

```

Find the full script here.

1. Hi. Loving this procedure. However I am getting the following error at step 7:

Urban: Layer error: Feature, argument ‘geometry’: Invalid type. Expected: Geometry. Actual: Feature.
Water: Layer error: Feature, argument ‘geometry’: Invalid type. Expected: Geometry. Actual: Feature.
Vegetation: Layer error: Feature, argument ‘geometry’: Invalid type. Expected: Geometry. Actual: Feature.

What could be the issue?

1. Thanks for the speedy response! I am using the exact same script that you have here with some minor modifications at the names, and coordinates. See script below:

//Import the Sentinel-1 and Sentinel-2 ImageCollection.
var s2 = ee.ImageCollection(“COPERNICUS/S2”),
s1 = ee.ImageCollection(“COPERNICUS/S1_GRD”);

ee.ImageCollection(‘COPERNICUS/S2’);
ee.ImageCollection(‘COPERNICUS/S1’);

//Define the geographic and temporal domain

//Define period

var startdate = ee.Date.fromYMD(2016,1,1);
var enddate = ee.Date.fromYMD(2016,12,1);

// Define geographic domain.

var rectangle = ee.Geometry.Rectangle(37.99,-0.403, 38.44,-1.081 );
var Mwi = ee.Feature(rectangle);
Map.centerObject(Mwi, 12);

// filter s2 data
var Sentinel2 = s2.filterBounds(Mwi)
.filterDate(startdate, enddate)
.filterBounds(Mwi);

//// filter s1 data
var Sentinel1 = ee.ImageCollection(‘COPERNICUS/S1_GRD’)
.filterBounds(Mwi)
.filterDate(startdate, enddate)
.select(‘VV’);

// cloud function to remove clouds
var cloudfunction_ST2 = function(image){
//use add the cloud likelihood band to the image
//get pixels above the threshold
var cloud01 = quality.gt(0);
//create a mask from high likelihood pixels
//mask those pixels from the image
};

// remove the clouds
var ST2_nocloud = Sentinel2.map(cloudfunction_ST2);

// take the median
var st2median = ST2_nocloud.median();

//Calculate the NDBI, NDVI and NDWI from the median of Sentinel-2

// the normalized difference bare index
var ndbi = st2median.normalizedDifference([‘B12’, ‘B8’]);

// the normalized difference vegetation index
var ndvi = st2median.normalizedDifference([‘B8’, ‘B4’]);

// the normalize difference water index
var ndwi = st2median.normalizedDifference([‘B3’, ‘B8’]);

// Set the threshold for the indices
var bareThreshold = -0.32;
var vegetationThreshold = 0.65;
var waterThreshold = 0.2;

//Add the different layers to the canvas

// show the urban area
var ndbi_th = ndbi.gt(bareThreshold);
var ndbi_viz = {palette:”111101″};

// show the water areas
var ndwi_th = ndwi.gt(waterThreshold);
var ndwi_viz = {palette:”24069b”};

// show the forests
var ndvi_th = ndvi.gt(vegetationThreshold);
var ndvi_viz = {palette:”006b0c”};

2. Also, how did you decide on the thresholds for this:
var bareThreshold = -0.32;
var vegetationThreshold = 0.65;
var waterThreshold = 0.2;

1. There is a get link button in the GEE. This gives you an unique link for sharing your code. Can you share that one with me?

It’s always hard to come up with an exact value for a threshold. I determined them based on trial and error.

1. This looks great! Many many thanks for taking the time to help. Please tell me, where was my script going wrong? and how do you determine the thresholds? Do you click in the image?

2. Also, would you mind troubleshooting another script that I am trying to run to classify land cover using Sentinel 2? I used training data from a Worldview 2 image of the same site and saved them into a csv format.

Brought in the training data as a fusion table to classify sentinel images. but i get the following error:

‘classification: Layer error: No data was found in classifier training input’

where could I be going wrong?

Regards,

Pamela

3. Also, do you mind trouble shooting another script that I am trying to run. It is a supervised classification using CART and I extracted the training data from a WorldView2 image in form of pixels for 5 classes. I want to use the training data (in the form of fusion table) to classify Sentinel 2 data. Here is the link to the script:

Regards,

Pamela

3. Sorry, I have double posted. Kindly ignore.

4. Robin says:

Do you guys have experience to transform the image to vector via GEE? I would like to get the building information of the certain area.

5. Dragutin says:

Hello! I want to automated maped cropland grom my region od interesa. Can you jave any idea how vam I do in Google earth engine? Code?

6. syed says:

Iam trying to run your script in GEE but it fails to load the fusion table. How would I fix this? Any help would be appreciated.