OpenTemp.CalculateMonthlyAverages()

        /// <summary>
        /// Calculate the overall average every month.
        /// </summary>
        /// <remarks>The temperature for every cell is computed using all stations within 1000km, with the weight of each station
        /// linearly decreasing to 0 at 1000km.</remarks>
        /// <returns>Number of monthly averages calculated if successful; -ve for error code</returns>
        protected int CalculateMonthlyAverages(bool useOffsets)
        {
            // Initialize
            _monthlyAverages.Clear();
            Dictionary<string, float> stationAverages = new Dictionary<string, float>();
            // Log
            if (!useOffsets)
            {
                OpenTemp.WriteLine(string.Format("Generating monthly averages for the entire region..."));
            }
            else
            {
                OpenTemp.WriteLine(string.Format("Re-generating monthly averages with series offsets..."));
            }
            int nMonths = 0;
            // Iterate over all months (starting in 1880)
            DateTime date = new DateTime(1880, 1, 1);
            DateTime today = DateTime.Today;
            while (date < today)
            {
                stationAverages.Clear();

                // Store average readings for all stations
                foreach (Station station in _stations.Values)
                {
                    // Station average
                    int nSeries = 0;
                    float stationAverage = 0.0f;
                    foreach (Byte series in station.SeriesNumbers)
                    {
                        float seriesOffset = (useOffsets ? station.GetSeriesOffset(series) : 0.0f);
                        SortedList<DateTime, float?> readings = station.GetReadings(series);
                        if (readings.ContainsKey(date))
                        {
                            if (!readings[date].HasValue) continue;
                            stationAverage += readings[date].Value + seriesOffset;
                            nSeries++;
                        }
                    }
                    if (nSeries == 0) continue;
                    stationAverage /= (float)nSeries;

                    // Store the station average
                    stationAverages.Add(station.StationID, stationAverage);
                }

                // Iterate over all mesh cells
                float overallAverage = 0.0f;
                float cellAreaInAverage = 0.0f;
                foreach (Cell cell in _cells)
                {
                    float weight = 0.0f;
                    float weightSum = 0.0f;
                    float cellAverage = 0.0f;

                    foreach (CellStation cellStation in cell.Stations)
                    {
                        if (stationAverages.ContainsKey(cellStation.station.StationID))
                        {
                            weight = cellStation.weight;
                            weightSum += weight;
                            cellAverage += weight * stationAverages[cellStation.station.StationID];
                        }
                    }
                    if (weightSum > 0.0f)
                    {
                        cellAverage /= weightSum;
                        overallAverage += cellAverage * cell.Area;
                        cellAreaInAverage += cell.Area;
                    }
                }
                if (cellAreaInAverage > 0)
                {
                    overallAverage /= cellAreaInAverage;
                    _monthlyAverages.Add(date, overallAverage);      // TODO: Remove hard-code 0.1x scale
                }

                // Iterate and continue
                // TODO: Add error handling for months with no station data
                nMonths++;
                date = date.AddMonths(1);
                if (date.Month == 1)
                {
                    OpenTemp.Write(string.Format(" {0}", date.Year));
                }
            }

            // Done
            OpenTemp.WriteLine();
            OpenTemp.WriteLine(string.Format("  Monthly overall averages completed"));
            return nMonths;
        }