OpenCPN Partial API docs
Loading...
Searching...
No Matches
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
73extern wxString gWorldMapLocation;
74
75#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
76static const GLchar *vertex_shader_source =
77 "attribute vec2 position;\n"
78 "uniform mat4 MVMatrix;\n"
79 "uniform mat4 TransformMatrix;\n"
80 "uniform vec4 color;\n"
81 "varying vec4 fragColor;\n"
82 "void main() {\n"
83 " fragColor = color;\n"
84 " gl_Position = MVMatrix * TransformMatrix * vec4(position, 0.0, 1.0);\n"
85 "}\n";
86
87static 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
94static const GLfloat vertices2[] = {
95 0.0f, 0.5f, 0.5f, -0.5f, -0.5f, -0.5f,
96};
97
98static const GLfloat vertices3[] = {
99 0.0f, 0.5f, 0.0f, 0.5f, -0.5f, 0.0f, -0.5f, -0.5f, 0.0f,
100};
101
102enum Consts { INFOLOG_LEN = 512 };
103GLchar infoLog[INFOLOG_LEN];
104GLint fragment_shader;
105// GLint shader_program;
106GLint success;
107GLint vertex_shader;
108
109extern GLint color_tri_shader_program;
110#endif
111
112//-------------------------------------------------------------------------
113
114GSHHSChart::GSHHSChart() {
115 reader = NULL;
116 land = wxColor(250, 250, 250);
117 water = wxColor(0, 0, 0);
118}
119
120GSHHSChart::~GSHHSChart() {
121 if (reader) delete reader;
122}
123
124void 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
145void GSHHSChart::SetColorsDirect(wxColour newLand, wxColour newWater) {
146 land = newLand;
147 water = newWater;
148}
149
150void GSHHSChart::Reset() {
151 if (reader) delete reader;
152 reader = NULL;
153 gshhsCrossesLandReset();
154}
155
156int GSHHSChart::GetMinAvailableQuality() {
157 if (!reader) reader = new GshhsReader();
158 return reader->GetMinAvailableQuality();
159}
160
161int GSHHSChart::GetMaxAvailableQuality() {
162 if (!reader) reader = new GshhsReader();
163 return reader->GetMaxAvailableQuality();
164}
165
166void 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
187GshhsPolyCell::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
201GshhsPolyCell::~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
208void GshhsPolyCell::ClearPolyV() {
209 for (int i = 0; i < 6; i++) {
210 delete[] polyv[i];
211 polyv[i] = NULL;
212 }
213}
214
215void 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
243fail:
244 wxLogMessage(_T("gshhs ReadPoly failed"));
245}
246
247void 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
267fail:
268 wxLogMessage(_T("gshhs ReadPolygon failed"));
269}
270
271wxPoint2DDouble 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
277void 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
327typedef 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
341static std::list<float_2Dpt> g_pv;
342static std::list<GLvertex *> g_vertexes;
343static int g_type, g_pos;
344static float_2Dpt g_p1, g_p2;
345
346void __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
361void __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
386void __CALL_CONVENTION gshhserrorCallback(GLenum errorCode) {
387 const GLubyte *estring;
388 estring = gluErrorString(errorCode);
389 // wxLogMessage( _T("OpenGL Tessellation Error: %s"), estring );
390}
391
392void __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
406void __CALL_CONVENTION gshhsendCallback() {}
407
408void GshhsPolyCell::DrawPolygonFilledGL(ocpnDC &pnt, contour_list *p,
409 float_2Dpt **pv, 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#else
583#endif
584}
585#endif // #ifdef ocpnUSE_GL
586
587#define DRAW_POLY_FILLED(POLY, COL) \
588 if (POLY) DrawPolygonFilled(pnt, POLY, dx, vp, COL);
589#define DRAW_POLY_FILLED_GL(NUM, COL) \
590 DrawPolygonFilledGL(pnt, &poly##NUM, &polyv[NUM], &polyc[NUM], vp, COL, idl);
591
592void GshhsPolyCell::drawMapPlain(ocpnDC &pnt, double dx, ViewPort &vp,
593 wxColor seaColor, wxColor landColor,
594 bool idl) {
595#ifdef ocpnUSE_GL
596 if (!pnt.GetDC()) { // opengl
597#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
598#define NORM_FACTOR 4096.0
599 if (dx && (vp.m_projection_type == PROJECTION_MERCATOR ||
600 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR)) {
601 double ts =
602 40058986 * NORM_FACTOR; /* 360 degrees in normalized viewport */
603 glPushMatrix();
604 glTranslated(dx > 0 ? ts : -ts, 0, 0);
605 }
606#endif
607 DRAW_POLY_FILLED_GL(1, landColor);
608 DRAW_POLY_FILLED_GL(2, seaColor);
609 DRAW_POLY_FILLED_GL(3, landColor);
610 DRAW_POLY_FILLED_GL(4, seaColor);
611 DRAW_POLY_FILLED_GL(5, landColor);
612
613#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
614 if (dx) glPopMatrix();
615#endif
616
617 } else
618#endif
619 {
620 DRAW_POLY_FILLED(&poly1, landColor);
621 DRAW_POLY_FILLED(&poly2, seaColor);
622 DRAW_POLY_FILLED(&poly3, landColor);
623 DRAW_POLY_FILLED(&poly4, seaColor);
624 DRAW_POLY_FILLED(&poly5, landColor);
625 }
626}
627
628void GshhsPolyCell::DrawPolygonContour(ocpnDC &pnt, contour_list *p, double dx,
629 ViewPort &vp) {
630 double x1, y1, x2, y2;
631 double long_max, lat_max, long_min, lat_min;
632
633 long_min = (double)x0cell;
634 lat_min = (double)y0cell;
635 long_max = ((double)x0cell + (double)header->pasx);
636 lat_max = ((double)y0cell + (double)header->pasy);
637
638 // qWarning() << long_min << "," << lat_min << long_max << "," << lat_max;
639
640 for (unsigned int i = 0; i < p->size(); i++) {
641 if (!p->at(i).size()) continue;
642
643 unsigned int v;
644 for (v = 0; v < (p->at(i).size() - 1); v++) {
645 x1 = p->at(i).at(v).x;
646 y1 = p->at(i).at(v).y;
647 x2 = p->at(i).at(v + 1).x;
648 y2 = p->at(i).at(v + 1).y;
649
650 // Elimination des traits verticaux et horizontaux
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 x1 = p->at(i).at(v).x;
660 y1 = p->at(i).at(v).y;
661 x2 = p->at(i).at(0).x;
662 y2 = p->at(i).at(0).y;
663
664 if ((((x1 == x2) && ((x1 == long_min) || (x1 == long_max))) ||
665 ((y1 == y2) && ((y1 == lat_min) || (y1 == lat_max)))) == 0) {
666 wxPoint2DDouble AB = GetDoublePixFromLL(vp, x1 + dx, y1);
667 wxPoint2DDouble CD = GetDoublePixFromLL(vp, x2 + dx, y1);
668 pnt.DrawLine(AB.m_x, AB.m_y, CD.m_x, CD.m_y);
669 }
670 }
671}
672
673#define DRAW_POLY_CONTOUR(POLY) \
674 if (POLY) DrawPolygonContour(pnt, POLY, dx, vp);
675
676void GshhsPolyCell::drawSeaBorderLines(ocpnDC &pnt, double dx, ViewPort &vp) {
677 coasts.clear();
678 DRAW_POLY_CONTOUR(&poly1)
679 DRAW_POLY_CONTOUR(&poly2)
680 DRAW_POLY_CONTOUR(&poly3)
681 DRAW_POLY_CONTOUR(&poly4)
682 DRAW_POLY_CONTOUR(&poly5)
683}
684
685//========================================================================
686
687GshhsPolyReader::GshhsPolyReader(int quality) {
688 fpoly = NULL;
689
690 for (int i = 0; i < 360; i++) {
691 for (int j = 0; j < 180; j++) {
692 allCells[i][j] = NULL;
693 }
694 }
695 currentQuality = -1;
696 polyHeader.version = -1;
697 InitializeLoadQuality(quality);
698}
699
700//-------------------------------------------------------------------------
701GshhsPolyReader::~GshhsPolyReader() {
702 for (int i = 0; i < 360; i++) {
703 for (int j = 0; j < 180; j++) {
704 if (allCells[i][j] != NULL) {
705 delete allCells[i][j];
706 allCells[i][j] = NULL;
707 }
708 }
709 }
710
711 if (fpoly) {
712 fclose(fpoly);
713 }
714}
715
716//-------------------------------------------------------------------------
717int GshhsPolyReader::ReadPolyVersion() {
718 wxString fname = GshhsReader::getFileName_Land(0);
719 if (fpoly) fclose(fpoly);
720 fpoly = fopen(fname.mb_str(), "rb");
721
722 /* init header */
723 if (!fpoly) return 0;
724
725 readPolygonFileHeader(fpoly, &polyHeader);
726
727 return polyHeader.version;
728}
729
730void GshhsPolyReader::InitializeLoadQuality(
731 int quality) // 5 levels: 0=low ... 4=full
732{
733 if (currentQuality != quality) {
734 currentQuality = quality;
735
736 wxString fname = GshhsReader::getFileName_Land(quality);
737
738 if (fpoly) fclose(fpoly);
739
740 fpoly = fopen(fname.mb_str(), "rb");
741 if (fpoly) readPolygonFileHeader(fpoly, &polyHeader);
742
743 for (int i = 0; i < 360; i++) {
744 for (int j = 0; j < 180; j++) {
745 if (allCells[i][j] != NULL) {
746 delete allCells[i][j];
747 allCells[i][j] = NULL;
748 }
749 }
750 }
751 }
752}
753
754static inline bool my_intersects(const wxLineF &line1, const wxLineF &line2) {
755 double x1 = line1.m_p1.x, y1 = line1.m_p1.y, x2 = line1.m_p2.x,
756 y2 = line1.m_p2.y;
757 double x3 = line2.m_p1.x, y3 = line2.m_p1.y, x4 = line2.m_p2.x,
758 y4 = line2.m_p2.y;
759
760 // implementation is based on Graphics Gems III's "Faster Line Segment
761 // Intersection"
762 double ax = x2 - x1, ay = y2 - y1;
763 double bx = x3 - x4, by = y3 - y4;
764 double cx = x1 - x3, cy = y1 - y3;
765
766#define INTER_LIMIT 1e-7
767 double denominator = ay * bx - ax * by;
768 if (denominator < 1e-10) {
769 if (fabs((y1 * ax - ay * x1) * bx - (y3 * bx - by * x3) * ax) > INTER_LIMIT)
770 return false; /* different intercepts, no intersection */
771 if (fabs((x1 * ay - ax * y1) * by - (x3 * by - bx * y3) * ay) > INTER_LIMIT)
772 return false; /* different intercepts, no intersection */
773
774 return true;
775 }
776
777 const double reciprocal = 1 / denominator;
778 const double na = (by * cx - bx * cy) * reciprocal;
779
780 if (na < -INTER_LIMIT || na > 1 + INTER_LIMIT) return false;
781
782 const double nb = (ax * cy - ay * cx) * reciprocal;
783 if (nb < -INTER_LIMIT || nb > 1 + INTER_LIMIT) return false;
784
785 return true;
786}
787
788bool GshhsPolyReader::crossing1(wxLineF trajectWorld) {
789 double x1 = trajectWorld.p1().x, y1 = trajectWorld.p1().y;
790 double x2 = trajectWorld.p2().x, y2 = trajectWorld.p2().y;
791
792 int clonmin, clonmax, clatmax, clatmin;
793 clonmin = (int)floor(GSSH_SUBM * wxMin(x1, x2));
794 clonmax = (int)ceil(GSSH_SUBM * wxMax(x1, x2));
795
796 if (clonmin < 0) {
797 clonmin += GSSH_SUBM * 360;
798 clonmax += GSSH_SUBM * 360;
799 }
800
801 if (clonmax - clonmin > GSSH_SUBM * 180) { /* dont go long way around world */
802 clonmin = (int)floor(GSSH_SUBM * wxMax(x1, x2)) - GSSH_SUBM * 360;
803 clonmax = (int)ceil(GSSH_SUBM * wxMin(x1, x2));
804 }
805
806 clatmin = (int)floor(GSSH_SUBM * wxMin(y1, y2));
807 clatmax = (int)ceil(GSSH_SUBM * wxMax(y1, y2));
808 wxASSERT(clatmin >= -GSSH_SUBM * 90 && clatmax <= GSSH_SUBM * 89);
809
810 // TODO: optimize by traversing only the cells the segment passes through,
811 // rather than all of the cells which fit in the bounding box,
812 // this may make a worthwhile difference for longer segments in some
813 // cases.
814 int clon, clonx, clat;
815 for (clon = clonmin; clon < clonmax; clon++) {
816 clonx = clon;
817 while (clonx < 0) clonx += GSSH_SUBM * 360;
818 while (clonx >= GSSH_SUBM * 360) clonx -= GSSH_SUBM * 360;
819
820 wxASSERT(clonx >= 0 && clonx < GSSH_SUBM * 360);
821
822 if (clonx < GSSH_SUBM * 180) {
823 if (x1 > 180) x1 -= 360;
824 if (x2 > 180) x2 -= 360;
825 } else {
826 if (x1 < 180) x1 += 360;
827 if (x2 < 180) x2 += 360;
828 }
829
830 wxLineF rtrajectWorld(x1, y1, x2, y2);
831
832 for (clat = clatmin; clat < clatmax; clat++) {
833 int cloni = clonx / GSSH_SUBM,
834 clati = (GSSH_SUBM * 90 + clat) / GSSH_SUBM;
835 GshhsPolyCell *&cel = allCells[cloni][clati];
836 if (!cel) {
837 mutex1.Lock();
838 if (!cel) {
839 /* load the needed cell from disk */
840 cel = new GshhsPolyCell(fpoly, cloni, clati - 90, &polyHeader);
841 wxASSERT(cel);
842 }
843 mutex1.Unlock();
844 }
845
846 int hash = GSSH_SUBM * (GSSH_SUBM * (90 - clati) + clat - cloni) + clonx;
847 std::vector<wxLineF> *&high_res_map = cel->high_res_map[hash];
848 wxASSERT(hash >= 0 && hash < GSSH_SUBM * GSSH_SUBM);
849 if (!high_res_map) {
850 mutex2.Lock();
851 if (!high_res_map) {
852 /* Build the needed sub cell of line segments from the cell */
853 contour_list &poly1 = cel->getPoly1();
854
855 double minlat = (double)clat / GSSH_SUBM,
856 maxlat = (double)(clat + 1) / GSSH_SUBM;
857 double minlon = (double)clonx / GSSH_SUBM,
858 maxlon = (double)(clonx + 1) / GSSH_SUBM;
859 high_res_map = new std::vector<wxLineF>;
860 for (unsigned int pi = 0; pi < poly1.size(); pi++) {
861 contour &c = poly1[pi];
862 double lx = c[c.size() - 1].x, ly = c[c.size() - 1].y;
863 /* must compute states because sometimes a
864 segment starts and ends outside our cell, but passes
865 through it so must be included */
866 int lstatex = lx < minlon ? -1 : lx > maxlon ? 1 : 0;
867 int lstatey = ly < minlat ? -1 : ly > maxlat ? 1 : 0;
868
869 for (unsigned int pj = 0; pj < c.size(); pj++) {
870 double clon = c[pj].x, clat = c[pj].y;
871 // gshhs data shouldn't, but sometimes contains zero segments
872 // which enlarges our table, but
873 // more importantly, the fast segment intersection test
874 // and doesn't correctly account for it
875 if (lx == clon && ly == clat) continue;
876
877 int statex = clon < minlon ? -1 : clon > maxlon ? 1 : 0;
878 int statey = clat < minlat ? -1 : clat > maxlat ? 1 : 0;
879
880 if ((!statex || lstatex != statex) &&
881 (!statey || lstatey != statey))
882 high_res_map->push_back(wxLineF(lx, ly, clon, clat));
883
884 lx = clon, ly = clat;
885 lstatex = statex, lstatey = statey;
886 }
887 }
888 }
889 mutex2.Unlock();
890 }
891
892 for (std::vector<wxLineF>::iterator it2 = high_res_map->begin();
893 it2 != high_res_map->end(); it2++)
894 if (my_intersects(rtrajectWorld, *it2)) return true;
895 }
896 }
897
898 return false;
899}
900
901void GshhsPolyReader::readPolygonFileHeader(FILE *polyfile,
902 PolygonFileHeader *header) {
903 fseek(polyfile, 0, SEEK_SET);
904 if (fread(header, sizeof(PolygonFileHeader), 1, polyfile) != 1)
905 wxLogMessage(_T("gshhs ReadPolygonFileHeader failed"));
906}
907
908//-------------------------------------------------------------------------
909void GshhsPolyReader::drawGshhsPolyMapPlain(ocpnDC &pnt, ViewPort &vp,
910 wxColor const &seaColor,
911 wxColor const &landColor) {
912 if (!fpoly) return;
913
914 pnt.SetPen(wxNullPen);
915
916 int clonmin, clonmax, clatmax, clatmin; // cellules visibles
917 LLBBox bbox = vp.GetBBox();
918 clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
919 clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
920 if (clatmin <= 0) clatmin--;
921 if (clatmax >= 0) clatmax++;
922 if (clonmin <= 0) clonmin--;
923 if (clonmax >= 0) clonmax++;
924 int dx, clon, clonx, clat;
925 GshhsPolyCell *cel;
926
927 ViewPort nvp = vp;
928#ifdef ocpnUSE_GL
929 if (!pnt.GetDC()) { // opengl
930 // clear cached data when the projection changes
931 if (vp.m_projection_type != last_rendered_vp.m_projection_type ||
932 (last_rendered_vp.m_projection_type == PROJECTION_POLAR &&
933 last_rendered_vp.clat * vp.clat <= 0)) {
934 last_rendered_vp = vp;
935 for (int clon = 0; clon < 360; clon++)
936 for (int clat = 0; clat < 180; clat++)
937 if (allCells[clon][clat]) allCells[clon][clat]->ClearPolyV();
938 }
939#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
940 glEnableClientState(GL_VERTEX_ARRAY);
941
942 // use a viewport that allows the vertexes to be reused over many frames
943 // TODO fix for multicanvas
944 if (glChartCanvas::HasNormalizedViewPort(vp)) {
945 glPushMatrix();
946 glChartCanvas::MultMatrixViewPort(vp);
947 nvp = glChartCanvas::NormalizedViewPort(vp);
948 }
949#endif
950 }
951#endif
952 for (clon = clonmin; clon < clonmax; clon++) {
953 clonx = clon;
954 while (clonx < 0) clonx += 360;
955 while (clonx >= 360) clonx -= 360;
956
957 for (clat = clatmin; clat < clatmax; clat++) {
958 if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
959 if (allCells[clonx][clat + 90] == NULL) {
960 cel = new GshhsPolyCell(fpoly, clonx, clat, &polyHeader);
961 wxASSERT(cel);
962 allCells[clonx][clat + 90] = cel;
963 } else {
964 cel = allCells[clonx][clat + 90];
965 }
966 bool idl = false;
967
968 // only mercator needs the special idl fixes
969 if (vp.m_projection_type != PROJECTION_MERCATOR &&
970 vp.m_projection_type != PROJECTION_EQUIRECTANGULAR)
971 dx = 0;
972 else if (pnt.GetDC()) // dc
973 dx = clon - clonx;
974 else { // opengl
975 int clonn = clonx;
976 if (clonn >= 180) {
977 clonn -= 360;
978 idl = true;
979 }
980 if (vp.clon - clonn > 180)
981 dx = 1;
982 else if (vp.clon - clonn < -180)
983 dx = -1;
984 else
985 dx = 0;
986 }
987
988 cel->drawMapPlain(pnt, dx, nvp, seaColor, landColor, idl);
989 }
990 }
991 }
992
993#ifdef ocpnUSE_GL
994#if !defined(USE_ANDROID_GLES2) && !defined(ocpnUSE_GLSL)
995 if (!pnt.GetDC()) { // opengl
996 if (glChartCanvas::HasNormalizedViewPort(vp)) glPopMatrix();
997 glDisableClientState(GL_VERTEX_ARRAY);
998 }
999#endif
1000#endif
1001}
1002
1003//-------------------------------------------------------------------------
1004void GshhsPolyReader::drawGshhsPolyMapSeaBorders(ocpnDC &pnt, ViewPort &vp) {
1005 if (!fpoly) return;
1006 int clonmin, clonmax, clatmax, clatmin; // cellules visibles
1007 LLBBox bbox = vp.GetBBox();
1008 clonmin = bbox.GetMinLon(), clonmax = bbox.GetMaxLon(),
1009 clatmin = bbox.GetMinLat(), clatmax = bbox.GetMaxLat();
1010
1011 int dx, clon, clonx, clat;
1012 GshhsPolyCell *cel;
1013
1014 for (clon = clonmin; clon < clonmax; clon++) {
1015 clonx = clon;
1016 while (clonx < 0) clonx += 360;
1017 while (clonx >= 360) clonx -= 360;
1018
1019 for (clat = clatmin; clat < clatmax; clat++) {
1020 if (clonx >= 0 && clonx <= 359 && clat >= -90 && clat <= 89) {
1021 if (allCells[clonx][clat + 90] == NULL) {
1022 cel = new GshhsPolyCell(fpoly, clonx, clat, &polyHeader);
1023 wxASSERT(cel);
1024 allCells[clonx][clat + 90] = cel;
1025 } else {
1026 cel = allCells[clonx][clat + 90];
1027 }
1028 dx = clon - clonx;
1029 cel->drawSeaBorderLines(pnt, dx, vp);
1030 }
1031 }
1032 }
1033}
1034
1035int GshhsPolygon::readInt4() {
1036 union {
1037 unsigned int n;
1038 unsigned char buf[4];
1039 } res;
1040
1041 unsigned char in[4];
1042
1043 int nb = 0;
1044 nb += fread(&in, 1, 4, file);
1045 res.buf[3] = in[0];
1046 res.buf[2] = in[1];
1047 res.buf[1] = in[2];
1048 res.buf[0] = in[3];
1049
1050 if (nb != 4) {
1051 ok = false;
1052 res.n = 0;
1053 }
1054
1055 return res.n;
1056}
1057
1058int GshhsPolygon::readInt2() {
1059 union {
1060 unsigned int n;
1061 unsigned char buf[4];
1062 } v;
1063
1064 int nb = 0;
1065 nb += fread(&v.buf[0], 2, 1, file);
1066 if (nb != 2) {
1067 ok = false;
1068 v.n = 0;
1069 }
1070 return v.buf[1] << 8 | v.buf[0];
1071}
1072
1073GshhsPolygon::GshhsPolygon(FILE *file_) {
1074 file = file_;
1075 ok = true;
1076 id = readInt4();
1077 n = readInt4();
1078 flag = readInt4();
1079 west = readInt4() * 1e-6;
1080 east = readInt4() * 1e-6;
1081 south = readInt4() * 1e-6;
1082 north = readInt4() * 1e-6;
1083 area = readInt4();
1084
1085 if (((flag >> 8) & 255) >= 7) { // GSHHS Release 2.0
1086 areaFull = readInt4();
1087 container = readInt4();
1088 ancestor = readInt4();
1089
1090 greenwich = (flag >> 16) & 1;
1091 antarctic = (west == 0 && east == 360);
1092 if (ok) {
1093 double x = 0, y = 0;
1094 for (int i = 0; i < n; i++) {
1095 x = readInt4() * 1e-6;
1096 if (greenwich && x > 270) x -= 360;
1097 y = readInt4() * 1e-6;
1098 lsPoints.push_back(new GshhsPoint(x, y));
1099 }
1100 if (antarctic) {
1101 lsPoints.insert(lsPoints.begin(), new GshhsPoint(360, y));
1102 lsPoints.insert(lsPoints.begin(), new GshhsPoint(360, -90));
1103 lsPoints.push_back(new GshhsPoint(0, -90));
1104 }
1105 }
1106 } else {
1107 greenwich = (flag >> 16) & 1;
1108 antarctic = (west == 0 && east == 360);
1109 if (ok) {
1110 for (int i = 0; i < n; i++) {
1111 double x = 0, y = 0;
1112 x = readInt4() * 1e-6;
1113 if (greenwich && x > 270) x -= 360;
1114 y = readInt4() * 1e-6;
1115 lsPoints.push_back(new GshhsPoint(x, y));
1116 }
1117 }
1118 }
1119}
1120
1121//--------------------------------------------------------
1122
1123GshhsPolygon::~GshhsPolygon() {
1124 std::vector<GshhsPoint *>::iterator itp;
1125 for (itp = lsPoints.begin(); itp != lsPoints.end(); itp++) {
1126 delete *itp;
1127 *itp = NULL;
1128 }
1129 lsPoints.clear();
1130}
1131
1132//==========================================================
1133
1134GshhsReader::GshhsReader() {
1135 maxQualityAvailable = -1;
1136 minQualityAvailable = -1;
1137
1138 for (int i = 0; i < 5; i++) {
1139 qualityAvailable[i] = false;
1140 if (GshhsReader::gshhsFilesExists(i)) {
1141 qualityAvailable[i] = true;
1142 if (minQualityAvailable < 0) minQualityAvailable = i;
1143 maxQualityAvailable = i;
1144 }
1145 }
1146
1147 if (maxQualityAvailable < 0) {
1148 wxString msg(
1149 _T("Unable to initialize background world map. No GSHHS datafiles ")
1150 _T("found in "));
1151 msg += gWorldMapLocation;
1152 wxLogMessage(msg);
1153 }
1154
1155 // int q = selectBestQuality( vp );
1156 // if( ! qualityAvailable[q] ) {
1157 // int q = maxQualityAvailable;
1158 // }
1159
1160 int q = 0;
1161
1162 gshhsPoly_reader = new GshhsPolyReader(q);
1163
1164 for (int qual = 0; qual < 5; qual++) {
1165 lsPoly_boundaries[qual] = new std::vector<GshhsPolygon *>;
1166 lsPoly_rivers[qual] = new std::vector<GshhsPolygon *>;
1167 }
1168
1169 quality = -1;
1170 LoadQuality(q);
1171}
1172
1173int GshhsReader::ReadPolyVersion() {
1174 return gshhsPoly_reader->ReadPolyVersion();
1175}
1176
1177//-------------------------------------------------------
1178
1179GshhsReader::~GshhsReader() {
1180 clearLists();
1181 delete gshhsPoly_reader;
1182}
1183
1184//-----------------------------------------------------------------------
1185void GshhsReader::clearLists() {
1186 std::vector<GshhsPolygon *>::iterator itp;
1187 for (int qual = 0; qual < 5; qual++) {
1188 for (itp = lsPoly_boundaries[qual]->begin();
1189 itp != lsPoly_boundaries[qual]->end(); itp++) {
1190 delete *itp;
1191 *itp = NULL;
1192 }
1193 for (itp = lsPoly_rivers[qual]->begin(); itp != lsPoly_rivers[qual]->end();
1194 itp++) {
1195 delete *itp;
1196 *itp = NULL;
1197 }
1198
1199 lsPoly_boundaries[qual]->clear();
1200 lsPoly_rivers[qual]->clear();
1201 delete lsPoly_boundaries[qual];
1202 delete lsPoly_rivers[qual];
1203 }
1204}
1205//-----------------------------------------------------------------------
1206
1207wxString GshhsReader::getNameExtension(int quality) {
1208 wxString ext;
1209 switch (quality) {
1210 case 0:
1211 ext = _T("c");
1212 break;
1213 case 1:
1214 ext = _T("l");
1215 break;
1216 case 2:
1217 ext = _T("i");
1218 break;
1219 case 3:
1220 ext = _T("h");
1221 break;
1222 case 4:
1223 ext = _T("f");
1224 break;
1225 default:
1226 ext = _T("l");
1227 break;
1228 }
1229 return ext;
1230}
1231
1232wxString GshhsReader::getFileName_Land(int quality) {
1233 wxString ext = GshhsReader::getNameExtension(quality);
1234 wxString fname =
1235 gWorldMapLocation + wxString::Format(_T("poly-%c-1.dat"), ext.GetChar(0));
1236 return fname;
1237}
1238
1239wxString GshhsReader::getFileName_boundaries(int quality) {
1240 wxString ext = GshhsReader::getNameExtension(quality);
1241 wxString fname = gWorldMapLocation +
1242 wxString::Format(_T("wdb_borders_%c.b"), ext.GetChar(0));
1243 return fname;
1244}
1245
1246wxString GshhsReader::getFileName_rivers(int quality) {
1247 wxString ext = GshhsReader::getNameExtension(quality);
1248 wxString fname = gWorldMapLocation +
1249 wxString::Format(_T("wdb_rivers_%c.b"), ext.GetChar(0));
1250 return fname;
1251}
1252
1253//-----------------------------------------------------------------------
1254bool GshhsReader::gshhsFilesExists(int quality) {
1255 if (!wxFile::Access(GshhsReader::getFileName_Land(quality), wxFile::read))
1256 return false;
1257 // Borders disabled anyway since the perf optimizations if( ! wxFile::Access(
1258 // GshhsReader::getFileName_boundaries( quality ), wxFile::read ) ) return
1259 // false; Rivers disabled anyway since the perf optimizations if( !
1260 // wxFile::Access( GshhsReader::getFileName_rivers( quality ), wxFile::read )
1261 // ) return false;
1262
1263 return true;
1264}
1265
1266//-----------------------------------------------------------------------
1267void GshhsReader::LoadQuality(int newQuality) // 5 levels: 0=low ... 4=full
1268{
1269 if (quality == newQuality) return;
1270
1271 wxStopWatch perftimer;
1272
1273 wxString fname;
1274
1275 quality = newQuality;
1276 if (quality < 0)
1277 quality = 0;
1278 else if (quality > 4)
1279 quality = 4;
1280
1281 gshhsPoly_reader->InitializeLoadQuality(quality);
1282#if 0 /* too slow to load the whole world at once */
1283 if( lsPoly_boundaries[quality]->size() == 0 ) {
1284 fname = getFileName_boundaries( quality );
1285 file = fopen( fname.mb_str(), "rb" );
1286
1287 if( file != NULL ) {
1288 ok = true;
1289 while( ok ) {
1290 GshhsPolygon *poly = new GshhsPolygon( file );
1291
1292 ok = poly->isOk();
1293 if( ok )
1294 if( poly->getLevel() < 2 )
1295 lsPoly_boundaries[quality]->push_back( poly );
1296 else delete poly;
1297 else delete poly;
1298 }
1299 fclose( file );
1300 }
1301 }
1302
1303 if( lsPoly_rivers[quality]->size() == 0 ) {
1304 fname = getFileName_rivers( quality );
1305 file = fopen( fname.mb_str(), "rb" );
1306 if( file != NULL ) {
1307 ok = true;
1308 while( ok ) {
1309 GshhsPolygon *poly = new GshhsPolygon( file );
1310 ok = poly->isOk();
1311 if( ok ) {
1312 lsPoly_rivers[quality]->push_back( poly );
1313 }
1314 else delete poly;
1315 }
1316 fclose( file );
1317 }
1318 }
1319#endif
1320 wxLogMessage(_T("Loading World Chart Q=%d in %ld ms."), quality,
1321 perftimer.Time());
1322}
1323
1324//-----------------------------------------------------------------------
1325std::vector<GshhsPolygon *> &GshhsReader::getList_boundaries() {
1326 return *lsPoly_boundaries[quality];
1327}
1328//-----------------------------------------------------------------------
1329std::vector<GshhsPolygon *> &GshhsReader::getList_rivers() {
1330 return *lsPoly_rivers[quality];
1331}
1332
1333//=====================================================================
1334
1335int GshhsReader::GSHHS_scaledPoints(GshhsPolygon *pol, wxPoint *pts,
1336 double declon, ViewPort &vp) {
1337 LLBBox box;
1338 box.Set(pol->south, pol->west + declon, pol->north, pol->east + declon);
1339 if (vp.GetBBox().IntersectOut(box)) return 0;
1340
1341 // Remove small polygons.
1342
1343 wxPoint2DDouble p1 = GetDoublePixFromLL(vp, pol->west + declon, pol->north);
1344 wxPoint2DDouble p2 = GetDoublePixFromLL(vp, pol->east + declon, pol->south);
1345
1346 if (p1.m_x == p2.m_x && p1.m_y == p2.m_y) return 0;
1347
1348 double x, y;
1349 std::vector<GshhsPoint *>::iterator itp;
1350 int xx, yy, oxx = 0, oyy = 0;
1351 int j = 0;
1352
1353 for (itp = (pol->lsPoints).begin(); itp != (pol->lsPoints).end(); itp++) {
1354 x = (*itp)->lon + declon;
1355 y = (*itp)->lat;
1356 wxPoint2DDouble p = GetDoublePixFromLL(vp, y, x);
1357 xx = p.m_x, yy = p.m_y;
1358 if (j == 0 || (oxx != xx || oyy != yy)) { // Remove close points
1359 oxx = xx;
1360 oyy = yy;
1361 pts[j].x = xx;
1362 pts[j].y = yy;
1363 j++;
1364 }
1365 }
1366
1367 return j;
1368}
1369
1370//-----------------------------------------------------------------------
1371void GshhsReader::GsshDrawLines(ocpnDC &pnt, std::vector<GshhsPolygon *> &lst,
1372 ViewPort &vp, bool isClosed) {
1373 std::vector<GshhsPolygon *>::iterator iter;
1374 GshhsPolygon *pol;
1375 wxPoint *pts = NULL;
1376 int i;
1377 int nbp;
1378
1379 int nbmax = 10000;
1380 pts = new wxPoint[nbmax];
1381 wxASSERT(pts);
1382
1383 for (i = 0, iter = lst.begin(); iter != lst.end(); iter++, i++) {
1384 pol = *iter;
1385
1386 if (nbmax < pol->n + 2) {
1387 nbmax = pol->n + 2;
1388 delete[] pts;
1389 pts = new wxPoint[nbmax];
1390 wxASSERT(pts);
1391 }
1392
1393 nbp = GSHHS_scaledPoints(pol, pts, 0, 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 nbp = GSHHS_scaledPoints(pol, pts, -360, vp);
1408 if (nbp > 1) {
1409 if (pol->isAntarctic()) {
1410 pts++;
1411 nbp -= 2;
1412 pnt.DrawLines(nbp, pts);
1413 pts--;
1414 } else {
1415 pnt.DrawLines(nbp, pts);
1416 if (isClosed)
1417 pnt.DrawLine(pts[0].x, pts[0].y, pts[nbp - 1].x, pts[nbp - 1].y);
1418 }
1419 }
1420 }
1421 delete[] pts;
1422}
1423
1424//-----------------------------------------------------------------------
1425void GshhsReader::drawContinents(ocpnDC &pnt, ViewPort &vp,
1426 wxColor const &seaColor,
1427 wxColor const &landColor) {
1428 LoadQuality(selectBestQuality(vp));
1429 gshhsPoly_reader->drawGshhsPolyMapPlain(pnt, vp, seaColor, landColor);
1430}
1431
1432//-----------------------------------------------------------------------
1433void GshhsReader::drawSeaBorders(ocpnDC &pnt, ViewPort &vp) {
1434 pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1435 gshhsPoly_reader->drawGshhsPolyMapSeaBorders(pnt, vp);
1436}
1437
1438//-----------------------------------------------------------------------
1439void GshhsReader::drawBoundaries(ocpnDC &pnt, ViewPort &vp) {
1440 pnt.SetBrush(*wxTRANSPARENT_BRUSH);
1441
1442 if (pnt.GetDC()) {
1443 wxPen *pen = wxThePenList->FindOrCreatePen(*wxBLACK, 1, wxPENSTYLE_DOT);
1444 pnt.SetPen(*pen);
1445 } else {
1446 wxPen *pen = wxThePenList->FindOrCreatePen(wxColor(0, 0, 0, 80), 2,
1447 wxPENSTYLE_LONG_DASH);
1448 pnt.SetPen(*pen);
1449 }
1450 GsshDrawLines(pnt, getList_boundaries(), vp, false);
1451}
1452
1453//-----------------------------------------------------------------------
1454void GshhsReader::drawRivers(ocpnDC &pnt, ViewPort &vp) {
1455 GsshDrawLines(pnt, getList_rivers(), vp, false);
1456}
1457
1458//-----------------------------------------------------------------------
1459int GshhsReader::selectBestQuality(ViewPort &vp) {
1460 int bestQuality = 0;
1461
1462 if (vp.chart_scale < 500000 && qualityAvailable[4])
1463 bestQuality = 4;
1464 else if (vp.chart_scale < 2000000 && qualityAvailable[3])
1465 bestQuality = 3;
1466 else if (vp.chart_scale < 8000000 && qualityAvailable[2])
1467 bestQuality = 2;
1468 else if (vp.chart_scale < 20000000 && qualityAvailable[1])
1469 bestQuality = 1;
1470 else if (qualityAvailable[0])
1471 bestQuality = 0;
1472 else
1473 while (!qualityAvailable[bestQuality] &&
1474 bestQuality <=
1475 4) // Find the worst quality actually available and use that
1476 // (normally we would use crude, but it is missing)
1477 bestQuality++;
1478
1479 while (!qualityAvailable[bestQuality]) {
1480 bestQuality--;
1481 if (bestQuality < 0) break;
1482 }
1483
1484 // if( bestQuality < 0 )
1485 // for( int i=0; i<5; i++ )
1486 // if( qualityAvailable[i] ) bestQuality = i;
1487
1488 return bestQuality;
1489}
1490
1491/* so plugins can determine if a line segment crosses land, must call from main
1492 thread once at startup to initialize array */
1493static GshhsReader *reader = NULL;
1494void gshhsCrossesLandInit() {
1495 if (!reader) {
1496 reader = new GshhsReader();
1497 }
1498 /* load best possible quality for crossing tests */
1499 int bestQuality = 4;
1500 while (!reader->qualityAvailable[bestQuality] && bestQuality > 0)
1501 bestQuality--;
1502 reader->LoadQuality(bestQuality);
1503 wxLogMessage("GSHHG: Loaded quality %d for land crossing detection.",
1504 bestQuality);
1505}
1506
1507void gshhsCrossesLandReset() {
1508 if (reader) delete reader;
1509 reader = NULL;
1510}
1511
1512bool gshhsCrossesLand(double lat1, double lon1, double lat2, double lon2) {
1513 if (!reader) {
1514 gshhsCrossesLandInit();
1515 }
1516 if (lon1 < 0) lon1 += 360;
1517 if (lon2 < 0) lon2 += 360;
1518
1519 wxLineF trajectWorld(lon1, lat1, lon2, lat2);
1520 return reader->crossing1(trajectWorld);
1521}
Wrapper class for OpenGL shader programs.
Definition shaders.h:57
Represents the view port for chart display in OpenCPN.
Definition viewport.h:84
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:221
int pix_width
Width of the viewport in physical pixels.
Definition viewport.h:219
wxPoint2DDouble GetDoublePixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates with double precision.
Definition viewport.cpp:145
double clon
Center longitude of the viewport in degrees.
Definition viewport.h:194
double clat
Center latitude of the viewport in degrees.
Definition viewport.h:192
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:214
Device context class that can use either wxDC or OpenGL for drawing.
Definition ocpndc.h:64
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:476