using System; using System.Collections; using System.Collections.Generic; using System.Linq; using DHI.Generic.MikeZero.DFS; using System.IO; namespace org.openda.dotnet.DHIStochObserver { /// /// Reads a DFS0 file. /// - Assumptions: /// 1) Equidistant time series. /// 2) All items in the dfs0 are the same variable type. /// 3) the variableID (key) is contained in the title. /// 4) Each item has a name containing the x,y,z coordinate (where z is an integer layer). /// 5) Instantaneous time series only. /// public class Dfs0Reader : DFSBase, IDfsRead { private readonly IDfsFile _dfs0File ; private readonly int _numTimeSteps; private readonly int _firstTimeStepIndex; private readonly double _refdateMJD; private readonly int _numItems; private readonly List _itemIDs; private readonly List _quantities; private readonly List _times; private readonly double _deleteValueDouble; private readonly float _deleteValueFloat; private readonly List _xyLayerPoints; //private readonly List _exchangeItems; //private readonly List _timeSeries; /// /// DFS0 reader. Gets information from the dfs file, and reads data. /// /// full path string to dfs0 file. public Dfs0Reader(string dfsfile) : base(dfsfile) { // Set ObservationFile if (!File.Exists(dfsfile)) throw new FileNotFoundException("\n ERROR: DFS File Not Found! \n Could not find: {0} \n", dfsfile); // Determine Type string fileExtension = Path.GetExtension(dfsfile); if (System.String.Compare(fileExtension, ".dfs0", System.StringComparison.OrdinalIgnoreCase) == 0) { fileExtension = Path.GetExtension(dfsfile); } else { throw new Exception("\n ERROR: Observation File Type Incorrect! Expecting dfs0. \n \n"); } // Open the file as a generic dfs file _dfs0File = DfsFileFactory.DfsGenericOpen(dfsfile); // Header information is contained in the IDfsFileInfo IDfsFileInfo fileInfo = _dfs0File.FileInfo; // Check for dfs compliance CheckDFSCompliance(); // Number of time steps (same for all items) _numTimeSteps = fileInfo.TimeAxis.NumberOfTimeSteps; // Starting from... int _firstTimeStepIndex = fileInfo.TimeAxis.FirstTimeStepIndex; // Number of variable items in dfs0 _numItems = _dfs0File.ItemInfo.Count; // Add the IDs to list (Keys) _itemIDs = new List(); _quantities = new List(); _xyLayerPoints = new List(); foreach (var itemInfo in _dfs0File.ItemInfo) { String name = itemInfo.Name; var coords = name.Split(','); double x = Convert.ToDouble(coords[0]); double y = Convert.ToDouble(coords[1]); int zLayer = Convert.ToInt32(coords[2]); _quantities.Add(_dfs0File.FileInfo.FileTitle); _itemIDs.Add(name); _xyLayerPoints.Add(new XYLayerPoint(x, y, zLayer)); } //Gather all times _times = _dfs0File.FileInfo.TimeAxis.GetDateTimes().ToList(); _times = _timesteps; DateTime firstTime = _times[0]; if (_dfs0File.FileInfo.TimeAxis.TimeAxisType != TimeAxisType.CalendarEquidistant){ //Handle pseudo irreggular files double[] dates = new double[_numTimeSteps]; //just make 1 bigger for easy indexing for (int iTimeStep = _firstTimeStepIndex; iTimeStep < _numTimeSteps; iTimeStep++){ for (int iItem = 1; iItem < _numItems+1; iItem++){ IDfsItemData data1 = _dfs0File.ReadItemTimeStep(iItem, iTimeStep); double offsetTime = data1.Time; if (iItem==1){ dates[iTimeStep]=offsetTime; } else { if (Math.Abs(offsetTime-dates[iTimeStep])>1.0){ throw new Exception("Non Equidistant Calander is not regular"); } } } if (iTimeStep > 0) { _times[iTimeStep] =_times[0].AddSeconds(dates[iTimeStep]); } } } IList infoAllTimes = _dfs0File.ItemInfo; String TimeSeriesName=infoAllTimes[0].Name; // Delelte Values _deleteValueDouble = _dfs0File.FileInfo.DeleteValueDouble; _deleteValueFloat = _dfs0File.FileInfo.DeleteValueFloat; } /// /// Get a dictionary of datetime,double values with real data. /// The order or returned data is by time then time series. /// ie. down the column then accross. /// /// start time (not included) /// end time (inclusive) /// Dictionary of real values with corresponding DateTime public List GetDataFromTimeRange(DateTime startTime, DateTime endTime) { List timeIndicesWithinRange = new List(); List valueSet = new List(); // Find the start and end indexes for (int i = 0; i < _times.Count; i++) { // if t_start 0 && DateTime.Compare( _times[i], endTime ) <= 0 ) { timeIndicesWithinRange.Add(i); } } for (int i = 0; i < _numItems; i++) { for (int j = 0; j < timeIndicesWithinRange.Count; j++) { double? value = GetDataValue(i, timeIndicesWithinRange[j]); if(value != null) { valueSet.Add(new DataPoint(_times[timeIndicesWithinRange[j]], (double)value, _xyLayerPoints[i], _itemIDs[i])); } } } return valueSet; } private double? GetDataValue(int itemNum, int timeIdx) { if (_dfs0File.ItemInfo[itemNum].DataType == DfsSimpleType.Float) { var datastruct = (IDfsItemData)_dfs0File.ReadItemTimeStep(itemNum + 1, timeIdx); // ReSharper disable CompareOfFloatsByEqualityOperator if (datastruct.Data[0] != _deleteValueFloat) // ReSharper restore CompareOfFloatsByEqualityOperator { return Convert.ToDouble(datastruct.Data[0]); } } else if (_dfs0File.ItemInfo[itemNum].DataType == DfsSimpleType.Double) { var datastruct = (IDfsItemData)_dfs0File.ReadItemTimeStep(itemNum + 1, timeIdx); // ReSharper disable CompareOfFloatsByEqualityOperator if (datastruct.Data[0] != _deleteValueDouble) // ReSharper restore CompareOfFloatsByEqualityOperator { return datastruct.Data[0]; } } // if values are not real, return null. return null; } /// /// Return the start date of the DFS file. /// public DateTime StartTime { get { return _times[0]; } } /// /// Return the end date of the DFS file. /// public DateTime EndTime { get { return _times[_times.Count-1]; } } /// /// Return number of time steps in dfs file. Does not check for NaN /// public int NumberTimeSteps { get { return _numTimeSteps; }} /// /// Return number of items in the dfs file. /// public int NumberOfItems { get { return _numItems; } } /// /// Return the ID Keys of each item. /// public string[] ItemIDs { get { return _itemIDs.ToArray(); } } /// /// Checks if the dfs is compliant (calendar, equidistant, instantaneous items...). /// private void CheckDFSCompliance() { // CHECK if Calendar Equidistant (only one supported). if (_dfs0File.FileInfo.TimeAxis.TimeAxisType != TimeAxisType.CalendarEquidistant) { System.Console.WriteLine("Disables test on CalendarEquidistant"); //throw new Exception("Error in dfs0 file. Only CalendarEquidistant supported"); } // CHECK if not all items are "Instantaneous", then error foreach (var iteminfo in _dfs0File.ItemInfo) { if (iteminfo.ValueType != DataValueType.Instantaneous) { throw new Exception("Error in dfs0 file. Only Instantaneous items supported"); } } } } }