using System;
using System.Collections.Generic;
using System.Linq;
using DHI.Generic.MikeZero.DFS;
using DHI.Generic.MikeZero.DFS.dfs123;
namespace org.openda.dotnet.DHIStochObserver
{
///
/// Reads a DFS2 file.
/// - Assumptions:
/// 1) Equidistant time series.
/// 2) All items in the dfs2 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 DFS2Reader : IDfsRead
{
private readonly Dfs2File _dfs2File;
private readonly int _numTimeSteps;
private readonly int _numItems;
private readonly List _itemIDs;
private readonly List _times;
private readonly double _deleteValueDouble;
private readonly float _deleteValueFloat;
private readonly List _xyLayerPoints;
///
/// dfs2 reader. Gets information from the dfs file, and reads data.
///
/// full path string to dfs2 file.
public DFS2Reader(string dfsfile)
{
throw new NotImplementedException("ToDo dfs2");
// Open the file as a generic dfs file
_dfs2File = DfsFileFactory.Dfs2FileOpen(dfsfile);
// Header information is contained in the IDfsFileInfo
IDfsFileInfo fileInfo = _dfs2File.FileInfo;
// Check for dfs compliance
CheckDFSCompliance();
// Number of time steps (same for all items)
_numTimeSteps = fileInfo.TimeAxis.NumberOfTimeSteps;
// Number of variable items in dfs2
_numItems = _dfs2File.ItemInfo.Count;
// Add the IDs to list (Keys)
_itemIDs = new List();
foreach (var itemInfo in _dfs2File.ItemInfo)
{
_itemIDs.Add(itemInfo.Name);
}
_times = _dfs2File.FileInfo.TimeAxis.GetDateTimes().ToList();
// Delelte Values
_deleteValueDouble = _dfs2File.FileInfo.DeleteValueDouble;
_deleteValueFloat = _dfs2File.FileInfo.DeleteValueFloat;
_xyLayerPoints = new List();
foreach (var itemInfo in _dfs2File.ItemInfo)
{
throw new NotImplementedException("ToDo dfs2");
}
}
///
/// 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 (_dfs2File.ItemInfo[itemNum].DataType == DfsSimpleType.Float)
{
var datastruct = (IDfsItemData)_dfs2File.ReadItemTimeStep(itemNum + 1, timeIdx);
// ReSharper disable CompareOfFloatsByEqualityOperator
if (datastruct.Data[0] != _deleteValueFloat)
// ReSharper restore CompareOfFloatsByEqualityOperator
{
return Convert.ToDouble(datastruct.Data[0]);
}
}
else if (_dfs2File.ItemInfo[itemNum].DataType == DfsSimpleType.Double)
{
var datastruct = (IDfsItemData)_dfs2File.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 (_dfs2File.FileInfo.TimeAxis.TimeAxisType != TimeAxisType.CalendarEquidistant)
{
throw new Exception("Error in dfs0 file. Only CalendarEquidistant supported");
}
// CHECK if not all items are "Instantaneous", then error
foreach (var iteminfo in _dfs2File.ItemInfo)
{
if (iteminfo.ValueType != DataValueType.Instantaneous)
{
throw new Exception("Error in dfs0 file. Only Instantaneous items supported");
}
}
}
}
}