HVAC-03 / app /building_info_form.py
mabuseif's picture
Upload 27 files
845939b verified
"""
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"]
@staticmethod
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()