/// <summary> /// объединяет два диапазона. Началом становится начало диапазона 1, а концом конец диапазона 2. За интервал принимается минимальный из двух /// </summary> /// <param name="rangeInterval1"></param> /// <param name="rangeInterval2"></param> /// <returns></returns> private static RangeInterval combineIntervals(RangeInterval rangeInterval1, RangeInterval rangeInterval2) { int i1 = (int)rangeInterval1.Interval; int i2 = (int)rangeInterval2.Interval; StandartIntervals inter = (StandartIntervals)Math.Min(i1, i2); return(new RangeInterval() { Diapason = new Diapason <DateTime>(rangeInterval1.Diapason.From, rangeInterval2.Diapason.To), Interval = inter }); }
/// <summary> /// обработать ряд и получить сведения о полноте, интервалах, количестве ошибок /// </summary> /// <returns></returns> public static QualityInfo ProcessRange(RawRange Range) { if (Range.Count < Vars.Options.QualifierSectionLength * 1.5) { return(null); } TimeSpan maxemptyspace = TimeSpan.MinValue; TimeSpan tmp; //поиск наибольшего пустого пропуска for (int i = 1; i < Range.Count; i++) { tmp = Range[i].Date - Range[i - 1].Date; if (tmp > maxemptyspace) { maxemptyspace = tmp; } } //поиск всех интервалов измерений //весь ряд делится на отрезки по 10 измерений. //на каждом отрезке ищется минимальный интервал, он принимается основным для этого отрезка //если в ряде встречаются только такие интервалы, то весь ряд заносится в результат с одним интервалом //иначе границей раздела интервалов берётся гранича разделов отрезков с разными интервалами List <RawItem> range = new List <RawItem>(Range); range.Sort(new DateTimeComparerRawItem()); //делим ряд по 10 измерений и для каждого отрезка находим минимальный интервал List <Diapason <int> > diapasons = new List <Diapason <int> >(); List <StandartIntervals> intervals = new List <StandartIntervals>(); //int k = 0; for (int i = 0; i < range.Count - Vars.Options.QualifierSectionLength; i += Vars.Options.QualifierSectionLength) { Diapason <int> d = new Diapason <int>(i, i + Vars.Options.QualifierSectionLength); TimeSpan minSpan = TimeSpan.MaxValue; for (int j = i; j < i + Vars.Options.QualifierSectionLength; j++) { // k++; TimeSpan nsp = range[j].Date > range[j + 1].Date ? range[j].Date - range[j + 1].Date : range[j + 1].Date - range[j].Date; if (nsp.TotalMinutes == 0) { continue; } if (nsp < minSpan && Enum.IsDefined(typeof(StandartIntervals), (int)nsp.TotalMinutes)) { minSpan = nsp; } if (nsp.TotalDays > Vars.Options.QualifierDaysToBeginMissing) { minSpan = TimeSpan.MaxValue; break; } } StandartIntervals interval; if (minSpan == TimeSpan.MaxValue) { interval = StandartIntervals.Missing; } else { int minutes = (int)minSpan.TotalMinutes; interval = (StandartIntervals)minutes; } diapasons.Add(d); intervals.Add(interval); } //поиск изменений в интервалах и добавление в общий список List <RangeInterval> rangeIntervals = new List <RangeInterval>(); int s1 = 0; for (int i = 0; i < intervals.Count - 1; i++) { if (intervals[i] != intervals[i + 1]) { DateTime sd = range[diapasons[s1].From].Date; DateTime ed = range[diapasons[i].To].Date; StandartIntervals inter = intervals[s1]; rangeIntervals.Add(new RangeInterval() { Diapason = new Diapason <DateTime>(sd, ed), Interval = inter }); s1 = i + 1; } } //добавление последнего DateTime end = range.Last().Date; //конец диапазона DateTime start; if (rangeIntervals.Count > 0) { start = rangeIntervals.Last().Diapason.To; } else { start = range[0].Date; } rangeIntervals.Add(new RangeInterval() { Diapason = new Diapason <DateTime>(start, end), Interval = intervals.Last() }); // добавление диапазона //обход всех найденных диапазонов и объединение маленьких или одинаковых до тех пор, пока будет совершено хоть одно объединение int minimal = Vars.Options.QualifierDaysToNewInterval * 24 * 60; bool hasCompl = true; while (hasCompl) { //если диапазонов нет или один, то выходим if (rangeIntervals.Count == 0 || rangeIntervals.Count == 1) { break; } //если диапазонов два, то ищем маленький диапазон в конце и в начале if (rangeIntervals.Count == 2) { if (rangeIntervals[0].LengthMinutes < minimal || rangeIntervals[1].LengthMinutes < minimal) //объединяем диапазоны и выходим { RangeInterval ri = combineIntervals(rangeIntervals[0], rangeIntervals[1]); rangeIntervals.Clear(); rangeIntervals.Add(ri); hasCompl = true; continue; } break; } //если больше двух диапазонов, то пробуем объединить все маленькие диапазоны hasCompl = false; for (int i = 1; i < rangeIntervals.Count - 1; i++) { RangeInterval r1 = rangeIntervals[i - 1]; RangeInterval ri = rangeIntervals[i]; RangeInterval r2 = rangeIntervals[i + 1]; int i1 = (int)r1.Interval; int ii = (int)ri.Interval; int i2 = (int)r2.Interval; int l1 = r1.LengthMinutes; int li = ri.LengthMinutes; int l2 = r2.LengthMinutes; #region объединение диапазонов с одинаковым интервалом if (i1 == ii) //если у левого и среднего диапазона интервалы одинаковые { RangeInterval rr = combineIntervals(r1, ri); rangeIntervals.Remove(ri); rangeIntervals.Remove(r1); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } if (ii == i2) //если у правого и среднего диапазона интервалы одинаковые { RangeInterval rr = combineIntervals(ri, r2); rangeIntervals.Remove(ri); rangeIntervals.Remove(r2); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } #endregion #region объединение диапазонов, меньших, чем минимальный if (l1 < minimal) //если левый меньше минимального, то объединяем с средним { RangeInterval rr = combineIntervals(r1, ri); rangeIntervals.Remove(ri); rangeIntervals.Remove(r1); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } if (li < minimal) //если средний меньше минимального, то объединяем с тем, у кого меньше интервал { if (i1 < i2) //если левый интервал меньше правого { RangeInterval rr = combineIntervals(r1, ri); rangeIntervals.Remove(ri); rangeIntervals.Remove(r1); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } if (i1 > i2) //если правый интервал меньше правого { RangeInterval rr = combineIntervals(ri, r2); rangeIntervals.Remove(ri); rangeIntervals.Remove(r2); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } if (i1 == i2)//если интервалы равны, то объединяем всех троих { RangeInterval rr = combineIntervals(r1, r2); rangeIntervals.Remove(r1); rangeIntervals.Remove(ri); rangeIntervals.Remove(r2); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } } if (l2 < minimal && i == rangeIntervals.Count - 1) //если правый меньше минимального и это последний элемент, то объединяем с средним { RangeInterval rr = combineIntervals(ri, r2); rangeIntervals.Remove(ri); rangeIntervals.Remove(r2); rangeIntervals.Insert(i - 1, rr); hasCompl = true; break; } #endregion } } QualityInfo res = new QualityInfo(rangeIntervals, maxemptyspace, range.Count); return(res); }
/// <summary> /// кнопка восстановить ряд /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonRepairRange_Click(object sender, EventArgs e) { Cursor = Cursors.WaitCursor; InterpolateMethods method = (InterpolateMethods)(new EnumTypeConverter <InterpolateMethods>().ConvertFrom(comboBoxInterpolateMethod.SelectedItem)); StandartIntervals interval = (StandartIntervals)(new EnumTypeConverter <StandartIntervals>().ConvertFrom(comboBoxRepairInterval.SelectedItem)); Action <int, string> action = new Action <int, string>((percent, text) => { try { if (this.InvokeRequired) { _ = Invoke(new Action(() => { progressBarStatus.Value = percent; })); } else { progressBarStatus.Value = percent; } } catch (Exception) { } }); Action <RawRange, RawRange, double> actionAfter = new Action <RawRange, RawRange, double>((resultRange, baseRange, r) => { _ = this.Invoke(new Action(() => { string additionalText = ""; if (method == InterpolateMethods.NearestMeteostation) //для восстановления ряда выводим доп. информацию { //Информация о коэфф корреляции и базовом ряде if (double.IsNaN(r)) { additionalText += "Восстановление проводилось на основе ряда наблюдений, заданного пользователем\r\n"; } else { additionalText += $"Восстановление проводилось на основе ряда наблюдений {(baseRange.Meteostation != null ? $" на МС {baseRange.Meteostation.Name} " : "")}с коэффициентом корреляции {r:0.00} \r\n"; } //предупреждение, что не все данные восстановлены RangeInterval baseInterval = baseRange.Quality.Intervals.OrderByDescending((i) => i.LengthMinutes).First(); //выбираем самый длинный интервал наблюдений в базовом ряде if (baseInterval.LengthMinutes > (int)interval) //если максимальный интервал базового ряда больше, чем требуемый интервал восстановления { additionalText += $"\r\nВнимание!! Интервал наблюдений ряда, на основе которого производилось восстановление ({baseInterval.Interval.Description()}), больше, чем требуемый интервал. Поэтому не удалось восстановить все значения ряда до {interval.Description()}\r\n"; } } resultRange.Name = "Восстановленный ряд до интервала" + interval.Description(); _ = MessageBox.Show(this, $"Ряд восстановлен до интервала {interval.Description()}\r\n{additionalText}", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information); if (resultRange == null) { DialogResult = DialogResult.Cancel; } else { DialogResult = DialogResult.OK; Result = resultRange; } Cursor = Cursors.Arrow; Result = resultRange; Close(); })); }); try { //если выбрана ступенчатый метод или линейная интерполяция и в ряде есть пропуски больше, чем 1 интервал, то надо уточнить у пользователя if ((method == InterpolateMethods.Linear || method == InterpolateMethods.Stepwise) && rangeQuality.MaxEmptySpace.TotalMinutes > ((int)interval)) { if (MessageBox.Show(this, "Ряд содержит пропуски данных больше, чем один выбранный интервал наблюдений.\r\nВ таком случае не рекомендуется использовать линейную интерполяцию и ступенчатое восстановление.\r\nВы уверены, что хотите продолжить восстановление?", "Восстановление ряда", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.No) { return; } } RawRange baseRange = null; if (method == InterpolateMethods.NearestMeteostation) { if (radioButtonSelPoint.Checked) { if (range.Position.IsEmpty) { FormSelectMapPointDialog fsp = new FormSelectMapPointDialog("Выберите координаты ряда " + range.Name, PointLatLng.Empty, Vars.Options.CacheFolder, Resources.rp5_marker, Vars.Options.MapProvider); if (fsp.ShowDialog(this) == DialogResult.OK) { range.Position = fsp.Result; } else { Cursor = Cursors.Arrow; return; } } } else { baseRange = Program.winMain.mainHelper.OpenFile(this); if (baseRange == null) { Cursor = Cursors.Arrow; return; } } } _ = new Restorer().ProcessRange(range, new RestorerParameters() { Interval = interval, Method = method, Coordinates = range.Position, BaseRange = baseRange, ReplaceExistMeasurements = checkBoxReplaceExist.Checked }, action, actionAfter); } catch (WebException exc) { Cursor = Cursors.Arrow; _ = MessageBox.Show(this, exc.Message, "Восстановление ряда", MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.Cancel; } catch (WindEnergyException wex) { Cursor = Cursors.Arrow; _ = MessageBox.Show(this, wex.Message, "Восстановление ряда", MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.Cancel; } catch (ApplicationException exc) { Cursor = Cursors.Arrow; _ = MessageBox.Show(this, exc.Message + "\r\nПопробуйте уменьшить длину ряда", "Восстановление ряда", MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.Cancel; } }