/// <summary>Called by the graph presenter to get a list of all actual series to put on the graph.</summary> /// <param name="reader">A storage reader.</param> /// <param name="simulationDescriptions">A list of simulation descriptions that are in scope.</param> /// <param name="simulationFilter"></param> public IEnumerable <SeriesDefinition> CreateSeriesDefinitions(IStorageReader reader, List <SimulationDescription> simulationDescriptions, List <string> simulationFilter = null) { var seriesDefinitions = new List <SeriesDefinition>(); // If this series doesn't have a table name then it must be getting its data from other models. if (TableName == null) { seriesDefinitions.Add(new SeriesDefinition(this, "Current", colModifier: 0, markerModifier: 0)); } else { int checkpointNumber = 0; foreach (var checkpointName in reader.CheckpointNames) { if (checkpointName == "Current" || reader.GetCheckpointShowOnGraphs(checkpointName)) { // Colour modifier can be in range [-1, 1] but we will // use only 0..1 so that we start with the normal colour // and gradually get brighter. double colourModifier = (double)checkpointNumber / reader.CheckpointNames.Count; double markerModifier = 1 + 1.0 * checkpointNumber / reader.CheckpointNames.Count; // TableName exists so get the vary by fields and the simulation descriptions. var varyByFieldNames = GetVaryByFieldNames(); if (simulationFilter == null) { simulationFilter = simulationDescriptions.Select(d => d.Name).Distinct().ToList(); } var inScopeSimulationNames = CreateInScopeWhereClause(reader, simulationFilter); if (varyByFieldNames.Count == 0 || varyByFieldNames.Contains("Graph series")) { // No vary by fields. Just plot the whole table in a single // series with data that is in scope. seriesDefinitions.Add(new SeriesDefinition(this, checkpointName, colourModifier, markerModifier, inScopeSimulationNames, Filter)); } else { // There are one or more vary by fields. Create series definitions // for each combination of vary by fields. seriesDefinitions.AddRange(CreateDefinitionsUsingVaryBy(varyByFieldNames, checkpointName, colourModifier, markerModifier, simulationDescriptions, inScopeSimulationNames)); } // If we don't have any definitions then see if the vary by fields // refer to string fields in the database table. if (seriesDefinitions.Count == 0) { seriesDefinitions = CreateDefinitionsFromFieldInTable(reader, checkpointName, colourModifier, markerModifier, varyByFieldNames, inScopeSimulationNames); } // Paint all definitions. var painter = GetSeriesPainter(); foreach (var seriesDefinition in seriesDefinitions) { painter.Paint(seriesDefinition); } checkpointNumber++; } } } return(seriesDefinitions); }
/// <summary>Called by the graph presenter to get a list of all actual series to put on the graph.</summary> /// <param name="definitions">A list of definitions to add to.</param> /// <param name="reader">A storage reader.</param> /// <param name="simulationFilter"></param> public void GetSeriesToPutOnGraph(IStorageReader reader, List <SeriesDefinition> definitions, List <string> simulationFilter = null) { List <SeriesDefinition> seriesDefinitions = new List <SeriesDefinition>(); // If this series doesn't have a table name then it must be getting its data from other models. if (TableName == null) { seriesDefinitions.Add(new SeriesDefinition(this, "Current", colModifier: 0, markerModifier: 0)); seriesDefinitions[0].ReadData(reader, simulationDescriptions); } else { int checkpointNumber = 0; foreach (var checkpointName in reader.CheckpointNames) { if (checkpointName == "Current" || reader.GetCheckpointShowOnGraphs(checkpointName)) { // Colour modifier can be in range [-1, 1] but we will // use only 0..1 so that we start with the normal colour // and gradually get brighter. double colourModifier = (double)checkpointNumber / reader.CheckpointNames.Count; double markerModifier = 1 + 1.0 * checkpointNumber / reader.CheckpointNames.Count; // TableName exists so get the vary by fields and the simulation descriptions. var varyByFieldNames = GetVaryByFieldNames(); simulationDescriptions = FindSimulationDescriptions(); if (simulationFilter == null) { simulationFilter = simulationDescriptions.Select(d => d.Name).Distinct().ToList(); } var whereClauseForInScopeData = CreateInScopeWhereClause(reader, simulationFilter); if (varyByFieldNames.Count == 0 || varyByFieldNames.Contains("Graph series")) { // No vary by fields. Just plot the whole table in a single // series with data that is in scope. seriesDefinitions.Add(new SeriesDefinition(this, checkpointName, colourModifier, markerModifier, whereClauseForInScopeData, Filter)); } else { // There are one or more vary by fields. Create series definitions // for each combination of vary by fields. seriesDefinitions.AddRange(CreateDefinitionsUsingVaryBy(varyByFieldNames, checkpointName, colourModifier, markerModifier, simulationDescriptions, whereClauseForInScopeData)); } // If we don't have any definitions then see if the vary by fields // refer to string fields in the database table. if (seriesDefinitions.Count == 0) { seriesDefinitions = CreateDefinitionsFromFieldInTable(reader, checkpointName, colourModifier, markerModifier, varyByFieldNames, whereClauseForInScopeData); } // Paint all definitions. var painter = GetSeriesPainter(); foreach (var seriesDefinition in seriesDefinitions) { painter.Paint(seriesDefinition); } // Tell each series definition to read its data. foreach (var seriesDefinition in seriesDefinitions) { seriesDefinition.ReadData(reader, simulationDescriptions); } // Remove series that have no data. seriesDefinitions.RemoveAll(d => !MathUtilities.ValuesInArray(d.X) || !MathUtilities.ValuesInArray(d.Y)); checkpointNumber++; } } } definitions.AddRange(seriesDefinitions); // We might have child models that want to add to our series definitions e.g. regression. foreach (IGraphable series in Apsim.Children(this, typeof(IGraphable))) { series.GetSeriesToPutOnGraph(reader, definitions); } }
/// <summary>Get a list of all actual series to put on the graph.</summary> /// <param name="storage">Storage service (required for access to checkpoint names).</param> /// <param name="definitions">Series definitions to be used (allows for caching of data).</param> /// <param name="simulationsFilter">Unused simulation names filter.</param> public IEnumerable <SeriesDefinition> GetSeriesToPutOnGraph(IStorageReader storage, IEnumerable <SeriesDefinition> definitions, List <string> simulationsFilter = null) { stats.Clear(); equationColours.Clear(); int checkpointNumber = 0; List <SeriesDefinition> regressionLines = new List <SeriesDefinition>(); foreach (var checkpointName in storage.CheckpointNames) { if (checkpointName != "Current" && !storage.GetCheckpointShowOnGraphs(checkpointName)) // smh // If "Show on graphs" is disabled on this checkpoint, skip it. { continue; } // Get all x/y data List <double> x = new List <double>(); List <double> y = new List <double>(); foreach (SeriesDefinition definition in definitions) { if (definition.CheckpointName == checkpointName) { if (definition.X != null && definition.Y != null) { if (ReflectionUtilities.IsNumericType(definition.X.GetType().GetElementType()) && ReflectionUtilities.IsNumericType(definition.Y.GetType().GetElementType())) { x.AddRange(definition.X.Cast <object>().Select(xi => Convert.ToDouble(xi, CultureInfo.InvariantCulture)).ToArray()); y.AddRange(definition.Y.Cast <object>().Select(yi => Convert.ToDouble(yi, CultureInfo.InvariantCulture)).ToArray()); } } } } try { if (ForEachSeries) { // Display a regression line for each series. // todo - should this also filter on checkpoint name? foreach (SeriesDefinition definition in definitions) { if (definition.X is double[] && definition.Y is double[]) { SeriesDefinition regressionSeries = PutRegressionLineOnGraph(definition.X, definition.Y, definition.Colour, null); if (regressionSeries != null) { regressionLines.Add(regressionSeries); equationColours.Add(definition.Colour); } } } } else { var regresionLineName = "Regression line"; if (checkpointName != "Current") { regresionLineName = "Regression line (" + checkpointName + ")"; } // Display a single regression line for all data. if (x.Count > 0 && y.Count == x.Count) { SeriesDefinition regressionSeries = PutRegressionLineOnGraph(x, y, ColourUtilities.ChooseColour(checkpointNumber), regresionLineName); if (regressionSeries != null) { regressionLines.Add(regressionSeries); equationColours.Add(ColourUtilities.ChooseColour(checkpointNumber)); } } } if (showOneToOne) { if (x.Count > 0 && y.Count == x.Count) { regressionLines.Add(Put1To1LineOnGraph(x, y)); } } } catch (Exception err) { IEnumerable <string> xs = definitions.Select(d => d.XFieldName).Distinct(); IEnumerable <string> ys = definitions.Select(d => d.YFieldName).Distinct(); string xFields = string.Join(", ", xs); string yFields = string.Join(", ", ys); throw new InvalidOperationException($"Unable to create regression line for checkpoint {checkpointName}. (x variables = [{xFields}], y variables = [{yFields}])", err); } checkpointNumber++; } return(regressionLines); }