File size: 11,762 Bytes
c8eb3d2
cc84d9b
 
 
f56f009
 
cc84d9b
 
 
63e1843
cc84d9b
 
c8eb3d2
cc84d9b
f56f009
cc84d9b
 
 
 
 
 
 
 
f56f009
5c1befb
f56f009
cc84d9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cd62afd
 
cc84d9b
 
 
 
 
 
5c2605b
cc84d9b
 
 
 
 
 
 
 
 
 
 
7713fc4
5c2605b
cc84d9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5c2605b
7713fc4
cc84d9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7713fc4
cc84d9b
 
 
7c447dd
7713fc4
cc84d9b
 
 
0378b80
cc84d9b
 
 
 
 
7713fc4
695d8e1
cc84d9b
afc637b
686a5a1
7c447dd
cc84d9b
cd62afd
 
686a5a1
cd62afd
 
f51e482
cc84d9b
6c6f4c1
 
5c2605b
 
 
 
6c6f4c1
 
 
 
 
 
 
 
5c2605b
6c6f4c1
 
 
5c2605b
 
 
6c6f4c1
 
 
 
5c2605b
 
 
cc84d9b
5c2605b
 
 
 
 
 
cc84d9b
5c2605b
 
cc84d9b
5c2605b
 
 
 
 
 
 
 
 
 
cc84d9b
7c447dd
 
 
 
 
 
 
 
 
 
6c6f4c1
b913a99
 
 
 
 
 
 
 
 
1b7a877
 
 
b913a99
 
 
 
 
 
 
 
7a9d437
681daed
b913a99
 
 
 
da9bc47
6a54993
da9bc47
 
 
b913a99
 
114f421
b913a99
da9bc47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681daed
da9bc47
 
 
 
 
 
 
 
114f421
7713fc4
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
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
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 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 (Ah)": 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)

    solar_generation_per_month_kWh = 0

    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}")
            
            # Automatically calculate solar generation per month
            solar_generation_per_month_kWh = results["System Size (kW)"] * 72
            st.write(f"- **Estimated Solar Generation per Month**: {solar_generation_per_month_kWh:.2f} kWh")
            
            st.markdown("### Solar Panel Layout")
            for _ in range(results["Strings in Parallel"]):
                st.text("β€”".join(["πŸ”‹"] * results["Panels in Series"]))

            # Now enable ROI calculation section after solar system calculation
            st.session_state.solar_generation = solar_generation_per_month_kWh  # Store solar generation in session state
            st.session_state.solar_system_ready = True  # Mark the solar system as ready

        else:
            st.error("Please provide valid inputs for panel rating, roof area, and inverter type.")

    # ROI Calculation Section
    if 'solar_system_ready' in st.session_state and st.session_state.solar_system_ready:
        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)

        if st.button("Calculate ROI"):
            if total_installation_cost > 0 and electricity_charges > 0 and 'solar_generation' in st.session_state:
                # Use solar generation per month from session state
                solar_generation_per_month_kWh = st.session_state.solar_generation

                # 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"- **Estimated Solar Generation per Month**: {solar_generation_per_month_kWh:.2f} kWh")
                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.")
    else:
        st.warning("Please calculate the solar system first to proceed with the ROI calculation.")

if __name__ == "__main__":
    main()