using SQLVision.Core.Enums; using SQLVision.Core.Models; using SQLVision.Visualizers.Interfaces; using System.Data; namespace SQLVision.Visualizers.Visualizers; public class ChartVisualizer : IVisualizer { public FrameworkElement Visualize(DataTable data, OutputDefinition definition) { if (data.Rows.Count == 0) { return CreateEmptyChartMessage("Нет данных для построения графика"); } try { var chartType = definition.ChartType; var cartesianChart = new CartesianChart { Series = CreateSeries(data, definition), XAxes = CreateXAxes(data, definition), YAxes = CreateYAxes(definition), LegendPosition = LegendPosition.Right, TooltipPosition = LiveChartsCore.Measure.TooltipPosition.Hidden }; return cartesianChart; } catch (Exception) { return CreateEmptyChartMessage("Ошибка при построении графика"); } } private ISeries[] CreateSeries(DataTable data, OutputDefinition definition) { var series = new List(); if (!string.IsNullOrEmpty(definition.SeriesColumn)) { // Разделение по сериям var seriesGroups = data.AsEnumerable() .GroupBy(row => row[definition.SeriesColumn]) .ToList(); foreach (var group in seriesGroups) { var seriesName = group.Key.ToString(); var values = group.Select(row => { if (string.IsNullOrEmpty(definition.YAxisColumn)) return Convert.ToDouble(row[1]); return Convert.ToDouble(row[definition.YAxisColumn]); }).ToArray(); series.Add(CreateSeriesByType(definition.ChartType, values, seriesName)); } } else { // Одна серия var values = data.AsEnumerable() .Select(row => { if (string.IsNullOrEmpty(definition.YAxisColumn)) return Convert.ToDouble(row[1]); return Convert.ToDouble(row[definition.YAxisColumn]); }).ToArray(); series.Add(CreateSeriesByType(definition.ChartType, values, definition.Description)); } return series.ToArray(); } private ISeries CreateSeriesByType(ChartType chartType, double[] values, string name) { return chartType switch { ChartType.Line => new LineSeries { Values = values, Name = name, Fill = null, GeometrySize = 8, LineSmoothness = 0 }, ChartType.Bar => new ColumnSeries { Values = values, Name = name }, ChartType.Area => new LineSeries { Values = values, Name = name, Fill = new SolidColorPaint(SKColors.Blue.WithAlpha(50)) }, ChartType.Scatter => new ScatterSeries { Values = values.Select((v, i) => new ObservablePoint(i, v)), Name = name, GeometrySize = 10 }, _ => new LineSeries { Values = values, Name = name } }; } private Axis[] CreateXAxes(DataTable data, OutputDefinition definition) { var labels = new List(); if (!string.IsNullOrEmpty(definition.XAxisColumn)) { labels = data.AsEnumerable() .Select(row => row[definition.XAxisColumn].ToString()) .ToList(); } else if (data.Columns.Count > 0) { // Берем первый столбец для оси X labels = data.AsEnumerable() .Select(row => row[0].ToString()) .ToList(); } else { labels = Enumerable.Range(0, data.Rows.Count) .Select(i => i.ToString()) .ToList(); } return new[] { new Axis { Labels = labels.ToArray(), LabelsRotation = labels.Count > 10 ? 45 : 0, TextSize = 12 } }; } private Axis[] CreateYAxes(OutputDefinition definition) { return new[] { new Axis { Name = string.IsNullOrEmpty(definition.YAxisColumn) ? "Значения" : definition.YAxisColumn, TextSize = 12 } }; } private FrameworkElement CreateEmptyChartMessage(string message) { var textBlock = new TextBlock { Text = message, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center, FontSize = 16, Foreground = new Microsoft.UI.Xaml.Media.SolidColorBrush(Microsoft.UI.Colors.Gray) }; var border = new Border { Child = textBlock, Background = new Microsoft.UI.Xaml.Media.SolidColorBrush(Microsoft.UI.Colors.Transparent), Padding = new Thickness(20) }; return border; } public bool CanVisualize(OutputType type) => type == OutputType.Chart; }