Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| import ezdxf | |
| import tempfile | |
| import pandas as pd | |
| import os | |
| # ---------- Function to extract data from DXF ---------- | |
| def extract_entities(uploaded_file): | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".dxf") as tmp: | |
| tmp.write(uploaded_file.read()) | |
| tmp_path = tmp.name | |
| doc = ezdxf.readfile(tmp_path) | |
| msp = doc.modelspace() | |
| plan_data = {"rooms": [], "doors": [], "windows": []} | |
| section_data = {"slabs": [], "beams": [], "columns": []} | |
| for e in msp: | |
| if e.dxftype() == "LWPOLYLINE" and "plan" in e.dxf.layer.lower(): | |
| points = e.get_points() | |
| plan_data["rooms"].append({"points": points}) | |
| elif e.dxftype() == "LINE" and "door" in e.dxf.layer.lower(): | |
| plan_data["doors"].append({"start": e.dxf.start, "end": e.dxf.end}) | |
| elif e.dxftype() == "LINE" and "window" in e.dxf.layer.lower(): | |
| plan_data["windows"].append({"start": e.dxf.start, "end": e.dxf.end}) | |
| elif "section" in e.dxf.layer.lower(): | |
| if "slab" in e.dxf.layer.lower(): | |
| section_data["slabs"].append(e) | |
| elif "beam" in e.dxf.layer.lower(): | |
| section_data["beams"].append(e) | |
| elif "column" in e.dxf.layer.lower(): | |
| section_data["columns"].append(e) | |
| return plan_data, section_data | |
| # ---------- Function to estimate quantities ---------- | |
| def estimate_quantities(plan_data, section_data): | |
| items = [] | |
| # Assumptions (typical values based on AU construction norms) | |
| wall_height_ft = 10 | |
| wall_thickness_in = 9 | |
| brick_volume_in3 = 9 * 4.5 * 3 | |
| concrete_mix_ratio = {'cement': 1, 'sand': 1.5, 'crush': 3} | |
| dry_volume_factor = 1.54 | |
| cement_bag_volume_cuft = 1.25 # 1 bag = 1.25 cuft of cement | |
| # === 1. Brick masonry estimation === | |
| for room in plan_data["rooms"]: | |
| points = room["points"] | |
| if len(points) < 2: | |
| continue | |
| perimeter = 0 | |
| for i in range(len(points)): | |
| x1, y1 = points[i][0:2] | |
| x2, y2 = points[(i+1) % len(points)][0:2] | |
| dist = ((x2 - x1)**2 + (y2 - y1)**2)**0.5 | |
| perimeter += dist | |
| height_in = wall_height_ft * 12 | |
| wall_volume_in3 = perimeter * height_in * wall_thickness_in | |
| wall_volume_cft = wall_volume_in3 / 1728 | |
| num_bricks = wall_volume_in3 / brick_volume_in3 | |
| items.append({ | |
| "Item": "Brick Masonry", | |
| "Length": round(perimeter, 2), | |
| "Width": wall_thickness_in, | |
| "Height": height_in, | |
| "Quantity": round(wall_volume_cft, 2), | |
| "Unit": "CFT" | |
| }) | |
| items.append({ | |
| "Item": "Bricks", | |
| "Length": "-", | |
| "Width": "-", | |
| "Height": "-", | |
| "Quantity": round(num_bricks), | |
| "Unit": "No" | |
| }) | |
| # === 2. Concrete works (slabs, beams, columns) === | |
| concrete_items = section_data["slabs"] + section_data["beams"] + section_data["columns"] | |
| for component in concrete_items: | |
| # Dummy default dimensions (can later be detected from DXF entities) | |
| length = 12 # ft | |
| width = 1 # ft | |
| depth = 0.5 # ft | |
| volume = length * width * depth | |
| dry_volume = volume * dry_volume_factor | |
| total_parts = sum(concrete_mix_ratio.values()) | |
| cement_cuft = dry_volume * (concrete_mix_ratio['cement'] / total_parts) | |
| cement_bags = cement_cuft / cement_bag_volume_cuft | |
| sand_cuft = dry_volume * (concrete_mix_ratio['sand'] / total_parts) | |
| crush_cuft = dry_volume * (concrete_mix_ratio['crush'] / total_parts) | |
| steel_kg = volume * 80 # approx 80 kg/m3 (AU rule of thumb) | |
| items.append({ | |
| "Item": "Concrete", | |
| "Length": length, | |
| "Width": width, | |
| "Height": depth, | |
| "Quantity": round(volume, 2), | |
| "Unit": "CFT" | |
| }) | |
| items.append({ | |
| "Item": "Cement", | |
| "Length": "-", | |
| "Width": "-", | |
| "Height": "-", | |
| "Quantity": round(cement_bags, 1), | |
| "Unit": "Bags" | |
| }) | |
| items.append({ | |
| "Item": "Sand", | |
| "Length": "-", | |
| "Width": "-", | |
| "Height": "-", | |
| "Quantity": round(sand_cuft, 1), | |
| "Unit": "CFT" | |
| }) | |
| items.append({ | |
| "Item": "Crush", | |
| "Length": "-", | |
| "Width": "-", | |
| "Height": "-", | |
| "Quantity": round(crush_cuft, 1), | |
| "Unit": "CFT" | |
| }) | |
| items.append({ | |
| "Item": "Steel", | |
| "Length": "-", | |
| "Width": "-", | |
| "Height": "-", | |
| "Quantity": round(steel_kg, 1), | |
| "Unit": "kg" | |
| }) | |
| df = pd.DataFrame(items) | |
| return df | |
| # ---------- Streamlit App UI ---------- | |
| st.set_page_config(layout="centered") | |
| st.title("🏗️ AutoCAD Drawing Estimator (AU Standards)") | |
| uploaded_file = st.file_uploader("Upload AutoCAD DXF File", type=["dxf"]) | |
| if uploaded_file: | |
| with st.spinner("Reading and analyzing the drawing..."): | |
| try: | |
| plan_data, section_data = extract_entities(uploaded_file) | |
| df = estimate_quantities(plan_data, section_data) | |
| st.success("✅ Drawing processed and estimate generated.") | |
| st.dataframe(df) | |
| output_path = "Construction_Estimate.xlsx" | |
| df.to_excel(output_path, index=False) | |
| with open(output_path, "rb") as f: | |
| st.download_button("📥 Download Estimate (Excel)", f, file_name=output_path) | |
| except Exception as e: | |
| st.error(f"Error processing file: {e}") | |