Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import numpy as np | |
| import joblib | |
| from PIL import Image | |
| import base64 | |
| from io import BytesIO | |
| # Page configuration | |
| st.set_page_config( | |
| page_title="HDD Solution Predictor", | |
| page_icon="๐ง", | |
| layout="centered", | |
| initial_sidebar_state="collapsed" | |
| ) | |
| # Function to convert image to base64 | |
| def image_to_base64(image_path): | |
| try: | |
| with open(image_path, "rb") as img_file: | |
| return base64.b64encode(img_file.read()).decode() | |
| except: | |
| return None | |
| # Enhanced CSS with better styling | |
| st.markdown(""" | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'); | |
| /* Hide Streamlit branding */ | |
| #MainMenu {visibility: hidden;} | |
| footer {visibility: hidden;} | |
| header {visibility: hidden;} | |
| .stDeployButton {visibility: hidden;} | |
| /* Main container */ | |
| .main { | |
| background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |
| min-height: 100vh; | |
| padding: 2rem 1rem; | |
| } | |
| /* Logo container */ | |
| .logo-section { | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| padding: 1.5rem; | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 20px; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255,255,255,0.2); | |
| } | |
| .logo-image { | |
| max-width: 200px; | |
| height: auto; | |
| filter: drop-shadow(0 4px 8px rgba(0,0,0,0.1)); | |
| } | |
| /* Title styling */ | |
| .main-title { | |
| font-family: 'Inter', sans-serif; | |
| font-size: 2.5rem; | |
| font-weight: 700; | |
| color: white; | |
| text-align: center; | |
| margin: 1rem 0 0.5rem 0; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .subtitle { | |
| font-family: 'Inter', sans-serif; | |
| font-size: 1.1rem; | |
| color: rgba(255,255,255,0.9); | |
| text-align: center; | |
| margin-bottom: 2rem; | |
| font-weight: 400; | |
| } | |
| /* Input container */ | |
| .input-container { | |
| background: white; | |
| border-radius: 25px; | |
| padding: 2.5rem; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.1); | |
| margin: 2rem auto; | |
| max-width: 500px; | |
| border: 1px solid rgba(255,255,255,0.2); | |
| } | |
| /* Input labels - Clean and simple */ | |
| .stSelectbox label, .stSlider label { | |
| font-family: 'Inter', sans-serif !important; | |
| font-weight: 600 !important; | |
| color: #2c3e50 !important; | |
| font-size: 1rem !important; | |
| margin-bottom: 0.5rem !important; | |
| display: block !important; | |
| } | |
| /* Selectbox styling - Force dark text on light background */ | |
| .stSelectbox > div > div { | |
| background-color: #ffffff !important; | |
| border-radius: 10px !important; | |
| border: 2px solid #e1e8ff !important; | |
| font-family: 'Inter', sans-serif !important; | |
| color: #000000 !important; | |
| } | |
| /* Critical: Force dropdown text to be visible */ | |
| .stSelectbox [data-baseweb="select"] { | |
| background-color: #ffffff !important; | |
| } | |
| .stSelectbox [data-baseweb="select"] > div { | |
| background-color: #ffffff !important; | |
| color: #000000 !important; | |
| font-weight: 600 !important; | |
| } | |
| /* Target the actual button that shows selected value */ | |
| .stSelectbox [data-baseweb="select"] > div > div[role="button"] { | |
| background-color: #ffffff !important; | |
| color: #000000 !important; | |
| font-weight: 600 !important; | |
| border: 2px solid #e1e8ff !important; | |
| border-radius: 10px !important; | |
| padding: 0.75rem 1rem !important; | |
| min-height: 50px !important; | |
| } | |
| /* Force text color in the button */ | |
| .stSelectbox [data-baseweb="select"] > div > div[role="button"] > div { | |
| color: #000000 !important; | |
| font-weight: 600 !important; | |
| font-size: 1rem !important; | |
| } | |
| /* Target dropdown options when opened */ | |
| .stSelectbox [data-baseweb="select"] [data-baseweb="menu"] { | |
| background-color: #ffffff !important; | |
| border: 2px solid #e1e8ff !important; | |
| border-radius: 10px !important; | |
| box-shadow: 0 4px 12px rgba(0,0,0,0.15) !important; | |
| } | |
| .stSelectbox [data-baseweb="select"] [data-baseweb="menu"] > ul > li { | |
| background-color: #ffffff !important; | |
| color: #000000 !important; | |
| font-weight: 600 !important; | |
| padding: 0.75rem 1rem !important; | |
| } | |
| .stSelectbox [data-baseweb="select"] [data-baseweb="menu"] > ul > li:hover { | |
| background-color: #f8f9ff !important; | |
| color: #000000 !important; | |
| } | |
| /* Slider styling */ | |
| .stSlider > div > div { | |
| background-color: #f8f9ff; | |
| border-radius: 15px; | |
| padding: 1.2rem; | |
| border: 2px solid #e1e8ff; | |
| } | |
| /* Input container heading */ | |
| .input-container h3 { | |
| color: #2c3e50 !important; | |
| font-weight: 700 !important; | |
| font-family: 'Inter', sans-serif !important; | |
| margin-bottom: 1.5rem !important; | |
| } | |
| /* Button styling */ | |
| .stButton > button { | |
| background: linear-gradient(135deg, #6c5ce7, #fd79a8); | |
| color: white; | |
| border: none; | |
| border-radius: 20px; | |
| padding: 1rem 2rem; | |
| font-family: 'Inter', sans-serif; | |
| font-weight: 600; | |
| font-size: 1.1rem; | |
| box-shadow: 0 10px 25px rgba(108, 92, 231, 0.3); | |
| transition: all 0.3s ease; | |
| width: 100%; | |
| margin-top: 2rem; | |
| height: 60px; | |
| } | |
| .stButton > button:hover { | |
| transform: translateY(-3px); | |
| box-shadow: 0 15px 35px rgba(108, 92, 231, 0.4); | |
| } | |
| /* Result styling */ | |
| .result-container { | |
| margin: 2rem auto; | |
| max-width: 500px; | |
| border-radius: 25px; | |
| padding: 2.5rem; | |
| text-align: center; | |
| box-shadow: 0 20px 40px rgba(0,0,0,0.15); | |
| border: 3px solid rgba(255,255,255,0.3); | |
| } | |
| .solution-badge { | |
| display: inline-block; | |
| font-size: 4rem; | |
| font-weight: 700; | |
| color: white; | |
| background: rgba(255,255,255,0.2); | |
| border-radius: 50%; | |
| width: 100px; | |
| height: 100px; | |
| line-height: 100px; | |
| margin-bottom: 1rem; | |
| border: 4px solid rgba(255,255,255,0.3); | |
| box-shadow: 0 10px 20px rgba(0,0,0,0.1); | |
| } | |
| .solution-title { | |
| color: white; | |
| font-family: 'Inter', sans-serif; | |
| font-size: 1.5rem; | |
| font-weight: 700; | |
| margin-bottom: 0.5rem; | |
| text-shadow: 0 2px 4px rgba(0,0,0,0.1); | |
| } | |
| .solution-description { | |
| color: rgba(255,255,255,0.95); | |
| font-family: 'Inter', sans-serif; | |
| font-size: 1.1rem; | |
| font-weight: 500; | |
| line-height: 1.4; | |
| } | |
| /* Solution colors */ | |
| .solution-a { background: linear-gradient(135deg, #4CAF50, #45a049); } | |
| .solution-b { background: linear-gradient(135deg, #FF9800, #f57c00); } | |
| .solution-c { background: linear-gradient(135deg, #E91E63, #c2185b); } | |
| .solution-d { background: linear-gradient(135deg, #9C27B0, #7b1fa2); } | |
| .solution-e { background: linear-gradient(135deg, #8BC34A, #689f38); } | |
| /* Footer */ | |
| .footer { | |
| text-align: center; | |
| margin-top: 2rem; | |
| color: rgba(255,255,255,0.95); | |
| font-family: 'Inter', sans-serif; | |
| font-size: 0.9rem; | |
| text-shadow: 0 1px 2px rgba(0,0,0,0.1); | |
| } | |
| /* Responsive design */ | |
| @media (max-width: 768px) { | |
| .input-container { | |
| margin: 1rem; | |
| padding: 2rem 1.5rem; | |
| } | |
| .main-title { | |
| font-size: 2rem; | |
| } | |
| .solution-badge { | |
| width: 80px; | |
| height: 80px; | |
| line-height: 80px; | |
| font-size: 3rem; | |
| } | |
| .logo-section { | |
| margin-bottom: 1rem; | |
| padding: 1rem; | |
| } | |
| .logo-image { | |
| max-width: 150px; | |
| } | |
| } | |
| </style> | |
| """, unsafe_allow_html=True) | |
| # Load model function | |
| def load_model(): | |
| try: | |
| model = joblib.load('decision_tree_model.pkl') | |
| le_soil = joblib.load('dt_soil_encoder.pkl') | |
| le_water = joblib.load('dt_water_encoder.pkl') | |
| le_solution = joblib.load('dt_solution_encoder.pkl') | |
| return model, le_soil, le_water, le_solution | |
| except FileNotFoundError: | |
| st.error("โ ๏ธ Model files not found! Please run the training script first.") | |
| return None, None, None, None | |
| # Prediction function | |
| def predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution): | |
| try: | |
| import pandas as pd | |
| # Encode inputs | |
| soil_encoded = le_soil.transform([soil_type])[0] | |
| water_encoded = le_water.transform([high_water])[0] | |
| # Create feature DataFrame with proper column names to match training | |
| feature_data = { | |
| 'Diameter': [diameter], | |
| 'soil_encoded': [soil_encoded], | |
| 'water_encoded': [water_encoded] | |
| } | |
| features_df = pd.DataFrame(feature_data) | |
| # Make prediction | |
| prediction_encoded = model.predict(features_df)[0] | |
| prediction = le_solution.inverse_transform([prediction_encoded])[0] | |
| return prediction | |
| except Exception as e: | |
| return f"Error: {str(e)}" | |
| def main(): | |
| # Logo section | |
| st.markdown('<div class="logo-section">', unsafe_allow_html=True) | |
| # Try to display logo with base64 encoding | |
| logo_base64 = image_to_base64('logo2.e8c5ff97.png') | |
| if logo_base64: | |
| st.markdown(f''' | |
| <img src="data:image/png;base64,{logo_base64}" class="logo-image" alt="MEA Logo"> | |
| ''', unsafe_allow_html=True) | |
| else: | |
| # Fallback: Try direct image display | |
| try: | |
| st.image('logo2.e8c5ff97.png', width=200) | |
| except: | |
| st.markdown(''' | |
| <div style="text-align: center; color: rgba(255,255,255,0.8); padding: 1rem;"> | |
| <h3 style="margin: 0; font-family: 'Inter', sans-serif;">๐ข MEA</h3> | |
| <p style="margin: 0.5rem 0 0 0; font-family: 'Inter', sans-serif; font-size: 0.9rem;"> | |
| Metropolitan Electricity Authority | |
| </p> | |
| </div> | |
| ''', unsafe_allow_html=True) | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Title and subtitle | |
| st.markdown('<h1 class="main-title">๐ง HDD Solution Predictor</h1>', unsafe_allow_html=True) | |
| st.markdown('<p class="subtitle">Get instant recommendations for your drilling project</p>', unsafe_allow_html=True) | |
| # Load model | |
| model_data = load_model() | |
| if model_data[0] is None: | |
| st.stop() | |
| model, le_soil, le_water, le_solution = model_data | |
| # Input container | |
| st.markdown('<div class="input-container">', unsafe_allow_html=True) | |
| # Input controls with better spacing | |
| st.markdown("### ๐ Project Parameters") | |
| # Create two columns for better layout | |
| col1, col2 = st.columns(2) | |
| with col1: | |
| diameter = st.slider( | |
| "๐ฉ Pipe Diameter (m)", | |
| min_value=0.5, | |
| max_value=2.0, | |
| value=1.2, | |
| step=0.1, | |
| help="Select the diameter of the pipe to be installed" | |
| ) | |
| with col2: | |
| soil_type = st.selectbox( | |
| "๐๏ธ Soil Type", | |
| options=['clay', 'sand'], | |
| index=0, | |
| help="Select the predominant soil type at the drilling site" | |
| ) | |
| # Full width for water table | |
| high_water = st.selectbox( | |
| "๐ง High Water Table", | |
| options=['no', 'yes'], | |
| index=0, | |
| help="Is there a high water table present at the site?" | |
| ) | |
| # Predict button | |
| if st.button("๐ฎ Get Solution Recommendation"): | |
| prediction = predict_solution(diameter, soil_type, high_water, model, le_soil, le_water, le_solution) | |
| # Solution details | |
| solution_details = { | |
| 'A': { | |
| 'name': 'Enhanced Protection', | |
| 'description': 'Sheetpile + Trench + Grouting', | |
| 'class': 'solution-a', | |
| 'icon': '๐ก๏ธ' | |
| }, | |
| 'B': { | |
| 'name': 'Maximum Protection', | |
| 'description': 'Sheetpile + Trench + Grouting + Casing', | |
| 'class': 'solution-b', | |
| 'icon': '๐ฐ' | |
| }, | |
| 'C': { | |
| 'name': 'Moderate Protection', | |
| 'description': 'Sheetpile + Trench', | |
| 'class': 'solution-c', | |
| 'icon': '๐จ' | |
| }, | |
| 'D': { | |
| 'name': 'Basic Protection', | |
| 'description': 'Grouting Only', | |
| 'class': 'solution-d', | |
| 'icon': '๐ง' | |
| }, | |
| 'E': { | |
| 'name': 'Minimal Intervention', | |
| 'description': 'No Additional Measures', | |
| 'class': 'solution-e', | |
| 'icon': 'โ ' | |
| } | |
| } | |
| if prediction in solution_details: | |
| details = solution_details[prediction] | |
| st.markdown(f''' | |
| <div class="result-container {details['class']}"> | |
| <div class="solution-badge">{prediction}</div> | |
| <div class="solution-title">{details['name']}</div> | |
| <div class="solution-description">{details['description']}</div> | |
| </div> | |
| ''', unsafe_allow_html=True) | |
| else: | |
| st.error(f"โ Prediction error: {prediction}") | |
| st.markdown('</div>', unsafe_allow_html=True) | |
| # Footer | |
| st.markdown(''' | |
| <div class="footer"> | |
| <p>๐ก Powered by Decision Tree AI with 100% accuracy</p> | |
| <p>๐ข Metropolitan Electricity Authority (MEA)</p> | |
| </div> | |
| ''', unsafe_allow_html=True) | |
| if __name__ == "__main__": | |
| main() |