31#include <wx/glcanvas.h>
32#include <wx/graphics.h>
33#include <wx/progdlg.h>
35#include "pi_shaders.h"
46extern double g_ContentScaleFactor;
47float g_piGLMinSymbolLineWidth = 0.9;
49enum GRIB_OVERLAP { _GIN, _GON, _GOUT };
56 double lat_max,
double lon_min,
double lon_max,
58 if (((vp->lon_min - Marge) > (lon_max + Marge)) ||
59 ((vp->lon_max + Marge) < (lon_min - Marge)) ||
60 ((vp->lat_max + Marge) < (lat_min - Marge)) ||
61 ((vp->lat_min - Marge) > (lat_max + Marge)))
65 if ((vp->lon_min <= lon_min) && (vp->lon_max >= lon_max) &&
66 (vp->lat_max >= lat_max) && (vp->lat_min <= lat_min))
75 double m_miny = vp->lat_min;
76 double m_maxy = vp->lat_max;
77 if (y < m_miny || y > m_maxy)
return FALSE;
79 double m_minx = vp->lon_min;
80 double m_maxx = vp->lon_max;
82 if (x < m_maxx - 360.)
84 else if (x > m_minx + 360.)
87 if (x < m_minx || x > m_maxx)
return FALSE;
93static wxString MToString(
int DataCenterModel )
95 switch( DataCenterModel ) {
96 case NOAA_GFS:
return _T(
"NOAA_GFS");
97 case NOAA_NCEP_WW3:
return _T(
"NOAA_NCEP_WW3");
98 case NOAA_NCEP_SST:
return _T(
"NOAA_NCEP_SST");
99 case NOAA_RTOFS:
return _T(
"NOAA_RTOFS");
100 case FNMOC_WW3_GLB:
return _T(
"FNMOC_WW3");
101 case FNMOC_WW3_MED:
return _T(
"FNMOC_WW3");
102 case NORWAY_METNO:
return _T(
"NORWAY_METNO");
103 default :
return _T(
"OTHER_DATA_CENTER");
109static GLuint texture_format = 0;
113static GLboolean QueryExtension(
const char *extName )
125 extNameLen = strlen( extName );
127 p = (
char *) glGetString( GL_EXTENSIONS );
132 end = p + strlen( p );
135 int n = strcspn( p,
" " );
136 if( ( extNameLen == n ) && ( strncmp( extName, p, n ) == 0 ) ) {
144#if defined(__WXMSW__)
145#define systemGetProcAddress(ADDR) wglGetProcAddress(ADDR)
146#elif defined(__WXOSX__)
148#define systemGetProcAddress(ADDR) dlsym(RTLD_DEFAULT, ADDR)
150#define systemGetProcAddress(ADDR) glXGetProcAddress((const GLubyte *)ADDR)
155void LineBuffer::pushLine(
float x0,
float y0,
float x1,
float y1) {
156 buffer.push_back(x0);
157 buffer.push_back(y0);
158 buffer.push_back(x1);
159 buffer.push_back(y1);
162void LineBuffer::pushPetiteBarbule(
int b,
int l) {
163 int tilt = (l * 100) / 250;
164 pushLine(b, 0, b + tilt, -l);
167void LineBuffer::pushGrandeBarbule(
int b,
int l) {
168 int tilt = (l * 100) / 250;
169 pushLine(b, 0, b + tilt, -l);
172void LineBuffer::pushTriangle(
int b,
int l) {
173 int dim = (l * 100) / 250;
174 pushLine(b, 0, b + dim, -l);
175 pushLine(b + (dim * 2), 0, b + dim, -l);
178void LineBuffer::Finalize() {
179 count = buffer.size() / 4;
180 lines =
new float[buffer.size()];
182 for (std::list<float>::iterator it = buffer.begin(); it != buffer.end(); it++)
186int adjustSpacing(
int dialogSetSpacing) {
187#ifdef __OCPN__ANDROID__
191 wxSize sz = GetOCPNCanvasWindow()->GetClientSize();
192 int sizeMin = wxMin(sz.x, sz.y);
193 int space = ((double)dialogSetSpacing) * (sizeMin / 2) / 100;
198 return dialogSetSpacing;
206 : m_dlg(dlg), m_Settings(dlg.m_OverlaySettings) {
207 if (wxGetDisplaySize().x > 0) {
213 m_pixelMM = (double)PlugInGetDisplaySizeMM() /
214 wxMax(wxGetDisplaySize().x, wxGetDisplaySize().y);
216 m_pixelMM = wxMax(.02, m_pixelMM);
222 m_pGribTimelineRecordSet =
nullptr;
223 m_last_vp_scale = 0.;
226#if wxUSE_GRAPHICS_CONTEXT
229 m_Font_Message =
nullptr;
232 for (
int i = 0; i < GribOverlaySettings::SETTINGS_COUNT; i++)
233 m_pOverlay[i] =
nullptr;
235 m_ParticleMap =
nullptr;
236 m_tParticleTimer.Connect(
237 wxEVT_TIMER, wxTimerEventHandler(GRIBOverlayFactory::OnParticleTimer),
239 m_bUpdateParticles =
false;
243 if (m_pixelMM < 0.2) {
244 windArrowSize = 5.0 / m_pixelMM;
245 windArrowSize = wxMin(
246 windArrowSize, wxMax(wxGetDisplaySize().x, wxGetDisplaySize().y) / 20);
251 double s = 2 * M_PI / 10.;
252 for (
double a = 0; a < 2 * M_PI; a += s)
253 m_WindArrowCache[0].pushLine(r * sin(a), r * cos(a), r * sin(a + s),
256 int dec = -windArrowSize / 2;
257 int pointerLength = windArrowSize / 3;
260 for (i = 1; i < 14; i++) {
263 arrow.pushLine(dec, 0, dec + windArrowSize, 0);
264 arrow.pushLine(dec, 0, dec + pointerLength, pointerLength / 2);
265 arrow.pushLine(dec, 0, dec + pointerLength,
266 -(pointerLength / 2));
269 int featherPosition = windArrowSize / 6;
272 dec + windArrowSize - featherPosition;
273 int b2 = dec + windArrowSize;
275 int lpetite = windArrowSize / 5;
276 int lgrande = lpetite * 2;
279 m_WindArrowCache[1].pushPetiteBarbule(b1, lpetite);
281 m_WindArrowCache[2].pushGrandeBarbule(b2, lgrande);
283 m_WindArrowCache[3].pushGrandeBarbule(b2, lgrande);
284 m_WindArrowCache[3].pushPetiteBarbule(b2 - featherPosition, lpetite);
286 m_WindArrowCache[4].pushGrandeBarbule(b2, lgrande);
287 m_WindArrowCache[4].pushGrandeBarbule(b2 - featherPosition, lgrande);
289 m_WindArrowCache[5].pushGrandeBarbule(b2, lgrande);
290 m_WindArrowCache[5].pushGrandeBarbule(b2 - featherPosition, lgrande);
291 m_WindArrowCache[5].pushPetiteBarbule(b2 - featherPosition * 2, lpetite);
293 m_WindArrowCache[6].pushGrandeBarbule(b2, lgrande);
294 m_WindArrowCache[6].pushGrandeBarbule(b2 - featherPosition, lgrande);
295 m_WindArrowCache[6].pushGrandeBarbule(b2 - featherPosition * 2, lgrande);
297 m_WindArrowCache[7].pushGrandeBarbule(b2, lgrande);
298 m_WindArrowCache[7].pushGrandeBarbule(b2 - featherPosition, lgrande);
299 m_WindArrowCache[7].pushGrandeBarbule(b2 - featherPosition * 2, lgrande);
300 m_WindArrowCache[7].pushPetiteBarbule(b2 - featherPosition * 3, lpetite);
302 m_WindArrowCache[8].pushGrandeBarbule(b2, lgrande);
303 m_WindArrowCache[8].pushGrandeBarbule(b2 - featherPosition, lgrande);
304 m_WindArrowCache[8].pushGrandeBarbule(b2 - featherPosition * 2, lgrande);
305 m_WindArrowCache[8].pushGrandeBarbule(b2 - featherPosition * 3, lgrande);
307 m_WindArrowCache[9].pushTriangle(b1 - featherPosition, lgrande);
309 m_WindArrowCache[10].pushTriangle(b1 - featherPosition, lgrande);
310 m_WindArrowCache[10].pushGrandeBarbule(b1 - featherPosition * 2, lgrande);
312 m_WindArrowCache[11].pushTriangle(b1 - featherPosition, lgrande);
313 m_WindArrowCache[11].pushGrandeBarbule(b1 - featherPosition * 2, lgrande);
314 m_WindArrowCache[11].pushGrandeBarbule(b1 - featherPosition * 3, lgrande);
316 m_WindArrowCache[12].pushTriangle(b1 - featherPosition, lgrande);
317 m_WindArrowCache[12].pushGrandeBarbule(b1 - featherPosition * 2, lgrande);
318 m_WindArrowCache[12].pushGrandeBarbule(b1 - featherPosition * 3, lgrande);
319 m_WindArrowCache[12].pushGrandeBarbule(b1 - featherPosition * 4, lgrande);
321 m_WindArrowCache[13].pushTriangle(b1 - featherPosition, lgrande);
322 m_WindArrowCache[13].pushTriangle(b1 - featherPosition * 3, lgrande);
324 for (i = 0; i < 14; i++) m_WindArrowCache[i].Finalize();
327 for (
int i = 0; i < 2; i++) {
333 if (m_pixelMM > 0.2) {
334 arrowSize = 5.0 / m_pixelMM;
336 arrowSize, wxMax(wxGetDisplaySize().x, wxGetDisplaySize().y) / 20);
337 dec1 = arrowSize / 6;
338 dec2 = arrowSize / 8;
344 dec = -arrowSize / 2;
346 m_SingleArrow[i].pushLine(dec, 0, dec + arrowSize, 0);
347 m_SingleArrow[i].pushLine(dec - 2, 0, dec + dec1, dec1 + 1);
348 m_SingleArrow[i].pushLine(dec - 2, 0, dec + dec1, -(dec1 + 1));
349 m_SingleArrow[i].Finalize();
351 m_DoubleArrow[i].pushLine(dec, -dec2, dec + arrowSize, -dec2);
352 m_DoubleArrow[i].pushLine(dec, dec2, dec + arrowSize, +dec2);
354 m_DoubleArrow[i].pushLine(dec - 2, 0, dec + dec1, dec1 + 1);
355 m_DoubleArrow[i].pushLine(dec - 2, 0, dec + dec1, -(dec1 + 1));
356 m_DoubleArrow[i].Finalize();
360GRIBOverlayFactory::~GRIBOverlayFactory() {
365 if (m_oDC)
delete m_oDC;
366 if (m_Font_Message)
delete m_Font_Message;
369void GRIBOverlayFactory::Reset() {
370 m_pGribTimelineRecordSet =
nullptr;
375void GRIBOverlayFactory::SetMessageFont() {
382 (fo.GetPointSize() * g_ContentScaleFactor / OCPN_GetWinDIPScaleFactor()));
384 if (m_Font_Message)
delete m_Font_Message;
385 m_Font_Message =
new wxFont(fo);
388void GRIBOverlayFactory::SetGribTimelineRecordSet(
391 m_pGribTimelineRecordSet = pGribTimelineRecordSet;
394void GRIBOverlayFactory::ClearCachedData(
void) {
396 for (
int i = 0; i < GribOverlaySettings::SETTINGS_COUNT; i++) {
397 delete m_pOverlay[i];
398 m_pOverlay[i] =
nullptr;
402#ifdef __OCPN__ANDROID__
403#include "pi_shaders.h"
406bool GRIBOverlayFactory::RenderGLGribOverlay(wxGLContext *pcontext,
408 if (g_bpause)
return false;
412 if (!m_oDC || !m_oDC->UsesGL()) {
419#ifndef USE_ANDROID_GLES2
420 glGetIntegerv(GL_SMOOTH_LINE_WIDTH_RANGE, &parms[0]);
422 glGetIntegerv(GL_ALIASED_LINE_WIDTH_RANGE, &parms[0]);
424 g_piGLMinSymbolLineWidth = wxMax(parms[0], 1);
430 m_oDC->SetDC(
nullptr);
434 bool rv = DoRenderGribOverlay(vp);
441bool GRIBOverlayFactory::RenderGribOverlay(wxDC &dc,
PlugIn_ViewPort *vp) {
442 if (!m_oDC || m_oDC->UsesGL()) {
454#if wxUSE_GRAPHICS_CONTEXT
456 pmdc =
dynamic_cast<wxMemoryDC*
>(&dc);
457 wxGraphicsContext *pgc = wxGraphicsContext::Create( *pmdc );
462 bool rv = DoRenderGribOverlay(vp);
467void GRIBOverlayFactory::SettingsIdToGribId(
int i,
int &idx,
int &idy,
472 case GribOverlaySettings::WIND:
475 case GribOverlaySettings::WIND_GUST:
480 case GribOverlaySettings::PRESSURE:
485 case GribOverlaySettings::WAVE:
490 case GribOverlaySettings::CURRENT:
495 case GribOverlaySettings::PRECIPITATION:
500 case GribOverlaySettings::CLOUD:
505 case GribOverlaySettings::AIR_TEMPERATURE:
510 case GribOverlaySettings::SEA_TEMPERATURE:
515 case GribOverlaySettings::CAPE:
520 case GribOverlaySettings::COMP_REFL:
529 if (!m_pGribTimelineRecordSet) {
530 DrawMessageWindow((m_Message), vp->pix_width, vp->pix_height,
537 m_TexFontNumbers.Build(*m_Font_Message);
539 if (m_oDC) m_oDC->SetFont(*m_Font_Message);
542 m_Message_Hiden.Empty();
545 if (m_pdc && vp->view_scale_ppm != m_last_vp_scale) ClearCachedData();
547 m_last_vp_scale = vp->view_scale_ppm;
551 wxArrayPtrVoid **pIA = m_pGribTimelineRecordSet->
m_IsobarArray;
553 for (
int overlay = 1; overlay >= 0; overlay--) {
554 for (
int i = 0; i < GribOverlaySettings::SETTINGS_COUNT; i++) {
555 if (i == GribOverlaySettings::WIND) {
557 if (m_dlg.m_bDataPlot[i]) RenderGribOverlayMap(i, pGR, vp);
559 if (m_dlg.m_bDataPlot[i]) {
560 RenderGribBarbedArrows(i, pGR, vp);
561 RenderGribIsobar(i, pGR, pIA, vp);
562 RenderGribNumbers(i, pGR, vp);
563 RenderGribParticles(i, pGR, vp);
565 if (m_Settings.Settings[i].m_iBarbedVisibility)
566 RenderGribBarbedArrows(i, pGR, vp);
571 if (i == GribOverlaySettings::PRESSURE) {
573 if (m_dlg.m_bDataPlot[i]) {
574 RenderGribIsobar(i, pGR, pIA, vp);
575 RenderGribNumbers(i, pGR, vp);
577 if (m_Settings.Settings[i].m_iIsoBarVisibility)
578 RenderGribIsobar(i, pGR, pIA, vp);
583 if (m_dlg.InDataPlot(i) && !m_dlg.m_bDataPlot[i])
continue;
586 RenderGribOverlayMap(i, pGR, vp);
588 RenderGribBarbedArrows(i, pGR, vp);
589 RenderGribIsobar(i, pGR, pIA, vp);
590 RenderGribDirectionArrows(i, pGR, vp);
591 RenderGribNumbers(i, pGR, vp);
592 RenderGribParticles(i, pGR, vp);
597 if (!m_Message_Hiden.IsEmpty()) m_Message_Hiden.Append(_T(
"\n"));
598 m_Message_Hiden.Append(_(
"Warning : Data at Geopotential Height"))
600 .Append(m_Settings.GetAltitudeFromIndex(
602 m_Settings.Settings[GribOverlaySettings::PRESSURE].m_Units))
604 .Append(m_Settings.GetUnitSymbol(GribOverlaySettings::PRESSURE))
607 if (m_dlg.ProjectionEnabled()) {
609 m_dlg.GetProjectedLatLon(x, y);
610 DrawProjectedPosition(x, y);
612 if (!m_Message_Hiden.IsEmpty()) m_Message_Hiden.Append(_T(
"\n"));
613 m_Message_Hiden.Append(m_Message);
614 DrawMessageWindow(m_Message_Hiden, vp->pix_width, vp->pix_height,
617 if (m_dlg.m_highlight_latmax - m_dlg.m_highlight_latmin > 0.01 &&
618 m_dlg.m_highlight_lonmax - m_dlg.m_highlight_lonmin > 0.01) {
620 GetCanvasPixLL(vp, &p1, m_dlg.m_highlight_latmin, m_dlg.m_highlight_lonmin);
621 GetCanvasPixLL(vp, &p2, m_dlg.m_highlight_latmax, m_dlg.m_highlight_lonmax);
623 m_pdc->SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
625 wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT),
626 wxBRUSHSTYLE_CROSSDIAG_HATCH));
627 m_pdc->DrawRectangle(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
631 m_oDC->SetPen(wxPen(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT)));
633 wxBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT),
634 wxBRUSHSTYLE_CROSSDIAG_HATCH));
635 m_oDC->DrawRectangle(p1.x, p1.y, p2.x - p1.x, p2.y - p1.y);
643static inline bool isClearSky(
int settings,
double v) {
644 return ((settings == GribOverlaySettings::PRECIPITATION) ||
645 (settings == GribOverlaySettings::CLOUD)) &&
650void GRIBOverlayFactory::GetCalibratedGraphicColor(
int settings,
double val_in,
651 unsigned char *data) {
652 unsigned char r, g, b, a;
653 a = m_Settings.m_iOverlayTransparency;
655 if (val_in != GRIB_NOTDEF) {
656 val_in = m_Settings.CalibrateValue(settings, val_in);
659 if ((settings == GribOverlaySettings::PRECIPITATION ||
660 settings == GribOverlaySettings::CLOUD) &&
663 if ((settings == GribOverlaySettings::COMP_REFL) && val_in < 5) a = 0;
665 GetGraphicColor(settings, val_in, r, g, b);
667 r = 255, g = 255, b = 255, a = 0;
675bool GRIBOverlayFactory::CreateGribGLTexture(
GribOverlay *pGO,
int settings,
678 pGR->getLonMin() == 0 && pGR->getLonMax() + pGR->
getDi() >= 360.;
681 int tw, th, samples = 1;
684 if (pGR->
getNi() > 1024 || pGR->
getNj() > 1024) {
690 dw = (tw > 1022) ? 1022. / tw : 1.;
691 dh = (th > 1022) ? 1022. / th : 1.;
692 delta = wxMin(dw, dh);
700 tw = samples * (pGR->
getNi() - 1) + 1 + 2 * !repeat;
701 th = samples * (pGR->
getNj() - 1) + 1 + 2;
702 if (tw >= 512 || th >= 512 || samples == 16)
break;
707 if (tw > 1024 || th > 1024)
return false;
709 pGO->m_iTexDataDim[0] = tw;
710 pGO->m_iTexDataDim[1] = th;
712#ifdef USE_ANDROID_GLES2
721 if (((xp != 0) && !(xp & (xp - 1))))
733 if (((xp != 0) && !(xp & (xp - 1))))
749 unsigned char *data =
new unsigned char[tw * th * 4];
751 for (
int j = 0; j < pGR->
getNj(); j++) {
752 for (
int i = 0; i < pGR->
getNi(); i++) {
754 int y = (j + 1) * delta;
755 int x = (i + !repeat) * delta;
756 int doff = 4 * (y * tw + x);
757 GetCalibratedGraphicColor(settings, v, data + doff);
760 }
else if (samples == 1) {
761 for (
int j = 0; j < pGR->
getNj(); j++) {
762 for (
int i = 0; i < pGR->
getNi(); i++) {
766 int doff = 4 * (y * tw + x);
767 GetCalibratedGraphicColor(settings, v, data + doff);
771 for (
int j = 0; j < pGR->
getNj(); j++) {
772 for (
int i = 0; i < pGR->
getNi(); i++) {
773 double v00 = pGR->
getValue(i, j), v01 = GRIB_NOTDEF;
774 double v10 = GRIB_NOTDEF, v11 = GRIB_NOTDEF;
775 if (i < pGR->getNi() - 1) {
777 if (j < pGR->getNj() - 1) v11 = pGR->
getValue(i + 1, j + 1);
779 if (j < pGR->getNj() - 1) v10 = pGR->
getValue(i, j + 1);
781 for (
int ys = 0; ys < samples; ys++) {
782 int y = j * samples + ys + 1;
783 double yd = (double)ys / samples;
785 double a0 = 1, a1 = 1;
786 if (v10 == GRIB_NOTDEF) {
788 if (v00 == GRIB_NOTDEF)
792 }
else if (v00 == GRIB_NOTDEF)
795 v0 = (1 - yd) * v00 + yd * v10;
796 if (v11 == GRIB_NOTDEF) {
798 if (v01 == GRIB_NOTDEF)
802 }
else if (v01 == GRIB_NOTDEF)
805 v1 = (1 - yd) * v01 + yd * v11;
807 for (
int xs = 0; xs < samples; xs++) {
808 int x = i * samples + xs + !repeat;
809 double xd = (double)xs / samples;
811 if (v1 == GRIB_NOTDEF)
812 v = v0, a = (1 - xd) * a0;
813 else if (v0 == GRIB_NOTDEF)
816 v = (1 - xd) * v0 + xd * v1;
817 a = (1 - xd) * a0 + xd * a1;
820 int doff = 4 * (y * tw + x);
821 GetCalibratedGraphicColor(settings, v, data + doff);
824 if (i == pGR->
getNi() - 1)
break;
826 if (j == pGR->
getNj() - 1)
break;
833 memcpy(data, data + 4 * tw * 1, 4 * tw);
834 memcpy(data + 4 * tw * (th - 1), data + 4 * tw * (th - 2), 4 * tw);
835 for (
int x = 0; x < tw; x++) {
838 doff = 4 * ((th - 1) * tw + x);
843 for (
int y = 0; y < th; y++) {
844 int doff = 4 * y * tw, soff = doff + 4;
845 memcpy(data + doff, data + soff, 4);
847 doff = 4 * (y * tw + tw - 1), soff = doff - 4;
848 memcpy(data + doff, data + soff, 4);
853 glGenTextures(1, &texture);
854 glBindTexture(texture_format, texture);
856 glTexParameteri(texture_format, GL_TEXTURE_WRAP_S,
857 repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
858 glTexParameteri(texture_format, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
859 glTexParameteri(texture_format, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
860 glTexParameteri(texture_format, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
863 glPushClientAttrib(GL_CLIENT_PIXEL_STORE_BIT);
865 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
866 glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
867 glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
868 glPixelStorei(GL_UNPACK_ROW_LENGTH, tw);
870 glTexImage2D(texture_format, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE,
875 glTexImage2D(texture_format, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE,
881 pGO->m_iTexture = texture;
882 pGO->m_iTextureDim[0] = tw;
883 pGO->m_iTextureDim[1] = th;
889wxImage GRIBOverlayFactory::CreateGribImage(
int settings,
GribRecord *pGR,
892 const wxPoint &porg) {
894 GetCanvasPixLL(vp, &pmin, pGR->getLatMin(), pGR->getLonMin());
896 GetCanvasPixLL(vp, &pmax, pGR->getLatMax(), pGR->getLonMax());
898 int width = abs(pmax.x - pmin.x);
899 int height = abs(pmax.y - pmin.y);
903 if (width > m_ParentSize.GetWidth() || height > m_ParentSize.GetHeight())
907 wxImage gr_image(width, height);
908 gr_image.InitAlpha();
911 for (
int ipix = 0; ipix < (width - grib_pixel_size + 1);
912 ipix += grib_pixel_size) {
913 for (
int jpix = 0; jpix < (height - grib_pixel_size + 1);
914 jpix += grib_pixel_size) {
918 GetCanvasLLPix(vp, p, &lat, &lon);
921 if (v != GRIB_NOTDEF) {
922 v = m_Settings.CalibrateValue(settings, v);
923 wxColour c = GetGraphicColor(settings, v);
927 isClearSky(settings, v) ? 0 : m_Settings.m_iOverlayTransparency;
929 unsigned char r = c.Red();
930 unsigned char g = c.Green();
931 unsigned char b = c.Blue();
933 for (
int xp = 0; xp < grib_pixel_size; xp++)
934 for (
int yp = 0; yp < grib_pixel_size; yp++) {
935 gr_image.SetRGB(ipix + xp, jpix + yp, r, g, b);
936 gr_image.SetAlpha(ipix + xp, jpix + yp, a);
939 for (
int xp = 0; xp < grib_pixel_size; xp++)
940 for (
int yp = 0; yp < grib_pixel_size; yp++)
941 gr_image.SetAlpha(ipix + xp, jpix + yp, 0);
946 return gr_image.Blur(4);
958 {0, _T(
"#d90000")}, {1, _T(
"#d92a00")}, {2, _T(
"#d96e00")},
959 {3, _T(
"#d9b200")}, {4, _T(
"#d4d404")}, {5, _T(
"#a6d906")},
960 {7, _T(
"#06d9a0")}, {9, _T(
"#00d9b0")}, {12, _T(
"#00d9c0")},
961 {15, _T(
"#00aed0")}, {18, _T(
"#0083e0")}, {21, _T(
"#0057e0")},
962 {24, _T(
"#0000f0")}, {27, _T(
"#0400f0")}, {30, _T(
"#1c00f0")},
963 {36, _T(
"#4800f0")}, {42, _T(
"#6900f0")}, {48, _T(
"#a000f0")},
964 {56, _T(
"#f000f0")}};
967 {0, _T(
"#00d900")}, {1, _T(
"#2ad900")}, {2, _T(
"#6ed900")},
968 {3, _T(
"#b2d900")}, {4, _T(
"#d4d400")}, {5, _T(
"#d9a600")},
969 {7, _T(
"#d90000")}, {9, _T(
"#d90040")}, {12, _T(
"#d90060")},
970 {15, _T(
"#ae0080")}, {18, _T(
"#8300a0")}, {21, _T(
"#5700c0")},
971 {24, _T(
"#0000d0")}, {27, _T(
"#0400e0")}, {30, _T(
"#0800e0")},
972 {36, _T(
"#a000e0")}, {42, _T(
"#c004c0")}, {48, _T(
"#c008a0")},
973 {56, _T(
"#c0a008")}};
977 {0, _T(
"#288CFF")}, {3, _T(
"#00AFFF")}, {6, _T(
"#00DCE1")},
978 {9, _T(
"#00F7B0")}, {12, _T(
"#00EA9C")}, {15, _T(
"#82F059")},
979 {18, _T(
"#F0F503")}, {21, _T(
"#FFED00")}, {24, _T(
"#FFDB00")},
980 {27, _T(
"#FFC700")}, {30, _T(
"#FFB400")}, {33, _T(
"#FF9800")},
981 {36, _T(
"#FF7E00")}, {39, _T(
"#F77800")}, {42, _T(
"#EC7814")},
982 {45, _T(
"#E4711E")}, {48, _T(
"#E06128")}, {51, _T(
"#DC5132")},
983 {54, _T(
"#D5453C")}, {57, _T(
"#CD3A46")}, {60, _T(
"#BE2C50")},
984 {63, _T(
"#B41A5A")}, {66, _T(
"#AA1464")}, {70, _T(
"#962878")},
985 {75, _T(
"#8C328C")}};
989 {0, _T(
"#283282")}, {5, _T(
"#273c8c")}, {10, _T(
"#264696")},
990 {14, _T(
"#2350a0")}, {18, _T(
"#1f5aaa")}, {22, _T(
"#1a64b4")},
991 {26, _T(
"#136ec8")}, {29, _T(
"#0c78e1")}, {32, _T(
"#0382e6")},
992 {35, _T(
"#0091e6")}, {38, _T(
"#009ee1")}, {41, _T(
"#00a6dc")},
993 {44, _T(
"#00b2d7")}, {47, _T(
"#00bed2")}, {50, _T(
"#28c8c8")},
994 {53, _T(
"#78d2aa")}, {56, _T(
"#8cdc78")}, {59, _T(
"#a0eb5f")},
995 {62, _T(
"#c8f550")}, {65, _T(
"#f3fb02")}, {68, _T(
"#ffed00")},
996 {71, _T(
"#ffdd00")}, {74, _T(
"#ffc900")}, {78, _T(
"#ffab00")},
997 {82, _T(
"#ff8100")}, {86, _T(
"#f1780c")}, {90, _T(
"#e26a23")},
998 {95, _T(
"#d5453c")}, {100, _T(
"#b53c59")}};
1003 {-2, _T(
"#cc04ae")}, {2, _T(
"#8f06e4")}, {6, _T(
"#486afa")},
1004 {10, _T(
"#00ffff")}, {15, _T(
"#00d54b")}, {19, _T(
"#59d800")},
1005 {23, _T(
"#f2fc00")}, {27, _T(
"#ff1500")}, {32, _T(
"#ff0000")},
1006 {36, _T(
"#d80000")}, {40, _T(
"#a90000")}, {44, _T(
"#870000")},
1007 {48, _T(
"#690000")}, {52, _T(
"#550000")}, {56, _T(
"#330000")}};
1010static ColorMap PrecipitationMap[] = {
1011 {0, _T(
"#ffffff")}, {.01, _T(
"#c8f0ff")}, {.02, _T(
"#b4e6ff")},
1012 {.05, _T(
"#8cd3ff")}, {.07, _T(
"#78caff")}, {.1, _T(
"#6ec1ff")},
1013 {.2, _T(
"#64b8ff")}, {.5, _T(
"#50a6ff")}, {.7, _T(
"#469eff")},
1014 {1.0, _T(
"#3c96ff")}, {2.0, _T(
"#328eff")}, {5.0, _T(
"#1e7eff")},
1015 {7.0, _T(
"#1476f0")}, {10, _T(
"#0a6edc")}, {20, _T(
"#0064c8")},
1016 {50, _T(
"#0052aa")}};
1020 {0, _T(
"#ffffff")}, {1, _T(
"#f0f0e6")}, {10, _T(
"#e6e6dc")},
1021 {20, _T(
"#dcdcd2")}, {30, _T(
"#c8c8b4")}, {40, _T(
"#aaaa8c")},
1022 {50, _T(
"#969678")}, {60, _T(
"#787864")}, {70, _T(
"#646450")},
1023 {80, _T(
"#5a5a46")}, {90, _T(
"#505036")}};
1026 {0, _T(
"#ffffff")}, {5, _T(
"#06E8E4")}, {10, _T(
"#009BE9")},
1027 {15, _T(
"#0400F3")}, {20, _T(
"#00F924")}, {25, _T(
"#06C200")},
1028 {30, _T(
"#009100")}, {35, _T(
"#FAFB00")}, {40, _T(
"#EBB608")},
1029 {45, _T(
"#FF9400")}, {50, _T(
"#FD0002")}, {55, _T(
"#D70000")},
1030 {60, _T(
"#C20300")}, {65, _T(
"#F900FE")}, {70, _T(
"#945AC8")}};
1033 {0, _T(
"#0046c8")}, {5, _T(
"#0050f0")}, {10, _T(
"#005aff")},
1034 {15, _T(
"#0069ff")}, {20, _T(
"#0078ff")}, {30, _T(
"#000cff")},
1035 {45, _T(
"#00a1ff")}, {60, _T(
"#00b6fa")}, {100, _T(
"#00c9ee")},
1036 {150, _T(
"#00e0da")}, {200, _T(
"#00e6b4")}, {300, _T(
"#82e678")},
1037 {500, _T(
"#9bff3b")}, {700, _T(
"#ffdc00")}, {1000, _T(
"#ffb700")},
1038 {1500, _T(
"#f37800")}, {2000, _T(
"#d4440c")}, {2500, _T(
"#c8201c")},
1039 {3000, _T(
"#ad0430")},
1043 {0, _T(
"#6271B7")}, {3, _T(
"#3961A9")}, {6, _T(
"#4A94A9")},
1044 {9, _T(
"#4D8D7B")}, {12, _T(
"#53A553")}, {15, _T(
"#53A553")},
1045 {18, _T(
"#359F35")}, {21, _T(
"#A79D51")}, {24, _T(
"#9F7F3A")},
1046 {27, _T(
"#A16C5C")}, {30, _T(
"#A16C5C")}, {33, _T(
"#813A4E")},
1047 {36, _T(
"#AF5088")}, {39, _T(
"#AF5088")}, {42, _T(
"#754A93")},
1048 {45, _T(
"#754A93")}, {48, _T(
"#6D61A3")}, {51, _T(
"#44698D")},
1049 {54, _T(
"#44698D")}, {57, _T(
"#5C9098")}, {60, _T(
"#7D44A5")},
1050 {63, _T(
"#7D44A5")}, {66, _T(
"#7D44A5")}, {69, _T(
"#E7D7D7")},
1051 {72, _T(
"#E7D7D7")}, {75, _T(
"#E7D7D7")}, {78, _T(
"#DBD483")},
1052 {81, _T(
"#DBD483")}, {84, _T(
"#DBD483")}, {87, _T(
"#CDC470")},
1053 {90, _T(
"#CDC470")}, {93, _T(
"#CDC470")}, {96, _T(
"#CDC470")},
1054 {99, _T(
"#808080")}};
1057static ColorMap *ColorMaps[] = {CurrentMap, GenericMap, WindMap, AirTempMap, SeaTempMap, PrecipitationMap, CloudMap};
1061 GENERIC_GRAPHIC_INDEX,
1063 AIRTEMP__GRAPHIC_INDEX,
1064 SEATEMP_GRAPHIC_INDEX,
1065 PRECIPITATION_GRAPHIC_INDEX,
1066 CLOUD_GRAPHIC_INDEX,
1067 CURRENT_GRAPHIC_INDEX,
1073static void InitColor(
ColorMap *map,
size_t maplen) {
1075 for (
size_t i = 0; i < maplen; i++) {
1078 map[i].g = c.Green();
1079 map[i].b = c.Blue();
1083void GRIBOverlayFactory::InitColorsTable() {
1084 InitColor(CurrentMap, (
sizeof CurrentMap) / (
sizeof *CurrentMap));
1085 InitColor(GenericMap, (
sizeof GenericMap) / (
sizeof *GenericMap));
1086 InitColor(WindMap, (
sizeof WindMap) / (
sizeof *WindMap));
1087 InitColor(AirTempMap, (
sizeof AirTempMap) / (
sizeof *AirTempMap));
1088 InitColor(SeaTempMap, (
sizeof SeaTempMap) / (
sizeof *SeaTempMap));
1089 InitColor(PrecipitationMap,
1090 (
sizeof PrecipitationMap) / (
sizeof *PrecipitationMap));
1091 InitColor(CloudMap, (
sizeof CloudMap) / (
sizeof *CloudMap));
1092 InitColor(CAPEMap, (
sizeof CAPEMap) / (
sizeof *CAPEMap));
1093 InitColor(REFCMap, (
sizeof REFCMap) / (
sizeof *REFCMap));
1094 InitColor(WindyMap, (
sizeof WindyMap) / (
sizeof *WindyMap));
1097void GRIBOverlayFactory::GetGraphicColor(
int settings,
double val_in,
1098 unsigned char &r,
unsigned char &g,
1100 int colormap_index = m_Settings.Settings[settings].m_iOverlayMapColors;
1105 double min = m_Settings.GetMin(settings), max = m_Settings.GetMax(settings);
1108 val_in /= max - min;
1110 switch (colormap_index) {
1111 case CURRENT_GRAPHIC_INDEX:
1113 maplen = (
sizeof CurrentMap) / (
sizeof *CurrentMap);
1115 case GENERIC_GRAPHIC_INDEX:
1117 maplen = (
sizeof GenericMap) / (
sizeof *GenericMap);
1119 case WIND_GRAPHIC_INDEX:
1121 maplen = (
sizeof WindMap) / (
sizeof *WindMap);
1123 case AIRTEMP__GRAPHIC_INDEX:
1125 maplen = (
sizeof AirTempMap) / (
sizeof *AirTempMap);
1127 case SEATEMP_GRAPHIC_INDEX:
1129 maplen = (
sizeof SeaTempMap) / (
sizeof *SeaTempMap);
1131 case PRECIPITATION_GRAPHIC_INDEX:
1132 map = PrecipitationMap;
1133 maplen = (
sizeof PrecipitationMap) / (
sizeof *PrecipitationMap);
1135 case CLOUD_GRAPHIC_INDEX:
1137 maplen = (
sizeof CloudMap) / (
sizeof *CloudMap);
1139 case CAPE_GRAPHIC_INDEX:
1141 maplen = (
sizeof CAPEMap) / (
sizeof *CAPEMap);
1143 case REFC_GRAPHIC_INDEX:
1145 maplen = (
sizeof REFCMap) / (
sizeof *REFCMap);
1147 case WINDY_GRAPHIC_INDEX:
1149 maplen = (
sizeof WindyMap) / (
sizeof *WindyMap);
1156 double cmax = map[maplen - 1].val;
1158 for (
int i = 1; i < maplen; i++) {
1159 double nmapvala = map[i - 1].val / cmax;
1160 double nmapvalb = map[i].val / cmax;
1161 if (nmapvalb > val_in || i == maplen - 1) {
1162 if (m_bGradualColors) {
1163 double d = (val_in - nmapvala) / (nmapvalb - nmapvala);
1164 r = (1 - d) * map[i - 1].r + d * map[i].r;
1165 g = (1 - d) * map[i - 1].g + d * map[i].g;
1166 b = (1 - d) * map[i - 1].b + d * map[i].b;
1178wxColour GRIBOverlayFactory::GetGraphicColor(
int settings,
double val_in) {
1179 unsigned char r, g, b;
1180 GetGraphicColor(settings, val_in, r, g, b);
1181 return wxColour(r, g, b);
1184wxString GRIBOverlayFactory::getLabelString(
double value,
int settings) {
1186 wxString f = _T(
"%.*f");
1189 case GribOverlaySettings::PRESSURE:
1191 if (m_Settings.Settings[settings].m_Units == 2)
1193 else if (m_Settings.Settings[settings].m_Units == 0 &&
1194 m_Settings.Settings[settings].m_bAbbrIsoBarsNumbers) {
1195 value -= floor(value / 100.) * 100.;
1199 case GribOverlaySettings::WAVE:
1200 case GribOverlaySettings::CURRENT:
1201 case GribOverlaySettings::AIR_TEMPERATURE:
1202 case GribOverlaySettings::SEA_TEMPERATURE:
1205 case GribOverlaySettings::PRECIPITATION:
1206 p = value < 100. ? 2 : value < 10. ? 1 : 0;
1207 p += m_Settings.Settings[settings].m_Units == 1 ? 1 : 0;
1212 return wxString::Format(f, p, value);
1216wxImage &GRIBOverlayFactory::getLabel(
double value,
int settings,
1217 wxColour back_color) {
1218 std::map<double, wxImage>::iterator it;
1219 it = m_labelCache.find(value);
1220 if (it != m_labelCache.end())
return m_labelCache[value];
1222 wxString labels = getLabelString(value, settings);
1224 wxColour text_color;
1225 GetGlobalColor(_T (
"UBLCK" ), &text_color);
1226 wxPen penText(text_color);
1228 wxBrush backBrush(back_color);
1230 wxFont mfont(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
1231 wxFONTWEIGHT_NORMAL);
1235 sdc.GetTextExtent(labels, &w, &h,
nullptr,
nullptr, &mfont);
1237 int label_offset = 5;
1239 wxBitmap bm(w + label_offset * 2, h + 2);
1244 mdc.SetPen(penText);
1245 mdc.SetBrush(backBrush);
1246 mdc.SetTextForeground(text_color);
1247 mdc.SetTextBackground(back_color);
1252 mdc.DrawRectangle(xd, yd, w + (label_offset * 2), h + 2);
1253 mdc.DrawText(labels, label_offset + xd, yd + 1);
1255 mdc.SelectObject(wxNullBitmap);
1257 m_labelCache[value] = bm.ConvertToImage();
1259 m_labelCache[value].InitAlpha();
1261 return m_labelCache[value];
1264double square(
double x) {
return x * x; }
1266void GRIBOverlayFactory::RenderGribBarbedArrows(
int settings,
GribRecord **pGR,
1268 if (!m_Settings.Settings[settings].m_bBarbedArrows)
return;
1274 SettingsIdToGribId(settings, idx, idy, polar);
1275 if (idx < 0 || idy < 0)
return;
1280 if (!pGRX || !pGRY)
return;
1283 GetGlobalColor(_T (
"YELO2" ), &colour);
1287#ifndef __OCPN__ANDROID__
1289 glEnable(GL_LINE_SMOOTH);
1291 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1292 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1298 glEnableClientState(GL_VERTEX_ARRAY);
1302 if (m_Settings.Settings[settings].m_bBarbArrFixSpac) {
1304 int space = adjustSpacing(m_Settings.Settings[settings].m_iBarbArrSpacing);
1307 uvp.rotation = uvp.skew = 0;
1311 for (
int i = 0; i < m_ParentSize.GetWidth(); i += (space + arrowSize)) {
1312 for (
int j = 0; j < m_ParentSize.GetHeight(); j += (space + arrowSize)) {
1314 GetCanvasLLPix(vp, wxPoint(i, j), &lat, &lon);
1318 drawWindArrowWithBarbs(settings, i, j, vkn * 3.6 / 1.852,
1319 (ang - 90) * M_PI / 180, (lat < 0.), colour,
1325 double minspace = wxMax(m_Settings.Settings[settings].m_iBarbArrSpacing,
1326 windArrowSize * 1.2);
1327 double minspace2 = square(minspace);
1330 int imax = pGRX->
getNi();
1331 int jmax = pGRX->
getNj();
1333 wxPoint firstpx(-1000, -1000);
1334 wxPoint oldpx(-1000, -1000);
1335 wxPoint oldpy(-1000, -1000);
1337 for (
int i = 0; i < imax; i++) {
1342 pGRX->
getXY(i, pGRX->
getNj() / 2, &lonl, &latl);
1344 GetCanvasPixLL(vp, &pl, latl, lonl);
1346 if (pl.x <= firstpx.x &&
1347 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1351 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) < minspace2)
continue;
1354 if (i == 0) firstpx = pl;
1357 for (
int j = 0; j < jmax; j++) {
1358 double lat = pGRX->
getY(j);
1360 if (!PointInLLBox(vp, lon, lat))
continue;
1363 GetCanvasPixLL(vp, &p, lat, lon);
1365 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) < minspace2)
continue;
1369 if (lon > 180) lon -= 360;
1374 if (vx != GRIB_NOTDEF && vy != GRIB_NOTDEF) {
1376 vkn = sqrt(vx * vx + vy * vy);
1377 ang = atan2(vy, -vx);
1378 drawWindArrowWithBarbs(settings, p.x, p.y, vkn * 3.6 / 1.852, ang,
1379 (lat < 0.), colour, vp->rotation);
1386 if (!m_pdc) glDisableClientState(GL_VERTEX_ARRAY);
1390void GRIBOverlayFactory::RenderGribIsobar(
int settings,
GribRecord **pGR,
1391 wxArrayPtrVoid **pIsobarArray,
1393 if (!m_Settings.Settings[settings].m_bIsoBars)
return;
1398 SettingsIdToGribId(settings, idx, idy, polar);
1399 if (idx < 0)
return;
1401 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1405 wxColour back_color;
1406 GetGlobalColor(_T (
"DILG1" ), &back_color);
1409 if (!pIsobarArray[idx]) {
1411 if (idy >= 0 && !polar && pGR[idy]) {
1412 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1413 if (!pGRM->isOk()) {
1414 m_Message_Hiden.Append(_(
"IsoBar Unable to compute record magnitude"));
1421 pIsobarArray[idx] =
new wxArrayPtrVoid;
1424 wxGenericProgressDialog *progressdialog =
nullptr;
1425 wxDateTime start = wxDateTime::Now();
1427 double min = m_Settings.GetMin(settings);
1428 double max = m_Settings.GetMax(settings);
1431 double factor = (settings == GribOverlaySettings::PRESSURE &&
1432 m_Settings.Settings[settings].m_Units == 2)
1436 for (
double press = min; press <= max;
1437 press += (m_Settings.Settings[settings].m_iIsoBarSpacing * factor)) {
1439 progressdialog->Update(press - min);
1441 wxDateTime now = wxDateTime::Now();
1442 if ((now - start).GetSeconds() > 3 && press - min < (max - min) / 2) {
1443 progressdialog =
new wxGenericProgressDialog(
1444 _(
"Building Isobar map"), _(
"Wind"), max - min + 1,
nullptr,
1445 wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME);
1450 m_Settings.CalibrationFactor(settings, press,
true),
1451 m_Settings.CalibrationOffset(settings), pGRA);
1453 pIsobarArray[idx]->Add(piso);
1455 delete progressdialog;
1461 for (
unsigned int i = 0; i < pIsobarArray[idx]->GetCount(); i++) {
1463 piso->drawIsoLine(
this, m_pdc, vp,
true);
1470 piso->drawIsoLineLabels(
this, m_pdc, vp, density, first,
1471 getLabel(piso->getValue(), settings, back_color));
1473 piso->drawIsoLineLabelsGL(
this, vp, density, first,
1474 getLabelString(piso->getValue(), settings),
1475 back_color, m_TexFontNumbers);
1479void GRIBOverlayFactory::FillGrid(
GribRecord *pGR) {
1481 int imax = pGR->
getNi();
1482 int jmax = pGR->
getNj();
1484 for (
int i = 0; i < imax; i++) {
1485 for (
int j = 1; j < jmax - 1; j++) {
1486 if (pGR->
getValue(i, j) == GRIB_NOTDEF) {
1489 if (pGR->
getValue(i, j - 1) != GRIB_NOTDEF) {
1493 if (pGR->
getValue(i, j + 1) != GRIB_NOTDEF) {
1497 if (div > 1) pGR->setValue(i, j, acc / div);
1502 for (
int j = 0; j < jmax; j++) {
1503 for (
int i = 1; i < imax - 1; i++) {
1504 if (pGR->
getValue(i, j) == GRIB_NOTDEF) {
1507 if (pGR->
getValue(i - 1, j) != GRIB_NOTDEF) {
1511 if (pGR->
getValue(i + 1, j) != GRIB_NOTDEF) {
1515 if (div > 1) pGR->setValue(i, j, acc / div);
1520 pGR->setFilled(
true);
1523void GRIBOverlayFactory::RenderGribDirectionArrows(
int settings,
1526 if (!m_Settings.Settings[settings].m_bDirectionArrows)
return;
1531 SettingsIdToGribId(settings, idx, idy, polar);
1532 if (idx < 0 || idy < 0)
return;
1536 if (!pGRX || !pGRY)
return;
1537 if (!pGRX->isFilled()) FillGrid(pGRX);
1538 if (!pGRY->isFilled()) FillGrid(pGRY);
1543 arrowSizeIdx = m_Settings.Settings[settings].m_iDirectionArrowSize;
1544 if (arrowSizeIdx == 0) {
1545 if (m_pixelMM > 0.2)
1548 arrowSize = 5. / m_pixelMM;
1554 GetGlobalColor(_T (
"DILG3" ), &colour);
1558 if (m_pixelMM > 0.2) {
1560 glEnable(GL_LINE_SMOOTH);
1562 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1563 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1565 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1571 glEnableClientState(GL_VERTEX_ARRAY);
1575 if (m_Settings.Settings[settings].m_bDirArrFixSpac) {
1578 int space = adjustSpacing(m_Settings.Settings[settings].m_iDirArrSpacing);
1580 for (
int i = 0; i < m_ParentSize.GetWidth(); i += (space + arrowSize)) {
1581 for (
int j = 0; j < m_ParentSize.GetHeight(); j += (space + arrowSize)) {
1582 double lat, lon, sh, dir;
1584 GetCanvasLLPix(vp, wxPoint(i, j), &lat, &lon);
1590 if (dir == GRIB_NOTDEF || sh == GRIB_NOTDEF)
continue;
1595 scale = wxMax(1.0, sh);
1598 dir = (dir - 90) * M_PI / 180.;
1601 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1602 drawSingleArrow(i, j, dir + vp->rotation, colour, arrowWidth,
1603 arrowSizeIdx,
scale);
1604 else if (m_Settings.Settings[settings].m_iDirectionArrowForm == 1)
1605 drawDoubleArrow(i, j, dir + vp->rotation, colour, arrowWidth,
1606 arrowSizeIdx,
scale);
1608 drawSingleArrow(i, j, dir + vp->rotation, colour,
1609 wxMax(1, wxMin(8, (
int)(sh + 0.5))), arrowSizeIdx,
1618 wxMax(m_Settings.Settings[settings].m_iDirArrSpacing,
1619 m_Settings.Settings[settings].m_iDirectionArrowSize * 1.2);
1620 double minspace2 = square(minspace);
1623 int imax = pGRX->
getNi();
1624 int jmax = pGRX->
getNj();
1626 wxPoint firstpx(-1000, -1000);
1627 wxPoint oldpx(-1000, -1000);
1628 wxPoint oldpy(-1000, -1000);
1630 for (
int i = 0; i < imax; i++) {
1632 pGRX->
getXY(i, pGRX->
getNj() / 2, &lonl, &latl);
1635 GetCanvasPixLL(vp, &pl, latl, lonl);
1637 if (pl.x <= firstpx.x &&
1638 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1642 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) < minspace2)
continue;
1645 if (i == 0) firstpx = pl;
1647 for (
int j = 0; j < jmax; j++) {
1649 pGRX->
getXY(i, j, &lon, &lat);
1652 GetCanvasPixLL(vp, &p, lat, lon);
1654 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) >= minspace2) {
1657 if (lon > 180) lon -= 360;
1659 if (PointInLLBox(vp, lon, lat)) {
1660 double sh, dir, wdh;
1666 if (dir == GRIB_NOTDEF || sh == GRIB_NOTDEF)
continue;
1674 wdh = (8 / 2.5 * sh) + 0.5;
1675 scale = wxMax(1.0, sh);
1678 dir = (dir - 90) * M_PI / 180.;
1681 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1682 drawSingleArrow(p.x, p.y, dir + vp->rotation, colour, arrowWidth,
1683 arrowSizeIdx,
scale);
1684 else if (m_Settings.Settings[settings].m_iDirectionArrowForm == 1)
1685 drawDoubleArrow(p.x, p.y, dir + vp->rotation, colour, arrowWidth,
1686 arrowSizeIdx,
scale);
1688 drawSingleArrow(p.x, p.y, dir + vp->rotation, colour,
1689 wxMax(1, wxMin(8, (
int)wdh)), arrowSizeIdx,
1698 if (!m_pdc) glDisableClientState(GL_VERTEX_ARRAY);
1702void GRIBOverlayFactory::RenderGribOverlayMap(
int settings,
GribRecord **pGR,
1704 if (!m_Settings.Settings[settings].m_bOverlayMap)
return;
1706 const int grib_pixel_size = 4;
1709 SettingsIdToGribId(settings, idx, idy, polar);
1710 if (idx < 0 || !pGR[idx])
return;
1712 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1715 if (idy >= 0 && !polar && pGR[idy]) {
1716 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1717 if (!pGRM->isOk()) {
1718 m_Message_Hiden.Append(
1719 _(
"OverlayMap Unable to compute record magnitude"));
1726 if (!pGRA->isFilled()) FillGrid(pGRA);
1729 GetCanvasPixLL(vp, &porg, pGRA->getLatMax(), pGRA->getLonMin());
1734 if (Intersect(vp, pGRA->getLatMin(), pGRA->getLatMax(), pGRA->getLonMin(),
1735 pGRA->getLonMax(), 0.) != _GOUT)
1737 if (Intersect(vp, pGRA->getLatMin(), pGRA->getLatMax(),
1738 pGRA->getLonMin() - 360., pGRA->getLonMax() - 360.,
1744 if (!m_pOverlay[settings]) m_pOverlay[settings] =
new GribOverlay;
1752 texture_format = GL_TEXTURE_2D;
1754 if (!texture_format)
1756 m_Message_Hiden.Append(
1757 _(
"Overlays not supported by this graphics hardware (Disable "
1760 if (!pGO->m_iTexture) CreateGribGLTexture(pGO, settings, pGRA);
1762 if (pGO->m_iTexture)
1763 DrawGLTexture(pGO, pGRA, vp);
1765 m_Message_Hiden.IsEmpty()
1767 .Append(_(
"Overlays too wide and can't be displayed:"))
1769 .Append(GribOverlaySettings::NameFromIndex(settings))
1770 : m_Message_Hiden.Append(_T(
",")).Append(
1771 GribOverlaySettings::NameFromIndex(settings));
1776 if (fabs(vp->rotation) > 0.1) {
1777 m_Message_Hiden.Append(_(
1778 "overlays suppressed if not north-up in DC mode (enable OpenGL)"));
1780 if (!pGO->m_pDCBitmap) {
1782 CreateGribImage(settings, pGRA, vp, grib_pixel_size, porg);
1783 if (bl_image.IsOk()) {
1785 pGO->m_pDCBitmap =
new wxBitmap(bl_image);
1787 new wxMask(*(pGO->m_pDCBitmap), wxColour(0, 0, 0));
1788 pGO->m_pDCBitmap->SetMask(gr_mask);
1792 if (pGO->m_pDCBitmap)
1793 m_pdc->DrawBitmap(*(pGO->m_pDCBitmap), porg.x, porg.y,
true);
1795 m_Message_Hiden.IsEmpty()
1798 "Please Zoom or Scale Out to view invisible overlays:"))
1800 .Append(GribOverlaySettings::NameFromIndex(settings))
1801 : m_Message_Hiden.Append(_T(
",")).Append(
1802 GribOverlaySettings::NameFromIndex(settings));
1810void GRIBOverlayFactory::RenderGribNumbers(
int settings,
GribRecord **pGR,
1812 if (!m_Settings.Settings[settings].m_bNumbers)
return;
1817 SettingsIdToGribId(settings, idx, idy, polar);
1818 if (idx < 0)
return;
1820 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1825 if (idy >= 0 && !polar && pGR[idy]) {
1826 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1827 if (!pGRM->isOk()) {
1828 m_Message_Hiden.Append(
1829 _(
"GribNumbers Unable to compute record magnitude"));
1838 m_TexFontNumbers.GetTextExtent(_T(
"1234"), &wstring,
nullptr);
1840 if (m_Settings.Settings[settings].m_bNumFixSpac) {
1843 int space = adjustSpacing(m_Settings.Settings[settings].m_iNumbersSpacing);
1846 uvp.rotation = uvp.skew = 0;
1849 GetCanvasPixLL(&uvp, &ptl, wxMin(pGRA->getLatMax(), 89.0),
1851 GetCanvasPixLL(&uvp, &pbr, wxMax(pGRA->getLatMin(), -89.0),
1853 if (ptl.x >= pbr.x) {
1856 pbr.x = m_ParentSize.GetWidth();
1859 for (
int i = wxMax(ptl.x, 0); i < wxMin(pbr.x, m_ParentSize.GetWidth());
1860 i += (space + wstring)) {
1861 for (
int j = wxMax(ptl.y, 0); j < wxMin(pbr.y, m_ParentSize.GetHeight());
1862 j += (space + wstring)) {
1863 double lat, lon, val;
1864 GetCanvasLLPix(vp, wxPoint(i, j), &lat, &lon);
1866 if (val != GRIB_NOTDEF) {
1867 double value = m_Settings.CalibrateValue(settings, val);
1868 wxColour back_color = GetGraphicColor(settings, value);
1870 DrawNumbers(wxPoint(i, j), value, settings, back_color);
1877 wxMax(m_Settings.Settings[settings].m_iNumbersSpacing, wstring * 1.2);
1878 double minspace2 = square(minspace);
1881 int imax = pGRA->
getNi();
1882 int jmax = pGRA->
getNj();
1884 wxPoint firstpx(-1000, -1000);
1885 wxPoint oldpx(-1000, -1000);
1886 wxPoint oldpy(-1000, -1000);
1888 for (
int i = 0; i < imax; i++) {
1890 pGRA->
getXY(i, pGRA->
getNj() / 2, &lonl, &latl);
1893 GetCanvasPixLL(vp, &pl, latl, lonl);
1895 if (pl.x <= firstpx.x &&
1896 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1900 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) >= minspace2) {
1902 if (i == 0) firstpx = pl;
1904 for (
int j = 0; j < jmax; j++) {
1906 pGRA->
getXY(i, j, &lon, &lat);
1909 GetCanvasPixLL(vp, &p, lat, lon);
1911 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) >= minspace2) {
1914 if (lon > 180) lon -= 360;
1916 if (PointInLLBox(vp, lon, lat)) {
1919 if (mag != GRIB_NOTDEF) {
1920 double value = m_Settings.CalibrateValue(settings, mag);
1921 wxColour back_color = GetGraphicColor(settings, value);
1923 DrawNumbers(p, value, settings, back_color);
1935void GRIBOverlayFactory::DrawNumbers(wxPoint p,
double value,
int settings,
1936 wxColour back_color) {
1938 wxImage &label = getLabel(value, settings, back_color);
1940 int w = label.GetWidth(), h = label.GetHeight();
1941 for (
int y = 0; y < h; y++)
1942 for (
int x = 0; x < w; x++)
1943 label.SetAlpha(x, y, m_Settings.m_iOverlayTransparency);
1945 m_pdc->DrawBitmap(label, p.x, p.y,
true);
1951 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1952 glColor4ub(back_color.Red(), back_color.Green(), back_color.Blue(),
1953 m_Settings.m_iOverlayTransparency);
1957 wxString label = getLabelString(value, settings);
1959 m_TexFontNumbers.GetTextExtent(label, &w, &h);
1961 int label_offsetx = 5, label_offsety = 1;
1962 int x = p.x - label_offsetx, y = p.y - label_offsety;
1963 w += 2 * label_offsetx, h += 2 * label_offsety;
1968 glVertex2i(x + w, y);
1969 glVertex2i(x + w, y + h);
1970 glVertex2i(x, y + h);
1973 glColor4ub(0, 0, 0, m_Settings.m_iOverlayTransparency);
1975 glBegin(GL_LINE_LOOP);
1977 glVertex2i(x + w, y);
1978 glVertex2i(x + w, y + h);
1979 glVertex2i(x, y + h);
1982 glEnable(GL_TEXTURE_2D);
1983 m_TexFontNumbers.RenderString(label, p.x, p.y);
1984 glDisable(GL_TEXTURE_2D);
1990 wxFont font(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
1991 wxFONTWEIGHT_NORMAL);
1994 wxString label = getLabelString(value, settings);
1996 m_oDC->SetFont(font);
1998 m_oDC->GetTextExtent(label, &w, &h);
2000 int label_offsetx = 5, label_offsety = 1;
2001 int x = p.x - label_offsetx, y = p.y - label_offsety;
2002 w += 2 * label_offsetx, h += 2 * label_offsety;
2004 m_oDC->SetBrush(wxBrush(back_color));
2005 m_oDC->DrawRoundedRectangle(x, y, w, h, 0);
2008 m_oDC->SetPen(wxPen(wxColour(0, 0, 0), 1));
2009 m_oDC->DrawLine(x, y, x + w, y);
2010 m_oDC->DrawLine(x + w, y, x + w, y + h);
2011 m_oDC->DrawLine(x + w, y + h, x, y + h);
2012 m_oDC->DrawLine(x, y + h, x, y);
2014 m_oDC->DrawText(label, p.x, p.y);
2021void GRIBOverlayFactory::RenderGribParticles(
int settings,
GribRecord **pGR,
2023 if (!m_Settings.Settings[settings].m_bParticles)
return;
2029 SettingsIdToGribId(settings, idx, idy, polar);
2030 if (idx < 0 || idy < 0)
return;
2035 if (!pGRX || !pGRY)
return;
2040 if (m_ParticleMap && m_ParticleMap->m_Setting != settings) ClearParticles();
2042 if (!m_ParticleMap) m_ParticleMap =
new ParticleMap(settings);
2044 std::vector<Particle> &particles = m_ParticleMap->m_Particles;
2046 const int max_duration = 50;
2047 const int run_count = 6;
2049 double density = m_Settings.Settings[settings].m_dParticleDensity;
2052 int history_size = 27 / sqrt(density);
2053 history_size = wxMin(history_size, MAX_PARTICLE_HISTORY);
2055 std::vector<Particle>::iterator it;
2057 if (m_ParticleMap->history_size != history_size) {
2058 for (
unsigned int i = 0; i < particles.size(); i++) {
2060 if (m_ParticleMap->history_size > history_size &&
2061 it.m_HistoryPos >= history_size) {
2062 it = particles[particles.size() - 1];
2063 particles.pop_back();
2068 it.m_HistorySize = it.m_HistoryPos + 1;
2070 m_ParticleMap->history_size = history_size;
2076 if (lvp.bValid ==
false || vp->view_scale_ppm != lvp.view_scale_ppm ||
2077 vp->skew != lvp.skew || vp->rotation != lvp.rotation) {
2078 for (it = particles.begin(); it != particles.end(); it++)
2079 for (
int i = 0; i < it->m_HistorySize; i++) {
2081 float(&p)[2] = n.m_Pos;
2082 if (p[0] == -10000)
continue;
2085 GetCanvasPixLL(vp, &ps, p[1], p[0]);
2086 n.m_Screen[0] = ps.x;
2087 n.m_Screen[1] = ps.y;
2092 if (vp->clat != lvp.clat || vp->clon != lvp.clon) {
2094 GetCanvasPixLL(vp, &p1, 0, 0);
2095 GetCanvasPixLL(&lvp, &p2, 0, 0);
2099 for (it = particles.begin(); it != particles.end(); it++)
2100 for (
int i = 0; i < it->m_HistorySize; i++) {
2102 float(&p)[2] = n.m_Pos;
2103 if (p[0] == -10000)
continue;
2105 n.m_Screen[0] += p1.x;
2106 n.m_Screen[1] += p1.y;
2114 if (m_bUpdateParticles) {
2115 for (
unsigned int i = 0; i < particles.size(); i++) {
2119 if (++it.m_Run < run_count)
continue;
2124 it = particles[particles.size() - 1];
2125 particles.pop_back();
2132 float(&pp)[2] = it.m_History[it.m_HistoryPos].m_Pos;
2135 if (++it.m_HistorySize > history_size) it.m_HistorySize = history_size;
2137 if (++it.m_HistoryPos >= history_size) it.m_HistoryPos = 0;
2140 float(&p)[2] = n.m_Pos;
2141 double vkn = 0, ang;
2143 if (it.
m_Duration < max_duration - history_size &&
2146 vkn > 0 && vkn < 100) {
2147 vkn = m_Settings.CalibrateValue(settings, vkn);
2149 if (settings == GribOverlaySettings::CURRENT)
2150 d = vkn * run_count;
2152 d = vkn * run_count / 4;
2158 PositionBearingDistanceMercator_Plugin(pp[1], pp[0], ang,
2164 float angr = ang / 180 * M_PI;
2165 p[0] = pp[0] + sinf(angr) * d / 60;
2166 p[1] = pp[1] + cosf(angr) * d / 60;
2168 float angr = ang / 180 * M_PI;
2169 float latr = pp[1] * M_PI / 180;
2171 float sD = sinf(D), cD = cosf(D);
2172 float sy = sinf(latr), cy = cosf(latr);
2173 float sa = sinf(angr), ca = cosf(angr);
2175 p[0] = pp[0] + asinf(sa * sD / cy) * 180 / M_PI;
2176 p[1] = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
2179 GetCanvasPixLL(vp, &ps, p[1], p[0]);
2181 n.m_Screen[0] = ps.x;
2182 n.m_Screen[1] = ps.y;
2184 wxColor c = GetGraphicColor(settings, vkn);
2186 n.m_Color[0] = c.Red();
2187 n.m_Color[1] = c.Green();
2188 n.m_Color[2] = c.Blue();
2194 m_bUpdateParticles =
false;
2196 int total_particles = density * pGRX->
getNi() * pGRX->
getNj();
2199 if (total_particles > 60000) total_particles = 60000;
2202 int remove_particles = ((int)particles.size() - total_particles) / 16;
2203 for (
int i = 0; i < remove_particles; i++) particles.pop_back();
2207 int new_particles = (total_particles - (int)particles.size()) / 64;
2209 for (
int npi = 0; npi < new_particles; npi++) {
2212 for (
int i = 0; i < 20; i++) {
2215 (float)rand() / RAND_MAX * (pGRX->getLonMax() - pGRX->getLonMin()) +
2218 (float)rand() / RAND_MAX * (pGRX->getLatMax() - pGRX->getLatMin()) +
2222 vkn > 0 && vkn < 100)
2223 vkn = m_Settings.CalibrateValue(settings, vkn);
2229 if (settings != GribOverlaySettings::CURRENT || vkn > 1 - (
double)i / 20)
2235 np.m_HistoryPos = 0;
2236 np.m_HistorySize = 1;
2238 if (run == run_count) run = 0;
2240 memcpy(np.m_History[np.m_HistoryPos].m_Pos, p,
sizeof p);
2243 GetCanvasPixLL(vp, &ps, p[1], p[0]);
2244 np.m_History[np.m_HistoryPos].m_Screen[0] = ps.x;
2245 np.m_History[np.m_HistoryPos].m_Screen[1] = ps.y;
2247 wxColour c = GetGraphicColor(settings, vkn);
2248 np.m_History[np.m_HistoryPos].m_Color[0] = c.Red();
2249 np.m_History[np.m_HistoryPos].m_Color[1] = c.Green();
2250 np.m_History[np.m_HistoryPos].m_Color[2] = c.Blue();
2252 particles.push_back(np);
2258 glEnable(GL_LINE_SMOOTH);
2260 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2261 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
2266 unsigned char *&ca = m_ParticleMap->color_array;
2267 float *&va = m_ParticleMap->vertex_array;
2268 float *&caf = m_ParticleMap->color_float_array;
2270 if (m_ParticleMap->array_size < particles.size() && !m_pdc) {
2271 m_ParticleMap->array_size = 2 * particles.size();
2277 new unsigned char[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 8];
2278 caf =
new float[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 8];
2279 va =
new float[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 4];
2283 for (std::vector<Particle>::iterator it = particles.begin();
2284 it != particles.end(); it++) {
2285 wxUint8 alpha = 250;
2287 int i = it->m_HistoryPos;
2289 bool lip_valid =
false;
2290 float *lp =
nullptr, lip[2];
2295 float(&dp)[2] = it->m_History[i].m_Pos;
2296 if (dp[0] != -10000) {
2297 float(&sp)[2] = it->m_History[i].m_Screen;
2298 wxUint8(&ci)[3] = it->m_History[i].m_Color;
2300 wxUint8 c[4] = {ci[0], ci[1], (
unsigned char)(ci[2] + 240 - alpha / 2),
2303 cf[0] = ci[0] / 256.;
2304 cf[1] = ci[1] / 256.;
2305 cf[2] = ((
unsigned char)(ci[2] + 240 - alpha / 2)) / 256.;
2306 cf[3] = alpha / 256.;
2308 if (lp && fabsf(lp[0] - sp[0]) < vp->pix_width) {
2313 float d = (float)it->m_Run / run_count;
2314 for (
int j = 0; j < 2; j++) sip[j] = d * lp[j] + (1 - d) * sp[j];
2316 if (lip_valid && fabsf(lip[0] - sip[0]) < vp->pix_width) {
2318 m_pdc->SetPen(wxPen(wxColour(c[0], c[1], c[2]), 2));
2319 m_pdc->DrawLine(sip[0], sip[1], lip[0], lip[1]);
2321 memcpy(ca + 4 * cnt, c,
sizeof lc);
2322 memcpy(caf + 4 * cnt, cf,
sizeof lcf);
2323 memcpy(va + 2 * cnt, lip,
sizeof sp);
2325 memcpy(ca + 4 * cnt, lc,
sizeof c);
2326 memcpy(caf + 4 * cnt, lcf,
sizeof cf);
2327 memcpy(va + 2 * cnt, sip,
sizeof sp);
2332 memcpy(lip, sip,
sizeof lip);
2336 memcpy(lc, c,
sizeof lc);
2337 memcpy(lcf, cf,
sizeof lcf);
2343 i = history_size - 1;
2344 if (i >= it->m_HistorySize)
break;
2347 if (i == it->m_HistoryPos)
break;
2349 alpha -= 240 / history_size;
2355 m_oDC->DrawGLLineArray(cnt, va, caf, ca,
false);
2364 if (!m_pdc) glFlush();
2367 int time = sw.Time();
2371 m_tParticleTimer.Start(wxMax(50 - time, 2 * time), wxTIMER_ONE_SHOT);
2374 static int total_time;
2376 static int total_count;
2377 if(++total_count == 100) {
2378 printf(
"time: %.2f\n", (
double)total_time / total_count);
2379 total_time = total_count = 0;
2384void GRIBOverlayFactory::OnParticleTimer(wxTimerEvent &event) {
2385 m_bUpdateParticles =
true;
2388 if (GetCanvasCount() > 1)
2389 GetCanvasByIndex(1)->Refresh(
false);
2391 GetOCPNCanvasWindow()->Refresh(
false);
2394void GRIBOverlayFactory::DrawProjectedPosition(
int x,
int y) {
2397 dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
2398 dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
2399 dc.DrawRectangle(x, y, 20, 20);
2400 dc.DrawLine(x, y, x + 20, y + 20);
2401 dc.DrawLine(x, y + 20, x + 20, y);
2404 m_oDC->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
2405 m_oDC->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
2406 m_oDC->DrawRectangle(x - 10, y - 10, 20, 20);
2407 m_oDC->StrokeLine(x - 10, y - 10, x + 10, y + 10);
2408 m_oDC->StrokeLine(x - 10, y + 10, x + 10, y - 10);
2413void GRIBOverlayFactory::DrawMessageWindow(wxString msg,
int x,
int y,
2415 if (msg.empty())
return;
2417 int ScaleBare_H = 30;
2422 dc.SetPen(*wxTRANSPARENT_PEN);
2424 dc.SetBrush(wxColour(243, 229, 47));
2426 dc.GetMultiLineTextExtent(msg, &w, &h);
2428 int yp = y - (ScaleBare_H + GetChartbarHeight() + h);
2430 int label_offset = 10;
2431 int wdraw = w + (label_offset * 2);
2432 dc.DrawRectangle(0, yp, wdraw, h);
2433 dc.DrawLabel(msg, wxRect(label_offset, yp, wdraw, h),
2434 wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
2437 m_oDC->SetFont(*mfont);
2438 m_oDC->SetPen(*wxTRANSPARENT_PEN);
2440 m_oDC->SetBrush(wxColour(243, 229, 47));
2442 m_oDC->GetTextExtent(msg, &w, &h);
2445 int label_offset = 10;
2446 int wdraw = w + (label_offset * 2);
2447 wdraw *= g_ContentScaleFactor;
2448 h *= g_ContentScaleFactor;
2449 int yp = y - (ScaleBare_H + GetChartbarHeight() + h);
2451 m_oDC->DrawRectangle(0, yp, wdraw, h);
2452 m_oDC->DrawText(msg, label_offset, yp);
2481void GRIBOverlayFactory::drawDoubleArrow(
int x,
int y,
double ang,
2482 wxColour arrowColor,
int arrowWidth,
2483 int arrowSizeIdx,
double scale) {
2485 wxPen pen(arrowColor, 2);
2487 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2488#if wxUSE_GRAPHICS_CONTEXT
2489 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2493 wxPen pen(arrowColor, arrowWidth);
2498 drawLineBuffer(m_DoubleArrow[arrowSizeIdx], x, y, ang,
scale);
2501void GRIBOverlayFactory::drawSingleArrow(
int x,
int y,
double ang,
2502 wxColour arrowColor,
int arrowWidth,
2503 int arrowSizeIdx,
double scale) {
2505 wxPen pen(arrowColor, arrowWidth);
2507 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2508#if wxUSE_GRAPHICS_CONTEXT
2509 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2513 wxPen pen(arrowColor, arrowWidth);
2518 drawLineBuffer(m_SingleArrow[arrowSizeIdx], x, y, ang,
scale);
2521void GRIBOverlayFactory::drawWindArrowWithBarbs(
int settings,
int x,
int y,
2522 double vkn,
double ang,
2523 bool south, wxColour arrowColor,
2524 double rotate_angle) {
2525 if (m_Settings.Settings[settings].m_iBarbedColour == 1)
2526 arrowColor = GetGraphicColor(settings, vkn);
2532 float penWidth = .6 / m_pixelMM;
2534 float penWidth = .4 / m_pixelMM;
2536 penWidth = wxMin(penWidth, 3.0);
2539 wxPen pen(arrowColor, 2);
2541 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2543#if wxUSE_GRAPHICS_CONTEXT
2544 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2550 wxPen pen(arrowColor, penWidth);
2566 cacheidx = (int)(vkn + 2.5) / 5;
2568 cacheidx = (int)(vkn + 5) / 10 + 4;
2572 ang += rotate_angle;
2574 drawLineBuffer(m_WindArrowCache[cacheidx], x, y, ang, 1.0, south,
2575 m_bDrawBarbedArrowHead);
2578void GRIBOverlayFactory::drawLineBuffer(
LineBuffer &buffer,
int x,
int y,
2579 double ang,
double scale,
bool south,
2582 float six = sinf(ang), cox = cosf(ang), siy, coy;
2584 siy = -six, coy = -cox;
2586 siy = six, coy = cox;
2589 int count = buffer.count;
2594 wxASSERT(
sizeof vertexes /
sizeof *vertexes >= (
unsigned)count * 4);
2595 for (
int i = 0; i < 2 * count; i++) {
2597 if (!head && i > 1) j += 4;
2598 float *k = buffer.lines + 2 * j;
2599 vertexes[2 * i + 0] = k[0] * cox *
scale + k[1] * siy *
scale + x;
2600 vertexes[2 * i + 1] = k[0] * six *
scale - k[1] * coy *
scale + y;
2604 for (
int i = 0; i < count; i++) {
2605 float *l = vertexes + 4 * i;
2606#if wxUSE_GRAPHICS_CONTEXT
2607 if (m_hiDefGraphics && m_gdc)
2608 m_gdc->StrokeLine(l[0], l[1], l[2], l[3]);
2611 m_pdc->DrawLine(l[0], l[1], l[2], l[3]);
2616 for (
int i = 0; i < count; i++) {
2617 float *l = vertexes + 4 * i;
2618 if (m_hiDefGraphics)
2619 m_oDC->StrokeLine(l[0], l[1], l[2], l[3]);
2621 m_oDC->DrawLine(l[0], l[1], l[2], l[3]);
2636 double uv[],
double x,
double y,
2637 double width,
double height) {
2640 glEnable(texture_format);
2643 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2645 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
2650 coords[1] = -height;
2652 coords[3] = -height;
2658 extern int pi_texture_2D_shader_program;
2659 glUseProgram(pi_texture_2D_shader_program);
2662 GLint mPosAttrib = glGetAttribLocation(pi_texture_2D_shader_program,
"aPos");
2663 GLint mUvAttrib = glGetAttribLocation(pi_texture_2D_shader_program,
"aUV");
2666 GLint texUni = glGetUniformLocation(pi_texture_2D_shader_program,
"uTex");
2667 glUniform1i(texUni, 0);
2670 glBindBuffer(GL_ARRAY_BUFFER, 0);
2671 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2674 glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
2676 glEnableVertexAttribArray(mPosAttrib);
2679 glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv);
2681 glEnableVertexAttribArray(mUvAttrib);
2687 mat4x4_rotate_Z(Q, I, angle);
2694 glGetUniformLocation(pi_texture_2D_shader_program,
"TransformMatrix");
2695 glUniformMatrix4fv(matloc, 1, GL_FALSE, (
const GLfloat *)Q);
2698 glActiveTexture(GL_TEXTURE0);
2705 GLushort indices1[] = {0,1,3,2};
2706 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
2729 glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
2730 glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
2732 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
2734 glDisable(GL_BLEND);
2735 glDisable(texture_format);
2739 glUniformMatrix4fv(matloc, 1, GL_FALSE, (
const GLfloat *)I);
2745 glColor4f(1, 1, 1, 1);
2746 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
2748 if (texture_format != GL_TEXTURE_2D) {
2749 for (
int i = 0; i < 4; i++) {
2750 uv[i * 2] *= pGR->
getNi();
2751 uv[(i * 2) + 1] *= pGR->
getNj();
2756 glTexCoord2d(uv[0], uv[1]), glVertex2f(x - width, y - height);
2757 glTexCoord2d(uv[2], uv[3]), glVertex2f(x, y - height);
2758 glTexCoord2d(uv[4], uv[5]), glVertex2f(x, y);
2759 glTexCoord2d(uv[6], uv[7]), glVertex2f(x - width, y);
2767 glEnable(texture_format);
2768 glBindTexture(texture_format, pGO->m_iTexture);
2771 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2773 double lat_min = pGR->getLatMin(), lon_min = pGR->getLonMin();
2775 bool repeat = pGR->getLonMin() == 0 && pGR->getLonMax() + pGR->
getDi() == 360;
2785 double pw = vp->view_scale_ppm * 1e6 / (pow(2, fabs(vp->clat) / 25));
2789 int xsquares = ceil(vp->pix_width / pw), ysquares = ceil(vp->pix_height / pw);
2792 if (vp->rotation == 0 && vp->m_projection_type == PI_PROJECTION_MERCATOR)
2798 xsquares = wxMax(xsquares, 2);
2799 ysquares = wxMax(ysquares, 2);
2802 double xs = vp->pix_width / double(xsquares),
2803 ys = vp->pix_height / double(ysquares);
2805 typedef double mx[2][2];
2807 mx *lva =
new mx[xsquares + 1];
2808 int tw = pGO->m_iTextureDim[0], th = pGO->m_iTextureDim[1];
2809 double latstep = fabs(pGR->
getDj()) / (th - 2 - 1) * (pGR->
getNj() - 1);
2810 double lonstep = pGR->
getDi() / (tw - 2 * !repeat - 1) * (pGR->
getNi() - 1);
2812 double potNormX = (double)pGO->m_iTexDataDim[0] / tw;
2813 double potNormY = (double)pGO->m_iTexDataDim[1] / th;
2815 double clon = (lon_min + pGR->getLonMax()) / 2;
2817 for (
double y = 0; y < vp->pix_height + ys / 2; y += ys) {
2820 for (
double x = 0; x < vp->pix_width + xs / 2; x += xs) {
2823 GetCanvasLLPix(vp, p, &lat, &lon);
2826 if (clon - lon > 180)
2828 else if (lon - clon > 180)
2833 (((lon - lon_min) / lonstep - repeat + 1.5) / tw) * potNormX;
2834 lva[i][j][1] = (((lat - lat_min) / latstep + 1.5) / th) * potNormY;
2836 if (pGR->
getDj() < 0) lva[i][j][1] = 1 - lva[i][j][1];
2838 if (x > 0 && y > 0) {
2839 double u0 = lva[i - 1][!j][0], v0 = lva[i - 1][!j][1];
2840 double u1 = lva[i][!j][0], v1 = lva[i][!j][1];
2841 double u2 = lva[i][j][0], v2 = lva[i][j][1];
2842 double u3 = lva[i - 1][j][0], v3 = lva[i - 1][j][1];
2847 else if (u0 - u1 > .5)
2851 else if (u0 - u2 > .5)
2855 else if (u0 - u3 > .5)
2860 ((u0 >= 0 || u1 >= 0 || u2 >= 0 || u3 >= 0) &&
2861 (u0 <= 1 || u1 <= 1 || u2 <= 1 || u3 <= 1))) &&
2862 (v0 >= 0 || v1 >= 0 || v2 >= 0 || v3 >= 0) &&
2863 (v0 <= 1 || v1 <= 1 || v2 <= 1 || v3 <= 1)) {
2875 DrawSingleGLTexture(pGO, pGR, uv, x, y, xs, ys);
2886 glDisable(GL_BLEND);
2887 glDisable(texture_format);
GRIB Data Visualization and Rendering Factory.
@ Idx_COMP_REFL
Composite radar reflectivity.
@ Idx_PRECIP_TOT
Total precipitation.
@ Idx_AIR_TEMP
Air temperature at 2m.
@ Idx_PRESSURE
Surface pressure.
@ Idx_WVDIR
Wave direction.
@ Idx_CLOUD_TOT
Total cloud cover.
@ Idx_WIND_GUST
Wind gust speed at surface.
@ Idx_WIND_VX
Surface wind velocity X component.
@ Idx_HTSIGW
Significant wave height.
@ Idx_SEACURRENT_VY
Sea current velocity Y component.
@ Idx_SEA_TEMP
Sea surface temperature.
@ Idx_WIND_VY
Surface wind velocity Y component.
@ Idx_SEACURRENT_VX
Sea current velocity X component.
@ Idx_CAPE
Convective Available Potential Energy.
GRIB Weather Data Control Interface.
Container for rendered GRIB data visualizations in texture or bitmap form.
GribRecord * m_GribRecordPtrArray[Idx_COUNT]
Array of pointers to GRIB records representing different meteorological parameters.
Represents a meteorological data grid from a GRIB (Gridded Binary) file.
double getDi() const
Returns the grid spacing in longitude (i) direction in degrees.
void getXY(int i, int j, double *x, double *y) const
Converts grid indices to longitude/latitude coordinates.
double getDj() const
Returns the grid spacing in latitude (j) direction in degrees.
double getInterpolatedValue(double px, double py, bool numericalInterpolation=true, bool dir=false) const
Get spatially interpolated value at exact lat/lon position.
int getNi() const
Returns the number of points in the longitude (i) direction of the grid.
int getNj() const
Returns the number of points in the latitude (j) direction of the grid.
static bool getInterpolatedValues(double &M, double &A, const GribRecord *GRX, const GribRecord *GRY, double px, double py, bool numericalInterpolation=true)
Gets spatially interpolated wind or current vector values at a specific latitude/longitude point.
double getY(int j) const
Converts grid index j to latitude in degrees.
double getValue(int i, int j) const
Returns the data value at a specific grid point.
A specialized GribRecordSet that represents temporally interpolated weather data with isobar renderin...
wxArrayPtrVoid * m_IsobarArray[Idx_COUNT]
Array of cached isobar calculations for each data type (wind, pressure, etc).
Assembles input characters to lines.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
wxFont GetOCPNGUIScaledFont_PlugIn(wxString item)
Gets a uniquely scaled font copy for responsive UI elements.
OpenGL Platform Abstraction Layer.
Manager for particle animation system.
Individual particle for wind/current animation.
int m_Duration
Duration this particle should exist in animation cycles.