import os import streamlit as st from streamlit_folium import st_folium import folium from app.utils import load_image, image_to_base64_jpeg, get_env from app.plant_id_client import PlantIdClient from app.location import find_observations_in_hyd, observations_to_markers, hyderabad_bbox st.set_page_config(page_title="Plant Identifier – Hyderabad Locator", page_icon="🌿", layout="wide") st.title("🌿 Plant Identifier – Hyderabad Locator") st.write("Upload a plant photo, identify it with AI, and see where it was observed in Hyderabad.") with st.sidebar: st.header("Settings") api_key = st.text_input("Plant.id API Key", value=get_env("PLANT_ID_API_KEY"), type="password") st.caption("You can also put this in a `.env` file as `PLANT_ID_API_KEY`.") st.divider() st.markdown("**Tip:** Better results with a clear leaf/flower close-up, good lighting, and neutral background.") uploaded = st.file_uploader("Upload a plant photo (JPG/PNG)", type=["jpg", "jpeg", "png"]) if uploaded and api_key: image = load_image(uploaded) st.image(image, caption="Uploaded image", use_column_width=True) b64 = image_to_base64_jpeg(image) client = PlantIdClient(api_key) with st.spinner("Identifying plant via Plant.id..."): try: result = client.identify([b64]) except Exception as e: st.error(f"Plant.id API error: {e}") st.stop() candidates = client.parse_top_candidates(result) if not candidates: st.warning("No candidates returned. Try a clearer image.") st.stop() st.subheader("Top Matches") top = None for idx, c in enumerate(candidates, start=1): with st.expander(f"{idx}. {c['scientific_name']} (confidence: {c['probability']:.2%})", expanded=(idx == 1)): if idx == 1: top = c st.write("**Scientific name:**", c["scientific_name"] or "—") st.write("**Common names:**", ", ".join(c["common_names"]) if c["common_names"] else "—") if c["wiki"]: st.write("**About:**", c["wiki"]) if c["url"]: st.write(f"[More details]({c['url']})") # Map Hyderabad occurrences for the top candidate if top and top.get("scientific_name"): st.subheader(f"📍 Observations in Hyderabad: {top['scientific_name']}") with st.spinner("Fetching observations from iNaturalist..."): try: observations = find_observations_in_hyd(top["scientific_name"], per_page=100) except Exception as e: st.error(f"iNaturalist API error: {e}") observations = [] markers = observations_to_markers(observations) swlat, swlng, nelat, nelng = hyderabad_bbox() # Create a Folium map centered roughly in Hyderabad center_lat = (swlat + nelat) / 2.0 center_lng = (swlng + nelng) / 2.0 fmap = folium.Map(location=[center_lat, center_lng], zoom_start=11) for lat, lng, o in markers: popup_lines = [] if o.get("species_guess"): popup_lines.append(f"{o['species_guess']}") if o.get("observed_on"): popup_lines.append(f"Date: {o['observed_on']}") if o.get("place_guess"): popup_lines.append(f"Place: {o['place_guess']}") popup_html = "
".join(popup_lines) if popup_lines else "iNaturalist observation" folium.Marker([lat, lng], popup=popup_html).add_to(fmap) # draw bbox folium.Rectangle( bounds=[[swlat, swlng], [nelat, nelng]], fill=False ).add_to(fmap) st_map = st_folium(fmap, width=1000, height=600) st.caption("Map shows iNaturalist community observations within a Hyderabad bounding box.") else: st.info("Enter your Plant.id API key in the sidebar and upload an image to begin.")