@@ -90,6 +90,48 @@ QString formatPressureScore(double score) {
9090 return QString::number (score, ' f' , 1 );
9191}
9292
93+ QString formatExposureScore (double score) {
94+ return QString::number (score, ' f' , 1 );
95+ }
96+
97+ QString formatExposureSeconds (double seconds) {
98+ return QString (" %1 agent-sec" ).arg (seconds, 0 , ' f' , 1 );
99+ }
100+
101+ double hazardExposureSecondsForKind (
102+ const safecrowd::domain::HazardExposureSummary& summary,
103+ safecrowd::domain::EnvironmentHazardKind kind) {
104+ double total = 0.0 ;
105+ for (const auto & metric : summary.hazards ) {
106+ if (metric.kind == kind) {
107+ total += metric.exposedAgentSeconds ;
108+ }
109+ }
110+ return total;
111+ }
112+
113+ double totalHazardExposureSeconds (const safecrowd::domain::HazardExposureSummary& summary) {
114+ double total = 0.0 ;
115+ for (const auto & metric : summary.hazards ) {
116+ total += metric.exposedAgentSeconds ;
117+ }
118+ return total;
119+ }
120+
121+ const safecrowd::domain::HazardExposureMetric* peakHazardExposureMetric (
122+ const safecrowd::domain::HazardExposureSummary& summary) {
123+ const safecrowd::domain::HazardExposureMetric* best = nullptr ;
124+ for (const auto & metric : summary.hazards ) {
125+ if (best == nullptr
126+ || metric.peakExposedAgentCount > best->peakExposedAgentCount
127+ || (metric.peakExposedAgentCount == best->peakExposedAgentCount
128+ && metric.exposedAgentSeconds > best->exposedAgentSeconds )) {
129+ best = &metric;
130+ }
131+ }
132+ return best;
133+ }
134+
93135QTableWidget* createComparisonTable (const QStringList& headers, QWidget* parent) {
94136 auto * table = new QTableWidget (0 , headers.size (), parent);
95137 table->setHorizontalHeaderLabels (headers);
@@ -833,6 +875,8 @@ ScenarioResultNavigationView resultNavigationViewFromSaved(SavedResultNavigation
833875 switch (view) {
834876 case SavedResultNavigationView::Hotspot:
835877 return ScenarioResultNavigationView::Hotspot;
878+ case SavedResultNavigationView::HazardExposure:
879+ return ScenarioResultNavigationView::HazardExposure;
836880 case SavedResultNavigationView::Zone:
837881 return ScenarioResultNavigationView::Zone;
838882 case SavedResultNavigationView::Groups:
@@ -849,6 +893,8 @@ SavedResultNavigationView savedResultNavigationView(ScenarioResultNavigationView
849893 switch (view) {
850894 case ScenarioResultNavigationView::Hotspot:
851895 return SavedResultNavigationView::Hotspot;
896+ case ScenarioResultNavigationView::HazardExposure:
897+ return SavedResultNavigationView::HazardExposure;
852898 case ScenarioResultNavigationView::Zone:
853899 return SavedResultNavigationView::Zone;
854900 case ScenarioResultNavigationView::Groups:
@@ -1012,6 +1058,7 @@ QWidget* ScenarioBatchResultWidget::createCanvasPanel() {
10121058 auto * tabs = new QTabWidget (graphPanel);
10131059 remainingChart_ = new ComparisonGraphWidget (ComparisonGraphMode::Remaining, tabs);
10141060 exitsChart_ = new ComparisonGraphWidget (ComparisonGraphMode::Exits, tabs);
1061+ exposureTable_ = createComparisonTable ({" Scenario" , " Total" , " Fire" , " Smoke" , " Peak" , " Peak at" }, tabs);
10151062 pressureTable_ = createComparisonTable ({" Scenario" , " Peak score" , " Exposed / Critical" , " Hotspots" , " Events" , " Peak at" }, tabs);
10161063 static_cast <ComparisonGraphWidget*>(remainingChart_)->setResults (results_, selectedCompareIndices_, currentResultIndex_);
10171064 static_cast <ComparisonGraphWidget*>(exitsChart_)->setResults (results_, selectedCompareIndices_, currentResultIndex_);
@@ -1020,6 +1067,7 @@ QWidget* ScenarioBatchResultWidget::createCanvasPanel() {
10201067 });
10211068 tabs->addTab (remainingChart_, " Remaining" );
10221069 tabs->addTab (exitsChart_, " Exits" );
1070+ tabs->addTab (exposureTable_, " Exposure" );
10231071 tabs->addTab (pressureTable_, " Pressure" );
10241072 graphLayout->addWidget (tabs, 1 );
10251073 layout->addWidget (graphPanel, 1 );
@@ -1087,7 +1135,7 @@ QWidget* ScenarioBatchResultWidget::createSummaryPanel() {
10871135 layout->setContentsMargins (0 , 0 , 10 , 0 );
10881136 layout->setSpacing (12 );
10891137
1090- auto * intro = createLabel (" Choose which completed scenarios appear together in the comparison graphs and pressure summary table ." , content, ui::FontRole::Caption);
1138+ auto * intro = createLabel (" Choose which completed scenarios appear together in the comparison graphs and risk summary tables ." , content, ui::FontRole::Caption);
10911139 intro->setSizePolicy (QSizePolicy::Ignored, QSizePolicy::Preferred);
10921140 intro->setStyleSheet (ui::mutedTextStyleSheet ());
10931141 layout->addWidget (intro);
@@ -1517,6 +1565,7 @@ void ScenarioBatchResultWidget::refreshComparisonSelection() {
15171565 static_cast <ComparisonGraphWidget*>(exitsChart_)->setResults (results_, selectedCompareIndices_, currentResultIndex_);
15181566 }
15191567 refreshComparisonCountLabel ();
1568+ refreshExposureComparisonTable ();
15201569 refreshPressureComparisonTable ();
15211570}
15221571
@@ -1529,6 +1578,53 @@ void ScenarioBatchResultWidget::refreshComparisonCountLabel() {
15291578 .arg (static_cast <int >(results_.size ())));
15301579}
15311580
1581+ void ScenarioBatchResultWidget::refreshExposureComparisonTable () {
1582+ if (exposureTable_ == nullptr ) {
1583+ return ;
1584+ }
1585+
1586+ const std::vector<int > visibleIndices = selectedCompareIndices_;
1587+ exposureTable_->setRowCount (static_cast <int >(visibleIndices.size ()));
1588+ for (int row = 0 ; row < static_cast <int >(visibleIndices.size ()); ++row) {
1589+ const auto index = visibleIndices[static_cast <std::size_t >(row)];
1590+ if (index < 0 || index >= static_cast <int >(results_.size ())) {
1591+ continue ;
1592+ }
1593+
1594+ const auto & result = results_[static_cast <std::size_t >(index)];
1595+ const auto & summary = result.artifacts .hazardExposureSummary ;
1596+ const bool emphasized = index == currentResultIndex_;
1597+ const auto * peakHazard = peakHazardExposureMetric (summary);
1598+ exposureTable_->setItem (row, 0 , tableItem (QString::fromStdString (result.scenario .name ), emphasized));
1599+ exposureTable_->setItem (row, 1 , tableItem (formatExposureSeconds (totalHazardExposureSeconds (summary)), emphasized));
1600+ exposureTable_->setItem (
1601+ row,
1602+ 2 ,
1603+ tableItem (formatExposureSeconds (hazardExposureSecondsForKind (
1604+ summary,
1605+ safecrowd::domain::EnvironmentHazardKind::Fire)), emphasized));
1606+ exposureTable_->setItem (
1607+ row,
1608+ 3 ,
1609+ tableItem (formatExposureSeconds (hazardExposureSecondsForKind (
1610+ summary,
1611+ safecrowd::domain::EnvironmentHazardKind::Smoke)), emphasized));
1612+ exposureTable_->setItem (
1613+ row,
1614+ 4 ,
1615+ tableItem (
1616+ peakHazard == nullptr
1617+ ? QString (" 0 people" )
1618+ : QString (" %1 people" ).arg (static_cast <int >(peakHazard->peakExposedAgentCount )),
1619+ emphasized));
1620+ exposureTable_->setItem (
1621+ row,
1622+ 5 ,
1623+ tableItem (peakHazard == nullptr ? QString (" Pending" ) : formatSeconds (peakHazard->peakAtSeconds ), emphasized));
1624+ }
1625+ exposureTable_->resizeRowsToContents ();
1626+ }
1627+
15321628void ScenarioBatchResultWidget::refreshPressureComparisonTable () {
15331629 if (pressureTable_ == nullptr ) {
15341630 return ;
@@ -1610,6 +1706,7 @@ void ScenarioBatchResultWidget::setComparisonSelection(std::vector<int> indices)
16101706 static_cast <ComparisonGraphWidget*>(exitsChart_)->setResults (results_, selectedCompareIndices_, currentResultIndex_);
16111707 }
16121708 refreshComparisonCountLabel ();
1709+ refreshExposureComparisonTable ();
16131710 refreshPressureComparisonTable ();
16141711}
16151712
@@ -1892,6 +1989,7 @@ void ScenarioBatchResultWidget::refreshSelectedResult() {
18921989 if (exitsChart_ != nullptr ) {
18931990 static_cast <ComparisonGraphWidget*>(exitsChart_)->setResults (results_, selectedCompareIndices_, currentResultIndex_);
18941991 }
1992+ refreshExposureComparisonTable ();
18951993 refreshPressureComparisonTable ();
18961994 refreshResultNavigationPanel ();
18971995 if (detailLabel_ != nullptr ) {
@@ -1901,7 +1999,9 @@ void ScenarioBatchResultWidget::refreshSelectedResult() {
19011999 ? QString (" Baseline" )
19022000 : formatDeltaSeconds (selectedFinalSeconds - finalSeconds (results_[static_cast <std::size_t >(baselineIndex)])))
19032001 : QString (" No baseline" );
1904- detailLabel_->setText (QString (" %1 (%2)\n Final: %3\n Delta vs baseline: %4\n Evacuated: %5 / %6 (%7)\n Risk: %8\n Hotspots: %9\n Bottlenecks: %10\n Pressure hotspots: %11\n Critical pressure: %12 agents / %13 events\n Peak pressure: %14 at %15\n T90 / T95: %16 / %17" )
2002+ const auto & exposureSummary = result.artifacts .hazardExposureSummary ;
2003+ const auto * peakHazard = peakHazardExposureMetric (exposureSummary);
2004+ detailLabel_->setText (QString (" %1 (%2)\n Final: %3\n Delta vs baseline: %4\n Evacuated: %5 / %6 (%7)\n Risk: %8\n Hotspots: %9\n Bottlenecks: %10\n Hazard exposure: %11 (fire %12 / smoke %13)\n Hazard peak: %14 at %15; score %16\n Pressure hotspots: %17\n Critical pressure: %18 agents / %19 events\n Peak pressure: %20 at %21\n T90 / T95: %22 / %23" )
19052005 .arg (QString::fromStdString (result.scenario .name ))
19062006 .arg (scenarioRoleLabel (result.scenario .role ))
19072007 .arg (formatSeconds (selectedFinalSeconds))
@@ -1912,6 +2012,18 @@ void ScenarioBatchResultWidget::refreshSelectedResult() {
19122012 .arg (safecrowd::domain::scenarioRiskLevelLabel (result.risk .completionRisk ))
19132013 .arg (static_cast <int >(result.risk .hotspots .size ()))
19142014 .arg (static_cast <int >(result.risk .bottlenecks .size ()))
2015+ .arg (formatExposureSeconds (totalHazardExposureSeconds (exposureSummary)))
2016+ .arg (formatExposureSeconds (hazardExposureSecondsForKind (
2017+ exposureSummary,
2018+ safecrowd::domain::EnvironmentHazardKind::Fire)))
2019+ .arg (formatExposureSeconds (hazardExposureSecondsForKind (
2020+ exposureSummary,
2021+ safecrowd::domain::EnvironmentHazardKind::Smoke)))
2022+ .arg (peakHazard == nullptr
2023+ ? QString (" 0 people" )
2024+ : QString (" %1 people" ).arg (static_cast <int >(peakHazard->peakExposedAgentCount )))
2025+ .arg (peakHazard == nullptr ? QString (" Pending" ) : formatSeconds (peakHazard->peakAtSeconds ))
2026+ .arg (formatExposureScore (exposureSummary.totalExposureScore ))
19152027 .arg (static_cast <int >(result.artifacts .pressureSummary .peakHotspots .size ()))
19162028 .arg (static_cast <int >(result.artifacts .pressureSummary .peakCriticalAgentCount ))
19172029 .arg (static_cast <int >(result.artifacts .pressureSummary .criticalEvents .size ()))
0 commit comments