MSU576 commited on
Commit
376b30f
·
verified ·
1 Parent(s): 2d92e43

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +149 -260
app.py CHANGED
@@ -481,225 +481,27 @@ def compute_gsd_metrics(diams: List[float], passing: List[float]) -> Dict[str, f
481
 
482
 
483
 
484
- # 4) Session state initialization
485
- if "sites" not in st.session_state:
486
- # initialize with a default site
487
- st.session_state["sites"] = ({
488
- # ---------------------------
489
- # Basic Project Info
490
- # ---------------------------
491
- "Site Name": None,
492
- "Project Name": None,
493
- "Site ID": None,
494
- "Coordinates": "",
495
- "lat": None,
496
- "lon": None,
497
- "Project Description": "",
498
-
499
- # ---------------------------
500
- # Soil Recognition / Classifier
501
- # ---------------------------
502
- "Soil Class": None,
503
- "Soil Recognizer Confidence": None,
504
- "USCS": None,
505
- "AASHTO": None,
506
- "GI": None,
507
- "GSD": None, # Grain size distribution metrics
508
- "classifier_inputs": {}, # User/lab/OCR extracted inputs
509
- "classifier_decision": None, # Full text decision from classifier
510
-
511
- # ---------------------------
512
- # Site Characterization
513
- # ---------------------------
514
- "Topography": None, # manual topo entry
515
- "Drainage": None, # manual drainage notes
516
- "Current Land Use": None, # linked to environmental data
517
- "Regional Geology": None, # manual geology notes
518
-
519
- # ---------------------------
520
- # Investigations & Lab
521
- # ---------------------------
522
- "Field Investigation": [], # e.g. borehole logs
523
- "Laboratory Results": [], # lab test results
524
-
525
- # ---------------------------
526
- # Geotechnical Parameters (incl. REPORT_FIELDS)
527
- # ---------------------------
528
- "Load Bearing Capacity": None, # kPa or psf
529
- "Skin Shear Strength": None, # kPa
530
- "Relative Compaction": None, # %
531
- "Rate of Consolidation": None, # mm/yr or days
532
- "Nature of Construction": None, # text
533
- "Borehole Count": None, # number
534
- "Max Depth (m)": None, # m
535
- "SPT N (avg)": None, # blows/ft
536
- "CBR (%)": None, # %
537
- "Allowable Bearing (kPa)": None, # kPa
538
-
539
- # ---------------------------
540
- # Earth Engine Data
541
- # ---------------------------
542
- "Soil Profile": { # SoilGrids / OpenLandMap
543
- "Clay": None, # % clay
544
- "Sand": None, # % sand
545
- "Silt": None, # % silt
546
- "OrganicCarbon": None, # % organic carbon
547
- "pH": None # soil pH
548
- },
549
- "Topo Data": None, # Avg elevation
550
- "Seismic Data": None, # PGA/g
551
- "Flood Data": None, # % flood occurrence
552
- "Environmental Data": {
553
- "Landcover Stats": None, # histogram
554
- "Forest Loss": None, # optional (Hansen)
555
- "Urban Fraction": None # optional calc
556
- },
557
- "Weather Data": { # climate summaries
558
- "Rainfall": None,
559
- "Temperature": None,
560
- "Humidity": None
561
- },
562
- "Atmospheric Data": { # pollution, aerosols
563
- "AerosolOpticalDepth": None,
564
- "NO2": None,
565
- "CO": None,
566
- "PM2.5": None # from Sentinel-5P aerosol index
567
- },
568
-
569
- # ---------------------------
570
- # Map & Visualization
571
- # ---------------------------
572
- "ROI": None, # GeoJSON of ROI
573
- "roi_coords": None, # Flattened coords (lat,lon)
574
- "map_snapshot": None, # PNG snapshot
575
-
576
- # ---------------------------
577
- # AI / Reporting
578
- # ---------------------------
579
- "chat_history": [],
580
- "LLM_Report_Text": None,
581
- "report_convo_state": 0,
582
- "report_missing_fields": [],
583
- "report_answers": {}
584
- })
585
-
586
-
587
- if "active_site" not in st.session_state:
588
- st.session_state["active_site"] = 0
589
-
590
- if "llm_model" not in st.session_state:
591
- st.session_state["llm_model"] = "groq/compound"
592
-
593
- # -------------------
594
- # API Keys
595
- # -------------------
596
- GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
597
- GEM_API_KEY = os.environ.get("GEM_API")
598
- if "GEM_API" in st.secrets:
599
- GEM_API_KEY = st.secrets["GEM_API"]
600
 
601
  # -------------------
602
- # Universal LLM Call
603
  # -------------------
604
- def llm_generate(prompt: str, model: str = None, max_tokens: int = 512) -> str:
605
- """Universal LLM call for Groq, Gemini, DeepSeek."""
606
- model_name = model or st.session_state["llm_model"]
607
-
608
- try:
609
- # ------------------- GEMINI -------------------
610
- if model_name.lower().startswith("gemini"):
611
- import requests
612
- if not GEM_API_KEY:
613
- return "[LLM error: No GEM_API key found in secrets]"
614
-
615
- url = f"https://generativelanguage.googleapis.com/v1beta/models/{model_name}:generateContent?key={GEM_API_KEY}"
616
- headers = {"Content-Type": "application/json"}
617
- payload = {
618
- "contents": [{"role": "user", "parts": [{"text": prompt}]}],
619
- "generationConfig": {"temperature": 0.2, "maxOutputTokens": max_tokens}
620
- }
621
- resp = requests.post(url, headers=headers, json=payload, timeout=60)
622
- resp.raise_for_status()
623
- data = resp.json()
624
- return data["candidates"][0]["content"]["parts"][0]["text"].strip()
625
-
626
- # ------------------- GROQ / DEEPSEEK -------------------
627
- elif "groq" in model_name or "llama" in model_name or "deepseek" in model_name:
628
- from groq import Groq
629
- if not GROQ_API_KEY:
630
- return "[LLM error: No GROQ_API key found in secrets/env]"
631
-
632
- client = Groq(api_key=GROQ_API_KEY)
633
- completion = client.chat.completions.create(
634
- model=model_name,
635
- messages=[{"role": "user", "content": prompt}],
636
- temperature=0.2,
637
- max_tokens=max_tokens
638
- )
639
- return completion.choices[0].message.content
640
-
641
- else:
642
- return f"[LLM error: Unknown model {model_name}]"
643
-
644
- except Exception as e:
645
- return f"[LLM error: {e}]"
646
-
647
- # 5) UI helper: nice CSS for chat bubbles & page styling
648
- st.markdown("""
649
- <style>
650
- /* Background and card styling */
651
- body { background: #0b0b0b; color: #e9eef6; }
652
- .stApp > .main > .block-container { padding-top: 18px; }
653
- /* Landing and cards */
654
- .gm-card { background: linear-gradient(180deg, rgba(255,122,0,0.04), rgba(255,122,0,0.02)); border-radius:12px; padding:14px; border:1px solid rgba(255,122,0,0.06);}
655
- .gm-cta { background: linear-gradient(90deg,#ff7a00,#ff3a3a); color:white; padding:10px 14px; border-radius:10px; font-weight:700; }
656
- /* Chat bubbles */
657
- .chat-bot { background: #0f1720; border-left:4px solid #FF7A00; padding:10px 12px; border-radius:12px; margin:6px 0; color:#e9eef6; }
658
- .chat-user { background: #1a1f27; padding:10px 12px; border-radius:12px; margin:6px 0; color:#cfe6ff; text-align:right;}
659
- .small-muted { color:#9aa7bf; font-size:12px; }
660
- </style>
661
- """, unsafe_allow_html=True)
662
-
663
- # 6) Sidebar: navigation, site selector, model selector
664
- from streamlit_option_menu import option_menu
665
-
666
- with st.sidebar:
667
- st.markdown("<h2 style='color:#FF8C00;margin:8px 0'>GeoMate V2</h2>", unsafe_allow_html=True)
668
- # LLM model selector
669
- st.session_state["llm_model"] = st.selectbox("Select LLM model", options=[
670
- "meta-llama/llama-4-maverick-17b-128e-instruct",
671
- "llama-3.1-8b-instant",
672
- "meta-llama/llama-guard-4-12b",
673
- "llama-3.3-70b-versatile",
674
- "groq/compound",
675
- "deepseek-r1-distill-llama-70b", # ✅ Added DeepSeek
676
- "gemini-1.5-pro", # ✅ Added Gemini
677
- "gemini-1.5-flash"
678
- ], index=0)
679
- st.markdown("---")
680
-
681
- # Site management controls
682
- st.markdown("### Project Sites")
683
- site_names = [s.get("Site Name", f"Site {i}") for i,s in enumerate(st.session_state["sites"])]
684
- # Add new site input
685
- new_site_name = st.text_input("New site name", value="", key="new_site_name_input")
686
- if st.button("➕ Add / Create Site"):
687
- if new_site_name.strip() == "":
688
- st.warning("Enter a name for the new site.")
689
- elif len(st.session_state["sites"]) >= MAX_SITES:
690
- st.error(f"Maximum of {MAX_SITES} sites allowed.")
691
- else:
692
- idx = len(st.session_state["sites"])
693
- #idx = len(st.session_state["sites"]) + 1
694
- st.session_state["sites"].append({
695
- "Site Name": new_site_name.strip(),
696
- "Project Name": "Project - " + new_site_name.strip(),
697
- "Site ID": idx,
698
  "Coordinates": "",
699
  "lat": None,
700
  "lon": None,
701
  "Project Description": "",
702
-
703
  # ---------------------------
704
  # Soil Recognition / Classifier
705
  # ---------------------------
@@ -708,75 +510,75 @@ with st.sidebar:
708
  "USCS": None,
709
  "AASHTO": None,
710
  "GI": None,
711
- "GSD": None, # Grain size distribution metrics
712
- "classifier_inputs": {}, # User/lab/OCR extracted inputs
713
- "classifier_decision": None, # Full text decision from classifier
714
-
715
  # ---------------------------
716
  # Site Characterization
717
  # ---------------------------
718
- "Topography": None, # manual topo entry
719
- "Drainage": None, # manual drainage notes
720
- "Current Land Use": None, # linked to environmental data
721
- "Regional Geology": None, # manual geology notes
722
-
723
  # ---------------------------
724
  # Investigations & Lab
725
  # ---------------------------
726
- "Field Investigation": [], # e.g. borehole logs
727
- "Laboratory Results": [], # lab test results
728
-
729
  # ---------------------------
730
- # Geotechnical Parameters (incl. REPORT_FIELDS)
731
  # ---------------------------
732
- "Load Bearing Capacity": None, # kPa or psf
733
- "Skin Shear Strength": None, # kPa
734
- "Relative Compaction": None, # %
735
- "Rate of Consolidation": None, # mm/yr or days
736
- "Nature of Construction": None, # text
737
- "Borehole Count": None, # number
738
- "Max Depth (m)": None, # m
739
- "SPT N (avg)": None, # blows/ft
740
- "CBR (%)": None, # %
741
- "Allowable Bearing (kPa)": None, # kPa
742
-
743
  # ---------------------------
744
  # Earth Engine Data
745
  # ---------------------------
746
- "Soil Profile": { # SoilGrids / OpenLandMap
747
- "Clay": None, # % clay
748
- "Sand": None, # % sand
749
- "Silt": None, # % silt
750
- "OrganicCarbon": None, # % organic carbon
751
- "pH": None # soil pH
752
  },
753
- "Topo Data": None, # Avg elevation
754
- "Seismic Data": None, # PGA/g
755
- "Flood Data": None, # % flood occurrence
756
  "Environmental Data": {
757
- "Landcover Stats": None, # histogram
758
- "Forest Loss": None, # optional (Hansen)
759
- "Urban Fraction": None # optional calc
760
  },
761
- "Weather Data": { # climate summaries
762
  "Rainfall": None,
763
  "Temperature": None,
764
  "Humidity": None
765
  },
766
- "Atmospheric Data": { # pollution, aerosols
767
  "AerosolOpticalDepth": None,
768
  "NO2": None,
769
  "CO": None,
770
- "PM2.5": None # from Sentinel-5P aerosol index
771
  },
772
-
773
  # ---------------------------
774
  # Map & Visualization
775
  # ---------------------------
776
- "ROI": None, # GeoJSON of ROI
777
- "roi_coords": None, # Flattened coords (lat,lon)
778
- "map_snapshot": None, # PNG snapshot
779
-
780
  # ---------------------------
781
  # AI / Reporting
782
  # ---------------------------
@@ -785,9 +587,85 @@ with st.sidebar:
785
  "report_convo_state": 0,
786
  "report_missing_fields": [],
787
  "report_answers": {}
788
- })
 
 
 
 
 
 
 
 
 
789
 
 
 
 
 
 
790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  st.success(f"Site '{new_site_name.strip()}' created.")
792
  st.session_state["active_site"] = idx
793
  st.rerun()
@@ -795,15 +673,26 @@ with st.sidebar:
795
  # Active site selector
796
  if site_names:
797
  active_index = st.selectbox("Active Site", options=list(range(len(site_names))),
798
- format_func=lambda x: site_names[x], index=st.session_state["active_site"])
 
799
  st.session_state["active_site"] = active_index
 
800
  st.markdown("---")
801
- st.write("Active Site JSON (live)")
802
- st.json(st.session_state["sites"][st.session_state["active_site"]])
 
 
 
803
 
804
  st.markdown("---")
805
  st.markdown("© GeoMate • Advanced geotechnical copilot", unsafe_allow_html=True)
806
 
 
 
 
 
 
 
807
  # 7) Pages implementation
808
  import streamlit as st
809
  import torch
 
481
 
482
 
483
 
484
+ import streamlit as st
485
+ import os
486
+ import pandas as pd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
 
488
  # -------------------
489
+ # 1) Session state initialization
490
  # -------------------
491
+ if "sites" not in st.session_state:
492
+ st.session_state["sites"] = [
493
+ {
494
+ # ---------------------------
495
+ # Basic Project Info
496
+ # ---------------------------
497
+ "Site Name": "Default Site",
498
+ "Project Name": "Project - Default Site",
499
+ "Site ID": 0,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
500
  "Coordinates": "",
501
  "lat": None,
502
  "lon": None,
503
  "Project Description": "",
504
+
505
  # ---------------------------
506
  # Soil Recognition / Classifier
507
  # ---------------------------
 
510
  "USCS": None,
511
  "AASHTO": None,
512
  "GI": None,
513
+ "GSD": None,
514
+ "classifier_inputs": {},
515
+ "classifier_decision": None,
516
+
517
  # ---------------------------
518
  # Site Characterization
519
  # ---------------------------
520
+ "Topography": None,
521
+ "Drainage": None,
522
+ "Current Land Use": None,
523
+ "Regional Geology": None,
524
+
525
  # ---------------------------
526
  # Investigations & Lab
527
  # ---------------------------
528
+ "Field Investigation": [],
529
+ "Laboratory Results": [],
530
+
531
  # ---------------------------
532
+ # Geotechnical Parameters
533
  # ---------------------------
534
+ "Load Bearing Capacity": None,
535
+ "Skin Shear Strength": None,
536
+ "Relative Compaction": None,
537
+ "Rate of Consolidation": None,
538
+ "Nature of Construction": None,
539
+ "Borehole Count": None,
540
+ "Max Depth (m)": None,
541
+ "SPT N (avg)": None,
542
+ "CBR (%)": None,
543
+ "Allowable Bearing (kPa)": None,
544
+
545
  # ---------------------------
546
  # Earth Engine Data
547
  # ---------------------------
548
+ "Soil Profile": {
549
+ "Clay": None,
550
+ "Sand": None,
551
+ "Silt": None,
552
+ "OrganicCarbon": None,
553
+ "pH": None
554
  },
555
+ "Topo Data": None,
556
+ "Seismic Data": None,
557
+ "Flood Data": None,
558
  "Environmental Data": {
559
+ "Landcover Stats": None,
560
+ "Forest Loss": None,
561
+ "Urban Fraction": None
562
  },
563
+ "Weather Data": {
564
  "Rainfall": None,
565
  "Temperature": None,
566
  "Humidity": None
567
  },
568
+ "Atmospheric Data": {
569
  "AerosolOpticalDepth": None,
570
  "NO2": None,
571
  "CO": None,
572
+ "PM2.5": None
573
  },
574
+
575
  # ---------------------------
576
  # Map & Visualization
577
  # ---------------------------
578
+ "ROI": None,
579
+ "roi_coords": None,
580
+ "map_snapshot": None,
581
+
582
  # ---------------------------
583
  # AI / Reporting
584
  # ---------------------------
 
587
  "report_convo_state": 0,
588
  "report_missing_fields": [],
589
  "report_answers": {}
590
+ }
591
+ ]
592
+
593
+ if "active_site" not in st.session_state:
594
+ st.session_state["active_site"] = 0
595
+
596
+ if "llm_model" not in st.session_state:
597
+ st.session_state["llm_model"] = "groq/compound"
598
+
599
+ MAX_SITES = 10 # optional: maximum number of sites
600
 
601
+ # -------------------
602
+ # 2) Sidebar: Site management
603
+ # -------------------
604
+ with st.sidebar:
605
+ st.markdown("<h2 style='color:#FF8C00;margin:8px 0'>GeoMate V2</h2>", unsafe_allow_html=True)
606
 
607
+ # Site management controls
608
+ st.markdown("### Project Sites")
609
+
610
+ site_names = [s.get("Site Name", f"Site {i}") for i, s in enumerate(st.session_state["sites"])]
611
+
612
+ # Add new site
613
+ new_site_name = st.text_input("New site name", value="", key="new_site_name_input")
614
+ if st.button("➕ Add / Create Site"):
615
+ if new_site_name.strip() == "":
616
+ st.warning("Enter a name for the new site.")
617
+ elif len(st.session_state["sites"]) >= MAX_SITES:
618
+ st.error(f"Maximum of {MAX_SITES} sites allowed.")
619
+ else:
620
+ idx = len(st.session_state["sites"])
621
+ st.session_state["sites"].append({
622
+ "Site Name": new_site_name.strip(),
623
+ "Project Name": "Project - " + new_site_name.strip(),
624
+ "Site ID": idx,
625
+ "Coordinates": "",
626
+ "lat": None,
627
+ "lon": None,
628
+ "Project Description": "",
629
+ "Soil Class": None,
630
+ "Soil Recognizer Confidence": None,
631
+ "USCS": None,
632
+ "AASHTO": None,
633
+ "GI": None,
634
+ "GSD": None,
635
+ "classifier_inputs": {},
636
+ "classifier_decision": None,
637
+ "Topography": None,
638
+ "Drainage": None,
639
+ "Current Land Use": None,
640
+ "Regional Geology": None,
641
+ "Field Investigation": [],
642
+ "Laboratory Results": [],
643
+ "Load Bearing Capacity": None,
644
+ "Skin Shear Strength": None,
645
+ "Relative Compaction": None,
646
+ "Rate of Consolidation": None,
647
+ "Nature of Construction": None,
648
+ "Borehole Count": None,
649
+ "Max Depth (m)": None,
650
+ "SPT N (avg)": None,
651
+ "CBR (%)": None,
652
+ "Allowable Bearing (kPa)": None,
653
+ "Soil Profile": {"Clay": None, "Sand": None, "Silt": None, "OrganicCarbon": None, "pH": None},
654
+ "Topo Data": None,
655
+ "Seismic Data": None,
656
+ "Flood Data": None,
657
+ "Environmental Data": {"Landcover Stats": None, "Forest Loss": None, "Urban Fraction": None},
658
+ "Weather Data": {"Rainfall": None, "Temperature": None, "Humidity": None},
659
+ "Atmospheric Data": {"AerosolOpticalDepth": None, "NO2": None, "CO": None, "PM2.5": None},
660
+ "ROI": None,
661
+ "roi_coords": None,
662
+ "map_snapshot": None,
663
+ "chat_history": [],
664
+ "LLM_Report_Text": None,
665
+ "report_convo_state": 0,
666
+ "report_missing_fields": [],
667
+ "report_answers": {}
668
+ })
669
  st.success(f"Site '{new_site_name.strip()}' created.")
670
  st.session_state["active_site"] = idx
671
  st.rerun()
 
673
  # Active site selector
674
  if site_names:
675
  active_index = st.selectbox("Active Site", options=list(range(len(site_names))),
676
+ format_func=lambda x: site_names[x],
677
+ index=st.session_state["active_site"])
678
  st.session_state["active_site"] = active_index
679
+
680
  st.markdown("---")
681
+
682
+ # Display site info as table instead of JSON
683
+ active_site = st.session_state["sites"][st.session_state["active_site"]]
684
+ df = pd.DataFrame(active_site.items(), columns=["Field", "Value"])
685
+ st.dataframe(df, use_container_width=True, height=400)
686
 
687
  st.markdown("---")
688
  st.markdown("© GeoMate • Advanced geotechnical copilot", unsafe_allow_html=True)
689
 
690
+ # -------------------
691
+ # 3) Main Page Content
692
+ # -------------------
693
+ st.title("🌍 GeoMate V2 — Geotechnical Copilot")
694
+ st.write("Select a site in the sidebar to view and manage project data.")
695
+
696
  # 7) Pages implementation
697
  import streamlit as st
698
  import torch