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)))
77 if (y < m_miny || y > m_maxy)
return FALSE;
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 "NOAA_GFS";
97 case NOAA_NCEP_WW3:
return "NOAA_NCEP_WW3";
98 case NOAA_NCEP_SST:
return "NOAA_NCEP_SST";
99 case NOAA_RTOFS:
return "NOAA_RTOFS";
100 case FNMOC_WW3_GLB:
return "FNMOC_WW3";
101 case FNMOC_WW3_MED:
return "FNMOC_WW3";
102 case NORWAY_METNO:
return "NORWAY_METNO";
103 default :
return "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__
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) {
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() {
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) {
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();
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(
"\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()) {
610 DrawProjectedPosition(x, y);
612 if (!m_Message_Hiden.IsEmpty()) m_Message_Hiden.Append(
"\n");
613 m_Message_Hiden.Append(m_Message);
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) {
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) {
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,
"#d90000"}, {1,
"#d92a00"}, {2,
"#d96e00"}, {3,
"#d9b200"},
959 {4,
"#d4d404"}, {5,
"#a6d906"}, {7,
"#06d9a0"}, {9,
"#00d9b0"},
960 {12,
"#00d9c0"}, {15,
"#00aed0"}, {18,
"#0083e0"}, {21,
"#0057e0"},
961 {24,
"#0000f0"}, {27,
"#0400f0"}, {30,
"#1c00f0"}, {36,
"#4800f0"},
962 {42,
"#6900f0"}, {48,
"#a000f0"}, {56,
"#f000f0"}};
965 {0,
"#00d900"}, {1,
"#2ad900"}, {2,
"#6ed900"}, {3,
"#b2d900"},
966 {4,
"#d4d400"}, {5,
"#d9a600"}, {7,
"#d90000"}, {9,
"#d90040"},
967 {12,
"#d90060"}, {15,
"#ae0080"}, {18,
"#8300a0"}, {21,
"#5700c0"},
968 {24,
"#0000d0"}, {27,
"#0400e0"}, {30,
"#0800e0"}, {36,
"#a000e0"},
969 {42,
"#c004c0"}, {48,
"#c008a0"}, {56,
"#c0a008"}};
973 {0,
"#288CFF"}, {3,
"#00AFFF"}, {6,
"#00DCE1"}, {9,
"#00F7B0"},
974 {12,
"#00EA9C"}, {15,
"#82F059"}, {18,
"#F0F503"}, {21,
"#FFED00"},
975 {24,
"#FFDB00"}, {27,
"#FFC700"}, {30,
"#FFB400"}, {33,
"#FF9800"},
976 {36,
"#FF7E00"}, {39,
"#F77800"}, {42,
"#EC7814"}, {45,
"#E4711E"},
977 {48,
"#E06128"}, {51,
"#DC5132"}, {54,
"#D5453C"}, {57,
"#CD3A46"},
978 {60,
"#BE2C50"}, {63,
"#B41A5A"}, {66,
"#AA1464"}, {70,
"#962878"},
983 {0,
"#283282"}, {5,
"#273c8c"}, {10,
"#264696"}, {14,
"#2350a0"},
984 {18,
"#1f5aaa"}, {22,
"#1a64b4"}, {26,
"#136ec8"}, {29,
"#0c78e1"},
985 {32,
"#0382e6"}, {35,
"#0091e6"}, {38,
"#009ee1"}, {41,
"#00a6dc"},
986 {44,
"#00b2d7"}, {47,
"#00bed2"}, {50,
"#28c8c8"}, {53,
"#78d2aa"},
987 {56,
"#8cdc78"}, {59,
"#a0eb5f"}, {62,
"#c8f550"}, {65,
"#f3fb02"},
988 {68,
"#ffed00"}, {71,
"#ffdd00"}, {74,
"#ffc900"}, {78,
"#ffab00"},
989 {82,
"#ff8100"}, {86,
"#f1780c"}, {90,
"#e26a23"}, {95,
"#d5453c"},
995 {-2,
"#cc04ae"}, {2,
"#8f06e4"}, {6,
"#486afa"}, {10,
"#00ffff"},
996 {15,
"#00d54b"}, {19,
"#59d800"}, {23,
"#f2fc00"}, {27,
"#ff1500"},
997 {32,
"#ff0000"}, {36,
"#d80000"}, {40,
"#a90000"}, {44,
"#870000"},
998 {48,
"#690000"}, {52,
"#550000"}, {56,
"#330000"}};
1001static ColorMap PrecipitationMap[] = {
1002 {0,
"#ffffff"}, {.01,
"#c8f0ff"}, {.02,
"#b4e6ff"}, {.05,
"#8cd3ff"},
1003 {.07,
"#78caff"}, {.1,
"#6ec1ff"}, {.2,
"#64b8ff"}, {.5,
"#50a6ff"},
1004 {.7,
"#469eff"}, {1.0,
"#3c96ff"}, {2.0,
"#328eff"}, {5.0,
"#1e7eff"},
1005 {7.0,
"#1476f0"}, {10,
"#0a6edc"}, {20,
"#0064c8"}, {50,
"#0052aa"}};
1008static ColorMap CloudMap[] = {{0,
"#ffffff"}, {1,
"#f0f0e6"}, {10,
"#e6e6dc"},
1009 {20,
"#dcdcd2"}, {30,
"#c8c8b4"}, {40,
"#aaaa8c"},
1010 {50,
"#969678"}, {60,
"#787864"}, {70,
"#646450"},
1011 {80,
"#5a5a46"}, {90,
"#505036"}};
1013static ColorMap REFCMap[] = {{0,
"#ffffff"}, {5,
"#06E8E4"}, {10,
"#009BE9"},
1014 {15,
"#0400F3"}, {20,
"#00F924"}, {25,
"#06C200"},
1015 {30,
"#009100"}, {35,
"#FAFB00"}, {40,
"#EBB608"},
1016 {45,
"#FF9400"}, {50,
"#FD0002"}, {55,
"#D70000"},
1017 {60,
"#C20300"}, {65,
"#F900FE"}, {70,
"#945AC8"}};
1020 {0,
"#0046c8"}, {5,
"#0050f0"}, {10,
"#005aff"}, {15,
"#0069ff"},
1021 {20,
"#0078ff"}, {30,
"#000cff"}, {45,
"#00a1ff"}, {60,
"#00b6fa"},
1022 {100,
"#00c9ee"}, {150,
"#00e0da"}, {200,
"#00e6b4"}, {300,
"#82e678"},
1023 {500,
"#9bff3b"}, {700,
"#ffdc00"}, {1000,
"#ffb700"}, {1500,
"#f37800"},
1024 {2000,
"#d4440c"}, {2500,
"#c8201c"}, {3000,
"#ad0430"},
1028 {0,
"#6271B7"}, {3,
"#3961A9"}, {6,
"#4A94A9"}, {9,
"#4D8D7B"},
1029 {12,
"#53A553"}, {15,
"#53A553"}, {18,
"#359F35"}, {21,
"#A79D51"},
1030 {24,
"#9F7F3A"}, {27,
"#A16C5C"}, {30,
"#A16C5C"}, {33,
"#813A4E"},
1031 {36,
"#AF5088"}, {39,
"#AF5088"}, {42,
"#754A93"}, {45,
"#754A93"},
1032 {48,
"#6D61A3"}, {51,
"#44698D"}, {54,
"#44698D"}, {57,
"#5C9098"},
1033 {60,
"#7D44A5"}, {63,
"#7D44A5"}, {66,
"#7D44A5"}, {69,
"#E7D7D7"},
1034 {72,
"#E7D7D7"}, {75,
"#E7D7D7"}, {78,
"#DBD483"}, {81,
"#DBD483"},
1035 {84,
"#DBD483"}, {87,
"#CDC470"}, {90,
"#CDC470"}, {93,
"#CDC470"},
1036 {96,
"#CDC470"}, {99,
"#808080"}};
1039static ColorMap *ColorMaps[] = {CurrentMap, GenericMap, WindMap, AirTempMap, SeaTempMap, PrecipitationMap, CloudMap};
1043 GENERIC_GRAPHIC_INDEX,
1045 AIRTEMP__GRAPHIC_INDEX,
1046 SEATEMP_GRAPHIC_INDEX,
1047 PRECIPITATION_GRAPHIC_INDEX,
1048 CLOUD_GRAPHIC_INDEX,
1049 CURRENT_GRAPHIC_INDEX,
1055static void InitColor(
ColorMap *map,
size_t maplen) {
1057 for (
size_t i = 0; i < maplen; i++) {
1060 map[i].g = c.Green();
1061 map[i].b = c.Blue();
1065void GRIBOverlayFactory::InitColorsTable() {
1066 InitColor(CurrentMap, (
sizeof CurrentMap) / (
sizeof *CurrentMap));
1067 InitColor(GenericMap, (
sizeof GenericMap) / (
sizeof *GenericMap));
1068 InitColor(WindMap, (
sizeof WindMap) / (
sizeof *WindMap));
1069 InitColor(AirTempMap, (
sizeof AirTempMap) / (
sizeof *AirTempMap));
1070 InitColor(SeaTempMap, (
sizeof SeaTempMap) / (
sizeof *SeaTempMap));
1071 InitColor(PrecipitationMap,
1072 (
sizeof PrecipitationMap) / (
sizeof *PrecipitationMap));
1073 InitColor(CloudMap, (
sizeof CloudMap) / (
sizeof *CloudMap));
1074 InitColor(CAPEMap, (
sizeof CAPEMap) / (
sizeof *CAPEMap));
1075 InitColor(REFCMap, (
sizeof REFCMap) / (
sizeof *REFCMap));
1076 InitColor(WindyMap, (
sizeof WindyMap) / (
sizeof *WindyMap));
1079void GRIBOverlayFactory::GetGraphicColor(
int settings,
double val_in,
1080 unsigned char &r,
unsigned char &g,
1082 int colormap_index = m_Settings.Settings[settings].m_iOverlayMapColors;
1087 double min = m_Settings.GetMin(settings), max = m_Settings.GetMax(settings);
1090 val_in /= max - min;
1092 switch (colormap_index) {
1093 case CURRENT_GRAPHIC_INDEX:
1095 maplen = (
sizeof CurrentMap) / (
sizeof *CurrentMap);
1097 case GENERIC_GRAPHIC_INDEX:
1099 maplen = (
sizeof GenericMap) / (
sizeof *GenericMap);
1101 case WIND_GRAPHIC_INDEX:
1103 maplen = (
sizeof WindMap) / (
sizeof *WindMap);
1105 case AIRTEMP__GRAPHIC_INDEX:
1107 maplen = (
sizeof AirTempMap) / (
sizeof *AirTempMap);
1109 case SEATEMP_GRAPHIC_INDEX:
1111 maplen = (
sizeof SeaTempMap) / (
sizeof *SeaTempMap);
1113 case PRECIPITATION_GRAPHIC_INDEX:
1114 map = PrecipitationMap;
1115 maplen = (
sizeof PrecipitationMap) / (
sizeof *PrecipitationMap);
1117 case CLOUD_GRAPHIC_INDEX:
1119 maplen = (
sizeof CloudMap) / (
sizeof *CloudMap);
1121 case CAPE_GRAPHIC_INDEX:
1123 maplen = (
sizeof CAPEMap) / (
sizeof *CAPEMap);
1125 case REFC_GRAPHIC_INDEX:
1127 maplen = (
sizeof REFCMap) / (
sizeof *REFCMap);
1129 case WINDY_GRAPHIC_INDEX:
1131 maplen = (
sizeof WindyMap) / (
sizeof *WindyMap);
1138 double cmax = map[maplen - 1].val;
1140 for (
int i = 1; i < maplen; i++) {
1141 double nmapvala = map[i - 1].val / cmax;
1142 double nmapvalb = map[i].val / cmax;
1143 if (nmapvalb > val_in || i == maplen - 1) {
1144 if (m_bGradualColors) {
1145 double d = (val_in - nmapvala) / (nmapvalb - nmapvala);
1146 r = (1 - d) * map[i - 1].r + d * map[i].r;
1147 g = (1 - d) * map[i - 1].g + d * map[i].g;
1148 b = (1 - d) * map[i - 1].b + d * map[i].b;
1160wxColour GRIBOverlayFactory::GetGraphicColor(
int settings,
double val_in) {
1161 unsigned char r, g, b;
1162 GetGraphicColor(settings, val_in, r, g, b);
1163 return wxColour(r, g, b);
1166wxString GRIBOverlayFactory::getLabelString(
double value,
int settings) {
1168 wxString f =
"%.*f";
1171 case GribOverlaySettings::PRESSURE:
1173 if (m_Settings.Settings[settings].m_Units == 2)
1175 else if (m_Settings.Settings[settings].m_Units == 0 &&
1176 m_Settings.Settings[settings].m_bAbbrIsoBarsNumbers) {
1177 value -= floor(value / 100.) * 100.;
1181 case GribOverlaySettings::WAVE:
1182 case GribOverlaySettings::CURRENT:
1183 case GribOverlaySettings::AIR_TEMPERATURE:
1184 case GribOverlaySettings::SEA_TEMPERATURE:
1187 case GribOverlaySettings::PRECIPITATION:
1188 p = value < 100. ? 2 : value < 10. ? 1 : 0;
1189 p += m_Settings.Settings[settings].m_Units == 1 ? 1 : 0;
1194 return wxString::Format(f, p, value);
1198wxImage &GRIBOverlayFactory::getLabel(
double value,
int settings,
1199 wxColour back_color) {
1200 std::map<double, wxImage>::iterator it;
1201 it = m_labelCache.find(value);
1202 if (it != m_labelCache.end())
return m_labelCache[value];
1204 wxString labels = getLabelString(value, settings);
1206 wxColour text_color;
1207 GetGlobalColor(_T (
"UBLCK" ), &text_color);
1208 wxPen penText(text_color);
1210 wxBrush backBrush(back_color);
1212 wxFont mfont(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
1213 wxFONTWEIGHT_NORMAL);
1217 sdc.GetTextExtent(labels, &w, &h,
nullptr,
nullptr, &mfont);
1219 int label_offset = 5;
1221 wxBitmap bm(w + label_offset * 2, h + 2);
1226 mdc.SetPen(penText);
1227 mdc.SetBrush(backBrush);
1228 mdc.SetTextForeground(text_color);
1229 mdc.SetTextBackground(back_color);
1234 mdc.DrawRectangle(xd, yd, w + (label_offset * 2), h + 2);
1235 mdc.DrawText(labels, label_offset + xd, yd + 1);
1237 mdc.SelectObject(wxNullBitmap);
1239 m_labelCache[value] = bm.ConvertToImage();
1241 m_labelCache[value].InitAlpha();
1243 return m_labelCache[value];
1246double square(
double x) {
return x * x; }
1248void GRIBOverlayFactory::RenderGribBarbedArrows(
int settings,
GribRecord **pGR,
1250 if (!m_Settings.Settings[settings].m_bBarbedArrows)
return;
1256 SettingsIdToGribId(settings, idx, idy, polar);
1257 if (idx < 0 || idy < 0)
return;
1262 if (!pGRX || !pGRY)
return;
1265 GetGlobalColor(_T (
"YELO2" ), &colour);
1269#ifndef __OCPN__ANDROID__
1271 glEnable(GL_LINE_SMOOTH);
1273 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1274 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1280 glEnableClientState(GL_VERTEX_ARRAY);
1284 if (m_Settings.Settings[settings].m_bBarbArrFixSpac) {
1287 adjustSpacing(m_Settings.Settings[settings].m_iBarbArrSpacing);
1289 int total_spacing = space_pixels + arrowSize;
1295 double center_lat, center_lon;
1299 wxPoint offset_point(center.x + total_spacing, center.y + total_spacing);
1300 double offset_lat, offset_lon;
1304 double lat_spacing = fabs(center_lat - offset_lat);
1305 double lon_spacing = fabs(center_lon - offset_lon);
1309 double start_lat = floor(vp->
lat_min / lat_spacing) * lat_spacing;
1310 double start_lon = floor(vp->
lon_min / lon_spacing) * lon_spacing;
1313 double end_lat = vp->
lat_max + lat_spacing;
1314 double end_lon = vp->
lon_max + lon_spacing;
1317 for (
double lat = start_lat; lat <= end_lat; lat += lat_spacing) {
1318 for (
double lon = start_lon; lon <= end_lon; lon += lon_spacing) {
1326 drawWindArrowWithBarbs(settings, p.x, p.y, vkn * 3.6 / 1.852,
1327 (ang - 90) * M_PI / 180, (lat < 0.), colour,
1334 double minspace = wxMax(m_Settings.Settings[settings].m_iBarbArrSpacing,
1335 windArrowSize * 1.2);
1336 double minspace2 = square(minspace);
1339 int imax = pGRX->
getNi();
1340 int jmax = pGRX->
getNj();
1342 wxPoint firstpx(-1000, -1000);
1343 wxPoint oldpx(-1000, -1000);
1344 wxPoint oldpy(-1000, -1000);
1346 for (
int i = 0; i < imax; i++) {
1351 pGRX->
getXY(i, pGRX->
getNj() / 2, &lonl, &latl);
1355 if (pl.x <= firstpx.x &&
1356 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1360 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) < minspace2)
continue;
1363 if (i == 0) firstpx = pl;
1366 for (
int j = 0; j < jmax; j++) {
1367 double lat = pGRX->
getY(j);
1369 if (!PointInLLBox(vp, lon, lat))
continue;
1374 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) < minspace2)
continue;
1378 if (lon > 180) lon -= 360;
1383 if (vx != GRIB_NOTDEF && vy != GRIB_NOTDEF) {
1385 vkn = sqrt(vx * vx + vy * vy);
1386 ang = atan2(vy, -vx);
1387 drawWindArrowWithBarbs(settings, p.x, p.y, vkn * 3.6 / 1.852, ang,
1395 if (!m_pdc) glDisableClientState(GL_VERTEX_ARRAY);
1399void GRIBOverlayFactory::RenderGribIsobar(
int settings,
GribRecord **pGR,
1400 wxArrayPtrVoid **pIsobarArray,
1402 if (!m_Settings.Settings[settings].m_bIsoBars)
return;
1407 SettingsIdToGribId(settings, idx, idy, polar);
1408 if (idx < 0)
return;
1410 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1414 wxColour back_color;
1415 GetGlobalColor(_T (
"DILG1" ), &back_color);
1418 if (!pIsobarArray[idx]) {
1420 if (idy >= 0 && !polar && pGR[idy]) {
1421 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1422 if (!pGRM->isOk()) {
1423 m_Message_Hiden.Append(_(
"IsoBar Unable to compute record magnitude"));
1430 pIsobarArray[idx] =
new wxArrayPtrVoid;
1433 wxGenericProgressDialog *progressdialog =
nullptr;
1434 wxDateTime start = wxDateTime::Now();
1436 double min = m_Settings.GetMin(settings);
1437 double max = m_Settings.GetMax(settings);
1440 double factor = (settings == GribOverlaySettings::PRESSURE &&
1441 m_Settings.Settings[settings].m_Units == 2)
1445 for (
double press = min; press <= max;
1446 press += (m_Settings.Settings[settings].m_iIsoBarSpacing * factor)) {
1448 progressdialog->Update(press - min);
1450 wxDateTime now = wxDateTime::Now();
1451 if ((now - start).GetSeconds() > 3 && press - min < (max - min) / 2) {
1452 progressdialog =
new wxGenericProgressDialog(
1453 _(
"Building Isobar map"), _(
"Wind"), max - min + 1,
nullptr,
1454 wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_REMAINING_TIME);
1459 m_Settings.CalibrationFactor(settings, press,
true),
1460 m_Settings.CalibrationOffset(settings), pGRA);
1462 pIsobarArray[idx]->Add(piso);
1464 delete progressdialog;
1470 for (
unsigned int i = 0; i < pIsobarArray[idx]->GetCount(); i++) {
1472 piso->drawIsoLine(
this, m_pdc, vp,
true);
1479 piso->drawIsoLineLabels(
this, m_pdc, vp, density, first,
1480 getLabel(piso->getValue(), settings, back_color));
1482 piso->drawIsoLineLabelsGL(
this, vp, density, first,
1483 getLabelString(piso->getValue(), settings),
1484 back_color, m_TexFontNumbers);
1488void GRIBOverlayFactory::FillGrid(
GribRecord *pGR) {
1490 int imax = pGR->
getNi();
1491 int jmax = pGR->
getNj();
1493 for (
int i = 0; i < imax; i++) {
1494 for (
int j = 1; j < jmax - 1; j++) {
1495 if (pGR->
getValue(i, j) == GRIB_NOTDEF) {
1498 if (pGR->
getValue(i, j - 1) != GRIB_NOTDEF) {
1502 if (pGR->
getValue(i, j + 1) != GRIB_NOTDEF) {
1506 if (div > 1) pGR->setValue(i, j, acc / div);
1511 for (
int j = 0; j < jmax; j++) {
1512 for (
int i = 1; i < imax - 1; i++) {
1513 if (pGR->
getValue(i, j) == GRIB_NOTDEF) {
1516 if (pGR->
getValue(i - 1, j) != GRIB_NOTDEF) {
1520 if (pGR->
getValue(i + 1, j) != GRIB_NOTDEF) {
1524 if (div > 1) pGR->setValue(i, j, acc / div);
1529 pGR->setFilled(
true);
1532void GRIBOverlayFactory::RenderGribDirectionArrows(
int settings,
1535 if (!m_Settings.Settings[settings].m_bDirectionArrows)
return;
1540 SettingsIdToGribId(settings, idx, idy, polar);
1541 if (idx < 0 || idy < 0)
return;
1545 if (!pGRX || !pGRY)
return;
1546 if (!pGRX->isFilled()) FillGrid(pGRX);
1547 if (!pGRY->isFilled()) FillGrid(pGRY);
1552 arrowSizeIdx = m_Settings.Settings[settings].m_iDirectionArrowSize;
1553 if (arrowSizeIdx == 0) {
1554 if (m_pixelMM > 0.2)
1557 arrowSize = 5. / m_pixelMM;
1563 GetGlobalColor(_T (
"DILG3" ), &colour);
1567 if (m_pixelMM > 0.2) {
1569 glEnable(GL_LINE_SMOOTH);
1571 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1572 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
1574 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1580 glEnableClientState(GL_VERTEX_ARRAY);
1584 if (m_Settings.Settings[settings].m_bDirArrFixSpac) {
1587 adjustSpacing(m_Settings.Settings[settings].m_iBarbArrSpacing);
1589 int total_spacing = space_pixels + arrowSize;
1595 double center_lat, center_lon;
1599 wxPoint offset_point(center.x + total_spacing, center.y + total_spacing);
1600 double offset_lat, offset_lon;
1604 double lat_spacing = fabs(center_lat - offset_lat);
1605 double lon_spacing = fabs(center_lon - offset_lon);
1609 double start_lat = floor(vp->
lat_min / lat_spacing) * lat_spacing;
1610 double start_lon = floor(vp->
lon_min / lon_spacing) * lon_spacing;
1613 double end_lat = vp->
lat_max + lat_spacing;
1614 double end_lon = vp->
lon_max + lon_spacing;
1617 for (
double lat = start_lat; lat <= end_lat; lat += lat_spacing) {
1618 for (
double lon = start_lon; lon <= end_lon; lon += lon_spacing) {
1630 if (dir == GRIB_NOTDEF || sh == GRIB_NOTDEF)
continue;
1634 scale = wxMax(1.0, sh);
1637 dir = (dir - 90) * M_PI / 180.;
1640 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1641 drawSingleArrow(p.x, p.y, dir + vp->
rotation, colour, arrowWidth,
1642 arrowSizeIdx,
scale);
1643 else if (m_Settings.Settings[settings].m_iDirectionArrowForm == 1)
1644 drawDoubleArrow(p.x, p.y, dir + vp->
rotation, colour, arrowWidth,
1645 arrowSizeIdx,
scale);
1647 drawSingleArrow(p.x, p.y, dir + vp->
rotation, colour,
1648 wxMax(1, wxMin(8, (
int)(sh + 0.5))), arrowSizeIdx,
1657 wxMax(m_Settings.Settings[settings].m_iDirArrSpacing,
1658 m_Settings.Settings[settings].m_iDirectionArrowSize * 1.2);
1659 double minspace2 = square(minspace);
1662 int imax = pGRX->
getNi();
1663 int jmax = pGRX->
getNj();
1665 wxPoint firstpx(-1000, -1000);
1666 wxPoint oldpx(-1000, -1000);
1667 wxPoint oldpy(-1000, -1000);
1669 for (
int i = 0; i < imax; i++) {
1671 pGRX->
getXY(i, pGRX->
getNj() / 2, &lonl, &latl);
1676 if (pl.x <= firstpx.x &&
1677 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1681 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) < minspace2)
continue;
1684 if (i == 0) firstpx = pl;
1686 for (
int j = 0; j < jmax; j++) {
1688 pGRX->
getXY(i, j, &lon, &lat);
1693 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) >= minspace2) {
1696 if (lon > 180) lon -= 360;
1698 if (PointInLLBox(vp, lon, lat)) {
1699 double sh, dir, wdh;
1705 if (dir == GRIB_NOTDEF || sh == GRIB_NOTDEF)
continue;
1713 wdh = (8 / 2.5 * sh) + 0.5;
1714 scale = wxMax(1.0, sh);
1717 dir = (dir - 90) * M_PI / 180.;
1720 if (m_Settings.Settings[settings].m_iDirectionArrowForm == 0)
1721 drawSingleArrow(p.x, p.y, dir + vp->
rotation, colour, arrowWidth,
1722 arrowSizeIdx,
scale);
1723 else if (m_Settings.Settings[settings].m_iDirectionArrowForm == 1)
1724 drawDoubleArrow(p.x, p.y, dir + vp->
rotation, colour, arrowWidth,
1725 arrowSizeIdx,
scale);
1727 drawSingleArrow(p.x, p.y, dir + vp->
rotation, colour,
1728 wxMax(1, wxMin(8, (
int)wdh)), arrowSizeIdx,
1737 if (!m_pdc) glDisableClientState(GL_VERTEX_ARRAY);
1741void GRIBOverlayFactory::RenderGribOverlayMap(
int settings,
GribRecord **pGR,
1743 if (!m_Settings.Settings[settings].m_bOverlayMap)
return;
1745 const int grib_pixel_size = 4;
1748 SettingsIdToGribId(settings, idx, idy, polar);
1749 if (idx < 0 || !pGR[idx])
return;
1751 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1754 if (idy >= 0 && !polar && pGR[idy]) {
1755 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1756 if (!pGRM->isOk()) {
1757 m_Message_Hiden.Append(
1758 _(
"OverlayMap Unable to compute record magnitude"));
1765 if (!pGRA->isFilled()) FillGrid(pGRA);
1773 if (Intersect(vp, pGRA->getLatMin(), pGRA->getLatMax(), pGRA->getLonMin(),
1774 pGRA->getLonMax(), 0.) != _GOUT)
1776 if (Intersect(vp, pGRA->getLatMin(), pGRA->getLatMax(),
1777 pGRA->getLonMin() - 360., pGRA->getLonMax() - 360.,
1783 if (!m_pOverlay[settings]) m_pOverlay[settings] =
new GribOverlay;
1791 texture_format = GL_TEXTURE_2D;
1793 if (!texture_format)
1795 m_Message_Hiden.Append(
1796 _(
"Overlays not supported by this graphics hardware (Disable "
1799 if (!pGO->m_iTexture) CreateGribGLTexture(pGO, settings, pGRA);
1801 if (pGO->m_iTexture)
1802 DrawGLTexture(pGO, pGRA, vp);
1804 m_Message_Hiden.IsEmpty()
1806 .Append(_(
"Overlays too wide and can't be displayed:"))
1808 .Append(GribOverlaySettings::NameFromIndex(settings))
1809 : m_Message_Hiden.Append(
",").Append(
1810 GribOverlaySettings::NameFromIndex(settings));
1816 m_Message_Hiden.Append(_(
1817 "overlays suppressed if not north-up in DC mode (enable OpenGL)"));
1819 if (!pGO->m_pDCBitmap) {
1821 CreateGribImage(settings, pGRA, vp, grib_pixel_size, porg);
1822 if (bl_image.IsOk()) {
1824 pGO->m_pDCBitmap =
new wxBitmap(bl_image);
1826 new wxMask(*(pGO->m_pDCBitmap), wxColour(0, 0, 0));
1827 pGO->m_pDCBitmap->SetMask(gr_mask);
1831 if (pGO->m_pDCBitmap)
1832 m_pdc->DrawBitmap(*(pGO->m_pDCBitmap), porg.x, porg.y,
true);
1834 m_Message_Hiden.IsEmpty()
1837 "Please Zoom or Scale Out to view invisible overlays:"))
1839 .Append(GribOverlaySettings::NameFromIndex(settings))
1840 : m_Message_Hiden.Append(
",").Append(
1841 GribOverlaySettings::NameFromIndex(settings));
1849void GRIBOverlayFactory::RenderGribNumbers(
int settings,
GribRecord **pGR,
1851 if (!m_Settings.Settings[settings].m_bNumbers)
return;
1856 SettingsIdToGribId(settings, idx, idy, polar);
1857 if (idx < 0)
return;
1859 GribRecord *pGRA = pGR[idx], *pGRM =
nullptr;
1864 if (idy >= 0 && !polar && pGR[idy]) {
1865 pGRM = GribRecord::MagnitudeRecord(*pGR[idx], *pGR[idy]);
1866 if (!pGRM->isOk()) {
1867 m_Message_Hiden.Append(
1868 _(
"GribNumbers Unable to compute record magnitude"));
1877 m_TexFontNumbers.GetTextExtent(wxString(
"1234"), &wstring,
nullptr);
1879 if (m_Settings.Settings[settings].m_bNumFixSpac) {
1882 int space = adjustSpacing(m_Settings.Settings[settings].m_iNumbersSpacing);
1892 if (ptl.x >= pbr.x) {
1895 pbr.x = m_ParentSize.GetWidth();
1898 for (
int i = wxMax(ptl.x, 0); i < wxMin(pbr.x, m_ParentSize.GetWidth());
1899 i += (space + wstring)) {
1900 for (
int j = wxMax(ptl.y, 0); j < wxMin(pbr.y, m_ParentSize.GetHeight());
1901 j += (space + wstring)) {
1902 double lat, lon, val;
1905 if (val != GRIB_NOTDEF) {
1906 double value = m_Settings.CalibrateValue(settings, val);
1907 wxColour back_color = GetGraphicColor(settings, value);
1909 DrawNumbers(wxPoint(i, j), value, settings, back_color);
1916 wxMax(m_Settings.Settings[settings].m_iNumbersSpacing, wstring * 1.2);
1917 double minspace2 = square(minspace);
1920 int imax = pGRA->
getNi();
1921 int jmax = pGRA->
getNj();
1923 wxPoint firstpx(-1000, -1000);
1924 wxPoint oldpx(-1000, -1000);
1925 wxPoint oldpy(-1000, -1000);
1927 for (
int i = 0; i < imax; i++) {
1929 pGRA->
getXY(i, pGRA->
getNj() / 2, &lonl, &latl);
1934 if (pl.x <= firstpx.x &&
1935 square(pl.x - firstpx.x) + square(pl.y - firstpx.y) <
1939 if (square(pl.x - oldpx.x) + square(pl.y - oldpx.y) >= minspace2) {
1941 if (i == 0) firstpx = pl;
1943 for (
int j = 0; j < jmax; j++) {
1945 pGRA->
getXY(i, j, &lon, &lat);
1950 if (square(p.x - oldpy.x) + square(p.y - oldpy.y) >= minspace2) {
1953 if (lon > 180) lon -= 360;
1955 if (PointInLLBox(vp, lon, lat)) {
1958 if (mag != GRIB_NOTDEF) {
1959 double value = m_Settings.CalibrateValue(settings, mag);
1960 wxColour back_color = GetGraphicColor(settings, value);
1962 DrawNumbers(p, value, settings, back_color);
1974void GRIBOverlayFactory::DrawNumbers(wxPoint p,
double value,
int settings,
1975 wxColour back_color) {
1977 wxImage &label = getLabel(value, settings, back_color);
1979 int w = label.GetWidth(), h = label.GetHeight();
1980 for (
int y = 0; y < h; y++)
1981 for (
int x = 0; x < w; x++)
1982 label.SetAlpha(x, y, m_Settings.m_iOverlayTransparency);
1984 m_pdc->DrawBitmap(label, p.x, p.y,
true);
1990 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1991 glColor4ub(back_color.Red(), back_color.Green(), back_color.Blue(),
1992 m_Settings.m_iOverlayTransparency);
1996 wxString label = getLabelString(value, settings);
1998 m_TexFontNumbers.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;
2007 glVertex2i(x + w, y);
2008 glVertex2i(x + w, y + h);
2009 glVertex2i(x, y + h);
2012 glColor4ub(0, 0, 0, m_Settings.m_iOverlayTransparency);
2014 glBegin(GL_LINE_LOOP);
2016 glVertex2i(x + w, y);
2017 glVertex2i(x + w, y + h);
2018 glVertex2i(x, y + h);
2021 glEnable(GL_TEXTURE_2D);
2022 m_TexFontNumbers.RenderString(label, p.x, p.y);
2023 glDisable(GL_TEXTURE_2D);
2029 wxFont font(9, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL,
2030 wxFONTWEIGHT_NORMAL);
2033 wxString label = getLabelString(value, settings);
2035 m_oDC->SetFont(font);
2037 m_oDC->GetTextExtent(label, &w, &h);
2039 int label_offsetx = 5, label_offsety = 1;
2040 int x = p.x - label_offsetx, y = p.y - label_offsety;
2041 w += 2 * label_offsetx, h += 2 * label_offsety;
2043 m_oDC->SetBrush(wxBrush(back_color));
2044 m_oDC->DrawRoundedRectangle(x, y, w, h, 0);
2047 m_oDC->SetPen(wxPen(wxColour(0, 0, 0), 1));
2048 m_oDC->DrawLine(x, y, x + w, y);
2049 m_oDC->DrawLine(x + w, y, x + w, y + h);
2050 m_oDC->DrawLine(x + w, y + h, x, y + h);
2051 m_oDC->DrawLine(x, y + h, x, y);
2053 m_oDC->DrawText(label, p.x, p.y);
2060void GRIBOverlayFactory::RenderGribParticles(
int settings,
GribRecord **pGR,
2062 if (!m_Settings.Settings[settings].m_bParticles)
return;
2068 SettingsIdToGribId(settings, idx, idy, polar);
2069 if (idx < 0 || idy < 0)
return;
2074 if (!pGRX || !pGRY)
return;
2079 if (m_ParticleMap && m_ParticleMap->m_Setting != settings) ClearParticles();
2081 if (!m_ParticleMap) m_ParticleMap =
new ParticleMap(settings);
2083 std::vector<Particle> &particles = m_ParticleMap->m_Particles;
2085 const int max_duration = 50;
2086 const int run_count = 6;
2088 double density = m_Settings.Settings[settings].m_dParticleDensity;
2091 int history_size = 27 / sqrt(density);
2092 history_size = wxMin(history_size, MAX_PARTICLE_HISTORY);
2094 std::vector<Particle>::iterator it;
2096 if (m_ParticleMap->history_size != history_size) {
2097 for (
unsigned int i = 0; i < particles.size(); i++) {
2099 if (m_ParticleMap->history_size > history_size &&
2100 it.m_HistoryPos >= history_size) {
2101 it = particles[particles.size() - 1];
2102 particles.pop_back();
2107 it.m_HistorySize = it.m_HistoryPos + 1;
2109 m_ParticleMap->history_size = history_size;
2117 for (it = particles.begin(); it != particles.end(); it++)
2118 for (
int i = 0; i < it->m_HistorySize; i++) {
2120 float(&p)[2] = n.m_Pos;
2121 if (p[0] == -10000)
continue;
2125 n.m_Screen[0] = ps.x;
2126 n.m_Screen[1] = ps.y;
2138 for (it = particles.begin(); it != particles.end(); it++)
2139 for (
int i = 0; i < it->m_HistorySize; i++) {
2141 float(&p)[2] = n.m_Pos;
2142 if (p[0] == -10000)
continue;
2144 n.m_Screen[0] += p1.x;
2145 n.m_Screen[1] += p1.y;
2153 if (m_bUpdateParticles) {
2154 for (
unsigned int i = 0; i < particles.size(); i++) {
2158 if (++it.m_Run < run_count)
continue;
2163 it = particles[particles.size() - 1];
2164 particles.pop_back();
2171 float(&pp)[2] = it.m_History[it.m_HistoryPos].m_Pos;
2174 if (++it.m_HistorySize > history_size) it.m_HistorySize = history_size;
2176 if (++it.m_HistoryPos >= history_size) it.m_HistoryPos = 0;
2179 float(&p)[2] = n.m_Pos;
2180 double vkn = 0, ang;
2182 if (it.
m_Duration < max_duration - history_size &&
2185 vkn > 0 && vkn < 100) {
2186 vkn = m_Settings.CalibrateValue(settings, vkn);
2188 if (settings == GribOverlaySettings::CURRENT)
2189 d = vkn * run_count;
2191 d = vkn * run_count / 4;
2203 float angr = ang / 180 * M_PI;
2204 p[0] = pp[0] + sinf(angr) * d / 60;
2205 p[1] = pp[1] + cosf(angr) * d / 60;
2207 float angr = ang / 180 * M_PI;
2208 float latr = pp[1] * M_PI / 180;
2210 float sD = sinf(D), cD = cosf(D);
2211 float sy = sinf(latr), cy = cosf(latr);
2212 float sa = sinf(angr), ca = cosf(angr);
2214 p[0] = pp[0] + asinf(sa * sD / cy) * 180 / M_PI;
2215 p[1] = asinf(sy * cD + cy * sD * ca) * 180 / M_PI;
2220 n.m_Screen[0] = ps.x;
2221 n.m_Screen[1] = ps.y;
2223 wxColor c = GetGraphicColor(settings, vkn);
2225 n.m_Color[0] = c.Red();
2226 n.m_Color[1] = c.Green();
2227 n.m_Color[2] = c.Blue();
2233 m_bUpdateParticles =
false;
2235 int total_particles = density * pGRX->
getNi() * pGRX->
getNj();
2238 if (total_particles > 60000) total_particles = 60000;
2241 int remove_particles = ((int)particles.size() - total_particles) / 16;
2242 for (
int i = 0; i < remove_particles; i++) particles.pop_back();
2246 int new_particles = (total_particles - (int)particles.size()) / 64;
2248 for (
int npi = 0; npi < new_particles; npi++) {
2251 for (
int i = 0; i < 20; i++) {
2253 p[0] =
static_cast<float>(rand()) /
static_cast<float>(RAND_MAX) *
2254 (pGRX->getLonMax() - pGRX->getLonMin()) +
2256 p[1] =
static_cast<float>(rand()) /
static_cast<float>(RAND_MAX) *
2257 (pGRX->getLatMax() - pGRX->getLatMin()) +
2261 vkn > 0 && vkn < 100)
2262 vkn = m_Settings.CalibrateValue(settings, vkn);
2268 if (settings != GribOverlaySettings::CURRENT || vkn > 1 - (
double)i / 20)
2274 np.m_HistoryPos = 0;
2275 np.m_HistorySize = 1;
2277 if (run == run_count) run = 0;
2279 memcpy(np.m_History[np.m_HistoryPos].m_Pos, p,
sizeof p);
2283 np.m_History[np.m_HistoryPos].m_Screen[0] = ps.x;
2284 np.m_History[np.m_HistoryPos].m_Screen[1] = ps.y;
2286 wxColour c = GetGraphicColor(settings, vkn);
2287 np.m_History[np.m_HistoryPos].m_Color[0] = c.Red();
2288 np.m_History[np.m_HistoryPos].m_Color[1] = c.Green();
2289 np.m_History[np.m_HistoryPos].m_Color[2] = c.Blue();
2291 particles.push_back(np);
2297 glEnable(GL_LINE_SMOOTH);
2299 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2300 glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
2305 unsigned char *&ca = m_ParticleMap->color_array;
2306 float *&va = m_ParticleMap->vertex_array;
2307 float *&caf = m_ParticleMap->color_float_array;
2309 if (m_ParticleMap->array_size < particles.size() && !m_pdc) {
2310 m_ParticleMap->array_size = 2 * particles.size();
2316 new unsigned char[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 8];
2317 caf =
new float[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 8];
2318 va =
new float[m_ParticleMap->array_size * MAX_PARTICLE_HISTORY * 4];
2322 for (std::vector<Particle>::iterator it = particles.begin();
2323 it != particles.end(); it++) {
2324 wxUint8 alpha = 250;
2326 int i = it->m_HistoryPos;
2328 bool lip_valid =
false;
2329 float *lp =
nullptr, lip[2];
2334 float(&dp)[2] = it->m_History[i].m_Pos;
2335 if (dp[0] != -10000) {
2336 float(&sp)[2] = it->m_History[i].m_Screen;
2337 wxUint8(&ci)[3] = it->m_History[i].m_Color;
2339 wxUint8 c[4] = {ci[0], ci[1], (
unsigned char)(ci[2] + 240 - alpha / 2),
2342 cf[0] = ci[0] / 256.;
2343 cf[1] = ci[1] / 256.;
2344 cf[2] = ((
unsigned char)(ci[2] + 240 - alpha / 2)) / 256.;
2345 cf[3] = alpha / 256.;
2347 if (lp && fabsf(lp[0] - sp[0]) < vp->
pix_width) {
2352 float d = (float)it->m_Run / run_count;
2353 for (
int j = 0; j < 2; j++) sip[j] = d * lp[j] + (1 - d) * sp[j];
2355 if (lip_valid && fabsf(lip[0] - sip[0]) < vp->
pix_width) {
2357 m_pdc->SetPen(wxPen(wxColour(c[0], c[1], c[2]), 2));
2358 m_pdc->DrawLine(sip[0], sip[1], lip[0], lip[1]);
2360 memcpy(ca + 4 * cnt, c,
sizeof lc);
2361 memcpy(caf + 4 * cnt, cf,
sizeof lcf);
2362 memcpy(va + 2 * cnt, lip,
sizeof sp);
2364 memcpy(ca + 4 * cnt, lc,
sizeof c);
2365 memcpy(caf + 4 * cnt, lcf,
sizeof cf);
2366 memcpy(va + 2 * cnt, sip,
sizeof sp);
2371 memcpy(lip, sip,
sizeof lip);
2375 memcpy(lc, c,
sizeof lc);
2376 memcpy(lcf, cf,
sizeof lcf);
2382 i = history_size - 1;
2383 if (i >= it->m_HistorySize)
break;
2386 if (i == it->m_HistoryPos)
break;
2388 alpha -= 240 / history_size;
2394 m_oDC->DrawGLLineArray(cnt, va, caf, ca,
false);
2403 if (!m_pdc) glFlush();
2406 int time = sw.Time();
2410 m_tParticleTimer.Start(wxMax(50 - time, 2 * time), wxTIMER_ONE_SHOT);
2413 static int total_time;
2415 static int total_count;
2416 if(++total_count == 100) {
2417 printf(
"time: %.2f\n", (
double)total_time / total_count);
2418 total_time = total_count = 0;
2423void GRIBOverlayFactory::OnParticleTimer(wxTimerEvent &event) {
2424 m_bUpdateParticles =
true;
2433void GRIBOverlayFactory::DrawProjectedPosition(
int x,
int y) {
2436 dc.SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
2437 dc.SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
2438 dc.DrawRectangle(x, y, 20, 20);
2439 dc.DrawLine(x, y, x + 20, y + 20);
2440 dc.DrawLine(x, y + 20, x + 20, y);
2443 m_oDC->SetPen(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
2444 m_oDC->SetBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
2445 m_oDC->DrawRectangle(x - 10, y - 10, 20, 20);
2446 m_oDC->StrokeLine(x - 10, y - 10, x + 10, y + 10);
2447 m_oDC->StrokeLine(x - 10, y + 10, x + 10, y - 10);
2452void GRIBOverlayFactory::DrawMessageWindow(wxString msg,
int x,
int y,
2454 if (msg.empty())
return;
2456 int ScaleBare_H = 30;
2461 dc.SetPen(*wxTRANSPARENT_PEN);
2463 dc.SetBrush(wxColour(243, 229, 47));
2465 dc.GetMultiLineTextExtent(msg, &w, &h);
2469 int label_offset = 10;
2470 int wdraw = w + (label_offset * 2);
2471 dc.DrawRectangle(0, yp, wdraw, h);
2472 dc.DrawLabel(msg, wxRect(label_offset, yp, wdraw, h),
2473 wxALIGN_LEFT | wxALIGN_CENTRE_VERTICAL);
2476 m_oDC->SetFont(*mfont);
2477 m_oDC->SetPen(*wxTRANSPARENT_PEN);
2479 m_oDC->SetBrush(wxColour(243, 229, 47));
2481 m_oDC->GetTextExtent(msg, &w, &h);
2484 int label_offset = 10;
2485 int wdraw = w + (label_offset * 2);
2486 wdraw *= g_ContentScaleFactor;
2487 h *= g_ContentScaleFactor;
2490 m_oDC->DrawRectangle(0, yp, wdraw, h);
2491 m_oDC->DrawText(msg, label_offset, yp);
2520void GRIBOverlayFactory::drawDoubleArrow(
int x,
int y,
double ang,
2521 wxColour arrowColor,
int arrowWidth,
2522 int arrowSizeIdx,
double scale) {
2524 wxPen pen(arrowColor, 2);
2526 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2527#if wxUSE_GRAPHICS_CONTEXT
2528 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2532 wxPen pen(arrowColor, arrowWidth);
2537 drawLineBuffer(m_DoubleArrow[arrowSizeIdx], x, y, ang,
scale);
2540void GRIBOverlayFactory::drawSingleArrow(
int x,
int y,
double ang,
2541 wxColour arrowColor,
int arrowWidth,
2542 int arrowSizeIdx,
double scale) {
2544 wxPen pen(arrowColor, arrowWidth);
2546 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2547#if wxUSE_GRAPHICS_CONTEXT
2548 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2552 wxPen pen(arrowColor, arrowWidth);
2557 drawLineBuffer(m_SingleArrow[arrowSizeIdx], x, y, ang,
scale);
2560void GRIBOverlayFactory::drawWindArrowWithBarbs(
int settings,
int x,
int y,
2561 double vkn,
double ang,
2562 bool south, wxColour arrowColor,
2563 double rotate_angle) {
2564 if (m_Settings.Settings[settings].m_iBarbedColour == 1)
2565 arrowColor = GetGraphicColor(settings, vkn);
2571 float penWidth = .6 / m_pixelMM;
2573 float penWidth = .4 / m_pixelMM;
2575 penWidth = wxMin(penWidth, 3.0);
2578 wxPen pen(arrowColor, 2);
2580 m_pdc->SetBrush(*wxTRANSPARENT_BRUSH);
2582#if wxUSE_GRAPHICS_CONTEXT
2583 if (m_hiDefGraphics && m_gdc) m_gdc->SetPen(pen);
2589 wxPen pen(arrowColor, penWidth);
2605 cacheidx = (int)(vkn + 2.5) / 5;
2607 cacheidx = (int)(vkn + 5) / 10 + 4;
2611 ang += rotate_angle;
2613 drawLineBuffer(m_WindArrowCache[cacheidx], x, y, ang, 1.0, south,
2614 m_bDrawBarbedArrowHead);
2617void GRIBOverlayFactory::drawLineBuffer(
LineBuffer &buffer,
int x,
int y,
2618 double ang,
double scale,
bool south,
2621 float six = sinf(ang), cox = cosf(ang), siy, coy;
2623 siy = -six, coy = -cox;
2625 siy = six, coy = cox;
2628 int count = buffer.count;
2633 wxASSERT(
sizeof vertexes /
sizeof *vertexes >= (
unsigned)count * 4);
2634 for (
int i = 0; i < 2 * count; i++) {
2636 if (!head && i > 1) j += 4;
2637 float *k = buffer.lines + 2 * j;
2638 vertexes[2 * i + 0] = k[0] * cox *
scale + k[1] * siy *
scale + x;
2639 vertexes[2 * i + 1] = k[0] * six *
scale - k[1] * coy *
scale + y;
2643 for (
int i = 0; i < count; i++) {
2644 float *l = vertexes + 4 * i;
2645#if wxUSE_GRAPHICS_CONTEXT
2646 if (m_hiDefGraphics && m_gdc)
2647 m_gdc->StrokeLine(l[0], l[1], l[2], l[3]);
2650 m_pdc->DrawLine(l[0], l[1], l[2], l[3]);
2655 for (
int i = 0; i < count; i++) {
2656 float *l = vertexes + 4 * i;
2657 if (m_hiDefGraphics)
2658 m_oDC->StrokeLine(l[0], l[1], l[2], l[3]);
2660 m_oDC->DrawLine(l[0], l[1], l[2], l[3]);
2675 double uv[],
double x,
double y,
2676 double width,
double height) {
2679 glEnable(texture_format);
2682 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2684 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
2689 coords[1] = -height;
2691 coords[3] = -height;
2697 extern int pi_texture_2D_shader_program;
2698 glUseProgram(pi_texture_2D_shader_program);
2701 GLint mPosAttrib = glGetAttribLocation(pi_texture_2D_shader_program,
"aPos");
2702 GLint mUvAttrib = glGetAttribLocation(pi_texture_2D_shader_program,
"aUV");
2705 GLint texUni = glGetUniformLocation(pi_texture_2D_shader_program,
"uTex");
2706 glUniform1i(texUni, 0);
2709 glBindBuffer(GL_ARRAY_BUFFER, 0);
2710 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
2713 glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, coords);
2715 glEnableVertexAttribArray(mPosAttrib);
2718 glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, uv);
2720 glEnableVertexAttribArray(mUvAttrib);
2726 mat4x4_rotate_Z(Q, I, angle);
2733 glGetUniformLocation(pi_texture_2D_shader_program,
"TransformMatrix");
2734 glUniformMatrix4fv(matloc, 1, GL_FALSE, (
const GLfloat *)Q);
2737 glActiveTexture(GL_TEXTURE0);
2744 GLushort indices1[] = {0,1,3,2};
2745 glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, indices1);
2768 glVertexAttribPointer(mPosAttrib, 2, GL_FLOAT, GL_FALSE, 0, co1);
2769 glVertexAttribPointer(mUvAttrib, 2, GL_FLOAT, GL_FALSE, 0, tco1);
2771 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
2773 glDisable(GL_BLEND);
2774 glDisable(texture_format);
2778 glUniformMatrix4fv(matloc, 1, GL_FALSE, (
const GLfloat *)I);
2784 glColor4f(1, 1, 1, 1);
2785 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
2787 if (texture_format != GL_TEXTURE_2D) {
2788 for (
int i = 0; i < 4; i++) {
2789 uv[i * 2] *= pGR->
getNi();
2790 uv[(i * 2) + 1] *= pGR->
getNj();
2795 glTexCoord2d(uv[0], uv[1]), glVertex2f(x - width, y - height);
2796 glTexCoord2d(uv[2], uv[3]), glVertex2f(x, y - height);
2797 glTexCoord2d(uv[4], uv[5]), glVertex2f(x, y);
2798 glTexCoord2d(uv[6], uv[7]), glVertex2f(x - width, y);
2806 glEnable(texture_format);
2807 glBindTexture(texture_format, pGO->m_iTexture);
2810 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
2812 double lat_min = pGR->getLatMin(), lon_min = pGR->getLonMin();
2814 bool repeat = pGR->getLonMin() == 0 && pGR->getLonMax() + pGR->
getDi() == 360;
2837 xsquares = wxMax(xsquares, 2);
2838 ysquares = wxMax(ysquares, 2);
2841 double xs = vp->
pix_width / double(xsquares),
2844 typedef double mx[2][2];
2846 mx *lva =
new mx[xsquares + 1];
2847 int tw = pGO->m_iTextureDim[0], th = pGO->m_iTextureDim[1];
2848 double latstep = fabs(pGR->
getDj()) / (th - 2 - 1) * (pGR->
getNj() - 1);
2849 double lonstep = pGR->
getDi() / (tw - 2 * !repeat - 1) * (pGR->
getNi() - 1);
2851 double potNormX = (double)pGO->m_iTexDataDim[0] / tw;
2852 double potNormY = (double)pGO->m_iTexDataDim[1] / th;
2854 double clon = (lon_min + pGR->getLonMax()) / 2;
2856 for (
double y = 0; y < vp->
pix_height + ys / 2; y += ys) {
2859 for (
double x = 0; x < vp->
pix_width + xs / 2; x += xs) {
2865 if (clon - lon > 180)
2867 else if (lon - clon > 180)
2872 (((lon - lon_min) / lonstep - repeat + 1.5) / tw) * potNormX;
2873 lva[i][j][1] = (((lat - lat_min) / latstep + 1.5) / th) * potNormY;
2875 if (pGR->
getDj() < 0) lva[i][j][1] = 1 - lva[i][j][1];
2877 if (x > 0 && y > 0) {
2878 double u0 = lva[i - 1][!j][0], v0 = lva[i - 1][!j][1];
2879 double u1 = lva[i][!j][0], v1 = lva[i][!j][1];
2880 double u2 = lva[i][j][0], v2 = lva[i][j][1];
2881 double u3 = lva[i - 1][j][0], v3 = lva[i - 1][j][1];
2886 else if (u0 - u1 > .5)
2890 else if (u0 - u2 > .5)
2894 else if (u0 - u3 > .5)
2899 ((u0 >= 0 || u1 >= 0 || u2 >= 0 || u3 >= 0) &&
2900 (u0 <= 1 || u1 <= 1 || u2 <= 1 || u3 <= 1))) &&
2901 (v0 >= 0 || v1 >= 0 || v2 >= 0 || v3 >= 0) &&
2902 (v0 <= 1 || v1 <= 1 || v2 <= 1 || v3 <= 1)) {
2914 DrawSingleGLTexture(pGO, pGR, uv, x, y, xs, ys);
2925 glDisable(GL_BLEND);
2926 glDisable(texture_format);
GRIB Data Visualization and Rendering Factory.
@ Idx_COMP_REFL
Composite radar reflectivity in dBZ (decibel relative to Z)
@ Idx_PRECIP_TOT
Precipitation data in millimeters per hour.
@ Idx_AIR_TEMP
Air temperature at 2m in Kelvin (K)
@ Idx_PRESSURE
Surface pressure in Pascal (Pa)
@ Idx_WVDIR
Wave direction.
@ Idx_CLOUD_TOT
Total cloud cover in % (percent, range 0-100%)
@ Idx_WIND_GUST
Wind gust speed at surface in m/s.
@ Idx_WIND_VX
Surface wind velocity X component in m/s.
@ Idx_HTSIGW
Significant wave height in meters.
@ Idx_SEACURRENT_VY
Sea current velocity Y component in m/s.
@ Idx_SEA_TEMP
Sea surface temperature in Kelvin (K)
@ Idx_WIND_VY
Surface wind velocity Y component in m/s.
@ Idx_SEACURRENT_VX
Sea current velocity X component in m/s.
@ Idx_CAPE
Convective Available Potential Energy in J/kg (Joules per kilogram)
GRIB Weather Data Control Interface.
void GetProjectedLatLon(int &x, int &y, PlugIn_ViewPort *vp)
Gets the projected position of vessel based on current course, speed and forecast time.
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.
Contains view parameters and status information for a chart display viewport.
double view_scale_ppm
Display scale in pixels per meter.
int pix_width
Viewport width in pixels.
double lon_max
Maximum longitude of the viewport.
double clon
Center longitude of the viewport in decimal degrees.
double lat_max
Maximum latitude of the viewport.
int pix_height
Viewport height in pixels.
double clat
Center latitude of the viewport in decimal degrees.
double skew
Display skew angle in radians.
double rotation
Display rotation angle in radians.
bool bValid
True if this viewport is valid and can be used for rendering.
double lon_min
Minimum longitude of the viewport.
double lat_min
Minimum latitude of the viewport.
int m_projection_type
Chart projection type (PROJECTION_MERCATOR, etc.)
@ PI_PROJECTION_MERCATOR
Mercator projection, standard for navigation charts.
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
int GetCanvasCount()
Gets total number of chart canvases.
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.
void PositionBearingDistanceMercator_Plugin(double lat, double lon, double brg, double dist, double *dlat, double *dlon)
Calculates destination point given starting point, bearing and distance.
double PlugInGetDisplaySizeMM()
Gets physical display size in millimeters.
void GetCanvasPixLL(PlugIn_ViewPort *vp, wxPoint *pp, double lat, double lon)
Converts lat/lon to canvas physical pixel coordinates.
int GetChartbarHeight()
Gets height of chart bar in pixels.
wxWindow * GetCanvasByIndex(int canvasIndex)
Gets chart canvas window by index.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
void GetCanvasLLPix(PlugIn_ViewPort *vp, wxPoint p, double *plat, double *plon)
Converts canvas physical pixel coordinates to lat/lon.
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.