/// <summary>Format a summary string about the weather file</summary> private void WriteSummary() { StringBuilder summary = new StringBuilder(); summary.AppendLine("File name: " + this.weatherData.FileName); summary.AppendLine("Latitude : " + this.weatherData.Latitude.ToString()); summary.AppendLine("TAV : " + String.Format("{0, 2:f2}", this.weatherData.Tav)); summary.AppendLine("AMP : " + String.Format("{0, 2:f2}", this.weatherData.Amp)); summary.AppendLine("Start : " + this.weatherData.StartDate.ToShortDateString()); summary.AppendLine("End : " + this.weatherData.EndDate.ToShortDateString()); summary.AppendLine(""); // Make sure the data in the table consists of full years of data i.e. // exclude the first and/or last year of data if they aren't complete. DataTable table = this.weatherData.GetAllData(); if (table != null && table.Rows.Count > 0) { DateTime firstDate = DataTableUtilities.GetDateFromRow(table.Rows[0]); DateTime lastDate = DataTableUtilities.GetDateFromRow(table.Rows[table.Rows.Count - 1]); if (firstDate.DayOfYear != 1) { firstDate = new DateTime(firstDate.Year + 1, 1, 1); } if (lastDate.Day != 31 || lastDate.Month != 12) { lastDate = new DateTime(lastDate.Year - 1, 12, 31); } double[] yearlyRainfall = MathUtilities.YearlyTotals(table, "Rain", firstDate, lastDate); double[] monthlyRainfall = MathUtilities.AverageMonthlyTotals(table, "rain", firstDate, lastDate); double[] monthlyMaxT = MathUtilities.AverageDailyTotalsForEachMonth(table, "maxt", firstDate, lastDate); double[] monthlyMinT = MathUtilities.AverageDailyTotalsForEachMonth(table, "mint", firstDate, lastDate); // long term average rainfall if (yearlyRainfall.Length != 0) { double totalYearlyRainfall = MathUtilities.Sum(yearlyRainfall); int numYears = lastDate.Year - firstDate.Year + 1; double meanYearlyRainfall = totalYearlyRainfall / numYears; double stddev = MathUtilities.StandardDeviation(yearlyRainfall); summary.AppendLine(String.Format("For years : {0} - {1}", firstDate.Year, lastDate.Year)); summary.AppendLine("Long term average yearly rainfall : " + String.Format("{0,3:f2}mm", meanYearlyRainfall)); summary.AppendLine("Yearly rainfall std deviation : " + String.Format("{0,3:f2}mm", stddev)); this.weatherDataView.Summarylabel = summary.ToString(); string title = String.Format("Long term average data for years : {0} - {1}", firstDate.Year, lastDate.Year); this.PopulateGraph(monthlyRainfall, monthlyMaxT, monthlyMinT, title); } } }
/// <summary> /// Create the array of monthly deciles for each month from the startDate. /// </summary> /// <param name="weatherData">The raw daily weather data</param> /// <param name="startDate">The starting date. The month is the start of the season.</param> /// <returns>Array of monthly deciles (from 10 - 100)</returns> private static double[,] CreatePercentileWeather(DataTable weatherData, DateTime startDate) { DateTime firstDate = DataTableUtilities.GetDateFromRow(weatherData.Rows[0]); DataView weatherView = new DataView(weatherData); weatherView.RowFilter = string.Format("Date >= #{0:yyyy-MM-dd}#", new DateTime(firstDate.Year, startDate.Month, startDate.Day)); // Create an array of lists, 1 for each month. List <double>[] sumsForEachMonth = new List <double> [12]; for (int i = 0; i < 12; i++) { sumsForEachMonth[i] = new List <double>(); } int currentMonth = startDate.Month; double sum = 0.0; double value; foreach (DataRowView row in weatherView) { // Get the date and rain for the row. DateTime rowDate = DataTableUtilities.GetDateFromRow(row.Row); value = Convert.ToDouble(row["rain"]); // get rain value if (currentMonth != rowDate.Month) // if new month then { sumsForEachMonth[currentMonth - 1].Add(sum); // store the month sum if (rowDate.Month == startDate.Month) // if back at the start of yearly period { sum = value; } currentMonth = rowDate.Month; } else { sum += value; //accumulate } } double[,] monthlyDeciles = new double[12, 10]; DateTime decileDate = new DateTime(startDate.Year, startDate.Month, 1);; for (int i = 0; i < 12; i++) { double[] sums = new double[sumsForEachMonth[i].Count]; Array.Copy(sumsForEachMonth[i].ToArray(), sums, sumsForEachMonth[i].Count); Array.Sort(sums); for (int dec = 1; dec <= 10; dec++) { monthlyDeciles[i, dec - 1] = MathUtilities.Percentile(sums, dec * 0.1); } } return(monthlyDeciles); }
/// <summary>Gets the last rainfall date in the observed data.</summary> /// <param name="observedData">The observed data.</param> /// <returns>The date of the last rainfall row or DateTime.MinValue if no data.</returns> private static DateTime GetLastRainfallDate(DataTable observedData) { if (observedData == null || observedData.Rows.Count == 0) { return(DateTime.MinValue); } int lastRowIndex = observedData.Rows.Count - 1; return(DataTableUtilities.GetDateFromRow(observedData.Rows[lastRowIndex])); }
/// <summary>Add a date column as the first column of a datatable.</summary> /// <param name="data">The data table to add the date to.</param> private static void AddDateColumn(DataTable data) { List <DateTime> dates = new List <DateTime>(); foreach (DataRow Row in data.Rows) { dates.Add(DataTableUtilities.GetDateFromRow(Row)); } DataTableUtilities.AddColumnOfObjects(data, "Date", dates.ToArray()); data.Columns["Date"].SetOrdinal(0); }
/// <summary> /// Find a row in the data table that matches a date. Will throw if not found. /// </summary> /// <param name="dateToFind">The date to find.</param> /// <returns>The index of the found row.</returns> private int FindRowForDate(DateTime dateToFind) { var firstDateInFile = DataTableUtilities.GetDateFromRow(data.Rows[0]); var rowIndex = (dateToFind - firstDateInFile).Days; // check to make sure dates are ok. if (rowIndex < 0) { throw new Exception($"Cannot find year in weather file. Year = {Years[currentYearIndex]}"); } if (DataTableUtilities.GetDateFromRow(data.Rows[rowIndex]) != dateToFind) { throw new Exception($"Non consecutive dates found in file {FileName}"); } return(rowIndex); }
/// <summary>Sums a column of a data table between dates.</summary> /// <param name="observedData">The data table.</param> /// <param name="columnName">Name of the column.</param> /// <param name="date1">The date1.</param> /// <param name="date2">The date2.</param> /// <returns>The sum of rainfall.</returns> private static double SumTableAfterDate(DataTable data, string columnName, DateTime date1) { double sum = 0; if (data.Columns.Contains("Rain")) { foreach (DataRow row in data.Rows) { DateTime rowDate = DataTableUtilities.GetDateFromRow(row); if (rowDate >= date1) { sum += Convert.ToDouble(row[columnName]); } } } return(sum); }
/// <summary>Sets the year in date column.</summary> /// <remarks> /// The patch data can go over a year i.e. starts in 2014 and goes into 2015. /// This method doesn't want to set all years to the one specified, rather /// it needs to work out what value needs to be subtracted from all years /// such that the first year of patch data is the same as the year specified. /// </remarks> /// <param name="patchData">The patch data.</param> /// <param name="year">The year to set the date to.</param> private static void SetYearInDateColumn(DataTable patchData, int year) { int firstYear = DataTableUtilities.GetDateFromRow(patchData.Rows[0]).Year; int offset = year - firstYear; DateTime[] dates = DataTableUtilities.GetColumnAsDates(patchData, "Date"); for (int i = 0; i < dates.Length; i++) { if (DateTime.IsLeapYear(dates[i].Year) && !DateTime.IsLeapYear(dates[i].Year + offset) && dates[i].Month == 2 && dates[i].Day == 29) { dates[i] = new DateTime(dates[i].Year + offset, dates[i].Month, 28); } else { dates[i] = new DateTime(dates[i].Year + offset, dates[i].Month, dates[i].Day); } } DataTableUtilities.AddColumnOfObjects(patchData, "Date", dates); }
/// <summary> /// Overlay data in fromData on top of toData for all years found in toData. /// </summary> /// <param name="fromData">Source data</param> /// <param name="toData">Destination data</param> public static void OverlayDataAllYears(DataTable fromData, DataTable toData) { DataTable clonedData = fromData.Copy(); // Loop through all years in the long term weather data and overlay the from data onto // each year of the to data if (clonedData.Rows.Count > 0) { int firstYear = DataTableUtilities.GetDateFromRow(toData.Rows[0]).Year; int lastYear = DataTableUtilities.GetDateFromRow(toData.Rows[toData.Rows.Count - 1]).Year; for (int year = firstYear; year <= lastYear; year++) { // Before overlaying the from data we need to change the year because the // OverlayData method uses date matching. SetYearInDateColumn(clonedData, year); // Now overlay the patch data. OverlayData(clonedData, toData, true); } } }
private void OnDoWeather(object sender, EventArgs e) { if (currentRowIndex >= data.Rows.Count) { throw new Exception("Have run out of weather data"); } var dateInFile = DataTableUtilities.GetDateFromRow(data.Rows[currentRowIndex]); if (currentYearIndex == -1 || (dateInFile.Day == StartDate.Day && dateInFile.Month == StartDate.Month)) { // Need to change years to next one in sequence. currentYearIndex++; if (currentYearIndex < Years.Length) { var dateToFind = new DateTime(Convert.ToInt32(Years[currentYearIndex]), StartDate.Month, StartDate.Day); currentRowIndex = FindRowForDate(dateToFind); } } MaxT = Convert.ToDouble(data.Rows[currentRowIndex]["MaxT"]); MinT = Convert.ToDouble(data.Rows[currentRowIndex]["MinT"]); Radn = Convert.ToDouble(data.Rows[currentRowIndex]["Radn"]); Rain = Convert.ToDouble(data.Rows[currentRowIndex]["Rain"]); if (data.Columns.Contains("VP")) { VP = Convert.ToDouble(data.Rows[currentRowIndex]["VP"]); } if (data.Columns.Contains("Wind")) { Wind = Convert.ToDouble(data.Rows[currentRowIndex]["Wind"]); } if (AirPressure == 0) { this.AirPressure = 1010; } currentRowIndex++; PreparingNewWeatherData?.Invoke(this, new EventArgs()); }
/// <summary> /// Add year, month, day and date columns to the specified Table. /// </summary> public static void AddDateToTable(DataTable table) { if (!table.Columns.Contains("Date")) { List <DateTime> dates = new List <DateTime>(); foreach (DataRow Row in table.Rows) { dates.Add(DataTableUtilities.GetDateFromRow(Row)); } DataTableUtilities.AddColumnOfObjects(table, "Date", dates.ToArray()); table.Columns["Date"].SetOrdinal(0); // remove year, day, pan, vp, code columns. int yearColumn = table.Columns.IndexOf("Year"); if (yearColumn != -1) { table.Columns.RemoveAt(yearColumn); } int dayColumn = table.Columns.IndexOf("Day"); if (dayColumn != -1) { table.Columns.RemoveAt(dayColumn); } int panColumn = table.Columns.IndexOf("pan"); if (panColumn != -1) { table.Columns.RemoveAt(panColumn); } int vpColumn = table.Columns.IndexOf("vp"); if (vpColumn != -1) { table.Columns.RemoveAt(vpColumn); } int codeColumn = table.Columns.IndexOf("code"); if (codeColumn != -1) { table.Columns.RemoveAt(codeColumn); } } }
/// <summary> /// Overlay data from table1 on top of table2 using the date in each row. Date /// dates in both tables have to exactly match before the data is overlaid. /// </summary> /// <param name="fromData">First data table</param> /// <param name="toData">The data table that will change</param> /// <param name="lowercaseCode">Lowercase the code?</param> public static void OverlayData(DataTable fromData, DataTable toData, bool lowercaseCode = false) { if (fromData != null && toData.Rows.Count > 0) { // This algorithm assumes that toData does not have missing days. DateTime firstDate = DataTableUtilities.GetDateFromRow(toData.Rows[0]); DateTime lastDate = DataTableUtilities.GetDateFromRow(toData.Rows[toData.Rows.Count - 1]); // Filter fromData so that it is in the same range as table2. DataView table1View = new DataView(fromData); table1View.RowFilter = string.Format("Date >= #{0:yyyy-MM-dd}# and Date <= #{1:yyyy-MM-dd}#", firstDate, lastDate); foreach (DataRowView table1Row in table1View) { DateTime table1Date = DataTableUtilities.GetDateFromRow(table1Row.Row); int table2RowIndex = (table1Date - firstDate).Days; if (table2RowIndex >= 0 && table2RowIndex < toData.Rows.Count) { DataRow table2Row = toData.Rows[table2RowIndex]; if (DataTableUtilities.GetDateFromRow(table2Row) == table1Date) { // Found the matching row OverlayRowData(table1Row.Row, table2Row, lowercaseCode); } else { throw new Exception("Non consecutive dates found in SILO data"); } } else { // Table 1 data is outside the range of table 2. } } } }
/// <summary>Format a summary string about the weather file</summary> private void WriteSummary(DataTable table) { StringBuilder summary = new StringBuilder(); summary.AppendLine("File name : " + this.weatherData.FileName); if (this.weatherData.ExcelWorkSheetName.Length > 0) { summary.AppendLine("Sheet Name: " + this.weatherData.ExcelWorkSheetName.ToString()); } summary.AppendLine("Latitude : " + this.weatherData.Latitude.ToString()); summary.AppendLine("TAV : " + String.Format("{0, 2:f2}", this.weatherData.Tav)); summary.AppendLine("AMP : " + String.Format("{0, 2:f2}", this.weatherData.Amp)); summary.AppendLine("Start : " + this.weatherData.StartDate.ToShortDateString()); summary.AppendLine("End : " + this.weatherData.EndDate.ToShortDateString()); summary.AppendLine(""); if (table != null && table.Rows.Count > 0) { dataFirstDate = DataTableUtilities.GetDateFromRow(table.Rows[0]); dataLastDate = DataTableUtilities.GetDateFromRow(table.Rows[table.Rows.Count - 1]); TimeSpan diff = dataLastDate - dataFirstDate; //modLMC - 16/03/2016 - don't change dates if data is within the same year if (diff.Days > 365) { if (dataFirstDate.DayOfYear != 1) { dataFirstDate = new DateTime(dataFirstDate.Year + 1, 1, 1); } } //modLMC - 16/03/2016 - don't change dates if data is within the same year if (dataFirstDate.Year != dataLastDate.Year) { if (dataLastDate.Day != 31 || dataLastDate.Month != 12) { dataLastDate = new DateTime(dataLastDate.Year - 1, 12, 31); } } double[] yearlyRainfall = MathUtilities.YearlyTotals(table, "Rain", dataFirstDate, dataLastDate); double[] monthlyRainfall = MathUtilities.AverageMonthlyTotals(table, "rain", dataFirstDate, dataLastDate); double[] monthlyMaxT = MathUtilities.AverageDailyTotalsForEachMonth(table, "maxt", dataFirstDate, dataLastDate); double[] monthlyMinT = MathUtilities.AverageDailyTotalsForEachMonth(table, "mint", dataFirstDate, dataLastDate); //what do we do if the date range is less than 1 year. //modlmc - 15/03/2016 - modified to pass in the "Month" values, and they may/may not contain a full year. if (monthlyRainfall.Length <= 12) { monthsToDisplay = DataTableUtilities.GetDistinctMonthsasStrings(table, dataFirstDate, dataLastDate); } // long term average rainfall if (yearlyRainfall.Length != 0) { double totalYearlyRainfall = MathUtilities.Sum(yearlyRainfall); int numYears = dataLastDate.Year - dataFirstDate.Year + 1; double meanYearlyRainfall = totalYearlyRainfall / numYears; double stddev = MathUtilities.StandardDeviation(yearlyRainfall); summary.AppendLine(String.Format("For years : {0} - {1}", dataFirstDate.Year, dataLastDate.Year)); summary.AppendLine("Long term average yearly rainfall : " + String.Format("{0,3:f2}mm", meanYearlyRainfall)); summary.AppendLine("Yearly rainfall std deviation : " + String.Format("{0,3:f2}mm", stddev)); string title = String.Format("Long term average data for years : {0} - {1}", dataFirstDate.Year, dataLastDate.Year); //modlmc - 15/03/2016 - modified to pass in the "Month" values, and they may/may not contain a full year. this.PopulateSummaryGraph(title, monthsToDisplay, monthlyRainfall, monthlyMaxT, monthlyMinT); } this.weatherDataView.Summarylabel = summary.ToString(); } }
/// <summary>Creates a monthly decile weather DataTable</summary> /// <param name="weatherData">The weather data.</param> /// <param name="startDate">First date for decile table.</param> /// <results>Montly decile data.</results> private static DataTable CreateDecileWeather(DataTable weatherData, DateTime startDate) { DateTime firstDate = DataTableUtilities.GetDateFromRow(weatherData.Rows[0]); DataView weatherView = new DataView(weatherData); weatherView.RowFilter = string.Format("Date >= #{0:yyyy-MM-dd}#", new DateTime(firstDate.Year, startDate.Month, startDate.Day)); // Create an array of lists, 1 for each month. List <double>[] sumsForEachMonth = new List <double> [12]; for (int i = 0; i < 12; i++) { sumsForEachMonth[i] = new List <double>(); } double sum = 0.0; foreach (DataRowView row in weatherView) { // Get the date and rain for the row. DateTime rowDate = DataTableUtilities.GetDateFromRow(row.Row); double value = Convert.ToDouble(row["rain"]); // Accumulate the value every day. sum += value; // At the end of each month, store the accumulated value into the right array element. // if (rowDate.AddDays(1).Day == 1) // end of month? - GOOD if (rowDate.Day == 1) // end of month? - REPRODUCE BUG IN YP { sumsForEachMonth[rowDate.Month - 1].Add(sum); } if (rowDate.Day == 1 && rowDate.Month == startDate.Month) { sum = value; } } DataTable decile = new DataTable(); decile.Columns.Add("Date", typeof(DateTime)); decile.Columns.Add("RainDecile1", typeof(double)); decile.Columns.Add("RainDecile5", typeof(double)); decile.Columns.Add("RainDecile9", typeof(double)); DateTime decileDate = new DateTime(startDate.Year, startDate.Month, 1);; for (int i = 0; i < 12; i++) { DataRow row = decile.NewRow(); row["Date"] = decileDate; if (i == 0) { row["RainDecile1"] = 0; row["RainDecile5"] = 0; row["RainDecile9"] = 0; } else { row["RainDecile1"] = GetValueForProbability(10, sumsForEachMonth[decileDate.Month - 1].ToArray()); row["RainDecile5"] = GetValueForProbability(50, sumsForEachMonth[decileDate.Month - 1].ToArray()); row["RainDecile9"] = GetValueForProbability(90, sumsForEachMonth[decileDate.Month - 1].ToArray()); } decile.Rows.Add(row); decileDate = decileDate.AddMonths(1); } return(decile); }