!pip install earthengine-api -qGoogle Earth Engine to GeoCroissant Support

Overview
This notebook demonstrates how to convert Google Earth Engine (GEE) satellite imagery assets into GeoCroissant format - a specialized extension of the ML Commons Croissant standard for geospatial datasets.
Authenticate and Initialize Earth Engine
This cell authenticates to Google Earth Engine using a service account and initializes the ee Python API for further operations.
import ee
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file(
"./code-earthengine.json",
scopes=["https://www.googleapis.com/auth/earthengine"],
)
ee.Initialize(credentials=credentials)Check Asset Metadata via REST API
This cell uses the REST API and your access token to fetch and print the metadata for a specific Earth Engine asset.
import requests
token = credentials.token
# API CHECK
url = "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG"
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(url, headers=headers)
print(response.json()){'type': 'IMAGE', 'name': 'projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG', 'id': 'COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG', 'properties': {'DATATAKE_IDENTIFIER': 'GS2A_20170430T190351_009691_N05.00', 'SPACECRAFT_NAME': 'Sentinel-2A', 'RADIO_ADD_OFFSET_B8A': -1000, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B8A': 114.911147676295, 'RADIO_ADD_OFFSET_B10': -1000, 'MEAN_SOLAR_AZIMUTH_ANGLE': 144.166615587836, 'SOLAR_IRRADIANCE_B12': 85.25, 'SOLAR_IRRADIANCE_B10': 367.15, 'SENSOR_QUALITY': 'PASSED', 'SOLAR_IRRADIANCE_B11': 245.59, 'GENERATION_TIME': 1695535572000, 'RADIO_ADD_OFFSET_B12': -1000, 'RADIO_ADD_OFFSET_B11': -1000, 'SOLAR_IRRADIANCE_B8A': 955.32, 'FORMAT_CORRECTNESS': 'PASSED', 'CLOUD_COVERAGE_ASSESSMENT': 0.255852831116763, 'SNOW_PIXEL_PERCENTAGE': 0.00209765272659894, 'RADIO_ADD_OFFSET_B1': -1000, 'RADIO_ADD_OFFSET_B2': -1000, 'DATASTRIP_ID': 'S2A_OPER_MSI_L1C_DS_S2RP_20230924T060612_S20170430T190351_N05.00', 'RADIO_ADD_OFFSET_B3': -1000, 'RADIO_ADD_OFFSET_B4': -1000, 'RADIO_ADD_OFFSET_B5': -1000, 'PROCESSING_BASELINE': '05.00', 'SENSING_ORBIT_NUMBER': 113, 'RADIO_ADD_OFFSET_B6': -1000, 'RADIO_ADD_OFFSET_B7': -1000, 'SENSING_ORBIT_DIRECTION': 'DESCENDING', 'GENERAL_QUALITY': 'FAILED', 'GRANULE_ID': 'L1C_T10SEG_A009691_20170430T190351', 'REFLECTANCE_CONVERSION_CORRECTION': 0.987349139279658, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B8': 119.437261123808, 'DATATAKE_TYPE': 'INS-NOBS', 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B9': 114.510594227324, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B6': 115.694920180767, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B7': 115.272311165959, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B4': 116.818521016093, 'MEAN_INCIDENCE_ZENITH_ANGLE_B1': 4.01002710851759, 'RADIO_ADD_OFFSET_B8': -1000, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B5': 116.211704638208, 'RADIOMETRIC_QUALITY': 'PASSED', 'RADIO_ADD_OFFSET_B9': -1000, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B2': 120.817153764908, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B3': 118.335666287999, 'MEAN_INCIDENCE_ZENITH_ANGLE_B5': 3.71790186283323, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B1': 114.772876267468, 'MEAN_INCIDENCE_ZENITH_ANGLE_B4': 3.65278993192244, 'MEAN_INCIDENCE_ZENITH_ANGLE_B3': 3.54497531210579, 'MEAN_INCIDENCE_ZENITH_ANGLE_B2': 3.44840391982784, 'MEAN_INCIDENCE_ZENITH_ANGLE_B9': 4.0941543848476, 'MEAN_INCIDENCE_ZENITH_ANGLE_B8': 3.4936762058753, 'MEAN_INCIDENCE_ZENITH_ANGLE_B7': 3.85962379212438, 'MEAN_INCIDENCE_ZENITH_ANGLE_B6': 3.78681583750869, 'MEAN_SOLAR_ZENITH_ANGLE': 26.371889065193, 'MEAN_INCIDENCE_ZENITH_ANGLE_B8A': 3.93547667443197, 'MGRS_TILE': '10SEG', 'CLOUDY_PIXEL_PERCENTAGE': 0.255852831116763, 'PRODUCT_ID': 'S2A_MSIL1C_20170430T190351_N0500_R113_T10SEG_20230924T060612', 'MEAN_INCIDENCE_ZENITH_ANGLE_B10': 3.60829266014082, 'SOLAR_IRRADIANCE_B9': 812.92, 'DEGRADED_MSI_DATA_PERCENTAGE': 0.0844, 'MEAN_INCIDENCE_ZENITH_ANGLE_B11': 3.76315609583164, 'MEAN_INCIDENCE_ZENITH_ANGLE_B12': 3.93813353642485, 'SOLAR_IRRADIANCE_B6': 1287.61, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B10': 117.01379889609, 'SOLAR_IRRADIANCE_B5': 1424.64, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B11': 115.504968700284, 'SOLAR_IRRADIANCE_B8': 1041.63, 'MEAN_INCIDENCE_AZIMUTH_ANGLE_B12': 114.679133996426, 'SOLAR_IRRADIANCE_B7': 1162.08, 'SOLAR_IRRADIANCE_B2': 1959.66, 'SOLAR_IRRADIANCE_B1': 1884.69, 'SOLAR_IRRADIANCE_B4': 1512.06, 'GEOMETRIC_QUALITY': 'PASSED', 'SOLAR_IRRADIANCE_B3': 1823.24}, 'updateTime': '2025-11-27T14:41:08.794343Z', 'startTime': '2017-04-30T19:04:11.415Z', 'endTime': '2017-04-30T19:04:11.415Z', 'geometry': {'type': 'Polygon', 'coordinates': [[[-123.00034179205349, 37.9475970867561], [-123.00033758169468, 37.03540523327759], [-123.00029882919976, 37.035370092333515], [-123.0002786366622, 37.035326084801994], [-123.00025234337808, 37.03531798690294], [-122.97146744090354, 37.02954498202823], [-122.78806337628276, 36.99798841554587], [-122.60481215367447, 36.966149922145085], [-122.5522785698939, 36.95712738922796], [-122.35600349740389, 36.95622304690406], [-122.15973391884847, 36.954995693957805], [-121.96347149860821, 36.95344535521865], [-121.7672179004439, 36.95157206201084], [-121.76716539030431, 36.95160813825383], [-121.76710775082039, 36.95163857717316], [-121.76710392557831, 36.951653376800685], [-121.75903140997843, 37.446330701468504], [-121.75075919661815, 37.94095746899704], [-121.75080484540763, 37.94099937286281], [-121.7508434512256, 37.94104550336488], [-121.75086214548108, 37.94104856554202], [-121.90701742537077, 37.942599864517334], [-122.06317885967235, 37.94394478501538], [-122.21934557169301, 37.94508330949206], [-122.37551668447043, 37.94601542308925], [-122.53169132080869, 37.946741113643746], [-122.68786860332327, 37.94726037168731], [-122.84404765451283, 37.947573190437666], [-123.0002275967593, 37.94767956582544], [-123.00028018331857, 37.947642997341596], [-123.00033818317065, 37.947611874966036], [-123.00034179205349, 37.9475970867561]]]}, 'bands': [{'id': 'B1', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B2', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 10980, 'height': 10980}, 'affineTransform': {'scaleX': 10, 'translateX': 499980, 'scaleY': -10, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B3', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 10980, 'height': 10980}, 'affineTransform': {'scaleX': 10, 'translateX': 499980, 'scaleY': -10, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B4', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 10980, 'height': 10980}, 'affineTransform': {'scaleX': 10, 'translateX': 499980, 'scaleY': -10, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B5', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B6', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B7', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B8', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 10980, 'height': 10980}, 'affineTransform': {'scaleX': 10, 'translateX': 499980, 'scaleY': -10, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B8A', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B9', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B10', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B11', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'B12', 'dataType': {'precision': 'INT', 'range': {'max': 65535}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 5490, 'height': 5490}, 'affineTransform': {'scaleX': 20, 'translateX': 499980, 'scaleY': -20, 'translateY': 4200000}}, 'pyramidingPolicy': 'MEAN'}, {'id': 'MSK_CLASSI_OPAQUE', 'dataType': {'precision': 'INT', 'range': {'max': 255}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MODE'}, {'id': 'MSK_CLASSI_CIRRUS', 'dataType': {'precision': 'INT', 'range': {'max': 255}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MODE'}, {'id': 'MSK_CLASSI_SNOW_ICE', 'dataType': {'precision': 'INT', 'range': {'max': 255}}, 'grid': {'crsCode': 'EPSG:32610', 'dimensions': {'width': 1830, 'height': 1830}, 'affineTransform': {'scaleX': 60, 'translateX': 499980, 'scaleY': -60, 'translateY': 4200000}}, 'pyramidingPolicy': 'MODE'}], 'sizeBytes': '1199846489'}
Convert Earth Engine Asset Metadata to GeoCroissant JSON-LD
This cell: - Authenticates to Earth Engine, - Fetches asset metadata, - Computes the bounding box and WKT geometry, - Builds a per-band asset dictionary, - Assembles a GeoCroissant-compliant JSON-LD object, - Saves it to gee.json.
import ee
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import json
import datetime
# 1. Authenticate to Earth Engine
SERVICE_ACCOUNT_FILE = "code-earthengine.json"
ASSET_ID = "COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG"
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=["https://www.googleapis.com/auth/earthengine"]
)
creds.refresh(Request())
ee.Initialize(credentials=creds)
token = creds.token
# 2. Fetch asset metadata
meta = ee.data.getAsset(ASSET_ID)
props = meta["properties"]
# 3. Compute bounding box
coords = meta["geometry"]["coordinates"][0]
lons, lats = zip(*coords)
bbox = f"{min(lats)} {min(lons)} {max(lats)} {max(lons)}"
wkt = "POLYGON((" + ", ".join(f"{x} {y}" for x, y in coords) + "))"
# 4. Build per-band assets
assets = {}
for band in meta["bands"]:
band_id = band["id"]
res = band["grid"]["affineTransform"]["scaleX"]
assets[band_id] = {
"href": f"ee://{ASSET_ID}/{band_id}",
"type": "image/tiff",
"roles": ["data"],
"band_name": band_id,
"data_type": band["dataType"]["precision"].lower(),
"spatial_resolution": res,
"description": f"Sentinel-2 band {band_id} of image {ASSET_ID}",
}
# 5. Convert bbox to array format
bbox_coords = [min(lons), min(lats), max(lons), max(lats)]
# 6. Build fileSet for the bands
fileSet_id = f"sentinel2-bands-{ASSET_ID.replace('/', '-')}"
band_files = []
for band in meta["bands"]:
band_files.append(
{
"name": f"{band['id']}.tif",
"path": f"ee://{ASSET_ID}/{band['id']}",
"contentSize": (
band.get("dimensions", {}).get("width", 0)
* band.get("dimensions", {}).get("height", 0)
* 2
), # Approximate size
}
)
# 7. Assemble Geo-Croissant JSON-LD (using correct prefixes & geocr IRIs)
geo_croissant = {
"@context": {
"@language": "en",
"@vocab": "https://schema.org/",
"citeAs": "cr:citeAs",
"column": "cr:column",
"conformsTo": "dct:conformsTo",
"cr": "http://mlcommons.org/croissant/",
"geocr": "http://mlcommons.org/croissant/geo/",
"rai": "http://mlcommons.org/croissant/RAI/",
"dct": "http://purl.org/dc/terms/",
"sc": "https://schema.org/",
"data": {"@id": "cr:data", "@type": "@json"},
"examples": {"@id": "cr:examples", "@type": "@json"},
"dataBiases": "cr:dataBiases",
"dataCollection": "cr:dataCollection",
"dataType": {"@id": "cr:dataType", "@type": "@vocab"},
"extract": "cr:extract",
"field": "cr:field",
"fileProperty": "cr:fileProperty",
"fileObject": "cr:fileObject",
"fileSet": "cr:fileSet",
"format": "cr:format",
"includes": "cr:includes",
"isLiveDataset": "cr:isLiveDataset",
"jsonPath": "cr:jsonPath",
"key": "cr:key",
"md5": "cr:md5",
"parentField": "cr:parentField",
"path": "cr:path",
"personalSensitiveInformation": "cr:personalSensitiveInformation",
"recordSet": "cr:recordSet",
"references": "cr:references",
"regex": "cr:regex",
"repeated": "cr:repeated",
"replace": "cr:replace",
"samplingRate": "cr:samplingRate",
"separator": "cr:separator",
"source": "cr:source",
"subField": "cr:subField",
"transform": "cr:transform",
},
"@type": "sc:Dataset",
"name": ASSET_ID.replace("/", "_"),
"alternateName": [
ASSET_ID.replace("/", "-"),
f"Sentinel-2-{props.get('MGRS_TILE', '')}",
],
"description": (
f"Sentinel-2 Level-1C image over MGRS tile {props.get('MGRS_TILE','')} acquired"
f" on {meta['startTime'][:10]}. This dataset contains"
f" {len(meta['bands'])} spectral bands with spatial resolutions ranging from"
" 10m to 60m."
),
"conformsTo": [
"http://mlcommons.org/croissant/1.0",
"http://mlcommons.org/croissant/geo/1.0"
],
"version": "1.0.0",
"creator": {
"@type": "Organization",
"name": "European Space Agency (ESA)",
"url": "https://www.esa.int/",
},
"url": f"https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/{ASSET_ID}",
"keywords": [
"Sentinel-2",
"satellite imagery",
"remote sensing",
"multispectral",
"Earth observation",
f"MGRS-{props.get('MGRS_TILE', '')}",
"Level-1C",
"ESA",
"Copernicus",
],
"citeAs": f"https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/{ASSET_ID}",
"datePublished": meta["startTime"][:10],
"license": "https://creativecommons.org/licenses/by/4.0/",
"spatialCoverage": {
"@type": "Place",
"geo": {
"@type": "GeoShape",
"box": f"{min(lats)} {min(lons)} {max(lats)} {max(lons)}"
}
},
"temporalCoverage": f"{meta['startTime']}/{meta['endTime']}",
"geocr:spatialResolution": "10-60m",
"geocr:coordinateReferenceSystem": "EPSG:4326",
"variableMeasured": [
{
"@type": "sc:PropertyValue",
"sc:name": "Cloudy pixel percentage",
"sc:value": props.get("CLOUDY_PIXEL_PERCENTAGE", 0),
},
{
"@type": "sc:PropertyValue",
"sc:name": "Cloud coverage assessment",
"sc:value": props.get("CLOUD_COVERAGE_ASSESSMENT", 0),
},
],
"recordSet": [
{
"@type": "cr:RecordSet",
"@id": f"sentinel2_bands_{ASSET_ID.replace('/', '_')}",
"name": f"sentinel2_bands_{ASSET_ID.replace('/', '_')}",
"description": f"Spectral bands for Sentinel-2 image {ASSET_ID}",
"field": [
{
"@type": "cr:Field",
"@id": f"{ASSET_ID.replace('/', '_')}/asset_id",
"name": f"{ASSET_ID.replace('/', '_')}/asset_id",
"description": "Asset identifier",
"dataType": "sc:Text",
"source": {
"fileSet": {"@id": fileSet_id},
"extract": {"fileProperty": "fullpath"},
},
},
{
"@type": "cr:Field",
"@id": f"{ASSET_ID.replace('/', '_')}/image_data",
"name": f"{ASSET_ID.replace('/', '_')}/image_data",
"description": "Satellite imagery data",
"dataType": "sc:ImageObject",
"source": {
"fileSet": {"@id": fileSet_id},
"extract": {"fileProperty": "fullpath"},
},
"geocr:bandConfiguration": {
"@type": "geocr:BandConfiguration",
"geocr:totalBands": len(meta["bands"]),
"geocr:bandNameList": [band["id"] for band in meta["bands"]],
},
},
],
}
],
"fileSet": [
{
"@type": "cr:FileSet",
"@id": fileSet_id,
"name": fileSet_id,
"description": f"Sentinel-2 spectral bands for {ASSET_ID}",
"includes": "*.tif",
"encodingFormat": "image/tiff",
"fileObject": [
{
"@type": "cr:FileObject",
"name": f"{band['id']}.tif",
"contentUrl": f"https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/{ASSET_ID}/{band['id']}",
"encodingFormat": "image/tiff",
"contentSize": (
band.get("dimensions", {}).get("width", 0)
* band.get("dimensions", {}).get("height", 0)
* 2
),
}
for band in meta["bands"]
],
}
],
"distribution": [
{
"@type": "cr:FileObject",
"@id": f"sentinel2-bands-{ASSET_ID.replace('/', '-')}",
"name": f"Sentinel-2 Bands for {ASSET_ID}",
"description": f"Downloadable Sentinel-2 spectral bands for {ASSET_ID}",
"contentUrl": f"https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/{ASSET_ID}",
"encodingFormat": "application/json",
}
],
}
# 8. Write to file
with open("gee.json", "w") as f:
json.dump(geo_croissant, f, indent=2)
print("Geo-Croissant JSON-LD saved to gee.json")Geo-Croissant JSON-LD saved to gee.json
Pretty Print GeoCroissant JSON-LD
This cell loads and pretty-prints the contents of gee.json for easy inspection.
import json
# Load and pretty-print the content of croissant.json
with open("gee.json", "r") as f:
croissant_data = json.load(f)
# Pretty-print JSON to console
print(json.dumps(croissant_data, indent=2)){
"@context": {
"@language": "en",
"@vocab": "https://schema.org/",
"citeAs": "cr:citeAs",
"column": "cr:column",
"conformsTo": "dct:conformsTo",
"cr": "http://mlcommons.org/croissant/",
"geocr": "http://mlcommons.org/croissant/geo/",
"rai": "http://mlcommons.org/croissant/RAI/",
"dct": "http://purl.org/dc/terms/",
"sc": "https://schema.org/",
"data": {
"@id": "cr:data",
"@type": "@json"
},
"examples": {
"@id": "cr:examples",
"@type": "@json"
},
"dataBiases": "cr:dataBiases",
"dataCollection": "cr:dataCollection",
"dataType": {
"@id": "cr:dataType",
"@type": "@vocab"
},
"extract": "cr:extract",
"field": "cr:field",
"fileProperty": "cr:fileProperty",
"fileObject": "cr:fileObject",
"fileSet": "cr:fileSet",
"format": "cr:format",
"includes": "cr:includes",
"isLiveDataset": "cr:isLiveDataset",
"jsonPath": "cr:jsonPath",
"key": "cr:key",
"md5": "cr:md5",
"parentField": "cr:parentField",
"path": "cr:path",
"personalSensitiveInformation": "cr:personalSensitiveInformation",
"recordSet": "cr:recordSet",
"references": "cr:references",
"regex": "cr:regex",
"repeated": "cr:repeated",
"replace": "cr:replace",
"samplingRate": "cr:samplingRate",
"separator": "cr:separator",
"source": "cr:source",
"subField": "cr:subField",
"transform": "cr:transform"
},
"@type": "sc:Dataset",
"name": "COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG",
"alternateName": [
"COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG",
"Sentinel-2-10SEG"
],
"description": "Sentinel-2 Level-1C image over MGRS tile 10SEG acquired on 2017-04-30. This dataset contains 16 spectral bands with spatial resolutions ranging from 10m to 60m.",
"conformsTo": [
"http://mlcommons.org/croissant/1.0",
"http://mlcommons.org/croissant/geo/1.0"
],
"version": "1.0.0",
"creator": {
"@type": "Organization",
"name": "European Space Agency (ESA)",
"url": "https://www.esa.int/"
},
"url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"keywords": [
"Sentinel-2",
"satellite imagery",
"remote sensing",
"multispectral",
"Earth observation",
"MGRS-10SEG",
"Level-1C",
"ESA",
"Copernicus"
],
"citeAs": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"datePublished": "2017-04-30",
"license": "https://creativecommons.org/licenses/by/4.0/",
"spatialCoverage": {
"@type": "Place",
"geo": {
"@type": "GeoShape",
"box": "36.95157206201084 -123.00034179205349 37.94767956582544 -121.75075919661815"
}
},
"temporalCoverage": "2017-04-30T19:04:11.415Z/2017-04-30T19:04:11.415Z",
"geocr:spatialResolution": "10-60m",
"geocr:coordinateReferenceSystem": "EPSG:4326",
"variableMeasured": [
{
"@type": "sc:PropertyValue",
"sc:name": "Cloudy pixel percentage",
"sc:value": 0.255852831116763
},
{
"@type": "sc:PropertyValue",
"sc:name": "Cloud coverage assessment",
"sc:value": 0.255852831116763
}
],
"recordSet": [
{
"@type": "cr:RecordSet",
"@id": "sentinel2_bands_COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG",
"name": "sentinel2_bands_COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG",
"description": "Spectral bands for Sentinel-2 image COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"field": [
{
"@type": "cr:Field",
"@id": "COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG/asset_id",
"name": "COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG/asset_id",
"description": "Asset identifier",
"dataType": "sc:Text",
"source": {
"fileSet": {
"@id": "sentinel2-bands-COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG"
},
"extract": {
"fileProperty": "fullpath"
}
}
},
{
"@type": "cr:Field",
"@id": "COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG/image_data",
"name": "COPERNICUS_S2_20170430T190351_20170430T190351_T10SEG/image_data",
"description": "Satellite imagery data",
"dataType": "sc:ImageObject",
"source": {
"fileSet": {
"@id": "sentinel2-bands-COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG"
},
"extract": {
"fileProperty": "fullpath"
}
},
"geocr:bandConfiguration": {
"@type": "geocr:BandConfiguration",
"geocr:totalBands": 16,
"geocr:bandNameList": [
"B1",
"B2",
"B3",
"B4",
"B5",
"B6",
"B7",
"B8",
"B8A",
"B9",
"B10",
"B11",
"B12",
"MSK_CLASSI_OPAQUE",
"MSK_CLASSI_CIRRUS",
"MSK_CLASSI_SNOW_ICE"
]
}
}
]
}
],
"fileSet": [
{
"@type": "cr:FileSet",
"@id": "sentinel2-bands-COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG",
"name": "sentinel2-bands-COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG",
"description": "Sentinel-2 spectral bands for COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"includes": "*.tif",
"encodingFormat": "image/tiff",
"fileObject": [
{
"@type": "cr:FileObject",
"name": "B1.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B1",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B2.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B2",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B3.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B3",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B4.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B4",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B5.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B5",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B6.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B6",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B7.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B7",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B8.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B8",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B8A.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B8A",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B9.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B9",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B10.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B10",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B11.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B11",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "B12.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/B12",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "MSK_CLASSI_OPAQUE.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/MSK_CLASSI_OPAQUE",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "MSK_CLASSI_CIRRUS.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/MSK_CLASSI_CIRRUS",
"encodingFormat": "image/tiff",
"contentSize": 0
},
{
"@type": "cr:FileObject",
"name": "MSK_CLASSI_SNOW_ICE.tif",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG/MSK_CLASSI_SNOW_ICE",
"encodingFormat": "image/tiff",
"contentSize": 0
}
]
}
],
"distribution": [
{
"@type": "cr:FileObject",
"@id": "sentinel2-bands-COPERNICUS-S2-20170430T190351_20170430T190351_T10SEG",
"name": "Sentinel-2 Bands for COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"description": "Downloadable Sentinel-2 spectral bands for COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"contentUrl": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-public/assets/COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG",
"encodingFormat": "application/json"
}
]
}
Validate GeoCroissant JSON-LD
This cell runs the mlcroissant validator on your GeoCroissant JSON-LD file to check for schema compliance.
!mlcroissant validate --jsonld=gee.jsonE0216 19:08:31.570067 127957704201728 validate.py:55] Found the following 1 error(s) during the validation:
- "_:a2fec96d-5ae9-49b5-b32d-9327d74d63ca" should have an attribute "@type": "https://schema.org/Text". Got None instead.
Visualize Earth Engine Asset and Thumbnails
This cell: - Loads the GeoCroissant JSON-LD, - Extracts the Earth Engine asset ID, - Authenticates and initializes Earth Engine, - Loads the image and centers a map, - Adds true-color and additional band layers, - Generates and displays RGB and band thumbnails, - Displays an interactive map using geemap.
import ee
import json
import numpy as np
import matplotlib.pyplot as plt
from google.oauth2 import service_account
from google.auth.transport.requests import Request
import requests
from PIL import Image
import io
# 1. Load Geo-Croissant JSON-LD
with open("gee.json") as f:
md = json.load(f)
# 2. Extract EE asset ID
parts = md["url"].split("/")
ee_id = "/".join(
parts[-3:]
) # e.g. "COPERNICUS/S2/20170430T190351_20170430T190351_T10SEG"
# 3. Authenticate & init EE
SERVICE_ACCOUNT_FILE = "code-earthengine.json"
creds = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=["https://www.googleapis.com/auth/earthengine"]
)
creds.refresh(Request())
ee.Initialize(credentials=creds)
# 4. Load image and get metadata
img = ee.Image(ee_id)
geometry = img.geometry()
bounds = geometry.bounds().getInfo()["coordinates"][0]
# 5. Get image dimensions and create a region
region = geometry.buffer(1000) # Buffer by 1km
# 6. Function to download band data as numpy array
def download_band_as_array(band_name, region, scale=30):
"""Download a single band from Earth Engine as numpy array"""
band_img = img.select(band_name)
thumb_url = band_img.getThumbURL(
{"region": region, "dimensions": 512, "format": "png", "min": 0, "max": 3000}
)
response = requests.get(thumb_url)
pil_image = Image.open(io.BytesIO(response.content))
array = np.array(pil_image)
return array
# 7. Download bands for visualization
print("Downloading band data...")
bands_to_visualize = ["B2", "B3", "B4", "B8", "B11", "B12"]
band_data = {}
for band in bands_to_visualize:
print(f"Downloading {band}...")
band_data[band] = download_band_as_array(band, region)
print(f" Shape: {band_data[band].shape}, dtype: {band_data[band].dtype}")
# 8. Create RGB composite
def create_rgb_composite(band_data, red_band="B4", green_band="B3", blue_band="B2"):
red = band_data[red_band]
green = band_data[green_band]
blue = band_data[blue_band]
if red.ndim == 3:
red = red[:, :, 0]
if green.ndim == 3:
green = green[:, :, 0]
if blue.ndim == 3:
blue = blue[:, :, 0]
red_norm = red.astype(np.float32) / 255.0
green_norm = green.astype(np.float32) / 255.0
blue_norm = blue.astype(np.float32) / 255.0
rgb = np.stack([red_norm, green_norm, blue_norm], axis=2)
return rgb
# 9. Create false color composite (NIR-Red-Green)
def create_false_color_composite(
band_data, red_band="B8", green_band="B4", blue_band="B3"
):
red = band_data[red_band]
green = band_data[green_band]
blue = band_data[blue_band]
if red.ndim == 3:
red = red[:, :, 0]
if green.ndim == 3:
green = green[:, :, 0]
if blue.ndim == 3:
blue = blue[:, :, 0]
red_norm = red.astype(np.float32) / 255.0
green_norm = green.astype(np.float32) / 255.0
blue_norm = blue.astype(np.float32) / 255.0
fcc = np.stack([red_norm, green_norm, blue_norm], axis=2)
return fcc
# 10. Calculate NDVI
def calculate_ndvi(band_data, nir_band="B8", red_band="B4"):
nir = band_data[nir_band].astype(np.float32)
red = band_data[red_band].astype(np.float32)
if nir.ndim == 3:
nir = nir[:, :, 0]
if red.ndim == 3:
red = red[:, :, 0]
ndvi = np.where((nir + red) == 0, 0, (nir - red) / (nir + red))
return ndvi
# 11. Combined visualization: B2, B3, B4, B8, True Color, False Color, NDVI
rgb_composite = create_rgb_composite(band_data)
false_color = create_false_color_composite(band_data)
ndvi = calculate_ndvi(band_data)
fig, axes = plt.subplots(1, 7, figsize=(36, 6))
fig.suptitle(f"Sentinel-2 Imagery: {ee_id}", fontsize=18)
# Bands B2, B3, B4, B8
for i, band in enumerate(["B2", "B3", "B4", "B8"]):
band_img = band_data[band]
if band_img.ndim == 3:
band_img = band_img[:, :, 0]
axes[i].imshow(band_img, cmap="gray")
axes[i].set_title(f"Band {band}")
axes[i].axis("off")
# True Color
axes[4].imshow(rgb_composite)
axes[4].set_title("True Color (B4-B3-B2)")
axes[4].axis("off")
# False Color
axes[5].imshow(false_color)
axes[5].set_title("False Color (B8-B4-B3)")
axes[5].axis("off")
# NDVI
im = axes[6].imshow(ndvi, cmap="RdYlGn", vmin=-1, vmax=1)
axes[6].set_title("NDVI")
axes[6].axis("off")
fig.colorbar(im, ax=axes[6], fraction=0.03, pad=0.04, label="NDVI")
plt.tight_layout(rect=[0, 0, 1, 0.94])
plt.show()Downloading band data...
Downloading B2...
Shape: (512, 512, 2), dtype: uint8
Downloading B3...
Shape: (512, 512, 2), dtype: uint8
Downloading B4...
Shape: (512, 512, 2), dtype: uint8
Downloading B8...
Shape: (512, 512, 2), dtype: uint8
Downloading B11...
Shape: (512, 512, 2), dtype: uint8
Downloading B12...
Shape: (512, 512, 2), dtype: uint8
