Elemente-Uebersicht: SIA-416 Bilanz pro Geschoss
Aggregiert alle raum_outline-Flaechen nach raum_sia-Klassifikation
(hnf/nnf/vf/ff) und zeigt sie als kompakte Mini-Tabelle direkt unter
dem Geschoss-Header in der Project-Browser-Uebersicht.
Backend (_build_overview):
- Neuer Returnschluessel siaBilanz: {geschossId: {hnf, nnf, vf, ff,
ohne, nf, total, count}} in m^2
- NF = HNF + NNF (Nutzflaeche nach SIA 416)
- Raeume ohne SIA-Tag landen in "ohne"
Frontend (ElementeUebersichtApp):
- Direkt unter dem Geschoss-Header eine Inline-Tabelle mit nur den
Klassen die > 0 sind (kein Spam wenn nichts klassifiziert ist)
- NF separat hervorgehoben (Accent-Farbe) als wichtigste Kennzahl
- Read-only, aktualisiert sich mit jedem state-emit (Raum-Aenderung,
SIA-Tag setzen, neue Raeume) automatisch
This commit is contained in:
@@ -116,7 +116,36 @@ def _build_overview(doc):
|
|||||||
out_geschosse.append({
|
out_geschosse.append({
|
||||||
"id": "__keingeschoss__", "name": "(kein Geschoss)", "okff": None,
|
"id": "__keingeschoss__", "name": "(kein Geschoss)", "okff": None,
|
||||||
})
|
})
|
||||||
return {"geschosse": out_geschosse, "items": items}
|
|
||||||
|
# SIA-416 Bilanz pro Geschoss: aggregiert alle raum_outline-Flaechen
|
||||||
|
# nach raum_sia-Klassifikation. Räume ohne SIA-Tag landen in "ohne".
|
||||||
|
# NF = HNF + NNF (Nutzflaeche). Wird im Frontend als Tabelle gerendert.
|
||||||
|
sia_bilanz = {} # {geschossId: {hnf, nnf, vf, ff, ohne, nf, total, count}}
|
||||||
|
for obj in doc.Objects:
|
||||||
|
meta = _elm._read_meta(obj)
|
||||||
|
if meta is None: continue
|
||||||
|
if meta.get("type") != "raum_outline": continue
|
||||||
|
try:
|
||||||
|
area, _, _ = _elm._raum_amp(obj.Geometry)
|
||||||
|
except Exception: continue
|
||||||
|
if not area or area <= 0: continue
|
||||||
|
g_id = meta.get("geschoss") or "__keingeschoss__"
|
||||||
|
sia = (meta.get("raum_sia") or "").lower()
|
||||||
|
if sia not in ("hnf", "nnf", "vf", "ff"):
|
||||||
|
sia = "ohne"
|
||||||
|
b = sia_bilanz.setdefault(g_id, {
|
||||||
|
"hnf": 0.0, "nnf": 0.0, "vf": 0.0, "ff": 0.0,
|
||||||
|
"ohne": 0.0, "count": 0,
|
||||||
|
})
|
||||||
|
b[sia] += float(area)
|
||||||
|
b["count"] += 1
|
||||||
|
# NF + Total ableiten
|
||||||
|
for b in sia_bilanz.values():
|
||||||
|
b["nf"] = b["hnf"] + b["nnf"]
|
||||||
|
b["total"] = b["hnf"] + b["nnf"] + b["vf"] + b["ff"] + b["ohne"]
|
||||||
|
|
||||||
|
return {"geschosse": out_geschosse, "items": items,
|
||||||
|
"siaBilanz": sia_bilanz}
|
||||||
|
|
||||||
|
|
||||||
class ElementeUebersichtBridge(panel_base.BaseBridge):
|
class ElementeUebersichtBridge(panel_base.BaseBridge):
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ export default function ElementeUebersichtApp() {
|
|||||||
|
|
||||||
const items = state.items || []
|
const items = state.items || []
|
||||||
const geschosse = state.geschosse || []
|
const geschosse = state.geschosse || []
|
||||||
|
const siaBilanz = state.siaBilanz || {}
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
let r = items
|
let r = items
|
||||||
@@ -163,6 +164,7 @@ export default function ElementeUebersichtApp() {
|
|||||||
const total = Object.values(groupForG).reduce((s, arr) => s + arr.length, 0)
|
const total = Object.values(groupForG).reduce((s, arr) => s + arr.length, 0)
|
||||||
if (total === 0) return null
|
if (total === 0) return null
|
||||||
const gOpen = expanded[g.id] !== false // default: open
|
const gOpen = expanded[g.id] !== false // default: open
|
||||||
|
const bilanz = siaBilanz[g.id]
|
||||||
return (
|
return (
|
||||||
<div key={g.id}>
|
<div key={g.id}>
|
||||||
<div onClick={() => toggle(g.id)}
|
<div onClick={() => toggle(g.id)}
|
||||||
@@ -192,6 +194,51 @@ export default function ElementeUebersichtApp() {
|
|||||||
fontFamily: 'DM Mono, monospace',
|
fontFamily: 'DM Mono, monospace',
|
||||||
}}>{total}</span>
|
}}>{total}</span>
|
||||||
</div>
|
</div>
|
||||||
|
{/* SIA-416 Bilanz pro Geschoss — read-only Mini-Tabelle,
|
||||||
|
nur angezeigt wenn mindestens ein klassifizierter Raum */}
|
||||||
|
{gOpen && bilanz && (bilanz.hnf + bilanz.nnf + bilanz.vf + bilanz.ff) > 0 && (
|
||||||
|
<div style={{
|
||||||
|
display: 'flex', flexWrap: 'wrap', gap: 10,
|
||||||
|
padding: '4px 14px 6px',
|
||||||
|
background: 'var(--bg-section)',
|
||||||
|
borderBottom: '1px solid var(--border-light)',
|
||||||
|
fontSize: 9, fontFamily: 'DM Mono, monospace',
|
||||||
|
color: 'var(--text-muted)',
|
||||||
|
}}>
|
||||||
|
{bilanz.hnf > 0 && (
|
||||||
|
<span title="Hauptnutzflaeche (SIA 416)">
|
||||||
|
HNF <strong style={{color:'var(--text-primary)'}}>{bilanz.hnf.toFixed(1)}</strong> m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{bilanz.nnf > 0 && (
|
||||||
|
<span title="Nebennutzflaeche">
|
||||||
|
NNF <strong style={{color:'var(--text-primary)'}}>{bilanz.nnf.toFixed(1)}</strong> m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{bilanz.vf > 0 && (
|
||||||
|
<span title="Verkehrsflaeche">
|
||||||
|
VF <strong style={{color:'var(--text-primary)'}}>{bilanz.vf.toFixed(1)}</strong> m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{bilanz.ff > 0 && (
|
||||||
|
<span title="Funktionsflaeche">
|
||||||
|
FF <strong style={{color:'var(--text-primary)'}}>{bilanz.ff.toFixed(1)}</strong> m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{bilanz.nf > 0 && (
|
||||||
|
<span title="Nutzflaeche = HNF + NNF"
|
||||||
|
style={{ paddingLeft: 4, borderLeft: '1px solid var(--border)' }}>
|
||||||
|
NF <strong style={{color:'var(--accent)'}}>{bilanz.nf.toFixed(1)}</strong> m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
{bilanz.ohne > 0 && (
|
||||||
|
<span title="Raeume ohne SIA-Klassifikation"
|
||||||
|
style={{ opacity: 0.6 }}>
|
||||||
|
— {bilanz.ohne.toFixed(1)} m²
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{gOpen && KIND_ORDER.map(k => {
|
{gOpen && KIND_ORDER.map(k => {
|
||||||
const arr = groupForG[k]
|
const arr = groupForG[k]
|
||||||
if (!arr || arr.length === 0) return null
|
if (!arr || arr.length === 0) return null
|
||||||
|
|||||||
Reference in New Issue
Block a user