rishiproject / app /streamlit_app.py
gaurinath's picture
Upload 8 files
41be7ed verified
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"<b>{o['species_guess']}</b>")
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 = "<br/>".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.")