Earth Engine supports assets backed by Cloud Optimized GeoTIFFs (COGs) stored in Google Cloud Storage (GCS).
When you register a COG-backed asset, Earth Engine indexes the spatial and metadata fields of the image at creation time. This makes the image performant when used in collections, with performance comparable to standard ingested assets (see the Cloud optimized GeoTIFF (COG) overview).
Prerequisites and Cloud Storage setup
Before registering a COG, you must set up your Google Cloud Storage resources correctly.
1. Bucket location (strict requirement)
For Earth Engine to access your Cloud Storage bucket, the bucket must be located in one of the following data center regions or multi-regions:
- The
usmulti-region - Any US dual-region that includes
us-central1 - The
us-central1region
If your bucket is placed in any other regional data center (such as us-east4
or europe-west1), Earth Engine will reject registration and read requests.
2. Storage class and billing
- We recommend using the Standard storage class. Standard storage has no minimum storage duration rules, preventing early deletion billing penalties if you update or replace temporary files.
- While Earth Engine computation loops don't incur Cloud Storage data retrieval surcharges, accessing non-Standard storage tiers from external tools (such as desktop GIS software) incurs costly retrieval fees.
- You must have billing enabled on your Google Cloud project to use GCS. See the Google Cloud Storage Quickstart to get started.
Step 1: Prepare your COGs
Your TIFF files must be optimized for the cloud. The TIFF must be:
- Tiled, with tile dimensions of: 256x256, 512x512, 1024x1024, or 2048x2048. We recommend 512x512 or higher for best performance.
- Arranged so that all Image File Directories (IFDs) are at the beginning.
- Include power-of-2 overviews.
- Use BAND interleave.
You can use GDAL to convert your images.
Using GDAL COG Driver (GDAL >= 3.1 recommended):
gdal_translate in.tif out.tif \
-of COG \
-co BIGTIFF=YES \
-co OVERVIEWS=IGNORE_EXISTING \
-co COMPRESS=ZSTD \
-co LEVEL=16 \
-co BLOCKSIZE=512 \
-co INTERLEAVE=BAND \
-co NUM_THREADS=ALL_CPUS \
-co PREDICTOR=2 # 2 for integers, 3 for floating point
Using standard GTiff driver:
First, generate internal power-of-2 overviews (using
gdal raster overview add
or gdaladdo), then copy
the image and overviews into a tiled COG layout:
gdaladdo -r nearest in.tif 2 4 8 16 32 # or -r cubic for continuous/float data
gdal_translate in.tif out.tif \
-co BIGTIFF=YES \
-co COPY_SRC_OVERVIEWS=YES \
-co TILED=YES \
-co BLOCKXSIZE=512 \
-co BLOCKYSIZE=512 \
-co COMPRESS=ZSTD \
-co ZSTD_LEVEL=16 \
-co INTERLEAVE=BAND \
-co NUM_THREADS=ALL_CPUS \
-co PREDICTOR=2 # 2 for integers, 3 for floating point
Step 2: Construct the manifest
Whether you register a COG using the Earth Engine Python client library, the
earthengine command-line tool, or the Earth Engine REST API, you must define
an image upload manifest specifying the asset destination, Google Cloud Storage
URIs, and optional band metadata.
Example: Single COG backing all bands If your COG contains all bands (e.g., RGB), the manifest requires only a single tileset:
{
"name": "projects/my-project/assets/my_cog_asset",
"tilesets": [
{
"id": "default_tileset",
"sources": [
{
"uris": [
"gs://google-sample-bucket/my_optimized_image.tif"
]
}
]
}
]
}
Example: Multiple COGs (One per band) If you have separate files for different bands (e.g., Red, Green, Blue in separate files):
{
"name": "projects/my-project/assets/my_multi_band_cog",
"tilesets": [
{
"id": "red_tileset",
"sources": [ { "uris": [ "gs://google-sample-bucket/band_red.tif" ] } ]
},
{
"id": "green_tileset",
"sources": [ { "uris": [ "gs://google-sample-bucket/band_green.tif" ] } ]
}
],
"bands": [
{ "id": "R", "tilesetId": "red_tileset", "tilesetBandIndex": 0 },
{ "id": "G", "tilesetId": "green_tileset", "tilesetBandIndex": 0 }
]
}
Step 3: Submit the registration request
You can submit your manifest to Earth Engine using the Earth Engine Python
client library, the earthengine command-line tool, or the Earth Engine REST
API. Choose the method that best fits your workflow.
Python client library
In a Python script or Jupyter notebook, use the Earth Engine Python
client library (ee.data) after initializing your project:
import ee # 1. Authenticate and initialize ee.Authenticate() ee.Initialize(project='your-project-id') # 2. Define the manifest as a Python dictionary manifest = { "name": "projects/your-project-id/assets/my_cog_asset", "tilesets": [ { "id": "default_tileset", "sources": [ { "uris": [ "gs://google-sample-bucket/my_optimized_image.tif" ] } ] } ] } # 3. Start the ingestion task (allow_overwrite=True corresponds to --force) result = ee.data.startExternalImageIngestion(manifest, allow_overwrite=True) print(f"Asset registered: {result['name']}")
Command-line tool
In your terminal, save your manifest to a file named
manifest.json and run the earthengine upload
command:
# 1. Verify your environment is authenticated earthengine authenticate # 2. Submit the external image upload task earthengine --project your-project-id \ upload external_image \ --manifest manifest.json
REST API (Python)
For automated workflows without the client library, send an authorized
POST request directly to the Earth Engine REST API endpoint
projects.image.importExternal (demonstrated here using
Python):
import json from pprint import pprint import ee from google.auth.transport.requests import AuthorizedSession # 1. Authenticate and start an authorized HTTP session ee.Authenticate() ee_project = 'your-project-id' session = AuthorizedSession( ee.data.get_persistent_credentials().with_quota_project(ee_project) ) # 2. Define the REST request body wrapping the manifest request = { 'imageManifest': { 'name': f'projects/{ee_project}/assets/my_cog_asset', 'tilesets': [ { 'id': 'default_tileset', 'sources': [ { 'uris': [ 'gs://google-sample-bucket/my_optimized_image.tif' ] } ] } ] } } # 3. Send the POST request url = ( 'https://earthengine.googleapis.com/v1alpha/' f'projects/{ee_project}/image:importExternal' ) response = session.post(url=url, data=json.dumps(request)) pprint(json.loads(response.content))
Once submitted, the registration request initiates a lightweight metadata indexing operation. Because COG registration does not copy pixel data, this operation typically completes in seconds.
Manifest restrictions for COG assets
When creating a manifest for COG-backed assets, note these strict restrictions (which differ from standard ingestion manifests):
- Single bucket: All URIs in the manifest must reside in the same GCS bucket.
- One URI per source: Each
ImageSourcemust contain exactly one URI. You cannot mosaic multiple files together into a single tileset source. To mosaic multiple COG scenes, register each COG as an asset and combine them into anee.ImageCollectionusing.mosaic(). - No
mask_bands: Top-levelmask_bandsis not supported. UsemissingDatain the manifest or ensure your TIFF has an internal nodata value. - No
data_typein tileset: Do not specifydata_typein thetilesetssection; it is automatically detected. - No
dimensionsin source: Do not specifydimensionsin theImageSource. - No
subdataset_prefix/subdataset_suffix. - No
min_tile_area_ratioorskip_metadata_read.
Step 4: Configure permissions (crucial for sharing)
Unlike standard Earth Engine assets, the pixel data of a COG remains in GCS. When sharing a COG-backed asset with others, you must grant them read access to both the Earth Engine asset and the GCS files.
1. Grant GCS bucket permissions
Collaborators need permissions to read the files from GCS.
Required permissions:
storage.buckets.get(to retrieve bucket metadata and location)storage.objects.get(to read the pixel data)
You can grant these permissions using the following standard Cloud Storage roles:
- Storage Bucket Viewer (provides
storage.buckets.getto read bucket metadata and location) - Storage Object Viewer (provides
storage.objects.getto read pixel data)
Steps to configure in Google Cloud Console:
- Go to the Cloud Storage Browser.
- Click your bucket name.
- Select the Permissions tab.
- Click GRANT ACCESS.
- Add the email addresses of your collaborators (or a Google Group,
or
allUsersfor public access). - Assign the roles Storage Bucket Viewer and Storage Object Viewer.
- Click Save.
2. Share the Earth Engine asset
Share the asset itself using the Earth Engine Asset Manager or CLI. See the Asset Management Guide for details.
Managing object generations
When you register a COG, Earth Engine stores the specific generation (version) of the GCS object. If you update the file in GCS, its generation changes.
- By default, Earth Engine will fail to load the asset and return an error:
Failed to load the GeoTIFF at gs://google-sample-bucket/my-object#old-generation. - This is designed to prevent metadata (like projection or footprint) from getting out of sync with the underlying pixels.
Best practice: If you need to update the underlying image data, register it as a new asset or re-run the upload command with the
--forceflag rather than overwriting files in place:earthengine --project your-project-id upload external_image --force --manifest manifest.json
Using registered assets
Once registered, a COG-backed asset is accessed in Earth Engine scripts just like any standard ingested image asset:
JavaScript
var image = ee.Image('projects/your-project-id/assets/my_cog_asset');
Python
image = ee.Image('projects/your-project-id/assets/my_cog_asset')