Skip to content

Instantly share code, notes, and snippets.

@xen0f0n
Created April 3, 2025 19:00
Show Gist options
  • Save xen0f0n/3d872d38f033d9231d302315b5ab80d3 to your computer and use it in GitHub Desktop.
Save xen0f0n/3d872d38f033d9231d302315b5ab80d3 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells" : [ {
"metadata" : { },
"cell_type" : "markdown",
"source" : [ "# 🛰️ Stream Cloud-Optimised GeoTIFFs into QGIS\n", "\n", "This notebook demonstrates how to query the Microsoft Planetary Computer STAC API,<br>\n", "retrieve Sentinel-2 and Landsat imagery, access their COG assets,<br>\n", "and programmatically build a QGIS project that streams false-colour composites - all without persistent downloads.\n", "<br>\n", "<br>\n", "<br>\n", "**NOTE on environment setup:**<br>\n", "If you're using Linux, this is likely the most straightforward way to create a Python virtual environment<br>\n", " that can access QGIS and GDAL (osgeo) bindings already installed system-wide:<br>\n", "\n", "```/usr/bin/python3 -m venv --upgrade-deps --system-site-packages .venv```<br>\n", "```source .venv/bin/activate```<br>\n", "```pip install planetary-computer pystac-client rich```" ],
"id" : "e24be54f123f4d5a"
}, {
"metadata" : {
"collapsed" : true,
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:25.724377Z",
"start_time" : "2025-04-03T17:00:25.497573Z"
}
},
"cell_type" : "code",
"source" : [ "import planetary_computer\n", "import pystac_client\n", "import rich.table" ],
"id" : "initial_id",
"outputs" : [ ],
"execution_count" : 1
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : "## Define the Area of Interest (AoI)",
"id" : "33c970db5188d480"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:25.732896Z",
"start_time" : "2025-04-03T17:00:25.729733Z"
}
},
"cell_type" : "code",
"source" : [ "area_of_interest = {\n", " \"type\": \"Polygon\",\n", " \"coordinates\": [[\n", " [31.862215436096932, 30.535598484802946],\n", " [31.862215436096932, 30.200273791875617],\n", " [32.500539123832795, 30.200273791875617],\n", " [32.500539123832795, 30.535598484802946],\n", " [31.862215436096932, 30.535598484802946]\n", " ]]\n", "}" ],
"id" : "f623d2674fa8a1f",
"outputs" : [ ],
"execution_count" : 2
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : "## Connect to the Planetary Computer STAC API",
"id" : "ed219d823657c3a2"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:26.513719Z",
"start_time" : "2025-04-03T17:00:25.843149Z"
}
},
"cell_type" : "code",
"source" : [ "# Use the Microsoft Planetary Computer STAC API\n", "catalog = pystac_client.Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace,\n", ")" ],
"id" : "1f2972979a49e176",
"outputs" : [ ],
"execution_count" : 3
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:32.369940Z",
"start_time" : "2025-04-03T17:00:26.529597Z"
}
},
"cell_type" : "code",
"source" : [ "# List all available STAC Collections\n", "for collection in catalog.get_collections():\n", " print(collection.id)" ],
"id" : "ac4ba3042173fe40",
"outputs" : [ {
"name" : "stdout",
"output_type" : "stream",
"text" : [ "daymet-annual-pr\n", "daymet-daily-hi\n", "3dep-seamless\n", "3dep-lidar-dsm\n", "fia\n", "sentinel-1-rtc\n", "gridmet\n", "daymet-annual-na\n", "daymet-monthly-na\n", "daymet-annual-hi\n", "daymet-monthly-hi\n", "daymet-monthly-pr\n", "gnatsgo-tables\n", "hgb\n", "cop-dem-glo-30\n", "cop-dem-glo-90\n", "terraclimate\n", "nasa-nex-gddp-cmip6\n", "gpm-imerg-hhr\n", "gnatsgo-rasters\n", "3dep-lidar-hag\n", "io-lulc-annual-v02\n", "goes-cmi\n", "conus404\n", "3dep-lidar-intensity\n", "3dep-lidar-pointsourceid\n", "mtbs\n", "noaa-c-cap\n", "3dep-lidar-copc\n", "modis-64A1-061\n", "alos-fnf-mosaic\n", "3dep-lidar-returns\n", "mobi\n", "landsat-c2-l2\n", "era5-pds\n", "chloris-biomass\n", "kaza-hydroforecast\n", "planet-nicfi-analytic\n", "modis-17A2H-061\n", "modis-11A2-061\n", "daymet-daily-pr\n", "3dep-lidar-dtm-native\n", "3dep-lidar-classification\n", "3dep-lidar-dtm\n", "gap\n", "modis-17A2HGF-061\n", "planet-nicfi-visual\n", "gbif\n", "modis-17A3HGF-061\n", "modis-09A1-061\n", "alos-dem\n", "alos-palsar-mosaic\n", "deltares-water-availability\n", "modis-16A3GF-061\n", "modis-21A2-061\n", "us-census\n", "jrc-gsw\n", "deltares-floods\n", "modis-43A4-061\n", "modis-09Q1-061\n", "modis-14A1-061\n", "hrea\n", "modis-13Q1-061\n", "modis-14A2-061\n", "sentinel-2-l2a\n", "modis-15A2H-061\n", "modis-11A1-061\n", "modis-15A3H-061\n", "modis-13A1-061\n", "daymet-daily-na\n", "nrcan-landcover\n", "modis-10A2-061\n", "ecmwf-forecast\n", "noaa-mrms-qpe-24h-pass2\n", "sentinel-1-grd\n", "nasadem\n", "io-lulc\n", "landsat-c2-l1\n", "drcog-lulc\n", "chesapeake-lc-7\n", "chesapeake-lc-13\n", "chesapeake-lu\n", "noaa-mrms-qpe-1h-pass1\n", "noaa-mrms-qpe-1h-pass2\n", "noaa-nclimgrid-monthly\n", "usda-cdl\n", "eclipse\n", "esa-cci-lc\n", "esa-cci-lc-netcdf\n", "fws-nwi\n", "usgs-lcmap-conus-v13\n", "usgs-lcmap-hawaii-v10\n", "noaa-climate-normals-tabular\n", "noaa-climate-normals-netcdf\n", "goes-glm\n", "noaa-climate-normals-gridded\n", "aster-l1t\n", "cil-gdpcir-cc-by-sa\n", "naip\n", "io-lulc-9-class\n", "io-biodiversity\n", "noaa-cdr-sea-surface-temperature-whoi\n", "noaa-cdr-ocean-heat-content\n", "cil-gdpcir-cc0\n", "cil-gdpcir-cc-by\n", "noaa-cdr-sea-surface-temperature-whoi-netcdf\n", "noaa-cdr-sea-surface-temperature-optimum-interpolation\n", "modis-10A1-061\n", "sentinel-5p-l2-netcdf\n", "sentinel-3-olci-wfr-l2-netcdf\n", "noaa-cdr-ocean-heat-content-netcdf\n", "hls2-l30\n", "sentinel-3-synergy-aod-l2-netcdf\n", "sentinel-3-synergy-v10-l2-netcdf\n", "sentinel-3-olci-lfr-l2-netcdf\n", "sentinel-3-sral-lan-l2-netcdf\n", "sentinel-3-slstr-lst-l2-netcdf\n", "sentinel-3-slstr-wst-l2-netcdf\n", "sentinel-3-sral-wat-l2-netcdf\n", "ms-buildings\n", "hls2-s30\n", "sentinel-3-slstr-frp-l2-netcdf\n", "sentinel-3-synergy-syn-l2-netcdf\n", "sentinel-3-synergy-vgp-l2-netcdf\n", "sentinel-3-synergy-vg1-l2-netcdf\n", "esa-worldcover\n" ]
} ],
"execution_count" : 4
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : [ "## Search for Sentinel-2 and Landsat Imagery\n", "We filter results based on:\n", "- Spatial coverage: our AoI\n", "- Temporal range: a single day\n", "- Data collections: Sentinel-2 Level-2A and Landsat Collection 2 Level-2" ],
"id" : "73726ef5278ab0b9"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:33.218938Z",
"start_time" : "2025-04-03T17:00:32.378960Z"
}
},
"cell_type" : "code",
"source" : [ "# Search for Sentinel-2 and Landsat imagery\n", "search = catalog.search(\n", " intersects=area_of_interest,\n", " datetime=['2025-03-13T00:00:00Z', '2025-03-14T00:00:00Z'],\n", " collections=[\n", " 'sentinel-2-l2a',\n", " 'landsat-c2-l2'\n", " ],\n", ")\n", "\n", "items = search.item_collection()\n", "for item in items:\n", " print(item.id)" ],
"id" : "1685dce360deb243",
"outputs" : [ {
"name" : "stdout",
"output_type" : "stream",
"text" : [ "S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215\n", "S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215\n", "LC08_L2SP_176039_20250313_02_T1\n" ]
} ],
"execution_count" : 5
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:33.236468Z",
"start_time" : "2025-04-03T17:00:33.231262Z"
}
},
"cell_type" : "code",
"source" : [ "# Display cloud cover for each result\n", "for item in items:\n", " print(f'Cloud cover for item {item.id}: {item.properties[\"eo:cloud_cover\"]:.2f}%')" ],
"id" : "3884310f4c941dad",
"outputs" : [ {
"name" : "stdout",
"output_type" : "stream",
"text" : [ "Cloud cover for item S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215: 0.03%\n", "Cloud cover for item S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215: 0.79%\n", "Cloud cover for item LC08_L2SP_176039_20250313_02_T1: 0.24%\n" ]
} ],
"execution_count" : 6
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : "# View metadata and assets for a Sentinel-2 and a Landsat STAC Item",
"id" : "e4190e69e71386fb"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:33.324956Z",
"start_time" : "2025-04-03T17:00:33.282175Z"
}
},
"cell_type" : "code",
"source" : [ "# Sentinel-2 STAC Item\n", "item = items[1]\n", "\n", "table = rich.table.Table(\"Asset Key\", \"Description\")\n", "for asset_key, asset in item.assets.items():\n", " table.add_row(asset_key, asset.title)\n", "\n", "table" ],
"id" : "c20ff0fd39b0fd96",
"outputs" : [ {
"data" : {
"text/plain" : [ "┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001B[1m \u001B[0m\u001B[1mAsset Key \u001B[0m\u001B[1m \u001B[0m┃\u001B[1m \u001B[0m\u001B[1mDescription \u001B[0m\u001B[1m \u001B[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ AOT │ Aerosol optical thickness (AOT) │\n", "│ B01 │ Band 1 - Coastal aerosol - 60m │\n", "│ B02 │ Band 2 - Blue - 10m │\n", "│ B03 │ Band 3 - Green - 10m │\n", "│ B04 │ Band 4 - Red - 10m │\n", "│ B05 │ Band 5 - Vegetation red edge 1 - 20m │\n", "│ B06 │ Band 6 - Vegetation red edge 2 - 20m │\n", "│ B07 │ Band 7 - Vegetation red edge 3 - 20m │\n", "│ B08 │ Band 8 - NIR - 10m │\n", "│ B09 │ Band 9 - Water vapor - 60m │\n", "│ B11 │ Band 11 - SWIR (1.6) - 20m │\n", "│ B12 │ Band 12 - SWIR (2.2) - 20m │\n", "│ B8A │ Band 8A - Vegetation red edge 4 - 20m │\n", "│ SCL │ Scene classfication map (SCL) │\n", "│ WVP │ Water vapour (WVP) │\n", "│ visual │ True color image │\n", "│ safe-manifest │ SAFE manifest │\n", "│ granule-metadata │ Granule metadata │\n", "│ inspire-metadata │ INSPIRE metadata │\n", "│ product-metadata │ Product metadata │\n", "│ datastrip-metadata │ Datastrip metadata │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└────────────────────┴───────────────────────────────────────┘\n" ],
"text/html" : [ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃<span style=\"font-weight: bold\"> Asset Key </span>┃<span style=\"font-weight: bold\"> Description </span>┃\n", "┡━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ AOT │ Aerosol optical thickness (AOT) │\n", "│ B01 │ Band 1 - Coastal aerosol - 60m │\n", "│ B02 │ Band 2 - Blue - 10m │\n", "│ B03 │ Band 3 - Green - 10m │\n", "│ B04 │ Band 4 - Red - 10m │\n", "│ B05 │ Band 5 - Vegetation red edge 1 - 20m │\n", "│ B06 │ Band 6 - Vegetation red edge 2 - 20m │\n", "│ B07 │ Band 7 - Vegetation red edge 3 - 20m │\n", "│ B08 │ Band 8 - NIR - 10m │\n", "│ B09 │ Band 9 - Water vapor - 60m │\n", "│ B11 │ Band 11 - SWIR (1.6) - 20m │\n", "│ B12 │ Band 12 - SWIR (2.2) - 20m │\n", "│ B8A │ Band 8A - Vegetation red edge 4 - 20m │\n", "│ SCL │ Scene classfication map (SCL) │\n", "│ WVP │ Water vapour (WVP) │\n", "│ visual │ True color image │\n", "│ safe-manifest │ SAFE manifest │\n", "│ granule-metadata │ Granule metadata │\n", "│ inspire-metadata │ INSPIRE metadata │\n", "│ product-metadata │ Product metadata │\n", "│ datastrip-metadata │ Datastrip metadata │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└────────────────────┴───────────────────────────────────────┘\n", "</pre>\n" ]
},
"execution_count" : 7,
"metadata" : { },
"output_type" : "execute_result"
} ],
"execution_count" : 7
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:33.347378Z",
"start_time" : "2025-04-03T17:00:33.337583Z"
}
},
"cell_type" : "code",
"source" : [ "# Landsat STAC Item\n", "item = items[2]\n", "\n", "table = rich.table.Table(\"Asset Key\", \"Description\")\n", "for asset_key, asset in item.assets.items():\n", " table.add_row(asset_key, asset.title)\n", "\n", "table" ],
"id" : "a4ba5772576fbe6f",
"outputs" : [ {
"data" : {
"text/plain" : [ "┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001B[1m \u001B[0m\u001B[1mAsset Key \u001B[0m\u001B[1m \u001B[0m┃\u001B[1m \u001B[0m\u001B[1mDescription \u001B[0m\u001B[1m \u001B[0m┃\n", "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ qa │ Surface Temperature Quality Assessment Band │\n", "│ ang │ Angle Coefficients File │\n", "│ red │ Red Band │\n", "│ blue │ Blue Band │\n", "│ drad │ Downwelled Radiance Band │\n", "│ emis │ Emissivity Band │\n", "│ emsd │ Emissivity Standard Deviation Band │\n", "│ trad │ Thermal Radiance Band │\n", "│ urad │ Upwelled Radiance Band │\n", "│ atran │ Atmospheric Transmittance Band │\n", "│ cdist │ Cloud Distance Band │\n", "│ green │ Green Band │\n", "│ nir08 │ Near Infrared Band 0.8 │\n", "│ lwir11 │ Surface Temperature Band │\n", "│ swir16 │ Short-wave Infrared Band 1.6 │\n", "│ swir22 │ Short-wave Infrared Band 2.2 │\n", "│ coastal │ Coastal/Aerosol Band │\n", "│ mtl.txt │ Product Metadata File (txt) │\n", "│ mtl.xml │ Product Metadata File (xml) │\n", "│ mtl.json │ Product Metadata File (json) │\n", "│ qa_pixel │ Pixel Quality Assessment Band │\n", "│ qa_radsat │ Radiometric Saturation and Terrain Occlusion Quality Assessment Band │\n", "│ qa_aerosol │ Aerosol Quality Assessment Band │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└──────────────────┴──────────────────────────────────────────────────────────────────────┘\n" ],
"text/html" : [ "<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n", "┃<span style=\"font-weight: bold\"> Asset Key </span>┃<span style=\"font-weight: bold\"> Description </span>┃\n", "┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n", "│ qa │ Surface Temperature Quality Assessment Band │\n", "│ ang │ Angle Coefficients File │\n", "│ red │ Red Band │\n", "│ blue │ Blue Band │\n", "│ drad │ Downwelled Radiance Band │\n", "│ emis │ Emissivity Band │\n", "│ emsd │ Emissivity Standard Deviation Band │\n", "│ trad │ Thermal Radiance Band │\n", "│ urad │ Upwelled Radiance Band │\n", "│ atran │ Atmospheric Transmittance Band │\n", "│ cdist │ Cloud Distance Band │\n", "│ green │ Green Band │\n", "│ nir08 │ Near Infrared Band 0.8 │\n", "│ lwir11 │ Surface Temperature Band │\n", "│ swir16 │ Short-wave Infrared Band 1.6 │\n", "│ swir22 │ Short-wave Infrared Band 2.2 │\n", "│ coastal │ Coastal/Aerosol Band │\n", "│ mtl.txt │ Product Metadata File (txt) │\n", "│ mtl.xml │ Product Metadata File (xml) │\n", "│ mtl.json │ Product Metadata File (json) │\n", "│ qa_pixel │ Pixel Quality Assessment Band │\n", "│ qa_radsat │ Radiometric Saturation and Terrain Occlusion Quality Assessment Band │\n", "│ qa_aerosol │ Aerosol Quality Assessment Band │\n", "│ tilejson │ TileJSON with default rendering │\n", "│ rendered_preview │ Rendered preview │\n", "└──────────────────┴──────────────────────────────────────────────────────────────────────┘\n", "</pre>\n" ]
},
"execution_count" : 8,
"metadata" : { },
"output_type" : "execute_result"
} ],
"execution_count" : 8
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : [ "## Organise asset (band) URIs\n", "\n", "We collect the URIs of the NIR, Red, and Green bands for each STAC Item. These will be used to build false-colour composites." ],
"id" : "3dbafb813534e93b"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:33.403966Z",
"start_time" : "2025-04-03T17:00:33.394385Z"
}
},
"cell_type" : "code",
"source" : [ "# Organise COG assets\n", "cog_info = {} # holds the relevant COG URIs and the platform name\n", "\n", "for item in items:\n", " id = item.id\n", "\n", " # quick hack - Planetary Computer's asset names for S2 and LS don't match\n", " if 'Sentinel' in item.properties['platform']:\n", " _uri_nir = item.assets['B08'].href\n", " _uri_red = item.assets['B04'].href\n", " _uri_green = item.assets['B03'].href\n", " else:\n", " _uri_nir = item.assets['nir08'].href\n", " _uri_red = item.assets['red'].href\n", " _uri_green = item.assets['green'].href\n", "\n", " _platform = item.properties['platform']\n", "\n", " cog_info[id] = {\n", " 'uri_nir08': _uri_nir,\n", " 'uri_red': _uri_red,\n", " 'uri_green': _uri_green,\n", " 'platform': _platform\n", " }" ],
"id" : "115402f66815350e",
"outputs" : [ ],
"execution_count" : 9
}, {
"metadata" : { },
"cell_type" : "markdown",
"source" : [ "# Programmatically build a QGIS project with streamed COGs\n", "We launch QGIS in headless mode and use GDAL to build a [.vrt (virtual GDAL dataset)](https://gdal.org/en/stable/drivers/raster/vrt.html) that combines the NIR, Red, and Green bands - streamed from their URIs using /vsicurl/ or /vsis3/. Each layer is added to a group based on its platform (e.g. Sentinel-2C, Landsat-8).\n", "\n", "#### About /vsicurl/ and /vsis3/\n", "GDAL supports a set of [Virtual File Systems (VSI)](https://gdal.org/en/stable/user/virtual_file_systems.html) that allow it to read files hosted remotely \"without prior download of the entire file\":\n", "- /vsicurl/ is used to stream files over HTTP or HTTPS.\n", "- /vsis3/ is used to stream files directly from Amazon S3 buckets.\n", "\n", "**NOTE 1:** To re-run the following block you'll have to restart the kernel. It might be an issue related to the QgsApplication initialisation. <br>\n", "**NOTE 2:** It's very important to set the CRS on the project level (```project.setCrs```)\n" ],
"id" : "4adf2107d53e85c8"
}, {
"metadata" : {
"ExecuteTime" : {
"end_time" : "2025-04-03T17:00:40.838523Z",
"start_time" : "2025-04-03T17:00:33.452934Z"
}
},
"cell_type" : "code",
"source" : [ "from qgis.core import QgsApplication, QgsRasterLayer, QgsCoordinateReferenceSystem, QgsProject\n", "from osgeo import gdal\n", "gdal.UseExceptions()\n", "\n", "# Optional GDAL speed tweaks\n", "gdal.SetConfigOption(\"GDAL_DISABLE_READDIR_ON_OPEN\", \"YES\")\n", "gdal.SetConfigOption(\"CPL_VSIL_CURL_ALLOWED_EXTENSIONS\", \".tif\")\n", "\n", "# Initialise QGIS Application\n", "qgs = QgsApplication([], False)\n", "qgs.initQgis()\n", "\n", "# Create a new QGIS project\n", "project = QgsProject.instance()\n", "root = project.layerTreeRoot()\n", "project.setCrs(QgsCoordinateReferenceSystem(\"EPSG:4326\"))\n", "\n", "for id, item_info in cog_info.items():\n", "\n", " # Wrap COG URLs with /vsicurl/ or /vsis3/\n", " try:\n", " if 's3://' in item_info['uri_nir08']:\n", " nir_path = f\"/vsis3/{item_info['uri_nir08'].replace('s3://', '')}\"\n", " red_path = f\"/vsis3/{item_info['uri_red'].replace('s3://', '')}\"\n", " green_path = f\"/vsis3/{item_info['uri_green'].replace('s3://', '')}\"\n", " else:\n", " nir_path = f\"/vsicurl/{item_info['uri_nir08']}\"\n", " red_path = f\"/vsicurl/{item_info['uri_red']}\"\n", " green_path = f\"/vsicurl/{item_info['uri_green']}\"\n", "\n", " # Define VRT path\n", " vrt_path = f\"{id}_false_colour.vrt\"\n", "\n", " # Build virtual raster (false colour composite)\n", " gdal.BuildVRT(\n", " vrt_path,\n", " [nir_path, red_path, green_path],\n", " separate=True,\n", " outputSRS=\"EPSG:32636\"\n", " )\n", "\n", " # Create QGIS raster layer\n", " layer_name = f\"{id}_false_colour\"\n", " layer = QgsRasterLayer(vrt_path, layer_name)\n", "\n", " if layer.isValid():\n", " project.addMapLayer(layer, addToLegend=False)\n", "\n", " # Group layer based on its platform (Sentinel-2C, Landsat-8)\n", " platform = item_info['platform']\n", " group = root.findGroup(platform) or root.addGroup(platform)\n", " group.insertLayer(0, layer)\n", "\n", " print(f\"Added {layer_name} to group '{group.name()}'\")\n", " else:\n", " print(f\"Failed to load {layer_name}\")\n", " except Exception as e:\n", " print(e)\n", "\n", "# Save the QGIS project\n", "project_path = \"streaming_COGs.qgz\"\n", "project.write(project_path)\n", "print(f\"Project saved to: {project_path}\")\n", "\n", "# Exit QGIS\n", "qgs.exitQgis()" ],
"id" : "a70538b9a98d82e9",
"outputs" : [ {
"name" : "stdout",
"output_type" : "stream",
"text" : [ "Added S2C_MSIL2A_20250313T082751_R021_T36RVU_20250313T142215_false_colour to group 'Sentinel-2C'\n", "Added S2C_MSIL2A_20250313T082751_R021_T36RUU_20250313T142215_false_colour to group 'Sentinel-2C'\n", "Added LC08_L2SP_176039_20250313_02_T1_false_colour to group 'landsat-8'\n", "Project saved to: streaming_COGs.qgz\n" ]
} ],
"execution_count" : 10
} ],
"metadata" : {
"kernelspec" : {
"display_name" : "Python 3",
"language" : "python",
"name" : "python3"
},
"language_info" : {
"codemirror_mode" : {
"name" : "ipython",
"version" : 2
},
"file_extension" : ".py",
"mimetype" : "text/x-python",
"name" : "python",
"nbconvert_exporter" : "python",
"pygments_lexer" : "ipython2",
"version" : "2.7.6"
}
},
"nbformat" : 4,
"nbformat_minor" : 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment