Spaces:
AZK53
/
Sleeping

AI_S1 / app.py
AZK53's picture
Update app.py
114f421 verified
import streamlit as st
import fitz # PyMuPDF
from PIL import Image
import pytesseract
import folium
from streamlit_folium import st_folium
import re
import tempfile
import requests
# Helper Functions
def extract_pdf_text(file):
"""Extract text from PDF."""
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
temp_file.write(file.read())
temp_path = temp_file.name
doc = fitz.open(temp_path)
text = ""
for page in doc:
text += page.get_text()
return text
def extract_image_text(file):
"""Extract text from image."""
img = Image.open(file)
return pytesseract.image_to_string(img)
def extract_sanction_load_from_text(text):
"""Extract sanction load from the bill text."""
match = re.search(r"load[:\-]?\s*(\d+)", text, re.IGNORECASE)
return int(match.group(1)) if match else None
def extract_location_from_pdf(text):
"""Extract location from the second line of the PDF text."""
lines = text.split('\n')
if len(lines) >= 2:
location = lines[1].strip()
return location
return None
def clean_address(address):
"""Clean the extracted address text to remove unnecessary characters."""
if address:
address = address.replace('\n', ' ').strip()
address = re.sub(r'\s+', ' ', address) # Replace multiple spaces with one
return address
return None
def extract_values_from_lines(text, start_line=53, end_line=60):
"""Extract numeric values from a specified range of lines in the text."""
lines = text.split('\n')
# Extract lines 53 to 60 (0-indexed, so 52 to 59 in the list)
relevant_lines = lines[start_line-1:end_line]
values = []
for line in relevant_lines:
match = re.search(r"(\d+\.?\d*)", line) # Match numeric values (including decimals)
if match:
values.append(float(match.group(1))) # Convert matched value to float
return values
def get_suggested_solar_size(values):
"""Suggest the solar size based on the maximum value found."""
if values:
max_value = max(values)
# Divide the maximum value by 360 to get the suggested solar size in kW
suggested_size = max_value / 360
return round(suggested_size, 2) # Round to 2 decimal places for kW
return None
def geocode_address_nominatim(address):
"""Convert address to latitude and longitude using Nominatim API."""
if not address:
return None, None
url = f"https://nominatim.openstreetmap.org/search?format=json&q={address}"
headers = {'User-Agent': 'EasySun-Solar-Planner/1.0 (your-email@example.com)'}
try:
response = requests.get(url, headers=headers)
if response.status_code != 200:
st.error(f"Failed to get a valid response from Nominatim API. Status Code: {response.status_code}")
return None, None
result = response.json()
if result:
lat = float(result[0]['lat'])
lon = float(result[0]['lon'])
st.write(f"Found location: Latitude {lat}, Longitude {lon}")
return lat, lon
else:
st.error(f"No results found for address: {address}")
return None, None
except requests.exceptions.RequestException as e:
st.error(f"Error connecting to Nominatim API: {e}")
return None, None
def calculate_solar_system(area_sqft, panel_rating, inverter_type, backup_time=0):
"""Calculate solar system size and components for the Pakistan region."""
panel_area_sqft = 20
system_capacity_kw = (panel_rating / 1000) * (area_sqft / panel_area_sqft) # Convert panel rating to kW
num_panels = area_sqft // panel_area_sqft
inverter_type = inverter_type.lower()
# Default parameters
battery_capacity = 0
cable_length = system_capacity_kw * 1.5
breaker_rating = system_capacity_kw * 1.25
earthing = system_capacity_kw * 0.75
# Inverter type based logic
if inverter_type == "off-grid" or inverter_type == "hybrid":
# Calculate battery capacity based on backup time (for Off-Grid and Hybrid only)
if backup_time > 0:
battery_capacity = system_capacity_kw * backup_time # Assuming 1 hour of battery per kW system size
else:
st.warning("Please provide backup time for Off-Grid or Hybrid inverter type.")
inverter_type = "Hybrid" if inverter_type == "hybrid" else "Off-Grid" if inverter_type == "off-grid" else "On-Grid"
# Panels in series and strings in parallel calculations
panels_in_series = 3
strings_in_parallel = num_panels // panels_in_series
return {
"System Size (kW)": round(system_capacity_kw, 2),
"Number of Panels": round(num_panels),
"Inverter Type": inverter_type.capitalize(),
"Battery Capacity (kWh)": round(battery_capacity, 2),
"Cable Length (meters)": round(cable_length, 2),
"Breaker Rating (Amps)": round(breaker_rating, 2),
"Earthing (meters)": round(earthing, 2),
"Panels in Series": panels_in_series,
"Strings in Parallel": strings_in_parallel,
}
# Streamlit UI
def main():
st.set_page_config(page_title="๐ŸŒž EasySun: Solar Planner for All", layout="wide")
# Header Section
st.markdown(
"""
<h1 style="text-align:center; color: #4CAF50;">๐ŸŒž EasySun: AI Solar Planner for All</h1>
<p style="text-align:center; font-size: 18px;">Simplified Solar Planning for Pakistan</p>
""", unsafe_allow_html=True)
# File Upload Section
st.markdown("### Step 1: Upload Your Electricity Bill")
uploaded_file = st.file_uploader("Upload your electricity bill (PDF/JPG)", type=["pdf", "jpg", "jpeg"])
sanction_load = None
location = None
city = None
lat, lon = None, None
if uploaded_file:
with st.spinner("Extracting information from the uploaded file..."):
if uploaded_file.type == "application/pdf":
bill_text = extract_pdf_text(uploaded_file)
else:
bill_text = extract_image_text(uploaded_file)
st.text_area("Extracted Text", bill_text)
sanction_load = extract_sanction_load_from_text(bill_text)
if sanction_load:
st.success(f"Sanction Load Extracted: {sanction_load} kW")
else:
st.error("Unable to extract sanction load.")
location = extract_location_from_pdf(bill_text)
if location:
location = clean_address(location)
st.success(f"Location Extracted: {location}")
else:
st.error("Unable to extract location.")
# City Input Section
st.markdown("### Step 2: Enter the City Information")
city = st.text_input("Enter the city of your location (required):")
if not city:
st.warning("City is required to proceed.")
return
# Full Address Generation
full_address = f"{location}, {city}, Pakistan" if location else f"{city}, Pakistan"
# Geocode Address Section
st.markdown("### Step 3: Geolocation of Your Address")
lat, lon = geocode_address_nominatim(full_address)
if lat and lon:
st.success(f"Location Found: Latitude {lat}, Longitude {lon}")
map_ = folium.Map(location=[lat, lon], zoom_start=12)
folium.Marker([lat, lon], popup=f"Location: {full_address}").add_to(map_)
st_folium(map_, width=700, height=500)
else:
st.error(f"Unable to find the location: {full_address}.")
# Extract Values and Suggested Solar Size Section
st.markdown("### Step 4: Extract Solar Size from Bill")
values = extract_values_from_lines(bill_text, start_line=53, end_line=60)
if values:
suggested_solar_size = get_suggested_solar_size(values)
if suggested_solar_size:
st.success(f"Suggested Solar System Size as per Utilization: {suggested_solar_size} kW")
else:
st.error("Unable to determine suggested solar size from the extracted values.")
else:
st.error("No numeric values found in lines 53-60.")
# Solar System Calculation Section
st.markdown("### Step 5: Solar System Calculation")
panel_rating = st.number_input("Enter Panel Rating (in W):", min_value=1, step=1)
inverter_type = st.selectbox("Select Inverter Type", ["On-Grid", "Off-Grid", "Hybrid"])
backup_time = 0
if inverter_type in ["Off-Grid", "Hybrid"]:
backup_time = st.number_input("Enter Backup Time (hours):", min_value=1, step=1)
area_sqft = st.number_input("Enter roof area (square feet):", min_value=1, step=1)
if st.button("Calculate Solar System"):
if area_sqft > 0 and panel_rating > 0:
results = calculate_solar_system(area_sqft, panel_rating, inverter_type, backup_time)
st.write("### Solar System Sizing Results")
for key, value in results.items():
st.write(f"- **{key}**: {value}")
st.markdown("### Solar Panel Layout")
for _ in range(results["Strings in Parallel"]):
st.text("โ€”".join(["๐Ÿ”‹"] * results["Panels in Series"]))
else:
st.error("Please provide valid inputs for panel rating, roof area, and inverter type.")
# ROI Calculation Section
st.markdown("### Step 6: ROI Calculation")
total_installation_cost = st.number_input("Enter total installation cost (in PKR):", min_value=1, step=1000)
electricity_charges = st.number_input("Enter electricity charges (in PKR per kWh):", min_value=1, step=1)
# Assuming monthly solar generation (this value can be adjusted based on location or other factors)
solar_generation_per_month_kWh = st.number_input("Enter solar generation per month (in kWh):", min_value=1, step=10)
if st.button("Calculate ROI"):
if total_installation_cost > 0 and electricity_charges > 0 and solar_generation_per_month_kWh > 0:
# Calculate monthly savings (based on solar generation and electricity charges)
monthly_savings = solar_generation_per_month_kWh * electricity_charges
# Calculate the ROI in months and years
roi_months = total_installation_cost / monthly_savings
roi_years = roi_months / 12
st.write("### ROI Calculation Results")
st.write(f"- **Monthly Savings**: {monthly_savings} PKR")
st.write(f"- **ROI (Months)**: {roi_months:.2f} months")
st.write(f"- **ROI (Years)**: {roi_years:.2f} years")
else:
st.error("Please provide valid inputs for installation cost, electricity charges, and solar generation.")
if __name__ == "__main__":
main()