Upload de manifesto de imagem

Se você precisar de mais flexibilidade ao fazer upload de imagens no Google Earth Engine (EE) do que a interface do Code Editor ou o comando upload da ferramenta de linha de comando "earthengine", faça o upload de uma imagem usando um arquivo JSON conhecido como "manifesto" e o comando upload image --manifest da ferramenta de linha de comando.

Confira um exemplo completo em este notebook do Colab que demonstra o upload de blocos de imagem como um único recurso usando um manifesto.

Configuração única

  1. Os uploads de manifestos só funcionam com arquivos localizados no Google Cloud Storage. Para começar a usar o Google Cloud Storage, crie um projeto do Google Cloud, se você ainda não tiver um. A configuração exige a especificação de um cartão de crédito para faturamento. A EE não está cobrando ninguém no momento, mas transferir arquivos para o Google Cloud Storage antes de fazer upload deles para a EE vai ter um pequeno custo. Para tamanhos de dados de upload típicos (dezenas ou centenas de gigabytes), o custo será bastante baixo.
  2. No projeto, ative a API Cloud Storage e crie um bucket.
  3. Instale o cliente Python do Earth Engine. Ele inclui a ferramenta de linha de comando earthengine, que será usada para fazer upload de dados.
  4. Para uploads automatizados, use uma conta de serviço do Google Cloud associada ao seu projeto. Você não precisa de uma conta de serviço para testar, mas quando tiver um momento, comece a se familiarizar com o uso delas.

Arquivos de origem muito grandes (100 GB ou mais) podem ser enviados mais rapidamente se forem divididos em vários blocos.

IDs e nomes dos recursos

Para recursos pertencentes a um projeto do Cloud, use esta convenção para nomes de recursos: projects/some-project-id/assets/some-asset-id.

Saiba mais sobre os nomes de recursos de projetos legados e de recursos de propriedade do usuário

Para projetos legados mais antigos, o nome do recurso no manifesto precisa ser um pouco diferente do ID do recurso visível em outros lugares no Earth Engine. Para fazer upload de recursos com IDs que começam com users/some_user ou projects/some_project, o nome do recurso no manifesto precisa ter a string projects/earthengine-legacy/assets/ adicionada ao ID. Por exemplo, o ID do recurso EE users/username/my_geotiff precisa ser enviado usando o nome projects/earthengine-legacy/assets/users/username/my_geotiff.

Sim, isso significa que IDs como projects/some_projects/some_asset são convertidos em nomes em que projects é mencionado duas vezes: projects/earthengine-legacy/assets/projects/some_projects/some_asset. Isso é confuso, mas necessário para se conformar aos padrões da API Google Cloud.

Como usar manifestos

Um manifesto básico é mostrado no bloco de código a seguir. Ele faz upload de um arquivo chamado small.tif de um bucket do Google Cloud Storage chamado gs://earthengine-test.

{
  "name": "projects/some-project-id/assets/some-asset-id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://earthengine-test/small.tif"
          ]
        }
      ]
    }
  ]
}

Para usá-lo, salve-o em um arquivo chamado manifest.json e execute:

earthengine upload image --manifest /path/to/manifest.json

O arquivo gs://earthengine-test/small.tif existe e é legível publicamente. Você pode usá-lo para testes.

Conjuntos de blocos

A estrutura de manifesto um tanto complicada do JSON é necessária para dar flexibilidade suficiente para resolver um desafio comum de upload: como descrever todas as maneiras possíveis de combinar pixels de vários arquivos de origem em um único recurso. Especificamente, há duas maneiras independentes de agrupar arquivos:

  • Mosaicos. Às vezes, vários arquivos representam vários blocos (por exemplo, cada bloco é um quadrado de 1x1 grau). Esses arquivos precisam ser mosaicos (fundidos) na mesma faixa em um recurso de EE.
  • Bandas separadas. Às vezes, vários arquivos representam várias bandas. Esses arquivos precisam ser empilhados como faixas em um recurso de EE.

Talvez seja necessário usar as duas maneiras ao mesmo tempo, mas isso é raro.

Para descrever essas opções, os manifestos introduzem o conceito de um tileset. Um único tileset corresponde a uma única origem do GDAL. Por isso, todas as origens em um único conjunto de blocos precisam ter a mesma estrutura do GDAL (número e tipo de faixas, projeção, transformação, valor ausente). Como uma fonte do GDAL pode ter várias bandas, um conjunto de blocos pode conter dados de várias bandas de EE.

Para a aquisição de mosaico, o manifesto será assim:

{
  "name": "projects/some-project-id/assets/some-asset-id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://bucket/N30W22.tif"
          ]
        },
        {
          "uris": [
            "gs://bucket/N31W22.tif"
          ]
        }
      ]
    }
  ]
}

Para bandas separadas, o manifesto vai ficar assim. Você também precisa adicionar uma seção bands, conforme explicado abaixo:

{
  "name": "projects/some-project-id/assets/some-asset-id",
  "bands": ...,
  "tilesets": [
    {
      "id": "tileset_for_band1",
      "sources": [
        {
          "uris": [
            "gs://bucket/band1.tif"
          ]
        }
      ]
    },
    {
      "id": "tileset_for_band2",
      "sources": [
        {
          "uris": [
            "gs://bucket/band2.tif"
          ]
        }
      ]
    }
  ]
}

No caso de faixas separadas, precisamos atribuir um ID diferente a cada bloco para maior clareza. O ID do bloco de telhas pode ser uma string arbitrária. Essas strings não são mantidas no recurso enviado. Os IDs de conjunto de blocos são usados apenas na transferência para distinguir os conjuntos de blocos empilhados.

Bandas

O segundo conceito importante é a correspondência de arquivos de origem com as faixas de recursos de EE. Isso é feito usando a seção bands do manifesto.

A seção bands pode ser omitida. Nesse caso, as faixas são criadas primeiro a partir dos arquivos do primeiro conjunto de blocos, depois do próximo e assim por diante. Por padrão, os intervalos são nomeados "b1", "b2" etc. Para substituir os nomes de banda padrão, inclua uma seção "bands" no final, como esta:

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://bucket/rgb.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "R",
      "tilesetBandIndex": 0
    },
    {
      "id": "G",
      "tilesetBandIndex": 1
    },
    {
      "id": "B",
      "tilesetBandIndex": 2
    }
  ]
}

O número de bandas de EE precisa ser igual ao número total de bandas em todos os conjuntos de blocos.

Se você não quiser transferir todas as bandas de um arquivo, use o campo tilesetBandIndex para indicar quais das bandas do GDAL devem ser transferidas. A primeira faixa tem um tilesetBandIndex de 0.

Exemplo:

Suponha que o arquivo de origem tenha quatro faixas: "tmin", "tmin_error", "tmax" e "tmax_error". Queremos apenas processar "tmin" e "tmax". As seções relevantes do manifesto vão ficar assim:

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "id": "temperature",
      "sources": [
        {
          "uris": [
            "gs://bucket/temperature.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "tmin",
      "tilesetBandIndex": 0,
      "tilesetId": "temperature"
    },
    {
      "id": "tmax",
      "tilesetBandIndex": 2,
      "tilesetId": "temperature"
    }
  ]
}

Máscaras de banda

O mascaramento de banda é controlado pelo componente maskBands do manifesto. Três configurações de máscara possíveis são compatíveis, mas a banda de máscara é sempre considerada a última banda em um determinado arquivo.

  1. Use a máscara para todas as faixas de dados no mesmo arquivo.
  2. Mascarar todas as faixas de dados provenientes de outros arquivos.
  3. Mascarar algumas faixas de dados.

1. O caso mais comum é um único GeoTIFF em que a última banda é usada como máscara para as outras bandas. Isso só funciona para GeoTIFFs do tipo Byte. Use o seguinte manifesto:

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "id": "data_tileset",
      "sources": [
        {
          "uris": [
            "gs://bucket/data_file.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "data_band",
      "tilesetId": "data_tileset"
    },
    {
      "id": "qa_band",
      "tilesetId": "data_tileset"
    }
  ],
  "maskBands": [
    {
      "tilesetId": "data_tileset"
    }
  ]
}

2. Para usar uma máscara GeoTIFF como uma máscara para todas as bandas em outro GeoTIFF, use o seguinte manifesto:

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "id": "data_tileset",
      "sources": [
        {
          "uris": [
            "gs://bucket/data_file.tif"
          ]
        }
      ]
    },
    {
      "id": "mask_tileset",
      "sources": [
        {
          "uris": [
            "gs://bucket/mask_file.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "data_band",
      "tilesetId": "data_tileset"
    },
    {
      "id": "qa_band",
      "tilesetId": "data_tileset"
    }
  ],
  "maskBands": [
    {
      "tilesetId": "mask_tileset"
    }
  ]
}

3. Para usar um GeoTIFF como uma máscara para uma banda específica em outro arquivo, use o seguinte manifesto (a diferença em relação ao caso anterior é que o campo bandIds em maskBands está definido):

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "id": "data_tileset",
      "sources": [
        {
          "uris": [
            "gs://bucket/data_file.tif"
          ]
        }
      ]
    },
    {
      "id": "mask_tileset",
      "sources": [
        {
          "uris": [
            "gs://bucket/mask_file.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "data_band",
      "tilesetId": "data_tileset"
    },
    {
      "id": "qa_band",
      "tilesetId": "data_tileset"
    }
  ],
  "maskBands": [
    {
      "tilesetId": "mask_tileset",
      "bandIds": ["data_band"]
    }
  ]
}

No último exemplo, estamos trabalhando com duas faixas do conjunto de blocos data_tileset, mas estamos aplicando uma máscara apenas a uma das faixas (data_band), conforme indicado pelo campo bandIds do único objeto de lista maskBands fornecido.

Somente a última faixa do conjunto de blocos mencionada em maskBands é usada como a faixa de máscara.

Política de pirâmide

Quando o Earth Engine constrói pirâmides de imagens durante a transferência, ele precisa reduzir repetidamente as grades de 2 x 2 pixels em um único pixel, transformando o valor do pixel de alguma forma. Por padrão, os valores de pixel são calculados em média, o que é a coisa certa a fazer na maioria dos casos, quando a banda raster representa dados mais ou menos contínuos. No entanto, há duas situações em que a utilização do padrão vai produzir resultados incorretos. Nesse caso, o campo pyramidingPolicy na definição da banda precisa ser definido. Se não for definido, o valor será "MEAN" por padrão.

Para a classificação de imagens raster (por exemplo, para a classificação de cobertura de terras), a maneira mais lógica de piramidar pixels é usar a maioria dos quatro valores para produzir o próximo. Isso é feito usando a política de pirâmide "MODE":

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://bucket/landcover.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "landcover",
      "pyramidingPolicy": "MODE"
    }
  ]
}

Para bandas raster em que nem "MEAN" nem "MODE" fazem sentido (por exemplo, pixels compactados), use a política de acúmulo de "SAMPLE". "SAMPLE" sempre recebe o valor do pixel superior esquerdo de cada grade 2x2. O exemplo a seguir atribui a política de acúmulo "MEAN" a uma banda que representa uma variável contínua ("NDVI") e "SAMPLE" à banda "QA" dos dados.

{
  "name": "projects/earthengine-legacy/assets/users/username/some_folder/some_id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://bucket/ndvi.tif"
          ]
        }
      ]
    }
  ],
  "bands": [
    {
      "id": "NDVI",
      "tilesetBandIndex": 0,
      "pyramidingPolicy": "MEAN"
    },
    {
      "id": "QA",
      "tilesetBandIndex": 1,
      "pyramidingPolicy": "SAMPLE"
    }
  ]
}

Horário de início e término

Todos os recursos precisam especificar o horário de início e término para dar mais contexto aos dados, especialmente se eles forem incluídos em coleções. Esses campos não são obrigatórios, mas é altamente recomendável usá-los sempre que possível.

O horário de início e término geralmente significa o horário da observação, não o horário em que o arquivo de origem foi produzido.

O horário de término é tratado como um limite exclusivo para simplificar. Por exemplo, para recursos que abrangem exatamente um dia, use a meia-noite de dois dias consecutivos (por exemplo, 1980-01-31T00:00:00 e 1980-02-01T00:00:00) para a hora de início e término. Se o recurso não tiver duração, defina o horário de término como o mesmo que o de início. Representar horários em manifestos como strings ISO 8601. Recomendamos que você suponha que o horário de término seja exclusivo (por exemplo, meia-noite do dia seguinte para recursos diários) para simplificar os valores de data.

Exemplo:

{
  "name": "projects/some-project-id/assets/some-asset-id",
  "tilesets": [
    {
      "sources": [
        {
          "uris": [
            "gs://bucket/img_20190612.tif"
          ]
        }
      ]
    }
  ],
  "startTime": "1980-01-31T00:00:00Z",
  "endTime": "1980-02-01T00:00:00Z"
}

Referência da estrutura do manifesto

A estrutura JSON a seguir inclui todos os campos possíveis do manifesto de upload de imagens. Encontre definições de campos na seção "Definições de campos do manifesto" a seguir.

{
  "name": <string>,
  "tilesets": [
    {
      "dataType": <string>,
      "id": <string>,
      "crs": <string>,
      "sources": [
        {
          "uris": [
            <string>
          ],
          "affineTransform": {
            "scaleX": <double>,
            "shearX": <double>,
            "translateX": <double>,
            "shearY": <double>,
            "scaleY": <double>,
            "translateY": <double>
          }
        }
      ]
    }
  ],
  "bands": [
    {
      "id": <string>,
      "tilesetId": <string>,
      "tilesetBandIndex": <int32>,
      "missingData": {
        "values": [<double>]
      },
      "pyramindingPolicy": <string>
    }
  ],
  "maskBands": [
    {
      "tilesetId": <string>,
      "bandIds": [
        <string>
      ]
    }
  ],
  "footprint": {
    "points": [
      {
        "x": <double>,
        "y": <double>
      }
    ],
    "bandId": <string>
  },
  "missingData": {
     "values": [<double>]
  },
  "pyramidingPolicy": <string>,
  "uriPrefix": <string>,
  "startTime": {
    "seconds": <integer>
  },
  "endTime": {
    "seconds": <integer>
  },
  "properties": {
    <unspecified>
  }
}

Definições de campos do manifesto

nome

string

O nome do recurso a ser criado. name tem o formato "projects/*/assets/**", por exemplo, "projects/earthengine-legacy/assets/users/USER/ASSET".

conjuntos de blocos

list

Uma lista de dicionários que definem propriedades para conjuntos de blocos. Consulte os campos de elemento do dicionário tilesets a seguir para mais informações.

tilesets[i].dataType

string

Especifica o tipo de dados numéricos. O padrão é o tipo que o GDAL informa. Nesse caso, não é necessário definir.

Tipo de dado Valor
Não especificado "DATA_TYPE_UNSPECIFIED"
Inteiro assinado de 8 bits "INT8"
Número inteiro não assinado de 8 bits "UINT8"
Inteiro assinado de 16 bits "INT16"
Número inteiro não assinado de 16 bits "UINT16"
Inteiro assinado de 32 bits "INT32"
Número inteiro sem assinatura de 32 bits "UINT32"
Flutuante de 32 bits "FLOAT32"
Ponto flutuante de 64 bits "FLOAT64"

tilesets[i].id

string

O ID do bloco de telhas. Precisa ser exclusivo entre os tilesets especificados no manifesto de recursos. Esse ID é descartado durante a etapa de processamento e é usado apenas para vincular um conjunto de blocos a uma faixa. A string vazia é um ID válido.

tilesets[i].crs

string

O sistema de referência de coordenadas da grade de pixels, especificado como um código padrão, sempre que possível (por exemplo, código EPSG), ou no formato WKT.

tilesets[i].sources

list

Uma lista de dicionários que definem as propriedades de um arquivo de imagem e os sidecars dele. Consulte os campos de elemento do dicionário sources a seguir para mais informações.

tilesets[i].sources[j].uris

list

Uma lista dos URIs dos dados a serem transferidos. Somente URIs do Google Cloud Storage são aceitos. Cada URI precisa ser especificado no seguinte formato: gs://bucket-id/object-id. O objeto principal precisa ser o primeiro elemento da lista, e os sidecars precisam ser listados depois. Cada URI é prefixado com ImageManifest.uriPrefix, se definido.

tilesets[i].sources[j].affineTransform

dictionary

Uma transformação afín opcional. Só deve ser especificado se os dados de uris (incluindo sidecars) não forem suficientes para colocar os pixels. Fornecido como um dicionário com as seguintes chaves: "scaleX", "shearX", "translateX", "shearY", "scaleY", "translateY". Consulte esta referência para mais informações.

Exemplos de chaves e valores:

{
  "scaleX": 0.1,
  "shearX": 0.0,
  "translateX": -180.0,
  "shearY": 0.0,
  "scaleY": -0.1,
  "translateY": 90.0
}

bandas

list

Uma lista de dicionários que definem as propriedades de uma única banda proveniente de um conjunto de telhas. A ordem das faixas do recurso é a mesma de bands. Consulte os campos de elemento do dicionário bands a seguir para mais informações.

bands[i].id

string

O ID (nome) da banda.

bands[i].tilesetId

string

O ID do conjunto de blocos correspondente à faixa.

bands[i].tilesetBandIndex

int32

O índice de banda com base em zero do conjunto detileset correspondente à banda.

bands[i].missingData.values

list

Uma lista de valores (tipo duplo) que não representam dados na faixa.

bands[i].pyramidingPolicy

string

A política de pirâmide. Consulte este link para mais informações. As opções incluem:

  • "MÉDIA" (padrão)
  • "MODO"
  • "SAMPLE"

maskBands

list

Uma lista de dicionários que definem as propriedades de uma única banda de máscara proveniente de um conjunto de telhas. É possível fornecer no máximo uma faixa de máscara. Consulte os campos de elemento do dicionário maskBands a seguir para mais informações.

maskBands[i].tilesetId

string

O ID do conjunto de telhas correspondente à faixa de máscara. A última faixa do conjunto de blocos é sempre usada como a faixa de máscara.

maskBands[i].bandIds

list of strings

Lista dos IDs das faixas a que a faixa de máscara se aplica. Se estiver vazio, a faixa de máscara será aplicada a todas as faixas no recurso. Cada banda só pode ter uma banda de máscara correspondente.

pegada

dictionary

Um dicionário que define as propriedades da pegada de todos os pixels válidos em uma imagem. Se estiver vazio, o espaço ocupado padrão será a imagem inteira. Consulte os campos de elemento do dicionário footprint a seguir para mais informações.

footprint.points

list

Uma lista de pontos que definem a pegada de todos os pixels válidos em uma imagem. Um ponto é definido por um dicionário com chaves "x" e "y" que têm valores flutuantes. Uma lista de pontos é usada para descrever um anel que forma o exterior de um polígono simples que precisa conter os centros de todos os pixels válidos da imagem. Precisa ser um anel linear: o último ponto precisa ser igual ao primeiro. As coordenadas estão na projeção da banda especificada por bandId.

Observação: use coordenadas não inteiras, como o centro de cada pixel, porque footprint é usado para incluir um pixel se ele (um retângulo 1x1) cruzar a pegada. Para evitar a seleção acidental de pixels vizinhos, não use coordenadas com valores inteiros, porque elas são os limites entre os pixels. Desenhar a pegada ao longo dos centros de pixel evita a inclusão de pixels indesejados, o que pode causar erros quando os pixels pretendidos estão adjacentes a um limite do mapa, como o antimeridiano ou um pólo.

Por exemplo, para uma imagem de 2 x 2 com os quatro pixels válidos, o seguinte é um anel possível:

[
  {
    "x": 0.5,
    "y": 0.5
  },
  {
    "x": 0.5,
    "y": 1.5
  },
  {
    "x": 1.5,
    "y": 1.5
  },
  {
    "x": 1.5,
    "y": 0.5
  },
  {
    "x": 0.5,
    "y": 0.5
  }
]

footprint.bandId

string

O ID da faixa cujo CRS define as coordenadas da pegada. Se estiver vazio, a primeira faixa será usada.

missingData.values

list

Uma lista de valores (tipo duplo) que não representam dados em todas as bandas da imagem. Aplicável a todas as bandas que não especificam o próprio missingData.

pyramidingPolicy

string

A política de pirâmide. Se não for especificado, a política "MEAN" será aplicada por padrão. Aplicável a todas as faixas que não especificam as próprias. Consulte este link para mais informações. As opções incluem:

  • "MÉDIA" (padrão)
  • "MODO"
  • "SAMPLE"

uriPrefix

string

Um prefixo opcional adicionado a todos os uris definidos no manifesto.

startTime

integer

O carimbo de data/hora associado ao recurso, se houver. Isso geralmente corresponde ao momento em que uma imagem de satélite foi tirada. Para recursos que correspondem a um intervalo de tempo, como valores médios em um mês ou ano, esse carimbo de data/hora corresponde ao início desse intervalo. Especificado como segundos e (opcionalmente) nanossegundos desde o início da época (1970-01-01). Supõe-se que esteja no fuso horário UTC.

endTime

integer

Para recursos que correspondem a um intervalo de tempo, como valores médios ao longo de um mês ou ano, esse carimbo de data/hora corresponde ao fim desse intervalo (exclusivo). Especificado como segundos e (opcionalmente) nanossegundos desde o início da época (1970-01-01). Supõe-se que esteja no fuso horário UTC.

properties

dictionary

Um dicionário plano arbitrário de pares de chave-valor. As chaves precisam ser strings, e os valores podem ser números ou strings. Os valores de lista ainda não são compatíveis com recursos enviados pelo usuário.

Limitações

Tamanho do manifesto JSON

O limite de tamanho do arquivo de manifesto JSON é de 10 MB. Se você tiver muitos arquivos para fazer upload, considere maneiras de reduzir o número de caracteres necessários para descrever o conjunto de dados. Por exemplo, use o campo uriPrefix para eliminar a necessidade de fornecer o caminho do bucket do Google Cloud para cada URI na uris. Se for necessário reduzir ainda mais o tamanho, tente encurtar os nomes dos arquivos.

Formato do arquivo de imagem

Cada arquivo de imagem precisa ser TIFF. Se o CRS não for especificado no manifesto, o arquivo precisará ser um GeoTIFF com um CRS incorporado.

Os arquivos TIFF podem ser compactados com DEFLATE, JPEG-XL/JXL, LERC, LERC_DEFLATE, LERC_ZSTD, LZMA, LZW, WEBP ou ZSTD.

Recomendações para a melhor experiência de upload de arquivos grandes:

  • Melhor escolha:o ZSTD oferece um bom equilíbrio entre velocidade e compactação.
  • Evite:a LZMA pode ser muito lenta, apesar da boa compactação.
  • Arquivos descompactados:resultam em arquivos maiores e em tempos de upload mais longos.
  • Compressão com perdas (por exemplo, JPEG: pode alterar os valores dos pixels. Use compactação sem perdas (por exemplo, DEFLATE, LZMA, LZW, ZSTD) a menos que você entenda o possível impacto nos seus dados.