Architecture¶
The Building Configurator is a single-page React application. All application state lives in one component (BuildingConfigurator), which renders one of two workspace layers depending on the user's current view.
Two-layer structure¶
The diagram shows how the app shell hands state to the two layers.
graph TD
App["App<br>(map shell + data seeding)"]
BC["BuildingConfigurator<br>State owner"]
OV["Overview layer<br>workspaceView = overview"]
CF["Configure layer<br>workspaceView = configure"]
App --> BC
BC --> OV
BC --> CF
The header toggle (Overview ↔ Configure) switches workspaceView in BuildingConfigurator. Both layers read from the same state; neither owns its own copy.
Overview layer¶
Displayed when workspaceView === 'overview'. The diagram shows its two fixed columns and the components in each.
graph TD
BC["BuildingConfigurator"]
subgraph OV["Overview layout, grid-cols: 430px | flex"]
BSA["BuildingSnapshotAside<br>(left column)"]
EEC["EnergyEnvelopeColumn<br>(right column)"]
BSA --> N1["Data quality notice"]
BSA --> N2["Energy hero<br>Heating / Electricity / Hot Water / Thermal efficiency"]
BSA --> N3["Building parameters table<br>snapshotRows (type, area, U-value, storeys …)"]
EEC --> LPV["LoadProfileViewer<br>Recharts line chart, hourly to monthly"]
EEC --> ECS["ElementCompositionSection<br>Envelope accordion per surface group"]
EEC --> TS["TechnologiesSection<br>one card per visible TECH_REGISTRY entry"]
end
BC --> OV
Data flowing into Overview¶
| Prop | Source | Destination |
|---|---|---|
energyTotals |
computeEnergyTotals(timeseries, thermalSummary) |
Energy hero numbers |
snapshotRows |
buildSnapshotRows(general, elements) |
Parameters table |
thermalRating |
getThermalRating(avgUValue) |
Thermal efficiency badge |
installedTechIds |
otherTechIds + batteryConfig.installed |
Technology cards |
pvSummary |
derived from surfacePvConfigs |
Solar PV card |
initialTimeseries |
buildingData.thematic.timeseries |
Load profile chart |
elements |
surface state | Envelope composition |
Configure layer¶
Displayed when workspaceView === 'configure'. The diagram shows its two columns, the right one divided into a centre panel and a selector column.
graph TD
BC["BuildingConfigurator"]
subgraph CF["Configure layout, grid-cols: 430px | flex"]
LA["Left aside"]
RS["Right section"]
LA --> BV["BuildingVisualization<br>Clickable SVG 3D preview<br>Rotates to face direction on element select"]
LA --> ED["Energy demand mini-panel<br>Same energyTotals as Overview, read-only"]
RS --> CP["Center panel<br>switches on panelView"]
RS --> SC["Selector column (w-72)<br>SurfaceGroupSelector"]
CP --> PB["panelView = building<br>BuildingEditor<br>Type / area / height / storeys"]
CP --> PSG["panelView = surface-group<br>(non-roof, element selected)<br>SurfaceGroupEditor<br>Geometry tab + Thermal tab + PV tab"]
CP --> PRG["panelView = surface-group (roof)<br>SurfaceGroupGrid (type picker)<br>+ embedded SurfaceGroupEditor"]
CP --> PPV["panelView = technology-pv<br>PvSurfaceManager<br>List of PV-enabled surfaces"]
CP --> PBT["panelView = technology-battery<br>BatteryEditor<br>Capacity / efficiency / cost params"]
SC --> SNav["Building nav item → panelView = building"]
SC --> SGNav["Surface group nav items<br>Wall / Roof / Floor / Window / Door"]
SC --> STNav["Technology nav items<br>one per visible TECH_REGISTRY entry<br>opens the entry's panelView"]
end
BC --> CF
Panel navigation state machine¶
panelView is driven by user interaction. The transitions are:
stateDiagram-v2
[*] --> building : initial / reset
building --> surface_group : click surface group in selector
building --> technology_pv : click Solar PV in selector
building --> technology_battery : click Battery in selector
surface_group --> building : click Building in selector
surface_group --> surface_group : click different group or surface
surface_group --> technology_pv : click Solar PV
surface_group --> technology_battery : click Battery
technology_pv --> surface_group : click a surface to configure its PV tab
technology_pv --> building : click Building
technology_battery --> building : click Building
State owned by BuildingConfigurator¶
The diagram shows the state fields held in BuildingConfigurator and the types they reference.
classDiagram
class BuildingConfigurator {
workspaceView: overview | configure
mode: basic | expert
panelView: building | surface-group | technology-pv | technology-battery
activeGroupType: ElementGroupKey | null
selectedId: string | null
elements: Record~string, BuildingElement~
general: GeneralConfig
roofConfig: RoofConfig
surfacePvConfigs: Record~string, PvConfig~
batteryConfig: BatteryConfig
otherTechIds: string[]
energyTotals: EnergyTotals
pvInvalidated: boolean
savedState: snapshot for unsaved-change detection
}
class BuildingElement {
id: string
label: string
type: wall | window | door | roof | floor
area: number
uValue: number
gValue: number | null
tilt: number
azimuth: number
source: city | default | custom
customMode: boolean
}
class PvConfig {
installed: boolean
geometryMode: surface | manual
system_capacity: number
tilt: number
azimuth: number
cont_energy_cap_max: number
cont_energy_eff: number
inv_eff: number
cost_energy_cap: number
}
class BatteryConfig {
installed: boolean
cont_energy_cap_max: number
cont_storage_cap_max: number
cont_energy_eff: number
cont_storage_loss: number
cost_energy_cap: number
cost_storage_cap: number
}
BuildingConfigurator "1" --> "0..*" BuildingElement : elements
BuildingConfigurator "1" --> "0..*" PvConfig : surfacePvConfigs
BuildingConfigurator "1" --> "1" BatteryConfig : batteryConfig
Data model output (exportToBuemGeojson)¶
The export assembles state into a BUEM GeoJSON FeatureCollection. Which fields appear depends on which technologies are installed.
graph LR
S["BuildingConfigurator state"]
S --> ID["identity<br>id / label / coordinates<br>buildingType / constructionPeriod<br>floorArea / roomHeight / storeys"]
S --> ENV["envelope<br>one feature per BuildingElement<br>area / uValue / tilt / azimuth"]
S --> PV["techs.pv_supply<br>per-surface PV params<br>only if installed = true"]
S --> BAT["techs.battery_storage<br>capacity / efficiency / cost<br>only if installed or includeInModel = true"]
S --> OTH["techs (other)<br>heat_pump / ev_charger / wind_turbine<br>only if in installedTechIds"]
Technology registry¶
src/app/config/techRegistry.ts defines every technology in TECH_REGISTRY. TechnologiesSection (Overview cards), SurfaceGroupSelector (Configure nav) and exportToBuemGeojson in buemAdapter.ts all read it, so adding, hiding or removing a technology starts in that file.
| Field | Effect |
|---|---|
visible |
false hides the card in Overview and the nav item in Configure. |
includeInModel |
true writes the technology's parameters to the export even when visible is false. |
scope |
per-surface: configured on each surface, no install toggle. building: install toggle and a configure panel. none: install toggle only. |
panelView |
The panel opened for the technology. Required when scope is building or per-surface. |
To add a building-level technology, append an entry, implement its panel component, and add a matching case in renderCenterPanel in BuildingConfigurator.tsx.