Zkone commited on
Commit
6fa599f
·
verified ·
1 Parent(s): 2678877

Update gee_utils.py

Browse files
Files changed (1) hide show
  1. gee_utils.py +85 -48
gee_utils.py CHANGED
@@ -3,78 +3,115 @@ import os
3
  import json
4
  import base64
5
 
 
 
 
 
 
 
 
 
6
  def init_gee():
7
- key_b64 = os.environ.get("GEE_PRIVATE_KEY")
8
- sa_email = os.environ.get("GEE_SERVICE_ACCOUNT")
9
-
10
- key_json = json.loads(base64.b64decode(key_b64).decode())
11
- credentials = ee.ServiceAccountCredentials(sa_email, key_data=json.dumps(key_json))
12
- ee.Initialize(credentials)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  def get_sentinel2_image(lat, lng, buffer_m, year):
15
- """Récupère la meilleure image Sentinel-2 pour une année donnée."""
16
- point = ee.Geometry.Point([lng, lat])
17
  region = point.buffer(buffer_m)
18
-
19
- start = f"{year}-01-01"
20
- end = f"{year}-12-31"
21
-
22
- collection = (
23
  ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
24
  .filterBounds(region)
25
- .filterDate(start, end)
26
  .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 20))
27
  .sort("CLOUDY_PIXEL_PERCENTAGE")
 
 
28
  )
29
-
30
- image = collection.first().clip(region)
31
  return image, region
32
 
 
33
  def compute_ndvi(image):
34
- ndvi = image.normalizedDifference(["B8", "B4"]).rename("NDVI")
35
- return ndvi
36
 
37
  def compute_ndwi(image):
38
- ndwi = image.normalizedDifference(["B3", "B8"]).rename("NDWI")
39
- return ndwi
40
 
41
  def get_ndvi_stats(ndvi, region):
42
  stats = ndvi.reduceRegion(
43
- reducer=ee.Reducer.mean().combine(
44
- ee.Reducer.min(), sharedInputs=True
45
- ).combine(
46
- ee.Reducer.max(), sharedInputs=True
47
- ),
48
  geometry=region,
49
  scale=10,
50
  maxPixels=1e9
51
  ).getInfo()
52
- return stats
 
 
 
 
 
53
 
54
  def classify_land(image, region):
55
- """Classification simple : végétation, urbain, eau, sol nu, agriculture."""
56
- ndvi = compute_ndvi(image)
57
- ndwi = compute_ndwi(image)
58
-
59
- vegetation = ndvi.gt(0.4)
60
- agriculture = ndvi.gt(0.2).And(ndvi.lte(0.4))
61
- water = ndwi.gt(0.0)
62
- urban = image.select("B11").gt(2000).And(ndvi.lt(0.2))
63
- bare = ndvi.lt(0.2).And(water.Not()).And(urban.Not())
64
-
65
- classified = (
66
- water.multiply(1)
67
- .add(vegetation.multiply(2))
68
- .add(agriculture.multiply(3))
69
- .add(urban.multiply(4))
70
- .add(bare.multiply(5))
71
- )
72
-
73
  total = region.area().getInfo()
 
 
 
 
 
 
 
 
 
74
  areas = {}
75
- for name, mask in [("eau",1),("végétation",2),("agriculture",3),("urbain",4),("sol nu",5)]:
76
- px = classified.eq(mask).multiply(ee.Image.pixelArea())
77
  area = px.reduceRegion(ee.Reducer.sum(), region, 10, maxPixels=1e9).getInfo()
78
- areas[name] = round((area.get("constant", 0) / total) * 100, 1)
79
-
 
80
  return areas
 
3
  import json
4
  import base64
5
 
6
+ # ── Chemins et constantes ────────────────────────────────────────────────────
7
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
8
+
9
+ GEE_PROJECT = os.environ.get("GEE_PROJECT", "ee-zanakone")
10
+ GEE_SA_EMAIL = os.environ.get("GEE_SA_EMAIL", "pipeline-agb@ee-zanakone.iam.gserviceaccount.com")
11
+ GEE_KEY_PATH = os.path.join(BASE_DIR, "cle_service_gee.json")
12
+
13
+ # ── Initialisation ───────────────────────────────────────────────────────────
14
  def init_gee():
15
+ """
16
+ Priorité :
17
+ 1. Fichier JSON local (développement local)
18
+ 2. Secret HF base64 (production Hugging Face)
19
+ """
20
+
21
+ # --- 1. Fichier local présent ? ---
22
+ if os.path.exists(GEE_KEY_PATH):
23
+ print(f"🔑 Clé GEE chargée depuis fichier local : {GEE_KEY_PATH}")
24
+ credentials = ee.ServiceAccountCredentials(
25
+ email=GEE_SA_EMAIL,
26
+ key_file=GEE_KEY_PATH
27
+ )
28
+
29
+ # --- 2. Secret HF (base64) ---
30
+ else:
31
+ key_b64 = os.environ.get("GEE_KEY_JSON")
32
+ if not key_b64:
33
+ raise EnvironmentError(
34
+ "❌ Aucune clé GEE trouvée.\n"
35
+ f" - En local : placez 'cle_service_gee.json' dans {BASE_DIR}\n"
36
+ " - Sur HF : ajoutez le secret GEE_KEY_JSON (base64)"
37
+ )
38
+ print("🔑 Clé GEE chargée depuis secret Hugging Face")
39
+ try:
40
+ key_dict = json.loads(base64.b64decode(key_b64.strip()).decode("utf-8"))
41
+ except Exception as e:
42
+ raise ValueError(f"❌ Décodage base64 échoué : {e}")
43
 
44
+ credentials = ee.ServiceAccountCredentials(
45
+ email=GEE_SA_EMAIL,
46
+ key_data=json.dumps(key_dict)
47
+ )
48
+
49
+ # --- Initialisation avec projet ---
50
+ try:
51
+ ee.Initialize(credentials, project=GEE_PROJECT)
52
+ print(f"✅ GEE initialisé — projet : {GEE_PROJECT}")
53
+ except Exception as e:
54
+ raise ConnectionError(f"❌ ee.Initialize() échoué : {e}")
55
+
56
+
57
+ # ── Fonctions d'analyse ──────────────────────────────────────────────────────
58
  def get_sentinel2_image(lat, lng, buffer_m, year):
59
+ point = ee.Geometry.Point([lng, lat])
 
60
  region = point.buffer(buffer_m)
61
+
62
+ image = (
 
 
 
63
  ee.ImageCollection("COPERNICUS/S2_SR_HARMONIZED")
64
  .filterBounds(region)
65
+ .filterDate(f"{year}-01-01", f"{year}-12-31")
66
  .filter(ee.Filter.lt("CLOUDY_PIXEL_PERCENTAGE", 20))
67
  .sort("CLOUDY_PIXEL_PERCENTAGE")
68
+ .first()
69
+ .clip(region)
70
  )
 
 
71
  return image, region
72
 
73
+
74
  def compute_ndvi(image):
75
+ return image.normalizedDifference(["B8", "B4"]).rename("NDVI")
76
+
77
 
78
  def compute_ndwi(image):
79
+ return image.normalizedDifference(["B3", "B8"]).rename("NDWI")
80
+
81
 
82
  def get_ndvi_stats(ndvi, region):
83
  stats = ndvi.reduceRegion(
84
+ reducer=ee.Reducer.mean().combine(ee.Reducer.min(), sharedInputs=True)
85
+ .combine(ee.Reducer.max(), sharedInputs=True),
 
 
 
86
  geometry=region,
87
  scale=10,
88
  maxPixels=1e9
89
  ).getInfo()
90
+ return {
91
+ "mean": round(stats.get("NDVI_mean") or 0, 3),
92
+ "min": round(stats.get("NDVI_min") or 0, 3),
93
+ "max": round(stats.get("NDVI_max") or 0, 3),
94
+ }
95
+
96
 
97
  def classify_land(image, region):
98
+ ndvi = compute_ndvi(image)
99
+ ndwi = compute_ndwi(image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
100
  total = region.area().getInfo()
101
+
102
+ masks = {
103
+ "eau": ndwi.gt(0.0),
104
+ "végétation": ndvi.gt(0.4),
105
+ "agriculture": ndvi.gt(0.2).And(ndvi.lte(0.4)),
106
+ "urbain": image.select("B11").gt(2000).And(ndvi.lt(0.2)),
107
+ "sol nu": ndvi.lt(0.2).And(ndwi.lte(0.0)).And(image.select("B11").lte(2000)),
108
+ }
109
+
110
  areas = {}
111
+ for name, mask in masks.items():
112
+ px = mask.multiply(ee.Image.pixelArea())
113
  area = px.reduceRegion(ee.Reducer.sum(), region, 10, maxPixels=1e9).getInfo()
114
+ val = area.get("NDVI") or area.get("B11") or area.get("nd") or 0
115
+ areas[name] = round((val / total) * 100, 1)
116
+
117
  return areas