diff --git a/Core.Tests/Core.Tests.csproj b/Core.Tests/Core.Tests.csproj
index bfaaf0f..2b39d64 100644
--- a/Core.Tests/Core.Tests.csproj
+++ b/Core.Tests/Core.Tests.csproj
@@ -43,24 +43,35 @@
+
-
+
+
-
+
PreserveNewest
-
+
PreserveNewest
-
+
PreserveNewest
-
+
+ PreserveNewest
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+
+
PreserveNewest
-
diff --git a/Core.Tests/Core.Tests.csproj.DotSettings b/Core.Tests/Core.Tests.csproj.DotSettings
index c1b9bd1..fa43f95 100644
--- a/Core.Tests/Core.Tests.csproj.DotSettings
+++ b/Core.Tests/Core.Tests.csproj.DotSettings
@@ -1,2 +1,3 @@
+ True
True
\ No newline at end of file
diff --git a/Core.Tests/Parsers/CntrlComparisonParserTests.cs b/Core.Tests/Parsers/CntrlComparisonParserTests.cs
new file mode 100644
index 0000000..6277361
--- /dev/null
+++ b/Core.Tests/Parsers/CntrlComparisonParserTests.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using LeafWeb.Core.Models;
+using LeafWeb.Core.Parsers;
+using NUnit.Framework;
+
+namespace LeafWeb.Core.Tests.Parsers
+{
+ [TestFixture]
+ public class CntrlComparisonParserTests
+ {
+ protected const string ContentDirectory = @"Parsers\LeafOutputData\";
+
+ private static FileInfo GetContentFile(string fileName)
+ {
+ var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ContentDirectory);
+ return new FileInfo(path + fileName);
+ }
+
+ [Test]
+ public void Parse_Valid()
+ {
+ var fileInfo = GetContentFile("cntrlcomparison_Wild Capsicum.csv");
+
+ CntrlComparison[] cntrlComparison;
+ using (var parser = new CntrlComparisonParser(fileInfo))
+ cntrlComparison = parser.Parse();
+
+ Assert.That(cntrlComparison.Length, Is.EqualTo(7 * 4));
+ }
+ }
+}
diff --git a/Core.Tests/Parsers/CurveDataListTests.cs b/Core.Tests/Parsers/CurveDataListTests.cs
new file mode 100644
index 0000000..0955333
--- /dev/null
+++ b/Core.Tests/Parsers/CurveDataListTests.cs
@@ -0,0 +1,31 @@
+using System;
+using System.IO;
+using LeafWeb.Core.Charter;
+using NUnit.Framework;
+
+namespace LeafWeb.Core.Tests.Parsers
+{
+ [TestFixture]
+ public class CurveDataListTests
+ {
+ protected const string ContentDirectory = @"Services\LeafOutputData\";
+
+ private static FileInfo GetContentFile(string fileName)
+ {
+ var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ContentDirectory);
+ return new FileInfo(path + fileName);
+ }
+
+ [Test]
+ public void Parse_Valid()
+ {
+ var fileInfo = GetContentFile("cntrlcomparison_Wild Capsicum.csv");
+ var cntrlComparison = new CurveDataList();
+ using (var reader = fileInfo.OpenText())
+ {
+ cntrlComparison.ReadFromStream(reader);
+ }
+ Assert.That(cntrlComparison.CurveData.Count, Is.EqualTo(7));
+ }
+ }
+}
diff --git a/Core.Tests/Services/LeafInputCsvParserTests.cs b/Core.Tests/Parsers/LeafInputCsvParserTests.cs
similarity index 97%
rename from Core.Tests/Services/LeafInputCsvParserTests.cs
rename to Core.Tests/Parsers/LeafInputCsvParserTests.cs
index 48ba5ba..003668f 100644
--- a/Core.Tests/Services/LeafInputCsvParserTests.cs
+++ b/Core.Tests/Parsers/LeafInputCsvParserTests.cs
@@ -3,15 +3,16 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using LeafWeb.Core.Models;
-using LeafWeb.Core.Services;
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
using NUnit.Framework;
-namespace LeafWeb.Core.Tests.Services
+namespace LeafWeb.Core.Tests.Parsers
{
[TestFixture]
public class LeafInputCsvParserTests
{
- protected const string ContentDirectory = @"Services\LeafInputData\";
+ protected const string ContentDirectory = @"Parsers\LeafInputData\";
private static FileInfo GetContentFile(string fileName)
{
diff --git a/Core.Tests/Services/LeafInputData/LeafInput-incompleteRows.csv b/Core.Tests/Parsers/LeafInputData/LeafInput-incompleteRows.csv
similarity index 100%
rename from Core.Tests/Services/LeafInputData/LeafInput-incompleteRows.csv
rename to Core.Tests/Parsers/LeafInputData/LeafInput-incompleteRows.csv
diff --git a/Core.Tests/Services/LeafInputData/LeafInput-noData.csv b/Core.Tests/Parsers/LeafInputData/LeafInput-noData.csv
similarity index 100%
rename from Core.Tests/Services/LeafInputData/LeafInput-noData.csv
rename to Core.Tests/Parsers/LeafInputData/LeafInput-noData.csv
diff --git a/Core.Tests/Services/LeafInputData/LeafInput-tabSeparated.csv b/Core.Tests/Parsers/LeafInputData/LeafInput-tabSeparated.csv
similarity index 100%
rename from Core.Tests/Services/LeafInputData/LeafInput-tabSeparated.csv
rename to Core.Tests/Parsers/LeafInputData/LeafInput-tabSeparated.csv
diff --git a/Core.Tests/Services/LeafInputData/LeafInput-titlesRemoved.csv b/Core.Tests/Parsers/LeafInputData/LeafInput-titlesRemoved.csv
similarity index 100%
rename from Core.Tests/Services/LeafInputData/LeafInput-titlesRemoved.csv
rename to Core.Tests/Parsers/LeafInputData/LeafInput-titlesRemoved.csv
diff --git a/Core.Tests/Services/LeafInputData/LeafInput-valid.csv b/Core.Tests/Parsers/LeafInputData/LeafInput-valid.csv
similarity index 100%
rename from Core.Tests/Services/LeafInputData/LeafInput-valid.csv
rename to Core.Tests/Parsers/LeafInputData/LeafInput-valid.csv
diff --git a/Core.Tests/Services/LeafOutputData/cntrlbestparameters_Wild Capsicum.csv b/Core.Tests/Parsers/LeafOutputData/cntrlbestparameters_Wild Capsicum.csv
similarity index 100%
rename from Core.Tests/Services/LeafOutputData/cntrlbestparameters_Wild Capsicum.csv
rename to Core.Tests/Parsers/LeafOutputData/cntrlbestparameters_Wild Capsicum.csv
diff --git a/Core.Tests/Services/LeafOutputData/cntrlcomparison_Wild Capsicum.csv b/Core.Tests/Parsers/LeafOutputData/cntrlcomparison_Wild Capsicum.csv
similarity index 100%
rename from Core.Tests/Services/LeafOutputData/cntrlcomparison_Wild Capsicum.csv
rename to Core.Tests/Parsers/LeafOutputData/cntrlcomparison_Wild Capsicum.csv
diff --git a/Core.Tests/Services/LeafOutputData/cntrlparameters_Wild Capsicum.csv b/Core.Tests/Parsers/LeafOutputData/cntrlparameters_Wild Capsicum.csv
similarity index 100%
rename from Core.Tests/Services/LeafOutputData/cntrlparameters_Wild Capsicum.csv
rename to Core.Tests/Parsers/LeafOutputData/cntrlparameters_Wild Capsicum.csv
diff --git a/Core.Tests/Services/CntrlComparisonCsvParserTests.cs b/Core.Tests/Services/CntrlComparisonCsvParserTests.cs
deleted file mode 100644
index 9c9a6f1..0000000
--- a/Core.Tests/Services/CntrlComparisonCsvParserTests.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using NUnit.Framework;
-
-namespace LeafWeb.Core.Tests.Services
-{
- [TestFixture]
- public class CntrlComparisonCsvParserTests
- {
- protected const string ContentDirectory = @"Services\LeafOutputData\";
-
- private static FileInfo GetContentFile(string fileName)
- {
- var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ContentDirectory);
- return new FileInfo(path + fileName);
- }
-
- }
-}
diff --git a/Core/Charter/CurveData.cs b/Core/Charter/CurveData.cs
new file mode 100644
index 0000000..9c47287
--- /dev/null
+++ b/Core/Charter/CurveData.cs
@@ -0,0 +1,37 @@
+using System.IO;
+
+namespace LeafWeb.Core.Charter
+{
+ public class CurveData
+ {
+ private readonly string _curveId;
+
+ public string CurveId => _curveId;
+
+ // 1
+ public CurveParamSet FixedCndFixedCmp { get; }
+
+ // 2
+ public CurveParamSet FixedCndEstimatedCmp { get; }
+
+ // 3
+ public CurveParamSet EstimatedCndFixedCmp { get; }
+
+ // 4
+ public CurveParamSet EstimatedCndEstimatedCmp { get; }
+
+
+ public CurveData(TextReader sr, ref int lineNbr)
+ {
+ // For each curve in the output file there are four sets of data.
+
+ FixedCndFixedCmp = new CurveParamSet(sr, ref lineNbr, ref _curveId);
+
+ FixedCndEstimatedCmp = new CurveParamSet(sr, ref lineNbr, ref _curveId);
+
+ EstimatedCndFixedCmp = new CurveParamSet(sr, ref lineNbr, ref _curveId);
+
+ EstimatedCndEstimatedCmp = new CurveParamSet(sr, ref lineNbr, ref _curveId);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core/Charter/CurveDataList.cs b/Core/Charter/CurveDataList.cs
new file mode 100644
index 0000000..3852d31
--- /dev/null
+++ b/Core/Charter/CurveDataList.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+using System.IO;
+
+namespace LeafWeb.Core.Charter
+{
+ public class CurveDataList
+ {
+ // Each element will be a PiscalCurve element that contains
+ // all of the output data for one curve.
+ public List CurveData { get; }
+
+ public CurveDataList()
+ {
+ CurveData = new List();
+ }
+
+ public bool ReadFromStream(StreamReader sr)
+ {
+ // Skip the first two lines.
+ sr.ReadLine();
+ sr.ReadLine();
+ var lineNbr = 2;
+
+ // Now, there should be one or more rows, delimited by a row that has
+ // CO2i in the first field. Read in all of these rows. The first field
+ // in each row is the curve ID (input filename).
+
+ var more = true;
+ while (more)
+ {
+ var curve = new CurveData(sr, ref lineNbr);
+
+ CurveData.Add(curve);
+
+ if (sr.EndOfStream)
+ more = false;
+ }
+ return true;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Core/Charter/CurveParamSet.cs b/Core/Charter/CurveParamSet.cs
new file mode 100644
index 0000000..2c00a04
--- /dev/null
+++ b/Core/Charter/CurveParamSet.cs
@@ -0,0 +1,166 @@
+using System.Collections.Generic;
+using System.IO;
+using LeafWeb.Core.Utility;
+
+namespace LeafWeb.Core.Charter
+{
+ public class CurveParamSet
+ {
+ public List AnetMeasChloro1Data { get; } = new List(); // y=AnetMeas column, x=PCO2c, for PointLimitType=1
+ public List AnetMeasChloro2Data { get; } = new List(); // y=AnetMeas column, x=PCO2c, for PointLimitType=2
+ public List AnetMeasChloro3Data { get; } = new List(); // y=AnetMeas column, x=PCO2c, for PointLimitType=3
+ public List AnetMeasInter1Data { get; } = new List(); // y=AnetMeas column, x=PCO2i, for PointLimitType=1
+ public List AnetMeasInter2Data { get; } = new List(); // y=AnetMeas column, x=PCO2i, for PointLimitType=2
+ public List AnetMeasInter3Data { get; } = new List(); // y=AnetMeas column, x=PCO2i, for PointLimitType=3
+
+ public List AcChloroData { get; } = new List();
+ public List AjChloroData { get; } = new List();
+ public List AtChloroData { get; } = new List();
+ public List AcInterData { get; } = new List();
+ public List AjInterData { get; } = new List();
+ public List AtInterData { get; } = new List();
+
+ public CurveParamSet(TextReader sr, ref int lineNbr, ref string curveId)
+ {
+ bool curveIdSet = false, doneWithAnet = false;
+ string line;
+ List phrases;
+
+ while (!doneWithAnet)
+ {
+ lineNbr++;
+ line = sr.ReadLine();
+ if (line == null)
+ {
+ throw new ParseException("Unexpected end-of-file at line " + lineNbr);
+ }
+
+ phrases = SplitCsvLine(line);
+
+ var firstField = phrases[0];
+ if (firstField.Equals("CO2i"))
+ {
+ doneWithAnet = true;
+ }
+ else
+ {
+ // The fields on the line:
+ // Column Name
+ // 0 CurveID
+ // 1 ChlFlUse
+ // 2 FitGi
+ // 3 FitGamma
+ // 4 FitKco
+ // 5 FitRd
+ // 6 FitAlpha
+ // 7 LimitCombina
+ // 8 PCO2i
+ // 9 PCO2c
+ // 10 AnetMeas
+ // 11 AnetCal
+ // 12 weitedrms
+ // 13 PointLimitType
+
+ if (!curveIdSet)
+ {
+ curveId = firstField;
+ curveIdSet = true;
+ }
+ var xyPoint1 = new XyPoint(phrases[9], phrases[10]); // AnetMeas(y), PCO2c(x)
+ var xyPoint2 = new XyPoint(phrases[8], phrases[10]);
+ var pointLimitType = int.Parse(phrases[13]);
+ switch (pointLimitType)
+ {
+ case 1:
+ AnetMeasChloro1Data.Add(xyPoint1);
+ AnetMeasInter1Data.Add(xyPoint2);
+ break;
+ case 2:
+ AnetMeasChloro2Data.Add(xyPoint1);
+ AnetMeasInter2Data.Add(xyPoint2);
+ break;
+ case 3:
+ AnetMeasChloro3Data.Add(xyPoint1);
+ AnetMeasInter3Data.Add(xyPoint2);
+ break;
+ }
+ }
+ }
+
+ // The next set of lines will have three pairs of x,y-coordinates to save.
+ // A blank line signals the end of the data.
+
+ var moreData = true;
+ while (moreData)
+ {
+ // The fields on the line:
+ // Column Name
+ // 0 CO2i
+ // 1 CO2cc
+ // 2 Ac
+ // 3 CO2cj
+ // 4 Aj
+ // 5 CO2ct
+ // 6 At
+
+ lineNbr++;
+ line = sr.ReadLine();
+ if (line == null)
+ {
+ throw new ParseException("Unexpected end-of-file at line " + lineNbr);
+ }
+
+ if (line.Length == 0)
+ moreData = false;
+ else
+ {
+ phrases = SplitCsvLine(line);
+ var xyPoint1 = new XyPoint(phrases[1],phrases[2]); // Ac(y),CO2cc(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AcChloroData.Add(xyPoint1);
+ xyPoint1 = new XyPoint(phrases[3], phrases[4]); // Aj(y),CO2cj(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AjChloroData.Add(xyPoint1);
+ xyPoint1 = new XyPoint(phrases[5], phrases[6]); // At(y),CO2ct(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AtChloroData.Add(xyPoint1);
+
+ xyPoint1 = new XyPoint(phrases[0], phrases[2]); // Ac(y),CO2i(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AcInterData.Add(xyPoint1);
+ xyPoint1 = new XyPoint(phrases[0], phrases[4]); // Aj(y),CO2i(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AjInterData.Add(xyPoint1);
+ xyPoint1 = new XyPoint(phrases[0], phrases[6]); // At(y),CO2i(x)
+ if (xyPoint1.YIsInRange(-20.0, 50.0))
+ AtInterData.Add(xyPoint1);
+ }
+ }
+ }
+
+ ///
+ /// This method assumes that the argument is a comma-, blank-, or
+ /// tab-separated set of strings. It returns an ArrayList of those
+ /// quantities. Consecutive commas will be returned as an empty string,
+ /// but all empty strings at the end of the line will be thrown away.
+ ///
+ ///
+ ///
+ private static List SplitCsvLine(string line)
+ {
+ int i;
+ var separator = new [] {',', ' ', '\t'};
+ var phrases = line.Split(separator);
+
+ var retPhrases = new List();
+ for (i = 0; i < phrases.Length; i++)
+ {
+ var phrase = phrases[i].Trim();
+ if (phrase.Length > 0)
+ retPhrases.Add(phrase);
+ }
+
+ return retPhrases;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core/Models/XYPoint.cs b/Core/Charter/XYPoint.cs
similarity index 60%
rename from Core/Models/XYPoint.cs
rename to Core/Charter/XYPoint.cs
index 1226f3d..b562267 100644
--- a/Core/Models/XYPoint.cs
+++ b/Core/Charter/XYPoint.cs
@@ -1,14 +1,11 @@
-using System;
-
-namespace LeafWeb.Core.Models
+namespace LeafWeb.Core.Charter
{
- [Serializable]
public class XyPoint
{
- public XyPoint(String x, String y)
+ public XyPoint(string x, string y)
{
- X = Double.Parse(x);
- Y = Double.Parse(y);
+ X = double.Parse(x);
+ Y = double.Parse(y);
}
public double X { get; private set; }
diff --git a/Core/Core.csproj b/Core/Core.csproj
index f9c0f43..3a76622 100644
--- a/Core/Core.csproj
+++ b/Core/Core.csproj
@@ -56,16 +56,26 @@
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/Core/Models/CntrlComparison.cs b/Core/Models/CntrlComparison.cs
index f96b6e4..85022c4 100644
--- a/Core/Models/CntrlComparison.cs
+++ b/Core/Models/CntrlComparison.cs
@@ -1,49 +1,15 @@
-using System;
using System.Collections.Generic;
-using System.IO;
+using System.ComponentModel.DataAnnotations;
namespace LeafWeb.Core.Models
{
- [Serializable]
+ ///
+ /// The file 'cntrlcomparison.csv', which is in comma-separated-value format, contains outputs from PISCAL that
+ /// facilitates examination of how well the fitting is.
+ ///
public class CntrlComparison
{
- // Each element will be a PiscalCurve element that contains
- // all of the output data for one curve.
- private readonly List _curveData;
-
- public CntrlComparison()
- {
- _curveData = new List();
- }
-
- public bool ReadFromStream(StreamReader sr, ref String errorMsg)
- {
- // Skip the first two lines.
- sr.ReadLine();
- sr.ReadLine();
- var lineNbr = 2;
-
- // Now, there should be one or more rows, delimited by a row that has
- // CO2i in the first field. Read in all of these rows. The first field
- // in each row is the curve ID (input filename).
-
- var more = true;
- while (more)
- {
- var curve = new CurveData(sr, ref errorMsg, ref lineNbr);
- if (errorMsg.Length > 0)
- return false;
-
- _curveData.Add(curve);
- if (sr.EndOfStream)
- more = false;
- }
- return true;
- }
-
- public List GetCurveData()
- {
- return _curveData;
- }
+ public virtual IEnumerable FittingInfo { get; set; }
+ public virtual IEnumerable PhotosyntheticInfo { get; set; }
}
}
\ No newline at end of file
diff --git a/Core/Models/CntrlComparisonFittingInfo.cs b/Core/Models/CntrlComparisonFittingInfo.cs
new file mode 100644
index 0000000..4e866fa
--- /dev/null
+++ b/Core/Models/CntrlComparisonFittingInfo.cs
@@ -0,0 +1,118 @@
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
+
+namespace LeafWeb.Core.Models
+{
+ ///
+ /// First model prediction is calculated at each sampling point.
+ /// Then model prediction is calculated at many selected levels of intercelluar CO2 partial pressure
+ /// levels under three limitation states to enable plotting curves.The same structure is then repeated for
+ /// each fitting of the same curve with different parameters to be estimated and with different curves. Note
+ /// that some common information about a curve is repeated for each data point to make the structure of the
+ /// file as regular as possible.
+ ///
+ public class CntrlComparisonFittingInfo
+ {
+ ///
+ /// the curve identifier, repeated for each point
+ ///
+ [ParseInfo(1)]
+ public string CurveID { get; set; }
+
+ ///
+ /// whether or not chlorophyll fluorescence data are used for identifying the limitation states
+ /// 0 = not used, 1 = used. (Currently this choice is still under evaluation and therefore ChlFlUse? = 0).
+ ///
+ [ParseInfo(2, alternateTitle: "ChlFlUse?")]
+ public bool ChlFlUse {get; set; }
+
+ ///
+ /// whether or not the internal conductance (gi) is fitted for.
+ /// 0 = not fitted and gi is either infinite or fixed at the value provided by the user.
+ /// 1 = gi is estiamted, repeated for each point
+ ///
+ [ParseInfo(3, alternateTitle: "FitGi?")]
+ public bool FitGi {get; set;}
+
+ ///
+ /// whether or not the chloroplastic CO2 partial pressure photocompensation point is
+ /// fitted for. 0= not fitted, a prescribed value is used, repeated for each point
+ ///
+ [ParseInfo(4, alternateTitle: "FitGamma*?")]
+ public bool FitGammaStar {get; set; }
+
+ ///
+ /// whether or not the apparent Michaelis - Menten constant Kco = Kc(1+O/Ko) is fitted for.
+ /// 0= not fitted, a prescribed value is calculated from Kc, Ko and the oxygen partial pressure.
+ /// 1= Fitted for, repeated for each point
+ ///
+ [ParseInfo(5, alternateTitle: "FitKco?")]
+ public bool FitKco {get; set; }
+
+ ///
+ /// whether the dark respiration is fitted for. 0= not fitted for, a prescribed value is used.
+ /// 1= fitted for. repeated for each point
+ ///
+ [ParseInfo(6, alternateTitle: "FitRd?")]
+ public bool FitRd {get; set;}
+
+ ///
+ /// whether alpha (the non-returned fraction of the glycolate carbon
+ /// recycled in the photorespiratory cycle) is fitted for.
+ /// 0= not fitted for and alpha = 0
+ /// 1= fitted for, repeated for each point
+ ///
+ [ParseInfo(7, alternateTitle: "FitAlpha?")]
+ public bool FitAlpha {get; set;}
+
+ ///
+ /// the combination of limitation states present in the A/Ci dataset. Parameters
+ /// are estimated for those limitation states that occur in the measured curve, repeated for each point
+ /// Rubisco: Rubisco limitation(Vcmax, Kco)
+ /// RuBP: RuBP regeneration limitation(J)
+ /// Tpu: export limitation(TPU, alpha)
+ ///
+ [ParseInfo(8)]
+ public string LimitCombina { get; set; }
+
+ ///
+ /// intercellular CO2 partial pressure
+ ///
+ [ParseInfo(9, units:"Pa")]
+ public double PCO2i { get; set; }
+
+ ///
+ /// chloroplastic CO2 partial pressure corresponding to the PCO2i of a point.
+ /// PCO2c=PCO2i-AnetCal/internal conductance
+ ///
+ [ParseInfo(10, units:"Pa")]
+ public double PCO2c { get; set; }
+
+ ///
+ /// measured net assimilation rate
+ ///
+ [ParseInfo(11, units: "umol/m2/s")]
+ public double AnetMeas { get; set; }
+
+ ///
+ /// calculated net assimilation rate
+ ///
+ [ParseInfo(12, units: "umol/m2/s")]
+ public double AnetCal { get; set; }
+
+ // TODO: readme has weitedrms, data file has PCOiCal
+ // weitedrms - (umol/m2/s), root mean square error of the fitting (weiting coefficient =1)
+ [ParseInfo(13)]
+ public double PCOiCal { get; set; }
+
+ ///
+ /// 1 = point limited by rubisco
+ /// 2 = point limited by RuBP regeneration
+ /// 3 = point limited by TPU
+ ///
+ [ParseInfo(14, units:"1,2,3")]
+ public int PointLimitType { get; set; }
+
+ public virtual CntrlComparison CntrlComparison { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Core/Models/CntrlComparisonPhotosyntheticInfo.cs b/Core/Models/CntrlComparisonPhotosyntheticInfo.cs
new file mode 100644
index 0000000..26abc6b
--- /dev/null
+++ b/Core/Models/CntrlComparisonPhotosyntheticInfo.cs
@@ -0,0 +1,56 @@
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
+
+namespace LeafWeb.Core.Models
+{
+ ///
+ /// The second section gives photosynthesis for each of the three limitation states at selected values of intercellular
+ /// partial pressure.Note that the corresponding values of chloraplastic CO2 partial pressure depend on the limitation state.
+ ///
+ public class CntrlComparisonPhotosyntheticInfo
+ {
+ ///
+ /// intercellular CO2 partial pressure
+ ///
+ [ParseInfo(1, units:"Pa")]
+ public double CO2i { get; set; }
+
+ ///
+ /// Chloraplastic CO2 partial pressure for Rubisco limited photosynthesis
+ ///
+ [ParseInfo(2, units: "Pa")]
+ public double CO2cc { get; set; }
+
+ ///
+ /// Rubisco-limited net assimilation rate calculated with the optimized parameters
+ ///
+ [ParseInfo(3, units: "umol/m2/s")]
+ public double Ac { get; set; }
+
+ ///
+ /// Chloraplastic CO2 partial pressure for RuBP regeneration limited photosynthesis
+ ///
+ [ParseInfo(4, units:"Pa")]
+ public double CO2cj { get; set; }
+
+ ///
+ /// RuBP regeneration-limited net assimilation rate calculated with estimated parameters
+ ///
+ [ParseInfo(5, units: "umol/m2/s")]
+ public double Aj { get; set; }
+
+ ///
+ /// Chloraplastic CO2 partial pressure for export limited photosynthesis
+ ///
+ [ParseInfo(6, units: "Pa")]
+ public double CO2ct { get; set; }
+
+ ///
+ /// TPU-limited net assimilation rate calculated with estimated parameters
+ ///
+ [ParseInfo(7, units: "umol/m2/s")]
+ public double At { get; set; }
+
+ public virtual CntrlComparison CntrlComparison { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Core/Models/CurveData.cs b/Core/Models/CurveData.cs
deleted file mode 100644
index e0f75db..0000000
--- a/Core/Models/CurveData.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using System;
-using System.IO;
-
-namespace LeafWeb.Core.Models
-{
- [Serializable]
- public class CurveData
- {
- private readonly string _curveId;
- private readonly CurveParamSet _paramSet1;
- private readonly CurveParamSet _paramSet2;
- private readonly CurveParamSet _paramSet3;
- private readonly CurveParamSet _paramSet4;
-
- public CurveData(StreamReader sr, ref string errMsg, ref int lineNbr)
- {
- // For each curve in the output file there are four sets of data.
-
- _paramSet1 = new CurveParamSet(sr, ref errMsg, ref lineNbr, ref _curveId);
- if (errMsg.Length > 0)
- return;
-
- _paramSet2 = new CurveParamSet(sr, ref errMsg, ref lineNbr, ref _curveId);
- if (errMsg.Length > 0)
- return;
-
- _paramSet3 = new CurveParamSet(sr, ref errMsg, ref lineNbr, ref _curveId);
- if (errMsg.Length > 0)
- return;
-
- _paramSet4 = new CurveParamSet(sr, ref errMsg, ref lineNbr, ref _curveId);
- }
-
- public string GetCurveId()
- {
- return _curveId;
- }
-
- public CurveParamSet CndctFixedCmpPntFixedParams()
- {
- return _paramSet1;
- }
-
- public CurveParamSet CndctFixedCmpPntEstimatedParams()
- {
- return _paramSet2;
- }
-
- public CurveParamSet CndctEstimatedCmpPntFixedParams()
- {
- return _paramSet3;
- }
-
- public CurveParamSet CndctEstimatedCmpPntEstimatedParams()
- {
- return _paramSet4;
- }
- }
-}
\ No newline at end of file
diff --git a/Core/Models/CurveParamSet.cs b/Core/Models/CurveParamSet.cs
deleted file mode 100644
index 6470d32..0000000
--- a/Core/Models/CurveParamSet.cs
+++ /dev/null
@@ -1,238 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Text;
-
-namespace LeafWeb.Core.Models
-{
- [Serializable]
- public class CurveParamSet
- {
- private readonly List _anetMeasChloro1Data; // y=AnetMeas column, x=PCO2c, for PointLimitType=1
- private readonly List _anetMeasChloro2Data; // y=AnetMeas column, x=PCO2c, for PointLimitType=2
- private readonly List _anetMeasChloro3Data; // y=AnetMeas column, x=PCO2c, for PointLimitType=3
- private readonly List _anetMeasInter1Data; // y=AnetMeas column, x=PCO2i, for PointLimitType=1
- private readonly List _anetMeasInter2Data; // y=AnetMeas column, x=PCO2i, for PointLimitType=2
- private readonly List _anetMeasInter3Data; // y=AnetMeas column, x=PCO2i, for PointLimitType=3
- private readonly List _acChloroData; // y=Ac column, x=CO2cc column
- private readonly List _ajChloroData; // y=Aj column, x=CO2cj column
- private readonly List _atChloroData; // y=At column, x=CO2ct column
- private readonly List _acInterData; // y=Ac column, x=CO2i column
- private readonly List _ajInterData; // y=Aj column, x=CO2i column
- private readonly List _atInterData; // y=At column, x=CO2i column
-
- public CurveParamSet()
- {
- }
-
- public CurveParamSet(StreamReader sr, ref String errMsg, ref int lineNbr, ref String curveId)
- {
- bool curveIdSet = false, doneWithAnet = false;
- String line;
- var errorMsg = new StringBuilder("");
- List phrases;
- XyPoint xyPoint1;
- _anetMeasChloro1Data = new List();
- _anetMeasChloro2Data = new List();
- _anetMeasChloro3Data = new List();
- _anetMeasInter1Data = new List();
- _anetMeasInter2Data = new List();
- _anetMeasInter3Data = new List();
- _acChloroData = new List();
- _ajChloroData = new List();
- _atChloroData = new List();
- _acInterData = new List();
- _ajInterData = new List();
- _atInterData = new List();
-
- while (!doneWithAnet)
- {
- lineNbr++;
- line = sr.ReadLine();
- if (line == null)
- {
- errorMsg.Append("Unexpected end-of-file at line " + lineNbr);
- sr.Close();
- errMsg = errorMsg.ToString();
- return;
- }
-
- phrases = SplitCsvLine(line);
-
- String firstField = phrases[0];
- if (firstField.Equals("CO2i"))
- {
- doneWithAnet = true;
- }
- else
- {
- // The fields on the line:
- // Column Name
- // 0 CurveID
- // 1 ChlFlUse
- // 2 FitGi
- // 3 FitGamma
- // 4 FitKco
- // 5 FitRd
- // 6 FitAlpha
- // 7 LimitCombina
- // 8 PCO2i
- // 9 PCO2c
- // 10 AnetMeas
- // 11 AnetCal
- // 12 weitedrms
- // 13 PointLimitType
-
- if (!curveIdSet)
- {
- curveId = firstField;
- curveIdSet = true;
- }
- xyPoint1 = new XyPoint(phrases[9], phrases[10]); // AnetMeas(y), PCO2c(x)
- var xyPoint2 = new XyPoint(phrases[8], phrases[10]);
- var pointLimitType = Int32.Parse(phrases[13]);
- switch (pointLimitType)
- {
- case 1:
- _anetMeasChloro1Data.Add(xyPoint1);
- _anetMeasInter1Data.Add(xyPoint2);
- break;
- case 2:
- _anetMeasChloro2Data.Add(xyPoint1);
- _anetMeasInter2Data.Add(xyPoint2);
- break;
- case 3:
- _anetMeasChloro3Data.Add(xyPoint1);
- _anetMeasInter3Data.Add(xyPoint2);
- break;
- }
- }
- }
-
- // The next set of lines will have three pairs of x,y-coordinates to save.
- // A blank line signals the end of the data.
-
- var moreData = true;
- while (moreData)
- {
- // The fields on the line:
- // Column Name
- // 0 CO2i
- // 1 CO2cc
- // 2 Ac
- // 3 CO2cj
- // 4 Aj
- // 5 CO2ct
- // 6 At
-
- lineNbr++;
- line = sr.ReadLine();
- if (line == null)
- {
- errorMsg.Append("Unexpected end-of-file at line " + lineNbr);
- sr.Close();
- errMsg = errorMsg.ToString();
- return;
- }
-
- if (line.Length == 0)
- moreData = false;
- else
- {
- phrases = SplitCsvLine(line);
- xyPoint1 = new XyPoint(phrases[1],phrases[2]); // Ac(y),CO2cc(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _acChloroData.Add(xyPoint1);
- xyPoint1 = new XyPoint(phrases[3], phrases[4]); // Aj(y),CO2cj(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _ajChloroData.Add(xyPoint1);
- xyPoint1 = new XyPoint(phrases[5], phrases[6]); // At(y),CO2ct(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _atChloroData.Add(xyPoint1);
-
- xyPoint1 = new XyPoint(phrases[0], phrases[2]); // Ac(y),CO2i(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _acInterData.Add(xyPoint1);
- xyPoint1 = new XyPoint(phrases[0], phrases[4]); // Aj(y),CO2i(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _ajInterData.Add(xyPoint1);
- xyPoint1 = new XyPoint(phrases[0], phrases[6]); // At(y),CO2i(x)
- if (xyPoint1.YIsInRange(-20.0, 50.0))
- _atInterData.Add(xyPoint1);
- }
- }
- }
-
- ///
- /// This method assumes that the argument is a comma-, blank-, or
- /// tab-separated set of strings. It returns an ArrayList of those
- /// quantities. Consecutive commas will be returned as an empty string,
- /// but all empty strings at the end of the line will be thrown away.
- ///
- ///
- ///
- private static List SplitCsvLine(String line)
- {
- int i;
- var separator = new [] {',', ' ', '\t'};
- var phrases = line.Split(separator);
-
- //int lastNonBlank = -1;
- var retPhrases = new List();
- for (i = 0; i < phrases.Length; i++)
- {
- var phrase = phrases[i].Trim();
- if (phrase.Length > 0)
- // lastNonBlank = i;
- retPhrases.Add(phrase);
- }
-
- return retPhrases;
- }
-
- public List> GetAnetMeasData()
- {
- var list = new List>
- {
- _anetMeasChloro1Data,
- _anetMeasChloro2Data,
- _anetMeasChloro3Data,
- _anetMeasInter1Data,
- _anetMeasInter2Data,
- _anetMeasInter3Data
- };
-
- return list;
- }
-
- public List GetAcChloroData()
- {
- return _acChloroData;
- }
-
- public List GetAjChloroData()
- {
- return _ajChloroData;
- }
-
- public List GetAtChloroData()
- {
- return _atChloroData;
- }
-
- public List GetAcInterData()
- {
- return _acInterData;
- }
-
- public List GetAjInterData()
- {
- return _ajInterData;
- }
-
- public List GetAtInterData()
- {
- return _atInterData;
- }
- }
-}
\ No newline at end of file
diff --git a/Core/Models/LeafInput.cs b/Core/Models/LeafInput.cs
index b253744..51359c1 100644
--- a/Core/Models/LeafInput.cs
+++ b/Core/Models/LeafInput.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
-using LeafWeb.Core.Services;
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
namespace LeafWeb.Core.Models
{
diff --git a/Core/Models/LeafInputData.cs b/Core/Models/LeafInputData.cs
index a7b5a7c..396d9f9 100644
--- a/Core/Models/LeafInputData.cs
+++ b/Core/Models/LeafInputData.cs
@@ -1,5 +1,6 @@
using System.ComponentModel.DataAnnotations;
-using LeafWeb.Core.Services;
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
namespace LeafWeb.Core.Models
{
diff --git a/Core/Models/LeafInputPhotosynthetic.cs b/Core/Models/LeafInputPhotosynthetic.cs
index 0b61462..0408930 100644
--- a/Core/Models/LeafInputPhotosynthetic.cs
+++ b/Core/Models/LeafInputPhotosynthetic.cs
@@ -1,4 +1,5 @@
-using LeafWeb.Core.Services;
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
namespace LeafWeb.Core.Models
{
diff --git a/Core/Models/LeafInputSite.cs b/Core/Models/LeafInputSite.cs
index 5a93aab..0237623 100644
--- a/Core/Models/LeafInputSite.cs
+++ b/Core/Models/LeafInputSite.cs
@@ -1,4 +1,5 @@
-using LeafWeb.Core.Services;
+using LeafWeb.Core.Parsers;
+using LeafWeb.Core.Utility;
namespace LeafWeb.Core.Models
{
diff --git a/Core/Parsers/CntrlComparisonParser.cs b/Core/Parsers/CntrlComparisonParser.cs
new file mode 100644
index 0000000..355399d
--- /dev/null
+++ b/Core/Parsers/CntrlComparisonParser.cs
@@ -0,0 +1,79 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using LeafWeb.Core.Models;
+using LeafWeb.Core.Utility;
+
+namespace LeafWeb.Core.Parsers
+{
+ public class CntrlComparisonParser : CsvParserBase
+ {
+ public CntrlComparisonParser(FileSystemInfo csvFile) : base(csvFile)
+ {
+ }
+
+ public CntrlComparison[] Parse()
+ {
+ var fittingTitles = GetNextCsvRowValues();
+ if (fittingTitles == null)
+ throw new ParseException($"Could not read data header row on line number {CsvReader.Row}");
+ var fitUnits = GetNextCsvRowValues();
+ if (fitUnits == null)
+ throw new ParseException($"Could not read data units row on line number {CsvReader.Row}");
+
+ return ParseCntrlComparisonSet(fittingTitles).ToArray();
+ }
+
+ private IEnumerable ParseCntrlComparisonSet(string[] fittingTitles)
+ {
+ var endOfFile = false;
+ while (!endOfFile)
+ {
+ // First Section
+ var fittingValues = new List();
+ string[] photosyntheticTitles;
+ while (true)
+ {
+ var values = GetNextCsvRowValues();
+ if (CsvReader.IsRecordEmpty())
+ throw new ParseException($"Encountered empty line while readding fitting info on line {CsvReader.Row}");
+ if (values == null) // end of file
+ yield break;
+ if (ParsedObjectFactory.IsPropertiesTitlesMatch(values))
+ {
+ photosyntheticTitles = values;
+ break;
+ }
+ fittingValues.Add(values);
+ }
+
+ var photosyntheticValues = new List();
+ while (true)
+ {
+ var values = GetNextCsvRowValues();
+ if (CsvReader.IsRecordEmpty()) // end of set
+ break;
+ if (values == null)
+ {
+ endOfFile = true;
+ break;
+ }
+ photosyntheticValues.Add(values);
+ }
+
+ var fittingInfo =
+ ParsedObjectFactory
+ .Create(fittingTitles, fittingValues.ToArray());
+ var photosyntheticInfo =
+ ParsedObjectFactory
+ .Create(photosyntheticTitles, photosyntheticValues.ToArray());
+
+ yield return new CntrlComparison
+ {
+ FittingInfo = fittingInfo,
+ PhotosyntheticInfo = photosyntheticInfo
+ };
+ }
+ }
+ }
+}
diff --git a/Core/Parsers/CsvParserBase.cs b/Core/Parsers/CsvParserBase.cs
new file mode 100644
index 0000000..5fd31e9
--- /dev/null
+++ b/Core/Parsers/CsvParserBase.cs
@@ -0,0 +1,49 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using CsvHelper;
+using CsvHelper.Configuration;
+
+namespace LeafWeb.Core.Parsers
+{
+ public class CsvParserBase : IDisposable
+ {
+ protected readonly FileSystemInfo CsvFile;
+ private readonly StreamReader _reader;
+ protected readonly CsvReader CsvReader;
+
+ protected CsvParserBase(FileSystemInfo csvFile)
+ {
+ CsvFile = csvFile;
+ if (!csvFile.Exists)
+ throw new FileNotFoundException($"Cannot find file '{csvFile.Name}'");
+
+ _reader = File.OpenText(csvFile.FullName);
+ var csvConfiguration = new CsvConfiguration { HasHeaderRecord = false, IgnoreBlankLines = false};
+ CsvReader = new CsvReader(_reader, csvConfiguration);
+ }
+
+ protected string[] GetNextCsvRowValues()
+ {
+ // get values from row
+ if (!CsvReader.Read())
+ return null;
+
+ // put all the values from this row into an array
+ var values = new List();
+ var index = 0;
+ string value;
+ while (CsvReader.TryGetField(index, out value))
+ {
+ values.Add(value);
+ index++;
+ }
+ return values.ToArray();
+ }
+
+ public void Dispose()
+ {
+ _reader.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core/Services/LeafInputCsvParser.cs b/Core/Parsers/LeafInputCsvParser.cs
similarity index 67%
rename from Core/Services/LeafInputCsvParser.cs
rename to Core/Parsers/LeafInputCsvParser.cs
index 18f5263..3740659 100644
--- a/Core/Services/LeafInputCsvParser.cs
+++ b/Core/Parsers/LeafInputCsvParser.cs
@@ -2,33 +2,22 @@
using System.Collections.Generic;
using System.IO;
using CsvHelper;
-using CsvHelper.Configuration;
using LeafWeb.Core.Models;
+using LeafWeb.Core.Utility;
-namespace LeafWeb.Core.Services
+namespace LeafWeb.Core.Parsers
{
- public class LeafInputCsvParser : IDisposable
+ public class LeafInputCsvParser : CsvParserBase
{
- private readonly FileSystemInfo _csvFile;
- private readonly StreamReader _reader;
- private readonly CsvReader _csv;
-
- public LeafInputCsvParser(FileSystemInfo csvFile)
+ public LeafInputCsvParser(FileSystemInfo csvFile) : base(csvFile)
{
- _csvFile = csvFile;
- if (!csvFile.Exists)
- throw new FileNotFoundException("Cannot find file '" + csvFile.Name + "'");
-
- _reader = File.OpenText(csvFile.FullName);
- var csvConfiguration = new CsvConfiguration {HasHeaderRecord = false};
- _csv = new CsvReader(_reader, csvConfiguration);
}
public LeafInput Parse()
{
// First 10 lines
var leafInput = ParseLeafInput();
- leafInput.FileName = _csvFile.Name;
+ leafInput.FileName = CsvFile.Name;
// Next 3 (Header, Units, and Values)
leafInput.Site = ParseLeafInputSite();
@@ -47,46 +36,28 @@ namespace LeafWeb.Core.Services
var items = new List();
for (var i = 0; i < 10; i++)
{
- if (!_csv.Read())
- throw new ParseException("Could not read line number " + _csv.Row);
+ if (!CsvReader.Read())
+ throw new ParseException("Could not read line number " + CsvReader.Row);
string field;
- if (!_csv.TryGetField(0, out field))
- throw new ParseException("Could not read first field on line number " + _csv.Row);
+ if (!CsvReader.TryGetField(0, out field))
+ throw new ParseException("Could not read first field on line number " + CsvReader.Row);
items.Add(field);
}
return ParsedObjectFactory.Create(items.ToArray());
}
- private string[] GetNextCsvRowValues()
- {
- // get values from row
- if (!_csv.Read())
- return null;
-
- // put all the values from this row into an array
- var values = new List();
- var index = 0;
- string value;
- while (_csv.TryGetField(index, out value))
- {
- values.Add(value);
- index++;
- }
- return values.ToArray();
- }
-
private LeafInputSite ParseLeafInputSite()
{
var titles = GetNextCsvRowValues();
if (titles == null)
- throw new ParseException("Could not read site header row on line number " + _csv.Row);
+ throw new ParseException("Could not read site header row on line number " + CsvReader.Row);
var units = GetNextCsvRowValues();
if (units == null)
- throw new ParseException("Could not read site units row on line number " + _csv.Row);
+ throw new ParseException("Could not read site units row on line number " + CsvReader.Row);
var values = GetNextCsvRowValues();
if (values == null)
- throw new ParseException("Could not read site value row on line number " + _csv.Row);
+ throw new ParseException("Could not read site value row on line number " + CsvReader.Row);
var items = new List>();
for (var i = 0; i < titles.Length && i < values.Length; i++)
@@ -101,13 +72,13 @@ namespace LeafWeb.Core.Services
{
var titles = GetNextCsvRowValues();
if (titles == null)
- throw new ParseException("Could not read photosynthetic header row on line number " + _csv.Row);
+ throw new ParseException("Could not read photosynthetic header row on line number " + CsvReader.Row);
var units = GetNextCsvRowValues();
if (units == null)
- throw new ParseException("Could not read photosynthetic units row on line number " + _csv.Row);
+ throw new ParseException("Could not read photosynthetic units row on line number " + CsvReader.Row);
var values = GetNextCsvRowValues();
if (values == null)
- throw new ParseException("Could not read photosynthetic value row on line number " + _csv.Row);
+ throw new ParseException("Could not read photosynthetic value row on line number " + CsvReader.Row);
var items = new List>();
for (var i = 0; i < titles.Length && i < values.Length; i++)
@@ -122,10 +93,10 @@ namespace LeafWeb.Core.Services
{
var titles = GetNextCsvRowValues();
if (titles == null)
- throw new ParseException("Could not read data header row on line number " + _csv.Row);
+ throw new ParseException("Could not read data header row on line number " + CsvReader.Row);
var units = GetNextCsvRowValues();
if (units == null)
- throw new ParseException("Could not read data units row on line number " + _csv.Row);
+ throw new ParseException("Could not read data units row on line number " + CsvReader.Row);
var valueArrays = new List();
while (true)
@@ -138,11 +109,6 @@ namespace LeafWeb.Core.Services
return ParsedObjectFactory.Create(titles, valueArrays.ToArray());
}
- public void Dispose()
- {
- _reader.Dispose();
- }
-
public static void ExportCsv(string filename, IEnumerable leafInputs)
{
using (var textWriter = new StreamWriter(filename))
diff --git a/Core/Utility/BoolTypeConverter.cs b/Core/Utility/BoolTypeConverter.cs
new file mode 100644
index 0000000..eccfe67
--- /dev/null
+++ b/Core/Utility/BoolTypeConverter.cs
@@ -0,0 +1,47 @@
+using System;
+using System.ComponentModel;
+using System.Globalization;
+
+namespace LeafWeb.Core.Utility
+{
+ // http://stackoverflow.com/a/19022931/99492
+ public class BoolTypeConverter : TypeConverter
+ {
+ public static void Register()
+ {
+ TypeDescriptor.AddAttributes(typeof(Boolean),
+ new TypeConverterAttribute(typeof(BoolTypeConverter)));
+ }
+
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ if (destinationType == typeof(bool))
+ {
+ return true;
+ }
+ return base.CanConvertTo(context, destinationType);
+ }
+
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (value is string)
+ {
+ var s = value as string;
+ if (string.IsNullOrEmpty(s))
+ return false;
+ switch (s.Trim().ToUpper())
+ {
+ case "TRUE":
+ case "YES":
+ case "1":
+ case "-1":
+ return true;
+
+ default:
+ return false;
+ }
+ }
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Core/Services/ParseException.cs b/Core/Utility/ParseException.cs
similarity index 80%
rename from Core/Services/ParseException.cs
rename to Core/Utility/ParseException.cs
index 13ba277..5cda437 100644
--- a/Core/Services/ParseException.cs
+++ b/Core/Utility/ParseException.cs
@@ -1,6 +1,6 @@
using System;
-namespace LeafWeb.Core.Services
+namespace LeafWeb.Core.Utility
{
public class ParseException : Exception
{
diff --git a/Core/Services/ParseInfoAttribute.cs b/Core/Utility/ParseInfoAttribute.cs
similarity index 95%
rename from Core/Services/ParseInfoAttribute.cs
rename to Core/Utility/ParseInfoAttribute.cs
index ef39b68..a313a6b 100644
--- a/Core/Services/ParseInfoAttribute.cs
+++ b/Core/Utility/ParseInfoAttribute.cs
@@ -1,9 +1,8 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
-using LeafWeb.Core.Utility;
-namespace LeafWeb.Core.Services
+namespace LeafWeb.Core.Utility
{
[AttributeUsage(AttributeTargets.Property)]
public class ParseInfoAttribute : Attribute
diff --git a/Core/Services/ParsedObjectFactory.cs b/Core/Utility/ParsedObjectFactory.cs
similarity index 67%
rename from Core/Services/ParsedObjectFactory.cs
rename to Core/Utility/ParsedObjectFactory.cs
index 2ea33e0..a08e11e 100644
--- a/Core/Services/ParsedObjectFactory.cs
+++ b/Core/Utility/ParsedObjectFactory.cs
@@ -1,12 +1,19 @@
using System;
+using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Fasterflect;
-namespace LeafWeb.Core.Services
+namespace LeafWeb.Core.Utility
{
public static class ParsedObjectFactory where T : new()
{
+ static ParsedObjectFactory()
+ {
+ // register this for usage by TypeDescriptor.GetConverter
+ BoolTypeConverter.Register();
+ }
+
private static PropertyInfo[] GetProperties()
{
var propertyInfos = typeof(T).Properties();
@@ -52,7 +59,7 @@ namespace LeafWeb.Core.Services
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
- throw new ParseException(string.Format("Cannot convert value '{0}' for {1} at line number {2}", value, property.Name, lineNumber));
+ throw new ParseException($"Cannot convert value '{value}' for {property.Name} at line number {lineNumber}");
property.Set(obj, convertedVal);
}
}
@@ -83,7 +90,7 @@ namespace LeafWeb.Core.Services
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
- throw new ParseException(string.Format("Cannot convert value '{0}' for {1} in position {2}", value, property.Name, position));
+ throw new ParseException($"Cannot convert value '{value}' for {property.Name} in position {position}");
property.Set(obj, convertedVal);
}
}
@@ -113,7 +120,7 @@ namespace LeafWeb.Core.Services
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
- throw new ParseException(string.Format("Cannot convert value '{0}' for {1} in position {2}", value, property.Name, position));
+ throw new ParseException($"Cannot convert value '{value}' for {property.Name} in position {position}");
property.Set(obj, convertedVal);
}
}
@@ -127,26 +134,55 @@ namespace LeafWeb.Core.Services
private static PropertyInfo MatchProperty(PropertyInfo[] properties, string title, int position)
{
- var property =
- properties
- .FirstOrDefault(p => p.Attribute().IsTitleMatch(title));
+ return
+ properties.FirstOrDefault(p => p.Attribute().IsTitleMatch(title)) ??
+ properties.FirstOrDefault(p => p.Attribute().IsPositionMatch(position));
+ }
- if (property == null)
+ private static PropertyInfo MatchPropertyExact(PropertyInfo[] properties, string title, int position)
+ {
+ return
+ properties
+ .FirstOrDefault(p =>
+ {
+ var attribute = p.Attribute();
+ return attribute.IsTitleMatch(title) && attribute.IsPositionMatch(position);
+ });
+ }
+
+ public static bool IsPropertiesTitlesMatch(string[] titles)
+ {
+ var properties = GetProperties();
+ var propertyMatch = 0;
+ var propertyNoMatch = 0;
+
+ for (var i = 0; i < titles.Length; i++)
{
- property =
- properties
- .FirstOrDefault(p => p.Attribute().IsPositionMatch(position));
+ var title = titles[i];
+ var position = i + 1;
+ if (string.IsNullOrEmpty(title))
+ continue;
+
+ var property = MatchPropertyExact(properties, title, position);
+
+ if (property != null)
+ propertyMatch++;
+ else
+ propertyNoMatch++;
}
- return property;
+
+ return propertyMatch / (double) propertyNoMatch > .9;
}
private static bool TryConvertValue(PropertyInfo property, object value, out object convertedValue)
{
- try
+ try
{
// http://stackoverflow.com/questions/3531318/convert-changetype-fails-on-nullable-types
var t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
- convertedValue = Convert.ChangeType(value, t);
+ // convertedValue = Convert.ChangeType(value, t);
+ var converter = TypeDescriptor.GetConverter(t);
+ convertedValue = converter.ConvertTo(value, t);
}
catch (Exception)
{
diff --git a/Core/Utility/StringExtensions.cs b/Core/Utility/StringExtensions.cs
index cffcb11..2a8b942 100644
--- a/Core/Utility/StringExtensions.cs
+++ b/Core/Utility/StringExtensions.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Globalization;
+using System.Globalization;
using System.Linq;
namespace LeafWeb.Core.Utility
@@ -9,9 +8,9 @@ namespace LeafWeb.Core.Utility
public static string SplitCamelCase(this string str)
{
return str.Aggregate(
- String.Empty,
+ string.Empty,
(current, c) =>
- current + (Char.IsUpper(c) && current.Length > 0 ? " " + c : c.ToString(CultureInfo.InvariantCulture)));
+ current + (char.IsUpper(c) && current.Length > 0 ? " " + c : c.ToString(CultureInfo.InvariantCulture)));
}
}
}
diff --git a/Web/Charter/LeafWebCharter.ascx.cs b/Web/Charter/LeafWebCharter.ascx.cs
index 8f9e9c3..1d9b842 100644
--- a/Web/Charter/LeafWebCharter.ascx.cs
+++ b/Web/Charter/LeafWebCharter.ascx.cs
@@ -6,23 +6,24 @@ using System.IO;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
+using LeafWeb.Core.Charter;
using LeafWeb.Core.Models;
namespace LeafWeb.Web.Charter
{
public partial class LeafWebCharter : UserControl
{
- protected void ReadFile(StreamReader sr, ref String errMsg)
+ protected void ReadFile(StreamReader sr, ref string errMsg)
{
- var pisOut = new CntrlComparison();
- if (!pisOut.ReadFromStream(sr, ref errMsg))
+ var pisOut = new CurveDataList();
+ if (!pisOut.ReadFromStream(sr))
{
ErrorLBL.Text = errMsg;
ErrorLBL.Visible = true;
return;
}
Session["LeafChartData"] = pisOut;
- var aCopy = (CntrlComparison) Session["LeafChartData"];
+ var aCopy = (CurveDataList) Session["LeafChartData"];
// The data was successfully read from the file. We must now
// display the curveIDs from the file and prompt the user to pick
@@ -31,12 +32,12 @@ namespace LeafWeb.Web.Charter
var curveDT = new DataTable();
curveDT.Columns.Add(new DataColumn("curveID"));
- var curveData = pisOut.GetCurveData();
+ var curveData = pisOut.CurveData;
for (var i = 0; i < curveData.Count; i++)
{
var aCurve = curveData[i];
var dr = curveDT.NewRow();
- dr["curveID"] = aCurve.GetCurveId();
+ dr["curveID"] = aCurve.CurveId;
curveDT.Rows.Add(dr);
}
@@ -48,7 +49,7 @@ namespace LeafWeb.Web.Charter
// cntrlcomparison
- public void ProduceCharts(CntrlComparison pisOut)
+ public void ProduceCharts(CurveDataList pisOut)
{
// If the session has timed out, use the selected index from the GridView
// to determine which job to chart.
@@ -62,14 +63,14 @@ namespace LeafWeb.Web.Charter
// break;
// }
- var curveData = pisOut.GetCurveData();
+ var curveData = pisOut.CurveData;
var curve = curveData[1];
- var curveId = curve.GetCurveId();
- CurveSeries(curveId, curve.CndctFixedCmpPntFixedParams(), ChartChloro1, ChartInter1, "Internal conductance fixed, compensation point and M-M constants fixed");
- CurveSeries(curveId, curve.CndctFixedCmpPntEstimatedParams(), ChartChloro2, ChartInter2, "Internal conductance fixed, compensation point and M-M constants estimated");
- CurveSeries(curveId, curve.CndctEstimatedCmpPntFixedParams(), ChartChloro3, ChartInter3, "Internal conductance estimated, compensation point and M-M constants fixed");
- CurveSeries(curveId, curve.CndctEstimatedCmpPntEstimatedParams(), ChartChloro4, ChartInter4, "Internal conductance estimated, compensation point and M-M constants estimated");
+ var curveId = curve.CurveId;
+ CurveSeries(curveId, curve.FixedCndFixedCmp, ChartChloro1, ChartInter1, "Internal conductance fixed, compensation point and M-M constants fixed");
+ CurveSeries(curveId, curve.FixedCndEstimatedCmp, ChartChloro2, ChartInter2, "Internal conductance fixed, compensation point and M-M constants estimated");
+ CurveSeries(curveId, curve.EstimatedCndFixedCmp, ChartChloro3, ChartInter3, "Internal conductance estimated, compensation point and M-M constants fixed");
+ CurveSeries(curveId, curve.EstimatedCndEstimatedCmp, ChartChloro4, ChartInter4, "Internal conductance estimated, compensation point and M-M constants estimated");
}
private static Chart GetChart()
@@ -203,11 +204,9 @@ namespace LeafWeb.Web.Charter
private static void CurveSeries(string curveId, CurveParamSet paramSet, Chart chloroChart, Chart interChart, string chartTitle)
{
- var paramSetAnet = paramSet.GetAnetMeasData();
-
- var anetMeasChloro1 = paramSetAnet[0];
- var anetMeasChloro2 = paramSetAnet[1];
- var anetMeasChloro3 = paramSetAnet[2];
+ var anetMeasChloro1 = paramSet.AnetMeasChloro1Data;
+ var anetMeasChloro2 = paramSet.AnetMeasChloro2Data;
+ var anetMeasChloro3 = paramSet.AnetMeasChloro3Data;
// Set the points for the symbol series for paramater set 1, chloroplastic
setAnetMeasPoints(anetMeasChloro1, chloroChart.Series["Rubisco-limited"]);
@@ -217,9 +216,9 @@ namespace LeafWeb.Web.Charter
setAnetMeasPoints(anetMeasChloro3, tpuSeries);
chloroChart.Series.Add(tpuSeries);
- var anetMeasInter1 = paramSetAnet[3];
- var anetMeasInter2 = paramSetAnet[4];
- var anetMeasInter3 = paramSetAnet[5];
+ var anetMeasInter1 = paramSet.AnetMeasInter1Data;
+ var anetMeasInter2 = paramSet.AnetMeasInter2Data;
+ var anetMeasInter3 = paramSet.AnetMeasInter3Data;
// Set the points for the symbol series for paramater set 1, intercellular
setAnetMeasPoints(anetMeasInter1, interChart.Series["Rubisco-limited"]);
@@ -229,18 +228,18 @@ namespace LeafWeb.Web.Charter
setAnetMeasPoints(anetMeasInter3, tpuSeries);
interChart.Series.Add(tpuSeries);
- var acChloroList = paramSet.GetAcChloroData();
- var ajChloroList = paramSet.GetAjChloroData();
- var atChloroList = paramSet.GetAtChloroData();
+ var acChloroList = paramSet.AcChloroData;
+ var ajChloroList = paramSet.AjChloroData;
+ var atChloroList = paramSet.AtChloroData;
// Set the points on the asymptote curve for parameter set 1, chloroplast
setAsymptotePoints(acChloroList, chloroChart.Series["acCurve"]);
setAsymptotePoints(ajChloroList, chloroChart.Series["ajCurve"]);
setAsymptotePoints(atChloroList, chloroChart.Series["atCurve"]);
- var acInterList = paramSet.GetAcInterData();
- var ajInterList = paramSet.GetAjInterData();
- var atInterList = paramSet.GetAtInterData();
+ var acInterList = paramSet.AcInterData;
+ var ajInterList = paramSet.AjInterData;
+ var atInterList = paramSet.AtInterData;
// Set the points on the asymptote curve for parameter set 1, intercellular
setAsymptotePoints(acInterList, interChart.Series["acCurve"]);
@@ -251,8 +250,7 @@ namespace LeafWeb.Web.Charter
var titleFont = new Font("Times New Roman", 12, FontStyle.Bold);
var title = new Title("LeafWeb curveID = " + curveId +
- "\n" + chartTitle);
- title.Font = titleFont;
+ "\n" + chartTitle) {Font = titleFont};
chloroChart.Titles.Add(title);
interChart.Titles.Add(title);
diff --git a/Web/Content/Documents/aci_pce_final.pdf b/Web/Content/Documents/aci_pce_final.pdf
index 353a8c7..1817602 100644
Binary files a/Web/Content/Documents/aci_pce_final.pdf and b/Web/Content/Documents/aci_pce_final.pdf differ
diff --git a/Web/Web.csproj b/Web/Web.csproj
index 529fb3d..fa798cb 100644
--- a/Web/Web.csproj
+++ b/Web/Web.csproj
@@ -143,8 +143,6 @@
-
-
LeafWebCharter.ascx
ASPXCodeBehind
@@ -152,8 +150,6 @@
LeafWebCharter.ascx
-
-
@@ -173,7 +169,7 @@
-
+
Web.config
@@ -185,6 +181,12 @@
+
+
+ {25baed75-7e75-4d11-90d9-358472054df6}
+ Core
+
+
10.0
$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)