Os algoritmos criados no Earth Engine são executados na nuvem do Google, distribuídos em muitos computadores. A depuração pode ser desafiadora porque os erros podem ocorrer no código do lado do cliente ou na execução do lado do servidor das instruções codificadas e resultar de problemas de escalonamento, bem como de erros sintáticos ou lógicos. Os bits do programa que estão sendo executados em algum lugar na nuvem não estão disponíveis para inspeção, a menos que você solicite. Este documento apresenta estratégias, ferramentas e soluções de depuração para ajudar a resolver erros comuns e depurar scripts do Earth Engine.
Erros de sintaxe
Erros de sintaxe ocorrem quando o código viola as regras da linguagem de programação (JavaScript ou Python no Earth Engine). Esses erros impedem a execução do código e geralmente são detectados antes da execução. Se você encontrar um erro de sintaxe, analise cuidadosamente a linha destacada ou a mensagem de erro e consulte recursos como a Referência da linguagem Python ou o Guia de estilo do JavaScript do Google. Um lintificador de código também pode ajudar a identificar e corrigir esses problemas.
Erros do lado do cliente
Apesar do código estar sintaticamente correto, pode haver erros associados à consistência ou lógica do script. Os exemplos a seguir demonstram erros ao usar uma variável e um método que não existem.
Erro: este código não funciona.
Editor de código (JavaScript)
// Load a Sentinel-2 image. var image = ee.Image('USGS/SRTMGL1_003'); // Error: "bandNames" is not defined in this scope. var display = image.visualize({bands: bandNames, min: 0, max: 9000}); // Error: image.selfAnalyze is not a function var silly = image.selfAnalyze();
import ee import geemap.core as geemap
Colab (Python)
# Load a Sentinel-2 image. image = ee.Image('USGS/SRTMGL1_003') # NameError: name 'band_names' is not defined. display = image.visualize(bands=band_names, min=0, max=9000) # AttributeError: 'Image' object has no attribute 'selfAnalyze'. silly = image.selfAnalyze()
O primeiro erro informa que a variável bandNames
não está definida
no escopo em que é referenciada. Como solução, defina a variável ou forneça um
argumento de lista para o parâmetro bands
. O segundo erro demonstra o que
acontece quando a função selfAnalyze()
inexistente é chamada. Como
esse não é um método real em imagens, o erro informa que não é uma função. Em ambos
os casos, o erro é descritivo do problema.
Atribuição de tipo de objeto desconhecida
O erro "...is not a function
" pode ocorrer porque o Earth Engine não
conhece o tipo de uma variável. As manifestações comuns desse problema são resultado de:
- Fazer algo com um objeto retornado por
first()
(o tipo dos elementos em uma coleção é desconhecido). - Fazer algo com um objeto retornado por
get()
(o tipo de elemento armazenado em uma propriedade é desconhecido). - Fazer algo com um argumento de função (na função) quando o tipo do argumento é desconhecido.
Como exemplo do primeiro:
Erro: este código não funciona.
Editor de código (JavaScript)
var collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017'); // Error: collection.first(...).area is not a function var area = collection.first().area();
import ee import geemap.core as geemap
Colab (Python)
collection = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017') # AttributeError: 'Element' object has no attribute 'area'. area = collection.first().area()
A solução em todos os casos é converter o objeto de tipo desconhecido com o construtor
do tipo conhecido. Continuando o exemplo anterior, a solução é converter para
ee.Feature
:
Solução: use uma transmissão.
Editor de código (JavaScript)
var area = ee.Feature(collection.first()).area();
import ee import geemap.core as geemap
Colab (Python)
area = ee.Feature(collection.first()).area()
É possível chamar qualquer método em Element
aqui com segurança, porque é isso que o Earth Engine considera.
Evitar misturar funções de cliente e servidor
O exemplo a seguir é menos óbvio:
Erro: o código não faz o que você quer.
Editor de código (JavaScript)
// Don't mix EE objects and JavaScript objects. var image = ee.Image('USGS/SRTMGL1_003'); var nonsense = image + 2; // You can print this, but it's not what you were hoping for. print(nonsense); // Error: g.eeObject.name is not a function Map.addLayer(nonsense);
import ee import geemap.core as geemap
Colab (Python)
# Don't mix EE objects and Python objects. image = ee.Image('USGS/SRTMGL1_003') nonsense = image + 2 # TypeError: unsupported operand type(s) for +: 'Image' and 'int'. display(nonsense) # TypeError: unsupported operand type(s) for +: 'Image' and 'int'. m = geemap.Map() m.add_layer(nonsense) m
Supondo que o autor desse código pretendia adicionar 2
a cada pixel da
imagem, essa não é a maneira certa de fazer isso. Especificamente, esse código mistura incorretamente um objeto do lado do servidor (image
) com um operador do lado do cliente (+
). Os resultados podem ser surpreendentes. No primeiro caso, a impressão de
nonsense
no editor de código JavaScript vai realizar a operação solicitada
(+
) convertendo image
e 2
em strings e
concatenando-as. A string resultante é involuntária (no Python, uma TypeError é gerada).
No segundo caso, ao adicionar nonsense
ao mapa, o erro g.eeObject.name is not a function
criptográfico é exibido no editor de código JavaScript
porque o objeto adicionado ao mapa, nonsense
, é uma string, não um
objeto EE (no Python, um TypeError é gerado). Para evitar resultados não intencionais e
erros não informativos, não misture objetos e funções do servidor com objetos, primitivos
ou funções do cliente. A solução neste exemplo é usar uma função do servidor.
Solução: use uma função do servidor.
Editor de código (JavaScript)
Map.addLayer(image.add(2));
import ee import geemap.core as geemap
Colab (Python)
m = geemap.Map() m.add_layer(image.add(2)) m
Consulte a página Cliente x servidor para mais detalhes.
Bloqueio do navegador do editor de código JavaScript
O congelamento ou travamento do navegador pode ocorrer quando a execução do JavaScript no cliente leva muito tempo ou
quando há uma espera por algo do Earth Engine. Duas fontes comuns desse erro são
loops for e/ou getInfo()
no código do editor de código JavaScript, com o
pior cenário de getInfo()
dentro de um loop for. Os loops For podem fazer com que o
navegador trave porque o código é executado na sua máquina. Por outro lado,
getInfo()
solicita de forma síncrona o resultado de uma computação do Earth Engine,
bloqueando até que o resultado seja recebido. Se a computação demorar muito, o bloqueio
poderá fazer com que o navegador trave. Evite loops for e getInfo()
ao
trabalhar no editor de código. Consulte a página
Cliente x servidor para mais
detalhes.
Erros do lado do servidor
Apesar da consistência lógica no código do cliente, podem haver bugs que só se tornam aparentes no tempo de execução no servidor. O exemplo a seguir demonstra o que acontece ao tentar acessar uma banda que não existe.
Erro: este código não funciona.
Editor de código (JavaScript)
// Load a Sentinel-2 image. var s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'); // Error: Image.select: Pattern 'nonBand' did not match any bands. print(s2image.select(['nonBand']));
import ee import geemap.core as geemap
Colab (Python)
# Load a Sentinel-2 image. s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR' ) # EEException: Image.select: Band pattern 'non_band' did not match any bands. print(s2image.select(['non_band']).getInfo())
Neste exemplo, o erro informa que não há uma banda chamada
nonBand
. A solução mais óbvia é especificar um nome de banda que
exista. É possível descobrir os nomes das bandas imprimindo a imagem e
inspecionando-a no console ou imprimindo a lista de nomes de bandas retornada por
image.bandNames()
.
Imutabilidade
Os objetos criados no lado do servidor no Earth Engine são immutable. (Qualquer
ee.Object
é um Object
do lado do servidor). Isso significa que, se você
quiser fazer uma alteração no objeto, salve o estado alterado em uma nova
variável. Por exemplo, esta abordagem não vai funcionar para definir uma propriedade na imagem Sentinel-2:
Erro: esse código não faz o que você quer.
Editor de código (JavaScript)
var s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR'); s2image.set('myProperty', 'This image is not assigned to a variable'); // This will not result in an error, but will not find 'myProperty'. print(s2image.get('myProperty')); // null
import ee import geemap.core as geemap
Colab (Python)
s2image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20160625T100617_20160625T170310_T33UVR' ) s2image.set('my_property', 'This image is not assigned to a variable') # This will not result in an error, but will not find 'my_property'. display(s2image.get('my_property')) # None
Neste exemplo, s2image.set()
retorna uma cópia da imagem com a nova
propriedade, mas a imagem armazenada na variável s2image
não é alterada. É necessário
salvar a imagem retornada por s2image.set()
em uma nova variável. Exemplo:
Solução: capture o resultado em uma variável.
Editor de código (JavaScript)
s2image = s2image.set('myProperty', 'OK'); print(s2image.get('myProperty')); // OK
import ee import geemap.core as geemap
Colab (Python)
s2image = s2image.set('my_property', 'OK') display(s2image.get('my_property')) # OK
Funções mapeadas
Outro contexto em que as funções do cliente e do servidor não se misturam é nas funções
mapeadas. Especificamente, as operações especificadas pela função mapeada são executadas na nuvem,
portanto, funções do cliente, como getInfo
e Export
,
além de print
e método em Map
e Chart
no
editor de código JavaScript, não funcionam em funções mapeadas. Exemplo:
Erro: este código não funciona.
Editor de código (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); // Error: A mapped function's arguments cannot be used in client-side operations var badMap3 = collection.map(function(image) { print(image); return image; });
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') # Error: A mapped function's arguments cannot be used in client-side operations. bad_map_3 = collection.map(lambda image: print(image.getInfo()))
Esse erro um tanto misterioso é resultado do processo que o Earth Engine usa para transformar esse código em um conjunto de instruções que podem ser executadas nos servidores do Google. As funções do lado do cliente e as estruturas de controle não podem ser usadas para operar na imagem do argumento transmitida para a função mapeada. Para evitar esse erro, não use funções do lado do cliente em funções mapeadas. Consulte a página Cliente x servidor para saber mais sobre a distinção entre as funções do cliente e do servidor.
As funções mapeadas têm requisitos adicionais. Por exemplo, as funções mapeadas precisam retornar algo:
Erro: este código não funciona.
Editor de código (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); // Error: User-defined methods must return a value. var badMap1 = collection.map(function(image) { // Do nothing. });
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') # Error: User-defined methods must return a value. bad_map_1 = collection.map(lambda image: None)
A solução mais óbvia é devolver o produto. Mas ele não pode retornar qualquer tipo
de coisa. Especificamente, as funções mapeadas em um ImageCollection
ou
FeatureCollection
precisam retornar um Image
ou
Feature
. Por exemplo, não é possível retornar uma data de uma função mapeada em
um ImageCollection
:
Erro: este código não funciona.
Editor de código (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); var badMap2 = collection.map(function(image) { return image.date(); }); // Error: Collection.map: A mapped algorithm must return a Feature or Image. print(badMap2);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') bad_map_2 = collection.map(lambda image: image.date()) # EEException: Collection.map: # A mapped algorithm must return a Feature or Image. print(bad_map_2.getInfo())
Para evitar isso, retorne a imagem de entrada com um novo conjunto de propriedades. Em seguida, se você precisar de uma lista
das datas das imagens na coleção, use aggregate_array()
:
Solução: defina uma propriedade.
Editor de código (JavaScript)
var collection = ee.ImageCollection('MODIS/006/MOD44B'); var okMap2 = collection.map(function(image) { return image.set('date', image.date()); }); print(okMap2); // Get a list of the dates. var datesList = okMap2.aggregate_array('date'); print(datesList);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('MODIS/006/MOD44B') ok_map_2 = collection.map(lambda image: image.set('date', image.date())) print(ok_map_2.getInfo()) # Get a list of the dates. dates_list = ok_map_2.aggregate_array('date') print(dates_list.getInfo())
Erros procedimentais
O padrão foi aplicado a uma imagem sem faixas
O erro "Pattern 'my_band' was applied to an Image with no bands"
significa que há uma chamada ee.Image.select()
para uma imagem
com uma lista de faixas vazia. Confira o que você pode fazer para resolver esse problema:
- Se a imagem for produzida a partir de uma ImageCollection com um redutor ou
usando as chamadas
first()
outoBands()
, verifique se a coleção de origem não está vazia. - Se a imagem for produzida a partir de um dicionário usando
ee.Dictionary().toImage()
, verifique se o dicionário não está vazio. - Se a imagem for independente, verifique se ela tem dados (e não apenas
ee.Image(0)
).
Erros de escalonamento
Embora um script possa estar sintaticamente correto, sem erros lógicos, e representar um conjunto válido de instruções para o servidor, ao paralelizar e executar a computação, os objetos resultantes podem ser muito grandes, muito numerosos ou levar muito tempo para serem computados. Nesse caso, você vai receber um erro indicando que o algoritmo não pode ser escalonado. Esses erros geralmente são os mais difíceis de diagnosticar e resolver. Exemplos desse tipo de erro incluem:
- O tempo de computação expirou
- Muitas agregações simultâneas
- Limite de memória do usuário excedido
- Ocorreu um erro interno
Melhorar a escalabilidade do código permite que você obtenha resultados mais rapidamente e também melhora a
disponibilidade de recursos de computação para todos os usuários. Cada tipo de erro é discutido nas
seções a seguir, após uma breve observação sobre reduceRegion()
, uma função usada com frequência
que é conhecida por causar todos os tipos de erro de dimensionamento.
reduceRegion()
Embora reduceRegion()
consuma muitos pixels para acionar uma
variedade de erros, também há parâmetros destinados a controlar a
computação, para que você possa superar os erros. Por exemplo, considere a redução
inadequada a seguir:
Erro: este código não funciona.
Editor de código (JavaScript)
var absurdComputation = ee.Image(1).reduceRegion({ reducer: 'count', geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false), scale: 100, }); // Error: Image.reduceRegion: Too many pixels in the region. // Found 80300348117, but only 10000000 allowed. print(absurdComputation);
import ee import geemap.core as geemap
Colab (Python)
absurd_computation = ee.Image(1).reduceRegion( reducer='count', geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False), scale=100, ) # EEException: Image.reduceRegion: Too many pixels in the region. # Found 80300348117, but only 10000000 allowed. print(absurd_computation.getInfo())
Este exemplo é apenas para demonstração. O objetivo desse erro é perguntar
se você realmente quer reduzir 80300348117 (80 bilhões)
de pixels. Caso contrário, aumente o scale
(tamanho do pixel em metros) de acordo ou
defina bestEffort
como verdadeiro para recalcular automaticamente uma escala maior. Consulte a página
reduceRegion()
para
mais detalhes sobre esses parâmetros.
O tempo de computação expirou
Suponha que você precise de todos esses pixels na computação. Nesse caso, aumente o
parâmetro maxPixels
para permitir que a computação seja concluída. No entanto, o Earth Engine vai levar algum tempo para concluir a computação. Como resultado, um
erro de "tempo limite de computação" pode ser gerado:
Ruim: não faça isso.
Editor de código (JavaScript)
var ridiculousComputation = ee.Image(1).reduceRegion({ reducer: 'count', geometry: ee.Geometry.Rectangle([-180, -90, 180, 90], null, false), scale: 100, maxPixels: 1e11 }); // Error: Computation timed out. print(ridiculousComputation);
import ee import geemap.core as geemap
Colab (Python)
ridiculous_computation = ee.Image(1).reduceRegion( reducer='count', geometry=ee.Geometry.Rectangle([-180, -90, 180, 90], None, False), scale=100, maxPixels=int(1e11), ) # Error: Computation timed out. print(ridiculous_computation.getInfo())
Esse erro significa que o Earth Engine esperou cerca de cinco minutos antes de interromper a
computação. A exportação permite que o Earth Engine realize a computação em um ambiente
com tempos de execução mais longos (mas não mais memória). Como o valor de retorno de reduceRegion()
é um dicionário, é possível usar o dicionário para definir as propriedades de um elemento com geometria nula:
Bom: use Export
.
Editor de código (JavaScript)
Export.table.toDrive({ collection: ee.FeatureCollection([ ee.Feature(null, ridiculousComputation) ]), description: 'ridiculousComputation', fileFormat: 'CSV' });
import ee import geemap.core as geemap
Colab (Python)
task = ee.batch.Export.table.toDrive( collection=ee.FeatureCollection([ee.Feature(None, ridiculous_computation)]), description='ridiculous_computation', fileFormat='CSV', ) # task.start()
Muitas agregações simultâneas
A parte "agregações" desse erro se refere a operações distribuídas em várias máquinas (como reduções que abrangem mais de um bloco). O Earth Engine tem limites para evitar que muitas agregações sejam executadas simultaneamente. Neste exemplo, o erro "Muitas agregações simultâneas" é acionado por uma redução em um mapa:
Ruim: não faça isso.
Editor de código (JavaScript)
var collection = ee.ImageCollection('LANDSAT/LT05/C02/T1') .filterBounds(ee.Geometry.Point([-123, 43])); var terribleAggregations = collection.map(function(image) { return image.set(image.reduceRegion({ reducer: 'mean', geometry: image.geometry(), scale: 30, maxPixels: 1e9 })); }); // Error: Quota exceeded: Too many concurrent aggregations. print(terribleAggregations);
import ee import geemap.core as geemap
Colab (Python)
collection = ee.ImageCollection('LANDSAT/LT05/C02/T1').filterBounds( ee.Geometry.Point([-123, 43]) ) def apply_mean_aggregation(image): return image.set( image.reduceRegion( reducer='mean', geometry=image.geometry(), scale=30, maxPixels=int(1e9), ) ) terrible_aggregations = collection.map(apply_mean_aggregation) # EEException: Computation timed out. print(terrible_aggregations.getInfo())
Supondo que o objetivo desse código seja receber estatísticas de imagem para cada imagem, uma
solução possível é Export
o resultado. Por exemplo, usando o fato
de que um ImageCollection
também é um FeatureCollection
, os
metadados associados às imagens podem ser exportados como uma tabela:
Bom: use Export
.
Editor de código (JavaScript)
Export.table.toDrive({ collection: terribleAggregations, description: 'terribleAggregations', fileFormat: 'CSV' });
import ee import geemap.core as geemap
Colab (Python)
task = ee.batch.Export.table.toDrive( collection=terrible_aggregations, description='terrible_aggregations', fileFormat='CSV', ) # task.start()
Limite de memória do usuário excedido
Uma maneira de paralelizar seus algoritmos no Earth Engine é dividindo as entradas em blocos, executando a mesma computação separadamente em cada bloco e combinando os resultados. Como consequência, todas as entradas necessárias para calcular um Bloco de saída precisam caber na memória. Por exemplo, quando a entrada é uma imagem com muitas bandas, ela pode acabar consumindo muita memória se todas as bandas forem usadas na computação. Para demonstrar, este exemplo usa muita memória forçando (desnecessariamente) uma coleção de imagens inteira em um bloco:
Ruim: não faça isso.
Editor de código (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var memoryHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, }); // Error: User memory limit exceeded. print(memoryHog);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] memory_hog = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, ) ) # EEException: User memory limit exceeded. print(memory_hog.getInfo())
Esse código muito ruim demonstra um motivo para não usar matrizes, a menos que seja realmente necessário. Consulte também a seção "Evitar a conversão desnecessária de tipos". Quando essa coleção é convertida em uma matriz gigante, ela precisa ser carregada na memória de uma só vez. Como é uma série temporal longa de imagens, a matriz é grande e não cabe na memória.
Uma solução possível é definir o parâmetro tileScale
como um valor
maior. Valores mais altos de tileScale resultam em blocos menores por um fator de
tileScale^2
. Por exemplo, o exemplo a seguir permite que o cálculo
seja concluído:
Editor de código (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var smallerHog = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, tileScale: 16 }); print(smallerHog);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] smaller_hog = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .toArray() .arrayReduce(ee.Reducer.mean(), [0]) .arrayProject([1]) .arrayFlatten([bands]) .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, tileScale=16, ) ) print(smaller_hog.getInfo())
No entanto, a solução mais recomendada é não usar matrizes desnecessariamente, para que você não
precise mexer com tileScale
:
Bom: evite matrizes.
Editor de código (JavaScript)
var bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7']; var okMemory = ee.ImageCollection('LANDSAT/LT05/C02/T1').select(bands) .mean() .reduceRegion({ reducer: 'mean', geometry: ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale: 1, bestEffort: true, }); print(okMemory);
import ee import geemap.core as geemap
Colab (Python)
bands = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7'] ok_memory = ( ee.ImageCollection('LANDSAT/LT05/C02/T1') .select(bands) .mean() .reduceRegion( reducer=ee.Reducer.mean(), geometry=ee.Geometry.Point([-122.27, 37.87]).buffer(1000), scale=1, bestEffort=True, ) ) print(ok_memory.getInfo())
A menos que seja necessário resolver um erro de memória, não defina tileScale
,
porque blocos menores também resultam em uma sobrecarga maior de paralelização.
Erros internos
Talvez você encontre um erro parecido com este:
Se esse erro aparecer, clique no link "Report error" (Informar erro) que aparece no console do editor de código JavaScript. Você também pode enviar feedback pelo botão Help. Esse erro pode ser resultado de erros lógicos no script que só se tornam óbvios no momento da execução ou um problema com o funcionamento interno do Earth Engine. Em qualquer caso, o erro não é informativo e precisa ser informado para que possa ser corrigido.
Erros internos incluem um ID request
, como este:
Essas strings funcionam como identificadores exclusivos para ajudar a equipe do Earth Engine a identificar problemas específicos. Inclua essa string nos relatórios de bugs.
Métodos de depuração
Você programou sua análise, a executou e recebeu um erro. E agora? Esta seção descreve técnicas gerais de depuração para isolar e corrigir o problema.
Inspecionar variáveis e camadas do mapa
Digamos que você tenha uma análise muito complexa que produz um erro. Se não for óbvio onde o erro se origina, uma boa estratégia inicial é imprimir ou visualizar objetos intermediários e inspecioná-los para garantir que a estrutura do objeto seja consistente com a lógica do script. Especificamente, é possível inspecionar os valores de pixel das camadas adicionadas ao mapa com as ferramentas do editor de código ou do inspetor do geemap. Se você imprimir algo, abra as propriedades com os zippies (▶). Confira alguns itens:
- Nomes de bandas. Os nomes das faixas de imagem correspondem ao seu código?
- Valores de pixel. Seus dados têm o intervalo certo? Ele está mascarado adequadamente?
- Nulo. Há algum valor nulo que não deveria existir?
- Tamanhos. O tamanho é zero quando não deveria ser?
aside()
Pode ser trabalhoso colocar cada etapa intermediária de uma análise em uma variável para que
ela possa ser impressa e inspecionada. Para imprimir valores intermediários de uma longa cadeia de
chamadas de função, use o método aside()
. Exemplo:
Editor de código (JavaScript)
var image = ee.Image(ee.ImageCollection('COPERNICUS/S2') .filterBounds(ee.Geometry.Point([-12.29, 168.83])) .aside(print) .filterDate('2011-01-01', '2016-12-31') .first());
import ee import geemap.core as geemap
Colab (Python)
image = ee.Image( ee.ImageCollection('COPERNICUS/S2_HARMONIZED') .filterBounds(ee.Geometry.Point([-12.29, 168.83])) .aside(display) .filterDate('2011-01-01', '2016-12-31') .first() )
Lembre-se de que aside(print)
(editor de código JavaScript) e aside(display)
(geemap do Python) estão chamando funções do lado do cliente e ainda falham em funções mapeadas. Também é possível usar o aside
com funções definidas pelo usuário. Exemplo:
Editor de código (JavaScript)
var composite = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterBounds(ee.Geometry.Point([106.91, 47.91])) .map(function(image) { return image.addBands(image.normalizedDifference(['B5', 'B4'])); }) .aside(Map.addLayer, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'collection') .qualityMosaic('nd'); Map.setCenter(106.91, 47.91, 11); Map.addLayer(composite, {bands: ['B4', 'B3', 'B2'], max: 0.3}, 'composite');
import ee import geemap.core as geemap
Colab (Python)
m = geemap.Map() m.set_center(106.91, 47.91, 11) composite = ( ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA') .filterBounds(ee.Geometry.Point([106.91, 47.91])) .map(lambda image: image.addBands(image.normalizedDifference(['B5', 'B4']))) .aside(m.add_layer, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'collection') .qualityMosaic('nd') ) m.add_layer(composite, {'bands': ['B4', 'B3', 'B2'], 'max': 0.3}, 'composite') m
Como executar uma função em first()
A impressão e a visualização são úteis para depuração quando disponíveis, mas, ao depurar uma função mapeada em uma coleção, não é possível imprimir na função, conforme descrito na seção de funções mapeadas. Nesse caso, é útil isolar elementos problemáticos na coleção e testar a função mapeada em um elemento individual. Ao testar a função sem mapeá-la, use instruções de impressão para entender o problema. Veja o exemplo a seguir.
Erro: este código não funciona.
Editor de código (JavaScript)
var image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU'); var someFeatures = ee.FeatureCollection([ ee.Feature(ee.Geometry.Point([-2.02, 48.43])), ee.Feature(ee.Geometry.Point([-2.80, 48.37])), ee.Feature(ee.Geometry.Point([-1.22, 48.29])), ee.Feature(ee.Geometry.Point([-1.73, 48.65])), ]); var problem = someFeatures.map(function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }); // Error in map(ID=2): // Number.divide: Parameter 'left' is required. print(problem);
import ee import geemap.core as geemap
Colab (Python)
image = ee.Image( 'COPERNICUS/S2_HARMONIZED/20150821T111616_20160314T094808_T30UWU' ) some_features = ee.FeatureCollection([ ee.Feature(ee.Geometry.Point([-2.02, 48.43])), ee.Feature(ee.Geometry.Point([-2.80, 48.37])), ee.Feature(ee.Geometry.Point([-1.22, 48.29])), ee.Feature(ee.Geometry.Point([-1.73, 48.65])), ]) # Define a function to be mapped over the collection. def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) problem = some_features.map(function_to_map) # EEException: Error in map(ID=2): # Number.divide: Parameter 'left' is required. print(problem.getInfo())
Para depurar isso, é instrutivo examinar o erro. Felizmente, esse erro útil
informa que há um problema com o recurso ID=2
. Para
investigar mais, é útil refatorar um pouco o código. Especificamente, não é possível
ter instruções de impressão na função quando ela é mapeada em uma coleção, conforme descrito
nesta seção. O objetivo da depuração é isolar o recurso problemático e executar a
função com algumas instruções de impressão. Com a mesma imagem e recursos usados
anteriormente:
Editor de código (JavaScript)
// Define a function to be mapped over the collection. var functionToMap = function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); // Debug: print(dictionary); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }; // Isolate the feature that's creating problems. var badFeature = ee.Feature(someFeatures .filter(ee.Filter.eq('system:index', '2')) .first()); // Test the function with print statements added. functionToMap(badFeature); // Inspect the bad feature in relation to the image. Map.centerObject(badFeature, 11); Map.addLayer(badFeature, {}, 'bad feature'); Map.addLayer(image, {bands: ['B4', 'B3', 'B2'], max: 3000}, 'image');
import ee import geemap.core as geemap
Colab (Python)
# Define a function to be mapped over the collection. def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) # Debug: display(dictionary) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) # Isolate the feature that's creating problems. bad_feature = ee.Feature( some_features.filter(ee.Filter.eq('system:index', '2')).first() ) # Test the function with print statements added. function_to_map(bad_feature) # Inspect the bad feature in relation to the image. m = geemap.Map() m.center_object(bad_feature, 11) m.add_layer(bad_feature, {}, 'bad feature') m.add_layer(image, {'bands': ['B4', 'B3', 'B2'], 'max': 3000}, 'image') m
Agora, como a função é executada apenas em um recurso, você pode colocar uma chamada de impressão ("display" para o geemap do Python) dentro dela. Inspecione o objeto impresso para descobrir (ah ha!) que o objeto
retornado por reduceRegion()
tem valores nulos para cada faixa. Isso explica por que a
divisão está falhando: porque não é possível dividir nulos por nulos. Por que ele é nulo?
Para investigar, adicione a imagem de entrada e o elemento incorreto ao mapa e centralize o elemento incorreto. Ao fazer isso, você descobre que o problema é devido ao ponto estar fora
dos limites da imagem. Com base nessa descoberta, o código de depuração é:
Editor de código (JavaScript)
var functionToMap = function(feature) { var dictionary = image.reduceRegion({ reducer: 'first', geometry: feature.geometry(), scale: 10, }); return feature.set({ result: ee.Number(dictionary.get('B5')) .divide(dictionary.get('B4')) }); }; var noProblem = someFeatures .filterBounds(image.geometry()) .map(functionToMap); print(noProblem);
import ee import geemap.core as geemap
Colab (Python)
def function_to_map(feature): dictionary = image.reduceRegion( reducer=ee.Reducer.first(), geometry=feature.geometry(), scale=10 ) return feature.set( {'result': ee.Number(dictionary.get('B5')).divide(dictionary.get('B4'))} ) no_problem = some_features.filterBounds(image.geometry()).map(function_to_map) display(no_problem)
Profiler
O Profiler fornece informações sobre o tempo de EECU e o uso de memória (por algoritmo e recurso) resultante da computação realizada enquanto ele está ativado. Procure as entradas na parte de cima do perfil para informações sobre as operações que consomem mais recursos. Para scripts de execução longa ou ineficientes, as entradas na parte de cima do Profiler fornecem pistas sobre onde concentrar os esforços para otimizar o script. Observação importante: o próprio perfilador influencia a performance do script. Portanto, execute-o apenas quando necessário.