feat: 파이차트 생성 기능

hhsung_work
HyungJune Kim 10 months ago
parent ff95feeafc
commit 6bc2fc30fb

@ -182,6 +182,7 @@
</Style> </Style>
</Grid.Resources> </Grid.Resources>
<StackPanel>
<StackPanel Style="{StaticResource VisibleWhenLineNStackArea}"> <StackPanel Style="{StaticResource VisibleWhenLineNStackArea}">
<Grid Margin="0 0 0 20" > <Grid Margin="0 0 0 20" >
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
@ -195,7 +196,7 @@
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/> FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
</Grid> </Grid>
<Grid Margin="0 0 0 15" Style="{StaticResource VisibleWhenLineNStackArea}"> <Grid Margin="0 0 0 15">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/> <ColumnDefinition Width="80"/>
<ColumnDefinition/> <ColumnDefinition/>
@ -216,20 +217,84 @@
Style="{StaticResource MaterialDesignFilterChipListBox}"/> Style="{StaticResource MaterialDesignFilterChipListBox}"/>
</Border> </Border>
</Grid> </Grid>
</StackPanel>
<StackPanel Style="{StaticResource VisibleWhenPie}">
<Grid Margin="0 0 0 15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="필드" VerticalAlignment="Top"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
<Border Grid.Column="1" CornerRadius="10"
Background="White" Margin="15 0 0 15">
<ListBox ItemsSource="{Binding YFieldCandidates}"
DisplayMemberPath="Display"
SelectionMode="Extended"
helper:MultiSelectBehavior.SelectedItems="{Binding SelectedYFields, Mode=OneWay}"
helper:MultiSelectBehavior.KeyPath="Key"
helper:MultiSelectBehavior.ValuePath="Value"
Height="Auto" Background="White"
FontSize="16" FontWeight="Bold"
Style="{StaticResource MaterialDesignFilterChipListBox}"/>
</Border>
</Grid>
<StackPanel Orientation="Horizontal" Margin="0 0 0 15">
<RadioButton x:Name="rbStatus" Content="합계"
GroupName="pie" Margin="0 0 30 0"
Foreground="White" FontSize="20"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"/>
<RadioButton x:Name="pie" Content="평균"
GroupName="strpPlot" Grid.Column="1"
Foreground="White" FontSize="20"
Style="{StaticResource MaterialDesignUserForegroundRadioButton}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="0 0 0 15" Grid.Row="4"> <StackPanel Orientation="Horizontal" Margin="15 0 0 0" Grid.Row="1">
<CheckBox Content="마커 표시" IsChecked="{Binding ShowMarkers}" Margin="0 0 15 0" <CheckBox Content="마커 표시" IsChecked="{Binding ShowMarkers}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White" FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center" VerticalContentAlignment="Center">
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/> <CheckBox.Style>
<Style TargetType="CheckBox" BasedOn="{StaticResource MaterialDesignUserForegroundCheckBox}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedGraphType}" Value="LINE">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
<DataTrigger Binding="{Binding SelectedGraphType}" Value="STACKAREA">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
<CheckBox Content="범례 표시" IsChecked="{Binding ShowLegends}" Margin="0 0 15 0" <CheckBox Content="범례 표시" IsChecked="{Binding ShowLegends}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White" FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center" VerticalContentAlignment="Center"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/> Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/>
</StackPanel> <CheckBox Content="도넛 모드" IsChecked="{Binding IsDonutMode}" Margin="0 0 15 0"
</StackPanel> FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center">
</Grid> <CheckBox.Style>
<Style TargetType="CheckBox" BasedOn="{StaticResource MaterialDesignUserForegroundCheckBox}">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedGraphType}" Value="PIE">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="15 0" Grid.Row="1" HorizontalAlignment="Right"> <StackPanel Orientation="Horizontal" Margin="15 0" Grid.Row="1" HorizontalAlignment="Right">

@ -277,7 +277,7 @@ namespace SmartAquaViewer.ViewModel
GraphControlVM.SetStackAreaPlot(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends); GraphControlVM.SetStackAreaPlot(WaterQualityList, SelectedYFields, ShowMarkers, ShowLegends);
break; break;
case GraphType.PIE: case GraphType.PIE:
GraphControlVM.SetPieChart(WaterQualityList, SelectedYFields);
break; break;
default: default:
break; break;
@ -328,7 +328,7 @@ namespace SmartAquaViewer.ViewModel
// Y축 후보: 수치형 // Y축 후보: 수치형
foreach (var f in src) 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); YFieldCandidates.Add(f);
} }
@ -347,8 +347,7 @@ namespace SmartAquaViewer.ViewModel
break; break;
case GraphType.PIE: case GraphType.PIE:
SelectedYField = YFieldCandidates.FirstOrDefault(); //SelectedYField = YFieldCandidates.FirstOrDefault();
break; break;
} }
OnPropertyChanged(nameof(SelectedYFields)); OnPropertyChanged(nameof(SelectedYFields));

@ -557,11 +557,61 @@ namespace SmartAquaViewer.ViewModel
Model.InvalidatePlot(true); Model.InvalidatePlot(true);
} }
public void SetStackAreaPlot( public void SetPieChart(List<WaterQualityVO> collection, ObservableCollection<FieldItem> fields,
List<WaterQualityVO> rows, bool useAverage = false, bool donut = true,
FieldItem yAxisField) 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) private DateTime FloorToBucket(DateTime dt, TimeSpan bucket)

Loading…
Cancel
Save