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