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