/// <summary> /// загружает ряд наблюдений с ближайшей МС /// </summary> /// <param name="from">начало ряда</param> /// <param name="to">конец ряда</param> /// <param name="coordinates">координаты исходного ряда</param> /// <returns></returns> private static RawRange getNearestRange(DateTime from, DateTime to, PointLatLng coordinates) { if (from > to) { throw new WindEnergyException("Дата from больше, чем to"); } RawRange res = null; RP5MeteostationInfo nearestMS = Vars.RP5Meteostations.GetNearestMS(coordinates); if (nearestMS == null) { throw new Exception("Не удалось найти ближайшую метеостанцию в заданном радиусе"); } RP5ru provider = new RP5ru(Vars.Options.CacheFolder + "\\rp5.ru"); provider.GetMeteostationExtInfo(ref nearestMS); if (from < nearestMS.MonitoringFrom) //если исходный ряд начинается { from = nearestMS.MonitoringFrom; } if (from > to) { throw new Exception("Ряды не пересекаются: один из рядов заканчивается раньше, чем начинается другой"); } res = provider.GetRange(from, to, nearestMS); return(res); }
public FormLoadFromNASA(PointLatLng selectedPoint) { InitializeComponent(); Result = null; DialogResult = DialogResult.None; geocoder = new Arcgis(Vars.Options.CacheFolder + "\\arcgis"); comboBoxSpeedHeight.Items.AddRange(NasaWindSpeedHeight.WS10M.GetItems().ToArray()); comboBoxSpeedHeight.SelectedItem = NasaWindSpeedHeight.WS10M.Description(); point = selectedPoint; if (point.IsEmpty) //если точка пустая, то надо попробовать взять из последних { point = Vars.Options.LoadNasaLastPoint; } if (!point.IsEmpty) { spoint = new RP5MeteostationInfo(); spoint.Position = point; labelPointCoordinates.Text = $"Широта: {point.Lat:0.000} Долгота: {point.Lng:0.000}"; loadAddressAsync(point); buttonDownload.Enabled = true; dateTimePickerFromDate.Enabled = true; dateTimePickerToDate.Enabled = true; } }
private void gmapControlMap_OnMarkerClick(GMapMarker item, MouseEventArgs e) { item = item ?? throw new ArgumentNullException(nameof(item)); if (item.Tag.GetType() != typeof(RP5MeteostationInfo)) { return; } RP5MeteostationInfo mi = (RP5MeteostationInfo)item.Tag; if (IsDialog) { Result = mi; DialogResult = DialogResult.OK; Close(); } else { FormLoadFromRP5 frm = new FormLoadFromRP5(mi); if (frm.ShowDialog(this) == DialogResult.OK) { RawRange res = frm.Result; WindEnergy.UI.Ext.TabPageExt tab = Program.winMain.mainTabControl.OpenNewTab(res, res.Name); tab.HasNotSavedChanges = true; _ = Program.winMain.Focus(); } } }
private void linkLabelShowOnMap_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { FormShowMeteostationsMap fsmon = new FormShowMeteostationsMap(true); if (fsmon.ShowDialog(this) == DialogResult.OK) { this.selectedMeteostation = fsmon.Result; //установка времени начала и конца наблюдений dateTimePickerFromDate.MinDate = selectedMeteostation.MonitoringFrom; dateTimePickerToDate.MaxDate = DateTime.Now; labelDateRange.Text = "Выберите диапазон дат: (дата начала наблюдений: " + selectedMeteostation.MonitoringFrom.ToString() + ")"; //разблокировка элементов dateTimePickerFromDate.Enabled = true; dateTimePickerToDate.Enabled = true; buttonDownload.Enabled = true; linkLabelOpenNasa.Enabled = true; comboBoxPoint.Items.Clear(); comboBoxPoint.Items.Add(selectedMeteostation); comboBoxPoint.SelectedItem = selectedMeteostation; //запись координат labelCoordinates.Text = $"Координаты метеостанции: {selectedMeteostation.Position.ToString(4)}"; } }
/// <summary> /// кнопка выбора точки на карте /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonSelectPoint_Click(object sender, EventArgs e) { FormSelectMapPointDialog spt = new FormSelectMapPointDialog("Выберите точку на карте", point, Vars.Options.CacheFolder, Resources.rp5_marker, Vars.Options.MapProvider); if (spt.ShowDialog(this) == DialogResult.OK) { point = spt.Result; spoint = new RP5MeteostationInfo(); spoint.Position = point; labelPointCoordinates.Text = $"Широта: {point.Lat:0.000} Долгота: {point.Lng:0.000}"; loadAddressAsync(point); buttonDownload.Enabled = true; dateTimePickerFromDate.Enabled = true; dateTimePickerToDate.Enabled = true; } }
/// <summary> /// загрузка ряда из локальной БД /// </summary> /// <param name="fromDate">начальная дата</param> /// <param name="toDate">конечная дата</param> /// <param name="point_info">информация о МС</param> /// <param name="onPercentChange">не используется</param> /// <param name="checkStop">не используется</param> /// <returns></returns> public RawRange GetRange(DateTime fromDate, DateTime toDate, RP5MeteostationInfo point_info, Action <double> onPercentChange = null, Func <bool> checkStop = null) { //проверка параметров if (toDate < fromDate) { throw new WindEnergyException("Даты указаны неверно"); } if (string.IsNullOrWhiteSpace(point_info.ID)) { throw new ArgumentException("Поле ID не заполнено", nameof(point_info)); } if (!Directory.Exists(folder)) { throw new IOException("Папка БД Расписание Погоды не существует:\r\n" + folder); } string filename = folder + "\\" + PREFIX + point_info.ID + ".xlsx"; if (!File.Exists(filename)) { throw new FileNotFoundException("Файл не найден: " + filename); } //загрузка файла RawRange range = new ExcelFile().LoadRange(filename); if (range.First().Date > fromDate) { throw new WindEnergyException($"Начальной даты нет в локальной БД (доступны данные с {range.First().Date})"); } if (toDate > range.Last().Date) { throw new WindEnergyException($"Конечной даты нет в локальной БД (доступны данные до {range.Last().Date})"); } var res = range.Where(new Func <RawItem, bool>((item) => { return((item.Date >= fromDate) && (item.Date <= toDate)); })); RawRange result = new RawRange(res); if (result.Meteostation == null) { result.Meteostation = point_info; } result.Height = 10; //наблюдения в РП5 всегда на высоте 10м return(result); }
/// <summary> /// фильтрация по началу города /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void comboBoxPoint_TextUpdate(object sender, EventArgs e) { buttonDownload.Enabled = false; selectedMeteostation = null; dateTimePickerFromDate.Enabled = false; dateTimePickerToDate.Enabled = false; string curTextBox = comboBoxPoint.Text.Trim(); switch (Vars.Options.RP5SearchEngine) { case RP5SearchEngine.DBSearch: List <RP5MeteostationInfo> results = Vars.RP5Meteostations.Search(curTextBox); comboBoxPoint.Items.Clear(); comboBoxPoint.Items.AddRange(results.ToArray()); comboBoxPoint.SelectionStart = comboBoxPoint.Text.Length; break; case RP5SearchEngine.OnlineAPI: updateWMOListAsync(curTextBox); break; } }
/// <summary> /// загрузка ряда из формата WindEnergy /// </summary> /// <param name="filename"></param> /// <returns></returns> private RawRange loadCSVFile(string filename) { StreamReader sr = new StreamReader(filename, Encoding.UTF8); string title = sr.ReadLine(); string coordinates = sr.ReadLine(); string name = sr.ReadLine(); sr.ReadLine();//пропуск заголовка таблицы string data = sr.ReadToEnd(); sr.Close(); RawRange res = new RawRange() { Name = name }; //чтение координат файла string regex = @"^\d+[\.\,].\d*\s+\d+[\.\,].\d*$"; bool isMatch = new Regex(regex).IsMatch(coordinates); PointLatLng coord; if (isMatch) { string[] s = coordinates.Split(' '); double lat = double.Parse(s[0].Trim().Replace('.', Constants.DecimalSeparator)); double lon = double.Parse(s[1].Trim().Replace('.', Constants.DecimalSeparator)); coord = new PointLatLng(lat, lon); } else { coord = PointLatLng.Empty; } res.Position = coord; res.BeginChange(); string[] lines = data.Split('\n'); foreach (string line in lines) { string[] elems = line.Split(';'); if (elems.Length < 6) { continue; } if (elems[3] == "") { continue; } if (elems[4] == "") { continue; } double temp = elems[1] == "" ? double.NaN : double.Parse(elems[1].Replace('.', Constants.DecimalSeparator)); DateTime dt = DateTime.Parse(elems[0]); double spd = double.Parse(elems[4].Replace('.', Constants.DecimalSeparator)); double press = double.Parse(elems[5].Replace('.', Constants.DecimalSeparator)); double wet = elems[2] == "" ? double.NaN : double.Parse(elems[2].Replace('.', Constants.DecimalSeparator)); double dirs = double.Parse(elems[3].Replace('.', Constants.DecimalSeparator)); try { res.Add(new RawItem() { Date = dt, Direction = dirs, Speed = spd, Temperature = temp, Wetness = wet, Pressure = press }); } catch (Exception) { continue; } } //поиск информации о МС RP5MeteostationInfo meteostation = null; int start = title.IndexOf("ID=") + "ID=".Length; string id_s = title.Substring(start); meteostation = Vars.RP5Meteostations.GetByID(id_s); res.Meteostation = meteostation; res.EndChange(); return(res); }
/// <summary> /// открытие формы с уже выбранной МС /// </summary> public FormLoadFromRP5(RP5MeteostationInfo meteostaion) : this() { selectedMeteostation = meteostaion; }
/// <summary> /// проверка после выбора метеостанции /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void comboBoxPoint_SelectionChangeCommitted(object sender, EventArgs e) { //после того, как из поиска выбрана точка, надо проверить, есть ли на ней архив погоды. и вывести предупреждение, если ближайший архив далеко try { if (comboBoxPoint.SelectedItem == null) { return; } //выбор метеостанции if (comboBoxPoint.SelectedItem.GetType() == typeof(RP5ru.WmoInfo)) { List <RP5MeteostationInfo> meteost = engine.GetMeteostationsAtPoint(comboBoxPoint.SelectedItem as RP5ru.WmoInfo); RP5MeteostationInfo meteostation; if (meteost.Count == 0) { return; } if (meteost.Count == 1) { meteostation = meteost[0]; } else { string text = "Ближайшие метеостанции к выбранной точке:\r\n\r\n"; text += meteost[0].Name + ", (" + meteost[0].OwnerDistance + " км)\r\n\r\n"; text += meteost[1].Name + ", (" + meteost[1].OwnerDistance + " км)\r\n\r\n"; FormChooseMeteostAirportDialog dlg = new FormChooseMeteostAirportDialog("Загрузка ряда с rp5.ru", text, meteost[0].Name, meteost[1].Name); if (dlg.ShowDialog(this) == DialogResult.OK) { meteostation = meteost[dlg.Result - 1]; } else { return; } } this.selectedMeteostation = meteostation; } else { this.selectedMeteostation = comboBoxPoint.SelectedItem as RP5MeteostationInfo; } //установка времени начала и конца наблюдений dateTimePickerFromDate.MinDate = selectedMeteostation.MonitoringFrom; dateTimePickerToDate.MaxDate = DateTime.Now; labelDateRange.Text = "Выберите диапазон дат: (дата начала наблюдений: " + selectedMeteostation.MonitoringFrom.ToString() + ")"; //разблокировка элементов dateTimePickerFromDate.Enabled = true; dateTimePickerToDate.Enabled = true; buttonDownload.Enabled = true; linkLabelOpenNasa.Enabled = true; //запись координат labelCoordinates.Text = $"Координаты метеостанции: {selectedMeteostation.Position.ToString(4)}"; } catch (WebException) { _ = MessageBox.Show(this, "Ошибка подключения, проверьте соединение с Интернет", "Загрузка ряда", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } }
/// <summary> /// асинхронная загрузка списка адресов метеостанций /// </summary> /// <param name="query">запрос</param> private async void updateWMOListAsync(string query) { if (query.Length < 2) { return; } //действие обновления списка подсказок Action <List <RP5ru.WmoInfo> > updList = new Action <List <RP5ru.WmoInfo> >((list) => { comboBoxPoint.Items.Clear(); comboBoxPoint.Items.AddRange(list.ToArray()); comboBoxPoint.SelectionStart = comboBoxPoint.Text.Length; }); try { List <RP5ru.WmoInfo> results; await Task.Run(() => { Thread.Sleep(1000); //ждем 1 с //получаем новый текст string curTextBox = ""; if (InvokeRequired) { _ = Invoke(new Action(() => { curTextBox = comboBoxPoint.Text.Trim(); })); } else { curTextBox = comboBoxPoint.Text.Trim(); } if (query != curTextBox) //если этот текст изменился, то выходим { return; } selectedMeteostation = null; results = engine.Search(query); //обновление списка if (InvokeRequired) { _ = Invoke(updList, results); } else { updList.Invoke(results); } }).ConfigureAwait(false); } catch (WebException) { _ = MessageBox.Show(this, "Ошибка подключения, проверьте соединение с Интернет", "Загрузка ряда", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } catch (ApplicationException exc) { _ = MessageBox.Show(this, exc.Message, "Загрузка ряда", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Debug.WriteLine("updateList end"); }
/// <summary> /// получить ряд данных за указанный промежуток в заданной точке /// </summary> /// <param name="fromDate"></param> /// <param name="toDate"></param> /// <param name="point_info"></param> /// <param name="onPercentChange">Метод, вызываемый при изменении процента выполнения</param> /// <param name="checkStop"></param> /// <returns></returns> public RawRange GetRange(DateTime fromDate, DateTime toDate, RP5MeteostationInfo point_info, Action <double> onPercentChange = null, Func <bool> checkStop = null) { PointLatLng coord = point_info.Position; string fields = ""; foreach (string param in parameters.Values) { fields += param + ","; } fields = fields.Trim(','); //https://power.larc.nasa.gov/api/temporal/hourly/point?start=20200901&end=20210901&latitude=55&longitude=37&community=sb¶meters=WD10M&format=csv&user=anonimous&header=true&time-standard=lst string url = "https://power.larc.nasa.gov/api/temporal/hourly/point?start={1}&end={2}&latitude={3}&longitude={4}&community=sb¶meters={0}&format=csv&user=anonimous&header=true&time-standard=lst"; url = string.Format(url, fields, fromDate.ToString("yyyyMMdd"), toDate.ToString("yyyyMMdd"), coord.Lat.ToString("00.00").Replace(Constants.DecimalSeparator, '.'), coord.Lng.ToString("00.00").Replace(Constants.DecimalSeparator, '.')); string data = SendStringGetRequest(url, out HttpStatusCode code, false); if (checkStop != null && checkStop.Invoke()) //проверка остановки процесса { return(null); } if (code != HttpStatusCode.OK) //если есть ошибки, то выход с ошибкой { throw new ApplicationException("Произошла ошибка при запросе"); } RawRange res = new RawRange(); if (checkStop != null && checkStop.Invoke()) //проверка остановки процесса { return(null); } string dlines = data.Substring(data.IndexOf("-END HEADER-") + "-END HEADER-".Length); string[] lines = dlines.Split('\n'); List <string> header = new List <string>(lines[1].Split(',')); for (int i = 2; i < lines.Length - 1; i++) { string line = lines[i].Replace("\r", ""); line = Regex.Replace(line, @"[ ]+", " "); string[] elems = line.Trim().Split(','); if (header.Count != elems.Length) { continue; } try { Dictionary <MeteorologyParameters, double> values = new Dictionary <MeteorologyParameters, double>(); DateTime dt = DateTime.Parse(elems[0] + "." + elems[1] + "." + elems[2] + " " + elems[3] + ":00"); foreach (MeteorologyParameters key in parameters.Keys) { int index = header.IndexOf(parameters[key]); double val = double.Parse(elems[index].Replace('.', Constants.DecimalSeparator)); if (key == MeteorologyParameters.Pressure) { val *= Constants.MMHGART_IN_1KPA; //переводим в мм рт. ст. } values.Add(key, val); } RawItem item = new RawItem(dt, values); res.Add(item); } catch (ArgumentException) { continue; } } res.Position = point_info.Position; switch (speedHeight) { case NasaWindSpeedHeight.WS10M: res.Height = 10; break; case NasaWindSpeedHeight.WS50M: res.Height = 50; break; default: throw new Exception("Этот тип скорости не реализован"); } return(res); }
/// <summary> /// найти ближайшие метеостанции к выбранной точке прогноза погоды /// </summary> /// <param name="wmoInfo">информация о точке с погодой</param> /// <param name="loadExtInfo">загружать доп информацию о метеостанции (id, дата начала наблюдения)</param> /// <param name="forceDisableCache">откюлчение кэша</param> /// <returns></returns> public List <RP5MeteostationInfo> GetMeteostationsAtPoint(WmoInfo wmoInfo, bool loadExtInfo = true, bool forceDisableCache = false) { //страница погоды в заданной точке HtmlDocument point_page = SendHtmlGetRequest(wmoInfo.Link, out HttpStatusCode code, forceDisableCache: forceDisableCache); if (code != HttpStatusCode.OK) { throw new Exception("При загрузке страницы произошла ошибка " + code.ToString()); } //поиск ссылок на метеостанции Dictionary <string, IEnumerable <HtmlNode> > archives = new Dictionary <string, IEnumerable <HtmlNode> >(); List <RP5MeteostationInfo> res = new List <RP5MeteostationInfo>(); Dictionary <string, string> selectors = new Dictionary <string, string>() { { "a[id='archive_link']", "list" }, //архив погоды на метеостанции (старый вариант страницы) { "div.ArchiveInfo a.ArchiveStrLink", "block" }, //архив погоды на метеостанции (новый вариант страницы) { "a[id='wug_link']", "list" }, //на неофициальной метеостанции (этот архив пока что качать нельзя, но селектор оставим) { "a[id='metar_link']", "list" } //аэропорт }; foreach (string i in selectors.Keys) { var a = point_page.DocumentNode.QuerySelectorAll(i); //получаем все теги по такому селекторы if (a != null && a.Count() > 0) //если что-то нашлось, то парсим дальше { foreach (HtmlNode some_link in a) { RP5MeteostationInfo nm = new RP5MeteostationInfo(); nm.Name = wmoInfo.name; //искомая точка nm.Link = some_link.Attributes["href"].Value; //ссылка на страницу //дальнейшее зависит от типа блока switch (selectors[i]) { case "list": //СТАРЫЕ ВАРИАНТЫ БЛОКОВ ССЫЛОК НА МЕТЕОСТАНЦИИ И АЭРОПОРТЫ //название int s1 = some_link.InnerText.IndexOf(" ("); string onmouseover = some_link.Attributes["onmouseover"].Value; //значение атрибута вывода подсказки string nmm = onmouseover.Replace("tooltip(this, '", "").Replace("' , 'hint')", ""); nm.Name = some_link.InnerText.Substring(0, s1) + " (" + nmm + ")"; //расстояние до точки string content = some_link.InnerText; int start = content.IndexOf("( "); int end = content.IndexOf(" км"); if (start == -1 || end == -1) { nm.OwnerDistance = 0; } else { int l = end - (start + 2); string dist = content.Substring(start + 2, l); nm.OwnerDistance = double.Parse(dist.Replace('.', Constants.DecimalSeparator)); } //тип источника string a_id = some_link.Attributes["id"].Value; switch (a_id) { case "archive_link": nm.MeteoSourceType = MeteoSourceType.Meteostation; break; case "metar_link": nm.MeteoSourceType = MeteoSourceType.Airport; break; case "wug_link": continue; //для неофициальных метеостанций нельзя получить архив погоды =( //nm.MeteoSourceType = MeteoSourceType.UnofficialMeteostation; //break; default: throw new Exception("Этот тип метеостанции не реализован"); } break; case "block": //НОВЫЙ ТИП ССЫЛОК В ВИДЕ БЛОКА С ИНФОРМАЦИЕЙ //название onmouseover = some_link.Attributes["onmouseover"].Value; //значение атрибута вывода подсказки nmm = onmouseover.Replace("tooltip(this, '", "").Replace("' , 'hint')", ""); nm.Name = nmm; //расстояние до источника не указывается, значит 0 nm.OwnerDistance = 0; //пока в таких блоках только метеостанции nm.MeteoSourceType = MeteoSourceType.Meteostation; break; default: throw new Exception("Этот тип блока ещё не реализован"); } if (loadExtInfo) { try { GetMeteostationExtInfo(ref nm); //запись информации об id метеостанции, дате начала наблюдений } catch (Exception) { continue; } } if (nm != null) { res.Add(nm); } } } } return(res); }
/// <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); }
/// <summary> /// создание окна с выделенной метеостанцией на карте /// </summary> /// <param name="meteostation"></param> public FormShowMeteostationsMap(RP5MeteostationInfo meteostation) : this() { meteostation = meteostation ?? throw new ArgumentNullException(nameof(meteostation)); gmapControlMap.Position = meteostation.Position; }
/// <summary> /// выводит на карту только видимые метеостанции /// </summary> private void showVisibleMeteostations() { //загрузка списка метеостанций if (lay != null) { //расчет областей экрана int h = 15; //кол-во по высоте int w = 15; //кол-во по ширине double hd = gmapControlMap.ViewArea.HeightLat; //высота в градусах double wd = gmapControlMap.ViewArea.WidthLng; //ширина в градусах double sh = hd / h; // шаг по высоте double sw = wd / w; //шаг по ширине RP5MeteostationInfo[][] mts_map = new RP5MeteostationInfo[h][]; for (int i = 0; i < h; i++) { mts_map[i] = new RP5MeteostationInfo[w]; } RectLatLng[][] rec_map = new RectLatLng[h][]; for (int i = 0; i < h; i++) { rec_map[i] = new RectLatLng[w]; } for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { //левый верхний угол double lat = gmapControlMap.ViewArea.Lat - (i) * sh; double lng = gmapControlMap.ViewArea.Lng + (j) * sw; rec_map[i][j] = new RectLatLng(lat, lng, sw, sh); } } //выборка точек из БД List <RP5MeteostationInfo> pts = new List <RP5MeteostationInfo>(); foreach (var mts in Vars.RP5Meteostations.List) { if (gmapControlMap.ViewArea.Contains(mts.Position)) { pts.Add(mts); //добавление в общий список //поиск по всем областям bool exit = false; for (int i = 0; i < h && !exit; i++) { for (int j = 0; j < w; j++) { // если попадает в маленькую область и эта область ещё не заполнена if (rec_map[i][j].Contains(mts.Position) && mts_map[i][j] == null) {//добавляем на карту и выходим mts_map[i][j] = mts; exit = true; break; } } } } } List <RP5MeteostationInfo> res; if (pts.Count < h * w) { res = pts; } else { res = new List <RP5MeteostationInfo>(); for (int i = 0; i < mts_map.Length; i++) { if (mts_map[i] != null) { foreach (var dd in mts_map[i]) { if (dd != null) { res.Add(dd); } } } } } //вывод на карту lay.Clear(); showVisibleAMS();//вывод АМС без фильтрации, их мало foreach (var a in res) { showMarker(a.Position, a.Name, a); } } }
/// <summary> /// Загрузить ряд из файла csv, полученного с сайта /// </summary> /// <param name="file">файл csv</param> /// <param name="meteostation">Привязка к метеостанции. Если null, то будет найдена из БД по ID из заголовка</param> /// <returns></returns> public static RawRange LoadCSV(string file, RP5MeteostationInfo meteostation = null) { using (StreamReader sr = new StreamReader(file, Encoding.UTF8, true)) { RawRange res = new RawRange(); res.BeginChange(); //приостановка обработки событий изменения ряда //определение формата файла csv MeteoSourceType type; string title = sr.ReadLine(); if (title.Contains("WMO_ID")) { type = MeteoSourceType.Meteostation; } else if (title.Contains("METAR")) { type = MeteoSourceType.Airport; } else { throw new Exception("Файл повреждён или имеет неизвестный формат данных rp5.ru"); } //пропуск пустых строк (одна уже пропущена при чтении заголовка) for (int i = 0; i < 6; i++) { sr.ReadLine(); } switch (type) { case MeteoSourceType.Meteostation: //загрузка архива с метеостанции string data = sr.ReadToEnd(); sr.Close(); string[] lines = data.Replace("\"", "").Split('\n'); foreach (string line in lines) { string[] elems = line.Split(';'); if (elems.Length < 8) { continue; } if (elems[6] == "") { continue; } if (elems[7] == "") { continue; } double temp = elems[1] == "" ? double.NaN : double.Parse(elems[1].Replace('.', Constants.DecimalSeparator)); DateTime dt = DateTime.Parse(elems[0]); double spd = double.Parse(elems[7].Replace('.', Constants.DecimalSeparator)); double wet = elems[5] == "" ? double.NaN : double.Parse(elems[5].Replace('.', Constants.DecimalSeparator)); double press = elems[2] == "" ? double.NaN : double.Parse(elems[2].Replace('.', Constants.DecimalSeparator)); string dirs = elems[6]; WindDirections16 direct = GetWindDirectionFromString(dirs); try { res.Add(new RawItem() { Date = dt, DirectionRhumb = direct, Speed = spd, Temperature = temp, Wetness = wet, Pressure = press }); } catch (Exception) { continue; } } //поиск информации о МС if (meteostation == null) { int start = title.IndexOf("WMO_ID=") + "WMO_ID=".Length; int end = title.IndexOf(',', start); string id_s = title.Substring(start, end - start); meteostation = Vars.RP5Meteostations.GetByID(id_s); } break; case MeteoSourceType.Airport: //загрузка архива с аэропорта string data2 = sr.ReadToEnd(); sr.Close(); string[] lines2 = data2.Replace("\"", "").Split('\n'); foreach (string line in lines2) { string[] elems = line.Split(';'); if (elems.Length < 8) { continue; } if (elems[5] == "") { continue; } if (elems[6] == "") { continue; } double temp = elems[1] == "" ? double.NaN : double.Parse(elems[1].Replace('.', Constants.DecimalSeparator)); DateTime dt = DateTime.Parse(elems[0]); double spd = double.Parse(elems[6].Replace('.', Constants.DecimalSeparator)); double wet = elems[4] == "" ? double.NaN : double.Parse(elems[4].Replace('.', Constants.DecimalSeparator)); double press = elems[2] == "" ? double.NaN : double.Parse(elems[2].Replace('.', Constants.DecimalSeparator)); string dirs = elems[5]; WindDirections16 direct = RP5ru.GetWindDirectionFromString(dirs); res.Add(new RawItem() { Date = dt, DirectionRhumb = direct, Speed = spd, Temperature = temp, Wetness = wet, Pressure = press }); } //поиск информации о МС if (meteostation == null) { int start = title.IndexOf("METAR=") + "METAR=".Length; int end = title.IndexOf(',', start); string id_s = title.Substring(start, end - start); meteostation = Vars.RP5Meteostations.GetByCC_code(id_s); } break; case MeteoSourceType.UnofficialMeteostation: throw new Exception("Этот тип файла не поддерживается"); } res.Meteostation = meteostation; res.EndChange(); return(res); } }
/// <summary> /// получить из страницы архива погоды id метеостанции, дату начала наблюдений. Двнные запишутся в структуру info, где уже должна быть записана ссылка на страницу /// </summary> /// <returns></returns> public void GetMeteostationExtInfo(ref RP5MeteostationInfo info) { if (info == null) { throw new ArgumentException("info"); } if (string.IsNullOrWhiteSpace(info.Link)) { throw new ArgumentException("В структуре info должна быть ссылка на страницу архива погоды"); } HtmlDocument page = SendHtmlGetRequest(info.Link, out HttpStatusCode code); HtmlNode wmo = page.GetElementbyId("wmo_id"); //архив погоды на метеостанции HtmlNode metar = page.GetElementbyId("cc_str"); //аэропорт HtmlNode wug = page.GetElementbyId("wug"); //на неофициальной метеостанции //получение ID if (wmo != null) //метеостанция { info.ID = wmo.Attributes["value"].Value; info.MeteoSourceType = MeteoSourceType.Meteostation; } else if (metar != null) //аэропорт { //символьный код аэропорта HtmlNode cc_code_node = page.GetElementbyId("cc_str"); string cc_code = cc_code_node.Attributes["value"].Value.ToString(); info.CC_Code = cc_code; //id в системе рп5 string pg1 = page.Text; int start1 = pg1.IndexOf("fFileMetarGet("); int end1 = pg1.IndexOf(')', start1); if (start1 == -1 || end1 == -1) { throw new Exception("Что-то не так"); } start1 += +"fFileMetarGet(1317900300".Length; string id = pg1.Substring(start1 + 1, end1 - 1 - start1); info.ID = id; info.MeteoSourceType = MeteoSourceType.Airport; } else if (wug != null) //неофициальная метеостанция { info = null; return; } else { throw new WindEnergyException("Для этой метеостанции невозможно получить данные"); } //получение даты начала наблюдений string pg = page.Text; int start = pg.IndexOf("наблюдения с "); int end = pg.IndexOf("</span>", start); if (start == -1 || end == -1) { throw new Exception("Что-то не так"); } start += "наблюдения с ".Length; string fdate = pg.Substring(start, end - 1 - start); DateTime mon_from = DateTime.Parse(fdate); info.MonitoringFrom = mon_from; //получение координат метеостанции string pg2 = page.Text; int start2 = pg2.IndexOf("show_map("); int end2 = pg2.IndexOf(", 9);", start2); if (start2 == -1 || end2 == -1) { throw new Exception("Что-то не так"); } start2 += "show_map(".Length; string coordinates = pg2.Substring(start2, end2 - 1 - start2); //55.833333333333, 37.616666666667 string[] ar = coordinates.Split(','); double lat = double.Parse(ar[0].Trim().Replace('.', Constants.DecimalSeparator)); double lon = double.Parse(ar[1].Trim().Replace('.', Constants.DecimalSeparator)); info.Position = new PointLatLng(lat, lon); }
/// <summary> /// Загрузка ряда из файла xlsx /// </summary> /// <param name="fileName">адрес файла</param> /// <returns></returns> public override RawRange LoadRange(string fileName) { FileInfo fi = new FileInfo(fileName); using (ExcelPackage excelPackage = new ExcelPackage(fi)) { if (excelPackage.Workbook.Worksheets.Count == 0) { return(null); } ExcelWorksheet worksheet = excelPackage.Workbook.Worksheets[0]; if (worksheet.Cells[3, 1].Value == null) { return(null); } string title = worksheet.Cells[1, 1].Value.ToString(); string coordinates = worksheet.Cells[2, 1].Value.ToString(); string name = worksheet.Cells[3, 1].Value.ToString(); string height = worksheet.Cells[2, 2].Value != null ? worksheet.Cells[2, 2].Value.ToString() : "не число"; if (!double.TryParse(height, out double rangeHeight)) { rangeHeight = double.NaN; } RawRange res = new RawRange() { Name = name }; //чтение координат файла string regex = @"^\d+[\.\,].\d*\s+\d+[\.\,].\d*$"; bool isMatch = new Regex(regex).IsMatch(coordinates); PointLatLng coord; if (isMatch) { string[] s = coordinates.Split(' '); double lat = double.Parse(s[0].Trim().Replace('.', Constants.DecimalSeparator)); double lon = double.Parse(s[1].Trim().Replace('.', Constants.DecimalSeparator)); coord = new PointLatLng(lat, lon); } else { coord = PointLatLng.Empty; } res.Position = coord; res.BeginChange(); var arr = worksheet.Cells; for (int i = 5; i <= worksheet.Dimension.Rows; i++) { DateTime dt = getDateTime(arr[i, 1]); double temp = getDouble(arr[i, 2]); double spd = getDouble(arr[i, 5]); double press = getDouble(arr[i, 6]); double wet = getDouble(arr[i, 3]); double dirs = getDouble(arr[i, 4]); try { res.Add(new RawItem() { Date = dt, Direction = dirs, Speed = spd, Temperature = temp, Wetness = wet, Pressure = press }); } catch (Exception) { continue; } } //поиск информации о МС RP5MeteostationInfo meteostation = null; int start = title.IndexOf("ID=") + "ID=".Length; string id_s = title.Substring(start); if (id_s.ToLower() != "nasa" && id_s.ToLower() != "undefined") { meteostation = Vars.RP5Meteostations.GetByID(id_s); } res.Meteostation = meteostation; res.Height = rangeHeight; res.EndChange(); return(res); } }
/// <summary> /// загрузить файл данных с сайта и открыть ряд наблюдений /// </summary> /// <param name="fromDate">с какой даты</param> /// <param name="toDate">до какой даты</param> /// <param name="info">Метеостанция, с которой загружается ряд</param> /// <param name="onPercentChange"></param> /// <param name="checkStop"></param> /// <returns></returns> public RawRange GetRange(DateTime fromDate, DateTime toDate, RP5MeteostationInfo info, Action <double> onPercentChange = null, Func <bool> checkStop = null) { if (toDate < fromDate) { throw new WindEnergyException("Даты указаны неверно"); } switch (Vars.Options.RP5SourceEngine) { case RP5SourceType.LocalDBSearch: return(Vars.RP5Database.GetRange(fromDate, toDate, info, onPercentChange, checkStop)); case RP5SourceType.OnlineAPI: if (toDate - fromDate > TimeSpan.FromDays(365 * LOAD_STEP_YEARS)) // если надо скачать больше трёх лет, то скачиваем по частям { TimeSpan span = toDate - fromDate; RawRange res1 = new RawRange(); DateTime dt; int i = 0; int total = (int)(span.TotalDays / (365 * LOAD_STEP_YEARS)) + 1; for (dt = fromDate; dt <= toDate; dt += TimeSpan.FromDays(365 * LOAD_STEP_YEARS)) { if (checkStop != null) { if (checkStop.Invoke()) { break; } } if (onPercentChange != null) { double pc = ((i / (double)total) * 100d); onPercentChange.Invoke(pc); } RawRange r = GetRange(dt, dt + TimeSpan.FromDays(365 * LOAD_STEP_YEARS), info, onPercentChange, checkStop); res1.Add(r); res1.Name = r.Name; res1.Position = r.Position; res1.Meteostation = r.Meteostation; res1.Height = 10; //высота всегда 10м i++; } //DateTime fr = dt - TimeSpan.FromDays(365 * LOAD_STEP_YEARS); //RawRange r1 = GetRange(fr, toDate, info); return(res1); } #region отправка статистики загрузки string dataS, linkS; switch (info.MeteoSourceType) { case MeteoSourceType.Airport: dataS = "cc_id={0}&cc_str={1}&stat_p=1&s_date1={2}&s_ed3={4}&s_ed4={4}&s_ed5={5}&s_date2={3}&s_ed9=0&s_ed10=-1&s_pe=1&lng_id=2&s_dtimehi=-Период---"; linkS = "https://rp5.ru/responses/reStatistMetar.php"; dataS = string.Format(dataS, info.ID, //cc_id info.CC_Code, //cc_str fromDate.Date.ToString("dd.MM.yyyy"), //from toDate.Date.ToString("dd.MM.yyyy"), //to DateTime.Now.Month, //f_ed3 - только месяц DateTime.Now.Day //f_ed5 - только день ); break; case MeteoSourceType.Meteostation: dataS = "wmo_id={0}&stat_p=1&s_date1={1}&s_ed3={3}&s_ed4={3}&s_ed5={4}&s_date2={2}&s_ed9=0&s_ed10=-1&s_pe=1&lng_id=2&s_dtimehi=-срок---"; linkS = "https://rp5.ru/responses/reStatistSynop.php"; dataS = string.Format(dataS, info.ID, //wmo_id fromDate.Date.ToString("dd.MM.yyyy"), //from toDate.Date.ToString("dd.MM.yyyy"), //to DateTime.Now.Month, //f_ed3 - только месяц DateTime.Now.Day //f_ed5 - только день ); break; default: throw new Exception("Этот тип метеостанций не реализован"); } string strS = this.SendStringPostRequest(linkS, dataS, referer: "https://rp5.ru/", cookies: this.CookieData, customHeaders: this.Headers); #endregion #region получение ссылки на файл string data, link; //получение ссылки на файл switch (info.MeteoSourceType) { case MeteoSourceType.Airport: data = "metar={0}&a_date1={1}&a_date2={2}&f_ed3={3}&f_ed4={4}&f_ed5={5}&f_pe={6}&f_pe1={7}&lng_id=2"; link = "https://rp5.ru/responses/reFileMetar.php"; break; case MeteoSourceType.Meteostation: data = "wmo_id={0}&a_date1={1}&a_date2={2}&f_ed3={3}&f_ed4={4}&f_ed5={5}&f_pe={6}&f_pe1={7}&lng_id=2"; link = "https://rp5.ru/responses/reFileSynop.php"; break; default: throw new Exception("Этот тип метеостанций не реализован"); } data = string.Format(data, info.ID, //id fromDate.Date.ToString("dd.MM.yyyy"), //from toDate.Date.ToString("dd.MM.yyyy"), //to DateTime.Now.Month, //f_ed3 - только месяц DateTime.Now.Month, //f_ed4 - только месяц DateTime.Now.Day, //f_ed5 - только день 1, //f_pe 2 //f_pe1- кодировка (1 - ansi, 2 - utf8, 3 - Unicode) ); string str = this.SendStringPostRequest(link, data, referer: "https://rp5.ru/", cookies: this.CookieData, customHeaders: this.Headers); //ОШИБКИ rp5.ru //запросы к reFileSynop.php //FS004 несуществующий wmo_id //FS002 ошибки в исходных данных (параметрах запроса) //FS000 Ошибка авторизации //FS001- //запросы к reStatistSynop.php //S000 Ошибка авторизации //FM000 Время жизни статистики истекло для этой сессии if (str.Contains("FS004")) { throw new WindEnergyException("Для этого id нет архива погоды", ErrorReason.FS004); } if (str.Contains("FS002")) { throw new WindEnergyException("Ошибка в исходных данных", ErrorReason.FS002); } if (str.Contains("FS000")) { throw new WindEnergyException("Ошибка авторизации", ErrorReason.FS000); } if (str.Contains("FS001-")) { throw new WindEnergyException("Неправильный метод запроса. Ожидается POST", ErrorReason.FS001); } if (str.Contains("FM000")) { throw new WindEnergyException("Время жизни статистики истекло для этой сессии", ErrorReason.FM000); } if (str.Contains("FM004")) { throw new WindEnergyException("Внутренняя ошибка. Архив недоступен или не существует", ErrorReason.FM004); } int start = str.IndexOf("href=") + 5; str = str.Substring(start); int end = str.IndexOf(">"); string lnk = str.Substring(0, end); lnk = lnk.Split(' ')[0] ?? ""; bool checkLink = Uri.TryCreate(lnk, UriKind.Absolute, out Uri uriResult) && (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps); if (!checkLink) { throw new WindEnergyException("В парсере ответа rp5 произошла ошибка. Скорее всего, парсер устарел, обратитесь к разработчику"); } #endregion #region загрузка файла с сервера string tmp_dl_file = Vars.LocalFileSystem.GetTempFileName(); WebClient webcl = new WebClient(); webcl.DownloadFile(lnk, tmp_dl_file); webcl.Dispose(); //распаковка string tmp_unpack_file = tmp_dl_file + ".csv"; LocalFileSystem.UnGZip(tmp_dl_file, tmp_unpack_file); //открытие файла RawRange res = LoadCSV(tmp_unpack_file, info); res = new RawRange(res.OrderBy(x => x.Date).ToList()); res.Name = info.Name; res.Position = info.Position; res.Meteostation = info; res.Height = 10; //высота всегда 10м new Task(() => Vars.RP5Meteostations.TryAddMeteostation(info)).Start(); //если такой метеостанции нет в БД, то добавляем return(res); #endregion default: throw new Exception("Этот тип БД не реализован"); } }