feat: 파이차트 생성 기능

hhsung_work
HyungJune Kim 10 months ago
parent ff95feeafc
commit 6bc2fc30fb

@ -182,56 +182,121 @@
</Style>
</Grid.Resources>
<StackPanel Style="{StaticResource VisibleWhenLineNStackArea}">
<Grid Margin="0 0 0 20" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="X축" VerticalAlignment="Center"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
<TextBlock Text="{Binding SelectedXField.Display}" VerticalAlignment="Center"
Margin="15 0 0 0" Grid.Column="1"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
</Grid>
<StackPanel>
<StackPanel Style="{StaticResource VisibleWhenLineNStackArea}">
<Grid Margin="0 0 0 20" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="X축" VerticalAlignment="Center"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
<TextBlock Text="{Binding SelectedXField.Display}" VerticalAlignment="Center"
Margin="15 0 0 0" Grid.Column="1"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
</Grid>
<Grid Margin="0 0 0 15" Style="{StaticResource VisibleWhenLineNStackArea}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Y축" VerticalAlignment="Top"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"/>
<Grid Margin="0 0 0 15">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Y축" 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>
<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>
<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" Grid.Row="4">
<CheckBox Content="마커 표시" IsChecked="{Binding ShowMarkers}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/>
<CheckBox Content="범례 표시" IsChecked="{Binding ShowLegends}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/>
<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>
</StackPanel>
</Grid>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="15 0 0 0" Grid.Row="1">
<CheckBox Content="마커 표시" IsChecked="{Binding ShowMarkers}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center">
<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"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center"
Style="{StaticResource MaterialDesignUserForegroundCheckBox}"/>
<CheckBox Content="도넛 모드" IsChecked="{Binding IsDonutMode}" Margin="0 0 15 0"
FontSize="20" FontFamily="{StaticResource SCDream4}" Foreground="White"
VerticalContentAlignment="Center">
<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 Orientation="Horizontal" Margin="15 0" Grid.Row="1" HorizontalAlignment="Right">
<Button Content="그래프 생성" Style="{StaticResource MaterialDesignFlatLightBgButton}"
FontWeight="Bold" Command="{Binding DrawGraphCommand}"/>

@ -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));

@ -557,11 +557,61 @@ namespace SmartAquaViewer.ViewModel
Model.InvalidatePlot(true);
}
public void SetStackAreaPlot(
List<WaterQualityVO> rows,
FieldItem yAxisField)
public void SetPieChart(List<WaterQualityVO> collection, ObservableCollection<FieldItem> 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)

Loading…
Cancel
Save