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