diff --git a/SmartAquaViewer/View/EnegyView.xaml b/SmartAquaViewer/View/EnegyView.xaml
index d7596e6..74850c4 100644
--- a/SmartAquaViewer/View/EnegyView.xaml
+++ b/SmartAquaViewer/View/EnegyView.xaml
@@ -182,56 +182,121 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SmartAquaViewer/ViewModel/EnergyViewModel.cs b/SmartAquaViewer/ViewModel/EnergyViewModel.cs
index 673c0e3..15cfecc 100644
--- a/SmartAquaViewer/ViewModel/EnergyViewModel.cs
+++ b/SmartAquaViewer/ViewModel/EnergyViewModel.cs
@@ -277,7 +277,7 @@ namespace SmartAquaViewer.ViewModel
GraphControlVM.SetStackAreaPlot(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends);
break;
case GraphType.PIE:
-
+ GraphControlVM.SetPieChart(WaterQualityList, SelectedYFields);
break;
default:
break;
@@ -328,7 +328,7 @@ namespace SmartAquaViewer.ViewModel
// Y축 후보: 수치형
foreach (var f in src)
{
- if (SelectedGraphType == GraphType.STACKAREA && f.Name.Equals("TotalEnergy")) continue;
+ if (SelectedGraphType is GraphType.STACKAREA or GraphType.PIE && f.Name.Equals("TotalEnergy")) continue;
YFieldCandidates.Add(f);
}
@@ -347,8 +347,7 @@ namespace SmartAquaViewer.ViewModel
break;
case GraphType.PIE:
- SelectedYField = YFieldCandidates.FirstOrDefault();
-
+ //SelectedYField = YFieldCandidates.FirstOrDefault();
break;
}
OnPropertyChanged(nameof(SelectedYFields));
diff --git a/SmartAquaViewer/ViewModel/GraphControlViewModel.cs b/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
index 3a079e4..9249a95 100644
--- a/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
+++ b/SmartAquaViewer/ViewModel/GraphControlViewModel.cs
@@ -557,11 +557,61 @@ namespace SmartAquaViewer.ViewModel
Model.InvalidatePlot(true);
}
- public void SetStackAreaPlot(
- List rows,
- FieldItem yAxisField)
+ public void SetPieChart(List collection, ObservableCollection fields,
+ bool useAverage = false, bool donut = true,
+ double minLabelPercent = 0.03)
{
+ Model.Series.Clear();
+ Model.Axes.Clear();
+
+ if (collection == null || collection.Count == 0)
+ {
+ Model.InvalidatePlot(true);
+ return;
+ }
+
+ if (fields.Count == 0)
+ {
+ Model.InvalidatePlot(true);
+ return;
+ }
+
+ var agg = new List<(string name, double value)>();
+ foreach (var f in fields)
+ {
+ var values = collection.Select(r => ResolveEnergyField(r, f.Name!) ?? 0.0);
+ double v = useAverage ? (values.Any() ? values.Average() : 0.0) : values.Sum();
+ agg.Add((f.Display ?? f.Name!, v));
+ }
+ List<(string name, double value)> finalList;
+ finalList = agg;
+
+ double total = Math.Max(1e-9, finalList.Sum(x => x.value));
+
+ var ps = new PieSeries
+ {
+ AngleSpan = 360,
+ StartAngle = 0,
+ StrokeThickness = 0.5,
+ InsideLabelPosition = 0.8,
+ OutsideLabelFormat = null // 라벨은 내부만
+ };
+ if (donut) ps.InnerDiameter = 0.6; // 도넛 모드
+
+ foreach (var (name, value) in finalList)
+ {
+ var pct = value / total;
+ var label = (pct >= minLabelPercent) ? $"{name} ({pct:P0})" : ""; // 작은 조각 라벨 숨김
+ ps.Slices.Add(new PieSlice(name, value) { });
+ }
+
+ Model.Title = $"설비별 {(useAverage ? "평균" : "합계")} 소비 비중";
+ Model.Series.Add(ps);
+
+ Model.IsLegendVisible = true;
+
+ Model.InvalidatePlot(true);
}
private DateTime FloorToBucket(DateTime dt, TimeSpan bucket)