Spaces:
Sleeping
Sleeping
""" | |
Building information input form for HVAC Load Calculator. | |
This module provides the UI components for entering building information. | |
Author: Dr Majed Abuseif | |
Date: March 2025 | |
Version: 1.0.0 | |
""" | |
import streamlit as st | |
import pandas as pd | |
import numpy as np | |
import pycountry | |
from typing import Dict, List, Any, Optional, Tuple | |
import os | |
# Import data models | |
from data.building_components import Orientation, ComponentType | |
class BuildingInfoForm: | |
"""Class for building information input form.""" | |
def __init__(self): | |
"""Initialize the building information form.""" | |
self.countries = sorted([country.name for country in pycountry.countries]) | |
def display(self): | |
"""Display the building information form.""" | |
self.display_building_info_form(st.session_state) | |
def display_building_info_form(self, session_state: Dict[str, Any]) -> None: | |
"""Display building information input form in Streamlit.""" | |
st.header("Building Information") | |
if "building_info" not in session_state: | |
session_state["building_info"] = { | |
"project_name": "", | |
"building_name": "", | |
"country": "", | |
"city": "", | |
"building_type": "", | |
"floor_area": 0.0, | |
"width": 0.0, | |
"depth": 0.0, | |
"building_height": 3.0, | |
"orientation": "NORTH", | |
"operating_hours": "8:00-18:00" | |
} | |
default_values = { | |
"project_name": "", | |
"building_name": "", | |
"country": "", | |
"city": "", | |
"building_type": "", | |
"floor_area": 0.0, | |
"width": 0.0, | |
"depth": 0.0, | |
"building_height": 3.0, | |
"orientation": "NORTH", | |
"operating_hours": "8:00-18:00" | |
} | |
for key, default_value in default_values.items(): | |
if key not in session_state["building_info"]: | |
session_state["building_info"][key] = default_value | |
if "data_saved" not in session_state: | |
session_state["data_saved"] = False | |
with st.form(key="building_info_form"): | |
st.subheader("Project Information") | |
col1, col2 = st.columns(2) | |
with col1: | |
session_state["building_info"]["project_name"] = st.text_input( | |
"Project Name", | |
value=session_state["building_info"]["project_name"], | |
help="Enter the project's identification name" | |
) | |
session_state["building_info"]["building_name"] = st.text_input( | |
"Building Name", | |
value=session_state["building_info"]["building_name"], | |
help="Enter the building's identification name" | |
) | |
with col2: | |
session_state["building_info"]["country"] = st.selectbox( | |
"Country", | |
options=[""] + self.countries, | |
index=0 if not session_state["building_info"]["country"] else | |
self.countries.index(session_state["building_info"]["country"]) + 1, | |
help="Select the building's country location" | |
) | |
session_state["building_info"]["city"] = st.text_input( | |
"City", | |
value=session_state["building_info"]["city"], | |
help="Enter the building's city location" | |
) | |
st.subheader("Building Characteristics") | |
col1, col2 = st.columns(2) | |
with col1: | |
session_state["building_info"]["building_type"] = st.selectbox( | |
"Building Type", | |
["Residential", "Office", "Retail", "Educational", "Healthcare", "Industrial", "Other"], | |
index=1 if session_state["building_info"]["building_type"] == "" else | |
["Residential", "Office", "Retail", "Educational", "Healthcare", "Industrial", "Other"].index(session_state["building_info"]["building_type"]), | |
help="Select the building's purpose or usage type" | |
) | |
with col2: | |
session_state["building_info"]["building_height"] = st.number_input( | |
"Building Height (m)", | |
min_value=2.0, | |
max_value=1000.0, | |
value=float(session_state["building_info"]["building_height"]), | |
step=0.1, | |
help="Enter the total height of the building in meters" | |
) | |
st.subheader("Building Dimensions") | |
session_state["building_info"]["floor_area"] = st.number_input( | |
"Total Floor Area (m²)", | |
min_value=0.0, | |
value=float(session_state["building_info"]["floor_area"]), | |
step=10.0, | |
help="Enter the total floor area of the building in square meters (optional if width and depth provided)" | |
) | |
# Center the OR using columns | |
col1, col2, col3 = st.columns([2, 1, 2]) | |
with col2: | |
st.markdown("Enter the total floor area above OR the building width and depth below") | |
col1, col2 = st.columns(2) | |
with col1: | |
session_state["building_info"]["width"] = st.number_input( | |
"Width (m)", | |
min_value=0.0, | |
value=float(session_state["building_info"]["width"]), | |
step=1.0, | |
help="Enter the building's width in meters (optional if area provided)" | |
) | |
with col2: | |
session_state["building_info"]["depth"] = st.number_input( | |
"Depth (m)", | |
min_value=0.0, | |
value=float(session_state["building_info"]["depth"]), | |
step=1.0, | |
help="Enter the building's depth in meters (optional if area provided)" | |
) | |
st.subheader("Building Orientation") | |
session_state["building_info"]["orientation"] = st.selectbox( | |
"Building Orientation", | |
["NORTH", "NORTHEAST", "EAST", "SOUTHEAST", "SOUTH", "SOUTHWEST", "WEST", "NORTHWEST"], | |
index=["NORTH", "NORTHEAST", "EAST", "SOUTHEAST", "SOUTH", "SOUTHWEST", "WEST", "NORTHWEST"].index(session_state["building_info"]["orientation"]), | |
help="Select the direction of the building's main facade" | |
) | |
st.subheader("Operating Hours") | |
session_state["building_info"]["operating_hours"] = st.text_input( | |
"Operating Hours", | |
value=session_state["building_info"]["operating_hours"], | |
help="Enter the building's daily operating hours (e.g., 8:00-18:00)" | |
) | |
submitted = st.form_submit_button("Save Building Information") | |
if submitted: | |
valid, errors = self.validate_building_info(session_state["building_info"]) | |
if not valid: | |
for error in errors: | |
st.error(error) | |
else: | |
if session_state["building_info"]["width"] > 0 and session_state["building_info"]["depth"] > 0: | |
calculated_area = session_state["building_info"]["width"] * session_state["building_info"]["depth"] | |
if session_state["building_info"]["floor_area"] == 0: | |
session_state["building_info"]["floor_area"] = calculated_area | |
total_volume = session_state["building_info"]["floor_area"] * session_state["building_info"]["building_height"] | |
session_state["save_results"] = { | |
"success": "Building information saved successfully!", | |
"area": f"Total Floor Area: {session_state['building_info']['floor_area']:.1f} m²", | |
"volume": f"Total Building Volume: {total_volume:.1f} m³" | |
} | |
session_state["data_saved"] = True | |
# Display results if they exist | |
if "save_results" in session_state and session_state["data_saved"]: | |
st.success(session_state["save_results"]["success"]) | |
st.info(session_state["save_results"]["area"]) | |
st.info(session_state["save_results"]["volume"]) | |
# Proceed button with immediate navigation | |
if session_state["data_saved"]: | |
if st.button("Proceed to Climate Data"): | |
session_state["page"] = "Climate Data" | |
session_state["data_saved"] = False | |
if "save_results" in session_state: | |
del session_state["save_results"] | |
def validate_building_info(building_info: Dict[str, Any]) -> Tuple[bool, List[str]]: | |
"""Validate building information.""" | |
valid = True | |
errors = [] | |
required_fields = ["project_name", "building_name", "country", "city", "building_type"] | |
for field in required_fields: | |
if field not in building_info or not building_info[field]: | |
valid = False | |
errors.append(f"Missing required field: {field}") | |
if building_info.get("floor_area", 0) <= 0 and (building_info.get("width", 0) <= 0 or building_info.get("depth", 0) <= 0): | |
valid = False | |
errors.append("Must provide either floor area or both width and depth dimensions") | |
if building_info.get("building_height", 0) <= 0: | |
valid = False | |
errors.append("Building height must be greater than zero") | |
return valid, errors | |
if __name__ == "__main__": | |
form = BuildingInfoForm() | |
form.display() |