/// <summary> /// создание нового интерполятора с заданными значениями на основе существующего базового ряда наблюдения /// </summary> /// <param name="func">известные значения функции в заданной точке</param> /// <param name="baseRange">базовый ряд (с ближайшей МС), на основе которого будет происходить восстановление</param> /// <param name="parameterType">тип мтеорологического параметра</param> /// <param name="replaceExistMeasurements">Заменять существующие измерения в исходном ряде на расчетные</param> public NearestMSInterpolateMethod(Dictionary <double, double> func, RawRange baseRange, MeteorologyParameters parameterType, bool replaceExistMeasurements) { if (func.Keys.Count == 0) { Empty = true; return; } Empty = false; this.parameterType = parameterType; this.func = func; this.replaceExistMeasurements = replaceExistMeasurements; this.nearestRange = baseRange; //расчет диапазона сделанных измерений baseRange = new RawRange(baseRange.OrderBy(x => x.Date).ToList()); interpolationDiapason.From = Math.Max(baseRange[0].DateArgument, func.Keys.Min()); //максимальную дату из начал каждой функции interpolationDiapason.To = Math.Min(baseRange[baseRange.Count - 1].DateArgument, func.Keys.Max()); //минимальную дату из концов каждой функции double a = 0, b = 0, r = 0; //коэффициенты прямой и коэффициент корреляции Dictionary <double, double> baseFunc = baseRange.GetFunction(parameterType); //функция базового ряда List <double>[] tableCoeff = calcTableCoeff(func, baseFunc); //таблица для расчёта коэффициентов a, b, r a = getParameterA(tableCoeff); //коэффициенты прямой b = getParameterB(tableCoeff, a); r = getParameterR(tableCoeff); //коэффициент корреляции //проверка попадания коэфф корреляции в допустимый диапазон (если для этого параметра надо проверять диапазон) if (r < Vars.Options.MinimalCorrelationCoeff && Vars.Options.MinimalCorrelationControlParametres.Contains(parameterType)) { throw new Exception("Недостаточное коррелирование функций"); } //ФУНКЦИЯ ПОЛУЧЕНИЯ ЗНАЧЕНИЯ getRes = new Func <double, double>((x) => { //если в исходном ряде есть это значение и не надо заменять исходное, то его и возвращаем if (func.ContainsKey(x) && !replaceExistMeasurements) { return(func[x]); } //Если в базовой функции нет измерения за этой время, то возвращаем NaN //иначе расчитываем скорость по полученной зависимости. if (!baseFunc.ContainsKey(x)) { return(double.NaN); } else { if (baseFunc[x] == 0) //если в базовой функции это значение равно нулю, то возвращаем NaN, чтоб не завышать результат прибавлением b { return(double.NaN); } else { return(a * baseFunc[x] + b); } } }); }
/// <summary> /// ищет наиболее подходящую к заданной точке МС и получает её ряд. Если ряд не найден, то возвращает null /// </summary> /// <param name="coordinates"></param> /// <param name="r"></param> /// <param name="actionPercent"></param> /// <param name="Range">ряд, для которого подбирается функция</param> /// <exception cref="GetBaseRangeException">Возвращает иснформацию о параметрах, мешающих получить ближайшую МС</exception> /// <returns></returns> internal static RawRange TryGetBaseRange(RawRange Range, PointLatLng coordinates, out double r, Action <int, string> actionPercent) { bool nlaw = CheckNormalLaw(Range, Vars.Options.NormalLawPirsonCoefficientDiapason); if (!nlaw) { throw new WindEnergyException("Исходный ряд не подчиняется нормальному закону распределения"); } DateTime from = Range.Min((ri) => ri.Date).Date, to = Range.Max((ri) => ri.Date).Date; List <RP5MeteostationInfo> mts = Vars.RP5Meteostations.GetNearestMS(coordinates, Vars.Options.NearestMSRadius, false); Dictionary <double, double> funcSpeed = Range.GetFunction(MeteorologyParameters.Speed); //функция скорости на заданном ряде RawRange res = null; double rmax = double.MinValue, total_rmax = double.MinValue; RP5ru provider = new RP5ru(Vars.Options.CacheFolder + "\\rp5.ru"); for (int i = 0; i < mts.Count; i++) { if (actionPercent != null) { actionPercent.Invoke((int)((i * 1d / mts.Count) * 100d), "Поиск подходящей МС..."); } RP5MeteostationInfo m = mts[i]; //если нет диапазона измерений в БД, то загружаем с сайта if (m.MonitoringFrom == DateTime.MinValue) { provider.GetMeteostationExtInfo(ref m); } //если для этой МС нет наблюдений в этом периоде, то переходим на другую if (m.MonitoringFrom > from) { continue; } //загрузка ряда с очередной МС RawRange curRange = null; try { curRange = provider.GetRange(from, to, m); } catch (WindEnergyException wex) // если не удалось получить ряд этой МС, то переходим к следующей { continue; } curRange = new Checker().ProcessRange(curRange, new CheckerParameters(LimitsProviders.StaticLimits, curRange.Position), out CheckerInfo info, null); //исправляем ошибки //СКОРОСТЬ MeteorologyParameters parameter = MeteorologyParameters.Speed; Dictionary <double, double> funcSpeedCurrentNearest = curRange.GetFunction(parameter); //функция скорости на текущей МС //проверка на нормальный закон распределения bool normal = CheckNormalLaw(curRange, Vars.Options.NormalLawPirsonCoefficientDiapason); if (!normal) { continue; } //расчёт и проверка коэфф корреляции List <double>[] table = calcTableCoeff(funcSpeed, funcSpeedCurrentNearest); //таблица для расчет коэффициентов double current_r = getParameterR(table); //коэффициент корреляции //общий максимальный коэфф корреляции if (current_r > total_rmax) { total_rmax = current_r; } //проверяем, можно ли взять эту МС if (current_r > rmax) { //истина, если надо проверять этот параметр на допустимый диапазон корреляции bool needCheck = Vars.Options.MinimalCorrelationControlParametres.Contains(parameter); if ((needCheck && current_r >= Vars.Options.MinimalCorrelationCoeff) || !needCheck) { rmax = current_r; res = curRange; } } } r = rmax; if (res == null) { RP5MeteostationInfo mi = Vars.RP5Meteostations.GetNearestMS(coordinates); double l = EarthModel.CalculateDistance(mi.Position, coordinates); throw new GetBaseRangeException(total_rmax, Vars.Options.MinimalCorrelationCoeff, l, mts.Count, Vars.Options.NearestMSRadius, coordinates); } return(res); }