1 Google Earth Engine

Google Earth Engine (GEE) (https://earthengine.google.com/) is a cloud-based platform for geospatial analysis and interactive visualization at planetary-scale. GEE hosts a data catalog (https://developers.google.com/earth-engine/datasets/) of publicly available satellite imagery and geospatial datasets, and provides an application programming interface (API) and an associated web-based IDE (https://code.earthengine.google.com/) for accessing, analyzing and visualizing geospatial datasets using Google's high-performance computing capabilities. This allows us to work with very large datasets and get insights into a wide variety of issues such as climate monitoring, deforestation, and environmental protection.

In this tutorial, we show how to search geospatial datasets in the data catalog, and how to visualize and analyze these datasets using the code editor which is a web-based IDE. Specifically, we show how to retrieve Italy administrative boundaries and NO2 concentration levels. Then, we show how to create maps of NO2 values averaged over February to May in 2019 and 2020, and how to save the maps as image files.

More information about the use and examples of GEE can be found at https://developers.google.com/earth-engine/.

2 Data Catalog

GEE's data catalog (https://developers.google.com/earth-engine/datasets/) includes a large variety of publicly available geospatial datasets including observations from a variety of satellite and aerial imaging systems, climate and weather, topographic and socio-economic datasets.

We can use the search engine of the data catalog to find geospatial datasets. Each dataset has an associated link where we can see a brief description of the dataset including information such as temporal availability, data provider and collection ID. The collection ID allow us to import the data into our script environment for visualization and processing.

3 Code Editor

The Code Editor (https://code.earthengine.google.com/) is a web-based IDE that we can use to develop Earth Engine applications. We can open the code editor by going to https://code.earthengine.google.com/. The center panel contains a JavaScript code editor where we can write, run and save our scripts. The bottom panel shows the map layers that are created by the scripts. On the left panel we can find examples, saved scripts, a searchable API reference and an asset manager for private data. Finally, the right panel has an inspector for querying the map, an output console, and a manager to monitor long-running tasks.

GEE's Code Editor (https://earthengine.google.com/platform/).

Figure 3.1: GEE's Code Editor (https://earthengine.google.com/platform/).

In addition to the code editor, other ways to interact with GEE are the explorer (https://explorer.earthengine.google.com/#workspace) which is a web application that allows anyone to explore the data catalog and run simple analyses, and the client libraries (https://github.com/google/earthengine-api) that provide JavaScript and Python wrappers for the Earth Engine application programming interface (API).

4 Example: NO2 concentration levels in Italy

Here we show an example on how to retrieve, analyze, and visualize NO2 concentration levels in Italy in 2019 and 2020 using the GEE's Code Editor (https://code.earthengine.google.com/).

4.1 Importing Italy administrative boundaries

We can obtain the boundaries of the world countries from the Large Scale International Boundary (LSIB) dataset which has collection ID "USDOS/LSIB_SIMPLE/2017". First, we create a FeatureCollection called worldcountries with the boundaries of the world countries. Note that a Feature is a geographic data structure that corresponds to a vector data type and is composed of a geometry and a dictionary of properties, and a FeatureCollection is a collection of Features.

The FeatureCollection worldcountries has a field called country_na with the names of the countries and we can use ee.Filter.eq() to select the boundaries of Italy.

var worldcountries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
var filterCountry = ee.Filter.eq('country_na', 'Italy');
var country = worldcountries.filter(filterCountry);

We visualize Italy boundaries by adding country as a layer to the map, centering the map, and setting the zoom equal to 6 as follows:

Map.addLayer(country);
Map.centerObject(country, 6);
Italy boundaries.

Figure 4.1: Italy boundaries.

4.2 Importing NO2 values

Next, we import the NO2 concentrations retrieved from Sentinel-5P NRTI NO2: Near Real-Time Nitrogen Dioxide and with collection ID "COPERNICUS/S5P/NRTI/L3_NO2". This data is an ImageCollection where each Image corresponds to a raster data type that is composed of bands and a dictionary of properties. In the GEE's data catalog, we can see that the temporal availability of the dataset is from July 2018 to September 2020, and the band with name NO2_column_number_density provides the total vertical column of NO2 (ratio of the slant column density of NO2 and the total air mass factor) which is given in \(mol/m^2\). We create a variable called no2ic with the NO2_column_number_density band as follows:

var no2ic = ee.ImageCollection('COPERNICUS/S5P/NRTI/L3_NO2').select('NO2_column_number_density');

4.3 Filtering and averaging NO2 values

Then, we define a variable filterMonth with the months February to May, and use filter() to keep only images for these months.

var filterMonth = ee.Filter.calendarRange(2, 5, 'month');
var  no2 = no2ic.filter(filterMonth);

In order to be able to compare NO2 values in 2019 and 2020, we calculate the NO2 average values over each of these two years. Specifically, we create an Image called no2pre with the average values in 2019, and an Image called no2post with the average values in 2020. We multiply the average NO2 values by 1e6 and to convert the image units from \(mol/m^2\) to \(\mu mol/m^2\) . Finally, we use clip() to clip the images to the Italy border.

var filter19 = ee.Filter.calendarRange(2019, 2019, 'year');
var filter20 = ee.Filter.calendarRange(2020, 2020, 'year');

var no2pre  = no2.filter(filter19).mean().multiply(1e6).clip(country);
var no2post = no2.filter(filter20).mean().multiply(1e6).clip(country);

Note that we are averaging NO2 values over all days during February to May in 2019 and 2020. Before doing this, we could assess the quality of the values (for example checking which locations are covered by clouds) and preprocess the images before calculating the average values.

4.4 Adding data to the map

We use Map.addLayer() to add the data to the map and be able to visualize the data. In the function, we specify the visualization parameters defined in vizParams with the minimum a maximum values and the color palette. We also give names no2pre and no2post to be able to identify the data in the map.

var vizParams = {
  min: 0,
  max: 200,
  palette: ['black', 'purple', 'green', 'red']
};

Map.addLayer(no2pre, vizParams, 'no2pre');
Map.addLayer(no2post, vizParams, 'no2post');
Average NO2 concentration values in February to May 2020.

Figure 4.2: Average NO2 concentration values in February to May 2020.

4.5 Visualizing maps using a screen split display

An alternative way to visualize the maps is using a screen split display that allows us to compare the maps side-by-side. The code to create this screen split visualization is the following:

// Add no2pre to the default Map
Map.addLayer(no2pre, vizParams, 'splitpre');

// Make another Map and add no2post to it
var Map2 = ui.Map();
Map2.addLayer(no2post, vizParams, 'splitpost');

// Link the default Map to the Map2
var linker = ui.Map.Linker([ui.root.widgets().get(0), Map2]);

// Create a SplitPanel which holds the linked maps side-by-side
// wipe is set to true to let the user swipe the handle back and forth between the two visualizations
var splitPanel = ui.SplitPanel({
  firstPanel: linker.get(0),
  secondPanel: linker.get(1),
  orientation: 'horizontal',
  wipe: true,
  style: {stretch: 'both'}
});

// Set the SplitPanel as the only thing in root
ui.root.widgets().reset([splitPanel]);

// Center the SplitPanel on coordinates (10, 44) and set zoom level to 6
linker.get(0).setCenter(10, 44, 6);
screen split display of NO2 maps in 2019 and 2020.

Figure 4.3: screen split display of NO2 maps in 2019 and 2020.

4.6 Saving maps

Finally, we can save the maps as images to our Google Drive account by using Export.image.toDrive(). In the function, we specify the region to export as country. If the region is not specified, the region will default to the viewport at the time of invocation.

Export.image.toDrive({image: no2pre,description: 'mappre', region: country});
Export.image.toDrive({image: no2post, description: 'mappost', region: country});

When we run this code, export tasks to save each of the images are created in the Code Editor Tasks tab. We need to click the Run buttons next to the tasks to start them. Then the images are created and we can access them in our Google Drive account.

5 Complete code

// Importing Italy administrative boundaries

var worldcountries = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
var filterCountry = ee.Filter.eq('country_na', 'Italy');
var country = worldcountries.filter(filterCountry);

Map.addLayer(country);
Map.centerObject(country, 6);

// Importing NO2 values

var no2ic = ee.ImageCollection('COPERNICUS/S5P/NRTI/L3_NO2').select('NO2_column_number_density');

// Filtering and averaging NO2 values

var filterMonth = ee.Filter.calendarRange(2, 5, 'month');
var  no2 = no2ic.filter(filterMonth);

var filter19 = ee.Filter.calendarRange(2019, 2019, 'year');
var filter20 = ee.Filter.calendarRange(2020, 2020, 'year');

var no2pre  = no2.filter(filter19).mean().multiply(1e6).clip(country);
var no2post = no2.filter(filter20).mean().multiply(1e6).clip(country);

// Adding data to the map

var vizParams = {
  min: 0,
  max: 200,
  palette: ['black', 'purple', 'green', 'red']
};

Map.addLayer(no2pre, vizParams, 'no2pre');
Map.addLayer(no2post, vizParams, 'no2post');

// Visualizing maps using a screen split display

// Add no2pre to the default Map
Map.addLayer(no2pre, vizParams, 'splitpre');
// Make another Map and add no2post to it
var Map2 = ui.Map();
Map2.addLayer(no2post, vizParams, 'splitpost');
// Link the default Map to the Map2
var linker = ui.Map.Linker([ui.root.widgets().get(0), Map2]);
// Create a SplitPanel which holds the linked maps side-by-side
// wipe is set to true to let the user swipe the handle back and forth between the two visualizations
var splitPanel = ui.SplitPanel({
  firstPanel: linker.get(0),
  secondPanel: linker.get(1),
  orientation: 'horizontal',
  wipe: true,
  style: {stretch: 'both'}
});
// Set the SplitPanel as the only thing in root
ui.root.widgets().reset([splitPanel]);
// Center the SplitPanel on coordinates (10, 44) and set zoom level to 6
linker.get(0).setCenter(10, 44, 6);

// Saving maps

Export.image.toDrive({image: no2pre,description: 'mappre', region: country});
Export.image.toDrive({image: no2post, description: 'mappost', region: country});


Creative Commons License
This work by is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.