OpenCPN Partial API docs
Loading...
Searching...
No Matches
grib_request_dlg.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 ***************************************************************************/
17
24// Disable the maybe-uninitialized warning as it triggers in std::regex and
25// makes the build with sanitizers impossible
26#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
27
28#include "wx/wx.h"
29#include <wx/utils.h>
30#include <sstream>
31#include "email.h"
32#include "xy_grib_model_def.h"
33
34#include "pi_gl.h"
35
36#include "grib_request_dlg.h"
38#include <wx/file.h>
39#include <wx/wfstream.h>
40#include "grib_pi.h"
41
42#include <unordered_map>
43#include <regex>
44#include <string>
45
46#define RESOLUTIONS 4
47
48enum { SAILDOCS, ZYGRIB }; // grib providers
49enum { GFS, COAMPS, RTOFS, HRRR, ICON, ECMWF }; // forecast models
50
51wxString toMailFormat(int NEflag,
52 int a) // convert position to mail necessary format
53{
54 char c = NEflag == 1 ? a < 0 ? 'S' : 'N' : a < 0 ? 'W' : 'E';
55 wxString s;
56 s.Printf("%01d%c", abs(a), c);
57 return s;
58}
59
60extern int m_SavedZoneSelMode;
61extern int m_ZoneSelMode;
62
63//----------------------------------------------------------------------------------------------------------
64// GRIB Request Implementation
65//----------------------------------------------------------------------------------------------------------
66GribRequestSetting::GribRequestSetting(GRIBUICtrlBar &parent)
67 : GribRequestSettingBase(&parent), m_parent(parent) {
68 m_VpFocus = nullptr;
69 m_VpMouse = nullptr;
70 m_oDC = nullptr;
71 m_boundingBoxCanvasIndex = -1;
72
73 m_displayScale = 1.0;
74#if defined(__WXOSX__) || defined(__WXGTK3__)
75 // Support scaled HDPI displays.
76 m_displayScale = wxWindow::GetContentScaleFactor();
77#endif
78
79 ReadWorldModels();
80 InitRequestConfig();
81 m_connected = false;
82 m_downloading = false;
83 m_bTransferSuccess = true;
84 m_downloadType = GribDownloadType::NONE;
85
86 auto bg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)
87 .GetAsString(wxC2S_HTML_SYNTAX);
88 auto fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT)
89 .GetAsString(wxC2S_HTML_SYNTAX);
90 m_htmlWinWorld->SetBorders(10);
91 m_htmlWinWorld->SetPage(
92 "<html><body bgcolor="
93 "" +
94 bg +
95 ""
96 "><font color="
97 "" +
98 fg +
99 ""
100 ">" +
101 _("<h1>OpenCPN world forecast</h1>"
102 "<p>Free service providing GRIB files from global weather and ocean "
103 "models based on open data published by their respective "
104 "producers.</p>"
105 "<p>Select the model and the forecast length in hours. The length "
106 "counts from the start of the model run, not from the time of "
107 "download, and newly published runs are usually several hours old, "
108 "so the forecast reaches correspondingly less far into the future. "
109 "The maximum length depends on the selected model.</p>"
110 "<p>The data is updated as soon as new model runs finish and the "
111 "results are published by their producers.</p>"
112 "<p>The grib downloaded covers the area of the primary chart "
113 "canvas.</p>"
114 "<p>The service is provided on best effort basis and comes with no "
115 "guarantees. The server is hosted by a volunteer and the service is "
116 "provided free of charge and without accepting any liability "
117 "whatsoever for its continuous availability, or for any loss or damage "
118 "arising from its use. If you find the service useful, please "
119 "consider making a donation to the OpenCPN project.</p>"
120 "<p>Sources: European Centre for Medium-Range Weather Forecasts "
121 "(ECMWF, www.ecmwf.int, CC-BY-4.0), Deutscher Wetterdienst (DWD), "
122 "NOAA, Environment and Climate Change Canada (ECCC) and "
123 "M&eacute;t&eacute;o-France.</p>"
124 "<p>SMOC currents: Generated using E.U. Copernicus Marine Service "
125 "Information; https://doi.org/10.48670/moi-00016</p>"
126 "<p>Disclaimer: The data producers do not accept any liability "
127 "whatsoever for any error or omission in the data, their "
128 "availability, or for any loss or damage arising from their use.</p>"
129 "</font></body></html>"));
130 m_htmlInfoWin->SetBorders(10);
131 m_htmlInfoWin->SetPage(
132 "<html><body bgcolor="
133 "" +
134 bg +
135 ""
136 "><font color="
137 "" +
138 fg +
139 ""
140 ">" +
141 _("<h1>Grib weather forecasts</h1>"
142 "<p>Collection of local weather models from various sources available "
143 "for download over the internet.</p>"
144 "</font></body></html>"));
145 ReadLocalCatalog();
146 m_bLocal_source_selected = false;
147 EnableDownloadButtons();
148
149 m_selectedAtmModelIndex = 0;
150 m_selectedWaveModelIndex = 0;
151 InitializeXygribDialog();
152}
153
154GribRequestSetting::~GribRequestSetting() {
155 if (m_downloading) {
156 OCPN_cancelDownloadFileBackground(m_download_handle);
157 }
158 if (m_connected) {
159 Disconnect(
160 wxEVT_DOWNLOAD_EVENT,
161 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
162 }
163 delete m_VpFocus;
164 delete m_VpMouse;
165 delete m_oDC;
166}
167
168void GribRequestSetting::SaveConfig() {
169 wxFileConfig *pConf = GetOCPNConfigObject();
170 if (pConf) {
171 pConf->SetPath("/PlugIns/GRIB");
172
173 pConf->Write("MailRequestConfig", m_RequestConfigBase);
174 pConf->Write("MailSenderAddress", m_pSenderAddress->GetValue());
175 pConf->Write("MailRequestAddresses", m_MailToAddresses);
176 pConf->Write("ZyGribLogin", m_pLogin->GetValue());
177 pConf->Write("ZyGribCode", m_pCode->GetValue());
178 pConf->Write("SendMailMethod", m_SendMethod);
179 pConf->Write("MovingGribSpeed", m_sMovingSpeed->GetValue());
180 pConf->Write("MovingGribCourse", m_sMovingCourse->GetValue());
181
182 m_SavedZoneSelMode = m_cUseSavedZone->GetValue() ? SAVED_SELECTION
183 : m_rbManualSelect->GetValue() ? START_SELECTION
185 pConf->Write("ManualRequestZoneSizingMode", m_ZoneSelMode);
186 pConf->Write("ManualRequestZoneSizing", m_SavedZoneSelMode);
187
188 pConf->Write("RequestZoneMaxLat", m_spMaxLat->GetValue());
189 pConf->Write("RequestZoneMinLat", m_spMinLat->GetValue());
190 pConf->Write("RequestZoneMaxLon", m_spMaxLon->GetValue());
191 pConf->Write("RequestZoneMinLon", m_spMinLon->GetValue());
192
193 int sel = m_chWorldModel->GetSelection();
194 if (sel != wxNOT_FOUND) {
195 pConf->Write("WorldModel", m_world_models[sel]["name"].AsString());
196 }
197 pConf->Write("WorldForecastLength", m_spForecastLength->GetValue());
198 }
199}
200
201void GribRequestSetting::InitRequestConfig() {
202 wxFileConfig *pConf = GetOCPNConfigObject();
203
204 if (pConf) {
205 pConf->SetPath("/PlugIns/GRIB");
206 wxString l;
207 int m;
208 pConf->Read("MailRequestConfig", &m_RequestConfigBase, "000220XX........0");
209 pConf->Read("MailSenderAddress", &l, "");
210 m_pSenderAddress->ChangeValue(l);
211 pConf->Read("MailRequestAddresses", &m_MailToAddresses,
212 "query@saildocs.com;gribauto@zygrib.org");
213 pConf->Read("ZyGribLogin", &l, "");
214 m_pLogin->ChangeValue(l);
215 pConf->Read("ZyGribCode", &l, "");
216 m_pCode->ChangeValue(l);
217 pConf->Read("SendMailMethod", &m_SendMethod, 0);
218 pConf->Read("MovingGribSpeed", &m, 0);
219 m_sMovingSpeed->SetValue(m);
220 pConf->Read("MovingGribCourse", &m, 0);
221 m_sMovingCourse->SetValue(m);
222 pConf->Read("ManualRequestZoneSizingMode", &m, 0);
223 m_ZoneSelMode = m;
224 pConf->Read("ManualRequestZoneSizing", &m, 0);
226 m_cUseSavedZone->SetValue(m_SavedZoneSelMode == SAVED_SELECTION);
229 }
231 UpdateAreaSelectionState();
232 pConf->Read("RequestZoneMaxLat", &m, 0);
233 m_spMaxLat->SetValue(m);
234 pConf->Read("RequestZoneMinLat", &m, 0);
235 m_spMinLat->SetValue(m);
236 pConf->Read("RequestZoneMaxLon", &m, 0);
237 m_spMaxLon->SetValue(m);
238 pConf->Read("RequestZoneMinLon", &m, 0);
239 m_spMinLon->SetValue(m);
240 pConf->Read("WorldModel", &l, "");
241 if (FindWorldModel(l) != wxNOT_FOUND) {
242 m_chWorldModel->SetSelection(FindWorldModel(l));
243 }
244 pConf->Read("WorldForecastLength", &m, 24);
245 m_spForecastLength->SetValue(m);
246 wxCommandEvent evt;
247 OnWorldModelChoice(evt);
248
249 SetCoordinatesText();
250 // if GriDataConfig has been corrupted , take the standard one to fix a
251 // crash
252 if (m_RequestConfigBase.Len() != wxString("000220XX..............").Len())
253 m_RequestConfigBase = "000220XX..............";
254 }
255 // populate model, mail to, waves model choices
256 wxString s1[] = {"GFS", "COAMPS", "RTOFS", "HRRR", "ICON", "ECMWF"};
257 for (unsigned int i = 0; i < (sizeof(s1) / sizeof(wxString)); i++)
258 m_pModel->Append(s1[i]);
259 wxString s2[] = {"Saildocs", "zyGrib"};
260 for (unsigned int i = 0; i < (sizeof(s2) / sizeof(wxString)); i++)
261 m_pMailTo->Append(s2[i]);
262 wxString s3[] = {"WW3-GLOBAL", "WW3-MEDIT"};
263 for (unsigned int i = 0; i < (sizeof(s3) / sizeof(wxString)); i++)
264 m_pWModel->Append(s3[i]);
265 m_rButtonYes->SetLabel(_("Send"));
266 m_rButtonApply->SetLabel(_("OK"));
267 m_tResUnit->SetLabel(wxString::Format("\u00B0"));
268 m_sCourseUnit->SetLabel(wxString::Format("\u00B0"));
269
270 // Set wxSpinCtrl sizing
271 int w, h;
272 GetTextExtent("-360", &w, &h, nullptr, nullptr,
273 OCPNGetFont(_("Dialog"))); // optimal text control size
274 w += 30;
275 h += 4;
276 m_sMovingSpeed->SetMinSize(wxSize(w, h));
277 m_sMovingCourse->SetMinSize(wxSize(w, h));
278 m_spMaxLat->SetMinSize(wxSize(w, h));
279 m_spMinLat->SetMinSize(wxSize(w, h));
280 m_spMaxLon->SetMinSize(wxSize(w, h));
281 m_spMinLon->SetMinSize(wxSize(w, h));
282
283 // add tooltips
284 m_pSenderAddress->SetToolTip(
285 _("Address used to send request eMail. (Mandatory for LINUX)"));
286 m_pLogin->SetToolTip(_("This is your zyGrib's forum access Login"));
287 m_pCode->SetToolTip(
288 _("Get this Code in zyGrib's forum ( This is not your password! )"));
289 m_sMovingSpeed->SetToolTip(_("Enter your forescasted Speed (in Knots)"));
290 m_sMovingCourse->SetToolTip(_("Enter your forecasted Course"));
291
292 long i, j, k;
293 ((wxString)m_RequestConfigBase.GetChar(0)).ToLong(&i); // MailTo
294 m_pMailTo->SetSelection(i);
295 ((wxString)m_RequestConfigBase.GetChar(1)).ToLong(&i); // Model
296 m_pModel->SetSelection(i);
297 m_cMovingGribEnabled->SetValue(m_RequestConfigBase.GetChar(16) ==
298 'X'); // Moving Grib
299 ((wxString)m_RequestConfigBase.GetChar(2)).ToLong(&i); // Resolution
300 ((wxString)m_RequestConfigBase.GetChar(3)).ToLong(&j); // interval
301 ((wxString)m_RequestConfigBase.GetChar(4)).ToLong(&k, 16); // Time Range
302 k--; // range max = 2 to 16 stored in hexa from 1 to f
303
304#ifdef __WXMSW__ // show / hide sender elemants as necessary
305 m_pSenderSizer->ShowItems(false);
306#else
307 if (m_SendMethod == 0)
308 m_pSenderSizer->ShowItems(false);
309 else
310 m_pSenderSizer->ShowItems(
311 true); // possibility to use "sendmail" method with Linux
312#endif
313
314 m_tMouseEventTimer.Connect(
315 wxEVT_TIMER, wxTimerEventHandler(GribRequestSetting::OnMouseEventTimer),
316 nullptr, this);
317
318 m_RenderSelectionZoneState = RENDER_NONE;
319
320 ApplyRequestConfig(i, j, k);
321
322 ((wxString)m_RequestConfigBase.GetChar(5)).ToLong(&j); // Waves model
323 m_pWModel->SetSelection(j);
324
325 m_pWind->Enable(false); // always selected if available
326 m_pPress->Enable(false);
327
328 DimeWindow(this); // aplly global colours scheme
329
330 m_AllowSend = true;
331 m_MailImage->SetValue(WriteMail());
332}
333
334wxWindow *GetGRIBCanvas();
335void GribRequestSetting::OnClose(wxCloseEvent &event) {
336 if (m_downloading) {
337 OCPN_cancelDownloadFileBackground(m_download_handle);
338 m_downloading = false;
339 m_download_handle = 0;
340 }
341 if (m_connected) {
342 Disconnect(
343 wxEVT_DOWNLOAD_EVENT,
344 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
345 }
347 RENDER_NONE; // eventually stop graphical zone display
348 RequestRefresh(GetGRIBCanvas());
349
350 // allow to be back to old value if changes have not been saved
352 m_parent.SetRequestButtonBitmap(m_ZoneSelMode); // set appropriate bitmap
353 m_parent.m_highlight_latmax = 0;
354 m_parent.m_highlight_lonmax = 0;
355 m_parent.m_highlight_latmin = 0;
356 m_parent.m_highlight_lonmin = 0;
357 this->Hide();
358}
359
360void GribRequestSetting::SetRequestDialogSize() {
361 int y;
362 /*first let's size the mail display space*/
363 GetTextExtent("abc", nullptr, &y, nullptr, nullptr, OCPNGetFont(_("Dialog")));
364 m_MailImage->SetMinSize(
365 wxSize(-1, ((y * m_MailImage->GetNumberOfLines()) + 10)));
366
367 /*then as default sizing do not work with wxScolledWindow let's compute it*/
368 wxSize scroll =
369 m_fgScrollSizer->Fit(m_sScrolledDialog); // the area size to be scrolled
370
371#ifdef __WXGTK__
372 SetMinSize(wxSize(0, 0));
373#endif
374
375 wxWindow *frame = wxTheApp->GetTopWindow();
376
377 int w = frame->GetClientSize().x; // the display size
378 int h = frame->GetClientSize().y;
379 int dMargin = 80; // set a margin
380
381 // height available for the scrolled window.
382 h -= (m_rButtonCancel->GetSize().GetY() + dMargin);
383 w -= dMargin; // width available for the scrolled window
384 m_sScrolledDialog->SetMinSize(
385 wxSize(wxMin(w, scroll.x),
386 wxMin(h, scroll.y))); // set scrolled area size with margin
387
388 Layout();
389 Fit();
390#ifdef __WXGTK__
391 wxSize sd = GetSize();
392 if (sd.y == GetClientSize().y) sd.y += 30;
393 SetSize(wxSize(sd.x, sd.y));
394 SetMinSize(wxSize(sd.x, sd.y));
395#endif
396 Refresh();
397}
398
399void GribRequestSetting::SetVpSize(PlugIn_ViewPort *vp) {
400 double lonmax = vp->lon_max;
401 double lonmin = vp->lon_min;
402 if ((fabs(vp->lat_max) < 90.) && (fabs(lonmax) < 360.)) {
403 if (lonmax < -180.) lonmax += 360.;
404 if (lonmax > 180.) lonmax -= 360.;
405 }
406 if ((fabs(vp->lat_min) < 90.) && (fabs(lonmin) < 360.)) {
407 if (lonmin < -180.) lonmin += 360.;
408 if (lonmin > 180.) lonmin -= 360.;
409 }
410
411 bool bnew_val = false;
412 if (m_spMaxLat->GetValue() != (int)ceil(vp->lat_max)) bnew_val = true;
413 if (m_spMinLon->GetValue() != (int)floor(lonmin)) bnew_val = true;
414 if (m_spMinLat->GetValue() != (int)floor(vp->lat_min)) bnew_val = true;
415 if (m_spMaxLon->GetValue() != (int)ceil(lonmax)) bnew_val = true;
416
417 if (bnew_val) {
418 m_spMaxLat->SetValue((int)ceil(vp->lat_max));
419 m_spMinLon->SetValue((int)floor(lonmin));
420 m_spMinLat->SetValue((int)floor(vp->lat_min));
421 m_spMaxLon->SetValue((int)ceil(lonmax));
422
423 SetCoordinatesText();
424 m_MailImage->SetValue(WriteMail());
425 }
426}
427
428bool GribRequestSetting::MouseEventHook(wxMouseEvent &event) {
429 if (!(event.ShiftDown() || m_bpManualSelection->GetValue())) {
430 // Only handle selection when Shift is pressed, or when the "manual
431 // selection button" is pressed.
432 return false; // Let the chart handle normal panning
433 }
434 if (event.LeftDown() && !event.Dragging()) {
435 // Automatically switch to manual selection,
436 // without requiring user to set the manual mode checkbox.
438 m_rbManualSelect->SetValue(true); // Update the UI checkbox
439 }
440 // User can still switch back to auto mode by unchecking the manual mode.
443 return false;
444
445 if (event.Moving())
446 return false; // maintain status bar and tracking dialog updated
447
448 // Mouse event position is in logical pixels.
449 int xm = event.GetX() * m_displayScale;
450 int ym = event.GetY() * m_displayScale;
451
452 if (event.LeftDown() && !event.Dragging()) {
453 m_parent.pParent->SetFocus();
454 m_ZoneSelMode = DRAW_SELECTION; // Starting a new bounding box selection.
456 wxPoint(xm, ym); // Set the starting point of the bounding box.
459 // if (this->IsShown())
460 // this->Hide(); // eventually hide dialog in case of mode change
461 m_RenderSelectionZoneState = RENDER_NONE; // hide previous drawing
462 m_boundingBoxCanvasIndex = GetCanvasIndexUnderMouse();
463 } else if (event.LeftUp() && m_RenderSelectionZoneState == RENDER_DRAWING) {
464 m_ZoneSelMode = COMPLETE_SELECTION; // ask to complete selection
466 SetCoordinatesText();
467 UpdateAreaSelectionState();
468 m_MailImage->SetValue(WriteMail());
469 m_RenderSelectionZoneState = RENDER_COMPLETE;
470 // The zone selection is complete, so we can update the grib size estimate.
471 UpdateGribSizeEstimate();
472 m_boundingBoxCanvasIndex = -1;
473 } else if (event.Dragging()) {
474 if (m_boundingBoxCanvasIndex != GetCanvasIndexUnderMouse()) {
475 // The user cannot draw a selection across view ports.
476 return false;
477 }
478 if (m_RenderSelectionZoneState < RENDER_DRAWING) {
479 m_RenderSelectionZoneState = RENDER_DRAWING;
480 }
481 m_IsMaxLong = m_StartPoint.x > xm
482 ? true
483 : false; // find if startpoint is max longitude
484 GetCanvasLLPix(m_VpMouse, wxPoint(xm, ym), &m_Lat,
485 &m_Lon); // extend selection
486 m_Lat = wxMin(90.0, wxMax(-90.0, m_Lat));
487 m_Lon = wxMin(180.0, wxMax(-180.0, m_Lon));
488 if (!m_tMouseEventTimer.IsRunning())
489 m_tMouseEventTimer.Start(20, wxTIMER_ONE_SHOT);
490 }
491 return true;
492}
493
494void GribRequestSetting::OnMouseEventTimer(wxTimerEvent &event) {
495 // compute zone starting point lon/lat for zone drawing
496
497 // compute rounded coordinates
498 if (m_StartLat > m_Lat) {
499 m_spMaxLat->SetValue((int)ceil(m_StartLat));
500 m_spMinLat->SetValue((int)floor(m_Lat));
501 } else {
502 m_spMaxLat->SetValue((int)ceil(m_Lat));
503 m_spMinLat->SetValue((int)floor(m_StartLat));
504 }
505 if (m_IsMaxLong) {
506 m_spMaxLon->SetValue((int)ceil(m_StartLon));
507 m_spMinLon->SetValue((int)floor(m_Lon));
508 } else {
509 m_spMaxLon->SetValue((int)ceil(m_Lon));
510 m_spMinLon->SetValue((int)floor(m_StartLon));
511 }
512
513 RequestRefresh(GetGRIBCanvas());
514}
515
516void GribRequestSetting::SetCoordinatesText() {
517 m_stMaxLatNS->SetLabel(m_spMaxLat->GetValue() < 0 ? _("S") : _("N"));
518 m_stMinLonEW->SetLabel(m_spMinLon->GetValue() < 0 ? _("W") : _("E"));
519 m_stMaxLonEW->SetLabel(m_spMaxLon->GetValue() < 0 ? _("W") : _("E"));
520 m_stMinLatNS->SetLabel(m_spMinLat->GetValue() < 0 ? _("S") : _("N"));
521}
522
523template <typename T>
524std::string GribRequestSetting::FormatPerLocale(T value) {
525 std::stringstream ss;
526 // use the the user-preferred locale rather than the default "C" locale
527 ss.imbue(std::locale(""));
528 ss << value; // Output for a US locale would be: 12,345,678 for integers
529 return ss.str();
530}
531
532wxString GribRequestSetting::GetDownloadProgressText(long transferredBytes,
533 long totalBytes) {
534 if (totalBytes > 0) {
535 return wxString::Format(_("Downloading... %s kB / %s kB (%li%%)"),
536 FormatPerLocale(transferredBytes / 1024).c_str(),
537 FormatPerLocale(totalBytes / 1024).c_str(),
538 (int)((double)transferredBytes / totalBytes * 100));
539 } else {
540 return wxString::Format(_("Downloading... %s kB / ???"),
541 FormatPerLocale(transferredBytes / 1024).c_str());
542 }
543}
544
545void GribRequestSetting::onDLEvent(OCPN_downloadEvent &ev) {
546 // std::cout << "onDLEvent " << ev.getDLEventCondition() << " "
547 // << ev.getDLEventStatus() << std::endl;
548 switch (ev.getDLEventCondition()) {
550 m_bTransferSuccess =
551 (ev.getDLEventStatus() == OCPN_DL_NO_ERROR) ? true : false;
552 Disconnect(wxEVT_DOWNLOAD_EVENT,
553 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::
554 onDLEvent);
555 m_connected = false;
556 m_downloading = false;
557 m_download_handle = 0;
558 wxYieldIfNeeded();
559 break;
560
562 switch (m_downloadType) {
564 m_staticTextInfo->SetLabelText(
565 GetDownloadProgressText(ev.getTransferred(), ev.getTotal()));
566 break;
569 m_stLocalDownloadInfo->SetLabelText(
570 GetDownloadProgressText(ev.getTransferred(), ev.getTotal()));
571 break;
573 // Update XyGrib progress gauge
574 if (ev.getTotal()) {
575 m_xygribPanel->m_progress_gauge->SetValue(
576 100 * ev.getTransferred() / ev.getTotal());
577 }
578 // Update status text to display information on file size
579 m_xygribPanel->m_status_text->SetLabel(
580 GetDownloadProgressText(ev.getTransferred(), ev.getTotal()));
581 break;
582 default:
583 break;
584 }
585 wxYieldIfNeeded();
586 break;
587 default:
588 break;
589 }
590}
591
592void GribRequestSetting::OnWorldDownload(wxCommandEvent &event) {
593 if (m_downloading) {
594 OCPN_cancelDownloadFileBackground(m_download_handle);
595 m_downloading = false;
596 m_download_handle = 0;
597 Disconnect(
598 wxEVT_DOWNLOAD_EVENT,
599 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
600 m_connected = false;
601 m_btnDownloadWorld->SetLabelText(_("Download"));
602 m_staticTextInfo->SetLabelText(_("Download canceled"));
603 m_canceled = true;
604 m_downloadType = GribDownloadType::NONE;
605 EnableDownloadButtons();
606 wxTheApp->ProcessPendingEvents();
607 wxYieldIfNeeded();
608 return;
609 }
610 m_canceled = false;
611 m_downloading = true;
612 m_downloadType = GribDownloadType::WORLD;
613 EnableDownloadButtons();
614 m_btnDownloadWorld->SetLabelText(_("Cancel"));
615 m_staticTextInfo->SetLabelText(_("Preparing data on server..."));
616 wxYieldIfNeeded();
617 wxString model =
618 m_world_models[m_chWorldModel->GetSelection()]["name"].AsString();
619 int length = m_spForecastLength->GetValue();
620 std::ostringstream oss;
621 oss << "https://grib.bosun.io/grib?";
622 oss << "model=" << model;
623 oss << "&latmin=" << GetMinLat();
624 oss << "&latmax=" << GetMaxLat();
625 oss << "&lonmin=" << GetMinLon();
626 oss << "&lonmax=" << GetMaxLon();
627 oss << "&length=" << length;
628 oss << "&encoding=ccsds";
629 wxString filename =
630 wxString::Format("ocpn_%s_%i_%s.grb2", model.c_str(), length,
631 wxDateTime::Now().Format("%F-%H-%M"));
632 wxString path = m_parent.GetGribDir();
633 path.Append(wxFileName::GetPathSeparator());
634 path.Append(filename);
635 if (!m_connected) {
636 m_connected = true;
637 Connect(
638 wxEVT_DOWNLOAD_EVENT,
639 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
640 }
641 OCPN_downloadFileBackground(oss.str(), path, this, &m_download_handle);
642 while (m_downloading) {
643 wxTheApp->ProcessPendingEvents();
644 wxMilliSleep(10);
645 }
646 if (!m_canceled) {
647 if (m_bTransferSuccess) {
648 m_staticTextInfo->SetLabelText(
649 wxString::Format(_("Download complete: %s"), path.c_str()));
650 wxFileName fn(path);
651 m_parent.m_grib_dir = fn.GetPath();
652 m_parent.m_file_names.Clear();
653 m_parent.m_file_names.Add(path);
654 m_parent.OpenFile();
655 if (m_parent.pPlugIn) {
656 if (m_parent.pPlugIn->m_bZoomToCenterAtInit) m_parent.DoZoomToCenter();
657 }
658 m_parent.SetDialogsStyleSizePosition(true);
659 SaveConfig();
660 Close();
661 } else {
662 // On an HTTP error the server's explanation (e.g. area limits) is what
663 // got written to the file, show it unless it is a proxy's HTML page
664 wxString reason;
665 wxFile f;
666 if (wxFileName::GetSize(path) < 1024 && f.Open(path)) f.ReadAll(&reason);
667 reason.Trim().Trim(false);
668 if (reason.IsEmpty() || reason.StartsWith("<")) {
669 m_staticTextInfo->SetLabelText(_("Download failed"));
670 } else {
671 m_staticTextInfo->SetLabelText(
672 wxString::Format(_("Download failed: %s"), reason));
673 m_staticTextInfo->Wrap(m_panelWorld->GetClientSize().x - 10);
674 m_panelWorld->Layout();
675 }
676 }
677 }
678 if (m_canceled || !m_bTransferSuccess) wxRemoveFile(path);
679 m_btnDownloadWorld->SetLabelText(_("Download"));
680 m_downloadType = GribDownloadType::NONE;
681 EnableDownloadButtons();
682}
683
684enum LocalSourceItem { SOURCE, AREA, GRIB };
685
686enum LocalGribDownloadType { DIRECT, MANIFEST, WEBPAGE };
687
688struct GribCatalogInfo : public wxTreeItemData {
689 GribCatalogInfo(LocalSourceItem type, wxString name, wxString description,
690 wxString url, wxString filename,
691 LocalGribDownloadType download_type, double latmin,
692 double lonmin, double latmax, double lonmax)
693 : type(type),
694 name(name),
695 description(description),
696 url(url),
697 filename(filename),
698 download_type(download_type),
699 latmin(latmin),
700 lonmin(lonmin),
701 latmax(latmax),
702 lonmax(lonmax) {}
703 LocalSourceItem type;
704 wxString name;
705 wxString description;
706 wxString url;
707 wxString filename;
708 LocalGribDownloadType download_type;
709 double latmin;
710 double lonmin;
711 double latmax;
712 double lonmax;
713};
714
715void GribRequestSetting::FillTreeCtrl(wxJSONValue &data) {
716 m_SourcesTreeCtrl1->DeleteAllItems();
717 wxTreeItemId root =
718 m_SourcesTreeCtrl1->AddRoot(_("Local high resolution forecasts"));
719 if (data.HasMember("sources") && data["sources"].IsArray()) {
720 for (int i = 0; i < data["sources"].Size(); i++) {
721 wxJSONValue source = data["sources"][i];
722 auto info = new GribCatalogInfo(
723 LocalSourceItem::SOURCE, source["source"].AsString(),
724 source["description"].AsString(), source["url"].AsString(),
725 wxEmptyString, LocalGribDownloadType::WEBPAGE, 0, 0, 0, 0);
726 wxTreeItemId src_id = m_SourcesTreeCtrl1->AppendItem(
727 root, source["source"].AsString(), -1, -1, info);
728 if (source.HasMember("areas") && source["areas"].IsArray()) {
729 for (int j = 0; j < source["areas"].Size(); j++) {
730 wxJSONValue area = source["areas"][j];
731 auto info_1 = new GribCatalogInfo(
732 LocalSourceItem::AREA, area["name"].AsString(),
733 source["description"].AsString(), source["url"].AsString(),
734 wxEmptyString, LocalGribDownloadType::WEBPAGE,
735 area["boundary"]["lat_min"].AsDouble(),
736 area["boundary"]["lon_min"].AsDouble(),
737 area["boundary"]["lat_max"].AsDouble(),
738 area["boundary"]["lon_max"].AsDouble());
739 m_SourcesTreeCtrl1->AppendItem(src_id, area["name"].AsString(), -1,
740 -1, info_1);
741 if (area.HasMember("gribs") && area["gribs"].IsArray()) {
742 for (int k = 0; k < area["gribs"].Size(); k++) {
743 wxJSONValue grib = area["gribs"][k];
744 auto inf_1 = new GribCatalogInfo(
745 LocalSourceItem::GRIB, grib["name"].AsString(),
746 source["description"].AsString(),
747 grib.HasMember("url") ? grib["url"].AsString()
748 : grib["cat_url"].AsString(),
749 grib.HasMember("filename") ? grib["filename"].AsString() : "",
750 grib.HasMember("url") ? LocalGribDownloadType::DIRECT
751 : LocalGribDownloadType::MANIFEST,
752 area["boundary"]["lat_min"].AsDouble(),
753 area["boundary"]["lon_min"].AsDouble(),
754 area["boundary"]["lat_max"].AsDouble(),
755 area["boundary"]["lon_max"].AsDouble());
756 m_SourcesTreeCtrl1->AppendItem(
757 m_SourcesTreeCtrl1->GetLastChild(src_id),
758 grib["name"].AsString(), -1, -1, inf_1);
759 }
760 }
761 }
762 }
763 m_SourcesTreeCtrl1->CollapseAllChildren(src_id);
764 }
765 }
766 m_SourcesTreeCtrl1->Expand(root);
767}
768
769void GribRequestSetting::ReadLocalCatalog() {
770 wxJSONReader reader;
771 wxFileInputStream str(m_parent.pPlugIn->m_local_sources_catalog);
772 wxJSONValue root;
773 reader.Parse(str, &root);
774 FillTreeCtrl(root);
775}
776
777void GribRequestSetting::ReadWorldModels() {
778 int sel = m_chWorldModel->GetSelection();
779 wxString current =
780 sel == wxNOT_FOUND ? "" : m_world_models[sel]["name"].AsString();
781 wxJSONReader reader;
782 wxFileInputStream str(m_parent.pPlugIn->m_world_models_catalog);
783 m_world_models = wxJSONValue();
784 if (str.IsOk()) reader.Parse(str, &m_world_models);
785 m_chWorldModel->Clear();
786 for (int i = 0; i < m_world_models.Size(); i++) {
787 m_chWorldModel->Append(m_world_models[i]["title"].AsString());
788 }
789 sel = FindWorldModel(current);
790 m_chWorldModel->SetSelection(
791 sel != wxNOT_FOUND ? sel : (m_chWorldModel->IsEmpty() ? wxNOT_FOUND : 0));
792 wxCommandEvent evt;
793 OnWorldModelChoice(evt);
794}
795
796int GribRequestSetting::FindWorldModel(const wxString &name) {
797 for (int i = 0; i < m_world_models.Size(); i++) {
798 if (m_world_models[i]["name"].AsString() == name) return i;
799 }
800 return wxNOT_FOUND;
801}
802
803void GribRequestSetting::OnUpdateWorldModels(wxCommandEvent &event) {
804 m_canceled = false;
805 m_downloading = true;
806 m_downloadType = GribDownloadType::WORLD;
807 EnableDownloadButtons();
808 m_btnDownloadWorld->SetLabelText(_("Cancel"));
809 m_staticTextInfo->SetLabelText(_("Downloading model list update..."));
810 wxYieldIfNeeded();
811 if (!m_connected) {
812 m_connected = true;
813 Connect(
814 wxEVT_DOWNLOAD_EVENT,
815 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
816 }
817 wxString tmp = m_parent.pPlugIn->m_world_models_catalog + "new";
818 OCPN_downloadFileBackground(WORLD_MODELS_URL, tmp, this, &m_download_handle);
819 while (m_downloading) {
820 wxTheApp->ProcessPendingEvents();
821 wxMilliSleep(10);
822 }
823 if (!m_canceled) {
824 // Only replace the current list with a non-empty, parseable one
825 wxJSONValue models;
826 wxJSONReader reader;
827 wxFileInputStream str(tmp);
828 if (m_bTransferSuccess && str.IsOk() && reader.Parse(str, &models) == 0 &&
829 models.IsArray() && models.Size() > 0) {
830 wxRenameFile(tmp, m_parent.pPlugIn->m_world_models_catalog, true);
831 ReadWorldModels();
832 m_staticTextInfo->SetLabelText(_("Model list update complete."));
833 } else {
834 m_staticTextInfo->SetLabelText(_("Model list update failed"));
835 }
836 }
837 wxRemoveFile(tmp);
838 m_btnDownloadWorld->SetLabelText(_("Download"));
839 m_downloadType = GribDownloadType::NONE;
840 EnableDownloadButtons();
841}
842
843void GribRequestSetting::OnWorldModelChoice(wxCommandEvent &event) {
844 int sel = m_chWorldModel->GetSelection();
845 if (sel == wxNOT_FOUND) return;
846 int max = m_world_models[sel]["maxlength"].AsInt();
847 m_spForecastLength->SetRange(1, max);
848 m_spForecastLength->SetValue(std::min(m_spForecastLength->GetValue(), max));
849}
850
851void GribRequestSetting::HighlightArea(double latmax, double lonmax,
852 double latmin, double lonmin) {
853 m_parent.m_highlight_latmax = latmax;
854 m_parent.m_highlight_lonmax = lonmax;
855 m_parent.m_highlight_latmin = latmin;
856 m_parent.m_highlight_lonmin = lonmin;
857}
858
859void GribRequestSetting::OnLocalTreeSelChanged(wxTreeEvent &event) {
860 wxTreeItemId item = m_SourcesTreeCtrl1->GetSelection();
861 auto src = (GribCatalogInfo *)(m_SourcesTreeCtrl1->GetItemData(item));
862 if (src) {
863 if (src->type == LocalSourceItem::GRIB) {
864 m_stLocalDownloadInfo->SetLabelText(_("Download grib..."));
865 m_bLocal_source_selected = true;
866 HighlightArea(src->latmax, src->lonmax, src->latmin, src->lonmin);
867 } else {
868 m_stLocalDownloadInfo->SetLabelText(_("Select grib..."));
869 m_bLocal_source_selected = false;
870 HighlightArea(src->latmax, src->lonmax, src->latmin, src->lonmin);
871 }
872 }
873 EnableDownloadButtons();
874}
875
876void GribRequestSetting::OnUpdateLocalCatalog(wxCommandEvent &event) {
877 if (m_downloading) {
878 OCPN_cancelDownloadFileBackground(m_download_handle);
879 m_downloading = false;
880 m_download_handle = 0;
881 Disconnect(
882 wxEVT_DOWNLOAD_EVENT,
883 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
884 m_connected = false;
885 m_btnDownloadLocal->SetLabelText(_("Download"));
886 m_stLocalDownloadInfo->SetLabelText(_("Download canceled"));
887 m_canceled = true;
888 m_downloadType = GribDownloadType::NONE;
889 EnableDownloadButtons();
890 wxTheApp->ProcessPendingEvents();
891 wxYieldIfNeeded();
892 return;
893 }
894 m_canceled = false;
895 m_downloading = true;
896 m_downloadType = GribDownloadType::LOCAL_CATALOG;
897 EnableDownloadButtons();
898 m_btnDownloadLocal->SetLabelText(_("Cancel"));
899 m_stLocalDownloadInfo->SetLabelText(_("Downloading catalog update..."));
900 wxYieldIfNeeded();
901 if (!m_connected) {
902 m_connected = true;
903 Connect(
904 wxEVT_DOWNLOAD_EVENT,
905 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
906 }
907 OCPN_downloadFileBackground(CATALOG_URL,
908 m_parent.pPlugIn->m_local_sources_catalog + "new",
909 this, &m_download_handle);
910 while (m_downloading) {
911 wxTheApp->ProcessPendingEvents();
912 wxMilliSleep(10);
913 }
914 if (!m_canceled) {
915 if (m_bTransferSuccess) {
916 wxRenameFile(m_parent.pPlugIn->m_local_sources_catalog + "new",
917 m_parent.pPlugIn->m_local_sources_catalog, true);
918 ReadLocalCatalog();
919 m_stLocalDownloadInfo->SetLabelText(_("Catalog update complete."));
920 } else {
921 m_stLocalDownloadInfo->SetLabelText(_("Download failed"));
922 }
923 }
924 m_btnDownloadLocal->SetLabelText(_("Download"));
925 m_downloadType = GribDownloadType::NONE;
926 EnableDownloadButtons();
927}
928
929void GribRequestSetting::OnDownloadLocal(wxCommandEvent &event) {
930 if (m_downloading) {
931 OCPN_cancelDownloadFileBackground(m_download_handle);
932 m_downloading = false;
933 m_download_handle = 0;
934 Disconnect(
935 wxEVT_DOWNLOAD_EVENT,
936 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
937 m_connected = false;
938 m_btnDownloadLocal->SetLabelText(_("Download"));
939 m_stLocalDownloadInfo->SetLabelText(_("Download canceled"));
940 m_canceled = true;
941 m_downloadType = GribDownloadType::NONE;
942 EnableDownloadButtons();
943 wxTheApp->ProcessPendingEvents();
944 wxYieldIfNeeded();
945 return;
946 }
947 m_canceled = false;
948 m_downloading = true;
949 m_downloadType = GribDownloadType::LOCAL;
950 EnableDownloadButtons();
951 m_btnDownloadLocal->SetLabelText(_("Cancel"));
952 m_staticTextInfo->SetLabelText(_("Downloading grib..."));
953 wxYieldIfNeeded();
954 auto src = (GribCatalogInfo *)(m_SourcesTreeCtrl1->GetItemData(
955 m_SourcesTreeCtrl1->GetSelection()));
956 if (!src || src->type != LocalSourceItem::GRIB || src->url.IsEmpty()) {
957 m_downloading = false;
958 m_stLocalDownloadInfo->SetLabelText(_("Download can't be started."));
959 m_btnDownloadWorld->SetLabelText(_("Download"));
960 m_downloadType = GribDownloadType::NONE;
961 EnableDownloadButtons();
962 return;
963 }
964 if (!m_connected) {
965 m_connected = true;
966 Connect(
967 wxEVT_DOWNLOAD_EVENT,
968 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
969 }
970 wxString url = src->url;
971 // If it is a grib that changes location and requires help of, download the
972 // manifest first
973 if (src->download_type == LocalGribDownloadType::MANIFEST) {
974 wxString path = m_parent.GetGribDir();
975 path.Append(wxFileName::GetPathSeparator());
976 path.Append("grib_manifest.json");
977 OCPN_downloadFileBackground(url, path, this, &m_download_handle);
978 while (m_downloading) {
979 wxTheApp->ProcessPendingEvents();
980 wxMilliSleep(10);
981 }
982 bool success = true;
983 if (!m_canceled) {
984 if (!m_bTransferSuccess) {
985 success = false;
986 m_stLocalDownloadInfo->SetLabelText(_("Download failed"));
987 }
988 } else {
989 success = false;
990 m_stLocalDownloadInfo->SetLabelText(_("Download canceled"));
991 }
992 if (success) {
993 wxJSONReader reader;
994 wxFileInputStream str(path);
995 wxJSONValue root;
996 reader.Parse(str, &root);
997 if (root.HasMember("url")) {
998 wxString parsed = root["url"].AsString();
999 if (parsed.StartsWith("http")) {
1000 url = parsed;
1001 } else {
1002 m_stLocalDownloadInfo->SetLabelText(_("Download failed"));
1003 success = false;
1004 }
1005 }
1006 }
1007 wxRemoveFile(path);
1008 if (!success) { // Something went wrong, clean up and do not continue to
1009 // the actual download
1010 m_downloading = false;
1011 m_download_handle = 0;
1012 Disconnect(wxEVT_DOWNLOAD_EVENT,
1013 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::
1014 onDLEvent);
1015 m_connected = false;
1016 m_btnDownloadLocal->SetLabelText(_("Download"));
1017 m_stLocalDownloadInfo->SetLabelText(_("Download failed"));
1018 m_canceled = true;
1019 m_downloadType = GribDownloadType::NONE;
1020 EnableDownloadButtons();
1021 wxTheApp->ProcessPendingEvents();
1022 wxYieldIfNeeded();
1023 return;
1024 }
1025 }
1026 // Download the actual grib
1027 m_downloading = true;
1028 wxYieldIfNeeded();
1029 if (!m_connected) {
1030 m_connected = true;
1031 Connect(
1032 wxEVT_DOWNLOAD_EVENT,
1033 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
1034 }
1035 wxString filename;
1036 if (!src->filename.IsEmpty()) {
1037 filename = src->filename;
1038 } else {
1039 filename =
1040 url.AfterLast('/'); // Get last part of the URL and try to sanitize the
1041 // filename somewhat if we call some API...
1042 filename.Replace("?", "_");
1043 filename.Replace("&", "_");
1044 if (!(filename.Contains(".grb2") || filename.Contains(".grib2") ||
1045 filename.Contains(".grb") || filename.Contains(".grib"))) {
1046 filename.Append(".grb");
1047 }
1048 }
1049
1050 wxString path = m_parent.GetGribDir();
1051 path.Append(wxFileName::GetPathSeparator());
1052 path.Append(filename);
1053 OCPN_downloadFileBackground(url, path, this, &m_download_handle);
1054 while (m_downloading) {
1055 wxTheApp->ProcessPendingEvents();
1056 wxMilliSleep(10);
1057 }
1058 if (!m_canceled) {
1059 if (m_bTransferSuccess) {
1060 m_stLocalDownloadInfo->SetLabelText(_("Grib download complete."));
1061 m_stLocalDownloadInfo->SetLabelText(
1062 wxString::Format(_("Download complete: %s"), path.c_str()));
1063 wxFileName fn(path);
1064 m_parent.m_grib_dir = fn.GetPath();
1065 m_parent.m_file_names.Clear();
1066 m_parent.m_file_names.Add(
1067 path); //("/home/nohal/Downloads/Cherbourg_4km_WRF_WAM_240228-12.grb.bz2");
1068 m_parent.OpenFile();
1069 if (m_parent.pPlugIn) {
1070 if (m_parent.pPlugIn->m_bZoomToCenterAtInit) m_parent.DoZoomToCenter();
1071 }
1072 m_parent.SetDialogsStyleSizePosition(true);
1073 SaveConfig();
1074 Close();
1075 } else {
1076 m_stLocalDownloadInfo->SetLabelText(_("Download failed"));
1077 }
1078 }
1079 if (m_canceled || !m_bTransferSuccess) wxRemoveFile(path);
1080 m_btnDownloadWorld->SetLabelText(_("Download"));
1081 m_downloadType = GribDownloadType::NONE;
1082 EnableDownloadButtons();
1083}
1084
1085void GribRequestSetting::EnableDownloadButtons() {
1086 switch (m_downloadType) {
1088 m_btnDownloadWorld->Enable(true);
1089 m_btnDownloadLocal->Enable(false);
1090 m_buttonUpdateCatalog->Enable(false);
1091 m_btnUpdateWorldModels->Enable(false);
1092 break;
1095 m_btnDownloadWorld->Enable(false);
1096 m_btnDownloadLocal->Enable(m_bLocal_source_selected || m_downloading);
1097 m_buttonUpdateCatalog->Enable(false);
1098 m_btnUpdateWorldModels->Enable(false);
1099 break;
1101 m_xygribPanel->m_download_button->Enable(true);
1102 break;
1103 default:
1104 m_btnDownloadWorld->Enable(m_chWorldModel->GetSelection() != wxNOT_FOUND);
1105 m_btnDownloadLocal->Enable(m_bLocal_source_selected || m_downloading);
1106 m_buttonUpdateCatalog->Enable(true);
1107 m_btnUpdateWorldModels->Enable(true);
1108 m_xygribPanel->m_download_button->Enable(true);
1109 break;
1110 }
1111}
1112
1113void GribRequestSetting::StopGraphicalZoneSelection() {
1115 RENDER_NONE; // eventually stop graphical zone display
1116
1117 RequestRefresh(GetGRIBCanvas());
1118}
1119
1121 if (!vp || m_VpMouse == vp) return;
1122
1123 delete m_VpMouse;
1124 m_VpMouse = new PlugIn_ViewPort(*vp);
1125}
1126
1128 if (!vp || m_VpFocus == vp) return;
1129
1130 delete m_VpFocus;
1131 m_VpFocus = new PlugIn_ViewPort(*vp);
1132
1133 if (!m_AllowSend) return;
1134 if (m_rbManualSelect->GetValue()) return;
1135
1136 SetVpSize(vp);
1137 // Viewport has changed : XyGrib estimated size must be recalculated
1138 // when in automatic selection mode.
1139 UpdateGribSizeEstimate();
1140}
1141
1142void GribRequestSetting::ApplyRequestConfig(unsigned rs, unsigned it,
1143 unsigned tr) {
1144 // some useful strings
1145 const wxString res[][RESOLUTIONS] = {
1146 {"0.25", "0.5", "1.0", "2.0"},
1147 {"0.2", "0.8", "1.6", wxEmptyString},
1148 {"0.08", "0.24", "1.0", wxEmptyString}, // RTOFS
1149 {"0.03", "0.24", "1.0", wxEmptyString}, // HRRR
1150 {"0.0625", "0.125", wxEmptyString, wxEmptyString}, // ICON
1151 {"0.4", "1.0", "2.0", wxEmptyString} // ECMWF
1152 };
1153
1154 IsZYGRIB = m_pMailTo->GetCurrentSelection() == ZYGRIB;
1155 if (IsZYGRIB)
1156 m_pModel->SetSelection(GFS); // Model is always GFS when Zygrib selected
1157 IsGFS = m_pModel->GetCurrentSelection() == GFS;
1158 bool IsRTOFS = m_pModel->GetCurrentSelection() == RTOFS;
1159 bool IsHRRR = m_pModel->GetCurrentSelection() == HRRR;
1160 bool IsICON = m_pModel->GetCurrentSelection() == ICON;
1161 bool IsECMWF = m_pModel->GetCurrentSelection() == ECMWF;
1162
1163 // populate resolution choice
1164 m_pResolution->Clear();
1165 if (m_pModel->GetCurrentSelection() >= 0) {
1166 for (int i = 0; i < RESOLUTIONS; i++) {
1167 if (res[m_pModel->GetCurrentSelection()][i] != wxEmptyString) {
1168 wxString s = res[m_pModel->GetCurrentSelection()][i];
1169 m_pResolution->Append(s);
1170 }
1171 }
1172 }
1173 m_pResolution->SetSelection(rs);
1174
1175 unsigned l;
1176 // populate time interval choice
1177 l = (IsGFS || IsRTOFS || IsICON || IsECMWF) ? 3 : IsHRRR ? 1 : 6;
1178
1179 unsigned m;
1180 m = IsHRRR ? 2 : 25;
1181
1182 m_pInterval->Clear();
1183 for (unsigned i = l; i < m; i *= 2)
1184 m_pInterval->Append(wxString::Format("%d", i));
1185 m_pInterval->SetSelection(wxMin(it, m_pInterval->GetCount() - 1));
1186
1187 // populate time range choice
1188 l = IsZYGRIB ? 8
1189 : IsGFS ? 16
1190 : IsRTOFS ? 6
1191 : IsECMWF ? 10
1192 : IsICON ? 7
1193 : IsHRRR ? 2
1194 : 3;
1195 m_pTimeRange->Clear();
1196 for (unsigned i = 2; i < l + 1; i++)
1197 m_pTimeRange->Append(wxString::Format("%d", i));
1198 m_pTimeRange->SetSelection(wxMin(l - 2, tr));
1199
1200 m_pModel->Enable(!IsZYGRIB);
1201 m_pWind->SetValue(!IsRTOFS);
1202 m_pPress->SetValue(!IsRTOFS);
1203 m_pWaves->SetValue(m_RequestConfigBase.GetChar(8) == 'X' && IsGFS);
1204 m_pWaves->Enable(IsECMWF ||
1205 (IsGFS && m_pTimeRange->GetCurrentSelection() < 7));
1206 // gfs & time range less than 8 days
1207 m_pRainfall->SetValue(m_RequestConfigBase.GetChar(9) == 'X' &&
1208 (IsGFS || IsHRRR));
1209 m_pRainfall->Enable(IsGFS || IsHRRR);
1210 m_pCloudCover->SetValue(m_RequestConfigBase.GetChar(10) == 'X' && IsGFS);
1211 m_pCloudCover->Enable(IsGFS);
1212 m_pAirTemp->SetValue(m_RequestConfigBase.GetChar(11) == 'X' &&
1213 (IsGFS || IsHRRR || IsICON || IsECMWF));
1214 m_pAirTemp->Enable(IsGFS || IsHRRR || IsICON || IsECMWF);
1215 m_pSeaTemp->SetValue(m_RequestConfigBase.GetChar(12) == 'X' &&
1216 ((!IsZYGRIB && IsGFS) || IsRTOFS || IsHRRR || IsICON));
1217 m_pSeaTemp->Enable(!IsZYGRIB && (IsGFS || IsHRRR || IsICON));
1218 m_pWindGust->SetValue(m_RequestConfigBase.GetChar(14) == 'X' &&
1219 (IsGFS || IsHRRR || IsICON));
1220 m_pWindGust->Enable(IsGFS || IsHRRR || IsICON);
1221 m_pCAPE->SetValue(m_RequestConfigBase.GetChar(15) == 'X' &&
1222 (IsGFS || IsHRRR));
1223 m_pCAPE->Enable(IsGFS || IsHRRR);
1224 m_pReflectivity->Enable(IsGFS || IsHRRR);
1225
1226 m_pAltitudeData->SetValue(
1227 (IsGFS || IsICON || IsECMWF)
1228 ? m_RequestConfigBase.GetChar(17) == 'X'
1229 : false); // altitude data zigrib + saildocs GFS and ICON
1230 m_pAltitudeData->Enable(IsGFS || IsICON || IsECMWF);
1231 m_p850hpa->SetValue(IsZYGRIB ? m_RequestConfigBase.GetChar(18) == 'X'
1232 : false); // only zygrib
1233 m_p850hpa->Enable(IsZYGRIB);
1234 m_p700hpa->SetValue(IsZYGRIB ? m_RequestConfigBase.GetChar(19) == 'X'
1235 : false); // only zigrib
1236 m_p700hpa->Enable(IsZYGRIB);
1237 m_p500hpa->SetValue((IsGFS || IsICON || IsECMWF)
1238 ? m_RequestConfigBase.GetChar(20) == 'X'
1239 : false); // zigrib + saildocs GFS ICON ECMWF
1240 m_p500hpa->Enable(IsGFS || IsICON || IsECMWF);
1241 m_p300hpa->SetValue(IsZYGRIB ? m_RequestConfigBase.GetChar(21) == 'X'
1242 : false); // only zigrib
1243 m_p300hpa->Enable(IsZYGRIB);
1244
1245 m_pCurrent->SetValue(IsRTOFS);
1246 m_pCurrent->Enable(false);
1247
1248 // show parameters only if necessary
1249 m_cMovingGribEnabled->Show(!IsZYGRIB); // show/hide Moving settings
1250 m_fgMovingParams->ShowItems(m_cMovingGribEnabled->IsChecked() &&
1251 m_cMovingGribEnabled->IsShown());
1252
1253 m_fgLog->ShowItems(IsZYGRIB); // show/hide zigrib login
1254
1255 m_pWModel->Show(IsZYGRIB && m_pWaves->IsChecked()); // show/hide waves model
1256
1257 m_fgAltitudeData->ShowItems(
1258 m_pAltitudeData->IsChecked()); // show/hide altitude params
1259}
1260
1261void GribRequestSetting::OnTopChange(wxCommandEvent &event) {
1262 // deactivate momentary ZyGrib option
1263 if (m_pMailTo->GetCurrentSelection() == ZYGRIB) {
1264 m_pMailTo->SetSelection(0);
1266 this,
1267 _("Sorry...\nZyGrib momentary stopped providing this service...\nOnly "
1268 "Saildocs option is available"),
1269 _("Warning"), wxOK);
1270 }
1271 ApplyRequestConfig(m_pResolution->GetCurrentSelection(),
1272 m_pInterval->GetCurrentSelection(),
1273 m_pTimeRange->GetCurrentSelection());
1274
1275 m_cMovingGribEnabled->Show(m_pMailTo->GetCurrentSelection() == SAILDOCS);
1276
1277 if (m_AllowSend) m_MailImage->SetValue(WriteMail());
1278
1279 SetRequestDialogSize();
1280}
1281
1282void GribRequestSetting::UpdateAreaSelectionState() {
1283 bool readOnly = (m_ZoneSelMode == AUTO_SELECTION);
1284 m_spMaxLat->Enable(!readOnly);
1285 m_spMaxLon->Enable(!readOnly);
1286 m_spMinLat->Enable(!readOnly);
1287 m_spMinLon->Enable(!readOnly);
1288 m_cUseSavedZone->Enable(m_ZoneSelMode != AUTO_SELECTION);
1289}
1290
1291void GribRequestSetting::OnZoneSelectionModeChange(wxCommandEvent &event) {
1292 StopGraphicalZoneSelection(); // eventually stop graphical zone display
1293
1295 // Recompute zone based on the viewport that has the focus.
1296 SetVpSize(m_VpFocus);
1297 }
1298
1299 if (event.GetId() == AUTOSELECT) {
1300 m_rbManualSelect->SetValue(false);
1301 } else if (event.GetId() == MANSELECT) {
1302 m_rbCurrentView->SetValue(false);
1303 // set temporarily zone selection mode if manual selection set, put it
1304 // directly in "drawing" position else put it in "auto selection" position
1307 m_cUseSavedZone->SetValue(false);
1308 } else if (event.GetId() == SAVEDZONE) {
1309 // set temporarily zone selection mode if saved selection set, put it
1310 // directly in "no selection" position else put it directly in "drawing"
1311 // position
1313 m_cUseSavedZone->GetValue() ? SAVED_SELECTION : DRAW_SELECTION;
1314 }
1315 m_parent.SetRequestButtonBitmap(m_ZoneSelMode); // set appopriate bitmap
1316 UpdateAreaSelectionState(); // update the state of the coordinate spinners
1317 if (m_AllowSend) m_MailImage->SetValue(WriteMail());
1318
1319 SetRequestDialogSize();
1320}
1321
1323 wxPoint p;
1325
1326 int x = (m_StartPoint.x < p.x) ? m_StartPoint.x : p.x;
1327 int y = (m_StartPoint.y < p.y) ? m_StartPoint.y : p.y;
1328
1329 int zw = fabs((double)p.x - m_StartPoint.x);
1330 int zh = fabs((double)p.y - m_StartPoint.y);
1331
1332 wxPoint center;
1333 center.x = x + (zw / 2);
1334 center.y = y + (zh / 2);
1335
1336 wxFont fo = *OCPNGetFont(_("Dialog"));
1337 fo.SetPointSize(
1338 (fo.GetPointSize() * m_displayScale / OCPN_GetWinDIPScaleFactor()));
1339 wxFont *font = &fo;
1340 wxColour pen_color, back_color;
1341 GetGlobalColor("DASHR", &pen_color);
1342 GetGlobalColor("YELO1", &back_color);
1343
1344 int label_offsetx = 5, label_offsety = 1;
1345
1346 double size;
1347 EstimateFileSize(&size);
1348
1349 wxString label(_("Coord. "));
1350 label.Append(toMailFormat(1, m_spMaxLat->GetValue()) + " ");
1351 label.Append(toMailFormat(0, m_spMinLon->GetValue()) + " ");
1352 label.Append(toMailFormat(1, m_spMinLat->GetValue()) + " ");
1353 label.Append(toMailFormat(0, m_spMaxLon->GetValue()) + "\n");
1354 label.Append("Estim. Size ")
1355 .Append((wxString::Format("%1.2f ", size) + _("MB")));
1356
1357 if (m_pdc) {
1358 wxPen pen(pen_color);
1359 pen.SetWidth(3);
1360 m_pdc->SetPen(pen);
1361 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
1362 m_pdc->DrawRectangle(x, y, zw, zh);
1363
1364 int w, h, sl;
1365#ifdef __WXMAC__
1366 wxScreenDC sdc;
1367 sdc.GetMultiLineTextExtent(label, &w, &h, &sl, font);
1368#else
1369 m_pdc->GetMultiLineTextExtent(label, &w, &h, &sl, font);
1372#endif
1373 w += 2 * label_offsetx, h += 2 * label_offsety;
1374 x = center.x - (w / 2);
1375 y = center.y - (h / 2);
1376
1377 h *= m_displayScale;
1378 w *= m_displayScale;
1379
1380 wxBitmap bm(w, h);
1381 wxMemoryDC mdc(bm);
1382 mdc.Clear();
1383
1384 mdc.SetFont(*font);
1385 mdc.SetBrush(back_color);
1386 mdc.SetPen(*wxTRANSPARENT_PEN);
1387 mdc.SetTextForeground(wxColor(0, 0, 0));
1388 mdc.DrawRectangle(0, 0, w, h);
1389 mdc.DrawLabel(label, wxRect(label_offsetx, label_offsety, w, h));
1390
1391 wxImage im = bm.ConvertToImage();
1392 im.InitAlpha();
1393 w = im.GetWidth(), h = im.GetHeight();
1394 for (int j = 0; j < h; j++)
1395 for (int i = 0; i < w; i++) im.SetAlpha(i, j, 155);
1396
1397 m_pdc->DrawBitmap(im, x, y, true);
1398
1399 } else {
1400#ifdef ocpnUSE_GL
1401#ifndef USE_ANDROID_GLES2
1402
1403 if (!m_oDC) {
1404 m_oDC = new pi_ocpnDC();
1405 }
1406
1407 m_oDC->SetVP(m_VpMouse);
1408 m_oDC->SetPen(wxPen(pen_color, 3));
1409
1410 wxPoint outline[5];
1411 outline[0] = wxPoint(x, y);
1412 outline[1] = wxPoint(x + zw, y);
1413 outline[2] = wxPoint(x + zw, y + zh);
1414 outline[3] = wxPoint(x, y + zh);
1415 outline[4] = wxPoint(x, y);
1416 m_oDC->DrawLines(5, outline);
1417
1418 m_oDC->SetFont(*font);
1419 int w, h, ww, hw;
1420 m_oDC->GetTextExtent(label, &w, &h);
1421 h *= m_displayScale;
1422 w *= m_displayScale;
1423
1424 m_oDC->GetTextExtent("W", &ww, &hw);
1425
1426 int label_offsetx_ = ww, label_offsety_ = 1;
1427 int x_ = center.x - w / 2;
1428 int y_ = center.y - h / 2;
1429
1430 w += 2 * label_offsetx_, h += 2 * label_offsety_;
1431
1432 m_oDC->SetBrush(wxBrush(back_color));
1433 m_oDC->DrawRoundedRectangle(x_, y_, w, h, 0);
1434
1435 /* draw bounding rectangle */
1436 m_oDC->SetPen(wxPen(wxColour(0, 0, 0), 1));
1437
1438 outline[0] = wxPoint(x_, y_);
1439 outline[1] = wxPoint(x_ + w, y_);
1440 outline[2] = wxPoint(x_ + w, y_ + h);
1441 outline[3] = wxPoint(x_, y_ + h);
1442 outline[4] = wxPoint(x_, y_);
1443 m_oDC->DrawLines(5, outline);
1444
1445 m_oDC->DrawText(label, x_ + label_offsetx_, y_ + label_offsety_);
1446
1447#endif
1448#endif
1449 }
1450 return true;
1451}
1452
1454 if (m_RenderSelectionZoneState == RENDER_NONE) return false;
1455 m_pdc = nullptr; // inform lower layers that this is OpenGL render
1456 return DoRenderZoneOverlay();
1457}
1458
1460 if (m_RenderSelectionZoneState == RENDER_NONE) return false;
1461 m_pdc = &dc;
1462 return DoRenderZoneOverlay();
1463}
1464
1465void GribRequestSetting::OnMovingClick(wxCommandEvent &event) {
1466 m_fgMovingParams->ShowItems(m_cMovingGribEnabled->IsChecked() &&
1467 m_cMovingGribEnabled->IsShown());
1468
1469 if (m_AllowSend) m_MailImage->SetValue(WriteMail());
1470 SetRequestDialogSize();
1471
1472 this->Refresh();
1473}
1474
1475void GribRequestSetting::OnCoordinatesChange(wxSpinEvent &event) {
1476 SetCoordinatesText();
1477
1478 StopGraphicalZoneSelection(); // eventually stop graphical zone display
1479
1480 if (!m_AllowSend) return;
1481
1482 m_MailImage->SetValue(WriteMail());
1483}
1484
1485void GribRequestSetting::OnAnyChange(wxCommandEvent &event) {
1486 m_fgAltitudeData->ShowItems(m_pAltitudeData->IsChecked());
1487
1488 m_pWModel->Show(IsZYGRIB && m_pWaves->IsChecked());
1489
1490 if (m_AllowSend) m_MailImage->SetValue(WriteMail());
1491
1492 SetRequestDialogSize();
1493}
1494
1495void GribRequestSetting::OnTimeRangeChange(wxCommandEvent &event) {
1496 m_pWModel->Show(IsZYGRIB && m_pWaves->IsChecked());
1497
1498 if (m_pModel->GetCurrentSelection() == 0) { // gfs
1499 if (m_pTimeRange->GetCurrentSelection() >
1500 6) { // time range more than 8 days
1501 m_pWaves->SetValue(0);
1502 m_pWaves->Enable(false);
1504 this,
1505 _("You request a forecast for more than 8 days horizon.\nThis is "
1506 "conflicting with Wave data which will be removed from your "
1507 "request.\nDon't forget that beyond the first 8 days, the "
1508 "resolution will be only 2.5\u00B0x2.5\u00B0\nand the time "
1509 "intervall 12 hours."),
1510 _("Warning!"));
1511 } else
1512 m_pWaves->Enable(true);
1513 }
1514
1515 if (m_AllowSend) m_MailImage->SetValue(WriteMail());
1516
1517 SetRequestDialogSize();
1518}
1519
1520void GribRequestSetting::OnOK(wxCommandEvent &event) {
1521 bool IsCOAMPS = m_pModel->GetCurrentSelection() == COAMPS;
1522 bool IsRTOFS = m_pModel->GetCurrentSelection() == RTOFS;
1523 bool IsICON = m_pModel->GetCurrentSelection() == ICON;
1524 bool IsECMWF = m_pModel->GetCurrentSelection() == ECMWF;
1525 m_RequestConfigBase.SetChar(
1526 0, (char)(m_pMailTo->GetCurrentSelection() + '0')); // recipient
1527 m_cMovingGribEnabled->IsChecked()
1528 ? m_RequestConfigBase.SetChar(16, 'X') // moving grib
1529 : m_RequestConfigBase.SetChar(16, '.');
1530
1531 if (!IsZYGRIB)
1532 m_RequestConfigBase.SetChar(
1533 1, (char)(m_pModel->GetCurrentSelection() + '0')); // model
1534 if (!IsECMWF)
1535 m_RequestConfigBase.SetChar(
1536 2, (char)(m_pResolution->GetCurrentSelection() + '0')); // resolution
1537
1538 m_RequestConfigBase.SetChar(3,
1539 (char)(m_pInterval->GetCurrentSelection() + '0'));
1540
1541 wxString range;
1542 range.Printf("%x", m_pTimeRange->GetCurrentSelection() +
1543 1); // range max = 2 to 16 stored in hexa 1 to f
1544 m_RequestConfigBase.SetChar(4, range.GetChar(0));
1545
1546 if (IsZYGRIB && m_pWModel->IsShown())
1547 m_RequestConfigBase.SetChar(
1548 5, (char)(m_pWModel->GetCurrentSelection() + '0')); // waves model
1549
1550 m_RequestConfigBase.SetChar(
1551 6, 'X'); // wind must be always selected as a default
1552 m_RequestConfigBase.SetChar(
1553 7, 'X'); // pressure must be always selected as a default
1554
1555 if (!IsCOAMPS && !IsRTOFS) {
1556 m_pWindGust->IsChecked() ? m_RequestConfigBase.SetChar(14, 'X') // Gust
1557 : m_RequestConfigBase.SetChar(14, '.');
1558 m_pWaves->IsChecked() ? m_RequestConfigBase.SetChar(8, 'X') // waves
1559 : m_RequestConfigBase.SetChar(8, '.');
1560 m_pRainfall->IsChecked() ? m_RequestConfigBase.SetChar(9, 'X') // rainfall
1561 : m_RequestConfigBase.SetChar(9, '.');
1562 m_pCloudCover->IsChecked() ? m_RequestConfigBase.SetChar(10, 'X') // clouds
1563 : m_RequestConfigBase.SetChar(10, '.');
1564 m_pAirTemp->IsChecked() ? m_RequestConfigBase.SetChar(11, 'X') // air temp
1565 : m_RequestConfigBase.SetChar(11, '.');
1566 m_pSeaTemp->IsChecked() ? m_RequestConfigBase.SetChar(12, 'X') // sea temp
1567 : m_RequestConfigBase.SetChar(12, '.');
1568 m_pCAPE->IsChecked() ? m_RequestConfigBase.SetChar(15, 'X') // cape
1569 : m_RequestConfigBase.SetChar(15, '.');
1570 }
1571 if (IsRTOFS) // current
1572 m_pCurrent->IsChecked() ? m_RequestConfigBase.SetChar(13, 'X')
1573 : m_RequestConfigBase.SetChar(13, '.');
1574
1575 if (IsGFS || IsICON || IsECMWF) {
1576 m_pAltitudeData->IsChecked()
1577 ? m_RequestConfigBase.SetChar(17, 'X') // altitude data
1578 : m_RequestConfigBase.SetChar(17, '.');
1579 m_p500hpa->IsChecked() ? m_RequestConfigBase.SetChar(20, 'X')
1580 : m_RequestConfigBase.SetChar(20, '.');
1581 }
1582 if (IsZYGRIB) {
1583 m_p850hpa->IsChecked() ? m_RequestConfigBase.SetChar(18, 'X')
1584 : m_RequestConfigBase.SetChar(18, '.');
1585 m_p700hpa->IsChecked() ? m_RequestConfigBase.SetChar(19, 'X')
1586 : m_RequestConfigBase.SetChar(19, '.');
1587 m_p300hpa->IsChecked() ? m_RequestConfigBase.SetChar(21, 'X')
1588 : m_RequestConfigBase.SetChar(21, '.');
1589 }
1590 SaveConfig();
1591 wxCloseEvent evt;
1592 OnClose(evt);
1593}
1594
1595wxString GribRequestSetting::WriteMail() {
1596 // define size limits for zyGrib
1597 int limit = IsZYGRIB ? 2 : 0; // new limit 2 mb
1598
1599 m_MailError_Nb = 0;
1600 // some useful strings
1601 const wxString s[] = {",", " "}; // separators
1602 const wxString p[][11] = {
1603 // parameters GFS from Saildocs
1604 {"APCP", "TCDC", "AIRTMP", "HTSGW,WVPER,WVDIR", "SEATMP", "GUST", "CAPE",
1605 wxEmptyString, wxEmptyString, "WIND500,HGT500", wxEmptyString},
1606 {}, // COAMPS
1607 {}, // RTOFS
1608 {}, // HRRR = same parameters as GFS
1609 // parametres ICON
1610 {"", "", "AIRTMP", "", "SFCTMP", "GUST", "", "", "", "WIND500,HGT500",
1611 ""},
1612 // parametres ECMWF
1613 {"", "", "TEMP", "WAVES", "", "", "", "", "", "WIND500,HGT500", ""},
1614 // parameters GFS from zygrib
1615 {"PRECIP", "CLOUD", "TEMP", "WVSIG WVWIND", wxEmptyString, "GUST", "CAPE",
1616 "A850", "A700", "A500", "A300"}};
1617
1618 wxString r_topmess, r_parameters, r_zone;
1619 // write the top part of the mail
1620 switch (m_pMailTo->GetCurrentSelection()) {
1621 case SAILDOCS: // Saildocs
1622 r_zone = toMailFormat(1, m_spMaxLat->GetValue()) + "," +
1623 toMailFormat(1, m_spMinLat->GetValue()) + "," +
1624 toMailFormat(2, m_spMinLon->GetValue()) + "," +
1625 toMailFormat(2, m_spMaxLon->GetValue());
1626 r_topmess = "send ";
1627 r_topmess.Append(m_pModel->GetStringSelection() + ":");
1628 r_topmess.Append(r_zone + "|");
1629 r_topmess.Append(m_pResolution->GetStringSelection())
1630 .Append(",")
1631 .Append(m_pResolution->GetStringSelection())
1632 .Append("|");
1633 double v;
1634 m_pInterval->GetStringSelection().ToDouble(&v);
1635 r_topmess.Append(wxString::Format("0,%d,%d", (int)v, (int)v * 2));
1636 m_pTimeRange->GetStringSelection().ToDouble(&v);
1637 r_topmess.Append(wxString::Format("..%d", (int)v * 24) + "|=\n");
1638 break;
1639 case ZYGRIB: // Zygrib
1640 double maxlon = (m_spMinLon->GetValue() > m_spMaxLon->GetValue() &&
1641 m_spMaxLon->GetValue() < 0)
1642 ? m_spMaxLon->GetValue() + 360
1643 : m_spMaxLon->GetValue();
1644 r_zone = toMailFormat(1, m_spMinLat->GetValue()) +
1645 toMailFormat(2, m_spMinLon->GetValue()) + " " +
1646 toMailFormat(1, m_spMaxLat->GetValue()) +
1647 toMailFormat(2, maxlon);
1648 r_topmess = "login : ";
1649 r_topmess.Append(m_pLogin->GetValue() + "\n");
1650 r_topmess.Append("code :");
1651 r_topmess.Append(m_pCode->GetValue() + "\n");
1652 r_topmess.Append("area : ");
1653 r_topmess.append(r_zone + "\n");
1654 r_topmess.Append("resol : ");
1655 r_topmess.append(m_pResolution->GetStringSelection() + "\n");
1656 r_topmess.Append("days : ");
1657 r_topmess.append(m_pTimeRange->GetStringSelection() + "\n");
1658 r_topmess.Append("hours : ");
1659 r_topmess.append(m_pInterval->GetStringSelection() + "\n");
1660 if (m_pWaves->IsChecked()) {
1661 r_topmess.Append("waves : ");
1662 r_topmess.append(m_pWModel->GetStringSelection() + "\n");
1663 }
1664 r_topmess.Append("meteo : ");
1665 r_topmess.append(m_pModel->GetStringSelection() + "\n");
1666 if (m_pLogin->GetValue().IsEmpty() || m_pCode->GetValue().IsEmpty())
1667 m_MailError_Nb = 6;
1668 break;
1669 }
1670 // write the parameters part of the mail
1671 int GFSZ = IsZYGRIB ? 6 : 0;
1672 switch (m_pModel->GetCurrentSelection()) {
1673 case GFS: // GFS
1674 r_parameters = "WIND" + s[m_pMailTo->GetCurrentSelection()] +
1675 "PRESS"; // the default minimum request parameters
1676 if (m_pRainfall->IsChecked())
1677 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1678 p[GFS + GFSZ][0]);
1679 if (m_pCloudCover->IsChecked())
1680 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1681 p[GFS + GFSZ][1]);
1682 if (m_pAirTemp->IsChecked())
1683 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1684 p[GFS + GFSZ][2]);
1685 if (m_pWaves->IsChecked())
1686 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1687 p[GFS + GFSZ][3]);
1688 if (m_pSeaTemp->IsChecked())
1689 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1690 p[GFS + GFSZ][4]);
1691 if (m_pWindGust->IsChecked())
1692 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1693 p[GFS + GFSZ][5]);
1694 if (m_pCAPE->IsChecked())
1695 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1696 p[GFS + GFSZ][6]);
1697 if (m_pAltitudeData->IsChecked()) {
1698 if (m_p850hpa->IsChecked())
1699 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1700 p[GFS + GFSZ][7]);
1701 if (m_p700hpa->IsChecked())
1702 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1703 p[GFS + GFSZ][8]);
1704 if (m_p500hpa->IsChecked())
1705 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1706 p[GFS + GFSZ][9]);
1707 if (m_p300hpa->IsChecked())
1708 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1709 p[GFS + GFSZ][10]);
1710 }
1711 break;
1712 case COAMPS: // COAMPS
1713 r_parameters = "WIND,PRMSL"; // the default parameters for this
1714 // model
1715 break;
1716 case RTOFS: // RTOFS
1717 r_parameters = "CUR,WTMP"; // the default parameters for this model
1718 break;
1719 case HRRR: // HRRR
1720 r_parameters = "WIND,PRMSL"; // the default parameters for this
1721 // model
1722 if (m_pRainfall->IsChecked())
1723 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[GFS][0]);
1724 if (m_pAirTemp->IsChecked())
1725 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[GFS][2]);
1726 if (m_pSeaTemp->IsChecked())
1727 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[GFS][4]);
1728 if (m_pWindGust->IsChecked())
1729 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[GFS][5]);
1730 if (m_pCAPE->IsChecked())
1731 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[GFS][6]);
1732 break;
1733 case ICON:
1734 r_parameters = "WIND,PRMSL"; // the default parameters for this
1735 // model
1736 if (m_pAirTemp->IsChecked())
1737 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ICON][2]);
1738 if (m_pSeaTemp->IsChecked())
1739 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ICON][4]);
1740 if (m_pWindGust->IsChecked())
1741 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ICON][5]);
1742 if (m_pAltitudeData->IsChecked()) {
1743 if (m_p500hpa->IsChecked())
1744 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ICON][9]);
1745 }
1746 break;
1747 case ECMWF:
1748 r_parameters = "WIND,MSLP"; // the default parameters for this
1749 // model
1750 if (m_pAirTemp->IsChecked())
1751 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ECMWF][2]);
1752 if (m_pAltitudeData->IsChecked()) {
1753 if (m_p500hpa->IsChecked())
1754 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] +
1755 p[ECMWF][9]);
1756 }
1757 if (m_pWaves->IsChecked())
1758 r_parameters.Append(s[m_pMailTo->GetCurrentSelection()] + p[ECMWF][3]);
1759 break;
1760 }
1761 if (!IsZYGRIB && m_cMovingGribEnabled->IsChecked()) // moving grib
1762 r_parameters.Append(wxString::Format("|%d,%d", m_sMovingSpeed->GetValue(),
1763 m_sMovingCourse->GetValue()));
1764
1765 // line lenth limitation
1766 int j = 0;
1767 char c = m_pMailTo->GetCurrentSelection() == SAILDOCS ? ',' : ' ';
1768 for (size_t i = 0; i < r_parameters.Len(); i++) {
1769 if (r_parameters.GetChar(i) == '|')
1770 j--; // do not split Saildocs "moving" values
1771 if (r_parameters.GetChar(i) == c) j++;
1772 if (j > 6) { // no more than 6 parameters on the same line
1773 r_parameters.insert(
1774 i + 1, m_pMailTo->GetCurrentSelection() == SAILDOCS ? "=\n" : "\n");
1775 break;
1776 }
1777 }
1778
1779 double size;
1780 m_MailError_Nb += EstimateFileSize(&size);
1781
1782 m_tFileSize->SetLabel(wxString::Format("%1.2f ", size) + _("MB"));
1783
1784 if (IsZYGRIB) {
1785 m_tLimit->SetLabel(wxString("( ") + _("Max") +
1786 wxString::Format(" %d ", limit) + _("MB") + " )");
1787 if (size > limit) m_MailError_Nb += 2;
1788 } else
1789 m_tLimit->SetLabel(wxEmptyString);
1790
1791 return wxString(r_topmess + r_parameters);
1792}
1793
1794int GribRequestSetting::EstimateFileSize(double *size) {
1795 if (!size) return 0; // Wrong parameter
1796 *size = 0.;
1797
1798 // too small zone ? ( mini 2 * resolutions )
1799 double reso, time, inter;
1800 m_pResolution->GetStringSelection().ToDouble(&reso);
1801 m_pTimeRange->GetStringSelection().ToDouble(&time);
1802 m_pInterval->GetStringSelection().ToDouble(&inter);
1803
1804 double maxlon = m_spMaxLon->GetValue(), minlon = m_spMinLon->GetValue();
1805 double maxlat = m_spMaxLat->GetValue(), minlat = m_spMinLat->GetValue();
1806 if (maxlat - minlat < 0) return 3; // maxlat must be > minlat
1807 double wlon = (maxlon > minlon ? 0 : 360) + maxlon - minlon;
1808 if (wlon > 180 || (maxlat - minlat > 180)) return 4; // ovoid too big area
1809
1810 if (fabs(wlon) < 2 * reso || maxlat - minlat < 2 * reso)
1811 return 5; // ovoid too small area
1812
1813 int npts = (int)(ceil(((double)(maxlat - minlat) / reso)) *
1814 ceil(((double)(wlon) / reso)));
1815
1816 if (m_pModel->GetCurrentSelection() == COAMPS) // limited area for COAMPS
1817 npts = wxMin(npts, (int)(ceil(40.0 / reso) * ceil(40.0 / reso)));
1818
1819 // Nombre de GribRecords
1820 int nbrec = (int)(time * 24 / inter) + 1;
1821 int nbPress = (m_pPress->IsChecked()) ? nbrec : 0;
1822 int nbWind = (m_pWind->IsChecked()) ? 2 * nbrec : 0;
1823 int nbwave = (m_pWaves->IsChecked()) ? 2 * nbrec : 0;
1824 int nbRain = (m_pRainfall->IsChecked()) ? nbrec - 1 : 0;
1825 int nbCloud = (m_pCloudCover->IsChecked()) ? nbrec - 1 : 0;
1826 int nbTemp = (m_pAirTemp->IsChecked()) ? nbrec : 0;
1827 int nbSTemp = (m_pSeaTemp->IsChecked()) ? nbrec : 0;
1828 int nbGUSTsfc = (m_pWindGust->IsChecked()) ? nbrec : 0;
1829 int nbCurrent = (m_pCurrent->IsChecked()) ? nbrec : 0;
1830 int nbCape = (m_pCAPE->IsChecked()) ? nbrec : 0;
1831 int nbAltitude =
1832 IsZYGRIB ? 5 * nbrec : 3 * nbrec; // five data types are included in each
1833 // ZyGrib altitude request and only
1834 // three in sSaildocs's
1835 int head = 84;
1836 double estime = 0.0;
1837 int nbits;
1838
1839 nbits = 13;
1840 estime += nbWind * (head + (nbits * npts) / 8 + 2);
1841 estime += nbCurrent * (head + (nbits * npts) / 8 + 2);
1842
1843 nbits = 11;
1844 estime += nbTemp * (head + (nbits * npts) / 8 + 2);
1845 estime += nbSTemp * (head + (nbits * npts) / 8 + 2);
1846
1847 nbits = 4;
1848 estime += nbRain * (head + (nbits * npts) / 8 + 2);
1849
1850 nbits = 15;
1851 estime += nbPress * (head + (nbits * npts) / 8 + 2);
1852
1853 nbits = 4;
1854 estime += nbCloud * (head + (nbits * npts) / 8 + 2);
1855
1856 nbits = 7;
1857 estime += nbGUSTsfc * (head + (nbits * npts) / 8 + 2);
1858
1859 nbits = 5;
1860 estime += nbCape * (head + (nbits * npts) / 8 + 2);
1861
1862 nbits = 6;
1863 estime += nbwave * (head + (nbits * npts) / 8 + 2);
1864
1865 if (m_pAltitudeData->IsChecked()) {
1866 int nbalt = 0;
1867 if (m_p850hpa->IsChecked()) nbalt++;
1868 if (m_p700hpa->IsChecked()) nbalt++;
1869 if (m_p500hpa->IsChecked()) nbalt++;
1870 if (m_p300hpa->IsChecked()) nbalt++;
1871
1872 nbits = 12;
1873 estime += nbAltitude * nbalt * (head + (nbits * npts) / 8 + 2);
1874 }
1875
1876 *size = estime / (1024. * 1024.);
1877
1878 return 0;
1879}
1880
1881const wxString EncodeURL(const wxString &uri) {
1882 static std::unordered_map<int, wxString> sEncodeMap = {
1883 {(int)'!', "%21"}, {(int)'#', "%23"}, {(int)'$', "%24"},
1884 {(int)'&', "%26"}, {(int)'\'', "%27"}, {(int)'(', "%28"},
1885 {(int)')', "%29"}, {(int)'*', "%2A"}, {(int)'+', "%2B"},
1886 {(int)',', "%2C"}, {(int)';', "%3B"}, {(int)'=', "%3D"},
1887 {(int)'?', "%3F"}, {(int)'@', "%40"}, {(int)'[', "%5B"},
1888 {(int)']', "%5D"}, {(int)' ', "%20"}, {(int)'|', "%7C"},
1889 {(int)':', "%3A"}, {(int)'\n', "%0A"}};
1890
1891 wxString encoded;
1892 for (size_t i = 0; i < uri.length(); ++i) {
1893 wxChar ch = uri[i];
1894 std::unordered_map<int, wxString>::iterator iter = sEncodeMap.find((int)ch);
1895 if (iter != sEncodeMap.end()) {
1896 encoded << iter->second;
1897 } else {
1898 encoded << ch;
1899 }
1900 }
1901 return encoded;
1902}
1903
1904void GribRequestSetting::OnSendMaiL(wxCommandEvent &event) {
1905 StopGraphicalZoneSelection(); // eventually stop graphical zone display
1906
1907 if (!m_AllowSend) {
1908 m_rButtonCancel->Show();
1909 m_rButtonApply->Show();
1910 m_rButtonYes->SetLabel(_("Send"));
1911
1912 m_MailImage->SetForegroundColour(
1913 wxColor(0, 0, 0)); // permit to send a (new) message
1914 m_AllowSend = true;
1915
1916 m_MailImage->SetValue(WriteMail());
1917 SetRequestDialogSize();
1918
1919 return;
1920 }
1921
1922 const wxString error[] = {
1923 "\n\n",
1924 _("Before sending an email to Zygrib you have to enter your Login and "
1925 "Code.\nPlease visit www.zygrib.org/ and follow instructions..."),
1926 _("Too big file! zyGrib limit is 2Mb!"),
1927 _("Error! Max Lat lower than Min Lat or Max Lon lower than Min Lon!"),
1928 _("Too large area! Each side must be less than 180\u00B0!"),
1929 _("Too small area for this resolution!")};
1930
1931 ::wxBeginBusyCursor();
1932
1933 m_MailImage->SetForegroundColour(wxColor(255, 0, 0));
1934 m_AllowSend = false;
1935
1936 if (m_MailError_Nb) {
1937 if (m_MailError_Nb > 7) {
1938 m_MailImage->SetValue(error[1] + error[0] + error[m_MailError_Nb - 6]);
1939 } else {
1940 if (m_MailError_Nb == 6) m_MailError_Nb = 1;
1941 m_MailImage->SetValue(error[m_MailError_Nb]);
1942 }
1943
1944 m_rButtonYes->SetLabel(_("Continue..."));
1945 SetRequestDialogSize();
1946
1947 ::wxEndBusyCursor();
1948
1949 return;
1950 }
1951
1952 std::string mailto =
1953 (m_pMailTo->GetCurrentSelection() == SAILDOCS
1954 ? m_MailToAddresses.BeforeFirst(';') // to request address
1955 : m_MailToAddresses.AfterFirst(';').BeforeFirst(';'))
1956 .ToStdString();
1957 std::string mailfrom = m_pSenderAddress->GetValue().ToStdString();
1958
1959 std::regex mailregex("^([a-z0-9+_\\.-]+)@([\\da-z\\.-]+)\\.([a-z\\.]{2,6})$");
1960
1961 if ((!mailto.empty() && !std::regex_match(mailto, mailregex)) ||
1962 (!mailfrom.empty() && !std::regex_match(mailfrom, mailregex))) {
1964 this,
1965 _("Sender or recipient e-mail address seems invalid.\nPlease correct "
1966 "it in the configuration file."),
1967 _("Error"), wxOK | wxICON_ERROR);
1968 return;
1969 }
1970
1971#ifdef __WXMAC__
1972 // macOS, at least Big Sur, requires the body to be URLEncoded, otherwise the
1973 // invocation of the mail application via sh/open in wxEmail fails due to
1974 // "invalid characters" in "filename" regardless of quotation used (which is
1975 // weird, but real)
1976 wxMailMessage *message =
1977 new wxMailMessage((m_pMailTo->GetCurrentSelection() == SAILDOCS)
1978 ? "grib-request"
1979 : "gribauto", // requested subject
1980 mailto,
1981 EncodeURL(WriteMail()), // message image
1982 mailfrom);
1983#else
1984 wxMailMessage *message =
1985 new wxMailMessage((m_pMailTo->GetCurrentSelection() == SAILDOCS)
1986 ? "grib-request"
1987 : "gribauto", // requested subject
1988 mailto,
1989 WriteMail(), // message image
1990 mailfrom);
1991#endif
1992
1993 wxEmail mail;
1994 if (mail.Send(*message, m_SendMethod)) {
1995#ifdef __WXMSW__
1996 m_MailImage->SetValue(
1997 _("Your request is ready. An email is prepared in your email "
1998 "environment. \nYou have just to verify and send it...\nSave or "
1999 "Cancel to finish...or Continue..."));
2000#else
2001 if (m_SendMethod == 0) {
2002 m_MailImage->SetValue(
2003 _("Your request is ready. An email is prepared in your email "
2004 "environment. \nYou have just to verify and send it...\nSave or "
2005 "Cancel to finish...or Continue..."));
2006 } else {
2007 m_MailImage->SetValue(
2008 _("Your request was sent \n(if your system has an MTA configured and "
2009 "is able to send email).\nSave or Cancel to finish...or "
2010 "Continue..."));
2011 }
2012#endif
2013 } else {
2014 m_MailImage->SetValue(
2015 _("Request can't be sent. Please verify your email systeme "
2016 "parameters.\nYou should also have a look at your log file.\nSave or "
2017 "Cancel to finish..."));
2018 m_rButtonYes->Hide();
2019 }
2020 m_rButtonYes->SetLabel(_("Continue..."));
2021 SetRequestDialogSize();
2022 delete message;
2023 ::wxEndBusyCursor();
2024}
2025
2028wxString GribRequestSetting::BuildXyGribUrl() {
2029 // Server's base address
2030 wxString urlStr =
2031 wxString::Format("http://grbsrv.opengribs.org/getmygribs2.php?");
2032 // Bounding box
2033 urlStr << wxString::Format("la1=%.0f", floor(GetMinLat()));
2034 urlStr << wxString::Format("&la2=%.0f", ceil(GetMaxLat()));
2035 urlStr << wxString::Format("&lo1=%.0f", floor(GetMinLon()));
2036 urlStr << wxString::Format("&lo2=%.0f", ceil(GetMaxLon()));
2037
2038 // Atmospheric Model & resolution reference
2039 urlStr << wxString::Format(
2040 "&model=%s",
2041 xygribAtmModelList[m_selectedAtmModelIndex]
2042 ->reqName[m_xygribPanel->m_resolution_choice->GetSelection()]);
2043 // Interval
2044 urlStr << wxString::Format(
2045 "&intv=%d",
2046 xygribAtmModelList[m_selectedAtmModelIndex]
2047 ->interval[m_xygribPanel->m_interval_choice->GetSelection()]);
2048 // Length in days
2049 urlStr << wxString::Format(
2050 "&days=%d", m_xygribPanel->m_duration_choice->GetSelection() + 1);
2051
2052 // Selected run
2053 // TODO : available runs depend on model
2054 wxString selStr = m_xygribPanel->m_run_choice->GetStringSelection();
2055 if (selStr.IsSameAs("18h", false)) {
2056 urlStr << "&cyc=18";
2057 } else if (selStr.IsSameAs("12h", false)) {
2058 urlStr << "&cyc=12";
2059 } else if (selStr.IsSameAs("6h", false)) {
2060 urlStr << "&cyc=06";
2061 } else if (selStr.IsSameAs("0h", false)) {
2062 urlStr << "&cyc=00";
2063 } else {
2064 urlStr << "&cyc=last";
2065 }
2066
2067 // Atmospheric data fields
2068 urlStr << "&par=";
2069 if (m_xygribPanel->m_wind_cbox->IsEnabled() &&
2070 m_xygribPanel->m_wind_cbox->IsChecked())
2071 urlStr << "W;";
2072 if (m_xygribPanel->m_pressure_cbox->IsEnabled() &&
2073 m_xygribPanel->m_pressure_cbox->IsChecked()) {
2074 if (xygribAtmModelList[m_selectedAtmModelIndex]->altPressure) {
2075 urlStr << "p;";
2076 } else {
2077 urlStr << "P;";
2078 }
2079 }
2080 if (m_xygribPanel->m_precipitation_cbox->IsEnabled() &&
2081 m_xygribPanel->m_precipitation_cbox->IsChecked())
2082 urlStr << "R;";
2083 if (m_xygribPanel->m_cloudcover_cbox->IsEnabled() &&
2084 m_xygribPanel->m_cloudcover_cbox->IsChecked())
2085 urlStr << "C;";
2086 if (m_xygribPanel->m_temperature_cbox->IsEnabled() &&
2087 m_xygribPanel->m_temperature_cbox->IsChecked())
2088 urlStr << "T;";
2089 if (m_xygribPanel->m_cape_cbox->IsEnabled() &&
2090 m_xygribPanel->m_cape_cbox->IsChecked())
2091 urlStr << "c;";
2092 if (m_xygribPanel->m_reflectivity_cbox->IsEnabled() &&
2093 m_xygribPanel->m_reflectivity_cbox->IsChecked())
2094 urlStr << "r;";
2095 if (m_xygribPanel->m_gust_cbox->IsEnabled() &&
2096 m_xygribPanel->m_gust_cbox->IsChecked())
2097 urlStr << "G;";
2098
2099 // Wave model a data fields
2100 if ((m_selectedWaveModelIndex >= 0) &&
2101 (xygribWaveModelList[m_selectedWaveModelIndex] != nullptr)) {
2102 wxString modelStr = wxString::Format(
2103 "&wmdl=%s", xygribWaveModelList[m_selectedWaveModelIndex]->reqName);
2104 wxString wParams = "";
2105 if (m_xygribPanel->m_waveheight_cbox->IsChecked()) {
2106 wParams << "s;";
2107 }
2108 if (m_xygribPanel->m_windwave_cbox->IsChecked()) {
2109 wParams << "h;d;p;";
2110 }
2111 if (wParams.length() > 0) {
2112 urlStr << wxString::Format("%s&wpar=%s", modelStr.c_str(),
2113 wParams.c_str());
2114 } else {
2115 urlStr << "&wmdl=none";
2116 }
2117 } else {
2118 urlStr << "&wmdl=none";
2119 }
2120
2121 return urlStr;
2122}
2123
2126wxString GribRequestSetting::BuildGribFileName() {
2127 wxString selStr = m_xygribPanel->m_resolution_choice->GetStringSelection();
2128 selStr.Replace(".", "P");
2129
2130 wxString fileName;
2131 if (m_selectedWaveModelIndex < 0) {
2132 fileName = wxString::Format(
2133 "XyGrib_%s_%s_%s.grb2", wxDateTime::Now().Format("%F-%H-%M"),
2134 m_xygribPanel->m_atmmodel_choice->GetStringSelection(), selStr);
2135 } else {
2136 fileName = wxString::Format(
2137 "XyGrib_%s_%s_%s_%s.grb2", wxDateTime::Now().Format("%F-%H-%M"),
2138 m_xygribPanel->m_atmmodel_choice->GetStringSelection(), selStr,
2139 m_xygribPanel->m_wavemodel_choice->GetStringSelection());
2140 }
2141
2142 return fileName;
2143}
2144
2145/*/ Handle action of Download/Cancel button.
2146 * This function gathers the current GRIB configuration and handles the
2147 * downloading of the GRIB file. If a transfer is already ongoing, it cancels
2148 * it.
2149 * @param event Event data from the GUI loop
2150 */
2151void GribRequestSetting::OnXyGribDownloadButton(wxCommandEvent &event) {
2152 // Check if we are already downloading a GRIB file
2153 if (m_downloading) {
2154 // Yes : it means that "Download" button has been changed to "Cancel"
2155 // button. So, let's cancel the on-going download
2156 OCPN_cancelDownloadFileBackground(m_download_handle);
2157 m_downloading = false;
2158 m_download_handle = 0;
2159 Disconnect(
2160 wxEVT_DOWNLOAD_EVENT,
2161 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
2162 m_connected = false;
2163 m_xygribPanel->m_progress_gauge->SetValue(0);
2164 m_xygribPanel->m_download_button->SetLabelText(_("Download"));
2165 m_xygribPanel->m_status_text->SetLabelText(_("Download cancelled"));
2166 m_canceled = true;
2167 m_downloadType = GribDownloadType::NONE;
2168 EnableDownloadButtons();
2169 wxTheApp->ProcessPendingEvents();
2170 wxYieldIfNeeded();
2171 return;
2172 }
2173 // No : start a new download
2174 if (m_gribSizeEstimate > XYGRIB_MAX_DOWNLOADABLE_GRIB_SIZE_MB * 1024 * 1024) {
2175 m_xygribPanel->m_status_text->SetLabelText(
2176 wxString::Format(_("Can't download GRIB file bigger than %d MB"),
2177 (int)XYGRIB_MAX_DOWNLOADABLE_GRIB_SIZE_MB));
2178 return;
2179 }
2180
2181 // First we memorize the current configuration. This is the one which will be
2182 // saved in OpenCPN' config file at exit
2183 MemorizeXyGribConfiguration();
2184 m_canceled = false;
2185 m_downloading = true;
2186 m_downloadType = GribDownloadType::XYGRIB;
2187 EnableDownloadButtons();
2188 // Change "Download" button into "Cancel" button
2189 m_xygribPanel->m_download_button->SetLabelText(_("Cancel"));
2190 m_xygribPanel->m_status_text->SetLabelText(
2191 _("Preparing GRIB file on server..."));
2192 wxYieldIfNeeded();
2193
2194 // Build the XyGrib DownloadURL from the current GUI configuration
2195 wxString requestUrl = BuildXyGribUrl();
2196
2197 // First we download the temporary XML file that will tell us where to
2198 // download the file on the server Downloading the XML triggers the
2199 // construction of the GRIB file on the server.
2200 wxString filename = wxString::Format("ocpn_xygrib_%s.xml",
2201 wxDateTime::Now().Format("%F-%H-%M"));
2202 wxString path = m_parent.GetGribDir();
2203 path << wxFileName::GetPathSeparator();
2204 path << filename;
2205 if (!m_connected) {
2206 m_connected = true;
2207 Connect(
2208 wxEVT_DOWNLOAD_EVENT,
2209 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
2210 }
2211 auto res = OCPN_downloadFileBackground(requestUrl.c_str(), path, this,
2212 &m_download_handle);
2213
2214 // Wait for the file to be downloaded. Note that it can take some time because
2215 // server will not send it to us until the GRIB file is ready, or an error
2216 // occured.
2217 while (m_downloading) {
2218 wxTheApp->ProcessPendingEvents();
2219 wxMilliSleep(10);
2220 }
2221
2222 if ((m_canceled) || (!m_bTransferSuccess)) {
2223 // Something went wrong : publish error on GUI
2224 m_xygribPanel->m_status_text->SetLabelText(_("Grib request failed"));
2225 m_xygribPanel->m_download_button->SetLabelText(_("Download"));
2226 m_downloadType = GribDownloadType::NONE;
2227 EnableDownloadButtons();
2228 wxRemoveFile(path);
2229 return;
2230 }
2231
2232 // Read the XML file in a wxString
2233 wxFile xmlFile;
2234 wxString strXml;
2235 bool readOk = xmlFile.Open(path);
2236 if (readOk) {
2237 readOk &= xmlFile.ReadAll(&strXml);
2238 if (readOk) {
2239 xmlFile.Close();
2240 }
2241 }
2242
2243 // Quick and dirty read of the XML file to check status (OK/NOK)
2244 wxString url;
2245 if (readOk && (((int)strXml.find("\"status\":true") == wxNOT_FOUND))) {
2246 // Status is NOK : stop download and report error to GUI
2247 wxString errorStr;
2248 int errPos = strXml.find("\"message\":\"");
2249 int errEnd = strXml.find("\"}");
2250 if ((errPos != wxNOT_FOUND) && (errEnd != wxNOT_FOUND)) {
2251 errPos += 11;
2252 errorStr = strXml.Mid(errPos, errEnd - errPos);
2253 } else {
2254 errorStr = "Unknown server error";
2255 }
2256
2257 m_xygribPanel->m_status_text->SetLabelText(
2258 wxString::Format("%s (%s)", _("Server Error"), errorStr));
2259 m_xygribPanel->m_download_button->SetLabelText(_("Download"));
2260 m_downloadType = GribDownloadType::NONE;
2261 EnableDownloadButtons();
2262 wxRemoveFile(path);
2263 return;
2264 } else {
2265 // Extract GRIB URL from XML
2266 int urlPos = strXml.find("\"url\":\"http:");
2267 int urlEnd = strXml.find(".grb2\"");
2268 if ((urlPos != wxNOT_FOUND) && (urlEnd != wxNOT_FOUND)) {
2269 urlPos += 7;
2270 urlEnd += 5;
2271 url = strXml.Mid(urlPos, urlEnd - urlPos);
2272 url.Replace("\\/", "/");
2273 } else {
2274 readOk = false;
2275 }
2276 }
2277
2278 wxRemoveFile(path);
2279
2280 // Check if there was any error in the previous phase
2281 if (!readOk) {
2282 // Yes : stop download and report error to GUI
2283 m_xygribPanel->m_status_text->SetLabelText(
2284 _("Error parsing XML file from server"));
2285 m_xygribPanel->m_download_button->SetLabelText(_("Download"));
2286 m_downloadType = GribDownloadType::NONE;
2287 EnableDownloadButtons();
2288 return;
2289 }
2290
2291 m_xygribPanel->m_status_text->SetLabelText(_("Downloading GRIB file"));
2292
2293 // Build name of the GRIB file for disk storage
2294 path = m_parent.GetGribDir();
2295 path << wxFileName::GetPathSeparator();
2296 path << BuildGribFileName();
2297
2298 // Start download
2299 m_canceled = false;
2300 m_downloading = true;
2301 if (!m_connected) {
2302 m_connected = true;
2303 Connect(
2304 wxEVT_DOWNLOAD_EVENT,
2305 (wxObjectEventFunction)(wxEventFunction)&GribRequestSetting::onDLEvent);
2306 }
2307 res =
2308 OCPN_downloadFileBackground(url.c_str(), path, this, &m_download_handle);
2309 // Wait for end of download. Note that the previously registered callback
2310 // "onDLEvent" will update the progress indicator on the GUI.
2311 while (m_downloading) {
2312 wxTheApp->ProcessPendingEvents();
2313 wxMilliSleep(10);
2314 }
2315
2316 // Download finished : restore "Download" button
2317 m_xygribPanel->m_download_button->SetLabelText(_("Download"));
2318
2319 if (!m_canceled) {
2320 if (m_bTransferSuccess) {
2321 // Transfer successfull : switch to GRIB display on chart
2322 m_xygribPanel->m_status_text->SetLabelText(_("Download complete"));
2323 wxFileName fn(path);
2324 m_parent.m_grib_dir = fn.GetPath();
2325 m_parent.m_file_names.Clear();
2326 m_parent.m_file_names.Add(path);
2327 m_parent.OpenFile();
2328 if (m_parent.pPlugIn) {
2329 if (m_parent.pPlugIn->m_bZoomToCenterAtInit) m_parent.DoZoomToCenter();
2330 }
2331 m_parent.SetDialogsStyleSizePosition(true);
2332 SaveConfig();
2333 Close();
2334 } else {
2335 // Download failed : report error to GUI
2336 m_xygribPanel->m_status_text->SetLabelText(_("Download failed"));
2337 }
2338 }
2339 if (m_canceled || !m_bTransferSuccess) wxRemoveFile(path);
2340 m_downloadType = GribDownloadType::NONE;
2341 EnableDownloadButtons();
2342}
2343
2348 AtmModelDef_t *selectedAtmModel = nullptr;
2349
2350 // Find the model descriptor associated to the selected atmospheric model
2351 wxString atmModel = m_xygribPanel->m_atmmodel_choice->GetStringSelection();
2352 int modelIndex = 0;
2353 while ((selectedAtmModel = xygribAtmModelList[modelIndex]) != nullptr) {
2354 if (selectedAtmModel->name.IsSameAs(atmModel, true)) {
2355 break;
2356 }
2357 modelIndex++;
2358 }
2359
2360 // If not found, use the first one. This is not supposed to happen.
2361 if (selectedAtmModel == nullptr) {
2362 selectedAtmModel = xygribAtmModelList[0];
2363 modelIndex = 0;
2364 }
2365
2366 // Enable or disable parameters according to the model definition
2367 if (selectedAtmModel->wind)
2368 m_xygribPanel->m_wind_cbox->Enable();
2369 else
2370 m_xygribPanel->m_wind_cbox->Disable();
2371
2372 if (selectedAtmModel->gust)
2373 m_xygribPanel->m_gust_cbox->Enable();
2374 else
2375 m_xygribPanel->m_gust_cbox->Disable();
2376
2377 if (selectedAtmModel->pressure)
2378 m_xygribPanel->m_pressure_cbox->Enable();
2379 else
2380 m_xygribPanel->m_pressure_cbox->Disable();
2381
2382 if (selectedAtmModel->temperature)
2383 m_xygribPanel->m_temperature_cbox->Enable();
2384 else
2385 m_xygribPanel->m_temperature_cbox->Disable();
2386
2387 if (selectedAtmModel->cape)
2388 m_xygribPanel->m_cape_cbox->Enable();
2389 else
2390 m_xygribPanel->m_cape_cbox->Disable();
2391
2392 if (selectedAtmModel->reflectivity)
2393 m_xygribPanel->m_reflectivity_cbox->Enable();
2394 else
2395 m_xygribPanel->m_reflectivity_cbox->Disable();
2396
2397 if (selectedAtmModel->cloudCover)
2398 m_xygribPanel->m_cloudcover_cbox->Enable();
2399 else
2400 m_xygribPanel->m_cloudcover_cbox->Disable();
2401
2402 if (selectedAtmModel->precipitation)
2403 m_xygribPanel->m_precipitation_cbox->Enable();
2404 else
2405 m_xygribPanel->m_precipitation_cbox->Disable();
2406
2407 // Fill the resolution choice selection
2408 m_xygribPanel->m_resolution_choice->Clear();
2409 for (int i = 0; i < selectedAtmModel->nbRes; i++) {
2410 m_xygribPanel->m_resolution_choice->Insert(selectedAtmModel->resolution[i],
2411 i);
2412 }
2413
2414 // Fill the duration choice selection
2415 m_xygribPanel->m_duration_choice->Clear();
2416 for (int i = 0; i < selectedAtmModel->duration; i++) {
2417 m_xygribPanel->m_duration_choice->Insert(wxString::Format("%d", i + 1), i);
2418 }
2419
2420 // Fill the interval choice selection
2421 m_xygribPanel->m_interval_choice->Clear();
2422 for (int i = 0; i < selectedAtmModel->nbInterval; i++) {
2423 m_xygribPanel->m_interval_choice->Insert(
2424 wxString::Format("%dh", selectedAtmModel->interval[i]), i);
2425 }
2426
2427 // Fill the run choice selection
2428 m_xygribPanel->m_run_choice->Clear();
2429 if (selectedAtmModel->runMask == XYGRIB_RUN_0_12) {
2430 m_xygribPanel->m_run_choice->Insert("0h", 0);
2431 m_xygribPanel->m_run_choice->Insert("12h", 1);
2432 m_xygribPanel->m_run_choice->Insert(_("Latest"), 2);
2433 } else {
2434 m_xygribPanel->m_run_choice->Insert("0h", 0);
2435 m_xygribPanel->m_run_choice->Insert("6h", 1);
2436 m_xygribPanel->m_run_choice->Insert("12h", 2);
2437 m_xygribPanel->m_run_choice->Insert("18h", 3);
2438 m_xygribPanel->m_run_choice->Insert(_("Latest"), 4);
2439 }
2440
2441 if (modelIndex == m_parent.xyGribConfig.atmModelIndex) {
2442 ApplyXyGribConfiguration();
2443 } else {
2444 m_selectedAtmModelIndex = modelIndex;
2445 m_xygribPanel->m_resolution_choice->SetSelection(0);
2446 m_xygribPanel->m_duration_choice->SetSelection(
2447 m_xygribPanel->m_duration_choice->GetCount() - 1);
2448 m_xygribPanel->m_interval_choice->SetSelection(0);
2449 m_xygribPanel->m_run_choice->SetSelection(
2450 m_xygribPanel->m_run_choice->GetCount() - 1);
2451 }
2452 MemorizeXyGribConfiguration();
2453}
2454
2459 WaveModelDef_t *selectedModel = nullptr;
2460
2461 // Find the model descriptor associated to the selected wave model
2462 wxString waveModel = m_xygribPanel->m_wavemodel_choice->GetStringSelection();
2463 int modelIndex = 0;
2464 while ((selectedModel = xygribWaveModelList[modelIndex]) != nullptr) {
2465 if (selectedModel->name.IsSameAs(waveModel, true)) {
2466 break;
2467 }
2468 modelIndex++;
2469 }
2470
2471 // Model found in the table ?
2472 if (selectedModel == nullptr) {
2473 // If the model is not found in the table, disable wave model downloading
2474 m_selectedWaveModelIndex = -1;
2475 m_xygribPanel->m_waveheight_cbox->Disable();
2476 m_xygribPanel->m_windwave_cbox->Disable();
2477 MemorizeXyGribConfiguration();
2478 return;
2479 }
2480
2481 m_selectedWaveModelIndex = modelIndex;
2482
2483 // Else configure parameters according to model definition
2484 if (selectedModel->significantHeight) {
2485 m_xygribPanel->m_waveheight_cbox->Enable();
2486 } else {
2487 m_xygribPanel->m_waveheight_cbox->Disable();
2488 }
2489
2490 if (selectedModel->windWaves) {
2491 m_xygribPanel->m_windwave_cbox->Enable();
2492 } else {
2493 m_xygribPanel->m_windwave_cbox->Disable();
2494 }
2495 MemorizeXyGribConfiguration();
2496}
2497
2501void GribRequestSetting::OnXyGribConfigChange(wxCommandEvent &event) {
2502 MemorizeXyGribConfiguration();
2503}
2504
2506void GribRequestSetting::InitializeXygribDialog() {
2507 AtmModelDef_t *selectedAtmModel = nullptr;
2508 WaveModelDef_t *selectedWaveModel = nullptr;
2509
2510 // Fill selection of atmospheric models with the ones found in the model table
2511 m_xygribPanel->m_atmmodel_choice->Clear();
2512 int modelIndex = 0;
2513 while ((selectedAtmModel = xygribAtmModelList[modelIndex]) != nullptr) {
2514 m_xygribPanel->m_atmmodel_choice->Insert(selectedAtmModel->name,
2515 modelIndex);
2516 modelIndex++;
2517 }
2518 // Select the initial value from configuration
2519 m_selectedAtmModelIndex = m_parent.xyGribConfig.atmModelIndex;
2520 selectedAtmModel = xygribAtmModelList[m_selectedAtmModelIndex];
2521
2522 // Fill selection of wave models with the ones found in the table
2523 m_xygribPanel->m_wavemodel_choice->Clear();
2524 modelIndex = 0;
2525 while ((selectedWaveModel = xygribWaveModelList[modelIndex]) != nullptr) {
2526 m_xygribPanel->m_wavemodel_choice->Insert(selectedWaveModel->name,
2527 modelIndex);
2528 modelIndex++;
2529 }
2530 // Add the "None" selection
2531 m_xygribPanel->m_wavemodel_choice->Insert("None", modelIndex);
2532 m_selectedWaveModelIndex = m_parent.xyGribConfig.waveModelIndex;
2533 selectedWaveModel = xygribWaveModelList[m_selectedWaveModelIndex];
2534 if (selectedWaveModel == nullptr) {
2535 m_selectedWaveModelIndex = -1;
2536 }
2537
2538 // Fill the resolution choice selection
2539 m_xygribPanel->m_resolution_choice->Clear();
2540 for (int i = 0; i < selectedAtmModel->nbRes; i++) {
2541 m_xygribPanel->m_resolution_choice->Insert(selectedAtmModel->resolution[i],
2542 i);
2543 }
2544
2545 // Fill the duration choice selection
2546 m_xygribPanel->m_duration_choice->Clear();
2547 for (int i = 0; i < selectedAtmModel->duration; i++) {
2548 m_xygribPanel->m_duration_choice->Insert(wxString::Format("%d", i + 1), i);
2549 }
2550
2551 // Fill the interval choice selection
2552 m_xygribPanel->m_interval_choice->Clear();
2553 for (int i = 0; i < selectedAtmModel->nbInterval; i++) {
2554 m_xygribPanel->m_interval_choice->Insert(
2555 wxString::Format("%dh", selectedAtmModel->interval[i]), i);
2556 }
2557
2558 // Fill the run choice selection
2559 m_xygribPanel->m_run_choice->Clear();
2560 if (selectedAtmModel->runMask == XYGRIB_RUN_0_12) {
2561 m_xygribPanel->m_run_choice->Insert("0h", 0);
2562 m_xygribPanel->m_run_choice->Insert("12h", 1);
2563 m_xygribPanel->m_run_choice->Insert(_("Latest"), 2);
2564 } else {
2565 m_xygribPanel->m_run_choice->Insert("0h", 0);
2566 m_xygribPanel->m_run_choice->Insert("6h", 1);
2567 m_xygribPanel->m_run_choice->Insert("12h", 2);
2568 m_xygribPanel->m_run_choice->Insert("18h", 3);
2569 m_xygribPanel->m_run_choice->Insert(_("Latest"), 4);
2570 }
2571
2572 if (selectedAtmModel->wind)
2573 m_xygribPanel->m_wind_cbox->Enable();
2574 else
2575 m_xygribPanel->m_wind_cbox->Disable();
2576
2577 if (selectedAtmModel->gust)
2578 m_xygribPanel->m_gust_cbox->Enable();
2579 else
2580 m_xygribPanel->m_gust_cbox->Disable();
2581
2582 if (selectedAtmModel->pressure)
2583 m_xygribPanel->m_pressure_cbox->Enable();
2584 else
2585 m_xygribPanel->m_pressure_cbox->Disable();
2586
2587 if (selectedAtmModel->temperature)
2588 m_xygribPanel->m_temperature_cbox->Enable();
2589 else
2590 m_xygribPanel->m_temperature_cbox->Disable();
2591
2592 if (selectedAtmModel->cape)
2593 m_xygribPanel->m_cape_cbox->Enable();
2594 else
2595 m_xygribPanel->m_cape_cbox->Disable();
2596
2597 if (selectedAtmModel->reflectivity)
2598 m_xygribPanel->m_reflectivity_cbox->Enable();
2599 else
2600 m_xygribPanel->m_reflectivity_cbox->Disable();
2601
2602 if (selectedAtmModel->cloudCover)
2603 m_xygribPanel->m_cloudcover_cbox->Enable();
2604 else
2605 m_xygribPanel->m_cloudcover_cbox->Disable();
2606
2607 if (selectedAtmModel->precipitation)
2608 m_xygribPanel->m_precipitation_cbox->Enable();
2609 else
2610 m_xygribPanel->m_precipitation_cbox->Disable();
2611
2612 if ((selectedWaveModel != nullptr) && (selectedWaveModel->significantHeight))
2613 m_xygribPanel->m_waveheight_cbox->Enable();
2614 else
2615 m_xygribPanel->m_waveheight_cbox->Disable();
2616
2617 if ((selectedWaveModel != nullptr) && (selectedWaveModel->windWaves))
2618 m_xygribPanel->m_windwave_cbox->Enable();
2619 else
2620 m_xygribPanel->m_windwave_cbox->Disable();
2621
2622 ApplyXyGribConfiguration();
2623}
2624
2629void GribRequestSetting::ApplyXyGribConfiguration() {
2630 m_xygribPanel->m_atmmodel_choice->SetSelection(
2631 m_parent.xyGribConfig.atmModelIndex);
2632 m_xygribPanel->m_wavemodel_choice->SetSelection(
2633 m_parent.xyGribConfig.waveModelIndex);
2634
2635 m_xygribPanel->m_wind_cbox->SetValue(m_parent.xyGribConfig.wind);
2636 m_xygribPanel->m_gust_cbox->SetValue(m_parent.xyGribConfig.gust);
2637 m_xygribPanel->m_pressure_cbox->SetValue(m_parent.xyGribConfig.pressure);
2638 m_xygribPanel->m_temperature_cbox->SetValue(
2639 m_parent.xyGribConfig.temperature);
2640 m_xygribPanel->m_cape_cbox->SetValue(m_parent.xyGribConfig.cape);
2641 m_xygribPanel->m_reflectivity_cbox->SetValue(
2642 m_parent.xyGribConfig.reflectivity);
2643 m_xygribPanel->m_cloudcover_cbox->SetValue(m_parent.xyGribConfig.cloudCover);
2644 m_xygribPanel->m_precipitation_cbox->SetValue(
2645 m_parent.xyGribConfig.precipitation);
2646 m_xygribPanel->m_waveheight_cbox->SetValue(m_parent.xyGribConfig.waveHeight);
2647 m_xygribPanel->m_windwave_cbox->SetValue(m_parent.xyGribConfig.windWaves);
2648 m_xygribPanel->m_resolution_choice->SetSelection(
2649 m_parent.xyGribConfig.resolutionIndex);
2650 m_xygribPanel->m_duration_choice->SetSelection(
2651 m_parent.xyGribConfig.durationIndex);
2652 m_xygribPanel->m_interval_choice->SetSelection(
2653 m_parent.xyGribConfig.intervalIndex);
2654 m_xygribPanel->m_run_choice->SetSelection(m_parent.xyGribConfig.runIndex);
2655
2656 UpdateGribSizeEstimate();
2657}
2658
2663void GribRequestSetting::MemorizeXyGribConfiguration() {
2664 m_parent.xyGribConfig.atmModelIndex =
2665 m_xygribPanel->m_atmmodel_choice->GetSelection();
2666 m_parent.xyGribConfig.waveModelIndex =
2667 m_xygribPanel->m_wavemodel_choice->GetSelection();
2668
2669 m_parent.xyGribConfig.wind = m_xygribPanel->m_wind_cbox->IsChecked();
2670 m_parent.xyGribConfig.gust = m_xygribPanel->m_gust_cbox->IsChecked();
2671 m_parent.xyGribConfig.pressure = m_xygribPanel->m_pressure_cbox->IsChecked();
2672 m_parent.xyGribConfig.temperature =
2673 m_xygribPanel->m_temperature_cbox->IsChecked();
2674 m_parent.xyGribConfig.cape = m_xygribPanel->m_cape_cbox->IsChecked();
2675 m_parent.xyGribConfig.reflectivity =
2676 m_xygribPanel->m_reflectivity_cbox->IsChecked();
2677 m_parent.xyGribConfig.cloudCover =
2678 m_xygribPanel->m_cloudcover_cbox->IsChecked();
2679 m_parent.xyGribConfig.precipitation =
2680 m_xygribPanel->m_precipitation_cbox->IsChecked();
2681 m_parent.xyGribConfig.waveHeight =
2682 m_xygribPanel->m_waveheight_cbox->IsChecked();
2683 m_parent.xyGribConfig.windWaves = m_xygribPanel->m_windwave_cbox->IsChecked();
2684
2685 m_parent.xyGribConfig.resolutionIndex =
2686 m_xygribPanel->m_resolution_choice->GetSelection();
2687 m_parent.xyGribConfig.durationIndex =
2688 m_xygribPanel->m_duration_choice->GetSelection();
2689 m_parent.xyGribConfig.intervalIndex =
2690 m_xygribPanel->m_interval_choice->GetSelection();
2691 m_parent.xyGribConfig.runIndex = m_xygribPanel->m_run_choice->GetSelection();
2692
2693 UpdateGribSizeEstimate();
2694}
2695
2699void GribRequestSetting::UpdateGribSizeEstimate() {
2700 double resolution;
2701 long days;
2702 long interval;
2703
2704 if (!m_xygribPanel->m_resolution_choice->GetStringSelection().ToCDouble(
2705 &resolution)) {
2706 m_xygribPanel->m_sizeestimate_text->SetLabel("Unknown");
2707 return;
2708 }
2709 if (!m_xygribPanel->m_duration_choice->GetStringSelection().ToCLong(&days)) {
2710 m_xygribPanel->m_sizeestimate_text->SetLabel("Unknown");
2711 return;
2712 }
2713 wxString intvStr = m_xygribPanel->m_interval_choice->GetStringSelection();
2714 intvStr.Replace("h", "");
2715 if (!intvStr.ToCLong(&interval)) {
2716 m_xygribPanel->m_sizeestimate_text->SetLabel("Unknown");
2717 return;
2718 }
2719
2720 if (m_VpFocus == nullptr) {
2721 m_xygribPanel->m_sizeestimate_text->SetLabel("Unknown");
2722 return;
2723 }
2724
2725 double xmax = GetMaxLon();
2726 double xmin = GetMinLon();
2727 double ymax = GetMaxLat();
2728 double ymin = GetMinLat();
2729
2730 int npts = (int)(ceil(fabs(xmax - xmin) / resolution) *
2731 ceil(fabs(ymax - ymin) / resolution));
2732
2733 // Number of GribRecords
2734 int nbrec = (int)days * 24 / interval + 1;
2735
2736 int nbPress = (m_xygribPanel->m_pressure_cbox->IsChecked() &&
2737 m_xygribPanel->m_pressure_cbox->IsEnabled())
2738 ? nbrec
2739 : 0;
2740 int nbWind = (m_xygribPanel->m_wind_cbox->IsChecked() &&
2741 m_xygribPanel->m_wind_cbox->IsEnabled())
2742 ? 2 * nbrec
2743 : 0;
2744 int nbRain = (m_xygribPanel->m_precipitation_cbox->IsChecked() &&
2745 m_xygribPanel->m_precipitation_cbox->IsEnabled())
2746 ? nbrec - 1
2747 : 0;
2748 int nbCloud = (m_xygribPanel->m_cloudcover_cbox->IsChecked() &&
2749 m_xygribPanel->m_cloudcover_cbox->IsEnabled())
2750 ? nbrec - 1
2751 : 0;
2752 int nbTemp = (m_xygribPanel->m_temperature_cbox->IsChecked() &&
2753 m_xygribPanel->m_temperature_cbox->IsEnabled())
2754 ? nbrec
2755 : 0;
2756
2757 int nbCAPEsfc = (m_xygribPanel->m_cape_cbox->IsChecked() &&
2758 m_xygribPanel->m_cape_cbox->IsEnabled())
2759 ? nbrec
2760 : 0;
2761 int nbReflectivity = (m_xygribPanel->m_reflectivity_cbox->IsChecked() &&
2762 m_xygribPanel->m_reflectivity_cbox->IsEnabled())
2763 ? nbrec
2764 : 0;
2765 int nbGUSTsfc = (m_xygribPanel->m_gust_cbox->IsChecked() &&
2766 m_xygribPanel->m_gust_cbox->IsEnabled())
2767 ? nbrec
2768 : 0;
2769
2770 int head = 179;
2771 int estimate = 0;
2772 int nbits; // this can vary when c3 packing is used. Average numbers are used
2773 // here
2774
2775 nbits = 12;
2776 estimate += nbWind * (head + (nbits * npts) / 8 + 2);
2777
2778 nbits = 9;
2779 estimate += nbTemp * (head + (nbits * npts) / 8 + 2);
2780
2781 // estime += nbTempMin*(head+(nbits*npts)/8+2 );
2782 // estime += nbTempMax*(head+(nbits*npts)/8+2 );
2783
2784 nbits = 9;
2785 estimate += nbRain * (head + 24 + (nbits * npts) / 8 + 2);
2786
2787 nbits = 14;
2788 estimate += nbPress * (head + (nbits * npts) / 8 + 2);
2789
2790 nbits = 7;
2791 estimate += nbCloud * (head + 24 + (nbits * npts) / 8 + 2);
2792
2793 nbits = 11;
2794 estimate += nbReflectivity * (head + (nbits * npts) / 8 + 2);
2795
2796 nbits = 12;
2797 estimate += nbCAPEsfc * (head + (nbits * npts) / 8 + 2);
2798
2799 nbits = 9;
2800 estimate += nbGUSTsfc * (head + (nbits * npts) / 8 + 2);
2801 // estime += nbSUNSDsfc*(head+(nbits*npts)/8+2 );
2802
2803 // keep a record of the atmosphere estimate. if 0 nothing was selected
2804 int atmEstimate = estimate;
2805
2806 // and now the wave estimate
2807 // recalculate number of points based on which model is used
2808 if (m_xygribPanel->m_wavemodel_choice->GetStringSelection().IsSameAs(
2809 "WW3")) // 0.5 deg
2810 {
2811 npts = (int)(ceil(fabs(xmax - xmin) / 0.5) * ceil(fabs(ymax - ymin) / 0.5));
2812 nbrec = (int)fmin(8, days) * 24 / interval + 1;
2813 } else if (m_xygribPanel->m_wavemodel_choice->GetStringSelection().IsSameAs(
2814 "GWAM")) // 0.25 deg
2815 {
2816 npts =
2817 (int)(ceil(fabs(xmax - xmin) / 0.25) * ceil(fabs(ymax - ymin) / 0.25));
2818 nbrec = (int)fmin(8, days) * 24 / interval + 1;
2819 } else if (m_xygribPanel->m_wavemodel_choice->GetStringSelection().IsSameAs(
2820 "EWAM")) // 0.1 x 0.05 deg
2821 {
2822 npts =
2823 (int)(ceil(fabs(xmax - xmin) / 0.05) * ceil(fabs(ymax - ymin) / 0.1));
2824 nbrec = (int)fmin(4, days) * 24 / interval + 1;
2825 } else {
2826 npts = 0;
2827 nbrec = 0;
2828 }
2829
2830 if (m_xygribPanel->m_waveheight_cbox->IsChecked()) {
2831 nbits = 9;
2832 estimate += nbrec * (head + (nbits * npts) / 8 + 2);
2833 }
2834
2835 if (m_xygribPanel->m_windwave_cbox->IsChecked()) {
2836 nbits = 15;
2837 estimate += nbrec * (head + (nbits * npts) / 8 + 2);
2838 nbits = 8;
2839 estimate += nbrec * (head + (nbits * npts) / 8 + 2);
2840 nbits = 10;
2841 estimate += nbrec * (head + (nbits * npts) / 8 + 2);
2842 }
2843
2844 int wavEstimate = estimate - atmEstimate;
2845 // reduce wave estimate due to land overlapping by average of 40%
2846 wavEstimate = wavEstimate * 0.6;
2847 // now adjusted estimate is
2848 estimate = atmEstimate + wavEstimate;
2849
2850 // adjust for packing ~ 65%
2851 estimate = estimate * 0.65;
2852
2853 wxString warningStr = "";
2854 if (estimate / (1024 * 1024) > XYGRIB_MAX_DOWNLOADABLE_GRIB_SIZE_MB) {
2855 warningStr = wxString::Format("(Warning GRIB exceeds %d MB limit)",
2856 XYGRIB_MAX_DOWNLOADABLE_GRIB_SIZE_MB);
2857 }
2858
2859 m_xygribPanel->m_sizeestimate_text->SetLabel(
2860 wxString::Format("%d kB %s", estimate / 1024, warningStr));
2861
2862 m_gribSizeEstimate = estimate;
2863}
2864
2866 if (m_rbManualSelect && m_rbManualSelect->GetValue()) {
2867 return m_spMinLat->GetValue();
2868 }
2869 return m_VpFocus->lat_min;
2870}
2871
2873 if (m_rbManualSelect && m_rbManualSelect->GetValue()) {
2874 return m_spMaxLat->GetValue();
2875 }
2876 return m_VpFocus->lat_max;
2877}
2878
2880 if (m_rbManualSelect && m_rbManualSelect->GetValue()) {
2881 return m_spMinLon->GetValue();
2882 }
2883 return m_VpFocus->lon_min;
2884}
2885
2887 if (m_rbManualSelect && m_rbManualSelect->GetValue()) {
2888 return m_spMaxLon->GetValue();
2889 }
2890 return m_VpFocus->lon_max;
2891}
void SetRequestButtonBitmap(int type)
Set the icon and tooltip for the download request button.
GribPi * pPlugIn
Plugin instance that owns this control bar.
wxString m_grib_dir
Directory containing GRIB files.
wxArrayString m_file_names
List of GRIB filenames being displayed.
Class GribRequestSettingBase.
wxButton * m_rButtonCancel
Button to Cancel a request to download, close the dialog without saving the configuration.
wxSpinCtrl * m_spMaxLon
A spinner for the max longitude of the bounding box for downloads.
wxSpinCtrl * m_spMaxLat
A spinner for the max latitude of the bounding box for downloads.
wxRadioButton * m_rbManualSelect
Radio button selected to indicate the download area is based on the area selected by the user.
wxRadioButton * m_rbCurrentView
Radio button selected to indicate the download area is based on the visible area of the chart in the ...
wxSpinCtrl * m_spMinLat
A spinner for the min latitude of the bounding box for downloads.
wxButton * m_rButtonApply
Button to Save the "download request" configuration.
wxButton * m_rButtonYes
Button to Send a download request through e-mail.
wxSpinCtrl * m_spMinLon
A spinner for the min longitude of the bounding box for downloads.
Manages GRIB file request configuration and downloads.
double m_StartLat
The latitude at the starting point of the bounding box.
void OnXyGribWaveModelChoice(wxCommandEvent &event) override
Handle action of changing wave model selection.
void OnSendMaiL(wxCommandEvent &event) override
Callback invoked when the user clicks the "Send" button in the "e-mail" tab.
double GetMinLat() const
Get the minimum latitude of the bounding box for the download request.
double GetMaxLat() const
Get the maximum latitude of the bounding box for the download request.
double m_Lat
The latitude at the mouse cursor while drawing a bounding box.
void OnVpUnderMouseChange(PlugIn_ViewPort *vp)
Callback invoked when the view port under mouse has changed.
bool RenderGlZoneOverlay()
Renders the GRIB area selection overlay using OpenGL.
bool RenderZoneOverlay(wxDC &dc)
Renders the GRIB area selection overlay using standard device context.
double m_Lon
The longitude at the mouse cursor while drawing a bounding box.
void OnXyGribAtmModelChoice(wxCommandEvent &event) override
Handle action of changing atmospheric model selection.
wxPoint m_StartPoint
Starting point of the bounding box in physical pixels.
bool DoRenderZoneOverlay()
Draws the GRIB area selection overlay on the chart.
bool MouseEventHook(wxMouseEvent &event)
Intercepts mouse events to handle GRIB area selection.
void OnVpWithFocusChange(PlugIn_ViewPort *vp)
Callback invoked when the focused view port has changed, such as in multi-chart mode when user switch...
PlugIn_ViewPort * m_VpMouse
The viewport under the mouse.
ZoneSelectionRenderState m_RenderSelectionZoneState
Current state of the bounding box overlay rendering.
void OnXyGribConfigChange(wxCommandEvent &event) override
Handle action of changing a GRIB configuration parameter.
double GetMaxLon() const
Get the maximum longitude of the bounding box for the download request.
PlugIn_ViewPort * m_VpFocus
The viewport currently in focus.
double m_StartLon
The longitude at the starting point of the bounding box.
double GetMinLon() const
Get the minimum longitude of the bounding box for the download request.
void OnOK(wxCommandEvent &event) override
Callback invoked when the user clicks the "OK" button in the "download" dialog.
Contains view parameters and status information for a chart display viewport.
double lon_max
Maximum longitude of the viewport.
double lat_max
Maximum latitude of the viewport.
double lon_min
Minimum longitude of the viewport.
double lat_min
Minimum latitude of the viewport.
The JSON parser.
Definition jsonreader.h:45
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:79
bool IsArray() const
Return TRUE if the type of the value stored is an array type.
Definition jsonval.cpp:749
int Size() const
Return the size of the array or map stored in this value.
Definition jsonval.cpp:1333
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1299
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:875
int AsInt() const
Return the stored value as an integer.
Definition jsonval.cpp:792
Email Request System for GRIB Data.
GRIB Data Visualization and Rendering Factory.
GRIB Weather Data Plugin for OpenCPN.
int m_SavedZoneSelMode
Persisted version of the GRIB area selection mode.
int m_ZoneSelMode
Tracks the current state of GRIB area selection for zone coordinates.
GRIB Weather Data Request and Download Management.
@ WORLD
Global forecast downloads (e.g., GFS)
@ XYGRIB
Downloads from XyGrib service.
@ NONE
No download source selected.
@ LOCAL_CATALOG
Downloads from configured catalogs.
@ LOCAL
Downloads from local sources.
@ AUTO_SELECTION
Area automatically set from current viewport bounds.
Definition grib_ui_dlg.h:87
@ DRAW_SELECTION
Manual mode has been selected.
@ START_SELECTION
User has clicked Shift + Left click and is drawing the bounding box by dragging the mouse.
Definition grib_ui_dlg.h:93
@ COMPLETE_SELECTION
Selection box completed in manual mode, coordinates have been captured after the user has released th...
@ SAVED_SELECTION
Area loaded from previously saved coordinates.
Definition grib_ui_dlg.h:88
#define SAVEDZONE
Save zone.
#define MANSELECT
Manual selection mode.
#define AUTOSELECT
Automatic selection mode.
@ OCPN_DL_EVENT_TYPE_PROGRESS
Download progress update.
@ OCPN_DL_EVENT_TYPE_END
Download has completed.
@ OCPN_DL_NO_ERROR
Download completed successfully.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
wxFileConfig * GetOCPNConfigObject()
Gets OpenCPN's configuration object.
void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, double lat, double lon)
Converts lat/lon to canvas physical pixel coordinates.
void DimeWindow(wxWindow *win)
Applies system color scheme to window.
int GetCanvasIndexUnderMouse()
Gets index of chart canvas under mouse cursor.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
void RequestRefresh(wxWindow *win)
Requests window refresh.
void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, double *plat, double *plon)
Converts canvas physical pixel coordinates to lat/lon.
OpenGL Platform Abstraction Layer.
void OCPN_cancelDownloadFileBackground(long handle)
Cancels a background download.
int OCPNMessageBox_PlugIn(wxWindow *parent, const wxString &message, const wxString &caption, int style, int x, int y)
Shows a message box dialog.
_OCPN_DLStatus OCPN_downloadFileBackground(const wxString &url, const wxString &outputFile, wxEvtHandler *handler, long *handle)
Asynchronously downloads a file in the background.
XyGrib Model Configuration and Definitions.