Escolar Documentos
Profissional Documentos
Cultura Documentos
completo do curso)
Uma introdução prática ao sensoriamento remoto aplicado usando o Google Earth
Engine.
Ujaval Gandhi
Introdução
Configurando o ambiente
Inscreva-se no Google Earth Engine
Conclua o Pré-Trabalho da Aula
Introdução ao Sensoriamento Remoto
Introdução ao Google Earth Engine
Faça os testes
Obtenha os materiais do curso
Módulo 1: Fundamentos do Earth Engine
01. Olá, Mundo
Exercício
Salvando seu trabalho
02. Trabalhando com coleções de imagens
Exercício
03. Filtrando coleções de imagens
Exercício
04. Criando mosaicos e composições a partir de ImageCollections
Exercício
05. Trabalhando com coleções de recursos
Exercício
06. Importando Dados
Exercício
07. Clipes de Imagens
Exercício
08. Exportando Dados
Exercício
Tarefa 1
Módulo 2: Earth Engine Intermediário
01. Objetos da Máquina Terrestre
Exercício
02. Cálculo de Índices
Exercício
03. Cálculo em ImageCollections
Exercício
04. Mascaramento de Nuvem
Exercício
05. Redutores
Exercício
06. Gráficos de séries temporais
Exercício
Tarefa 2
Módulo 3: Classificação Supervisionada
Introdução ao aprendizado de máquina e classificação supervisionada
01. Classificação Básica Supervisionada
Exercício
02. Avaliação de Precisão
Exercício
03. Aperfeiçoamento da Classificação
Exercício
04. Resultados de Classificação de Exportação
Exercício
05. Cálculo de Área
Exercício
Módulo 4: Detecção de Mudanças
Introdução à Detecção de Mudanças
01. Alteração do Índice Espectral
Exercício
02. Alteração da Distância Espectral
Exercício
03. Classificação Direta de Mudança
Exercício
04. Comparação pós-classificação
Exercício
Módulo 5: Aplicativos do Earth Engine
01. Cliente x Servidor
Exercício
02. Usando elementos da interface do usuário
Exercício
03. Construindo e Publicando um App
Exercício
04. Publicação do aplicativo
Exercício
05. Crie um aplicativo de painel dividido
Exercício
Módulo 6: API Python do Google Earth Engine
Introdução à API Python
Google Colab
Ambiente de Desenvolvimento Local
01. Sintaxe da API do Python
Exercício
02. Conversão Automática de Código Javascript para Python
Exercício
03. Exportações em Lote
Exercício
04. Criando Gráficos em Python
Crie um gráfico interativo usando geemap
Crie um gráfico usando o Matplotlib
Exercício
05. Automatizando downloads
Suplemento
Técnicas Avançadas de Classificação Supervisionada
Ajuste de hiperparâmetros
Resultados da classificação pós-processamento
Análise de Componentes Principais (PCA)
Compósitos Multitemporais para Classificação de Culturas
Correlação computacional
Calculando a Matriz de Correlação de Banda
Calculando Área por Classe
Gráficos de assinatura espectral
Identifique GCPs mal classificados
Normalização e Padronização de Imagens
Calcular a importância do recurso
Suavização de Séries Temporais e Preenchimento de Gaps
Suavização da janela móvel
Interpolação Temporal
Suavização Savitzky-Golay
Modelos de interface do usuário
Adicionando uma legenda discreta
Adicionando uma legenda contínua
Alterar aplicativo de IU de visualização
Aplicativo de IU do NDVI Explorer
Compartilhamento de código e módulos de script
Compartilhando um único script
Compartilhamento de vários scripts
Compartilhamento de código entre scripts
Repositórios públicos úteis
Projetos Guiados
Obtenha o código
Projeto 1: Monitoramento da Seca
Projeto 2: Mapeamento de Cheias
Projeto 3: Extraindo séries temporais
Projeto 4: Análise da Cobertura do Solo
Recursos de aprendizagem
Créditos de dados
Licença
Citação e Referência
Introdução
O Google Earth Engine é uma plataforma baseada em nuvem que permite o processamento em larga escala de imagens de satélite para
detectar mudanças, mapear tendências e quantificar diferenças na superfície da Terra. Este curso abrange toda a gama de tópicos do Earth
Engine para fornecer aos participantes habilidades práticas para dominar a plataforma e implementar seus projetos de sensoriamento
remoto.
(https://docs.google.com/presentation/d/1q8HRDTqgQEp3Hmi8IG0T7djPLTC1wRig3jXrwFTmoVE/edit?usp=sharing)
Configurando o ambiente
Inscreva-se no Google Earth Engine
Se você já possui uma conta do Google Earth Engine, pode pular esta etapa.
Visite https://signup.earthengine.google.com/ (https://signup.earthengine.google.com/) e inscreva-se com sua conta do Google. Você pode
usar sua conta existente do Gmail para se inscrever. Geralmente leva de 1 a 2 dias para aprovação. Portanto, faça esta etapa o mais rápido
possível.
(https://www.youtube.com/watch?v=xAyNu9HbK8s)
(https://www.youtube.com/watch?v=kpfncBHZBto)
Faça os testes
Depois de assistir aos vídeos, preencha os 2 questionários a seguir
Se você não vir o repositório na seção Leitor , clique no botão Atualizar cache do repositório na guia Scripts e ele aparecerá.
Atualizar cache do repositório
Módulo 1: Fundamentos do Earth Engine
O Módulo 1 foi projetado para fornecer habilidades básicas para encontrar conjuntos de dados necessários para o seu projeto, filtrá-los para
sua região de interesse, aplicar o processamento básico e exportar os resultados. Dominar isso permitirá que você comece a usar o Earth
Engine para seu projeto rapidamente e economize muito tempo pré-processando os dados.
(https://www.youtube.com/watch?v=RV3Sv5iogHs)
O Editor de Código é um Ambiente de Desenvolvimento Integrado (IDE) para a API Javascript do Earth Engine. Ele oferece uma maneira fácil
de digitar, depurar, executar e gerenciar código. Digite o código abaixo e clique em Executar para executá-lo e veja a saída na guia Console .
Dica: você pode usar o atalho de teclado Ctrl+Enter para executar o código no Code Editor
Olá Mundo
// Variables
var city = 'Bengaluru';
var country = 'India';
print(city, country);
// List
var majorCities = ['Mumbai', 'Delhi', 'Chennai', 'Kolkata'];
print(majorCities);
// Dictionary
var cityData = {
'city': city,
'population': 8400000,
'elevation': 930
};
print(cityData);
// Function
var greet = function(name) {
return 'Hello ' + name;
};
print(greet('World'));
// This is a comment
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F01c_Hello_World_(exercise))
Isso ocorre porque o repositório de classe compartilhada é um repositório somente leitura . Você pode clicar em Sim para salvar uma cópia em
seu repositório. Se esta for a primeira vez que você está usando o Earth Engine, você será solicitado a escolher o nome da sua pasta pessoal .
Escolha o nome com cuidado, pois ele não pode ser alterado depois de criado.
02. Trabalhando com coleções de imagens
A maioria dos conjuntos de dados no Earth Engine vem como um arquivo ImageCollection . Um ImageCollection é um conjunto de dados
que consiste em imagens tiradas em diferentes horários e locais - geralmente do mesmo satélite ou provedor de dados. Você pode carregar
uma coleção pesquisando o Catálogo de dados do Earth Engine (https://developers.google.com/earth-engine/datasets) pelo ImageCollection
ID . Pesquise o conjunto de dados Sentinel-2 Nível 1C e você encontrará seu id COPERNICUS/S2_SR . Visite a página do Sentinel-2, Nível 1C
(https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2) e consulte a seção Explorar no Earth Engine para encontrar
o snippet de código para carregar e visualizar a coleção. Este snippet é um excelente ponto de partida para seu trabalho com este conjunto
de dados. Clique no botão Copiar amostra de código e cole o código no editor de código. Clique em Executare você verá os blocos de imagens
carregados no mapa.
No trecho de código, você verá uma função Map.setCenter() que define a viewport para um local específico e nível de zoom. A função usa
a coordenada X (longitude), a coordenada Y (latitude) e os parâmetros do Nível de Zoom. Substitua as coordenadas X e Y pelas coordenadas
da sua cidade e clique em Executar para ver as imagens da sua cidade.
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F02c_Image_Collections_(exercise))
Filtrar por metadados : você pode aplicar um filtro nos metadados da imagem usando filtros como ee.Filter.eq() ,
ee.Filter.lt() etc. Você pode filtrar por valores de CAMINHO/LINHA, número de órbita, cobertura de nuvens etc.
Filtrar por data : você pode selecionar imagens em um determinado intervalo de datas usando filtros como ee.Filter.date() .
Filtrar por local : você pode selecionar o subconjunto de imagens com uma caixa delimitadora, local ou geometria usando o
ee.Filter.bounds() . Você também pode usar as ferramentas de desenho para desenhar uma geometria para filtragem.
Depois de aplicar os filtros, você pode usar a size() função para verificar quantas imagens correspondem aos filtros.
Abrir no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F03b_Filtering_Image_Collection_(complete))
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
// Filter by metadata
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30));
// Filter by date
var filtered = s2.filter(ee.Filter.date('2019-01-01', '2020-01-01'));
// Filter by location
var filtered = s2.filter(ee.Filter.bounds(geometry));
// Instead of applying filters one after the other, we can 'chain' them
// Use the . notation to apply all the filters together
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry));
print(filtered.size());
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F03c_Filtering_Image_Collection_(exercise))
// Add one more filter in the script below to select images
// from only one of the satellites - Sentinel-2A - from the
// Sentinel-2 constellation
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
print(filtered.size());
Também podemos criar uma imagem composta aplicando critérios de seleção a cada pixel de todos os pixels da pilha. Aqui usamos a
median() função para criar uma composição onde cada valor de pixel é a mediana de todos os pixels da pilha.
Dica: Se você precisar criar um mosaico onde as imagens estejam em uma ordem específica, você pode
usar a .sort() função para classificar sua coleção por uma propriedade primeiro.
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry));
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F04c_Mosaics_and_Composites_(exercise))
// Create a median composite for the year 2020 and load it to the map
Pesquise por GAUL Second Level Administrative Boundaries e carregue a coleção. Esta é uma coleção global que contém todos os limites
Admin2. Podemos aplicar um filtro usando a ADM1_NAME propriedade para obter todos os limites Admin2 (ou seja, distritos) de um estado.
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F05c_Feature_Collections_(exercise))
Importando um Shapefile
Enquanto em um software de área de trabalho, o recorte é desejável para remover partes desnecessárias
de uma imagem grande e economizar tempo de computação, no Earth Engine, o recorte pode realmente
aumentar o tempo de computação. Conforme descrito no guia de práticas recomendadas de codificação
do Earth Engine (https://developers.google.com/earth-engine/guides/best_practices?hl=en#if-you-dont-
need-to-clip,-dont-use-clip) , evite recortar as imagens ou faça isso no final do script.
// Find the feature id by adding the layer to the map and using Inspector.
var filtered = urban.filter(ee.Filter.eq('system:index', '00000000000000002bf8'));
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry));
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F07c_Clipping_(exercise))
Dica: o Editor de código oferece suporte ao preenchimento automático de funções de API usando a
combinação Ctrl+Espaço . Digite alguns caracteres de uma função e pressione Ctrl+Espaço para ver as
sugestões de preenchimento automático. Você também pode usar a mesma combinação de teclas para
preencher todos os parâmetros da função automaticamente.
Depois de executar este script, a guia Tarefas será destacada. Alterne para a guia e você verá as tarefas em espera. Clique em Executar ao lado
de cada tarefa para iniciar o processo.
Ao clicar no botão Executar , uma caixa de diálogo de confirmação será solicitada. Verifique as configurações e clique em Executar para iniciar
a exportação.
Assim que a exportação terminar, um arquivo GeoTiff para cada tarefa de exportação será adicionado ao seu Google Drive na pasta
especificada. Você pode baixá-los e usá-los em um software GIS.
Abrir no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F08b_Export_(complete))
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var urban = ee.FeatureCollection('users/ujavalgandhi/e2e/ne_10m_urban_areas');
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry));
Export.image.toDrive({
image: exportImage,
description: 'Bangalore_Composite_Raw',
folder: 'earthengine',
fileNamePrefix: 'bangalore_composite_raw',
region: geometry,
scale: 10,
maxPixels: 1e9
});
Export.image.toDrive({
image: visualized,
description: 'Bangalore_Composite_Visualized',
folder: 'earthengine',
fileNamePrefix: 'bangalore_composite_visualized',
region: geometry,
scale: 10,
maxPixels: 1e9
});
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A01-Earth-
Engine-Basics%2F08c_Export_(exercise))
// Write the export function to export the results for your chosen urban area
Tarefa 1
Carregue os dados das luzes noturnas de 2019 e 2020. Compare as imagens da sua região e encontre as mudanças na cidade.
Atribuição 1 Saída Esperada
// Assignment
// Export the Night Lights images for May,2015 and May,2020
// Workflow:
// Load the VIIRS Nighttime Day/Night Band Composites collection
// Filter the collection to the date range
// Extract the 'avg_rad' band which represents the nighttime lights
// Clip the image to the geometry of your city
// Export the resulting image as a GeoTIFF file.
// Hint1:
// Hint2:
(https://docs.google.com/presentation/d/10qOyxhubkwnsAVjniW54ETgwUHq3DXYKo3HGb6Gemi0/edit?usp=sharing)
// Casting
// You get an error because Earth Engine doesn't know what is the type of 'value'
// We need to cast it to appropriate type first
var value = ee.Number(value);
var newValue = value.add(1);
print(newValue);
// Dictionary
// Convert javascript objects to EE Objects
var data = {'city': 'Bengaluru', 'population': 8400000, 'elevation': 930};
var eeData = ee.Dictionary(data);
// Once converted, you can use the methods from the
// ee.Dictionary module
print(eeData.get('city'))
// Dates
// For any date computation, you should use ee.Date module
Como regra geral, você sempre deve usar os métodos da API do Earth Engine em seu código, há uma
exceção em que você precisará usar o método Javascript do lado do cliente. Se você deseja obter a hora
atual, o servidor não sabe sua hora. Você precisa usar o método javascript e convertê-lo em um objeto
Earth Engine.
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F01c_Earth_Engine_Objects_(exercise))
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var geometry = ee.Geometry.Point([77.60412933051538, 12.952912912328241]);
// For more complex indices, you can use the expression() function
// Note:
// For the SAVI formula, the pixel values need to converted to reflectances
// Multiplyng the pixel values by 'scale' gives us the reflectance value
// The scale value is 0.0001 for Sentinel-2 dataset
var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']};
var ndviVis = {min:0, max:1, palette: ['white', 'green']};
var ndwiVis = {min:0, max:0.5, palette: ['white', 'blue']};
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F02c_Calculating_Indices_(exercise))
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var admin2 = ee.FeatureCollection('FAO/GAUL_SIMPLIFIED_500m/2015/level2');
// Exercise
// Calculate the Normalized Difference Built-Up Index (NDBI) for the image
// Hint: NDBI = (SWIR1 – NIR) / (SWIR1 + NIR)
// Visualize the built-up area using a 'red' palette
03. Cálculo em ImageCollections
Até agora, aprendemos como executar a computação em imagens únicas. Se você quiser aplicar algum cálculo - como calcular um índice - a
muitas imagens, precisará usar map() . Você primeiro define uma função que recebe 1 imagem e retorna o resultado da computação nessa
imagem. Em seguida, você pode map() executar essa função sobre o ImageCollection, que resulta em um novo ImageCollection com os
resultados do cálculo. Isso é semelhante a um loop for com o qual você talvez esteja familiarizado - mas o uso map() permite que a
computação seja executada em paralelo. Saiba mais em Mapeamento sobre uma ImageCollection (https://developers.google.com/earth-
engine/guides/ic_mapping)
// Write a function that computes NDVI for an image and adds it as a band
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
var palette = [
'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
'74A901', '66A000', '529400', '3E8601', '207401', '056201',
'004C00', '023B01', '012E01', '011D01', '011301'];
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F03c_Computation_on_Image_Collections_(exercise))
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var admin1 = ee.FeatureCollection('FAO/GAUL_SIMPLIFIED_500m/2015/level1');
// Composite
var composite = withIndices.median();
print(composite);
A maioria dos conjuntos de dados de sensoriamento remoto vem com uma banda QA ou Cloud Mask que contém as informações sobre se os
pixels estão nublados ou não. Seu editor de código contém funções predefinidas para mascarar nuvens para conjuntos de dados populares na
guia Scripts → Exemplos → Máscara de nuvem .
O script abaixo pega a função de mascaramento do Sentinel-2 e mostra como aplicá-la em uma imagem.
Map.centerObject(image);
Map.addLayer(image, rgbVis, 'Full Image', false);
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F04c_Cloud_Masking_(exercise))
// Exercise
// Apply the above cloud masking function to SR image
// Add the masked image to the map
05. Redutores
Ao escrever código de computação paralela, uma operação Reduce permite que você calcule estatísticas em uma grande quantidade de
entradas. No Earth Engine, você precisa executar a operação de redução ao criar compostos, calcular estatísticas, fazer análises de
regressão, etc. A API do Earth Engine vem com um grande número de funções redutoras integradas (como ee.Reducer.sum() ,
ee.Reducer.histogram() , ee.Reducer.linearFit() etc.) de operações estatísticas em dados de entrada. Você pode executar
redutores usando a reduce() função. O Earth Engine suporta a execução de redutores em todas as estruturas de dados que podem conter
vários valores, como imagens (redutores executados em bandas diferentes), ImageCollection, FeatureCollection, lista, dicionário etc. O
script abaixo apresenta conceitos básicos relacionados a redutores.
print(filtered.size());
var collMean = filtered.reduce(ee.Reducer.mean());
print('Reducer on Collection', collMean);
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F05c_Reducers_(exercise))
var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']};
var image = ee.Image('COPERNICUS/S2_HARMONIZED/20190223T050811_20190223T051829_T44RPR');
Map.addLayer(image, rgbVis, 'Image');
Map.addLayer(geometry, {color: 'red'}, 'Farm');
Map.centerObject(geometry);
// Exercise
// Compute the average NDVI for the farm from the given image
// Hint: Use the reduceRegion() function
06. Gráficos de séries temporais
Agora podemos reunir todas as habilidades que aprendemos até agora - filtrar, mapear, reduzir e mascarar nuvens para criar um gráfico de
valores médios de NDVI para um determinado farm durante 1 ano. A API do Earth Engine vem com suporte para funções de gráficos
baseadas na API do Google Chart. Aqui usamos a ui.Chart.image.series() função para criar um gráfico de série temporal.
var filtered = s2
.filter(ee.Filter.date('2017-01-01', '2018-01-01'))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry));
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A02-Earth-
Engine-Intermediate%2F06c_Time_Series_Charts_(exercise))
// Assignment
// Use Gridded Climate dataset to chart a 40+ year time series
// if temparature at any location
// Workflow
// Load the TerraClimate collection
// Select the 'tmmx' band
// Scale the band values
// Filter the scaled collection
// Use ui.Chart.image.series() function to create the chart
// Hint1
// Data needed to be scaled by 0.1
// map() a function and multiply each image
// Multiplying creates a new image that doesn't have the same properties
// Use copyProperties() function to copy timestamp to new image
var tmax = terraclimate.select('tmmx')
var tmaxScaled = tmax.map(function(image) {
return image.multiply(0.1)
.copyProperties(image,['system:time_start']);
})
// Hint2
// You will need to specify a scale in meters for charting
// Use projection().nominalScale() to find the
// image resolution in meters
var image = ee.Image(terraclimate.first())
print(image.projection().nominalScale())
Módulo 3: Classificação Supervisionada
Introdução ao aprendizado de máquina e classificação supervisionada
A classificação supervisionada é indiscutivelmente a mais importante técnica clássica de aprendizado de máquina em sensoriamento
remoto. As aplicações vão desde a geração de mapas de Uso/Cobertura do Solo até a detecção de alterações. O Google Earth Engine é
adequado exclusivamente para fazer classificação supervisionada em escala. A natureza interativa do desenvolvimento do Earth Engine
permite o desenvolvimento iterativo de fluxos de trabalho de classificação supervisionados, combinando muitos conjuntos de dados
diferentes no modelo. Este módulo abrange o fluxo de trabalho básico de classificação supervisionada, avaliação de precisão, ajuste de
hiperparâmetros e detecção de alterações.
(https://docs.google.com/presentation/d/19L1b5vsxb38xS8GlHNKOjvPZ0IGqDhv93681btMEL5w/edit?usp=sharing)
Curiosidade: os classificadores na API do Earth Engine têm nomes que começam com smile - como
ee.Classifier.smileRandomForest() . A parte do sorriso refere-se à biblioteca JAVA Statistical
Machine Intelligence and Learning Engine (SMILE) (https://haifengl.github.io/index.html) , que é usada
pelo Google Earth Engine para implementar esses algoritmos.
var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');
// The following collections were created using the
// Drawing Tools in the code editor
var urban = ee.FeatureCollection('users/ujavalgandhi/e2e/urban_gcps');
var bare = ee.FeatureCollection('users/ujavalgandhi/e2e/bare_gcps');
var water = ee.FeatureCollection('users/ujavalgandhi/e2e/water_gcps');
var vegetation = ee.FeatureCollection('users/ujavalgandhi/e2e/vegetation_gcps');
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.select('B.*');
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50).train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
// // Classify the image.
var classified = composite.classify(classifier);
Map.addLayer(classified, {min: 0, max: 3, palette: ['gray', 'brown', 'blue', 'green']}, '2019');
var s2 = ee.ImageCollection('COPERNICUS/S2_SR_HARMONIZED');
var urbanAreas = ee.FeatureCollection('users/ujavalgandhi/e2e/ne_10m_urban_areas');
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.select('B.*');
var rgbVis = {min: 0.0, max: 3000, bands: ['B4', 'B3', 'B2']};
Map.addLayer(composite, rgbVis, 'image');
// Exercise
// Add training points for 4 classes
// Assign the 'landcover' property as follows
// urban: 0
// bare: 1
// water: 2
// vegetation: 3
// // Train a classifier.
// var classifier = ee.Classifier.smileRandomForest(50).train({
// features: training,
// classProperty: 'landcover',
// inputProperties: composite.bandNames()
// });
// // // Classify the image.
// var classified = composite.classify(classifier);
// Map.addLayer(classified, {min: 0, max: 3, palette: ['gray', 'brown', 'blue', 'green']}, '2019');
Avaliação de Precisão
Não se empolgue ajustando seu modelo para lhe dar a maior precisão de validação. Você deve usar
medidas qualitativas (como inspeção visual de resultados) junto com medidas quantitativas para avaliar os
resultados.
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.select('B.*');
// Add a random column and split the GCPs into training and validation set
var gcp = gcp.randomColumn();
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50)
.train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
//**************************************************************************
// Accuracy Assessment
//**************************************************************************
// Alternate workflow
// This is similar to machine learning practice
var validation = composite.sampleRegions({
collection: validationGcp,
properties: ['landcover'],
scale: 10,
tileScale: 16
});
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A03-
Supervised-Classification%2F02c_Accuracy_Assessment_(exercise))
//**************************************************************************
// Accuracy Assessment
//**************************************************************************
// Exercise
Nossos recursos de treinamento têm mais parâmetros e contêm valores da mesma escala. O resultado é uma classificação muito melhorada.
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
// Function to remove cloud and snow pixels from Sentinel-2 SR image
function maskCloudAndShadowsSR(image) {
var cloudProb = image.select('MSK_CLDPRB');
var snowProb = image.select('MSK_SNWPRB');
var cloud = cloudProb.lt(10);
var scl = image.select('SCL');
var shadow = scl.eq(3); // 3 = cloud shadow
var cirrus = scl.eq(10); // 10 = cirrus
// Cloud probability less than 10% or cloud shadow classification
var mask = cloud.and(cirrus.neq(1)).and(shadow.neq(1));
return image.updateMask(mask);
}
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.map(maskCloudAndShadowsSR)
.select('B.*');
var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2};
Map.addLayer(composite, visParams, 'RGB');
// Machine learning algorithms work best on images when all features have
// the same range
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50)
.train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
//**************************************************************************
// Accuracy Assessment
//**************************************************************************
// Printing of confusion matrix may time out. Alternatively, you can export it as CSV
print('Confusion Matrix', testConfusionMatrix);
print('Test Accuracy', testConfusionMatrix.accuracy());
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A03-
Supervised-Classification%2F03c_Improving_the_Classification_(exercise))
// Exercise
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
// Function to remove cloud and snow pixels from Sentinel-2 SR image
function maskCloudAndShadowsSR(image) {
var cloudProb = image.select('MSK_CLDPRB');
var snowProb = image.select('MSK_SNWPRB');
var cloud = cloudProb.lt(10);
var scl = image.select('SCL');
var shadow = scl.eq(3); // 3 = cloud shadow
var cirrus = scl.eq(10); // 10 = cirrus
// Cloud probability less than 10% or cloud shadow classification
var mask = cloud.and(cirrus.neq(1)).and(shadow.neq(1));
return image.updateMask(mask);
}
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.map(maskCloudAndShadowsSR)
.select('B.*');
var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2};
Map.addLayer(composite, visParams, 'RGB');
// Machine learning algorithms work best on images when all features have
// the same range
// Function to Normalize Image
// Pixel Values should be between 0 and 1
// Formula is (x - xmin) / (xmax - xmin)
//**************************************************************************
function normalize(image){
var bandNames = image.bandNames();
// Compute min and max of the image
var minDict = image.reduceRegion({
reducer: ee.Reducer.min(),
geometry: geometry,
scale: 20,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var maxDict = image.reduceRegion({
reducer: ee.Reducer.max(),
geometry: geometry,
scale: 20,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var mins = ee.Image.constant(minDict.values(bandNames));
var maxs = ee.Image.constant(maxDict.values(bandNames));
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50)
.train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
//**************************************************************************
// Accuracy Assessment
//**************************************************************************
//**************************************************************************
// Exporting Results
//**************************************************************************
// Create a Feature with null geometry and the value we want to export.
// Use .array() to convert Confusion Matrix to an Array so it can be
// exported in a CSV file
var fc = ee.FeatureCollection([
ee.Feature(null, {
'accuracy': testConfusionMatrix.accuracy(),
'matrix': testConfusionMatrix.array()
})
]);
print(fc);
Export.table.toDrive({
collection: fc,
description: 'Accuracy_Export',
folder: 'earthengine',
fileNamePrefix: 'accuracy',
fileFormat: 'CSV'
});
Exercício
Também é uma boa ideia exportar a imagem classificada como um ativo. Isso permitirá que você importe a imagem classificada em outro
script sem executar todo o fluxo de trabalho de classificação. Use a função Export.image.toAsset() para exportar a imagem classificada como
um ativo.
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
// Function to remove cloud and snow pixels from Sentinel-2 SR image
function maskCloudAndShadowsSR(image) {
var cloudProb = image.select('MSK_CLDPRB');
var snowProb = image.select('MSK_SNWPRB');
var cloud = cloudProb.lt(10);
var scl = image.select('SCL');
var shadow = scl.eq(3); // 3 = cloud shadow
var cirrus = scl.eq(10); // 10 = cirrus
// Cloud probability less than 10% or cloud shadow classification
var mask = cloud.and(cirrus.neq(1)).and(shadow.neq(1));
return image.updateMask(mask);
}
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(geometry))
.map(maskCloudAndShadowsSR)
.select('B.*');
var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2};
Map.addLayer(composite, visParams, 'RGB');
// Machine learning algorithms work best on images when all features have
// the same range
// Function to Normalize Image
// Pixel Values should be between 0 and 1
// Formula is (x - xmin) / (xmax - xmin)
//**************************************************************************
function normalize(image){
var bandNames = image.bandNames();
// Compute min and max of the image
var minDict = image.reduceRegion({
reducer: ee.Reducer.min(),
geometry: geometry,
scale: 20,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var maxDict = image.reduceRegion({
reducer: ee.Reducer.max(),
geometry: geometry,
scale: 20,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var mins = ee.Image.constant(minDict.values(bandNames));
var maxs = ee.Image.constant(maxDict.values(bandNames));
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50)
.train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
// Exercise
// This will allows you to import the classified image in another script
// without running the whole classification workflow.
// Hint: For images with discrete pixel values, we must set the
// pyramidingPolicy to 'mode'.
// The pyramidingPolicy parameter should a dictionary specifying
// the policy for each band. A simpler way to specify it for all
// bands is to use {'.default': 'mode'}
A ee.Image.pixelArea() função usa uma projeção de área igual personalizada para cálculo de área. O
resultado é a área em metros quadrados independente da projeção da imagem de entrada. Saber mais
(https://groups.google.com/g/google-earth-engine-developers/c/Ccaorx-obVw/m/_ZQdP2wVAgAJ)
Map.addLayer(classified,
{min: 0, max: 3, palette: ['gray', 'brown', 'blue', 'green']},
'Classified Image 2019');
// Since our image has only 0 and 1 pixel values, the vegetation
// pixels will have values equal to their area
var areaImage = vegetation.multiply(ee.Image.pixelArea());
// Now that each pixel for vegetation class in the image has the value
// equal to its area, we can sum up all the values in the region
// to get the total green cover.
Se você deseja calcular a área coberta por cada classe, pode usar um redutor agrupado
(https://developers.google.com/earth-engine/reducers_grouping) . Consulte o Suplemento para ver um
trecho de código.
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A03-
Supervised-Classification%2F05c_Calculating_Area_(exercise))
// Exercise
// Compute and print the percentage green cover of the city
Mudança de Banda Única : Medindo a mudança em uma imagem de banda única ou um índice espectral usando um limite
Mudança de banda múltipla : Medindo a distância espectral e o ângulo espectral entre duas imagens multibanda
Classificação de mudança : classificação de passagem única usando imagem empilhada contendo bandas de antes e depois de um
evento
Comparação pós-classificação : comparando duas imagens classificadas e computando transições de classe
Nas seções a seguir, aplicaremos as técnicas de classificação supervisionada para detecção de mudanças usando imagens multitemporais.
(https://docs.google.com/presentation/d/1vdFTWJ61yDuVfbfhpnumQ8zuMPGwGcHpHsBTRgo_o5I/edit?usp=sharing)
Aqui aplicamos esta técnica para mapear a extensão e a gravidade de um incêndio florestal. A taxa de queima normalizada (NBR) é um índice
projetado para destacar áreas de vegetação queimada. Calculamos o NBR para imagens antes e depois. Em seguida, aplicamos um limite
adequado para encontrar áreas queimadas.
Detecção de Mudança de Índice Espectral
Map.centerObject(geometry, 10)
var s2 = ee.ImageCollection("COPERNICUS/S2")
// Apply a threshold
var threshold = 0.3
Exercício
Map.centerObject(geometry, 10)
var s2 = ee.ImageCollection("COPERNICUS/S2")
// Exercise
Aqui usamos esta técnica para detectar deslizamentos de terra usando compósitos antes/depois. Você pode aprender mais sobre essa
técnica na apresentação de Detecção de Mudanças de Craig D'Souza (https://goo.gl/xotYhk) .
Detecção de mudança de distância espectral
Abrir no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A04-
Change-Detection%2F01b_Spectral_Distance_Change_(complete))
var filtered = s2
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.select('B.*')
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A04-
Change-Detection%2F01c_Spectral_Distance_Change_(exercise))
var geometry = ee.Geometry.Polygon([[
[75.70357667713435, 12.49723970868507],
[75.70357667713435, 12.470171844429931],
[75.7528434923199, 12.470171844429931],
[75.7528434923199, 12.49723970868507]
]]);
Map.centerObject(geometry)
var s2 = ee.ImageCollection("COPERNICUS/S2")
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.select('B.*')
// Exercise
// Inspect the distance image and find a suitable threshold
// that signifies damage after the landslides
// Apply the threshold and create a new image showing landslides
// Display the results
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2
.filter(ee.Filter.date('2019-01-01', '2019-02-01'))
.filter(ee.Filter.bounds(bangalore))
.map(maskS2clouds)
var filtered = s2
.filter(ee.Filter.date('2020-01-01', '2020-02-01'))
.filter(ee.Filter.bounds(bangalore))
.map(maskS2clouds)
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50).train({
features: training,
classProperty: 'class',
inputProperties: stackedImage.bandNames()
});
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?accept_repo=users%2Fujavalgandhi%2FEnd-to-End-
GEE&scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A04-Change-Detection%2F03c_Classifying_Change_(exercise))
// Add an NDBI band to improve the detection of changes.
// use addNDBI() function to add the NDBI band to both 2019 and 2020 composite images
// Hint1: You can save the resulting image in the same variable to avoid changing
// a lot of code.
// var image = addNDBI(image)
Map.centerObject(bangalore)
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
// 2019 Jan
var filtered = s2
.filter(ee.Filter.date('2019-01-01', '2019-02-01'))
.filter(ee.Filter.bounds(bangalore))
.select('B.*')
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50).train({
features: training,
classProperty: 'landcover',
inputProperties: before.bandNames()
});
// 2020 Jan
var after = s2
.filter(ee.Filter.date('2020-01-01', '2020-02-01'))
.filter(ee.Filter.bounds(bangalore))
.select('B.*')
.median()
.clip(bangalore)
Exercício
Para converter objetos do lado do cliente em objetos do lado do servidor, você pode usar a função de API apropriada. As funções do
lado do servidor começam com ee. , como ee.Date() , ee.Image() etc.
Para converter objetos do lado do servidor em objetos do lado do cliente, você pode chamar .getInfo() um objeto do Earth Engine.
Para a API Python, esta é a única maneira de extrair informações de um objeto do lado do servidor, mas a API Javascript fornece um
método melhor (e preferido) para trazer objetos do lado do servidor para o lado do cliente usando o evaluate() método . Este
método recupera de forma assíncrona o valor do objeto, sem bloquear a interface do usuário - o que significa que permitirá que seu
código continue a ser executado enquanto busca o valor.
Dica: você pode usar ee.Algorithms.ObjectType() para obter o tipo de um objeto do lado do servidor
// getInfo() blocks the execution of your code till the value is fetched
// If the value takes time to compute, your code editor will freeze
// This is not a good user experience
var s2 = ee.ImageCollection("COPERNICUS/S2_SR")
var filtered = s2.filter(ee.Filter.date('2020-01-01', '2021-01-01'))
// You need to define a 'callback' function which will be called once the
// value has been computed and ready to be used.
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A05-Earth-
Engine-Apps%2F01c_Client_vs_Server_(exercise))
var date = ee.Date.fromYMD(2019, 1, 1)
print(date)
// Exercise
// The print statement below combines a client-side string
// with a server-side string - resulting in an error.
// Hint:
// Convert the client-side string to a server-side string
// Use ee.String() to create a server-side string
// Use the .cat() function instead of + to combine 2 strings
A API do Earth Engine fornece uma biblioteca de widgets de interface do usuário (UI) - como botões, menus suspensos, controles deslizantes
etc. - que podem ser usados para criar aplicativos interativos. Todas as funções da interface do usuário estão contidas no ui. pacote - como
ui.Select() , ui.Button() . Você pode criar esses elementos chamando essas funções com os parâmetros apropriados. Saiba mais na
seção API da interface do usuário (https://developers.google.com/earth-engine/guides/ui) do Earth Engine do Guia do usuário do Earth
Engine.
Esta seção mostra como criar um seletor suspenso usando o ui.Select() widget.
// You can add any widgets from the ui.* module to the map
var years = ['2014', '2015', '2016', '2017'];
// Exercise
O código abaixo mostra como construir um aplicativo chamado Night Lights Explorer (https://santhosh-m.users.earthengine.app/view/night-
lights-explorer) que permite a qualquer um escolher um ano/mês e carregar o VIIRS Nighttime Day/Night Band Composite para o mês
selecionado. Copie/cole o código abaixo no seu Editor de código e clique em Executar .
Você verá um painel no lado direito com 2 caixas suspensas e um botão. Esses são widgets da interface do usuário (IU) fornecidos pela API do
Earth Engine que permite ao usuário selecionar os valores interativamente. Você pode selecionar os valores para ano e mês e clicar no botão
Carregar para ver a imagem do mês selecionado.
monthStrings.evaluate(function(monthList) {
monthSelector.items().reset(monthList)
monthSelector.setPlaceholder('select a month')
})
Map.setCenter(76.43, 12.41, 8)
ui.root.add(mainPanel);
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A05-Earth-
Engine-Apps%2F03c_Building_an_App_with_UI_Widgets_(exercise))
// Exercise
// Add a button called 'Reset'
// Clicking the button should remove all loaded layers
Quando solicitado a Escolher um projeto de nuvem para seus aplicativos , você pode selecionar Criar um novo projeto de nuvem, inserir um ID
exclusivo e clicar em Selecionar .
Você pode receber um erro solicitando que aceite os termos de serviço. Clique no link Cloud Terms of Service e siga as instruções. Feito isso,
clique em OK .
De volta à caixa de diálogo Publicar novo aplicativo , deixe todas as outras configurações como padrão e clique em Publicar .
O aplicativo será hospedado no Google Cloud e você poderá acessá-lo clicando no Nome do aplicativo do seu aplicativo na caixa de diálogo
Gerenciar aplicativos .
Você verá seu aplicativo baseado no Earth Engine em execução no navegador. Qualquer pessoa pode acessar e interagir com o aplicativo
apenas visitando o URL do aplicativo.
O processo de publicação do aplicativo leva alguns minutos. Portanto, se você receber um erro
informando que seu aplicativo ainda não está pronto, verifique novamente em alguns minutos.
monthStrings.evaluate(function(monthList) {
monthSelector.items().reset(monthList)
monthSelector.setPlaceholder('select a month')
})
// Exercise
// Set the map center to your area of interst
// Replace the author label with your name
// Publish the app.
Map.setCenter(76.43, 12.41, 8)
var authorLabel = ui.Label('App by: Ujaval Gandhi');
mainPanel.add(authorLabel);
ui.root.add(mainPanel);
No painel esquerdo, carregaremos um composto Sentinel-2 para o ano de 2020. No painel direito, carregaremos a classificação de 11 classes
de cobertura do solo da mesma região.
var s2 = ee.ImageCollection("COPERNICUS/S2");
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.map(scaleImage);
Exercício
Tente no Editor de Código ↗ (https://code.earthengine.google.co.in/?scriptPath=users%2Fujavalgandhi%2FEnd-to-End-GEE%3A05-Earth-
Engine-Apps%2F05c_Split_Panel_App_(exercise))
var admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2");
var selected = admin2
.filter(ee.Filter.eq('ADM1_NAME', 'Karnataka'))
.filter(ee.Filter.eq('ADM2_NAME', 'Bangalore Urban'))
var geometry = selected.geometry();
Map.centerObject(geometry)
var s2 = ee.ImageCollection("COPERNICUS/S2");
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.map(scaleImage);
// Adding a Legend
// The following code creates a legend with class names and colors
return ui.Panel({
widgets: [colorBox, description],
layout: ui.Panel.Layout.Flow('horizontal')
});
};
// Exercise
// Hint: UI Widgets can only be shown once in the app. Remove the
// print statement before adding the legend to the map.
// Hint: Load the legend in the right-hand side map.
(https://docs.google.com/presentation/d/1hPVRnxp2Vp1VHXBtu36SH_UtEOjPz70KcDV-zGIin3U/edit?usp=sharing)
Google Colab
Uma maneira fácil de começar a usar a API Python do Google Earth Engine é por meio do Google Colab (https://colab.research.google.com/) .
O Google Colaboratory fornece um ambiente hospedado para executar notebooks Python sem a necessidade de instalar o Python
localmente. Ele também vem pré-instalado com muitos pacotes úteis - incluindo a API Python do Google Earth Engine. Você pode
simplesmente visitar https://colab.research.google.com/ (https://colab.research.google.com/) e iniciar um novo notebook.
Vindo da programação no Earth Engine através do Code Editor, você precisará adaptar um pouco seus scripts para poder rodar em Python.
Para a maior parte do seu código, você usará os objetos e funções do lado do servidor da Earth Engine API - que serão exatamente os
mesmos em Python. Você só precisa fazer algumas alterações sintáticas.
Inicialização
Em primeiro lugar, você precisa executar as seguintes células para inicializar a API e autorizar sua conta. Você será solicitado a fazer login na
conta e permitir o acesso para visualizar e gerenciar seus dados do Earth Engine . Depois de aprovar, você receberá um código de verificação
que precisa ser inserido no prompt. Esta etapa precisa ser feita apenas uma vez por sessão.
import ee
ee.Authenticate()
ee.Initialize()
Variáveis
O código Python não usa a palavra-chave 'var'
código javascript:
population = 881549
print(population)
s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
geometry = ee.Geometry.Polygon([[
[82.60642647743225, 27.16350437805251],
[82.60984897613525, 27.1618529901377],
[82.61088967323303, 27.163695288375266],
[82.60757446289062, 27.16517483230927]
]])
Continuação de linha
Python não usa ponto-e-vírgula para finalização de linha. Para indicar a continuação da linha, você precisa usar o caractere \
código javascript:
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var filtered = s2.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-02-01', '2019-03-01'))
.filter(ee.Filter.bounds(geometry));
filtered = s2 \
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) \
.filter(ee.Filter.date('2019-02-01', '2019-03-01')) \
.filter(ee.Filter.bounds(geometry))
Funções
Em vez da palavra- function chave, o Python usa a palavra- def chave. Além disso, as funções in-line são definidas usando
lambda funções anônimas.
No exemplo abaixo, agora também o and operador - que é capitalizado como And na versão Python para evitar conflito com o
and operador integrado. O mesmo se aplica aos operadores Or e . , , em Python também são escritos como , e
. Not true false null True False None
código javascript:
function maskS2clouds(image) {
var qa = image.select('QA60')
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;
var mask = qa.bitwiseAnd(cloudBitMask).eq(0).and(
qa.bitwiseAnd(cirrusBitMask).eq(0))
return image.updateMask(mask)//.divide(10000)
.select("B.*")
.copyProperties(image, ["system:time_start"])
}
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
def maskS2clouds(image):
qa = image.select('QA60')
cloudBitMask = 1 << 10
cirrusBitMask = 1 << 11
mask = qa.bitwiseAnd(cloudBitMask).eq(0).And(
qa.bitwiseAnd(cirrusBitMask).eq(0))
return image.updateMask(mask) \
.select("B.*") \
.copyProperties(image, ["system:time_start"])
def addNDVI(image):
ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi')
return image.addBands(ndvi)
withNdvi = filtered \
.map(maskS2clouds) \
.map(addNDVI)
Argumentos da Função
Argumentos nomeados para funções do Earth Engine precisam estar entre aspas. Além disso, ao passar os argumentos nomeados como um
dicionário, ele precisa ser passado usando a palavra- ** chave.
código javascript:
composite = withNdvi.median()
ndvi = composite.select('ndvi')
stats = ndvi.reduceRegion(**{
'reducer': ee.Reducer.mean(),
'geometry': geometry,
'scale': 10,
'maxPixels': 1e10
})
Imprimindo Valores
A print() sintaxe da função é a mesma. Mas você deve se lembrar que no Code Editor quando você cann print , o valor do objeto do
servidor é buscado e depois impresso. Você deve fazer isso explicitamente chamando getInfo() qualquer objeto do lado do servidor.
código javascript:
print(stats.get('ndvi')
print(stats.get('ndvi').getInfo())
Funções em linha
A sintaxe para definir funções in-line também é um pouco diferente. Você precisa usar a palavra- lambda chave.
código javascript:
Exercício
Pegue o trecho de código Javascript abaixo e escreva o código Python equivalente na célula abaixo.
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
print(filtered.size());
import ee
ee.Authenticate()
ee.Initialize()
O geemap (https://github.com/giswqs/geemap) é um pacote Python de código aberto que vem com muitos recursos úteis que ajudam você a
usar o Earth Engine com eficiência em Python.
Ele vem com uma função que pode ajudá-lo a traduzir automaticamente o código javascript do mecanismo de terra para Python.
O Google Colab não vem pré-instalado com o pacote, então nós o instalamos via pip.
try:
import geemap
except ModuleNotFoundError:
if 'google.colab' in str(get_ipython()):
print('geemap not found, installing via pip in Google Colab...')
!pip install geemap --quiet
import geemap
else:
print('geemap not found, please install via conda in your environment')
A conversão automática do código é feita chamando a geemap.js_snippet_to_py() função. Primeiro criamos uma string com o código
javascript.
javascript_code = """
var geometry = ee.Geometry.Point([107.61303468448624, 12.130969369851766]);
Map.centerObject(geometry, 12)
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED')
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2
.filter(ee.Filter.date('2019-01-01', '2019-12-31'))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
// Write a function that computes NDVI for an image and adds it as a band
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
"""
lines = geemap.js_snippet_to_py(
javascript_code, add_new_cell=False,
import_ee=True, import_geemap=True, show_map=True)
for line in lines:
print(line.rstrip())
filtered = s2 \
.filter(ee.Filter.date('2019-01-01', '2019-12-31')) \
.filter(ee.Filter.bounds(geometry)) \
.map(maskS2clouds)
# Write a function that computes NDVI for an image and adds it as a band
def addNDVI(image):
ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi')
return image.addBands(ndvi)
withNdvi = filtered.map(addNDVI)
composite = withNdvi.median()
palette = [
'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
'74A901', '66A000', '529400', '3E8601', '207401', '056201',
'004C00', '023B01', '012E01', '011D01', '011301']
Exercício
Pegue o trecho de código Javascript abaixo e use geemap -o para convertê-lo automaticamente em Python.
Uma das perguntas mais frequentes dos usuários do Earth Engine é: Como faço o download de todas as imagens de uma coleção ? A API Python
do Earth Engine vem com um ee.batch módulo que permite iniciar exportações em lote e gerenciar tarefas. A maneira recomendada de
fazer exportações em lote como essa é usar as ee.batch.Export funções da API do Python e usar um loop for do Python para iterar e
exportar cada imagem. O ee.batch módulo também oferece a capacidade de controlar Tarefas - permitindo que você automatize as
exportações.
import ee
ee.Authenticate()
ee.Initialize()
filtered = s2 \
.filter(ee.Filter.date('2019-01-01', '2020-01-01')) \
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30)) \
.filter(ee.Filter.bounds(geometry)) \
.map(maskS2clouds)
# Write a function that computes NDVI for an image and adds it as a band
def addNDVI(image):
ndvi = image.normalizedDifference(['B5', 'B4']).rename('ndvi')
return image.addBands(ndvi)
withNdvi = filtered.map(addNDVI)
image_ids = withNdvi.aggregate_array('system:index').getInfo()
print('Total images: ', len(image_ids))
tasks = ee.batch.Task.list()
for task in tasks:
task_id = task.status()['id']
task_state = task.status()['state']
print(task_id, task_state)
tasks = ee.batch.Task.list()
for task in tasks:
task_id = task.status()['id']
task_state = task.status()['state']
if task_state == 'RUNNING' or task_state == 'READY':
task.cancel()
print('Task {} canceled'.format(task_id))
else:
print('Task {} state is {}'.format(task_id, task_state))
Exercício
O código abaixo usa os dados do TerraClimate e cria um ImageCollection com 12 imagens mensais de temperatura máxima. Ele também
extrai a geometria para a Austrália da coleção LSIB. Adicione o código para iniciar uma tarefa de exportação para cada imagem na coleção
para austrália.
import ee
lsib = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017')
australia = lsib.filter(ee.Filter.eq('country_na', 'Australia'))
geometry = australia.geometry()
terraclimate = ee.ImageCollection('IDAHO_EPSCOR/TERRACLIMATE')
tmax = terraclimate.select('tmmx')
def scale(image):
return image.multiply(0.1) \
.copyProperties(image,['system:time_start'])
tmaxScaled = tmax.map(scale)
filtered = tmaxScaled \
.filter(ee.Filter.date('2020-01-01', '2021-01-01')) \
.filter(ee.Filter.bounds(geometry))
image_ids = filtered.aggregate_array('system:index').getInfo()
print('Total images: ', len(image_ids))
A API Python do Google Earth Engine não vem com um módulo de gráficos. Mas você pode usar módulos de terceiros para criar gráficos
interativos. Você também pode converter os objetos Earth Engine em um dataframe Pandas e plotá-lo usando bibliotecas Python como
Matplotlib
Este notebook mostra como usar o geemap pacote para criar um gráfico de série temporal a partir de uma ImageCollection.
Referências:
import ee
try:
import geemap
except ModuleNotFoundError:
if 'google.colab' in str(get_ipython()):
print('geemap not found, installing via pip in Google Colab...')
!pip install geemap --quiet
import geemap
else:
print('geemap not found, please install via conda in your environment')
ee.Authenticate()
ee.Initialize()
terraclimate = ee.ImageCollection('IDAHO_EPSCOR/TERRACLIMATE')
tmax = terraclimate.select('tmmx')
def scale_image(image):
return ee.Image(image).multiply(0.1)\
.copyProperties(image, ['system:time_start'])
tmaxScaled = tmax.map(scale_image)
Filtre a coleção.
Para traçar uma série de imagens em Python, devemos primeiro extrair os valores de cada imagem e criar uma FeatureCollection.
def extract_data(image):
stats = image.reduceRegion(**{
'reducer':ee.Reducer.mean(),
'geometry':geometry,
'scale':5000
})
properties = {
'month': image.get('system:index'),
'tmmx': stats.get('tmmx')
}
return ee.Feature(None, properties)
data = ee.FeatureCollection(filtered.map(extract_data))
print(data.first().getInfo())
options = {
'title': 'Max Monthly Temperature at Bangalore',
'legend_location': 'top-right',
'height': '500px',
'ylabel': 'Temperature (C)',
'xlabel': 'Date',
'colors': ['blue']
}
import geemap
df = geemap.ee_to_pandas(data)
df
Agora temos um dataframe regular do Pandas que pode ser plotado com matplotlib .
%matplotlib inline
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
fig.set_size_inches(20,10)
df.plot(ax=ax,
title='Max Monthly Temperature at Bangalore',
x='month',
ylabel='Temperature (C)',
kind='line')
plt.tight_layout()
Exercício
Personalize o gráfico acima plotando-o como um gráfico de linhas na cor vermelha.
Este script abaixo fornece um exemplo completo de automatização de um download usando a API do Google Earth Engine. Ele usa a API do
Google Earth Engine para calcular a umidade média do solo para um determinado período de tempo em todos os distritos de um estado. O
resultado é baixado como um arquivo JSON e salvo localmente.
Siga as etapas abaixo para criar um script para baixar dados do GEE.
ee.Initialize()
date_string = end_date.format('YYYY_MM')
filename = 'ssm_{}.csv'.format(date_string.getInfo())
# Saving to current directory. You can change the path to appropriate location
output_path = os.path.join(filename)
# Datasets
# SMAP is in safe mode and not generating new data since August 2022
# https://nsidc.org/data/user-resources/data-announcements/user-notice-smap-safe-mode
soilmoisture = ee.ImageCollection("NASA_USDA/HSL/SMAP10KM_soil_moisture")
admin2 = ee.FeatureCollection("FAO/GAUL_SIMPLIFIED_500m/2015/level2")
# Filter to a state
karnataka = admin2.filter(ee.Filter.eq('ADM1_NAME', 'Karnataka'))
mean = filtered.mean()
stats = mean.reduceRegions(**{
'collection': karnataka,
'reducer': ee.Reducer.mean().setOutputs(['meanssm']),
'scale': 10000,
'crs': 'EPSG:32643'
})
# Select columns to keep and remove geometry to make the result lightweight
# Change column names to match your uploaded shapefile
columns = ['ADM2_NAME', 'meanssm']
exportCollection = stats.select(**{
'propertySelectors': columns,
'retainGeometry': False})
features = exportCollection.getInfo()['features']
data = []
for f in features:
data.append(f['properties'])
2. No terminal, navegue até o diretório onde você criou o arquivo e digite o comando abaixo para executar o script.
python download_data.py
3. O script baixará os dados do GEE e salvará um arquivo em seu diretório atual.
Suplemento
Esta seção contém scripts úteis e trechos de código que podem ser adaptados para seus projetos.
function maskCloudAndShadowsSR(image) {
var cloudProb = image.select('MSK_CLDPRB');
var snowProb = image.select('MSK_SNWPRB');
var cloud = cloudProb.lt(10);
var scl = image.select('SCL');
var shadow = scl.eq(3); // 3 = cloud shadow
var cirrus = scl.eq(10); // 10 = cirrus
// Cloud probability less than 10% or cloud shadow classification
var mask = cloud.and(cirrus.neq(1)).and(shadow.neq(1));
return image.updateMask(mask);
}
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(boundary))
.map(maskCloudAndShadowsSR)
.select('B.*')
var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 3000, gamma: 1.2};
Map.centerObject(boundary)
Map.addLayer(composite, visParams, 'RGB');
// Machine learning algorithms work best on images when all features have
// the same range
//**************************************************************************
// Feature Importance
//**************************************************************************
//**************************************************************************
// Hyperparameter Tuning
//**************************************************************************
Usando clustering não supervisionado para substituir o valor classificado pelo valor majoritário em cada cluster.
Substituindo pixels isolados com valor circundante com um filtro majoritário.
//**************************************************************************
// Post process by clustering
//**************************************************************************
//**************************************************************************
// Post process by replacing isolated pixels with surrounding value
//**************************************************************************
// PCA computation is expensive and can time out when displaying on the map
// Export the results and import them back
Export.image.toAsset({
image: pca,
description: 'Principal_Components_Image',
assetId: 'users/ujavalgandhi/e2e/arkavathy_pca',
region: geometry,
scale: scale,
maxPixels: 1e10})
// Once the export finishes, import the asset and display
var pcaImported = ee.Image('users/ujavalgandhi/e2e/arkavathy_pca')
var pcaVisParams = {bands: ['pc1', 'pc2', 'pc3'], min: -2, max: 2};
//**************************************************************************
// Function to calculate Principal Components
// Code adapted from https://developers.google.com/earth-engine/guides/arrays_eigen_analysis
//**************************************************************************
function PCA(maskedImage){
var image = maskedImage.unmask()
var scale = scale;
var region = geometry;
var bandNames = image.bandNames();
// Mean center the data to enable a faster covariance reducer
// and an SD stretch of the principal components.
var meanDict = image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: region,
scale: scale,
maxPixels: 1e13,
tileScale: 16
});
var means = ee.Image.constant(meanDict.values(bandNames));
var centered = image.subtract(means);
// This helper function returns a list of new band names.
var getNewBandNames = function(prefix) {
var seq = ee.List.sequence(1, bandNames.length());
return seq.map(function(b) {
return ee.String(prefix).cat(ee.Number(b).int());
});
};
// This function accepts mean centered imagery, a scale and
// a region in which to perform the analysis. It returns the
// Principal Components (PC) in the region as a new image.
var getPrincipalComponents = function(centered, scale, region) {
// Collapse the bands of the image into a 1D array per pixel.
var arrays = centered.toArray();
// Perform an eigen analysis and slice apart the values and vectors.
var eigens = covarArray.eigen();
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(boundary))
.map(maskCloudAndShadowsSR)
// There are 3 distinct crop seasons in the area of interest
// Jan-March = Winter (Rabi) Crops
// April-June = Summer Crops / Harvest
// July-December = Monsoon (Kharif) Crops
var cropCalendar = ee.List([[1,3], [4,6], [7,12]])
var visParams = {bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3, gamma: 1.2};
Map.addLayer(rabi, visParams, 'Rabi')
Map.addLayer(harvest, visParams, 'Harvest')
Map.addLayer(kharif, visParams, 'Kharif')
Correlação computacional
Uma técnica útil para ajudar na classificação da cultura é modelar a correlação entre a precipitação e as mudanças na vegetação. Isso
permite que o modelo capture respostas diferenciadas à precipitação (ou seja, colheitas alimentadas por invasão versus florestas
permanentes). Primeiro preparamos uma coleção de imagens onde cada imagem consiste em 2 bandas - precipitação cumulativa para cada
mês e NDVI médio para o próximo mês. Isso criará 11 imagens por ano que mostram precipitação e NDVI defasado de 1 mês em cada pixel. A
coleção é então reduzida usando o ee.Reducer.pearsonsCorrelation() que produz uma correlation banda. Valores positivos
mostrarão regiões onde a precipitação causou um aumento no NDVI. Adicionar esta faixa à sua imagem de entrada para classificação ajudará
muito o classificador na separação de diferentes tipos de vegetação.
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
// Write a function that computes NDVI for an image and adds it as a band
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
var s2Filtered = s2
.filter(ee.Filter.date('2020-01-01', '2021-01-01'))
.filter(ee.Filter.bounds(geometry))
.map(maskCloudAndShadowsSR)
.map(addNDVI)
// Rainfall
var chirps = ee.ImageCollection("UCSB-CHG/CHIRPS/PENTAD");
var chirpsFiltered = chirps
.filter(ee.Filter.date('2020-01-01', '2021-01-01'))
// Monsoon months
var months = ee.List.sequence(1, 11)
// Monthly Images
var byMonth = months.map(function(month) {
var monthlyRain = chirpsFiltered
.filter(ee.Filter.calendarRange(month, month, 'month'))
var totalRain = monthlyRain.sum()
Map.addLayer(correlation.select('correlation'),
{min:-1, max:1, palette: ['red', 'white', 'green']}, 'Correlation');
Map.addLayer(positive.selfMask(),
{palette: ['yellow']}, 'Positive Correlation', false);
// This image has 18 bands and we want to compute correlation between them.
// Get the band names
// These bands will be the input variables to the model
var bands = composite.bandNames();
print(bands);
// You can also export the sampled points and calculate correlation
// in Python or R. Reference Python implementation is at
// https://courses.spatialthoughts.com/python-dataviz.html#feature-correlation-matrix
Export.table.toDrive({
collection: samples,
description: 'Feature_Sample_Data',
folder: 'earthengine',
fileNamePrefix: 'feature_sample_data',
fileFormat: 'CSV',
selectors: bands.getInfo()
});
// Create a 2 band image with the area image and the classified image
// Divide the area image by 1e6 so area results are in Sq Km
var areaImage = ee.Image.pixelArea().divide(1e6).addBands(classified);
Também podemos traçar assinaturas espectrais de todas as amostras de treinamento para uma classe e verificar a qualidade do conjunto de
dados de treinamento. Se todas as amostras de treinamento mostrarem assinaturas semelhantes, isso indica que você fez um bom trabalho
ao coletar as amostras apropriadas. Você também pode detectar possíveis outliers desses gráficos.
Esses gráficos fornecem métodos qualitativos e visuais para verificar a separabilidade das classes. Para métodos quantitativos, pode-se
aplicar medidas como distância espectral, distância de Mahalanobis, distância de Bhattacharyya, distância de Jeffreys-Matusita (JM) etc.
Você pode encontrar o código para eles nesta resposta do Stack Exchange (https://gis.stackexchange.com/a/323778/5160) .
Assinaturas médias para todas as classes
var fc = ee.FeatureCollection(groups.map(function(item) {
// Extract the means
var values = ee.Dictionary(item).get('mean')
var groupNumber = ee.Dictionary(item).get('group')
var properties = ee.Dictionary.fromLists(bands, values)
var withClass = properties.set('class', classNames.get(groupNumber))
return ee.Feature(null, withClass)
}))
print(chart)
print(chart)
}
classChart(0, 'Urban')
classChart(1, 'Bare')
classChart(2, 'Water')
classChart(3, 'Vegetation')
Map.centerObject(gcp)
var arkavathy = basin.filter(ee.Filter.eq('HYBAS_ID', 4071139640))
var boundary = arkavathy.geometry()
var rgbVis = {
min: 0.0,
max: 3000,
bands: ['B4', 'B3', 'B2'],
};
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(boundary))
.select('B.*')
// Add a random column and split the GCPs into training and validation set
var gcp = gcp.randomColumn()
var trainingGcp = gcp.filter(ee.Filter.lt('random', 0.6));
var validationGcp = gcp.filter(ee.Filter.gte('random', 0.6));
//**************************************************************************
// Function to Normalize Image
// Pixel Values should be between 0 and 1
// Formula is (x - xmin) / (xmax - xmin)
//**************************************************************************
function normalize(image){
var bandNames = image.bandNames();
// Compute min and max of the image
var minDict = image.reduceRegion({
reducer: ee.Reducer.min(),
geometry: geometry,
scale: 10,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var maxDict = image.reduceRegion({
reducer: ee.Reducer.max(),
geometry: geometry,
scale: 10,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var mins = ee.Image.constant(minDict.values(bandNames));
var maxs = ee.Image.constant(maxDict.values(bandNames));
//**************************************************************************
// Function to Standardize Image
// (Mean Centered Imagery with Unit Standard Deviation)
// https://365datascience.com/tutorials/statistics-tutorials/standardization/
//**************************************************************************
function standardize(image){
var bandNames = image.bandNames();
// Mean center the data to enable a faster covariance reducer
// and an SD stretch of the principal components.
var meanDict = image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: geometry,
scale: 10,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var means = ee.Image.constant(meanDict.values(bandNames));
var centered = image.subtract(means)
return standardized
}
var standardizedImage = standardize(image)
var normalizedImage = normalize(image)
Map.addLayer(image,
{bands: ['B4', 'B3', 'B2'], min: 0, max: 0.3, gamma: 1.2}, 'Original Image');
Map.addLayer(normalizedImage,
{bands: ['B4', 'B3', 'B2'], min: 0, max: 1, gamma: 1.2}, 'Normalized Image');
Map.addLayer(standardizedImage,
{bands: ['B4', 'B3', 'B2'], min: -1, max: 2, gamma: 1.2}, 'Standarized Image');
Map.centerObject(geometry)
// Verify Normalization
// Verify Standadization
// Verify that the means are 0 and standard deviations are 1
var beforeDict = image.reduceRegion({
reducer: ee.Reducer.mean().combine({
reducer2: ee.Reducer.stdDev(), sharedInputs: true}),
geometry: geometry,
scale: 10,
maxPixels: 1e9,
bestEffort: true,
tileScale: 16
});
var filtered = s2
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.date('2019-01-01', '2020-01-01'))
.filter(ee.Filter.bounds(bangalore))
.select('B.*')
composite = addIndices(composite)
// Train a classifier.
var classifier = ee.Classifier.smileRandomForest(50).train({
features: training,
classProperty: 'landcover',
inputProperties: composite.bandNames()
});
// // Classify the image.
var classified = composite.classify(classifier);
Map.addLayer(classified, {min: 0, max: 3, palette: ['gray', 'brown', 'blue', 'green']}, '2019');
//**************************************************************************
// Calculate Feature Importance
//**************************************************************************
var originalCollection = s2
.filter(ee.Filter.date(startDate, endDate))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.map(addNDVI);
})
print(chart);
// Moving-Window Smoothing
// Convert to milliseconds
var millis = ee.Number(days).multiply(1000*60*60*24);
})
print(chart);
Map.centerObject(geometry, 16);
var bbox = Map.getBounds({asGeoJSON: true});
Export.video.toDrive({
collection: visCollectionOriginal,
description: 'Original_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'original',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
Export.video.toDrive({
collection: visCollectionSmoothed,
description: 'Smoothed_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'smoothed',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
Interpolação Temporal
O código abaixo mostra como fazer o preenchimento de lacunas temporais de dados de séries temporais. Uma explicação detalhada do
código e outros exemplos são descritos em nosso blog Temporal Gap-Filling with Linear Interpolation in GEE
(https://spatialthoughts.com/2021/11/08/temporal-interpolation-gee/) .
var originalCollection = s2
.filter(ee.Filter.date(startDate, endDate))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.map(addNDVI);
})
print(chart);
// Gap-filling
// Define a maxDifference filter to find all images within the specified days
// The filter needs the time difference in milliseconds
// Convert days to milliseconds
// For the first join, we need to match all images that are after the given image.
// To do this we need to match 2 conditions
// 1. The resulting images must be within the specified time-window of target image
// 2. The target image's timestamp must be lesser than the timestamp of resulting images
// Combine two filters to match both these conditions
var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
// This join will find all images after, sorted in descending order
// This will gives us images so that closest is last
var join1 = ee.Join.saveAll({
matchesKey: 'after',
ordering: 'system:time_start',
ascending: false})
// Do the second join now to match all images within the time-window
// that come before each image
var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
// This join will find all images before, sorted in ascending order
// This will gives us images so that closest is last
var join2 = ee.Join.saveAll({
matchesKey: 'before',
ordering: 'system:time_start',
ascending: true})
// Interpolation formula
// y = y1 + (y2-y1)*((t – t1) / (t2 – t1))
// y = interpolated image
// y1 = before image
// y2 = after image
// t = interpolation timestamp
// t1 = before image timestamp
// t2 = after image timestamp
var t = image.metadata('system:time_start').rename('t')
Export.video.toDrive({
collection: visCollectionOriginal,
description: 'Original_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'original',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
Export.video.toDrive({
collection: visualizeIGapFilled,
description: 'Gap_Filled_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'gap_filled',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
Suavização Savitzky-Golay
O filtro Savitzky–Golay ajusta um polinômio a um conjunto de pontos de dados em uma série temporal. A Open Earth Engine Library (OEEL)
(https://www.open-geocomputing.org/OpenEarthEngineLibrary/) fornece uma implementação eficiente desse filtro que pode ser aplicado
em uma ImageCollection. No entanto, as séries temporais devem ser pré-processadas para que haja imagens em intervalos regulares.
Usamos a técnica de interpolação descrita na seção anterior e preparamos uma série temporal contínua sem nenhum pixel mascarado. O
resultado é uma nova ImageCollection contendo imagens em um intervalo regular (5 dias) e com valores de pixel suavizados usando o filtro
Savitzky–Golay.
Suavização Savitzky-Golay
var s2 = ee.ImageCollection('COPERNICUS/S2_HARMONIZED');
var geometry = ee.Geometry.Point([74.80368345518073, 30.391793042969]);
var originalCollection = s2
.filter(ee.Filter.date(startDate, endDate))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
.map(addNDVI);
})
print(chart);
// Interpolation
// Define a maxDifference filter to find all images within the specified days
// The filter needs the time difference in milliseconds
// Convert days to milliseconds
// For the first join, we need to match all images that are after the given image.
// To do this we need to match 2 conditions
// 1. The resulting images must be within the specified time-window of target image
// 2. The target image's timestamp must be lesser than the timestamp of resulting images
// Combine two filters to match both these conditions
var filter1 = ee.Filter.and(maxDiffFilter, lessEqFilter)
// This join will find all images after, sorted in descending order
// This will gives us images so that closest is last
var join1 = ee.Join.saveAll({
matchesKey: 'after',
ordering: 'system:time_start',
ascending: false})
// Do the second join now to match all images within the time-window
// that come before each image
var filter2 = ee.Filter.and(maxDiffFilter, greaterEqFilter)
// This join will find all images before, sorted in ascending order
// This will gives us images so that closest is last
var join2 = ee.Join.saveAll({
matchesKey: 'before',
ordering: 'system:time_start',
ascending: true})
// Do the interpolation
// Interpolation formula
// y = y1 + (y2-y1)*((t – t1) / (t2 – t1))
// y = interpolated image
// y1 = before image
// y2 = after image
// t = interpolation timestamp
// t1 = before image timestamp
// t2 = after image timestamp
var t = image.metadata('system:time_start').rename('t')
// SavatskyGolayFilter
// https://www.open-geocomputing.org/OpenEarthEngineLibrary/#.ImageCollection.SavatskyGolayFilter
// Apply smoothing
var oeel=require('users/OEEL/lib:loadAll');
var order = 3;
print(sgFilteredCol.first())
// Display a time-series chart
var chart = ui.Chart.image.series({
imageCollection: sgFilteredCol.select(['ndvi', 'd_0_ndvi'], ['ndvi', 'ndvi_sg']),
region: geometry,
reducer: ee.Reducer.mean(),
scale: 20
}).setOptions({
lineWidth: 1,
title: 'NDVI Time Series',
interpolateNulls: false,
vAxis: {title: 'NDVI', viewWindow: {min: 0, max: 1}},
hAxis: {title: '', format: 'YYYY-MM'},
lineWidth: 1,
pointSize: 4,
series: {
0: {color: '#66c2a4', lineDashStyle: [1, 1], pointSize: 2}, // Original NDVI
1: {color: '#238b45', lineWidth: 2 }, // Smoothed NDVI
},
})
print(chart);
Export.video.toDrive({
collection: visCollectionRegular,
description: 'Regular_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'regular',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
Export.video.toDrive({
collection: visualizeSgFiltered,
description: 'Filtered_Time_Series',
folder: 'earthengine',
fileNamePrefix: 'sg_filtered',
framesPerSecond: 2,
dimensions: 800,
region: bbox})
legend.add(title);
legend.add(makeRow('gray','Built-up'))
legend.add(makeRow('brown','Bare Earth'))
legend.add(makeRow('blue','Water'))
legend.add(makeRow('green','Vegetation'))
Map.add(legend);
// Colorbar
var legend = ui.Thumbnail({
image: ee.Image.pixelLonLat().select(0),
params: {
bbox: [0, 0, 1, 0.1],
dimensions: '200x20',
format: 'png',
min: 0, max: 1,
palette: palette},
style: {stretch: 'horizontal', margin: '8px 8px', maxHeight: '40px'},
});
// Legend Labels
var labels = ui.Panel({
widgets: [
ui.Label(min, {margin: '4px 10px',textAlign: 'left', stretch: 'horizontal'}),
ui.Label((min+max)/2, {margin: '4px 20px', textAlign: 'center', stretch: 'horizontal'}),
ui.Label(max, {margin: '4px 10px',textAlign: 'right', stretch: 'horizontal'})],
layout: ui.Panel.Layout.flow('horizontal')});
Map.add(colorBar)
// Remove UI controls from both maps, but leave zoom control on the left map.
leftMap.setControlVisibility(false);
rightMap.setControlVisibility(false);
leftMap.setControlVisibility({zoomControl: true});
var s2 = ee.ImageCollection("COPERNICUS/S2")
var rgbVis = {
min: 0.0,
max: 0.3,
bands: ['B4', 'B3', 'B2'],
};
var palette = [
'FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718',
'74A901', '66A000', '529400', '3E8601', '207401', '056201',
'004C00', '023B01', '012E01', '011D01', '011301'];
// Write a function that computes NDVI for an image and adds it as a band
function addNDVI(image) {
var ndvi = image.normalizedDifference(['B8', 'B4']).rename('ndvi');
return image.addBands(ndvi);
}
function getComposite(geometry) {
var filtered = s2
.filter(ee.Filter.date('2019-01-01', '2019-12-31'))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 30))
.filter(ee.Filter.bounds(geometry))
.map(maskS2clouds)
// Map the function over the collection
var withNdvi = filtered.map(addNDVI);
// Create UI Elements
var title = ui.Label('Global NDVI Explorer');
title.style().set({
'position': 'top-center',
'fontSize': '24px'
});
var resultsPanel = ui.Panel();
var chartPanel = ui.Panel();
var selectionPanel = ui.Panel({
layout: ui.Panel.Layout.flow('horizontal'),
});
resultsPanel.style().set({
width: '400px',
position: 'bottom-right'
});
Map.clear()
Map.add(title);
Map.add(resultsPanel)
Map.onClick(displayChart)
// Use the current viewport
var bounds = ee.Geometry.Rectangle(Map.getBounds())
var composite = getComposite(bounds)
Map.addLayer(composite, rgbVis, 'Sentinel-2 Composite')
var label = ui.Label('Click anywhere to see the chart')
resetPanel.add(label)
var filtered = s2
.filter(ee.Filter.date('2019-01-01', '2019-12-31'))
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 50))
.map(maskS2clouds)
.map(addNDVI)
.filter(ee.Filter.bounds(geometry))
chartPanel.clear()
selectionPanel.clear()
selectionPanel.add(ui.Label('Choose an image to display:'))
chartPanel.add(chart)
filtered.aggregate_array('system:time_start').evaluate(function(ids) {
var dates = ee.List(ids).distinct().map(function(timestamp) {
return ee.Date(timestamp).format('YYYY-MM-dd')
})
dates.evaluate(function(dateList){
selectionPanel.add(ui.Select({
items: dateList,
onChange: addNdviLayer,
placeholder: 'Select a date'
}))
})
});
}
// Call the function to build the initial UI state.
resetEverything();
Ao tentar enviar um link para alguém, NÃO clique no botão Copiar caminho do script . Enviar este caminho
para alguém NÃO dará acesso ao seu código. O caminho do script funciona apenas em repositórios
públicos ou compartilhados.
Ao compartilhar o script usando Get Link , você também deve compartilhar quaisquer Ativos privados que você possa ter carregado e esteja
usando no script. Você pode compartilhar o ativo com um endereço de e-mail específico ou marcar a caixa Qualquer pessoa pode ler se quiser
que qualquer pessoa com o link do script possa acessá-lo. Deixar de fazer isso impedirá que outras pessoas executem seu script.
Compartilhamento de ativos carregados
Saiba mais na seção Links de script (https://developers.google.com/earth-engine/guides/playground#get-link) do Guia do usuário do Google
Earth Engine.
Compartilhamento de vários scripts
Se você deseja compartilhar uma coleção de scripts com outros usuários ou seus colaboradores, a melhor maneira é criar um novo
Repositório .
Você pode colocar vários scripts no repositório e compartilhar o repositório com outros usuários. Você pode conceder a eles acesso de
Leitor ou Gravador para que possam visualizar/adicionar/modificar/excluir scripts nesse repositório. Se você quiser torná-lo legível pelo
Público , marque a opção Qualquer um pode ler . Você verá uma URL na forma de
https://code.earthengine.google.co.in/?accept_repo=... . Quando você compartilha esse URL com outros usuários e eles visitam
esse link, seu repositório será adicionado ao Editor de código deles na pasta Leitor ou Gravador , dependendo do acesso.
Há muitos usuários do Earth Engine que compartilharam seus repositórios publicamente e escreveram módulos de script para executar uma
variedade de tarefas. Aqui está um exemplo de uso do grid módulo do users/gena/packages repositório para criar grades regularmente
espaçadas no Earth Engine.
Usando uma função de um módulo de script
Também temos algumas recomendações de alguns pacotes selecionados, que possuem funções muito úteis para torná-lo produtivo no Earth
Engine.
LEAF-Toolbox (https://github.com/rfernand387/LEAF-Toolbox/wiki) : aplicativo do Google Earth Engine que produz vários produtos
biofísicos de vegetação, incluindo índice de área foliar (LAI).
RivWidthCloud (https://github.com/seanyx/RivWidthCloudPaper) : Pacote para automatizar a extração da linha central e da largura
do rio para API Javascript e Python.
Projetos Guiados
Veja abaixo passo a passo passo a passo baseado em vídeo para implementar projetos do mundo real usando o Earth Engine. Você pode
continuar sua jornada de aprendizado implementando esses projetos para sua região de interesse após a aula.
Obtenha o código
1. Clique neste link (https://code.earthengine.google.co.in/?accept_repo=users/ujavalgandhi/End-to-End-Projects) para abrir o editor de
código do Google Earth Engine e adicionar o repositório à sua conta.
2. Se for bem-sucedido, você terá um novo repositório nomeado users/ujavalgandhi/End-to-End-Projects na guia Scripts na seção
Leitor .
Se você não vir o repositório na seção Leitor , clique no botão Atualizar cache do repositório na guia Scripts e ele aparecerá.
v=zHUCM3XLc6k&list=PLppGmFLhQ1HJ5VhW6BZfhPX6spUcTY7SR)
(https://www.youtube.com/watch?
v=jYsK9Y4ICrY&list=PLppGmFLhQ1HJzzKVS_4v8nBiXLYxAu100)
(https://www.youtube.com/watch?
v=LqSClCXrMl4&list=PLppGmFLhQ1HJV1CctqanQvXQI1JmqGDDD)
(https://youtube.com/playlist?
list=PLppGmFLhQ1HLl0St2wiOPePr58sKu0Vh1)
Créditos de dados
Sentinel-2 Nível-1C, Nível-2A e Sentinel-1 SAR GRD : Contém dados do Copernicus Sentinel.
TerraClimate: Clima mensal e balanço hídrico climático para superfícies terrestres globais, Universidade de Idaho : Abatzoglou, JT,
SZ Dobrowski, SA Parks, KC Hegewisch, 2018, Terraclimate, um conjunto de dados global de alta resolução do clima mensal e balanço
hídrico mensal de 1958- 2015, Scientific Data 5:170191, doi:10.1038/sdata.2017.191 (doi:10.1038/sdata.2017.191)
VIIRS Stray Light Corrigido Nighttime Day/Night Band Composites Versão 1 : CD Elvidge, KE Baugh, M. Zhizhin e F.-C. Hsu, “Por que
os dados do VIIRS são superiores ao DMSP para mapear as luzes noturnas,” Asia-Pacific Advanced Network 35, vol. 35, pág. 62, 2013.
FAO GAUL 500m: Camadas da Unidade Administrativa Global 2015, Unidades Administrativas de Segundo Nível : Origem dos
limites administrativos: Conjunto de dados das Camadas da Unidade Administrativa Global (GAUL), implementado pela FAO nos
projetos CountrySTAT e Sistema de Informação do Mercado Agrícola (AMIS).
CHIRPS Pentad: Climate Hazards Group InfraRed Precipitation with Station Data (versão 2.0 final) : Funk, Chris, Pete Peterson,
Martin Landsfeld, Diego Pedreros, James Verdin, Shraddhanand Shukla, Gregory Husak, James Rowland, Laura Harrison, Andrew
Hoell e Joel Michaelsen . “O clima ameaça a precipitação infravermelha com estações – um novo recorde ambiental para
monitoramento de extremos”. Scientific Data 2, 150066. doi:10.1038/sdata.2015.66 (doi:10.1038/sdata.2015.66) 2015.
MOD13Q1.006 Índices de Vegetação de Terra 16 dias Global 250m : Didan, K. (2015). MOD13Q1 Índices de vegetação MODIS/Terra
16 dias L3 Global 250m SIN Grid V006 [Conjunto de dados]. NASA EOSDIS Land Processa DAAC. Acessado em 2021-05-06 em
https://doi.org/10.5067/MODIS/MOD13Q1.006 (https://doi.org/10.5067/MODIS/MOD13Q1.006)
Natural Earth Urban Areas : Áreas Urbanas Shapefile. Transferido de Natural Earth. Dados de mapas vetoriais e raster gratuitos @
naturalearthdata.com.
Licença
O material do curso (texto, imagens, apresentações, vídeos) está licenciado sob uma Licença Creative Commons Atribuição 4.0 Internacional
(https://creativecommons.org/licenses/by/4.0/) .
O código (scripts, notebooks Jupyter) é licenciado sob a licença MIT. Para obter uma cópia, consulte https://opensource.org/licenses/MIT
(https://opensource.org/licenses/MIT)
Citação e Referência
Você pode citar os materiais do curso da seguinte forma
Gandhi, Ujaval, 2021. Curso completo do Google Earth Engine . Pensamentos Espaciais. https://courses.spatialthoughts.com/end-to-
end-gee.html (https://courses.spatialthoughts.com/end-to-end-gee.html)
Este curso é oferecido como uma aula on-line conduzida por instrutor. Visite Spatial Thoughts (https://spatialthoughts.com/events/) para
saber detalhes das próximas sessões.