56#include "glChartCanvas.h"
66#define __CALL_CONVENTION
68#define __CALL_CONVENTION
73extern wxString gWorldMapLocation;
75#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
76static const GLchar *vertex_shader_source =
77 "attribute vec2 position;\n"
78 "uniform mat4 MVMatrix;\n"
79 "uniform mat4 TransformMatrix;\n"
80 "uniform vec4 color;\n"
81 "varying vec4 fragColor;\n"
83 " fragColor = color;\n"
84 " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
87static const GLchar *fragment_shader_source =
88 "precision lowp float;\n"
89 "varying vec4 fragColor;\n"
91 " gl_FragColor = fragColor;\n"
94static const GLfloat vertices2[] = {
95 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
98static const GLfloat vertices3[] = {
99 0.0f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f,
102enum Consts { INFOLOG_LEN = 512 };
103GLchar infoLog[INFOLOG_LEN];
104GLint fragment_shader;
109extern GLint color_tri_shader_program;
114GSHHSChart::GSHHSChart() {
116 land = wxColor(250, 250, 250);
117 water = wxColor(0, 0, 0);
120GSHHSChart::~GSHHSChart() {
121 if (reader)
delete reader;
124void GSHHSChart::SetColorScheme(ColorScheme scheme) {
125 land = wxColor(170, 175, 80);
126 water = wxColor(170, 195, 240);
131 case GLOBAL_COLOR_SCHEME_DUSK:
134 case GLOBAL_COLOR_SCHEME_NIGHT:
141 land.Set(land.Red() * dim, land.Green() * dim, land.Blue() * dim);
142 water.Set(water.Red() * dim, water.Green() * dim, water.Blue() * dim);
145void GSHHSChart::SetColorsDirect(wxColour newLand, wxColour newWater) {
150void GSHHSChart::Reset() {
151 if (reader)
delete reader;
153 gshhsCrossesLandReset();
156int GSHHSChart::GetMinAvailableQuality() {
158 return reader->GetMinAvailableQuality();
161int GSHHSChart::GetMaxAvailableQuality() {
163 return reader->GetMaxAvailableQuality();
169 if (reader->GetPolyVersion() < 210 || reader->GetPolyVersion() > 240) {
171 _T(
"GSHHS World chart files have wrong version. Found %d, expected ")
173 reader->GetPolyVersion());
176 _T(
"Background world map loaded from GSHHS datafiles found in: ") +
181 reader->drawContinents(dc, vp, water, land);
187GshhsPolyCell::GshhsPolyCell(FILE *fpoly_,
int x0_,
int y0_,
194 for (
int i = 0; i < 6; i++) polyv[i] = NULL;
198 for (
int i = 0; i < GSSH_SUBM * GSSH_SUBM; i++) high_res_map[i] = NULL;
201GshhsPolyCell::~GshhsPolyCell() {
204 for (
int i = 0; i < GSSH_SUBM * GSSH_SUBM; i++)
delete high_res_map[i];
205 for (
int i = 0; i < 6; i++)
delete[] polyv[i];
208void GshhsPolyCell::ClearPolyV() {
209 for (
int i = 0; i < 6; i++) {
215void GshhsPolyCell::ReadPoly(contour_list &poly) {
218 int32_t num_vertices, num_contours;
220 if (fread(&num_contours,
sizeof num_contours, 1, fpoly) != 1)
goto fail;
222 for (
int c = 0; c < num_contours; c++) {
224 if (fread(&value,
sizeof value, 1, fpoly) !=
226 fread(&value,
sizeof value, 1, fpoly) != 1)
229 num_vertices = value;
232 for (
int v = 0; v < num_vertices; v++) {
233 if (fread(&X,
sizeof X, 1, fpoly) != 1 ||
234 fread(&Y,
sizeof Y, 1, fpoly) != 1)
237 tmp_contour.push_back(wxRealPoint(X * GSHHS_SCL, Y * GSHHS_SCL));
239 poly.push_back(tmp_contour);
244 wxLogMessage(_T(
"gshhs ReadPoly failed"));
247void GshhsPolyCell::ReadPolygonFile() {
253 tab_data = (x0cell / header->pasx) * (180 / header->pasy) +
254 (y0cell + 90) / header->pasy;
256 if (fread(&pos_data,
sizeof(
int), 1, fpoly) != 1)
goto fail;
258 fseek(fpoly, pos_data, SEEK_SET);
268 wxLogMessage(_T(
"gshhs ReadPolygon failed"));
271wxPoint2DDouble GetDoublePixFromLL(
ViewPort &vp,
double lat,
double lon) {
273 p.m_x -= vp.rv_rect.x, p.m_y -= vp.rv_rect.y;
277void GshhsPolyCell::DrawPolygonFilled(
ocpnDC &pnt, contour_list *p,
double dx,
278 ViewPort &vp, wxColor
const &color) {
292 for (c = 0; c < p->size(); c++) {
293 if (!p->at(c).size())
continue;
295 wxPoint *poly_pt =
new wxPoint[p->at(c).size()];
297 contour &cp = p->at(c);
300 for (v = 0; v < p->at(c).size(); v++) {
301 wxRealPoint &ccp = cp.at(v);
302 wxPoint2DDouble q = GetDoublePixFromLL(vp, ccp.y, ccp.x + dx);
303 if (std::isnan(q.m_x)) {
308 x = q.m_x, y = q.m_y;
310 if (v == 0 || x != x_old || y != y_old) {
311 poly_pt[pointCount].x = x;
312 poly_pt[pointCount].y = y;
319 if (pointCount > 1) pnt.DrawPolygonTessellated(pointCount, poly_pt, 0, 0);
341static std::list<float_2Dpt> g_pv;
342static std::list<GLvertex *> g_vertexes;
343static int g_type, g_pos;
344static float_2Dpt g_p1, g_p2;
346void __CALL_CONVENTION gshhscombineCallback(GLdouble coords[3],
347 GLdouble *vertex_data[4],
349 GLdouble **dataOut) {
352 vertex =
new GLvertex();
353 g_vertexes.push_back(vertex);
355 vertex->info.x = coords[0];
356 vertex->info.y = coords[1];
358 *dataOut = vertex->data;
361void __CALL_CONVENTION gshhsvertexCallback(GLvoid *arg) {
363 vertex = (GLvertex *)arg;
365 p.y = vertex->info.x;
366 p.x = vertex->info.y;
369 if (g_type != GL_TRIANGLES) {
371 g_pv.push_back(g_p1);
372 g_pv.push_back(g_p2);
375 if (g_type == GL_TRIANGLE_STRIP)
386void __CALL_CONVENTION gshhserrorCallback(GLenum errorCode) {
387 const GLubyte *estring;
388 estring = gluErrorString(errorCode);
392void __CALL_CONVENTION gshhsbeginCallback(GLenum type) {
395 case GL_TRIANGLE_STRIP:
396 case GL_TRIANGLE_FAN:
400 printf(
"tess unhandled begin type: %d\n", type);
406void __CALL_CONVENTION gshhsendCallback() {}
408void GshhsPolyCell::DrawPolygonFilledGL(
ocpnDC &pnt, contour_list *p,
409 float_2Dpt **pv,
int *pvc,
ViewPort &vp,
410 wxColor
const &color,
bool idl) {
417 for (
unsigned int c = 0; c < p->size(); c++) {
418 if (!p->at(c).size())
continue;
420 contour &cp = p->at(c);
422 GLUtesselator *tobj = gluNewTess();
424 gluTessCallback(tobj, GLU_TESS_VERTEX, (_GLUfuncptr)&gshhsvertexCallback);
425 gluTessCallback(tobj, GLU_TESS_BEGIN, (_GLUfuncptr)&gshhsbeginCallback);
426 gluTessCallback(tobj, GLU_TESS_END, (_GLUfuncptr)&gshhsendCallback);
427 gluTessCallback(tobj, GLU_TESS_COMBINE,
428 (_GLUfuncptr)&gshhscombineCallback);
429 gluTessCallback(tobj, GLU_TESS_ERROR, (_GLUfuncptr)&gshhserrorCallback);
431 gluTessNormal(tobj, 0, 0, 1);
432 gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
434 gluTessBeginPolygon(tobj, NULL);
435 gluTessBeginContour(tobj);
437 for (
unsigned int v = 0; v < p->at(c).size(); v++) {
438 wxRealPoint &ccp = cp.at(v);
440 if (v == 0 || ccp != cp.at(v - 1)) {
441 GLvertex *vertex =
new GLvertex();
442 g_vertexes.push_back(vertex);
445 if (glChartCanvas::HasNormalizedViewPort(vp))
446 q = GetDoublePixFromLL(vp, ccp.y, ccp.x);
448 q.m_x = ccp.y, q.m_y = ccp.x;
450 if (vp.m_projection_type != PROJECTION_POLAR) {
454 if (idl && ccp.x == 180) {
455 if (vp.m_projection_type != PROJECTION_MERCATOR &&
456 vp.m_projection_type != PROJECTION_EQUIRECTANGULAR)
461 vertex->info.x = q.m_x;
462 vertex->info.y = q.m_y;
464 gluTessVertex(tobj, (GLdouble *)vertex, (GLdouble *)vertex);
468 gluTessEndContour(tobj);
469 gluTessEndPolygon(tobj);
472 for (std::list<GLvertex *>::iterator it = g_vertexes.begin();
473 it != g_vertexes.end(); it++)
478 *pv =
new float_2Dpt[g_pv.size()];
480 for (std::list<float_2Dpt>::iterator it = g_pv.begin(); it != g_pv.end();
488#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
492 if (!vertex_shader) {
494 vertex_shader = glCreateShader(GL_VERTEX_SHADER);
495 glShaderSource(vertex_shader, 1, &vertex_shader_source, NULL);
496 glCompileShader(vertex_shader);
497 glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
499 glGetShaderInfoLog(vertex_shader, INFOLOG_LEN, NULL, infoLog);
500 printf(
"ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
504 if (!fragment_shader) {
506 fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
507 glShaderSource(fragment_shader, 1, &fragment_shader_source, NULL);
508 glCompileShader(fragment_shader);
509 glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
511 glGetShaderInfoLog(fragment_shader, INFOLOG_LEN, NULL, infoLog);
512 printf(
"ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
516 if (!shader_program) {
518 shader_program = glCreateProgram();
519 glAttachShader(shader_program, vertex_shader);
520 glAttachShader(shader_program, fragment_shader);
521 glLinkProgram(shader_program);
522 glGetProgramiv(shader_program, GL_LINK_STATUS, &success);
524 glGetProgramInfoLog(shader_program, INFOLOG_LEN, NULL, infoLog);
525 printf(
"ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
534 mat4x4_scale_aniso(mvp, m, 2.0 / (
float)vp.
pix_width,
538 if (glChartCanvas::HasNormalizedViewPort(vp)) {
540 GLint pos = glGetAttribLocation(color_tri_shader_program,
"position");
541 glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 *
sizeof(
float), *pv);
542 glEnableVertexAttribArray(pos);
548 GLint tmatloc = glGetUniformLocation(color_tri_shader_program,
"TransformMatrix");
549 glUniformMatrix4fv(tmatloc, 1, GL_FALSE, (
const GLfloat*)m);
551 glUseProgram(color_tri_shader_program);
552 glDrawArrays(GL_TRIANGLES, 0, *pvc);
555 float *pvt =
new float[2 * (*pvc)];
556 for (
int i = 0; i < *pvc; i++) {
557 float_2Dpt *pc = *pv + i;
560 pvt[(i * 2) + 1] = q.m_y;
563 GLShaderProgram *shader = pcolor_tri_shader_program[pnt.m_canvasIndex];
567 colorv[0] = color.Red() / float(256);
568 colorv[1] = color.Green() / float(256);
569 colorv[2] = color.Blue() / float(256);
571 shader->SetUniform4fv(
"color", colorv);
573 shader->SetAttributePointerf(
"position", pvt);
575 glDrawArrays(GL_TRIANGLES, 0, *pvc);
578 glDeleteBuffers(1, &vbo);
587#define DRAW_POLY_FILLED(POLY, COL) \
588 if (POLY) DrawPolygonFilled(pnt, POLY, dx, vp, COL);
589#define DRAW_POLY_FILLED_GL(NUM, COL) \
590 DrawPolygonFilledGL(pnt, &poly##NUM, &polyv[NUM], &polyc[NUM], vp, COL, idl);
592void GshhsPolyCell::drawMapPlain(
ocpnDC &pnt,
double dx,
ViewPort &vp,
593 wxColor seaColor, wxColor landColor,
597#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
598#define NORM_FACTOR 4096.0
599 if (dx && (vp.m_projection_type == PROJECTION_MERCATOR ||
600 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR)) {
602 40058986 * NORM_FACTOR;
604 glTranslated(dx > 0 ? ts : -ts, 0, 0);
607 DRAW_POLY_FILLED_GL(1, landColor);
608 DRAW_POLY_FILLED_GL(2, seaColor);
609 DRAW_POLY_FILLED_GL(3, landColor);
610 DRAW_POLY_FILLED_GL(4, seaColor);
611 DRAW_POLY_FILLED_GL(5, landColor);
613#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
614 if (dx) glPopMatrix();
620 DRAW_POLY_FILLED(&poly1, landColor);
621 DRAW_POLY_FILLED(&poly2, seaColor);
622 DRAW_POLY_FILLED(&poly3, landColor);
623 DRAW_POLY_FILLED(&poly4, seaColor);
624 DRAW_POLY_FILLED(&poly5, landColor);
628void GshhsPolyCell::DrawPolygonContour(
ocpnDC &pnt, contour_list *p,
double dx,
630 double x1, y1, x2, y2;
631 double long_max, lat_max, long_min, lat_min;
633 long_min = (double)x0cell;
634 lat_min = (double)y0cell;
635 long_max = ((double)x0cell + (
double)header->pasx);
636 lat_max = ((double)y0cell + (
double)header->pasy);
640 for (
unsigned int i = 0; i < p->size(); i++) {
641 if (!p->at(i).size())
continue;
644 for (v = 0; v < (p->at(i).size() - 1); v++) {
645 x1 = p->at(i).at(v).x;
646 y1 = p->at(i).at(v).y;
647 x2 = p->at(i).at(v + 1).x;
648 y2 = p->at(i).at(v + 1).y;
651 if ((((x1 == x2) && ((x1 == long_min) || (x1 == long_max))) ||
652 ((y1 == y2) && ((y1 == lat_min) || (y1 == lat_max)))) == 0) {
653 wxPoint2DDouble AB = GetDoublePixFromLL(vp, x1 + dx, y1);
654 wxPoint2DDouble CD = GetDoublePixFromLL(vp, x2 + dx, y1);
655 pnt.
DrawLine(AB.m_x, AB.m_y, CD.m_x, CD.m_y);
659 x1 = p->at(i).at(v).x;
660 y1 = p->at(i).at(v).y;
661 x2 = p->at(i).at(0).x;
662 y2 = p->at(i).at(0).y;
664 if ((((x1 == x2) && ((x1 == long_min) || (x1 == long_max))) ||
665 ((y1 == y2) && ((y1 == lat_min) || (y1 == lat_max)))) == 0) {
666 wxPoint2DDouble AB = GetDoublePixFromLL(vp, x1 + dx, y1);
667 wxPoint2DDouble CD = GetDoublePixFromLL(vp, x2 + dx, y1);
668 pnt.
DrawLine(AB.m_x, AB.m_y, CD.m_x, CD.m_y);
673#define DRAW_POLY_CONTOUR(POLY) \
674 if (POLY) DrawPolygonContour(pnt, POLY, dx, vp);
676void GshhsPolyCell::drawSeaBorderLines(
ocpnDC &pnt,
double dx,
ViewPort &vp) {
678 DRAW_POLY_CONTOUR(&poly1)
679 DRAW_POLY_CONTOUR(&poly2)
680 DRAW_POLY_CONTOUR(&poly3)
681 DRAW_POLY_CONTOUR(&poly4)
682 DRAW_POLY_CONTOUR(&poly5)
687GshhsPolyReader::GshhsPolyReader(
int quality) {
690 for (
int i = 0; i < 360; i++) {
691 for (
int j = 0; j < 180; j++) {
692 allCells[i][j] = NULL;
696 polyHeader.version = -1;
697 InitializeLoadQuality(quality);
701GshhsPolyReader::~GshhsPolyReader() {
702 for (
int i = 0; i < 360; i++) {
703 for (
int j = 0; j < 180; j++) {
704 if (allCells[i][j] != NULL) {
705 delete allCells[i][j];
706 allCells[i][j] = NULL;
717int GshhsPolyReader::ReadPolyVersion() {
718 wxString fname = GshhsReader::getFileName_Land(0);
719 if (fpoly) fclose(fpoly);
720 fpoly = fopen(fname.mb_str(),
"rb");
723 if (!fpoly)
return 0;
725 readPolygonFileHeader(fpoly, &polyHeader);
727 return polyHeader.version;
730void GshhsPolyReader::InitializeLoadQuality(
733 if (currentQuality != quality) {
734 currentQuality = quality;
736 wxString fname = GshhsReader::getFileName_Land(quality);
738 if (fpoly) fclose(fpoly);
740 fpoly = fopen(fname.mb_str(),
"rb");
741 if (fpoly) readPolygonFileHeader(fpoly, &polyHeader);
743 for (
int i = 0; i < 360; i++) {
744 for (
int j = 0; j < 180; j++) {
745 if (allCells[i][j] != NULL) {
746 delete allCells[i][j];
747 allCells[i][j] = NULL;
754static inline bool my_intersects(
const wxLineF &line1,
const wxLineF &line2) {
755 double x1 = line1.m_p1.x, y1 = line1.m_p1.y, x2 = line1.m_p2.x,
757 double x3 = line2.m_p1.x, y3 = line2.m_p1.y, x4 = line2.m_p2.x,
762 double ax = x2 - x1, ay = y2 - y1;
763 double bx = x3 - x4, by = y3 - y4;
764 double cx = x1 - x3, cy = y1 - y3;
766#define INTER_LIMIT 1e-7
767 double denominator = ay * bx - ax * by;
768 if (denominator < 1e-10) {
769 if (fabs((y1 * ax - ay * x1) * bx - (y3 * bx - by * x3) * ax) > INTER_LIMIT)
771 if (fabs((x1 * ay - ax * y1) * by - (x3 * by - bx * y3) * ay) > INTER_LIMIT)
777 const double reciprocal = 1 / denominator;
778 const double na = (by * cx - bx * cy) * reciprocal;
780 if (na < -INTER_LIMIT || na > 1 + INTER_LIMIT)
return false;
782 const double nb = (ax * cy - ay * cx) * reciprocal;
783 if (nb < -INTER_LIMIT || nb > 1 + INTER_LIMIT)
return false;
788bool GshhsPolyReader::crossing1(
wxLineF trajectWorld) {
789 double x1 = trajectWorld.p1().x, y1 = trajectWorld.p1().y;
790 double x2 = trajectWorld.p2().x, y2 = trajectWorld.p2().y;
792 int clonmin, clonmax, clatmax, clatmin;
793 clonmin = (int)floor(GSSH_SUBM * wxMin(x1, x2));
794 clonmax = (int)ceil(GSSH_SUBM * wxMax(x1, x2));
797 clonmin += GSSH_SUBM * 360;
798 clonmax += GSSH_SUBM * 360;
801 if (clonmax - clonmin > GSSH_SUBM * 180) {
802 clonmin = (int)floor(GSSH_SUBM * wxMax(x1, x2)) - GSSH_SUBM * 360;
803 clonmax = (int)ceil(GSSH_SUBM * wxMin(x1, x2));
806 clatmin = (int)floor(GSSH_SUBM * wxMin(y1, y2));
807 clatmax = (int)ceil(GSSH_SUBM * wxMax(y1, y2));
808 wxASSERT(clatmin >= -GSSH_SUBM * 90 && clatmax <= GSSH_SUBM * 89);
814 int clon, clonx, clat;
815 for (clon = clonmin; clon < clonmax; clon++) {
817 while (clonx < 0) clonx += GSSH_SUBM * 360;
818 while (clonx >= GSSH_SUBM * 360) clonx -= GSSH_SUBM * 360;
820 wxASSERT(clonx >= 0 && clonx < GSSH_SUBM * 360);
822 if (clonx < GSSH_SUBM * 180) {
823 if (x1 > 180) x1 -= 360;
824 if (x2 > 180) x2 -= 360;
826 if (x1 < 180) x1 += 360;
827 if (x2 < 180) x2 += 360;
830 wxLineF rtrajectWorld(x1, y1, x2, y2);
832 for (clat = clatmin; clat < clatmax; clat++) {
833 int cloni = clonx / GSSH_SUBM,
834 clati = (GSSH_SUBM * 90 + clat) / GSSH_SUBM;
840 cel =
new GshhsPolyCell(fpoly, cloni, clati - 90, &polyHeader);
846 int hash = GSSH_SUBM * (GSSH_SUBM * (90 - clati) + clat - cloni) + clonx;
847 std::vector<wxLineF> *&high_res_map = cel->high_res_map[hash];
848 wxASSERT(hash >= 0 && hash < GSSH_SUBM * GSSH_SUBM);
853 contour_list &poly1 = cel->getPoly1();
855 double minlat = (double)clat / GSSH_SUBM,
856 maxlat = (
double)(clat + 1) / GSSH_SUBM;
857 double minlon = (double)clonx / GSSH_SUBM,
858 maxlon = (
double)(clonx + 1) / GSSH_SUBM;
859 high_res_map =
new std::vector<wxLineF>;
860 for (
unsigned int pi = 0; pi < poly1.size(); pi++) {
861 contour &c = poly1[pi];
862 double lx = c[c.size() - 1].x, ly = c[c.size() - 1].y;
866 int lstatex = lx < minlon ? -1 : lx > maxlon ? 1 : 0;
867 int lstatey = ly < minlat ? -1 : ly > maxlat ? 1 : 0;
869 for (
unsigned int pj = 0; pj < c.size(); pj++) {
870 double clon = c[pj].x, clat = c[pj].y;
875 if (lx == clon && ly == clat)
continue;
877 int statex = clon < minlon ? -1 : clon > maxlon ? 1 : 0;
878 int statey = clat < minlat ? -1 : clat > maxlat ? 1 : 0;
880 if ((!statex || lstatex != statex) &&
881 (!statey || lstatey != statey))
882 high_res_map->push_back(
wxLineF(lx, ly, clon, clat));
884 lx = clon, ly = clat;
885 lstatex = statex, lstatey = statey;
892 for (std::vector<wxLineF>::iterator it2 = high_res_map->begin();
893 it2 != high_res_map->end(); it2++)
894 if (my_intersects(rtrajectWorld, *it2))
return true;
901void GshhsPolyReader::readPolygonFileHeader(FILE *polyfile,
903 fseek(polyfile, 0, SEEK_SET);
905 wxLogMessage(_T(
"gshhs ReadPolygonFileHeader failed"));
909void GshhsPolyReader::drawGshhsPolyMapPlain(
ocpnDC &pnt,
ViewPort &vp,
910 wxColor
const &seaColor,
911 wxColor
const &landColor) {
914 pnt.SetPen(wxNullPen);
916 int clonmin, clonmax, clatmax, clatmin;
917 LLBBox bbox = vp.GetBBox();
918 clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
919 clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
920 if (clatmin <= 0) clatmin--;
921 if (clatmax >= 0) clatmax++;
922 if (clonmin <= 0) clonmin--;
923 if (clonmax >= 0) clonmax++;
924 int dx, clon, clonx, clat;
931 if (vp.m_projection_type != last_rendered_vp.m_projection_type ||
932 (last_rendered_vp.m_projection_type == PROJECTION_POLAR &&
933 last_rendered_vp.
clat * vp.
clat <= 0)) {
934 last_rendered_vp = vp;
935 for (
int clon = 0; clon < 360; clon++)
936 for (
int clat = 0; clat < 180; clat++)
937 if (allCells[clon][clat]) allCells[clon][clat]->ClearPolyV();
939#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
940 glEnableClientState(GL_VERTEX_ARRAY);
944 if (glChartCanvas::HasNormalizedViewPort(vp)) {
946 glChartCanvas::MultMatrixViewPort(vp);
947 nvp = glChartCanvas::NormalizedViewPort(vp);
952 for (clon = clonmin; clon < clonmax; clon++) {
954 while (clonx < 0) clonx += 360;
955 while (clonx >= 360) clonx -= 360;
957 for (clat = clatmin; clat < clatmax; clat++) {
958 if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
959 if (allCells[clonx][clat + 90] == NULL) {
962 allCells[clonx][clat + 90] = cel;
964 cel = allCells[clonx][clat + 90];
969 if (vp.m_projection_type != PROJECTION_MERCATOR &&
970 vp.m_projection_type != PROJECTION_EQUIRECTANGULAR)
972 else if (pnt.GetDC())
980 if (vp.
clon - clonn > 180)
982 else if (vp.
clon - clonn < -180)
988 cel->drawMapPlain(pnt, dx, nvp, seaColor, landColor, idl);
994#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
996 if (glChartCanvas::HasNormalizedViewPort(vp)) glPopMatrix();
997 glDisableClientState(GL_VERTEX_ARRAY);
1004void GshhsPolyReader::drawGshhsPolyMapSeaBorders(
ocpnDC &pnt,
ViewPort &vp) {
1006 int clonmin, clonmax, clatmax, clatmin;
1007 LLBBox bbox = vp.GetBBox();
1008 clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
1009 clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
1011 int dx, clon, clonx, clat;
1014 for (clon = clonmin; clon < clonmax; clon++) {
1016 while (clonx < 0) clonx += 360;
1017 while (clonx >= 360) clonx -= 360;
1019 for (clat = clatmin; clat < clatmax; clat++) {
1020 if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
1021 if (allCells[clonx][clat + 90] == NULL) {
1024 allCells[clonx][clat + 90] = cel;
1026 cel = allCells[clonx][clat + 90];
1029 cel->drawSeaBorderLines(pnt, dx, vp);
1035int GshhsPolygon::readInt4() {
1038 unsigned char buf[4];
1041 unsigned char in[4];
1044 nb += fread(&in, 1, 4, file);
1058int GshhsPolygon::readInt2() {
1061 unsigned char buf[4];
1065 nb += fread(&v.buf[0], 2, 1, file);
1070 return v.buf[1] << 8 | v.buf[0];
1073GshhsPolygon::GshhsPolygon(FILE *file_) {
1079 west = readInt4() * 1e-6;
1080 east = readInt4() * 1e-6;
1081 south = readInt4() * 1e-6;
1082 north = readInt4() * 1e-6;
1085 if (((flag >> 8) & 255) >= 7) {
1086 areaFull = readInt4();
1087 container = readInt4();
1088 ancestor = readInt4();
1090 greenwich = (flag >> 16) & 1;
1091 antarctic = (west == 0 && east == 360);
1093 double x = 0, y = 0;
1094 for (
int i = 0; i < n; i++) {
1095 x = readInt4() * 1e-6;
1096 if (greenwich && x > 270) x -= 360;
1097 y = readInt4() * 1e-6;
1101 lsPoints.insert(lsPoints.begin(),
new GshhsPoint(360, y));
1102 lsPoints.insert(lsPoints.begin(),
new GshhsPoint(360, -90));
1107 greenwich = (flag >> 16) & 1;
1108 antarctic = (west == 0 && east == 360);
1110 for (
int i = 0; i < n; i++) {
1111 double x = 0, y = 0;
1112 x = readInt4() * 1e-6;
1113 if (greenwich && x > 270) x -= 360;
1114 y = readInt4() * 1e-6;
1123GshhsPolygon::~GshhsPolygon() {
1124 std::vector<GshhsPoint *>::iterator itp;
1125 for (itp = lsPoints.begin(); itp != lsPoints.end(); itp++) {
1134GshhsReader::GshhsReader() {
1135 maxQualityAvailable = -1;
1136 minQualityAvailable = -1;
1138 for (
int i = 0; i < 5; i++) {
1139 qualityAvailable[i] =
false;
1140 if (GshhsReader::gshhsFilesExists(i)) {
1141 qualityAvailable[i] =
true;
1142 if (minQualityAvailable < 0) minQualityAvailable = i;
1143 maxQualityAvailable = i;
1147 if (maxQualityAvailable < 0) {
1149 _T(
"Unable to initialize background world map. No GSHHS datafiles ")
1151 msg += gWorldMapLocation;
1164 for (
int qual = 0; qual < 5; qual++) {
1165 lsPoly_boundaries[qual] =
new std::vector<GshhsPolygon *>;
1166 lsPoly_rivers[qual] =
new std::vector<GshhsPolygon *>;
1173int GshhsReader::ReadPolyVersion() {
1174 return gshhsPoly_reader->ReadPolyVersion();
1179GshhsReader::~GshhsReader() {
1181 delete gshhsPoly_reader;
1185void GshhsReader::clearLists() {
1186 std::vector<GshhsPolygon *>::iterator itp;
1187 for (
int qual = 0; qual < 5; qual++) {
1188 for (itp = lsPoly_boundaries[qual]->begin();
1189 itp != lsPoly_boundaries[qual]->end(); itp++) {
1193 for (itp = lsPoly_rivers[qual]->begin(); itp != lsPoly_rivers[qual]->end();
1199 lsPoly_boundaries[qual]->clear();
1200 lsPoly_rivers[qual]->clear();
1201 delete lsPoly_boundaries[qual];
1202 delete lsPoly_rivers[qual];
1207wxString GshhsReader::getNameExtension(
int quality) {
1232wxString GshhsReader::getFileName_Land(
int quality) {
1233 wxString ext = GshhsReader::getNameExtension(quality);
1235 gWorldMapLocation + wxString::Format(_T(
"poly-%c-1.dat"), ext.GetChar(0));
1239wxString GshhsReader::getFileName_boundaries(
int quality) {
1240 wxString ext = GshhsReader::getNameExtension(quality);
1241 wxString fname = gWorldMapLocation +
1242 wxString::Format(_T(
"wdb_borders_%c.b"), ext.GetChar(0));
1246wxString GshhsReader::getFileName_rivers(
int quality) {
1247 wxString ext = GshhsReader::getNameExtension(quality);
1248 wxString fname = gWorldMapLocation +
1249 wxString::Format(_T(
"wdb_rivers_%c.b"), ext.GetChar(0));
1254bool GshhsReader::gshhsFilesExists(
int quality) {
1255 if (!wxFile::Access(GshhsReader::getFileName_Land(quality), wxFile::read))
1267void GshhsReader::LoadQuality(
int newQuality)
1269 if (quality == newQuality)
return;
1271 wxStopWatch perftimer;
1275 quality = newQuality;
1278 else if (quality > 4)
1281 gshhsPoly_reader->InitializeLoadQuality(quality);
1283 if( lsPoly_boundaries[quality]->size() == 0 ) {
1284 fname = getFileName_boundaries( quality );
1285 file = fopen( fname.mb_str(),
"rb" );
1287 if( file != NULL ) {
1294 if( poly->getLevel() < 2 )
1295 lsPoly_boundaries[quality]->push_back( poly );
1303 if( lsPoly_rivers[quality]->size() == 0 ) {
1304 fname = getFileName_rivers( quality );
1305 file = fopen( fname.mb_str(),
"rb" );
1306 if( file != NULL ) {
1312 lsPoly_rivers[quality]->push_back( poly );
1320 wxLogMessage(_T(
"Loading World Chart Q=%d in %ld ms."), quality,
1325std::vector<GshhsPolygon *> &GshhsReader::getList_boundaries() {
1326 return *lsPoly_boundaries[quality];
1329std::vector<GshhsPolygon *> &GshhsReader::getList_rivers() {
1330 return *lsPoly_rivers[quality];
1335int GshhsReader::GSHHS_scaledPoints(
GshhsPolygon *pol, wxPoint *pts,
1338 box.Set(pol->south, pol->west + declon, pol->north, pol->east + declon);
1339 if (vp.GetBBox().IntersectOut(box))
return 0;
1343 wxPoint2DDouble p1 = GetDoublePixFromLL(vp, pol->west + declon, pol->north);
1344 wxPoint2DDouble p2 = GetDoublePixFromLL(vp, pol->east + declon, pol->south);
1346 if (p1.m_x == p2.m_x && p1.m_y == p2.m_y)
return 0;
1349 std::vector<GshhsPoint *>::iterator itp;
1350 int xx, yy, oxx = 0, oyy = 0;
1353 for (itp = (pol->lsPoints).begin(); itp != (pol->lsPoints).end(); itp++) {
1354 x = (*itp)->lon + declon;
1356 wxPoint2DDouble p = GetDoublePixFromLL(vp, y, x);
1357 xx = p.m_x, yy = p.m_y;
1358 if (j == 0 || (oxx != xx || oyy != yy)) {
1371void GshhsReader::GsshDrawLines(
ocpnDC &pnt, std::vector<GshhsPolygon *> &lst,
1373 std::vector<GshhsPolygon *>::iterator iter;
1375 wxPoint *pts = NULL;
1380 pts =
new wxPoint[nbmax];
1383 for (i = 0, iter = lst.begin(); iter != lst.end(); iter++, i++) {
1386 if (nbmax < pol->n + 2) {
1389 pts =
new wxPoint[nbmax];
1393 nbp = GSHHS_scaledPoints(pol, pts, 0, vp);
1395 if (pol->isAntarctic()) {
1398 pnt.DrawLines(nbp, pts);
1401 pnt.DrawLines(nbp, pts);
1403 pnt.
DrawLine(pts[0].x, pts[0].y, pts[nbp - 1].x, pts[nbp - 1].y);
1407 nbp = GSHHS_scaledPoints(pol, pts, -360, vp);
1409 if (pol->isAntarctic()) {
1412 pnt.DrawLines(nbp, pts);
1415 pnt.DrawLines(nbp, pts);
1417 pnt.
DrawLine(pts[0].x, pts[0].y, pts[nbp - 1].x, pts[nbp - 1].y);
1426 wxColor
const &seaColor,
1427 wxColor
const &landColor) {
1428 LoadQuality(selectBestQuality(vp));
1429 gshhsPoly_reader->drawGshhsPolyMapPlain(pnt, vp, seaColor, landColor);
1434 pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1435 gshhsPoly_reader->drawGshhsPolyMapSeaBorders(pnt, vp);
1440 pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1443 wxPen *pen = wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxPENSTYLE_DOT);
1446 wxPen *pen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 80), 2,
1447 wxPENSTYLE_LONG_DASH);
1450 GsshDrawLines(pnt, getList_boundaries(), vp,
false);
1455 GsshDrawLines(pnt, getList_rivers(), vp,
false);
1459int GshhsReader::selectBestQuality(
ViewPort &vp) {
1460 int bestQuality = 0;
1462 if (vp.
chart_scale < 500000 && qualityAvailable[4])
1464 else if (vp.
chart_scale < 2000000 && qualityAvailable[3])
1466 else if (vp.
chart_scale < 8000000 && qualityAvailable[2])
1468 else if (vp.
chart_scale < 20000000 && qualityAvailable[1])
1470 else if (qualityAvailable[0])
1473 while (!qualityAvailable[bestQuality] &&
1479 while (!qualityAvailable[bestQuality]) {
1481 if (bestQuality < 0)
break;
1494void gshhsCrossesLandInit() {
1499 int bestQuality = 4;
1500 while (!reader->qualityAvailable[bestQuality] && bestQuality > 0)
1502 reader->LoadQuality(bestQuality);
1503 wxLogMessage(
"GSHHG: Loaded quality %d for land crossing detection.",
1507void gshhsCrossesLandReset() {
1508 if (reader)
delete reader;
1512bool gshhsCrossesLand(
double lat1,
double lon1,
double lat2,
double lon2) {
1514 gshhsCrossesLandInit();
1516 if (lon1 < 0) lon1 += 360;
1517 if (lon2 < 0) lon2 += 360;
1519 wxLineF trajectWorld(lon1, lat1, lon2, lat2);
1520 return reader->crossing1(trajectWorld);
Wrapper class for OpenGL shader programs.
Represents the view port for chart display in OpenCPN.
int pix_height
Height of the viewport in physical pixels.
int pix_width
Width of the viewport in physical pixels.
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
double clon
Center longitude of the viewport in degrees.
double clat
Center latitude of the viewport in degrees.
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Device context class that can use either wxDC or OpenGL for drawing.
void DrawLine(wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2, bool b_hiqual=true)
Draw a line between two points using either wxDC or OpenGL.