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