RubaAnam1 commited on
Commit
7c65a3b
·
verified ·
1 Parent(s): f52d6c7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +78 -322
app.py CHANGED
@@ -4,22 +4,14 @@ import os
4
  from urllib.parse import quote
5
 
6
  # =====================================================
7
- # PAGE CONFIG
8
  # =====================================================
9
 
10
- st.set_page_config(
11
- page_title="Biodiversity GIS Explorer",
12
- layout="wide"
13
- )
14
-
15
  st.title("🌍 Biodiversity GIS Explorer")
16
 
17
- st.write(
18
- "Explore biodiversity species using GBIF + AI analysis."
19
- )
20
-
21
  # =====================================================
22
- # GEMINI API
23
  # =====================================================
24
 
25
  def call_gemini(prompt, api_key):
@@ -30,467 +22,231 @@ def call_gemini(prompt, api_key):
30
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite:generateContent?key={api_key}"
31
 
32
  payload = {
33
- "contents": [
34
- {
35
- "parts": [{"text": prompt}]
36
- }
37
- ]
38
  }
39
 
40
  try:
41
-
42
- response = requests.post(url, json=payload)
43
-
44
- result = response.json()
45
-
46
- return result["candidates"][0]["content"]["parts"][0]["text"]
47
-
48
  except Exception as e:
49
-
50
  return f"Gemini Error: {e}"
51
 
52
 
53
  # =====================================================
54
- # COUNTRY CODES
55
  # =====================================================
56
 
57
  country_codes = {
58
-
59
  "sudan": "SD",
60
  "السودان": "SD",
61
-
62
  "egypt": "EG",
63
  "مصر": "EG",
64
-
65
  "kenya": "KE",
66
  "كينيا": "KE",
67
-
68
  "ethiopia": "ET",
69
  "إثيوبيا": "ET",
70
-
71
  "saudi arabia": "SA",
72
  "السعودية": "SA",
73
-
74
- "india": "IN",
75
- "الهند": "IN",
76
-
77
  "usa": "US",
78
  "united states": "US",
79
  "america": "US",
80
  "امريكا": "US",
81
-
82
  "canada": "CA",
83
  "كندا": "CA"
84
  }
85
 
86
 
87
  # =====================================================
88
- # SPECIES FILTERS
89
  # =====================================================
90
 
91
  species_filters = {
92
-
93
- "Birds": "class=Aves&rank=SPECIES",
94
-
95
- "Plants": "kingdom=Plantae&rank=SPECIES",
96
-
97
- "Mammals": "class=Mammalia&rank=SPECIES",
98
-
99
- "Reptiles": "class=Reptilia&rank=SPECIES",
100
-
101
- "Fish": "class=Actinopterygii&rank=SPECIES",
102
-
103
- "Insects": "class=Insecta&rank=SPECIES"
104
  }
105
 
106
 
107
  # =====================================================
108
- # GET SPECIES
109
  # =====================================================
110
 
111
  def get_species(country_code, filter_value):
112
 
113
  url = (
114
- f"https://api.gbif.org/v1/occurrence/search?"
115
  f"country={country_code}"
116
  f"&{filter_value}"
117
- f"&limit=200"
118
- f"&has_coordinate=true"
119
- f"&has_geospatial_issue=false"
120
- f"&occurrence_status=PRESENT"
121
  )
122
 
123
  try:
124
-
125
- response = requests.get(url)
126
-
127
- data = response.json()
128
-
129
  results = data.get("results", [])
130
 
131
  species = []
132
 
133
  bad_words = [
134
- "unclassified",
135
- "sp.",
136
- "cf.",
137
- "aff.",
138
- "unknown",
139
- "incertae",
140
- "birds",
141
- "plants"
142
  ]
143
 
144
  for item in results:
145
 
146
  sp = item.get("species")
 
147
 
 
148
  if not sp:
149
  continue
150
 
151
- sp_lower = sp.lower()
152
-
153
- # تنظيف البيانات
154
- if any(word in sp_lower for word in bad_words):
155
  continue
156
 
157
- # تجاهل الأسماء القصيرة
158
  if len(sp.split()) < 2:
159
  continue
160
 
161
- # منع التكرار
 
 
 
162
  if sp not in species:
163
  species.append(sp)
164
 
165
  return species[:10]
166
 
167
  except Exception as e:
168
-
169
  st.error(f"GBIF Error: {e}")
170
-
171
  return []
172
 
173
 
174
  # =====================================================
175
- # GET LINKS
176
  # =====================================================
177
 
178
  def get_species_links(species_name, country):
179
 
180
  links = []
181
 
182
- clean_name = " ".join(species_name.split()[:2])
183
-
184
- encoded_species = quote(clean_name)
185
-
186
- encoded_country = quote(country)
187
 
188
- # -------------------------------------------------
189
  # GBIF
190
- # -------------------------------------------------
191
-
192
- gbif_api = (
193
- f"https://api.gbif.org/v1/species/match?"
194
- f"name={encoded_species}"
195
- )
196
-
197
  try:
198
-
199
- r = requests.get(gbif_api).json()
 
200
 
201
  if "usageKey" in r:
202
-
203
- gbif_link = (
204
- f"https://www.gbif.org/species/"
205
- f"{r['usageKey']}"
206
- )
207
-
208
  links.append(
209
- f"- [GBIF Species Page]({gbif_link})"
210
  )
211
-
212
  except:
213
  pass
214
 
215
- # -------------------------------------------------
216
  # Wikipedia
217
- # -------------------------------------------------
218
-
219
- wiki_name = clean_name.replace(" ", "_")
220
-
221
- wiki_url = (
222
- f"https://en.wikipedia.org/wiki/{wiki_name}"
223
- )
224
-
225
- links.append(
226
- f"- [Wikipedia Article]({wiki_url})"
227
- )
228
-
229
- # -------------------------------------------------
230
- # Google Scholar
231
- # -------------------------------------------------
232
-
233
- scholar_url = (
234
- f"https://scholar.google.com/scholar?q="
235
- f"{encoded_species}+{encoded_country}"
236
- )
237
-
238
  links.append(
239
- f"- [Google Scholar ({country})]({scholar_url})"
240
  )
241
 
242
- # -------------------------------------------------
243
  # iNaturalist
244
- # -------------------------------------------------
245
-
246
- inaturalist = (
247
- f"https://www.inaturalist.org/search?q="
248
- f"{encoded_species}"
249
- )
250
-
251
- links.append(
252
- f"- [iNaturalist Observations]({inaturalist})"
253
- )
254
-
255
- # -------------------------------------------------
256
- # Encyclopedia of Life
257
- # -------------------------------------------------
258
-
259
- eol = (
260
- f"https://eol.org/search?q="
261
- f"{encoded_species}"
262
- )
263
-
264
- links.append(
265
- f"- [Encyclopedia of Life]({eol})"
266
- )
267
-
268
- # -------------------------------------------------
269
- # Biodiversity Heritage Library
270
- # -------------------------------------------------
271
-
272
- bhl = (
273
- f"https://www.biodiversitylibrary.org/search?"
274
- f"searchTerm={encoded_species}"
275
- )
276
-
277
- links.append(
278
- f"- [Biodiversity Heritage Library]({bhl})"
279
- )
280
-
281
- # -------------------------------------------------
282
- # Google Search
283
- # -------------------------------------------------
284
-
285
- google_search = (
286
- f"https://www.google.com/search?q="
287
- f"{encoded_species}+in+{encoded_country}"
288
- )
289
-
290
- links.append(
291
- f"- [Google Search ({country})]({google_search})"
292
- )
293
-
294
- # -------------------------------------------------
295
- # Images Search
296
- # -------------------------------------------------
297
-
298
- image_search = (
299
- f"https://www.google.com/search?"
300
- f"tbm=isch&q={encoded_species}+{encoded_country}"
301
- )
302
-
303
  links.append(
304
- f"- [Images Search]({image_search})"
305
- )
306
-
307
- # -------------------------------------------------
308
- # Research Papers
309
- # -------------------------------------------------
310
-
311
- research = (
312
- f"https://www.google.com/search?q="
313
- f"{encoded_species}+{encoded_country}+research+paper"
314
  )
315
 
 
316
  links.append(
317
- f"- [Research Papers]({research})"
318
- )
319
-
320
- # -------------------------------------------------
321
- # Conservation
322
- # -------------------------------------------------
323
-
324
- conservation = (
325
- f"https://www.google.com/search?q="
326
- f"{encoded_species}+{encoded_country}+conservation"
327
- )
328
-
329
- links.append(
330
- f"- [Conservation Information]({conservation})"
331
  )
332
 
333
  return links
334
 
335
 
336
  # =====================================================
337
- # USER INPUTS
338
  # =====================================================
339
 
340
- country = st.text_input(
341
- "Enter Country"
342
- )
343
 
344
  species_type = st.selectbox(
345
  "Select Species Type",
346
- [
347
- "Birds",
348
- "Plants",
349
- "Mammals",
350
- "Reptiles",
351
- "Fish",
352
- "Insects"
353
- ]
354
  )
355
 
356
  # =====================================================
357
- # SEARCH BUTTON
358
  # =====================================================
359
 
360
  if st.button("Search Biodiversity"):
361
 
362
- # -------------------------------------------------
363
- # CLEAN COUNTRY INPUT
364
- # -------------------------------------------------
365
-
366
  country_clean = country.strip().lower()
367
-
368
- country_code = country_codes.get(
369
- country_clean
370
- )
371
-
372
- # -------------------------------------------------
373
- # VALIDATION
374
- # -------------------------------------------------
375
 
376
  if not country:
377
-
378
- st.warning(
379
- "Please enter a country."
380
- )
381
-
382
  st.stop()
383
 
384
  if not country_code:
385
-
386
- st.error(
387
- f"Country '{country}' not supported yet."
388
- )
389
-
390
  st.stop()
391
 
392
- filter_value = species_filters.get(
393
- species_type
394
- )
395
 
396
- # -------------------------------------------------
397
- # FETCH SPECIES
398
- # -------------------------------------------------
399
-
400
- with st.spinner(
401
- "Fetching biodiversity data..."
402
- ):
403
-
404
- species_list = get_species(
405
- country_code,
406
- filter_value
407
- )
408
 
409
  if not species_list:
410
-
411
- st.warning(
412
- "No clean biodiversity data found."
413
- )
414
-
415
  st.stop()
416
 
417
- # -------------------------------------------------
418
- # DISPLAY SPECIES
419
- # -------------------------------------------------
420
-
421
  st.subheader("🐾 Species Found")
422
 
423
  for sp in species_list:
 
424
 
425
- st.write(f"• {sp}")
426
-
427
- # -------------------------------------------------
428
- # AI ANALYSIS
429
- # -------------------------------------------------
430
-
431
- species_text = "\n".join(species_list)
432
 
433
  prompt = f"""
434
  You are a biodiversity scientist.
435
 
436
- Country:
437
- {country}
438
-
439
- Species Type:
440
- {species_type}
441
-
442
- Species Found:
443
- {species_text}
444
 
445
- Write:
446
- 1. Biodiversity summary
447
- 2. Ecological importance
448
- 3. Conservation challenges
449
- 4. GIS/environment insights
450
 
451
- IMPORTANT:
452
- Only discuss species actually found in the country.
453
- Avoid hallucinations.
454
  """
455
 
456
- with st.spinner(
457
- "Generating AI analysis..."
458
- ):
459
 
460
- ai_text = call_gemini(
461
- prompt,
462
- os.getenv("GEMINI_API_KEY")
463
- )
464
-
465
- st.subheader(
466
- "🤖 AI Biodiversity Analysis"
467
  )
468
 
469
- st.write(ai_text)
470
-
471
  # =================================================
472
- # LINKS SECTION
473
  # =================================================
474
 
475
- st.subheader(
476
- "🔗 Scientific & Country Related Links"
477
- )
478
-
479
- with st.spinner(
480
- "Generating links..."
481
- ):
482
 
483
- for sp in species_list:
484
-
485
- st.markdown(f"## 🔬 {sp}")
486
-
487
- links = get_species_links(
488
- sp,
489
- country
490
- )
491
-
492
- for link in links:
493
-
494
- st.markdown(link)
495
-
496
- st.markdown("---")
 
4
  from urllib.parse import quote
5
 
6
  # =====================================================
7
+ # PAGE
8
  # =====================================================
9
 
10
+ st.set_page_config(page_title="Biodiversity GIS Explorer", layout="wide")
 
 
 
 
11
  st.title("🌍 Biodiversity GIS Explorer")
12
 
 
 
 
 
13
  # =====================================================
14
+ # GEMINI
15
  # =====================================================
16
 
17
  def call_gemini(prompt, api_key):
 
22
  url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-lite:generateContent?key={api_key}"
23
 
24
  payload = {
25
+ "contents": [{"parts": [{"text": prompt}]}]
 
 
 
 
26
  }
27
 
28
  try:
29
+ r = requests.post(url, json=payload)
30
+ data = r.json()
31
+ return data["candidates"][0]["content"]["parts"][0]["text"]
 
 
 
 
32
  except Exception as e:
 
33
  return f"Gemini Error: {e}"
34
 
35
 
36
  # =====================================================
37
+ # COUNTRY (fixed)
38
  # =====================================================
39
 
40
  country_codes = {
 
41
  "sudan": "SD",
42
  "السودان": "SD",
 
43
  "egypt": "EG",
44
  "مصر": "EG",
 
45
  "kenya": "KE",
46
  "كينيا": "KE",
 
47
  "ethiopia": "ET",
48
  "إثيوبيا": "ET",
 
49
  "saudi arabia": "SA",
50
  "السعودية": "SA",
 
 
 
 
51
  "usa": "US",
52
  "united states": "US",
53
  "america": "US",
54
  "امريكا": "US",
 
55
  "canada": "CA",
56
  "كندا": "CA"
57
  }
58
 
59
 
60
  # =====================================================
61
+ # TAXON FILTERS (FIXED - IMPORTANT)
62
  # =====================================================
63
 
64
  species_filters = {
65
+ "Birds": "taxon_key=212",
66
+ "Plants": "taxon_key=6",
67
+ "Mammals": "taxon_key=359",
68
+ "Reptiles": "taxon_key=358",
69
+ "Fish": "taxon_key=204",
70
+ "Insects": "taxon_key=216"
 
 
 
 
 
 
71
  }
72
 
73
 
74
  # =====================================================
75
+ # GET SPECIES (CLEAN + FIXED GBIF LOGIC)
76
  # =====================================================
77
 
78
  def get_species(country_code, filter_value):
79
 
80
  url = (
81
+ "https://api.gbif.org/v1/occurrence/search?"
82
  f"country={country_code}"
83
  f"&{filter_value}"
84
+ "&limit=200"
85
+ "&has_coordinate=true"
86
+ "&has_geospatial_issue=false"
87
+ "&occurrence_status=PRESENT"
88
  )
89
 
90
  try:
91
+ r = requests.get(url)
92
+ data = r.json()
 
 
 
93
  results = data.get("results", [])
94
 
95
  species = []
96
 
97
  bad_words = [
98
+ "unclassified", "sp.", "cf.", "aff.",
99
+ "unknown", "incertae", "plant", "bird"
 
 
 
 
 
 
100
  ]
101
 
102
  for item in results:
103
 
104
  sp = item.get("species")
105
+ kingdom = item.get("kingdom")
106
 
107
+ # FILTER 1: must exist
108
  if not sp:
109
  continue
110
 
111
+ # FILTER 2: remove junk names
112
+ sp_low = sp.lower()
113
+ if any(w in sp_low for w in bad_words):
 
114
  continue
115
 
116
+ # FILTER 3: must be real species name
117
  if len(sp.split()) < 2:
118
  continue
119
 
120
+ # FILTER 4: strict bird check
121
+ if species_type == "Birds" and kingdom != "Animalia":
122
+ continue
123
+
124
  if sp not in species:
125
  species.append(sp)
126
 
127
  return species[:10]
128
 
129
  except Exception as e:
 
130
  st.error(f"GBIF Error: {e}")
 
131
  return []
132
 
133
 
134
  # =====================================================
135
+ # LINKS (kept clean)
136
  # =====================================================
137
 
138
  def get_species_links(species_name, country):
139
 
140
  links = []
141
 
142
+ clean = " ".join(species_name.split()[:2])
143
+ q = quote(clean)
144
+ c = quote(country)
 
 
145
 
 
146
  # GBIF
 
 
 
 
 
 
 
147
  try:
148
+ r = requests.get(
149
+ f"https://api.gbif.org/v1/species/match?name={q}"
150
+ ).json()
151
 
152
  if "usageKey" in r:
 
 
 
 
 
 
153
  links.append(
154
+ f"- [GBIF](https://www.gbif.org/species/{r['usageKey']})"
155
  )
 
156
  except:
157
  pass
158
 
 
159
  # Wikipedia
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  links.append(
161
+ f"- [Wikipedia](https://en.wikipedia.org/wiki/{clean.replace(' ','_')})"
162
  )
163
 
 
164
  # iNaturalist
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
  links.append(
166
+ f"- [iNaturalist](https://www.inaturalist.org/search?q={q})"
 
 
 
 
 
 
 
 
 
167
  )
168
 
169
+ # Scholar
170
  links.append(
171
+ f"- [Google Scholar](https://scholar.google.com/scholar?q={q}+{c})"
 
 
 
 
 
 
 
 
 
 
 
 
 
172
  )
173
 
174
  return links
175
 
176
 
177
  # =====================================================
178
+ # UI
179
  # =====================================================
180
 
181
+ country = st.text_input("Enter Country")
 
 
182
 
183
  species_type = st.selectbox(
184
  "Select Species Type",
185
+ ["Birds", "Plants", "Mammals", "Reptiles", "Fish", "Insects"]
 
 
 
 
 
 
 
186
  )
187
 
188
  # =====================================================
189
+ # MAIN
190
  # =====================================================
191
 
192
  if st.button("Search Biodiversity"):
193
 
194
+ # normalize country
 
 
 
195
  country_clean = country.strip().lower()
196
+ country_code = country_codes.get(country_clean)
 
 
 
 
 
 
 
197
 
198
  if not country:
199
+ st.warning("Enter country")
 
 
 
 
200
  st.stop()
201
 
202
  if not country_code:
203
+ st.error(f"Country '{country}' not supported")
 
 
 
 
204
  st.stop()
205
 
206
+ filter_value = species_filters[species_type]
 
 
207
 
208
+ with st.spinner("Fetching GBIF data..."):
209
+ species_list = get_species(country_code, filter_value)
 
 
 
 
 
 
 
 
 
 
210
 
211
  if not species_list:
212
+ st.warning("No clean species found")
 
 
 
 
213
  st.stop()
214
 
 
 
 
 
215
  st.subheader("🐾 Species Found")
216
 
217
  for sp in species_list:
218
+ st.write("•", sp)
219
 
220
+ # =================================================
221
+ # AI
222
+ # =================================================
 
 
 
 
223
 
224
  prompt = f"""
225
  You are a biodiversity scientist.
226
 
227
+ Country: {country}
228
+ Type: {species_type}
 
 
 
 
 
 
229
 
230
+ Species:
231
+ {chr(10).join(species_list)}
 
 
 
232
 
233
+ Only analyze real species.
 
 
234
  """
235
 
236
+ st.subheader("🤖 AI Analysis")
 
 
237
 
238
+ st.write(
239
+ call_gemini(prompt, os.getenv("GEMINI_API_KEY"))
 
 
 
 
 
240
  )
241
 
 
 
242
  # =================================================
243
+ # LINKS
244
  # =================================================
245
 
246
+ st.subheader("🔗 Links")
 
 
 
 
 
 
247
 
248
+ for sp in species_list:
249
+ st.markdown(f"### {sp}")
250
+ for link in get_species_links(sp, country):
251
+ st.markdown(link)
252
+ st.markdown("---")