Quickstart

Neste guia, explicamos como começar a emitir consultas para a API REST do Earth Engine em Python usando o Google Colab. Os mesmos conceitos se aplicam ao acesso à API em outras linguagens e ambientes.

Observação: a API REST contém recursos avançados novos que podem não ser adequados para todos os usuários. Se você não conhece o Earth Engine, comece com o guia de JavaScript.

Antes de começar

Siga estas instruções para:

Configurar o notebook do Colab

Se você estiver começando este guia de início rápido do zero, crie um notebook do Colab clicando em NOVO NOTEBOOK na página inicial do Colab e insira os exemplos de código abaixo em uma nova célula de código. O Colab já tem o SDK Cloud instalado. Isso inclui a ferramenta de linha de comando gcloud, que pode ser usada para gerenciar os serviços do Cloud. Como alternativa, execute o notebook de demonstração usando o botão no início desta página.

Autenticar no Google Cloud

A primeira coisa a fazer é o login para que você possa fazer solicitações autenticadas ao Google Cloud.

No Colab, você pode executar:

PROJECT = 'my-project'
!gcloud auth login --project {PROJECT}

(Ou, se você estiver executando localmente, em uma linha de comando, supondo que o SDK Cloud esteja instalado:)

PROJECT='my-project'
gcloud auth login --project $PROJECT

Aceite a opção de fazer login usando sua conta de usuário do Google e conclua o processo de login.

Gerar um arquivo de chave privada para sua conta de serviço

Antes de usar a conta de serviço para autenticação, faça o download de um arquivo de chave privada. Para fazer isso no Colab, faça o download para a VM do notebook:

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY = 'my-secret-key.json'
!gcloud iam service-accounts keys create {KEY} --iam-account {SERVICE_ACCOUNT}

Ou, como alternativa, na linha de comando:

SERVICE_ACCOUNT='foo-name@project-name.iam.gserviceaccount.com'
KEY='my-secret-key.json'
gcloud iam service-accounts keys create $KEY --iam-account $SERVICE_ACCOUNT

Como acessar e testar suas credenciais

Agora você pode enviar sua primeira consulta para a API Earth Engine. Use a chave privada para receber credenciais. Use as credenciais para criar uma sessão autorizada para fazer solicitações HTTP. Insira isso em uma nova célula de código do notebook do Colab. Se você estiver usando a linha de comando, verifique se as bibliotecas necessárias estão instaladas.

from google.auth.transport.requests import AuthorizedSession
from google.oauth2 import service_account

credentials = service_account.Credentials.from_service_account_file(KEY)
scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/cloud-platform'])

session = AuthorizedSession(scoped_credentials)

url = 'https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/LANDSAT'

response = session.get(url)

from pprint import pprint
import json
pprint(json.loads(response.content))

Se tudo estiver configurado corretamente, a execução vai produzir uma saída como esta:

{'id': 'LANDSAT',
 'name': 'projects/earthengine-public/assets/LANDSAT',
 'type': 'FOLDER'}

Escolher um conjunto de dados

É possível pesquisar e explorar os conjuntos de dados disponíveis usando o Editor de código do Earth Engine em code.earthengine.google.com. Vamos procurar alguns dados do Sentinel 2. Se esta for sua primeira vez usando o Editor de Código, você precisará autorizar o acesso ao Earth Engine em seu nome ao fazer login. No Editor de Código, pesquise "sentinel" na caixa de pesquisa na parte de cima. Vários conjuntos de dados rasterizados aparecem:

Clique em "Sentinel-2: MultiSpectral Instrument (MSI), Level-1C":

As páginas de descrição de conjuntos de dados como esta incluem as informações essenciais para usar qualquer conjunto de dados no Data Catalog Público do Earth Engine, incluindo uma breve descrição do conjunto de dados, links para o provedor de dados para mais detalhes, informações sobre as restrições de uso que podem ser aplicadas ao conjunto de dados e o código do recurso do Earth Engine do conjunto de dados.

Nesse caso, vemos no lado direito da janela que esse é um recurso de coleção de imagens com o caminho COPERNICUS/S2_HARMONIZED.

Consultar imagens específicas

Esse conjunto de dados do Sentinel-2 inclui mais de dois milhões de imagens que cobrem o mundo de 2015 até o presente. Vamos emitir uma consulta projects.assets.listImages na coleção de imagens para encontrar alguns dados de abril de 2017 com baixa cobertura de nuvens que inclua um ponto específico em Mountain View, Califórnia.

import urllib

coords = [-122.085, 37.422]

project = 'projects/earthengine-public'
asset_id = 'COPERNICUS/S2_HARMONIZED'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:listImages?{}'.format(
  name, urllib.parse.urlencode({
    'startTime': '2017-04-01T00:00:00.000Z',
    'endTime': '2017-05-01T00:00:00.000Z',
    'region': '{"type":"Point", "coordinates":' + str(coords) + '}',
    'filter': 'CLOUDY_PIXEL_PERCENTAGE < 10',
}))

response = session.get(url)
content = response.content

for asset in json.loads(content)['images']:
    id = asset['id']
    cloud_cover = asset['properties']['CLOUDY_PIXEL_PERCENTAGE']
    print('%s : %s' % (id, cloud_cover))

Esse script consulta a coleção em busca de imagens correspondentes, decodifica a resposta JSON resultante e imprime o código do recurso e a cobertura de nuvens de cada recurso de imagem correspondente. O resultado será semelhante a isso:

COPERNICUS/S2_HARMONIZED/20170420T184921_20170420T190203_T10SEG : 4.3166
COPERNICUS/S2_HARMONIZED/20170430T190351_20170430T190351_T10SEG : 0

Evidentemente, há duas imagens sobre esse ponto que foram tiradas neste mês e têm baixa cobertura de nuvens.

Inspecionar uma imagem específica

Parece que uma das correspondências tem essencialmente zero cobertura de nuvens. Vamos analisar esse recurso, que tem o ID COPERNICUS/S2_HARMONIZED/20170430T190351_20170430T190351_T10SEG. Todos os recursos do catálogo público pertencem ao projeto earthengine-public. Confira um snippet em Python que emite uma consulta projects.assets.get para buscar os detalhes desse recurso específico por ID, exibir as bandas de dados disponíveis e mostrar informações mais detalhadas sobre a primeira banda:

asset_id = 'COPERNICUS/S2_HARMONIZED/20170430T190351_20170430T190351_T10SEG'
name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}'.format(name)

response = session.get(url)
content = response.content

asset = json.loads(content)
print('Band Names: %s' % ','.join(band['id'] for band in asset['bands']))
print('First Band: %s' % json.dumps(asset['bands'][0], indent=2, sort_keys=True))

A saída será semelhante a esta:

Band Names: B1,B2,B3,B4,B5,B6,B7,B8,B8A,B9,B10,B11,B12,QA10,QA20,QA60
First Band: {
  "dataType": {
    "precision": "INTEGER",
    "range": {
      "max": 65535
    }
  },
  "grid": {
    "affineTransform": {
      "scaleX": 60,
      "scaleY": -60,
      "translateX": 499980,
      "translateY": 4200000
    },
    "crsCode": "EPSG:32610",
    "dimensions": {
      "height": 1830,
      "width": 1830
    }
  },
  "id": "B1",
  "pyramidingPolicy": "MEAN"
}

A lista de bandas de dados corresponde ao que vimos anteriormente na descrição do conjunto de dados. Podemos ver que esse conjunto de dados tem dados inteiros de 16 bits no sistema de coordenadas EPSG:32610, ou UTM Zone 10N. Essa primeira banda tem o ID B1 e uma resolução de 60 metros por pixel. A origem da imagem está na posição (499980,4200000) nesse sistema de coordenadas.

O valor negativo de affineTransform.scaleY indica que a origem está no canto noroeste da imagem, como geralmente acontece: o aumento dos índices de pixel y corresponde à diminuição das coordenadas espaciais y (em direção ao sul).

Buscando valores de pixel

Vamos emitir uma consulta projects.assets.getPixels para buscar alguns dados das bandas de alta resolução dessa imagem. A página de descrição do conjunto de dados informa que as bandas B2, B3, B4 e B8 têm uma resolução de 10 metros por pixel. Esse script busca o bloco de dados de 256 x 256 pixels no canto superior esquerdo dessas quatro bandas. Carregar os dados no formato NPY numpy facilita a decodificação da resposta em uma matriz de dados do Python.

import numpy
import io

name = '{}/assets/{}'.format(project, asset_id)
url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'NPY',
    'bandIds': ['B2', 'B3', 'B4', 'B8'],
    'grid': {
        'affineTransform': {
            'scaleX': 10,
            'scaleY': -10,
            'translateX': 499980,
            'translateY': 4200000,
        },
        'dimensions': {'width': 256, 'height': 256},
    },
})

pixels_response = session.post(url, body)
pixels_content = pixels_response.content

array = numpy.load(io.BytesIO(pixels_content))
print('Shape: %s' % (array.shape,))
print('Data:')
print(array)

O resultado será semelhante a este:

Shape: (256, 256)
Data:
[[( 899, 586, 351, 189) ( 918, 630, 501, 248) (1013, 773, 654, 378) ...,
  (1014, 690, 419, 323) ( 942, 657, 424, 260) ( 987, 691, 431, 315)]
 [( 902, 630, 541, 227) (1059, 866, 719, 429) (1195, 922, 626, 539) ...,
  ( 978, 659, 404, 287) ( 954, 672, 426, 279) ( 990, 678, 397, 304)]
 [(1050, 855, 721, 419) (1257, 977, 635, 569) (1137, 770, 400, 435) ...,
  ( 972, 674, 421, 312) (1001, 688, 431, 311) (1004, 659, 378, 284)]
 ...,
 [( 969, 672, 375, 275) ( 927, 680, 478, 294) (1018, 724, 455, 353) ...,
  ( 924, 659, 375, 232) ( 921, 664, 438, 273) ( 966, 737, 521, 306)]
 [( 920, 645, 391, 248) ( 979, 728, 481, 327) ( 997, 708, 425, 324) ...,
  ( 927, 673, 387, 243) ( 927, 688, 459, 284) ( 962, 732, 509, 331)]
 [( 978, 723, 449, 330) (1005, 712, 446, 314) ( 946, 667, 393, 269) ...,
  ( 949, 692, 413, 271) ( 927, 689, 472, 285) ( 966, 742, 516, 331)]]

Para selecionar um conjunto diferente de pixels dessa imagem, basta especificar o affineTransform de acordo. Lembre-se de que o affineTransform é especificado no sistema de referência de coordenadas espaciais da imagem. Se você quiser ajustar o local da origem em coordenadas de pixel, use esta fórmula simples:

request_origin = image_origin + pixel_scale * offset_in_pixels

Como gerar uma imagem em miniatura

Podemos usar um mecanismo semelhante para gerar uma miniatura RGB dessa imagem. Em vez de solicitar dados na resolução nativa, vamos especificar uma região e as dimensões da imagem de forma explícita. Para conseguir uma miniatura da imagem inteira, podemos usar a geometria de pegada da imagem como a região da solicitação. Por fim, ao especificar as bandas de imagem vermelha, verde e azul e um intervalo apropriado de valores de dados, podemos obter uma imagem em miniatura RGB atraente.

Juntando tudo isso, o snippet em Python fica assim (usando o widget de exibição de imagens IPython do Colab):

url = 'https://earthengine.googleapis.com/v1alpha/{}:getPixels'.format(name)
body = json.dumps({
    'fileFormat': 'PNG',
    'bandIds': ['B4', 'B3', 'B2'],
    'region': asset['geometry'],
    'grid': {
        'dimensions': {'width': 256, 'height': 256},
    },
    'visualizationOptions': {
        'ranges': [{'min': 0, 'max': 3000}],
    },
})

image_response = session.post(url, body)
image_content = image_response.content

from IPython.display import Image
Image(image_content)

Esta é a imagem de miniatura resultante: