HDDprediciton / app.py
Sompote's picture
Upload app.py
447291f verified
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
@st.cache_resource
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()