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