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");
}
}
}
}
}