OpenCPN Partial API docs
gshhs.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: GSHHS Chart Object (Global Self-consistent, Hierarchical,
5  *High-resolution Shoreline) Author: Jesper Weissglas for the OpenCPN port.
6  *
7  * Derived from http://www.zygrib.org/ and
8  *http://sourceforge.net/projects/qtvlm/ which has the original copyright:
9  * zUGrib: meteorologic GRIB file data viewer
10  * Copyright (C) 2008 - Jacques Zaninetti - http://www.zygrib.org
11  *
12  ***************************************************************************
13  * Copyright (C) 2012 by David S. Register *
14  * *
15  * This program is free software; you can redistribute it and/or modify *
16  * it under the terms of the GNU General Public License as published by *
17  * the Free Software Foundation; either version 2 of the License, or *
18  * (at your option) any later version. *
19  * *
20  * This program is distributed in the hope that it will be useful, *
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
23  * GNU General Public License for more details. *
24  * *
25  * You should have received a copy of the GNU General Public License *
26  * along with this program; if not, write to the *
27  * Free Software Foundation, Inc., *
28  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
29  ***************************************************************************
30  *
31  *
32  */
33 
34 #include <wx/wxprec.h>
35 
36 #ifndef WX_PRECOMP
37 #include <wx/wx.h>
38 #endif
39 
40 #include <wx/file.h>
41 
42 #include "dychart.h"
43 
44 // #if defined(__OCPN__ANDROID__)
45 // #include <GLES2/gl2.h>
46 // #elif defined(__WXQT__) || defined(__WXGTK__)
47 // #include <GL/glew.h>
48 // //#define GL_GLEXT_PROTOTYPES
49 // //#include <GL/gl.h>
50 // //#include <GL/glext.h>
51 // #endif
52 
53 #include "ocpndc.h"
54 
55 #ifdef ocpnUSE_GL
56 #include "glChartCanvas.h"
57 #endif
58 
59 #include "gshhs.h"
60 #include "chartbase.h" // for projections
61 #ifdef ocpnUSE_GL
62  #include "shaders.h"
63 #endif
64 
65 #ifdef __WXMSW__
66 #define __CALL_CONVENTION //__stdcall
67 #else
68 #define __CALL_CONVENTION
69 #endif
70 
71 #include "linmath.h"
72 
73 extern wxString gWorldMapLocation;
74 
75 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
76 static 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"
82  "void main() {\n"
83  " fragColor = color;\n"
84  " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
85  "}\n";
86 
87 static const GLchar *fragment_shader_source =
88  "precision lowp float;\n"
89  "varying vec4 fragColor;\n"
90  "void main() {\n"
91  " gl_FragColor = fragColor;\n"
92  "}\n";
93 
94 static const GLfloat vertices2[] = {
95  0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
96 };
97 
98 static const GLfloat vertices3[] = {
99  0.0f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f,
100 };
101 
102 enum Consts { INFOLOG_LEN = 512 };
103 GLchar infoLog[INFOLOG_LEN];
104 GLint fragment_shader;
105 //GLint shader_program;
106 GLint success;
107 GLint vertex_shader;
108 
109 extern GLint color_tri_shader_program;
110 #endif
111 
112 //-------------------------------------------------------------------------
113 
114 GSHHSChart::GSHHSChart() {
115  reader = NULL;
116  land = wxColor(250, 250, 250);
117  water = wxColor(0, 0, 0);
118 }
119 
120 GSHHSChart::~GSHHSChart() {
121  if (reader) delete reader;
122 }
123 
124 void GSHHSChart::SetColorScheme(ColorScheme scheme) {
125  land = wxColor(170, 175, 80);
126  water = wxColor(170, 195, 240);
127 
128  float dim = 1.0;
129 
130  switch (scheme) {
131  case GLOBAL_COLOR_SCHEME_DUSK:
132  dim = 0.5;
133  break;
134  case GLOBAL_COLOR_SCHEME_NIGHT:
135  dim = 0.25;
136  break;
137  default:
138  return;
139  }
140 
141  land.Set(land.Red() * dim, land.Green() * dim, land.Blue() * dim);
142  water.Set(water.Red() * dim, water.Green() * dim, water.Blue() * dim);
143 }
144 
145 void GSHHSChart::SetColorsDirect(wxColour newLand, wxColour newWater) {
146  land = newLand;
147  water = newWater;
148 }
149 
150 void GSHHSChart::Reset() {
151  if (reader) delete reader;
152  reader = NULL;
153  gshhsCrossesLandReset();
154 }
155 
156 int GSHHSChart::GetMinAvailableQuality() {
157  if (!reader) reader = new GshhsReader();
158  return reader->GetMinAvailableQuality();
159 }
160 
161 int GSHHSChart::GetMaxAvailableQuality() {
162  if (!reader) reader = new GshhsReader();
163  return reader->GetMaxAvailableQuality();
164 }
165 
166 void GSHHSChart::RenderViewOnDC(ocpnDC &dc, ViewPort &vp) {
167  if (!reader) {
168  reader = new GshhsReader();
169  if (reader->GetPolyVersion() < 210 || reader->GetPolyVersion() > 240) {
170  wxLogMessage(
171  _T("GSHHS World chart files have wrong version. Found %d, expected ")
172  _T("210-220."),
173  reader->GetPolyVersion());
174  } else {
175  wxLogMessage(
176  _T("Background world map loaded from GSHHS datafiles found in: ") +
177  gWorldMapLocation);
178  }
179  }
180 
181  reader->drawContinents(dc, vp, water, land);
182 
183  /* this is very inefficient since it draws the entire world*/
184  // reader->drawBoundaries( dc, vp );
185 }
186 
187 GshhsPolyCell::GshhsPolyCell(FILE *fpoly_, int x0_, int y0_,
188  PolygonFileHeader *header_) {
189  header = header_;
190  fpoly = fpoly_;
191  x0cell = x0_;
192  y0cell = y0_;
193 
194  for (int i = 0; i < 6; i++) polyv[i] = NULL;
195 
196  ReadPolygonFile();
197 
198  for (int i = 0; i < GSSH_SUBM * GSSH_SUBM; i++) high_res_map[i] = NULL;
199 }
200 
201 GshhsPolyCell::~GshhsPolyCell() {
202  ClearPolyV();
203 
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];
206 }
207 
208 void GshhsPolyCell::ClearPolyV() {
209  for (int i = 0; i < 6; i++) {
210  delete[] polyv[i];
211  polyv[i] = NULL;
212  }
213 }
214 
215 void GshhsPolyCell::ReadPoly(contour_list &poly) {
216  double X, Y;
217  contour tmp_contour;
218  int32_t num_vertices, num_contours;
219  poly.clear();
220  if (fread(&num_contours, sizeof num_contours, 1, fpoly) != 1) goto fail;
221 
222  for (int c = 0; c < num_contours; c++) {
223  int32_t value;
224  if (fread(&value, sizeof value, 1, fpoly) !=
225  1 || /* discarding hole value */
226  fread(&value, sizeof value, 1, fpoly) != 1)
227  goto fail;
228 
229  num_vertices = value;
230 
231  tmp_contour.clear();
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)
235  goto fail;
236 
237  tmp_contour.push_back(wxRealPoint(X * GSHHS_SCL, Y * GSHHS_SCL));
238  }
239  poly.push_back(tmp_contour);
240  }
241  return;
242 
243 fail:
244  wxLogMessage(_T("gshhs ReadPoly failed"));
245 }
246 
247 void GshhsPolyCell::ReadPolygonFile() {
248  if (!fpoly) return;
249 
250  int pos_data;
251  int tab_data;
252 
253  tab_data = (x0cell / header->pasx) * (180 / header->pasy) +
254  (y0cell + 90) / header->pasy;
255  fseek(fpoly, sizeof(PolygonFileHeader) + tab_data * sizeof(int), SEEK_SET);
256  if (fread(&pos_data, sizeof(int), 1, fpoly) != 1) goto fail;
257 
258  fseek(fpoly, pos_data, SEEK_SET);
259 
260  ReadPoly(poly1);
261  ReadPoly(poly2);
262  ReadPoly(poly3);
263  ReadPoly(poly4);
264  ReadPoly(poly5);
265  return;
266 
267 fail:
268  wxLogMessage(_T("gshhs ReadPolygon failed"));
269 }
270 
271 wxPoint2DDouble GetDoublePixFromLL(ViewPort &vp, double lat, double lon) {
272  wxPoint2DDouble p = vp.GetDoublePixFromLL(lat, lon);
273  p.m_x -= vp.rv_rect.x, p.m_y -= vp.rv_rect.y;
274  return p;
275 }
276 
277 void GshhsPolyCell::DrawPolygonFilled(ocpnDC &pnt, contour_list *p, double dx,
278  ViewPort &vp, wxColor const &color) {
279  if (!p->size()) /* size of 0 is very common, and setting the brush is
280  actually quite slow, so exit early */
281  return;
282 
283  int x, y;
284  unsigned int c, v;
285  int pointCount;
286 
287  int x_old = 0;
288  int y_old = 0;
289 
290  pnt.SetBrush(color);
291 
292  for (c = 0; c < p->size(); c++) {
293  if (!p->at(c).size()) continue;
294 
295  wxPoint *poly_pt = new wxPoint[p->at(c).size()];
296 
297  contour &cp = p->at(c);
298  pointCount = 0;
299 
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)) {
304  pointCount = 0;
305  break;
306  }
307 
308  x = q.m_x, y = q.m_y;
309 
310  if (v == 0 || x != x_old || y != y_old) {
311  poly_pt[pointCount].x = x;
312  poly_pt[pointCount].y = y;
313  pointCount++;
314  x_old = x;
315  y_old = y;
316  }
317  }
318 
319  if (pointCount > 1) pnt.DrawPolygonTessellated(pointCount, poly_pt, 0, 0);
320 
321  delete[] poly_pt;
322  }
323 }
324 
325 #ifdef ocpnUSE_GL
326 
327 typedef union {
328  GLdouble data[6];
329  struct sGLvertex {
330  GLdouble x;
331  GLdouble y;
332  GLdouble z;
333  GLdouble r;
334  GLdouble g;
335  GLdouble b;
336  } info;
337 } GLvertex;
338 
339 #include <list>
340 
341 static std::list<float_2Dpt> g_pv;
342 static std::list<GLvertex *> g_vertexes;
343 static int g_type, g_pos;
344 static float_2Dpt g_p1, g_p2;
345 
346 void __CALL_CONVENTION gshhscombineCallback(GLdouble coords[3],
347  GLdouble *vertex_data[4],
348  GLfloat weight[4],
349  GLdouble **dataOut) {
350  GLvertex *vertex;
351 
352  vertex = new GLvertex();
353  g_vertexes.push_back(vertex);
354 
355  vertex->info.x = coords[0];
356  vertex->info.y = coords[1];
357 
358  *dataOut = vertex->data;
359 }
360 
361 void __CALL_CONVENTION gshhsvertexCallback(GLvoid *arg) {
362  GLvertex *vertex;
363  vertex = (GLvertex *)arg;
364  float_2Dpt p;
365  p.y = vertex->info.x;
366  p.x = vertex->info.y;
367 
368  // convert strips and fans into triangles
369  if (g_type != GL_TRIANGLES) {
370  if (g_pos > 2) {
371  g_pv.push_back(g_p1);
372  g_pv.push_back(g_p2);
373  }
374 
375  if (g_type == GL_TRIANGLE_STRIP)
376  g_p1 = g_p2;
377  else if (g_pos == 0)
378  g_p1 = p;
379  g_p2 = p;
380  }
381 
382  g_pv.push_back(p);
383  g_pos++;
384 }
385 
386 void __CALL_CONVENTION gshhserrorCallback(GLenum errorCode) {
387  const GLubyte *estring;
388  estring = gluErrorString(errorCode);
389  // wxLogMessage( _T("OpenGL Tessellation Error: %s"), estring );
390 }
391 
392 void __CALL_CONVENTION gshhsbeginCallback(GLenum type) {
393  switch (type) {
394  case GL_TRIANGLES:
395  case GL_TRIANGLE_STRIP:
396  case GL_TRIANGLE_FAN:
397  g_type = type;
398  break;
399  default:
400  printf("tess unhandled begin type: %d\n", type);
401  }
402 
403  g_pos = 0;
404 }
405 
406 void __CALL_CONVENTION gshhsendCallback() {}
407 
408 void GshhsPolyCell::DrawPolygonFilledGL(ocpnDC &pnt, contour_list *p, float_2Dpt **pv,
409  int *pvc, ViewPort &vp,
410  wxColor const &color, bool idl) {
411  if (!p->size()) // size of 0 is very common, exit early
412  return;
413 
414  // build the contour vertex array converted to normalized coordinates (if
415  // needed)
416  if (!*pv) {
417  for (unsigned int c = 0; c < p->size(); c++) {
418  if (!p->at(c).size()) continue;
419 
420  contour &cp = p->at(c);
421 
422  GLUtesselator *tobj = gluNewTess();
423 
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);
430 
431  gluTessNormal(tobj, 0, 0, 1);
432  gluTessProperty(tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO);
433 
434  gluTessBeginPolygon(tobj, NULL);
435  gluTessBeginContour(tobj);
436 
437  for (unsigned int v = 0; v < p->at(c).size(); v++) {
438  wxRealPoint &ccp = cp.at(v);
439 
440  if (v == 0 || ccp != cp.at(v - 1)) {
441  GLvertex *vertex = new GLvertex();
442  g_vertexes.push_back(vertex);
443 
444  wxPoint2DDouble q;
445  if (glChartCanvas::HasNormalizedViewPort(vp))
446  q = GetDoublePixFromLL(vp, ccp.y, ccp.x);
447  else // tesselation directly from lat/lon
448  q.m_x = ccp.y, q.m_y = ccp.x;
449 
450  if (vp.m_projection_type != PROJECTION_POLAR) {
451  // need to correctly pick +180 or -180 longitude for projections
452  // that have a discontiguous date line
453 
454  if (idl && ccp.x == 180) {
455  if (vp.m_projection_type != PROJECTION_MERCATOR &&
456  vp.m_projection_type != PROJECTION_EQUIRECTANGULAR)
457  q.m_x -= 360; // lat/lon coordinates
458  }
459  }
460 
461  vertex->info.x = q.m_x;
462  vertex->info.y = q.m_y;
463 
464  gluTessVertex(tobj, (GLdouble *)vertex, (GLdouble *)vertex);
465  }
466  }
467 
468  gluTessEndContour(tobj);
469  gluTessEndPolygon(tobj);
470  gluDeleteTess(tobj);
471 
472  for (std::list<GLvertex *>::iterator it = g_vertexes.begin();
473  it != g_vertexes.end(); it++)
474  delete *it;
475  g_vertexes.clear();
476  }
477 
478  *pv = new float_2Dpt[g_pv.size()];
479  int i = 0;
480  for (std::list<float_2Dpt>::iterator it = g_pv.begin(); it != g_pv.end();
481  it++)
482  (*pv)[i++] = *it;
483 
484  *pvc = g_pv.size();
485  g_pv.clear();
486  }
487 
488 #if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
489 
490 #if 0
491  // Are the shaders ready?
492  if (!vertex_shader) {
493  /* 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);
498  if (!success) {
499  glGetShaderInfoLog(vertex_shader, INFOLOG_LEN, NULL, infoLog);
500  printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
501  }
502  }
503 
504  if (!fragment_shader) {
505  /* 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);
510  if (!success) {
511  glGetShaderInfoLog(fragment_shader, INFOLOG_LEN, NULL, infoLog);
512  printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
513  }
514  }
515 
516  if (!shader_program) {
517  /* Link shaders */
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);
523  if (!success) {
524  glGetProgramInfoLog(shader_program, INFOLOG_LEN, NULL, infoLog);
525  printf("ERROR::SHADER::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
526  }
527  }
528 #endif
529  GLuint vbo = 0;
530 
531  // Build the shader viewport transform matrix
532  mat4x4 m, mvp;
533  mat4x4_identity(m);
534  mat4x4_scale_aniso(mvp, m, 2.0 / (float)vp.pix_width,
535  2.0 / (float)vp.pix_height, 1.0);
536  mat4x4_translate_in_place(mvp, -vp.pix_width / 2, vp.pix_height / 2, 0);
537 
538  if (glChartCanvas::HasNormalizedViewPort(vp)) {
539 #if 0
540  GLint pos = glGetAttribLocation(color_tri_shader_program, "position");
541  glVertexAttribPointer(pos, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), *pv);
542  glEnableVertexAttribArray(pos);
543 
544  // FIXME glUniformMatrix4fv( matloc, 1, GL_FALSE, (const
545  // GLfloat*)vp.vp_transform);
546  mat4x4 m;
547  mat4x4_identity(m);
548  GLint tmatloc = glGetUniformLocation(color_tri_shader_program, "TransformMatrix");
549  glUniformMatrix4fv(tmatloc, 1, GL_FALSE, (const GLfloat*)m);
550 
551  glUseProgram(color_tri_shader_program);
552  glDrawArrays(GL_TRIANGLES, 0, *pvc);
553 #endif
554  } else {
555  float *pvt = new float[2 * (*pvc)];
556  for (int i = 0; i < *pvc; i++) {
557  float_2Dpt *pc = *pv + i;
558  wxPoint2DDouble q = vp.GetDoublePixFromLL(pc->y, pc->x);
559  pvt[i * 2] = q.m_x;
560  pvt[(i * 2) + 1] = q.m_y;
561  }
562 
563  GLShaderProgram *shader = pcolor_tri_shader_program[pnt.m_canvasIndex];
564  shader->Bind();
565 
566  float colorv[4];
567  colorv[0] = color.Red() / float(256);
568  colorv[1] = color.Green() / float(256);
569  colorv[2] = color.Blue() / float(256);
570  colorv[3] = 1.0;
571  shader->SetUniform4fv("color", colorv);
572 
573  shader->SetAttributePointerf("position", pvt);
574 
575  glDrawArrays(GL_TRIANGLES, 0, *pvc);
576 
577  delete[] pvt;
578  glDeleteBuffers(1, &vbo);
579  shader->UnBind();
580  }
581 
582 
583 #else
584 #endif
585 }
586 #endif //#ifdef ocpnUSE_GL
587 
588 #define DRAW_POLY_FILLED(POLY, COL) \
589  if (POLY) DrawPolygonFilled(pnt, POLY, dx, vp, COL);
590 #define DRAW_POLY_FILLED_GL(NUM, COL) \
591  DrawPolygonFilledGL(pnt,&poly##NUM, &polyv[NUM], &polyc[NUM], vp, COL, idl);
592 
593 void GshhsPolyCell::drawMapPlain(ocpnDC &pnt, double dx, ViewPort &vp,
594  wxColor seaColor, wxColor landColor,
595  bool idl) {
596 #ifdef ocpnUSE_GL
597  if (!pnt.GetDC()) { // opengl
598 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
599 #define NORM_FACTOR 4096.0
600  if (dx && (vp.m_projection_type == PROJECTION_MERCATOR ||
601  vp.m_projection_type == PROJECTION_EQUIRECTANGULAR)) {
602  double ts =
603  40058986 * NORM_FACTOR; /* 360 degrees in normalized viewport */
604  glPushMatrix();
605  glTranslated(dx > 0 ? ts : -ts, 0, 0);
606  }
607 #endif
608  DRAW_POLY_FILLED_GL(1, landColor);
609  DRAW_POLY_FILLED_GL(2, seaColor);
610  DRAW_POLY_FILLED_GL(3, landColor);
611  DRAW_POLY_FILLED_GL(4, seaColor);
612  DRAW_POLY_FILLED_GL(5, landColor);
613 
614 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
615  if (dx) glPopMatrix();
616 #endif
617 
618  } else
619 #endif
620  {
621  DRAW_POLY_FILLED(&poly1, landColor);
622  DRAW_POLY_FILLED(&poly2, seaColor);
623  DRAW_POLY_FILLED(&poly3, landColor);
624  DRAW_POLY_FILLED(&poly4, seaColor);
625  DRAW_POLY_FILLED(&poly5, landColor);
626  }
627 }
628 
629 void GshhsPolyCell::DrawPolygonContour(ocpnDC &pnt, contour_list *p, double dx,
630  ViewPort &vp) {
631  double x1, y1, x2, y2;
632  double long_max, lat_max, long_min, lat_min;
633 
634  long_min = (double)x0cell;
635  lat_min = (double)y0cell;
636  long_max = ((double)x0cell + (double)header->pasx);
637  lat_max = ((double)y0cell + (double)header->pasy);
638 
639  // qWarning() << long_min << "," << lat_min << long_max << "," << lat_max;
640 
641  for (unsigned int i = 0; i < p->size(); i++) {
642  if (!p->at(i).size()) continue;
643 
644  unsigned int v;
645  for (v = 0; v < (p->at(i).size() - 1); v++) {
646  x1 = p->at(i).at(v).x;
647  y1 = p->at(i).at(v).y;
648  x2 = p->at(i).at(v + 1).x;
649  y2 = p->at(i).at(v + 1).y;
650 
651  // Elimination des traits verticaux et horizontaux
652  if ((((x1 == x2) && ((x1 == long_min) || (x1 == long_max))) ||
653  ((y1 == y2) && ((y1 == lat_min) || (y1 == lat_max)))) == 0) {
654  wxPoint2DDouble AB = GetDoublePixFromLL(vp, x1 + dx, y1);
655  wxPoint2DDouble CD = GetDoublePixFromLL(vp, x2 + dx, y1);
656  pnt.DrawLine(AB.m_x, AB.m_y, CD.m_x, CD.m_y);
657  }
658  }
659 
660  x1 = p->at(i).at(v).x;
661  y1 = p->at(i).at(v).y;
662  x2 = p->at(i).at(0).x;
663  y2 = p->at(i).at(0).y;
664 
665  if ((((x1 == x2) && ((x1 == long_min) || (x1 == long_max))) ||
666  ((y1 == y2) && ((y1 == lat_min) || (y1 == lat_max)))) == 0) {
667  wxPoint2DDouble AB = GetDoublePixFromLL(vp, x1 + dx, y1);
668  wxPoint2DDouble CD = GetDoublePixFromLL(vp, x2 + dx, y1);
669  pnt.DrawLine(AB.m_x, AB.m_y, CD.m_x, CD.m_y);
670  }
671  }
672 }
673 
674 #define DRAW_POLY_CONTOUR(POLY) \
675  if (POLY) DrawPolygonContour(pnt, POLY, dx, vp);
676 
677 void GshhsPolyCell::drawSeaBorderLines(ocpnDC &pnt, double dx, ViewPort &vp) {
678  coasts.clear();
679  DRAW_POLY_CONTOUR(&poly1)
680  DRAW_POLY_CONTOUR(&poly2)
681  DRAW_POLY_CONTOUR(&poly3)
682  DRAW_POLY_CONTOUR(&poly4)
683  DRAW_POLY_CONTOUR(&poly5)
684 }
685 
686 //========================================================================
687 
688 GshhsPolyReader::GshhsPolyReader(int quality) {
689  fpoly = NULL;
690 
691  for (int i = 0; i < 360; i++) {
692  for (int j = 0; j < 180; j++) {
693  allCells[i][j] = NULL;
694  }
695  }
696  currentQuality = -1;
697  polyHeader.version = -1;
698  InitializeLoadQuality(quality);
699 }
700 
701 //-------------------------------------------------------------------------
702 GshhsPolyReader::~GshhsPolyReader() {
703  for (int i = 0; i < 360; i++) {
704  for (int j = 0; j < 180; j++) {
705  if (allCells[i][j] != NULL) {
706  delete allCells[i][j];
707  allCells[i][j] = NULL;
708  }
709  }
710  }
711 
712  if (fpoly) {
713  fclose(fpoly);
714  }
715 }
716 
717 //-------------------------------------------------------------------------
718 int GshhsPolyReader::ReadPolyVersion() {
719  wxString fname = GshhsReader::getFileName_Land(0);
720  if (fpoly) fclose(fpoly);
721  fpoly = fopen(fname.mb_str(), "rb");
722 
723  /* init header */
724  if (!fpoly) return 0;
725 
726  readPolygonFileHeader(fpoly, &polyHeader);
727 
728  return polyHeader.version;
729 }
730 
731 void GshhsPolyReader::InitializeLoadQuality(
732  int quality) // 5 levels: 0=low ... 4=full
733 {
734  if (currentQuality != quality) {
735  currentQuality = quality;
736 
737  wxString fname = GshhsReader::getFileName_Land(quality);
738 
739  if (fpoly) fclose(fpoly);
740 
741  fpoly = fopen(fname.mb_str(), "rb");
742  if (fpoly) readPolygonFileHeader(fpoly, &polyHeader);
743 
744  for (int i = 0; i < 360; i++) {
745  for (int j = 0; j < 180; j++) {
746  if (allCells[i][j] != NULL) {
747  delete allCells[i][j];
748  allCells[i][j] = NULL;
749  }
750  }
751  }
752  }
753 }
754 
755 static inline bool my_intersects(const wxLineF &line1, const wxLineF &line2) {
756  double x1 = line1.m_p1.x, y1 = line1.m_p1.y, x2 = line1.m_p2.x,
757  y2 = line1.m_p2.y;
758  double x3 = line2.m_p1.x, y3 = line2.m_p1.y, x4 = line2.m_p2.x,
759  y4 = line2.m_p2.y;
760 
761  // implementation is based on Graphics Gems III's "Faster Line Segment
762  // Intersection"
763  double ax = x2 - x1, ay = y2 - y1;
764  double bx = x3 - x4, by = y3 - y4;
765  double cx = x1 - x3, cy = y1 - y3;
766 
767 #define INTER_LIMIT 1e-7
768  double denominator = ay * bx - ax * by;
769  if (denominator < 1e-10) {
770  if (fabs((y1 * ax - ay * x1) * bx - (y3 * bx - by * x3) * ax) > INTER_LIMIT)
771  return false; /* different intercepts, no intersection */
772  if (fabs((x1 * ay - ax * y1) * by - (x3 * by - bx * y3) * ay) > INTER_LIMIT)
773  return false; /* different intercepts, no intersection */
774 
775  return true;
776  }
777 
778  const double reciprocal = 1 / denominator;
779  const double na = (by * cx - bx * cy) * reciprocal;
780 
781  if (na < -INTER_LIMIT || na > 1 + INTER_LIMIT) return false;
782 
783  const double nb = (ax * cy - ay * cx) * reciprocal;
784  if (nb < -INTER_LIMIT || nb > 1 + INTER_LIMIT) return false;
785 
786  return true;
787 }
788 
789 bool GshhsPolyReader::crossing1(wxLineF trajectWorld) {
790  double x1 = trajectWorld.p1().x, y1 = trajectWorld.p1().y;
791  double x2 = trajectWorld.p2().x, y2 = trajectWorld.p2().y;
792 
793  int clonmin, clonmax, clatmax, clatmin;
794  clonmin = (int)floor(GSSH_SUBM * wxMin(x1, x2));
795  clonmax = (int)ceil(GSSH_SUBM * wxMax(x1, x2));
796 
797  if (clonmin < 0) {
798  clonmin += GSSH_SUBM * 360;
799  clonmax += GSSH_SUBM * 360;
800  }
801 
802  if (clonmax - clonmin > GSSH_SUBM * 180) { /* dont go long way around world */
803  clonmin = (int)floor(GSSH_SUBM * wxMax(x1, x2)) - GSSH_SUBM * 360;
804  clonmax = (int)ceil(GSSH_SUBM * wxMin(x1, x2));
805  }
806 
807  clatmin = (int)floor(GSSH_SUBM * wxMin(y1, y2));
808  clatmax = (int)ceil(GSSH_SUBM * wxMax(y1, y2));
809  wxASSERT(clatmin >= -GSSH_SUBM * 90 && clatmax <= GSSH_SUBM * 89);
810 
811  // TODO: optimize by traversing only the cells the segment passes through,
812  // rather than all of the cells which fit in the bounding box,
813  // this may make a worthwhile difference for longer segments in some
814  // cases.
815  int clon, clonx, clat;
816  for (clon = clonmin; clon < clonmax; clon++) {
817  clonx = clon;
818  while (clonx < 0) clonx += GSSH_SUBM * 360;
819  while (clonx >= GSSH_SUBM * 360) clonx -= GSSH_SUBM * 360;
820 
821  wxASSERT(clonx >= 0 && clonx < GSSH_SUBM * 360);
822 
823  if (clonx < GSSH_SUBM * 180) {
824  if (x1 > 180) x1 -= 360;
825  if (x2 > 180) x2 -= 360;
826  } else {
827  if (x1 < 180) x1 += 360;
828  if (x2 < 180) x2 += 360;
829  }
830 
831  wxLineF rtrajectWorld(x1, y1, x2, y2);
832 
833  for (clat = clatmin; clat < clatmax; clat++) {
834  int cloni = clonx / GSSH_SUBM,
835  clati = (GSSH_SUBM * 90 + clat) / GSSH_SUBM;
836  GshhsPolyCell *&cel = allCells[cloni][clati];
837  if (!cel) {
838  mutex1.Lock();
839  if (!cel) {
840  /* load the needed cell from disk */
841  cel = new GshhsPolyCell(fpoly, cloni, clati - 90, &polyHeader);
842  wxASSERT(cel);
843  }
844  mutex1.Unlock();
845  }
846 
847  int hash = GSSH_SUBM * (GSSH_SUBM * (90 - clati) + clat - cloni) + clonx;
848  std::vector<wxLineF> *&high_res_map = cel->high_res_map[hash];
849  wxASSERT(hash >= 0 && hash < GSSH_SUBM * GSSH_SUBM);
850  if (!high_res_map) {
851  mutex2.Lock();
852  if (!high_res_map) {
853  /* Build the needed sub cell of line segments from the cell */
854  contour_list &poly1 = cel->getPoly1();
855 
856  double minlat = (double)clat / GSSH_SUBM,
857  maxlat = (double)(clat + 1) / GSSH_SUBM;
858  double minlon = (double)clonx / GSSH_SUBM,
859  maxlon = (double)(clonx + 1) / GSSH_SUBM;
860  high_res_map = new std::vector<wxLineF>;
861  for (unsigned int pi = 0; pi < poly1.size(); pi++) {
862  contour &c = poly1[pi];
863  double lx = c[c.size() - 1].x, ly = c[c.size() - 1].y;
864  /* must compute states because sometimes a
865  segment starts and ends outside our cell, but passes
866  through it so must be included */
867  int lstatex = lx < minlon ? -1 : lx > maxlon ? 1 : 0;
868  int lstatey = ly < minlat ? -1 : ly > maxlat ? 1 : 0;
869 
870  for (unsigned int pj = 0; pj < c.size(); pj++) {
871  double clon = c[pj].x, clat = c[pj].y;
872  // gshhs data shouldn't, but sometimes contains zero segments
873  // which enlarges our table, but
874  // more importantly, the fast segment intersection test
875  // and doesn't correctly account for it
876  if (lx == clon && ly == clat) continue;
877 
878  int statex = clon < minlon ? -1 : clon > maxlon ? 1 : 0;
879  int statey = clat < minlat ? -1 : clat > maxlat ? 1 : 0;
880 
881  if ((!statex || lstatex != statex) &&
882  (!statey || lstatey != statey))
883  high_res_map->push_back(wxLineF(lx, ly, clon, clat));
884 
885  lx = clon, ly = clat;
886  lstatex = statex, lstatey = statey;
887  }
888  }
889  }
890  mutex2.Unlock();
891  }
892 
893  for (std::vector<wxLineF>::iterator it2 = high_res_map->begin();
894  it2 != high_res_map->end(); it2++)
895  if (my_intersects(rtrajectWorld, *it2)) return true;
896  }
897  }
898 
899  return false;
900 }
901 
902 void GshhsPolyReader::readPolygonFileHeader(FILE *polyfile,
903  PolygonFileHeader *header) {
904  fseek(polyfile, 0, SEEK_SET);
905  if (fread(header, sizeof(PolygonFileHeader), 1, polyfile) != 1)
906  wxLogMessage(_T("gshhs ReadPolygonFileHeader failed"));
907 }
908 
909 //-------------------------------------------------------------------------
910 void GshhsPolyReader::drawGshhsPolyMapPlain(ocpnDC &pnt, ViewPort &vp,
911  wxColor const &seaColor,
912  wxColor const &landColor) {
913  if (!fpoly) return;
914 
915  pnt.SetPen(wxNullPen);
916 
917  int clonmin, clonmax, clatmax, clatmin; // cellules visibles
918  LLBBox bbox = vp.GetBBox();
919  clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
920  clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
921  if (clatmin <= 0) clatmin--;
922  if (clatmax >= 0) clatmax++;
923  if (clonmin <= 0) clonmin--;
924  if (clonmax >= 0) clonmax++;
925  int dx, clon, clonx, clat;
926  GshhsPolyCell *cel;
927 
928  ViewPort nvp = vp;
929 #ifdef ocpnUSE_GL
930  if (!pnt.GetDC()) { // opengl
931  // clear cached data when the projection changes
932  if (vp.m_projection_type != last_rendered_vp.m_projection_type ||
933  (last_rendered_vp.m_projection_type == PROJECTION_POLAR &&
934  last_rendered_vp.clat * vp.clat <= 0)) {
935  last_rendered_vp = vp;
936  for (int clon = 0; clon < 360; clon++)
937  for (int clat = 0; clat < 180; clat++)
938  if (allCells[clon][clat]) allCells[clon][clat]->ClearPolyV();
939  }
940 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
941  glEnableClientState(GL_VERTEX_ARRAY);
942 
943  // use a viewport that allows the vertexes to be reused over many frames
944  // TODO fix for multicanvas
945  if (glChartCanvas::HasNormalizedViewPort(vp)) {
946  glPushMatrix();
947  glChartCanvas::MultMatrixViewPort(vp);
948  nvp = glChartCanvas::NormalizedViewPort(vp);
949  }
950 #endif
951  }
952 #endif
953  for (clon = clonmin; clon < clonmax; clon++) {
954  clonx = clon;
955  while (clonx < 0) clonx += 360;
956  while (clonx >= 360) clonx -= 360;
957 
958  for (clat = clatmin; clat < clatmax; clat++) {
959  if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
960  if (allCells[clonx][clat + 90] == NULL) {
961  cel = new GshhsPolyCell(fpoly, clonx, clat, &polyHeader);
962  wxASSERT(cel);
963  allCells[clonx][clat + 90] = cel;
964  } else {
965  cel = allCells[clonx][clat + 90];
966  }
967  bool idl = false;
968 
969  // only mercator needs the special idl fixes
970  if (vp.m_projection_type != PROJECTION_MERCATOR &&
971  vp.m_projection_type != PROJECTION_EQUIRECTANGULAR)
972  dx = 0;
973  else if (pnt.GetDC()) // dc
974  dx = clon - clonx;
975  else { // opengl
976  int clonn = clonx;
977  if (clonn >= 180) {
978  clonn -= 360;
979  idl = true;
980  }
981  if (vp.clon - clonn > 180)
982  dx = 1;
983  else if (vp.clon - clonn < -180)
984  dx = -1;
985  else
986  dx = 0;
987  }
988 
989  cel->drawMapPlain(pnt, dx, nvp, seaColor, landColor, idl);
990  }
991  }
992  }
993 
994 #ifdef ocpnUSE_GL
995 #if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
996  if (!pnt.GetDC()) { // opengl
997  if (glChartCanvas::HasNormalizedViewPort(vp)) glPopMatrix();
998  glDisableClientState(GL_VERTEX_ARRAY);
999  }
1000 #endif
1001 #endif
1002 }
1003 
1004 //-------------------------------------------------------------------------
1005 void GshhsPolyReader::drawGshhsPolyMapSeaBorders(ocpnDC &pnt, ViewPort &vp) {
1006  if (!fpoly) return;
1007  int clonmin, clonmax, clatmax, clatmin; // cellules visibles
1008  LLBBox bbox = vp.GetBBox();
1009  clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
1010  clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
1011 
1012  int dx, clon, clonx, clat;
1013  GshhsPolyCell *cel;
1014 
1015  for (clon = clonmin; clon < clonmax; clon++) {
1016  clonx = clon;
1017  while (clonx < 0) clonx += 360;
1018  while (clonx >= 360) clonx -= 360;
1019 
1020  for (clat = clatmin; clat < clatmax; clat++) {
1021  if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
1022  if (allCells[clonx][clat + 90] == NULL) {
1023  cel = new GshhsPolyCell(fpoly, clonx, clat, &polyHeader);
1024  wxASSERT(cel);
1025  allCells[clonx][clat + 90] = cel;
1026  } else {
1027  cel = allCells[clonx][clat + 90];
1028  }
1029  dx = clon - clonx;
1030  cel->drawSeaBorderLines(pnt, dx, vp);
1031  }
1032  }
1033  }
1034 }
1035 
1036 int GshhsPolygon::readInt4() {
1037  union {
1038  unsigned int n;
1039  unsigned char buf[4];
1040  } res;
1041 
1042  unsigned char in[4];
1043 
1044  int nb = 0;
1045  nb += fread(&in, 1, 4, file);
1046  res.buf[3] = in[0];
1047  res.buf[2] = in[1];
1048  res.buf[1] = in[2];
1049  res.buf[0] = in[3];
1050 
1051  if (nb != 4) {
1052  ok = false;
1053  res.n = 0;
1054  }
1055 
1056  return res.n;
1057 }
1058 
1059 int GshhsPolygon::readInt2() {
1060  union {
1061  unsigned int n;
1062  unsigned char buf[4];
1063  } v;
1064 
1065  int nb = 0;
1066  nb += fread(&v.buf[0], 2, 1, file);
1067  if (nb != 2) {
1068  ok = false;
1069  v.n = 0;
1070  }
1071  return v.buf[1] << 8 | v.buf[0];
1072 }
1073 
1074 GshhsPolygon::GshhsPolygon(FILE *file_) {
1075  file = file_;
1076  ok = true;
1077  id = readInt4();
1078  n = readInt4();
1079  flag = readInt4();
1080  west = readInt4() * 1e-6;
1081  east = readInt4() * 1e-6;
1082  south = readInt4() * 1e-6;
1083  north = readInt4() * 1e-6;
1084  area = readInt4();
1085 
1086  if (((flag >> 8) & 255) >= 7) { // GSHHS Release 2.0
1087  areaFull = readInt4();
1088  container = readInt4();
1089  ancestor = readInt4();
1090 
1091  greenwich = (flag >> 16) & 1;
1092  antarctic = (west == 0 && east == 360);
1093  if (ok) {
1094  double x = 0, y = 0;
1095  for (int i = 0; i < n; i++) {
1096  x = readInt4() * 1e-6;
1097  if (greenwich && x > 270) x -= 360;
1098  y = readInt4() * 1e-6;
1099  lsPoints.push_back(new GshhsPoint(x, y));
1100  }
1101  if (antarctic) {
1102  lsPoints.insert(lsPoints.begin(), new GshhsPoint(360, y));
1103  lsPoints.insert(lsPoints.begin(), new GshhsPoint(360, -90));
1104  lsPoints.push_back(new GshhsPoint(0, -90));
1105  }
1106  }
1107  } else {
1108  greenwich = (flag >> 16) & 1;
1109  antarctic = (west == 0 && east == 360);
1110  if (ok) {
1111  for (int i = 0; i < n; i++) {
1112  double x = 0, y = 0;
1113  x = readInt4() * 1e-6;
1114  if (greenwich && x > 270) x -= 360;
1115  y = readInt4() * 1e-6;
1116  lsPoints.push_back(new GshhsPoint(x, y));
1117  }
1118  }
1119  }
1120 }
1121 
1122 //--------------------------------------------------------
1123 
1124 GshhsPolygon::~GshhsPolygon() {
1125  std::vector<GshhsPoint *>::iterator itp;
1126  for (itp = lsPoints.begin(); itp != lsPoints.end(); itp++) {
1127  delete *itp;
1128  *itp = NULL;
1129  }
1130  lsPoints.clear();
1131 }
1132 
1133 //==========================================================
1134 
1135 GshhsReader::GshhsReader() {
1136  maxQualityAvailable = -1;
1137  minQualityAvailable = -1;
1138 
1139  for (int i = 0; i < 5; i++) {
1140  qualityAvailable[i] = false;
1141  if (GshhsReader::gshhsFilesExists(i)) {
1142  qualityAvailable[i] = true;
1143  if (minQualityAvailable < 0) minQualityAvailable = i;
1144  maxQualityAvailable = i;
1145  }
1146  }
1147 
1148  if (maxQualityAvailable < 0) {
1149  wxString msg(
1150  _T("Unable to initialize background world map. No GSHHS datafiles ")
1151  _T("found in "));
1152  msg += gWorldMapLocation;
1153  wxLogMessage(msg);
1154  }
1155 
1156  // int q = selectBestQuality( vp );
1157  // if( ! qualityAvailable[q] ) {
1158  // int q = maxQualityAvailable;
1159  // }
1160 
1161  int q = 0;
1162 
1163  gshhsPoly_reader = new GshhsPolyReader(q);
1164 
1165  for (int qual = 0; qual < 5; qual++) {
1166  lsPoly_boundaries[qual] = new std::vector<GshhsPolygon *>;
1167  lsPoly_rivers[qual] = new std::vector<GshhsPolygon *>;
1168  }
1169 
1170  quality = -1;
1171  LoadQuality(q);
1172 }
1173 
1174 int GshhsReader::ReadPolyVersion() {
1175  return gshhsPoly_reader->ReadPolyVersion();
1176 }
1177 
1178 //-------------------------------------------------------
1179 
1180 GshhsReader::~GshhsReader() {
1181  clearLists();
1182  delete gshhsPoly_reader;
1183 }
1184 
1185 //-----------------------------------------------------------------------
1186 void GshhsReader::clearLists() {
1187  std::vector<GshhsPolygon *>::iterator itp;
1188  for (int qual = 0; qual < 5; qual++) {
1189  for (itp = lsPoly_boundaries[qual]->begin();
1190  itp != lsPoly_boundaries[qual]->end(); itp++) {
1191  delete *itp;
1192  *itp = NULL;
1193  }
1194  for (itp = lsPoly_rivers[qual]->begin(); itp != lsPoly_rivers[qual]->end();
1195  itp++) {
1196  delete *itp;
1197  *itp = NULL;
1198  }
1199 
1200  lsPoly_boundaries[qual]->clear();
1201  lsPoly_rivers[qual]->clear();
1202  delete lsPoly_boundaries[qual];
1203  delete lsPoly_rivers[qual];
1204  }
1205 }
1206 //-----------------------------------------------------------------------
1207 
1208 wxString GshhsReader::getNameExtension(int quality) {
1209  wxString ext;
1210  switch (quality) {
1211  case 0:
1212  ext = _T("c");
1213  break;
1214  case 1:
1215  ext = _T("l");
1216  break;
1217  case 2:
1218  ext = _T("i");
1219  break;
1220  case 3:
1221  ext = _T("h");
1222  break;
1223  case 4:
1224  ext = _T("f");
1225  break;
1226  default:
1227  ext = _T("l");
1228  break;
1229  }
1230  return ext;
1231 }
1232 
1233 wxString GshhsReader::getFileName_Land(int quality) {
1234  wxString ext = GshhsReader::getNameExtension(quality);
1235  wxString fname =
1236  gWorldMapLocation + wxString::Format(_T("poly-%c-1.dat"), ext.GetChar(0));
1237  return fname;
1238 }
1239 
1240 wxString GshhsReader::getFileName_boundaries(int quality) {
1241  wxString ext = GshhsReader::getNameExtension(quality);
1242  wxString fname = gWorldMapLocation +
1243  wxString::Format(_T("wdb_borders_%c.b"), ext.GetChar(0));
1244  return fname;
1245 }
1246 
1247 wxString GshhsReader::getFileName_rivers(int quality) {
1248  wxString ext = GshhsReader::getNameExtension(quality);
1249  wxString fname = gWorldMapLocation +
1250  wxString::Format(_T("wdb_rivers_%c.b"), ext.GetChar(0));
1251  return fname;
1252 }
1253 
1254 //-----------------------------------------------------------------------
1255 bool GshhsReader::gshhsFilesExists(int quality) {
1256  if (!wxFile::Access(GshhsReader::getFileName_Land(quality), wxFile::read))
1257  return false;
1258  // Borders disabled anyway since the perf optimizations if( ! wxFile::Access(
1259  // GshhsReader::getFileName_boundaries( quality ), wxFile::read ) ) return
1260  // false; Rivers disabled anyway since the perf optimizations if( !
1261  // wxFile::Access( GshhsReader::getFileName_rivers( quality ), wxFile::read ) )
1262  // return false;
1263 
1264  return true;
1265 }
1266 
1267 //-----------------------------------------------------------------------
1268 void GshhsReader::LoadQuality(int newQuality) // 5 levels: 0=low ... 4=full
1269 {
1270  if (quality == newQuality) return;
1271 
1272  wxStopWatch perftimer;
1273 
1274  wxString fname;
1275 
1276  quality = newQuality;
1277  if (quality < 0)
1278  quality = 0;
1279  else if (quality > 4)
1280  quality = 4;
1281 
1282  gshhsPoly_reader->InitializeLoadQuality(quality);
1283 #if 0 /* too slow to load the whole world at once */
1284  if( lsPoly_boundaries[quality]->size() == 0 ) {
1285  fname = getFileName_boundaries( quality );
1286  file = fopen( fname.mb_str(), "rb" );
1287 
1288  if( file != NULL ) {
1289  ok = true;
1290  while( ok ) {
1291  GshhsPolygon *poly = new GshhsPolygon( file );
1292 
1293  ok = poly->isOk();
1294  if( ok )
1295  if( poly->getLevel() < 2 )
1296  lsPoly_boundaries[quality]->push_back( poly );
1297  else delete poly;
1298  else delete poly;
1299  }
1300  fclose( file );
1301  }
1302  }
1303 
1304  if( lsPoly_rivers[quality]->size() == 0 ) {
1305  fname = getFileName_rivers( quality );
1306  file = fopen( fname.mb_str(), "rb" );
1307  if( file != NULL ) {
1308  ok = true;
1309  while( ok ) {
1310  GshhsPolygon *poly = new GshhsPolygon( file );
1311  ok = poly->isOk();
1312  if( ok ) {
1313  lsPoly_rivers[quality]->push_back( poly );
1314  }
1315  else delete poly;
1316  }
1317  fclose( file );
1318  }
1319  }
1320 #endif
1321  wxLogMessage(_T("Loading World Chart Q=%d in %ld ms."), quality,
1322  perftimer.Time());
1323 }
1324 
1325 //-----------------------------------------------------------------------
1326 std::vector<GshhsPolygon *> &GshhsReader::getList_boundaries() {
1327  return *lsPoly_boundaries[quality];
1328 }
1329 //-----------------------------------------------------------------------
1330 std::vector<GshhsPolygon *> &GshhsReader::getList_rivers() {
1331  return *lsPoly_rivers[quality];
1332 }
1333 
1334 //=====================================================================
1335 
1336 int GshhsReader::GSHHS_scaledPoints(GshhsPolygon *pol, wxPoint *pts,
1337  double declon, ViewPort &vp) {
1338  LLBBox box;
1339  box.Set(pol->south, pol->west + declon, pol->north, pol->east + declon);
1340  if (vp.GetBBox().IntersectOut(box)) return 0;
1341 
1342  // Remove small polygons.
1343 
1344  wxPoint2DDouble p1 = GetDoublePixFromLL(vp, pol->west + declon, pol->north);
1345  wxPoint2DDouble p2 = GetDoublePixFromLL(vp, pol->east + declon, pol->south);
1346 
1347  if (p1.m_x == p2.m_x && p1.m_y == p2.m_y) return 0;
1348 
1349  double x, y;
1350  std::vector<GshhsPoint *>::iterator itp;
1351  int xx, yy, oxx = 0, oyy = 0;
1352  int j = 0;
1353 
1354  for (itp = (pol->lsPoints).begin(); itp != (pol->lsPoints).end(); itp++) {
1355  x = (*itp)->lon + declon;
1356  y = (*itp)->lat;
1357  wxPoint2DDouble p = GetDoublePixFromLL(vp, y, x);
1358  xx = p.m_x, yy = p.m_y;
1359  if (j == 0 || (oxx != xx || oyy != yy)) { // Remove close points
1360  oxx = xx;
1361  oyy = yy;
1362  pts[j].x = xx;
1363  pts[j].y = yy;
1364  j++;
1365  }
1366  }
1367 
1368  return j;
1369 }
1370 
1371 //-----------------------------------------------------------------------
1372 void GshhsReader::GsshDrawLines(ocpnDC &pnt, std::vector<GshhsPolygon *> &lst,
1373  ViewPort &vp, bool isClosed) {
1374  std::vector<GshhsPolygon *>::iterator iter;
1375  GshhsPolygon *pol;
1376  wxPoint *pts = NULL;
1377  int i;
1378  int nbp;
1379 
1380  int nbmax = 10000;
1381  pts = new wxPoint[nbmax];
1382  wxASSERT(pts);
1383 
1384  for (i = 0, iter = lst.begin(); iter != lst.end(); iter++, i++) {
1385  pol = *iter;
1386 
1387  if (nbmax < pol->n + 2) {
1388  nbmax = pol->n + 2;
1389  delete[] pts;
1390  pts = new wxPoint[nbmax];
1391  wxASSERT(pts);
1392  }
1393 
1394  nbp = GSHHS_scaledPoints(pol, pts, 0, vp);
1395  if (nbp > 1) {
1396  if (pol->isAntarctic()) {
1397  pts++;
1398  nbp -= 2;
1399  pnt.DrawLines(nbp, pts);
1400  pts--;
1401  } else {
1402  pnt.DrawLines(nbp, pts);
1403  if (isClosed)
1404  pnt.DrawLine(pts[0].x, pts[0].y, pts[nbp - 1].x, pts[nbp - 1].y);
1405  }
1406  }
1407 
1408  nbp = GSHHS_scaledPoints(pol, pts, -360, vp);
1409  if (nbp > 1) {
1410  if (pol->isAntarctic()) {
1411  pts++;
1412  nbp -= 2;
1413  pnt.DrawLines(nbp, pts);
1414  pts--;
1415  } else {
1416  pnt.DrawLines(nbp, pts);
1417  if (isClosed)
1418  pnt.DrawLine(pts[0].x, pts[0].y, pts[nbp - 1].x, pts[nbp - 1].y);
1419  }
1420  }
1421  }
1422  delete[] pts;
1423 }
1424 
1425 //-----------------------------------------------------------------------
1426 void GshhsReader::drawContinents(ocpnDC &pnt, ViewPort &vp,
1427  wxColor const &seaColor,
1428  wxColor const &landColor) {
1429  LoadQuality(selectBestQuality(vp));
1430  gshhsPoly_reader->drawGshhsPolyMapPlain(pnt, vp, seaColor, landColor);
1431 }
1432 
1433 //-----------------------------------------------------------------------
1434 void GshhsReader::drawSeaBorders(ocpnDC &pnt, ViewPort &vp) {
1435  pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1436  gshhsPoly_reader->drawGshhsPolyMapSeaBorders(pnt, vp);
1437 }
1438 
1439 //-----------------------------------------------------------------------
1440 void GshhsReader::drawBoundaries(ocpnDC &pnt, ViewPort &vp) {
1441  pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1442 
1443  if (pnt.GetDC()) {
1444  wxPen *pen = wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxPENSTYLE_DOT);
1445  pnt.SetPen(*pen);
1446  } else {
1447  wxPen *pen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 80), 2,
1448  wxPENSTYLE_LONG_DASH);
1449  pnt.SetPen(*pen);
1450  }
1451  GsshDrawLines(pnt, getList_boundaries(), vp, false);
1452 }
1453 
1454 //-----------------------------------------------------------------------
1455 void GshhsReader::drawRivers(ocpnDC &pnt, ViewPort &vp) {
1456  GsshDrawLines(pnt, getList_rivers(), vp, false);
1457 }
1458 
1459 //-----------------------------------------------------------------------
1460 int GshhsReader::selectBestQuality(ViewPort &vp) {
1461  int bestQuality = 0;
1462 
1463  if (vp.chart_scale < 500000 && qualityAvailable[4])
1464  bestQuality = 4;
1465  else if (vp.chart_scale < 2000000 && qualityAvailable[3])
1466  bestQuality = 3;
1467  else if (vp.chart_scale < 8000000 && qualityAvailable[2])
1468  bestQuality = 2;
1469  else if (vp.chart_scale < 20000000 && qualityAvailable[1])
1470  bestQuality = 1;
1471  else if (qualityAvailable[0])
1472  bestQuality = 0;
1473  else
1474  while (!qualityAvailable[bestQuality] &&
1475  bestQuality <=
1476  4) // Find the worst quality actually available and use that
1477  // (normally we would use crude, but it is missing)
1478  bestQuality++;
1479 
1480  while (!qualityAvailable[bestQuality]) {
1481  bestQuality--;
1482  if (bestQuality < 0) break;
1483  }
1484 
1485  // if( bestQuality < 0 )
1486  // for( int i=0; i<5; i++ )
1487  // if( qualityAvailable[i] ) bestQuality = i;
1488 
1489  return bestQuality;
1490 }
1491 
1492 /* so plugins can determine if a line segment crosses land, must call from main
1493  thread once at startup to initialize array */
1494 static GshhsReader *reader = NULL;
1495 void gshhsCrossesLandInit() {
1496  if (!reader) {
1497  reader = new GshhsReader();
1498  }
1499  /* load best possible quality for crossing tests */
1500  int bestQuality = 4;
1501  while (!reader->qualityAvailable[bestQuality] && bestQuality > 0)
1502  bestQuality--;
1503  reader->LoadQuality(bestQuality);
1504  wxLogMessage("GSHHG: Loaded quality %d for land crossing detection.",
1505  bestQuality);
1506 }
1507 
1508 void gshhsCrossesLandReset() {
1509  if (reader) delete reader;
1510  reader = NULL;
1511 }
1512 
1513 bool gshhsCrossesLand(double lat1, double lon1, double lat2, double lon2) {
1514  if (!reader) {
1515  gshhsCrossesLandInit();
1516  }
1517  if (lon1 < 0) lon1 += 360;
1518  if (lon2 < 0) lon2 += 360;
1519 
1520  wxLineF trajectWorld(lon1, lat1, lon2, lat2);
1521  return reader->crossing1(trajectWorld);
1522 }
Definition: ocpndc.h:58
Definition: gshhs.h:67