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