/// <summary> /// Create a new <see cref="ExportedSeries"/> instance. /// </summary> /// <param name="series">The actual series.</param> /// <param name="xAxisRequirements">X axis requirements of the series.</param> /// <param name="yAxisRequirements">Y axis requirements of the series.</param> /// <param name="labels">Axis labels required by the series.</param> public ExportedSeries(Series series, AxisRequirements xAxisRequirements, AxisRequirements yAxisRequirements, AxisLabelCollection labels) { Result = series; XAxisRequirements = xAxisRequirements; YAxisRequirements = yAxisRequirements; AxisLabels = labels; }
/// <summary> /// Export the series to an oxyplot series. /// </summary> /// <remarks> /// When dealing with string data, the returned data points are ints /// which are indices into the axis labels list. Therefore we /// need to know about any existing axis labels. /// </remarks> /// <param name="series">The series to be exported.</param> /// <param name="existingAxisLabels">Existing axis labels on the graph.</param> public ExportedSeries Export(ISeries series, AxisLabelCollection existingAxisLabels) { (Series oxyPlotSeries, AxisLabelCollection labels) = Export((T)series, existingAxisLabels); AxisType?xAxisType = GetRequiredAxisType(series.X.FirstOrDefault()); AxisType?yAxisType = GetRequiredAxisType(series.Y.FirstOrDefault()); AxisRequirements xAxisRequirements = new AxisRequirements(xAxisType, series.XFieldName); AxisRequirements yAxisRequirements = new AxisRequirements(yAxisType, series.YFieldName); return(new ExportedSeries(oxyPlotSeries, xAxisRequirements, yAxisRequirements, labels)); }
/// <summary> /// Ensure that this set of axis requirements is compatible with another. /// Throw if not. /// </summary> /// <param name="other">Another set of axis requirements.</param> public void ThrowIfIncompatibleWith(AxisRequirements other) { try { if (AxisKind != other.AxisKind && AxisKind != null && other.AxisKind != null) { throw new Exception($"Axis type {AxisKind} is incompatible with {other.AxisKind}"); } } catch (Exception err) { throw new Exception($"Axis required by {FieldName} is incompatible with axis required by {other.FieldName}", err); } }
/// <summary> /// Convert the given apsim graph to an oxyplot <see cref="PlotModel"/>. /// </summary> /// <param name="graph">The graph to be converted.</param> public IPlotModel ToPlotModel(IGraph graph) { if (graph.XAxis == null) { throw new NullReferenceException("Graph has no x-axis"); } if (graph.YAxis == null) { throw new NullReferenceException("Graph has no y-axis"); } if (graph.Legend == null) { throw new NullReferenceException("Graph has no legend configuration"); } if (graph.Series == null) { throw new NullReferenceException("Graph has no series"); } PlotModel plot = new PlotModel(); // Add series to graph. AxisLabelCollection labels = AxisLabelCollection.Empty(); ExportedSeries previous = null; AxisRequirements xAxisRequirements = null; AxisRequirements yAxisRequirements = null; foreach (Series graphSeries in graph.Series) { ExportedSeries series = graphSeries.ToOxyPlotSeries(labels); labels = series.AxisLabels; plot.Series.Add(series.Result); if (previous == null) { previous = series; } else { previous.ThrowIfIncompatibleWith(series); previous = series; } if (series.XAxisRequirements.AxisKind != null) { xAxisRequirements = series.XAxisRequirements; } if (series.YAxisRequirements.AxisKind != null) { yAxisRequirements = series.YAxisRequirements; } } // Axes (don't add them if there are no series to display on the graph). if (xAxisRequirements?.AxisKind != null) { plot.Axes.Add(graph.XAxis.ToOxyPlotAxis(xAxisRequirements, labels.XLabels)); } if (yAxisRequirements?.AxisKind != null) { plot.Axes.Add(graph.YAxis.ToOxyPlotAxis(yAxisRequirements, labels.YLabels)); } // Legend plot.Legends.Add(new Legend() { LegendOrientation = graph.Legend.Orientation.ToOxyPlotLegendOrientation(), LegendPosition = graph.Legend.Position.ToOxyPlotLegendPosition(), LegendPlacement = graph.Legend.InsideGraphArea ? OxyLegendPlacement.Inside : OxyLegendPlacement.Outside, Font = font, }); // Apply font plot.TitleFont = font; plot.SetLegendFont(font); plot.PlotAreaBorderThickness = new OxyThickness(0); plot.Title = graph.Title; return(plot); }
/// <summary> /// Convert the given apsim axis to an oxyplot <see cref="Axis"/>. /// </summary> /// <param name="graph">The graph to be converted.</param> public static Axis ToOxyPlotAxis(this APSIM.Shared.Graphing.Axis axis, AxisRequirements requirements, IEnumerable <string> labels) { if (requirements.AxisKind == null) { throw new InvalidOperationException("Unable to create series - axis requirements unknown, possibly because no series have any data"); } Axis result = CreateAxis((AxisType)requirements.AxisKind); result.Position = axis.Position.ToOxyAxisPosition(); result.Title = axis.Title; result.PositionAtZeroCrossing = axis.CrossesAtZero; if (axis.Minimum is double min) { if (double.IsNaN(min)) { Debug.WriteLine("Axis minimum is NaN"); } else { result.Minimum = min; } } if (axis.Maximum is double max) { if (double.IsNaN(max)) { Debug.WriteLine("Axis maximum is NaN"); } else { result.Maximum = max; } } if (axis.Interval is double interval) { if (double.IsNaN(interval)) { Debug.WriteLine("Axis interval is NaN"); } else { if (requirements.AxisKind == AxisType.DateTime) { Debug.WriteLine("WARNING: Axis interval is set manually on a date axis - need to double check the implementation."); } result.MajorStep = interval; } } if (axis.Inverted) { result.StartPosition = 1; result.EndPosition = 0; } // There are many other options which could be exposed to the user. result.MinorTickSize = 0; result.AxisTitleDistance = 10; result.AxislineStyle = OxyPlot.LineStyle.Solid; if (requirements.AxisKind == AxisType.Category && result is CategoryAxis categoryAxis) { categoryAxis.LabelField = "Label"; categoryAxis.Labels.AddRange(labels); } return(result); }