{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Exercice 2 : Importation d'images satellitaires\n", "\n", "Ce notebook illustre l'utilisation de données satellite réelles Sentinel-2 depuis le **Microsoft Planetary Computer**.\n", " \n", "**Source** : Microsoft Planetary Computer (STAC API) \n", "**Librairies** : rasterio, pandas, matplotlib, seaborn, numpy, planetary-computer, pystac-client\n", "\n", "**Explorer le catalogue** : https://planetarycomputer.microsoft.com/explore" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "import rasterio\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import numpy as np\n", "import planetary_computer\n", "from pystac_client import Client" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Connexion à Planetary Computer\n", "\n", "Accès au catalogue STAC pour télécharger une image Sentinel-2." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Connexion au Planetary Computer réussie\n", "Item Sentinel-2 trouvé: S2B_MSIL2A_20250824T185919_R013_T11UMT_20250824T224700\n", "4 bandes spectrales récupérées\n" ] } ], "source": [ "# Connexion au Planetary Computer\n", "catalog = Client.open(\n", " \"https://planetarycomputer.microsoft.com/api/stac/v1\",\n", " modifier=planetary_computer.sign_inplace\n", ")\n", "print(\"Connexion au Planetary Computer réussie\")\n", "\n", "# Récupérer un item Sentinel-2 spécifique\n", "item = catalog.get_collection(\"sentinel-2-l2a\").get_item(\n", " \"S2B_MSIL2A_20250824T185919_R013_T11UMT_20250824T224700\"\n", ")\n", "print(f\"Item Sentinel-2 trouvé: {item.id}\")\n", "\n", "# Extraire les URLs des bandes spectrales\n", "# B02=Bleu, B03=Vert, B04=Rouge, B08=NIR (Near Infrared)\n", "bands = {k: item.assets[k].href for k in ['B02', 'B03', 'B04', 'B08']}\n", "print(f\"4 bandes spectrales récupérées\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Chargement des bandes\n", "\n", "Lecture des bandes spectrales (bleu, vert, rouge, NIR) avec masquage des valeurs invalides." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Chargement: peut prendre 30-60 secondes)\n", "Bande bleue chargée (B02)\n", "Bande verte chargée (B03)\n", "Bande rouge chargée (B04)\n", "Bande NIR chargée (B08)\n", "\n", "Toutes les bandes chargées et masquées\n", " Dimensions: 10980 × 10980 pixels\n", " Pixels valides: 118,331,336 / 120,560,400 (98.2%)\n" ] } ], "source": [ "\n", "print(\"Chargement: peut prendre 30-60 secondes)\")\n", "# Charger la bande bleue (B02) et extraire les métadonnées\n", "with rasterio.open(bands['B02']) as src:\n", " blue = src.read(1) # Lire la première (et unique) bande\n", " transform = src.transform # Transformation affine (géoréférencement)\n", " crs = src.crs # Système de coordonnées (ex: EPSG:32618 pour UTM Zone 18N)\n", " nodata = src.nodata # Valeur représentant pixels invalides (nuages, ombres)\n", "print(f\"Bande bleue chargée (B02)\")\n", "\n", "# Charger les autres bandes (réutilise les métadonnées de B02)\n", "with rasterio.open(bands['B03']) as src:\n", " green = src.read(1)\n", "print(f\"Bande verte chargée (B03)\")\n", "with rasterio.open(bands['B04']) as src:\n", " red = src.read(1)\n", "print(f\"Bande rouge chargée (B04)\")\n", "with rasterio.open(bands['B08']) as src:\n", " nir = src.read(1)\n", "print(f\"Bande NIR chargée (B08)\")\n", "\n", "# Masquer les valeurs NoData \n", "mask = (blue != nodata) & (green != nodata) & (red != nodata) & (nir != nodata)\n", "\n", "# Remplacer pixels invalides par NaN pour calculer les stats\n", "blue_masked = np.where(mask, blue, np.nan)\n", "green_masked = np.where(mask, green, np.nan)\n", "red_masked = np.where(mask, red, np.nan)\n", "nir_masked = np.where(mask, nir, np.nan)\n", "\n", "print(f\"\\nToutes les bandes chargées et masquées\")\n", "print(f\" Dimensions: {red.shape[0]} × {red.shape[1]} pixels\")\n", "print(f\" Pixels valides: {np.sum(mask):,} / {mask.size:,} ({100*np.sum(mask)/mask.size:.1f}%)\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Statistiques avec la librarie Pandas" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
| \n", " | Bande | \n", "Min | \n", "Max | \n", "Moyenne | \n", "
|---|---|---|---|---|
| 0 | \n", "Bleu | \n", "1.0 | \n", "18144.0 | \n", "1960.8 | \n", "
| 1 | \n", "Vert | \n", "1.0 | \n", "17472.0 | \n", "2121.8 | \n", "
| 2 | \n", "Rouge | \n", "203.0 | \n", "17008.0 | \n", "2080.1 | \n", "
| 3 | \n", "NIR | \n", "1.0 | \n", "16544.0 | \n", "3114.8 | \n", "