AnikethSinha commited on
Commit
3fb0d07
·
verified ·
1 Parent(s): ff941ed

Deploy Geo-Intelligent AI System

Browse files
Files changed (4) hide show
  1. .gitattributes +1 -0
  2. app.py +1011 -0
  3. geoai_clean_final_best.keras +3 -0
  4. requirements.txt +5 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ geoai_clean_final_best.keras filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,1011 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import os
3
+ import json
4
+ import numpy as np
5
+ import cv2
6
+ import gradio as gr
7
+ import tensorflow as tf
8
+
9
+ from tensorflow.keras.models import load_model
10
+ from groq import Groq
11
+
12
+
13
+ # =========================================================
14
+ # CONFIGURATION
15
+ # =========================================================
16
+
17
+ MODEL_PATH = "geoai_clean_final_best.keras"
18
+
19
+ classes = ['AnnualCrop', 'Forest', 'HerbaceousVegetation', 'Highway', 'Industrial', 'Pasture', 'PermanentCrop', 'Residential', 'River', 'SeaLake']
20
+
21
+ geo_knowledge_base = {'AnnualCrop': {'land_cover_type': 'Agricultural Land', 'description': 'Land primarily used for seasonal crop cultivation and annual agricultural production.', 'characteristics': ['Regular field patterns', 'Seasonal vegetation cycles', 'Managed agricultural activity'], 'environmental_significance': 'Important for food production, crop monitoring, and agricultural land-use assessment.', 'applications': ['Crop monitoring', 'Agricultural planning', 'Land-use assessment']}, 'Forest': {'land_cover_type': 'Natural Vegetation', 'description': 'Land dominated by dense tree cover and forest vegetation.', 'characteristics': ['Dense vegetation', 'Continuous tree canopy', 'High biomass coverage'], 'environmental_significance': 'Important for biodiversity, carbon storage, and ecosystem monitoring.', 'applications': ['Deforestation monitoring', 'Biodiversity assessment', 'Environmental conservation']}, 'HerbaceousVegetation': {'land_cover_type': 'Natural Vegetation', 'description': 'Land dominated by grasses, herbs, and other non-woody vegetation.', 'characteristics': ['Low-growing vegetation', 'Grass-dominated surface', 'Limited tree cover'], 'environmental_significance': 'Important for habitat assessment, vegetation monitoring, and ecosystem analysis.', 'applications': ['Vegetation monitoring', 'Habitat assessment', 'Land-cover mapping']}, 'Highway': {'land_cover_type': 'Transportation Infrastructure', 'description': 'Land containing major road networks and associated transportation infrastructure.', 'characteristics': ['Linear road structures', 'Connected transport routes', 'Artificial surfaces'], 'environmental_significance': 'Useful for infrastructure mapping, urban expansion studies, and accessibility analysis.', 'applications': ['Road network monitoring', 'Infrastructure planning', 'Urban development analysis']}, 'Industrial': {'land_cover_type': 'Built-up Land', 'description': 'Land dominated by factories, warehouses, and large industrial infrastructure.', 'characteristics': ['Large artificial structures', 'Dense built surfaces', 'Industrial facilities'], 'environmental_significance': 'Relevant for urban growth, industrial development, and environmental impact monitoring.', 'applications': ['Industrial mapping', 'Urban planning', 'Environmental impact assessment']}, 'Pasture': {'land_cover_type': 'Agricultural Grassland', 'description': 'Grass-covered land primarily used for livestock grazing.', 'characteristics': ['Open grassland', 'Low vegetation', 'Agricultural land management'], 'environmental_significance': 'Important for livestock management and agricultural land-use monitoring.', 'applications': ['Grazing land assessment', 'Agricultural planning', 'Grassland monitoring']}, 'PermanentCrop': {'land_cover_type': 'Permanent Agricultural Land', 'description': 'Land used for long-term crops such as orchards, vineyards, and plantations.', 'characteristics': ['Persistent crop patterns', 'Organized planting structures', 'Long-term cultivation'], 'environmental_significance': 'Useful for monitoring permanent agricultural systems and long-term land management.', 'applications': ['Plantation monitoring', 'Agricultural mapping', 'Land management']}, 'Residential': {'land_cover_type': 'Built-up Land', 'description': 'Land dominated by houses, buildings, and residential infrastructure.', 'characteristics': ['Dense building patterns', 'Road networks', 'Urban housing structures'], 'environmental_significance': 'Important for urban growth analysis, population-related planning, and land-use monitoring.', 'applications': ['Urban planning', 'Settlement mapping', 'Urban expansion monitoring']}, 'River': {'land_cover_type': 'Water Body', 'description': 'A natural flowing water system visible within the landscape.', 'characteristics': ['Linear water features', 'Continuous flow patterns', 'Natural drainage structures'], 'environmental_significance': 'Important for water-resource management, flood assessment, and ecosystem monitoring.', 'applications': ['Flood monitoring', 'Water-resource assessment', 'Environmental monitoring']}, 'SeaLake': {'land_cover_type': 'Water Body', 'description': 'A large natural or artificial body of standing water.', 'characteristics': ['Large continuous water surface', 'Low surface texture', 'Distinct land-water boundaries'], 'environmental_significance': 'Important for water-resource monitoring and aquatic ecosystem assessment.', 'applications': ['Water-body monitoring', 'Environmental assessment', 'Coastal and lake analysis']}}
22
+
23
+
24
+ # =========================================================
25
+ # LOAD FINAL TRAINED MODEL
26
+ # =========================================================
27
+
28
+ print("Loading GeoAI model...")
29
+
30
+ xai_model = load_model(
31
+ MODEL_PATH,
32
+ compile=False
33
+ )
34
+
35
+ print("GeoAI model loaded successfully.")
36
+
37
+
38
+ # =========================================================
39
+ # GROQ LLM CONNECTION
40
+ # =========================================================
41
+
42
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY")
43
+
44
+ if not GROQ_API_KEY:
45
+ print(
46
+ "WARNING: GROQ_API_KEY is not configured. "
47
+ "Image classification will work, but LLM features "
48
+ "require the Hugging Face Space secret."
49
+ )
50
+
51
+ groq_client = Groq(
52
+ api_key=GROQ_API_KEY or "missing-key"
53
+ )
54
+
55
+
56
+ # =========================================================
57
+ # EXACT WORKING GEOAI FUNCTIONS
58
+ # =========================================================
59
+
60
+ def make_gradcam_heatmap(img_array, model, backbone_name="mobilenetv2_1.00_96"):
61
+
62
+ # Get nested MobileNetV2 backbone
63
+ backbone = model.get_layer(backbone_name)
64
+
65
+ # Model that returns final backbone feature maps
66
+ backbone_feature_model = tf.keras.Model(
67
+ inputs=backbone.input,
68
+ outputs=backbone.get_layer("out_relu").output
69
+ )
70
+
71
+ # Get preprocessing layers from full model
72
+ augmentation_layer = model.get_layer("data_augmentation")
73
+ resizing_layer = model.get_layer("resizing_1")
74
+ rescaling_layer = model.get_layer("rescaling_2")
75
+
76
+ # Get classification head layers
77
+ gap_layer = model.get_layer("global_average_pooling2d_2")
78
+ dropout_1 = model.get_layer("dropout_8")
79
+ dense_layer = model.get_layer("dense_12")
80
+ dropout_2 = model.get_layer("dropout_9")
81
+ output_layer = model.get_layer("dense_13")
82
+
83
+ with tf.GradientTape() as tape:
84
+
85
+ # Same preprocessing path as the final model
86
+ x = augmentation_layer(img_array, training=False)
87
+ x = resizing_layer(x)
88
+ x = rescaling_layer(x)
89
+
90
+ # Extract final convolutional feature maps
91
+ conv_outputs = backbone_feature_model(x, training=False)
92
+
93
+ tape.watch(conv_outputs)
94
+
95
+ # Same classification path as the final model
96
+ predictions = gap_layer(conv_outputs)
97
+ predictions = dropout_1(predictions, training=False)
98
+ predictions = dense_layer(predictions)
99
+ predictions = dropout_2(predictions, training=False)
100
+ predictions = output_layer(predictions)
101
+
102
+ predicted_index = tf.argmax(predictions[0])
103
+ class_score = predictions[:, predicted_index]
104
+
105
+ # Gradient of predicted class with respect to feature maps
106
+ gradients = tape.gradient(class_score, conv_outputs)
107
+
108
+ # Average gradients across spatial dimensions
109
+ pooled_gradients = tf.reduce_mean(
110
+ gradients,
111
+ axis=(0, 1, 2)
112
+ )
113
+
114
+ # Weight feature maps by importance
115
+ conv_outputs = conv_outputs[0]
116
+
117
+ heatmap = tf.reduce_sum(
118
+ conv_outputs * pooled_gradients,
119
+ axis=-1
120
+ )
121
+
122
+ # Keep positive influence only
123
+ heatmap = tf.maximum(heatmap, 0)
124
+
125
+ # Normalize to 0–1
126
+ max_value = tf.reduce_max(heatmap)
127
+
128
+ heatmap = tf.where(
129
+ max_value > 0,
130
+ heatmap / max_value,
131
+ heatmap
132
+ )
133
+
134
+ return heatmap.numpy()
135
+
136
+
137
+
138
+ def analyze_satellite_image(image_array, model=xai_model):
139
+
140
+ # Ensure image is float32 and normalized to 0–1
141
+ image_array = np.asarray(image_array, dtype=np.float32)
142
+
143
+ if image_array.max() > 1.0:
144
+ image_array = image_array / 255.0
145
+
146
+ # Ensure correct size
147
+ if image_array.shape[:2] != (64, 64):
148
+ image_array = cv2.resize(
149
+ image_array,
150
+ (64, 64)
151
+ )
152
+
153
+ # Ensure RGB
154
+ if image_array.ndim == 2:
155
+ image_array = cv2.cvtColor(
156
+ image_array,
157
+ cv2.COLOR_GRAY2RGB
158
+ )
159
+
160
+ if image_array.shape[-1] == 4:
161
+ image_array = image_array[:, :, :3]
162
+
163
+ # Add batch dimension
164
+ img_batch = np.expand_dims(
165
+ image_array,
166
+ axis=0
167
+ )
168
+
169
+ # Prediction
170
+ probabilities = model.predict(
171
+ img_batch,
172
+ verbose=0
173
+ )[0]
174
+
175
+ predicted_index = int(
176
+ np.argmax(probabilities)
177
+ )
178
+
179
+ predicted_class = classes[
180
+ predicted_index
181
+ ]
182
+
183
+ confidence = float(
184
+ probabilities[predicted_index] * 100
185
+ )
186
+
187
+ # Top 3 predictions
188
+ top3_indices = np.argsort(
189
+ probabilities
190
+ )[-3:][::-1]
191
+
192
+ top3_predictions = [
193
+ {
194
+ "class": classes[i],
195
+ "confidence": float(
196
+ probabilities[i] * 100
197
+ )
198
+ }
199
+ for i in top3_indices
200
+ ]
201
+
202
+ # Generate Grad-CAM
203
+ heatmap = make_gradcam_heatmap(
204
+ img_batch,
205
+ model
206
+ )
207
+
208
+ heatmap_resized = cv2.resize(
209
+ heatmap,
210
+ (64, 64)
211
+ )
212
+
213
+ # Colored heatmap
214
+ heatmap_uint8 = np.uint8(
215
+ 255 * heatmap_resized
216
+ )
217
+
218
+ heatmap_colored = cv2.applyColorMap(
219
+ heatmap_uint8,
220
+ cv2.COLORMAP_JET
221
+ )
222
+
223
+ heatmap_colored = cv2.cvtColor(
224
+ heatmap_colored,
225
+ cv2.COLOR_BGR2RGB
226
+ )
227
+
228
+ # Original image
229
+ original_uint8 = np.uint8(
230
+ np.clip(
231
+ image_array * 255,
232
+ 0,
233
+ 255
234
+ )
235
+ )
236
+
237
+ # Grad-CAM overlay
238
+ overlay = cv2.addWeighted(
239
+ original_uint8,
240
+ 0.6,
241
+ heatmap_colored,
242
+ 0.4,
243
+ 0
244
+ )
245
+
246
+ # Confidence interpretation
247
+ if confidence >= 90:
248
+ confidence_level = "High"
249
+ elif confidence >= 70:
250
+ confidence_level = "Moderate"
251
+ else:
252
+ confidence_level = "Low"
253
+
254
+ # Structured GeoAI output
255
+ result = {
256
+ "predicted_class": predicted_class,
257
+ "confidence": confidence,
258
+ "confidence_level": confidence_level,
259
+ "top3_predictions": top3_predictions
260
+ }
261
+
262
+ return result, heatmap_resized, overlay
263
+
264
+
265
+
266
+ def create_structured_geo_analysis(model_result):
267
+
268
+ predicted_class = model_result["predicted_class"]
269
+ knowledge = geo_knowledge_base[predicted_class]
270
+
271
+ # Reliability statement
272
+ confidence = model_result["confidence"]
273
+
274
+ if confidence >= 90:
275
+ reliability = (
276
+ "The model shows strong confidence in this prediction."
277
+ )
278
+ elif confidence >= 70:
279
+ reliability = (
280
+ "The prediction is reasonably confident, but alternative "
281
+ "land-cover classes should also be considered."
282
+ )
283
+ else:
284
+ reliability = (
285
+ "The prediction has low confidence and should be interpreted "
286
+ "carefully alongside the alternative predictions."
287
+ )
288
+
289
+ structured_analysis = {
290
+ "prediction": {
291
+ "land_cover_class": predicted_class,
292
+ "confidence_percentage": round(confidence, 2),
293
+ "confidence_level": model_result["confidence_level"]
294
+ },
295
+
296
+ "geospatial_information": {
297
+ "land_cover_type": knowledge["land_cover_type"],
298
+ "description": knowledge["description"],
299
+ "characteristics": knowledge["characteristics"],
300
+ "environmental_significance":
301
+ knowledge["environmental_significance"],
302
+ "potential_applications": knowledge["applications"]
303
+ },
304
+
305
+ "alternative_predictions": [
306
+ {
307
+ "class": item["class"],
308
+ "confidence_percentage":
309
+ round(item["confidence"], 2)
310
+ }
311
+ for item in model_result["top3_predictions"][1:]
312
+ ],
313
+
314
+ "explainability": {
315
+ "method": "Grad-CAM",
316
+ "interpretation": (
317
+ "The highlighted regions in the Grad-CAM visualization "
318
+ "represent areas that most influenced the model's prediction."
319
+ ),
320
+ "spatial_resolution_note": (
321
+ "The explanation identifies broad influential regions "
322
+ "rather than exact object boundaries."
323
+ )
324
+ },
325
+
326
+ "reliability_assessment": reliability
327
+ }
328
+
329
+ return structured_analysis
330
+
331
+
332
+
333
+ def ask_geoai_llm(question, structured_output):
334
+
335
+ # Convert verified GeoAI output into JSON context
336
+ geo_context = json.dumps(
337
+ structured_output,
338
+ indent=2
339
+ )
340
+
341
+ system_prompt = """
342
+ You are the natural-language reasoning component of a GeoAI system.
343
+
344
+ You receive verified structured information produced by:
345
+ 1. A satellite-image classification model
346
+ 2. A confidence analysis module
347
+ 3. A Grad-CAM explainability module
348
+ 4. A geospatial knowledge base
349
+
350
+ Rules:
351
+ - Answer using only the supplied GeoAI context.
352
+ - Do not invent geographic locations, coordinates, objects, or facts.
353
+ - Do not change the model's predicted class or confidence.
354
+ - If the requested information is unavailable, clearly say so.
355
+ - Treat Grad-CAM as broad model attention, not exact segmentation.
356
+ - Keep answers clear and concise.
357
+ """
358
+
359
+ user_prompt = f"""
360
+ VERIFIED GEOAI CONTEXT:
361
+
362
+ {geo_context}
363
+
364
+ USER QUESTION:
365
+ {question}
366
+
367
+ Answer the user's question using only the verified context above.
368
+ """
369
+
370
+ response = groq_client.chat.completions.create(
371
+ model="openai/gpt-oss-20b",
372
+ messages=[
373
+ {
374
+ "role": "system",
375
+ "content": system_prompt
376
+ },
377
+ {
378
+ "role": "user",
379
+ "content": user_prompt
380
+ }
381
+ ],
382
+ temperature=0.2,
383
+ max_completion_tokens=300
384
+ )
385
+
386
+ return response.choices[0].message.content
387
+
388
+
389
+
390
+ def gradio_ask_question(question, structured_state):
391
+
392
+ if not question or not question.strip():
393
+ return "Please enter a question."
394
+
395
+ if not structured_state:
396
+ return "Please upload and analyze a satellite image first."
397
+
398
+ answer = ask_geoai_llm(
399
+ question,
400
+ structured_state
401
+ )
402
+
403
+ return answer
404
+
405
+
406
+
407
+ def premium_analyze_image(uploaded_image):
408
+
409
+ if uploaded_image is None:
410
+ raise gr.Error(
411
+ "Upload a satellite image before starting the analysis."
412
+ )
413
+
414
+ image_array = np.asarray(
415
+ uploaded_image,
416
+ dtype=np.float32
417
+ )
418
+
419
+ if image_array.max() > 1.0:
420
+ image_array = image_array / 255.0
421
+
422
+ # Core GeoAI analysis
423
+ model_result, heatmap, overlay = analyze_satellite_image(
424
+ image_array,
425
+ model=xai_model
426
+ )
427
+
428
+ # Structured geospatial intelligence
429
+ structured_output = create_structured_geo_analysis(
430
+ model_result
431
+ )
432
+
433
+ # Detailed LLM report
434
+ initial_question = (
435
+ "Explain what this image shows, how reliable the result is, "
436
+ "and what the analysis can be used for."
437
+ )
438
+
439
+ llm_explanation = ask_geoai_llm(
440
+ initial_question,
441
+ structured_output
442
+ )
443
+
444
+ predicted_class = model_result["predicted_class"]
445
+ confidence = model_result["confidence"]
446
+ confidence_level = model_result["confidence_level"]
447
+
448
+ # Clean display name
449
+ display_class = (
450
+ predicted_class
451
+ .replace("HerbaceousVegetation", "Herbaceous Vegetation")
452
+ .replace("PermanentCrop", "Permanent Crop")
453
+ .replace("AnnualCrop", "Annual Crop")
454
+ .replace("SeaLake", "Sea / Lake")
455
+ )
456
+
457
+ # Hero prediction card
458
+ prediction_summary = f"""
459
+ <div class="prediction-card">
460
+ <div class="prediction-eyebrow">PRIMARY CLASSIFICATION</div>
461
+ <div class="prediction-name">{display_class}</div>
462
+ <div class="prediction-meta">
463
+ <span class="confidence-pill">{confidence:.2f}% confidence</span>
464
+ <span class="level-pill">{confidence_level} certainty</span>
465
+ </div>
466
+ </div>
467
+ """
468
+
469
+ # Top-3 scores
470
+ top3_scores = {
471
+ item["class"]: item["confidence"] / 100
472
+ for item in model_result["top3_predictions"]
473
+ }
474
+
475
+ # Compact model metadata
476
+ analysis_meta = f"""
477
+ <div class="meta-grid">
478
+ <div class="meta-item">
479
+ <span class="meta-label">MODEL</span>
480
+ <strong>Fine-tuned MobileNetV2</strong>
481
+ </div>
482
+ <div class="meta-item">
483
+ <span class="meta-label">EXPLAINABILITY</span>
484
+ <strong>Grad-CAM</strong>
485
+ </div>
486
+ <div class="meta-item">
487
+ <span class="meta-label">CONFIDENCE</span>
488
+ <strong>{confidence_level}</strong>
489
+ </div>
490
+ </div>
491
+ """
492
+
493
+ return (
494
+ prediction_summary,
495
+ uploaded_image,
496
+ overlay,
497
+ top3_scores,
498
+ llm_explanation,
499
+ analysis_meta,
500
+ structured_output
501
+ )
502
+
503
+
504
+
505
+ # =========================================================
506
+ # EXACT PREMIUM DESIGN SYSTEM
507
+ # =========================================================
508
+
509
+ premium_css = '\n/* =========================================================\n GEO-INTELLIGENT AI — PREMIUM DESIGN SYSTEM\n ========================================================= */\n\n:root {\n --bg-0: #05080d;\n --bg-1: #081019;\n --bg-2: #0b1520;\n --surface: rgba(12, 23, 34, 0.82);\n --surface-strong: rgba(14, 27, 40, 0.96);\n --surface-soft: rgba(255, 255, 255, 0.035);\n\n --border: rgba(148, 184, 207, 0.14);\n --border-hover: rgba(77, 226, 183, 0.34);\n\n --text-1: #f4f8fb;\n --text-2: #a9bac7;\n --text-3: #708695;\n\n --accent: #4de2b7;\n --accent-2: #54b9ff;\n --accent-soft: rgba(77, 226, 183, 0.12);\n\n --shadow-lg:\n 0 30px 80px rgba(0, 0, 0, 0.38),\n 0 1px 0 rgba(255, 255, 255, 0.03) inset;\n\n --radius-xl: 28px;\n --radius-lg: 22px;\n --radius-md: 16px;\n}\n\n\n/* ---------- GLOBAL ---------- */\n\n.gradio-container {\n max-width: 1500px !important;\n margin: 0 auto !important;\n padding: 0 28px 60px !important;\n background:\n radial-gradient(\n circle at 12% 8%,\n rgba(39, 188, 152, 0.10),\n transparent 28%\n ),\n radial-gradient(\n circle at 88% 12%,\n rgba(61, 145, 218, 0.10),\n transparent 26%\n ),\n linear-gradient(\n 180deg,\n var(--bg-0) 0%,\n var(--bg-1) 48%,\n #060b11 100%\n ) !important;\n color: var(--text-1) !important;\n min-height: 100vh;\n}\n\nbody {\n background: var(--bg-0) !important;\n}\n\n\n/* ---------- TOP NAV ---------- */\n\n.top-nav {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 24px 4px 18px;\n}\n\n.brand-lockup {\n display: flex;\n align-items: center;\n gap: 12px;\n}\n\n.brand-mark {\n width: 42px;\n height: 42px;\n display: grid;\n place-items: center;\n border-radius: 13px;\n background:\n linear-gradient(\n 135deg,\n rgba(77, 226, 183, 0.22),\n rgba(84, 185, 255, 0.14)\n );\n border: 1px solid rgba(77, 226, 183, 0.24);\n font-size: 21px;\n box-shadow: 0 10px 30px rgba(77, 226, 183, 0.08);\n}\n\n.brand-name {\n font-size: 16px;\n font-weight: 750;\n letter-spacing: -0.02em;\n}\n\n.brand-sub {\n color: var(--text-3);\n font-size: 11px;\n margin-top: 2px;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n}\n\n.system-status {\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 9px 13px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: rgba(255,255,255,0.025);\n color: var(--text-2);\n font-size: 12px;\n}\n\n.status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n background: var(--accent);\n box-shadow: 0 0 14px rgba(77, 226, 183, 0.75);\n}\n\n\n/* ---------- HERO ---------- */\n\n.hero {\n position: relative;\n overflow: hidden;\n padding: 74px 38px 68px;\n text-align: center;\n border: 1px solid var(--border);\n border-radius: 32px;\n background:\n linear-gradient(\n 135deg,\n rgba(77, 226, 183, 0.055),\n transparent 35%\n ),\n linear-gradient(\n 225deg,\n rgba(84, 185, 255, 0.055),\n transparent 38%\n ),\n rgba(8, 16, 25, 0.76);\n box-shadow: var(--shadow-lg);\n}\n\n.hero::before {\n content: "";\n position: absolute;\n width: 440px;\n height: 440px;\n left: 50%;\n top: -340px;\n transform: translateX(-50%);\n border-radius: 50%;\n background: rgba(77, 226, 183, 0.13);\n filter: blur(80px);\n pointer-events: none;\n}\n\n.hero-kicker {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n padding: 8px 13px;\n border-radius: 999px;\n border: 1px solid rgba(77, 226, 183, 0.18);\n background: rgba(77, 226, 183, 0.07);\n color: #8af0d2;\n font-size: 11px;\n font-weight: 700;\n letter-spacing: 0.12em;\n text-transform: uppercase;\n}\n\n.hero h1 {\n margin: 24px auto 16px !important;\n max-width: 950px;\n color: var(--text-1) !important;\n font-size: clamp(42px, 6vw, 76px) !important;\n line-height: 0.98 !important;\n letter-spacing: -0.055em !important;\n font-weight: 800 !important;\n}\n\n.hero-gradient {\n background:\n linear-gradient(\n 90deg,\n #ffffff 0%,\n #8cf2d5 46%,\n #74c9ff 100%\n );\n -webkit-background-clip: text;\n -webkit-text-fill-color: transparent;\n}\n\n.hero-copy {\n max-width: 730px;\n margin: 0 auto;\n color: var(--text-2);\n font-size: 17px;\n line-height: 1.75;\n}\n\n.capability-row {\n margin-top: 28px;\n display: flex;\n justify-content: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.capability-chip {\n padding: 9px 13px;\n border-radius: 999px;\n color: var(--text-2);\n background: rgba(255,255,255,0.035);\n border: 1px solid var(--border);\n font-size: 12px;\n}\n\n\n/* ---------- SECTION SYSTEM ---------- */\n\n.section-wrap {\n margin-top: 28px;\n}\n\n.section-heading {\n margin: 0 0 14px 4px;\n}\n\n.section-eyebrow {\n color: var(--accent);\n font-size: 11px;\n font-weight: 750;\n letter-spacing: 0.13em;\n text-transform: uppercase;\n}\n\n.section-title {\n margin-top: 7px;\n color: var(--text-1);\n font-size: 24px;\n font-weight: 750;\n letter-spacing: -0.035em;\n}\n\n.section-copy {\n margin-top: 7px;\n color: var(--text-3);\n font-size: 13px;\n}\n\n\n/* ---------- PREMIUM CARDS ---------- */\n\n.premium-card {\n border: 1px solid var(--border) !important;\n border-radius: var(--radius-xl) !important;\n background:\n linear-gradient(\n 145deg,\n rgba(255,255,255,0.025),\n transparent 42%\n ),\n var(--surface) !important;\n box-shadow: var(--shadow-lg);\n overflow: hidden;\n}\n\n.card-pad {\n padding: 22px;\n}\n\n\n/* ---------- UPLOAD ---------- */\n\n.upload-zone {\n min-height: 430px;\n}\n\n.upload-zone .image-container {\n border-radius: 20px !important;\n overflow: hidden !important;\n}\n\n.upload-zone [data-testid="image"] {\n min-height: 360px !important;\n}\n\n.upload-hint {\n margin-top: 14px;\n display: flex;\n gap: 8px;\n color: var(--text-3);\n font-size: 12px;\n}\n\n\n/* ---------- BUTTONS ---------- */\n\n.primary-action {\n margin-top: 14px !important;\n min-height: 54px !important;\n border: 0 !important;\n border-radius: 16px !important;\n background:\n linear-gradient(\n 110deg,\n #38cfa5,\n #47aef2\n ) !important;\n color: #03110d !important;\n font-weight: 800 !important;\n letter-spacing: -0.01em;\n box-shadow:\n 0 16px 34px rgba(55, 205, 163, 0.16) !important;\n transition:\n transform 180ms ease,\n box-shadow 180ms ease,\n filter 180ms ease !important;\n}\n\n.primary-action:hover {\n transform: translateY(-2px);\n filter: brightness(1.05);\n box-shadow:\n 0 20px 42px rgba(55, 205, 163, 0.23) !important;\n}\n\n.secondary-action {\n min-height: 48px !important;\n border-radius: 14px !important;\n border: 1px solid rgba(77, 226, 183, 0.24) !important;\n background: rgba(77, 226, 183, 0.08) !important;\n color: #9af4d9 !important;\n font-weight: 700 !important;\n}\n\n\n/* ---------- PREDICTION HERO ---------- */\n\n.prediction-card {\n position: relative;\n padding: 28px;\n min-height: 188px;\n border-radius: 24px;\n border: 1px solid rgba(77, 226, 183, 0.19);\n background:\n radial-gradient(\n circle at 92% 5%,\n rgba(84, 185, 255, 0.14),\n transparent 32%\n ),\n linear-gradient(\n 135deg,\n rgba(77, 226, 183, 0.10),\n rgba(255,255,255,0.018)\n );\n}\n\n.prediction-eyebrow {\n color: #83eacc;\n font-size: 10px;\n font-weight: 800;\n letter-spacing: 0.15em;\n}\n\n.prediction-name {\n margin-top: 16px;\n color: white;\n font-size: clamp(30px, 4vw, 48px);\n font-weight: 800;\n line-height: 1;\n letter-spacing: -0.05em;\n}\n\n.prediction-meta {\n margin-top: 22px;\n display: flex;\n gap: 9px;\n flex-wrap: wrap;\n}\n\n.confidence-pill,\n.level-pill {\n display: inline-flex;\n padding: 8px 11px;\n border-radius: 999px;\n font-size: 11px;\n font-weight: 700;\n}\n\n.confidence-pill {\n color: #9af4d9;\n background: rgba(77, 226, 183, 0.10);\n border: 1px solid rgba(77, 226, 183, 0.18);\n}\n\n.level-pill {\n color: #a8d8ff;\n background: rgba(84, 185, 255, 0.09);\n border: 1px solid rgba(84, 185, 255, 0.17);\n}\n\n\n/* ---------- IMAGE COMPARISON ---------- */\n\n.visual-panel [data-testid="image"] {\n min-height: 390px !important;\n}\n\n.visual-panel img {\n object-fit: contain !important;\n border-radius: 18px !important;\n}\n\n.panel-label {\n color: var(--text-2);\n font-size: 12px;\n font-weight: 700;\n letter-spacing: 0.02em;\n}\n\n\n/* ---------- META GRID ---------- */\n\n.meta-grid {\n display: grid;\n grid-template-columns: repeat(3, 1fr);\n gap: 10px;\n}\n\n.meta-item {\n min-height: 84px;\n padding: 15px;\n border-radius: 16px;\n background: rgba(255,255,255,0.025);\n border: 1px solid var(--border);\n}\n\n.meta-label {\n display: block;\n margin-bottom: 9px;\n color: var(--text-3);\n font-size: 9px;\n font-weight: 800;\n letter-spacing: 0.13em;\n}\n\n.meta-item strong {\n color: var(--text-1);\n font-size: 12px;\n}\n\n\n/* ---------- AI REPORT ---------- */\n\n.ai-report {\n padding: 10px 4px;\n}\n\n.ai-report h1,\n.ai-report h2,\n.ai-report h3 {\n color: var(--text-1) !important;\n letter-spacing: -0.03em;\n}\n\n.ai-report p,\n.ai-report li {\n color: #c1ced7 !important;\n font-size: 15px !important;\n line-height: 1.8 !important;\n}\n\n.ai-report strong {\n color: #eef8f5 !important;\n}\n\n\n/* ---------- COPILOT ---------- */\n\n.copilot-shell {\n border: 1px solid rgba(84, 185, 255, 0.16) !important;\n border-radius: 28px !important;\n background:\n radial-gradient(\n circle at 100% 0%,\n rgba(84, 185, 255, 0.08),\n transparent 32%\n ),\n var(--surface) !important;\n box-shadow: var(--shadow-lg);\n}\n\n.prompt-row {\n display: flex;\n flex-wrap: wrap;\n gap: 8px;\n margin: 4px 0 14px;\n}\n\n.prompt-chip {\n padding: 8px 11px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: rgba(255,255,255,0.025);\n color: var(--text-2);\n font-size: 11px;\n}\n\n.copilot-input textarea {\n min-height: 72px !important;\n border-radius: 16px !important;\n}\n\n.copilot-answer {\n min-height: 100px;\n padding: 4px;\n}\n\n\n/* ---------- LABEL / CONFIDENCE PANEL ---------- */\n\n.confidence-panel {\n border-radius: 22px !important;\n}\n\n.confidence-panel .label-wrap {\n background: transparent !important;\n}\n\n\n/* ---------- FOOTER ---------- */\n\n.app-footer {\n margin-top: 34px;\n padding: 22px 4px 4px;\n display: flex;\n justify-content: space-between;\n gap: 20px;\n border-top: 1px solid var(--border);\n color: var(--text-3);\n font-size: 11px;\n}\n\n\n/* ---------- GRADIO CLEANUP ---------- */\n\nfooter {\n display: none !important;\n}\n\nbutton,\ntextarea,\ninput {\n transition: all 180ms ease !important;\n}\n\n.block {\n border-color: var(--border) !important;\n}\n\nlabel span {\n color: var(--text-2) !important;\n}\n\n\n/* ---------- RESPONSIVE ---------- */\n\n@media (max-width: 900px) {\n\n .gradio-container {\n padding: 0 14px 40px !important;\n }\n\n .hero {\n padding: 52px 20px 48px;\n border-radius: 24px;\n }\n\n .hero h1 {\n font-size: 44px !important;\n }\n\n .hero-copy {\n font-size: 15px;\n }\n\n .meta-grid {\n grid-template-columns: 1fr;\n }\n\n .top-nav {\n align-items: flex-start;\n }\n\n .brand-sub {\n display: none;\n }\n\n .visual-panel [data-testid="image"],\n .upload-zone [data-testid="image"] {\n min-height: 280px !important;\n }\n\n .app-footer {\n flex-direction: column;\n }\n}\n\n\n@media (max-width: 560px) {\n\n .system-status {\n display: none;\n }\n\n .hero h1 {\n font-size: 38px !important;\n }\n\n .hero {\n padding-left: 16px;\n padding-right: 16px;\n }\n\n .prediction-card {\n padding: 22px;\n }\n\n .prediction-name {\n font-size: 34px;\n }\n}\n\n.prompt-chip-button {\n flex: 0 0 auto !important;\n width: auto !important;\n min-width: 0 !important;\n min-height: 40px !important;\n padding: 0 16px !important;\n\n border-radius: 999px !important;\n border: 1px solid rgba(148, 184, 207, 0.18) !important;\n\n background: rgba(255, 255, 255, 0.025) !important;\n color: #b9d7ea !important;\n\n font-size: 12px !important;\n font-weight: 600 !important;\n\n transition:\n transform 180ms ease,\n border-color 180ms ease,\n background 180ms ease,\n box-shadow 180ms ease !important;\n}\n\n.prompt-chip-button:hover {\n transform: translateY(-2px) !important;\n\n border-color: rgba(77, 226, 183, 0.45) !important;\n\n background: rgba(77, 226, 183, 0.08) !important;\n color: #a3f5dd !important;\n\n box-shadow:\n 0 10px 28px rgba(77, 226, 183, 0.08) !important;\n}\n'
510
+
511
+
512
+ # =========================================================
513
+ # EXACT WORKING PREMIUM INTERFACE
514
+ # =========================================================
515
+
516
+ with gr.Blocks(
517
+ title="Geo-Intelligent AI",
518
+ css=premium_css,
519
+ theme=gr.themes.Base()
520
+ ) as premium_demo:
521
+
522
+ # Persistent analysis context
523
+ structured_state = gr.State()
524
+
525
+ # =====================================================
526
+ # TOP NAVIGATION
527
+ # =====================================================
528
+
529
+ gr.HTML("""
530
+ <div class="top-nav">
531
+ <div class="brand-lockup">
532
+ <div class="brand-mark">◈</div>
533
+ <div>
534
+ <div class="brand-name">Geo-Intelligent AI</div>
535
+ <div class="brand-sub">
536
+ Explainable Geospatial Intelligence
537
+ </div>
538
+ </div>
539
+ </div>
540
+
541
+ <div class="system-status">
542
+ <span class="status-dot"></span>
543
+ AI systems operational
544
+ </div>
545
+ </div>
546
+ """)
547
+
548
+ # =====================================================
549
+ # HERO
550
+ # =====================================================
551
+
552
+ gr.HTML("""
553
+ <section class="hero">
554
+ <div class="hero-kicker">
555
+ ◉ MULTIMODAL GEOSPATIAL INTELLIGENCE
556
+ </div>
557
+
558
+ <h1>
559
+ Understand Earth imagery with
560
+ <span class="hero-gradient">intelligent context.</span>
561
+ </h1>
562
+
563
+ <p class="hero-copy">
564
+ Transform satellite imagery into explainable land-cover
565
+ intelligence using deep learning, visual attribution,
566
+ structured geospatial knowledge, and natural-language AI.
567
+ </p>
568
+
569
+ <div class="capability-row">
570
+ <span class="capability-chip">10 Land-Cover Classes</span>
571
+ <span class="capability-chip">90.44% Test Accuracy</span>
572
+ <span class="capability-chip">Grad-CAM Explainability</span>
573
+ <span class="capability-chip">LLM-Powered Analysis</span>
574
+ </div>
575
+ </section>
576
+ """)
577
+
578
+ # =====================================================
579
+ # ANALYSIS WORKSPACE
580
+ # =====================================================
581
+
582
+ gr.HTML("""
583
+ <div class="section-wrap">
584
+ <div class="section-heading">
585
+ <div class="section-eyebrow">01 / ANALYZE</div>
586
+ <div class="section-title">
587
+ Satellite Intelligence Workspace
588
+ </div>
589
+ <div class="section-copy">
590
+ Upload an image and run the complete GeoAI analysis pipeline.
591
+ </div>
592
+ </div>
593
+ </div>
594
+ """)
595
+
596
+ with gr.Row(equal_height=False):
597
+
598
+ # LEFT — UPLOAD
599
+ with gr.Column(
600
+ scale=5,
601
+ elem_classes=["premium-card", "card-pad"]
602
+ ):
603
+
604
+ input_image = gr.Image(
605
+ type="numpy",
606
+ label="Satellite Image",
607
+ sources=["upload"],
608
+ height=420,
609
+ elem_classes=["upload-zone"]
610
+ )
611
+
612
+ gr.HTML("""
613
+ <div class="upload-hint">
614
+ <span>◎</span>
615
+ <span>
616
+ Best results are obtained from satellite or
617
+ aerial land-cover imagery.
618
+ </span>
619
+ </div>
620
+ """)
621
+
622
+ analyze_button = gr.Button(
623
+ "Analyze Satellite Image →",
624
+ variant="primary",
625
+ elem_classes=["primary-action"]
626
+ )
627
+
628
+ # RIGHT — PRIMARY RESULT
629
+ with gr.Column(scale=4):
630
+
631
+ prediction_summary = gr.HTML(
632
+ """
633
+ <div class="prediction-card">
634
+ <div class="prediction-eyebrow">
635
+ AWAITING ANALYSIS
636
+ </div>
637
+
638
+ <div class="prediction-name">
639
+ No image analyzed
640
+ </div>
641
+
642
+ <div class="prediction-meta">
643
+ <span class="level-pill">
644
+ Upload an image to begin
645
+ </span>
646
+ </div>
647
+ </div>
648
+ """
649
+ )
650
+
651
+ with gr.Column(
652
+ elem_classes=["premium-card", "card-pad"]
653
+ ):
654
+
655
+ gr.HTML("""
656
+ <div class="section-eyebrow">
657
+ CONFIDENCE DISTRIBUTION
658
+ </div>
659
+ """)
660
+
661
+ prediction_output = gr.Label(
662
+ label="Top-3 Land-Cover Predictions",
663
+ num_top_classes=3,
664
+ elem_classes=["confidence-panel"]
665
+ )
666
+
667
+ analysis_meta = gr.HTML(
668
+ """
669
+ <div class="meta-grid">
670
+ <div class="meta-item">
671
+ <span class="meta-label">MODEL</span>
672
+ <strong>MobileNetV2</strong>
673
+ </div>
674
+
675
+ <div class="meta-item">
676
+ <span class="meta-label">
677
+ EXPLAINABILITY
678
+ </span>
679
+ <strong>Grad-CAM</strong>
680
+ </div>
681
+
682
+ <div class="meta-item">
683
+ <span class="meta-label">STATUS</span>
684
+ <strong>Ready</strong>
685
+ </div>
686
+ </div>
687
+ """
688
+ )
689
+
690
+ # =====================================================
691
+ # VISUAL INTELLIGENCE
692
+ # =====================================================
693
+
694
+ gr.HTML("""
695
+ <div class="section-wrap">
696
+ <div class="section-heading">
697
+ <div class="section-eyebrow">02 / EXPLAIN</div>
698
+
699
+ <div class="section-title">
700
+ Visual Intelligence
701
+ </div>
702
+
703
+ <div class="section-copy">
704
+ Compare the source image with the regions that most
705
+ influenced the model's prediction.
706
+ </div>
707
+ </div>
708
+ </div>
709
+ """)
710
+
711
+ with gr.Row(equal_height=True):
712
+
713
+ with gr.Column(
714
+ elem_classes=[
715
+ "premium-card",
716
+ "card-pad",
717
+ "visual-panel"
718
+ ]
719
+ ):
720
+
721
+ gr.HTML(
722
+ '<div class="panel-label">ORIGINAL IMAGE</div>'
723
+ )
724
+
725
+ original_output = gr.Image(
726
+ label=None,
727
+ interactive=False,
728
+ height=430,
729
+ show_label=False
730
+ )
731
+
732
+ with gr.Column(
733
+ elem_classes=[
734
+ "premium-card",
735
+ "card-pad",
736
+ "visual-panel"
737
+ ]
738
+ ):
739
+
740
+ gr.HTML(
741
+ '<div class="panel-label">GRAD-CAM ATTENTION</div>'
742
+ )
743
+
744
+ gradcam_output = gr.Image(
745
+ label=None,
746
+ interactive=False,
747
+ height=430,
748
+ show_label=False
749
+ )
750
+
751
+ # =====================================================
752
+ # AI INTELLIGENCE REPORT
753
+ # =====================================================
754
+
755
+ gr.HTML("""
756
+ <div class="section-wrap">
757
+ <div class="section-heading">
758
+ <div class="section-eyebrow">03 / INTERPRET</div>
759
+
760
+ <div class="section-title">
761
+ GeoAI Intelligence Report
762
+ </div>
763
+
764
+ <div class="section-copy">
765
+ A natural-language interpretation grounded in the
766
+ model output and geospatial knowledge layer.
767
+ </div>
768
+ </div>
769
+ </div>
770
+ """)
771
+
772
+ with gr.Column(
773
+ elem_classes=["premium-card", "card-pad"]
774
+ ):
775
+
776
+ explanation_output = gr.Markdown(
777
+ """
778
+ Upload and analyze a satellite image to generate
779
+ the intelligence report.
780
+ """,
781
+ elem_classes=["ai-report"]
782
+ )
783
+
784
+ # =====================================================
785
+ # GEOAI COPILOT
786
+ # =====================================================
787
+
788
+ gr.HTML("""
789
+ <div class="section-wrap">
790
+ <div class="section-heading">
791
+ <div class="section-eyebrow">04 / ASK</div>
792
+
793
+ <div class="section-title">
794
+ GeoAI Copilot
795
+ </div>
796
+
797
+ <div class="section-copy">
798
+ Ask follow-up questions about the currently
799
+ analyzed image.
800
+ </div>
801
+ </div>
802
+ </div>
803
+ """)
804
+
805
+ with gr.Column(
806
+ elem_classes=["copilot-shell", "card-pad"]
807
+ ):
808
+
809
+ # =============================================
810
+ # CLICKABLE SUGGESTED QUESTIONS
811
+ # =============================================
812
+
813
+ with gr.Row(
814
+ elem_classes=["prompt-row"]
815
+ ):
816
+
817
+ prompt_reliability = gr.Button(
818
+ "How reliable is this prediction?",
819
+ elem_classes=["prompt-chip-button"]
820
+ )
821
+
822
+ prompt_focus = gr.Button(
823
+ "What did the model focus on?",
824
+ elem_classes=["prompt-chip-button"]
825
+ )
826
+
827
+ prompt_importance = gr.Button(
828
+ "Why is this land cover important?",
829
+ elem_classes=["prompt-chip-button"]
830
+ )
831
+
832
+ prompt_alternatives = gr.Button(
833
+ "What are the alternative predictions?",
834
+ elem_classes=["prompt-chip-button"]
835
+ )
836
+
837
+ question_input = gr.Textbox(
838
+ label="Ask about this analysis",
839
+ placeholder=(
840
+ "Ask about the prediction, confidence, "
841
+ "explainability, significance, or applications..."
842
+ ),
843
+ lines=2,
844
+ elem_classes=["copilot-input"]
845
+ )
846
+
847
+ with gr.Row():
848
+
849
+ ask_button = gr.Button(
850
+ "Ask GeoAI →",
851
+ variant="secondary",
852
+ elem_classes=["secondary-action"]
853
+ )
854
+
855
+ clear_question_button = gr.Button(
856
+ "Clear Question"
857
+ )
858
+
859
+ answer_output = gr.Markdown(
860
+ """
861
+ Your GeoAI Copilot response will appear here.
862
+ """,
863
+ elem_classes=["copilot-answer"]
864
+ )
865
+
866
+ # =====================================================
867
+ # FOOTER
868
+ # =====================================================
869
+
870
+ gr.HTML("""
871
+ <div class="app-footer">
872
+ <span>
873
+ Geo-Intelligent AI System ·
874
+ Explainable Satellite Intelligence
875
+ </span>
876
+
877
+ <span>
878
+ MobileNetV2 · Grad-CAM ·
879
+ Structured GeoAI · Open-Weight LLM
880
+ </span>
881
+ </div>
882
+ """)
883
+
884
+ # =====================================================
885
+ # HELPER FUNCTION FOR SUGGESTED QUESTIONS
886
+ # =====================================================
887
+
888
+ def ask_suggested_question(
889
+ question,
890
+ structured_state
891
+ ):
892
+
893
+ answer = gradio_ask_question(
894
+ question,
895
+ structured_state
896
+ )
897
+
898
+ return question, answer
899
+
900
+ # =====================================================
901
+ # EVENT WIRING
902
+ # =====================================================
903
+
904
+ # Analyze satellite image
905
+ analyze_button.click(
906
+ fn=premium_analyze_image,
907
+ inputs=input_image,
908
+ outputs=[
909
+ prediction_summary,
910
+ original_output,
911
+ gradcam_output,
912
+ prediction_output,
913
+ explanation_output,
914
+ analysis_meta,
915
+ structured_state
916
+ ]
917
+ )
918
+
919
+ # Ask manually typed question
920
+ ask_button.click(
921
+ fn=gradio_ask_question,
922
+ inputs=[
923
+ question_input,
924
+ structured_state
925
+ ],
926
+ outputs=answer_output
927
+ )
928
+
929
+ # Press Enter to ask
930
+ question_input.submit(
931
+ fn=gradio_ask_question,
932
+ inputs=[
933
+ question_input,
934
+ structured_state
935
+ ],
936
+ outputs=answer_output
937
+ )
938
+
939
+ # Clear question and answer
940
+ clear_question_button.click(
941
+ fn=lambda: (
942
+ "",
943
+ "Your GeoAI Copilot response will appear here."
944
+ ),
945
+ inputs=None,
946
+ outputs=[
947
+ question_input,
948
+ answer_output
949
+ ]
950
+ )
951
+
952
+ # =====================================================
953
+ # SUGGESTED QUESTION EVENTS
954
+ # =====================================================
955
+
956
+ prompt_reliability.click(
957
+ fn=lambda state: ask_suggested_question(
958
+ "How reliable is this prediction?",
959
+ state
960
+ ),
961
+ inputs=structured_state,
962
+ outputs=[
963
+ question_input,
964
+ answer_output
965
+ ]
966
+ )
967
+
968
+ prompt_focus.click(
969
+ fn=lambda state: ask_suggested_question(
970
+ "What did the model focus on?",
971
+ state
972
+ ),
973
+ inputs=structured_state,
974
+ outputs=[
975
+ question_input,
976
+ answer_output
977
+ ]
978
+ )
979
+
980
+ prompt_importance.click(
981
+ fn=lambda state: ask_suggested_question(
982
+ "Why is this land cover important?",
983
+ state
984
+ ),
985
+ inputs=structured_state,
986
+ outputs=[
987
+ question_input,
988
+ answer_output
989
+ ]
990
+ )
991
+
992
+ prompt_alternatives.click(
993
+ fn=lambda state: ask_suggested_question(
994
+ "What are the alternative predictions?",
995
+ state
996
+ ),
997
+ inputs=structured_state,
998
+ outputs=[
999
+ question_input,
1000
+ answer_output
1001
+ ]
1002
+ )
1003
+
1004
+
1005
+
1006
+ # =========================================================
1007
+ # HUGGING FACE SPACES LAUNCH
1008
+ # =========================================================
1009
+
1010
+ if __name__ == "__main__":
1011
+ premium_demo.launch()
geoai_clean_final_best.keras ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bf5b8d0424e94f061bae591d9f882b77216130422ec5b627a17d099c3187fdf2
3
+ size 23721940
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio==6.19.0
2
+ tensorflow-cpu
3
+ opencv-python-headless
4
+ numpy
5
+ groq