OpenCPN Partial API docs
Loading...
Searching...
No Matches
viewport.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2015 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24// Must come before any GL includes:
25#include "gl_headers.h"
26
27#include <vector>
28
29#ifndef __WXMSW__
30#include <signal.h>
31#include <setjmp.h>
32#endif
33
34#if defined(__ANDROID__)
35#include <GLES2/gl2.h>
36#elif defined(__WXQT__) || defined(__WXGTK__)
37#include <GL/glew.h>
38#endif
39
40// For compilers that support precompilation, includes "wx.h".
41#include <wx/wxprec.h>
42#ifndef WX_PRECOMP
43#include <wx/wx.h>
44#endif
45
46#include <wx/aui/aui.h>
47#include <wx/clipbrd.h>
48#include <wx/graphics.h>
49#include <wx/image.h>
50#include <wx/listbook.h>
51#include <wx/listimpl.cpp>
52
53#ifdef __VISUALC__
54#include <wx/msw/msvcrt.h>
55#endif
56
57#include "model/ais_decoder.h"
59#include "model/cutil.h"
60#include "model/geodesic.h"
61#include "model/multiplexer.h"
62#include "model/routeman.h"
63#include "model/select.h"
64#include "model/select_item.h"
65
66#include "ais.h"
68#include "chartdb.h"
69#include "chartimg.h"
70#include "chcanv.h"
71#include "ch_info_win.h"
72#include "cm93.h" // for chart outline draw
73#include "concanv.h"
74#include "config.h"
75#include "dychart.h"
76#include "font_mgr.h"
77#include "gl_texture_descr.h"
78#include "gshhs.h"
79#include "kml.h"
80#include "navutil.h"
81#include "ocpndc.h"
82#include "ocpn_pixel.h"
83#include "ocpn_platform.h"
84#include "ocpn_region.h"
85#include "pluginmanager.h"
86#include "quilt.h"
87#include "routemanagerdialog.h"
88#include "s52plib.h"
89#include "s57chart.h" // for ArrayOfS57Obj
90#include "send_to_gps_dlg.h"
91#include "styles.h"
92#include "tcmgr.h"
93#include "tc_win.h"
94#include "thumbwin.h"
95#include "tide_time.h"
96#include "timers.h"
97#include "track_prop_dlg.h"
98#include "undo.h"
99
100#ifdef ocpnUSE_GL
101#include "gl_chart_canvas.h"
102#endif
103
104// FIXME (leamas) find new home
105ColorScheme global_color_scheme = GLOBAL_COLOR_SCHEME_DAY;
106
107//------------------------------------------------------------------------------
108// ViewPort Implementation
109//------------------------------------------------------------------------------
110ViewPort::ViewPort() {
111 bValid = false;
112 skew = 0.;
113 view_scale_ppm = 1;
114 rotation = 0.;
115 tilt = 0.;
116 b_quilt = false;
117 pix_height = pix_width = 0;
118 b_MercatorProjectionOverride = false;
119 lat0_cache = NAN;
120 m_projection_type = PROJECTION_MERCATOR;
121 m_displayScale = 1.0;
122}
123
124void ViewPort::SetPixelScale(double scale) { m_displayScale = scale; }
125
126// TODO: eliminate the use of this function
127wxPoint ViewPort::GetPixFromLL(double lat, double lon) {
128 wxPoint2DDouble p = GetDoublePixFromLL(lat, lon);
129 if (wxFinite(p.m_x) && wxFinite(p.m_y)) {
130 if ((abs(p.m_x) < 1e6) && (abs(p.m_y) < 1e6))
131 return wxPoint(wxRound(p.m_x), wxRound(p.m_y));
132 }
133 return wxPoint(INVALID_COORD, INVALID_COORD);
134}
135
136wxPoint2DDouble ViewPort::GetDoublePixFromLL(double lat, double lon) {
137 double easting = 0;
138 double northing = 0;
139 double xlon = lon;
140
141 /* Make sure lon and lon0 are same phase */
142 if (xlon * clon < 0.) {
143 if (xlon < 0.)
144 xlon += 360.;
145 else
146 xlon -= 360.;
147 }
148
149 if (fabs(xlon - clon) > 180.) {
150 if (xlon > clon)
151 xlon -= 360.;
152 else
153 xlon += 360.;
154 }
155
156 // update cache of trig functions used for projections
157 if (clat != lat0_cache) {
158 lat0_cache = clat;
159 switch (m_projection_type) {
160 case PROJECTION_MERCATOR:
161 case PROJECTION_WEB_MERCATOR:
162 cache0 = toSMcache_y30(clat);
163 break;
164 case PROJECTION_POLAR:
165 cache0 = toPOLARcache_e(clat);
166 break;
167 case PROJECTION_ORTHOGRAPHIC:
168 case PROJECTION_STEREOGRAPHIC:
169 case PROJECTION_GNOMONIC:
170 cache_phi0(clat, &cache0, &cache1);
171 break;
172 }
173 }
174
175 switch (m_projection_type) {
176 case PROJECTION_MERCATOR:
177 case PROJECTION_WEB_MERCATOR:
178 toSMcache(lat, xlon, cache0, clon, &easting, &northing);
179 break;
180
181 case PROJECTION_TRANSVERSE_MERCATOR:
182 // We calculate northings as referenced to the equator
183 // And eastings as though the projection point is midscreen.
184
185 double tmeasting, tmnorthing;
186 double tmceasting, tmcnorthing;
187 toTM(clat, clon, 0., clon, &tmceasting, &tmcnorthing);
188 toTM(lat, xlon, 0., clon, &tmeasting, &tmnorthing);
189
190 northing = tmnorthing - tmcnorthing;
191 easting = tmeasting - tmceasting;
192 break;
193
194 case PROJECTION_POLYCONIC:
195
196 // We calculate northings as referenced to the equator
197 // And eastings as though the projection point is midscreen.
198 double pceasting, pcnorthing;
199 toPOLY(clat, clon, 0., clon, &pceasting, &pcnorthing);
200
201 double peasting, pnorthing;
202 toPOLY(lat, xlon, 0., clon, &peasting, &pnorthing);
203
204 easting = peasting;
205 northing = pnorthing - pcnorthing;
206 break;
207
208 case PROJECTION_ORTHOGRAPHIC:
209 toORTHO(lat, xlon, cache0, cache1, clon, &easting, &northing);
210 break;
211
212 case PROJECTION_POLAR:
213 toPOLAR(lat, xlon, cache0, clat, clon, &easting, &northing);
214 break;
215
216 case PROJECTION_STEREOGRAPHIC:
217 toSTEREO(lat, xlon, cache0, cache1, clon, &easting, &northing);
218 break;
219
220 case PROJECTION_GNOMONIC:
221 toGNO(lat, xlon, cache0, cache1, clon, &easting, &northing);
222 break;
223
224 case PROJECTION_EQUIRECTANGULAR:
225 toEQUIRECT(lat, xlon, clat, clon, &easting, &northing);
226 break;
227
228 default:
229 printf("unhandled projection\n");
230 }
231
232 if (!wxFinite(easting) || !wxFinite(northing))
233 return wxPoint2DDouble(easting, northing);
234
235 double epix = easting * view_scale_ppm;
236 double npix = northing * view_scale_ppm;
237 double dxr = epix;
238 double dyr = npix;
239
240 // Apply VP Rotation
241 double angle = rotation;
242
243 if (angle) {
244 dxr = epix * cos(angle) + npix * sin(angle);
245 dyr = npix * cos(angle) - epix * sin(angle);
246 }
247 double x = (pix_width / 2.0) + dxr;
248 double y = (pix_height / 2.0) - dyr;
249 if (!g_bopengl) {
250 // Convert from physical to logical pixels when not using OpenGL.
251 // This ensures that the viewport corner coordinates remain the
252 // same in OpenGL and no-OpenGL mode (especially in MacOS).
253 x /= m_displayScale;
254 y /= m_displayScale;
255 }
256 return wxPoint2DDouble(x, y);
257}
258
259void ViewPort::GetLLFromPix(const wxPoint2DDouble &p, double *lat,
260 double *lon) {
261 // Calculate distance from the center of the viewport to the given point in
262 // physical pixels.
263 double dx = p.m_x - (pix_width / 2.0);
264 double dy = (pix_height / 2.0) - p.m_y;
265
266 double xpr = dx;
267 double ypr = dy;
268
269 // Apply VP Rotation
270 double angle = rotation;
271
272 if (angle) {
273 xpr = (dx * cos(angle)) - (dy * sin(angle));
274 ypr = (dy * cos(angle)) + (dx * sin(angle));
275 }
276 double d_east = xpr / view_scale_ppm;
277 double d_north = ypr / view_scale_ppm;
278
279 double slat = 0.0, slon = 0.0;
280 switch (m_projection_type) {
281 case PROJECTION_MERCATOR:
282 case PROJECTION_WEB_MERCATOR:
283 // TODO This could be fromSM_ECC to better match some Raster charts
284 // However, it seems that cm93 (and S57) prefer no eccentricity
285 // correction Think about it....
286 fromSM(d_east, d_north, clat, clon, &slat, &slon);
287 break;
288
289 case PROJECTION_TRANSVERSE_MERCATOR: {
290 double tmceasting, tmcnorthing;
291 toTM(clat, clon, 0., clon, &tmceasting, &tmcnorthing);
292
293 fromTM(d_east, d_north + tmcnorthing, 0., clon, &slat, &slon);
294 } break;
295
296 case PROJECTION_POLYCONIC: {
297 double polyeasting, polynorthing;
298 toPOLY(clat, clon, 0., clon, &polyeasting, &polynorthing);
299
300 fromPOLY(d_east, d_north + polynorthing, 0., clon, &slat, &slon);
301 } break;
302
303 case PROJECTION_ORTHOGRAPHIC:
304 fromORTHO(d_east, d_north, clat, clon, &slat, &slon);
305 break;
306
307 case PROJECTION_POLAR:
308 fromPOLAR(d_east, d_north, clat, clon, &slat, &slon);
309 break;
310
311 case PROJECTION_STEREOGRAPHIC:
312 fromSTEREO(d_east, d_north, clat, clon, &slat, &slon);
313 break;
314
315 case PROJECTION_GNOMONIC:
316 fromGNO(d_east, d_north, clat, clon, &slat, &slon);
317 break;
318
319 case PROJECTION_EQUIRECTANGULAR:
320 fromEQUIRECT(d_east, d_north, clat, clon, &slat, &slon);
321 break;
322
323 default:
324 printf("unhandled projection\n");
325 }
326
327 *lat = slat;
328
329 if (slon < -180.)
330 slon += 360.;
331 else if (slon > 180.)
332 slon -= 360.;
333 *lon = slon;
334}
335
336LLRegion ViewPort::GetLLRegion(const OCPNRegion &region) {
337 // todo: for these projecetions, improve this calculation by using the
338 // method in SetBoxes here
339#ifndef ocpnUSE_GL
340 return LLRegion(GetBBox());
341#else
342
343 if (!glChartCanvas::CanClipViewport(*this)) return LLRegion(GetBBox());
344
345 OCPNRegionIterator it(region);
346 LLRegion r;
347 while (it.HaveRects()) {
348 wxRect rect = it.GetRect();
349
350 int x1 = rect.x, y1 = rect.y, x2 = x1 + rect.width, y2 = y1 + rect.height;
351 int p[8] = {x1, y1, x2, y1, x2, y2, x1, y2};
352 double pll[2896]; // Max splits is 180, ((180 * 2) + 2) * 8 = 2896.
353 int j;
354
355 /* if the viewport is rotated, we must split the segments as straight lines
356 in lat/lon coordinates map to curves in projected coordinate space */
357 if (fabs(rotation) >= 0.0001) {
358 j = 0;
359 double lastlat, lastlon;
360 int li = 6;
361 GetLLFromPix(wxPoint(p[li], p[li + 1]), &lastlat, &lastlon);
362 for (int i = 0; i < 8; i += 2) {
363 double lat, lon;
364 GetLLFromPix(wxPoint(p[i], p[i + 1]), &lat, &lon);
365
366 // use 2 degree grid
367 double grid = 2;
368 int lat_splits = floor(fabs(lat - lastlat) / grid);
369 double lond = fabs(lon - lastlon);
370 int lon_splits = floor((lond > 180 ? 360 - lond : lond) / grid);
371 int splits = wxMax(lat_splits, lon_splits) + 1;
372
373 for (int k = 1; k < splits; k++) {
374 float d = (float)k / splits;
375 GetLLFromPix(wxPoint((1 - d) * p[li] + d * p[i],
376 (1 - d) * p[li + 1] + d * p[i + 1]),
377 pll + j, pll + j + 1);
378 j += 2;
379 }
380 pll[j++] = lat;
381 pll[j++] = lon;
382 li = i;
383 lastlat = lat, lastlon = lon;
384 }
385 } else {
386 j = 8;
387 for (int i = 0; i < j; i += 2)
388 GetLLFromPix(wxPoint(p[i], p[i + 1]), pll + i, pll + i + 1);
389 }
390
391 // resolve (this works even if rectangle crosses both 0 and 180)
392 for (int i = 0; i < j; i += 2) {
393 if (pll[i + 1] <= clon - 180)
394 pll[i + 1] += 360;
395 else if (pll[i + 1] >= clon + 180)
396 pll[i + 1] -= 360;
397 }
398
399 r.Union(LLRegion(j / 2, pll));
400 it.NextRect();
401 }
402 return r;
403#endif
404}
405
407 double maxlat;
408 bool subtract;
409 OCPNRegion r;
410};
411
413 const LLRegion &llregion,
414 int chart_native_scale) {
415 double rotation_save = rotation;
416 rotation = 0;
417
418 std::list<ContourRegion> cregions;
419 for (auto i = llregion.contours.begin(); i != llregion.contours.end(); i++) {
420 float *contour_points = new float[2 * i->size()];
421 int idx = 0;
422 for (auto j = i->begin(); j != i->end(); j++) {
423 contour_points[idx++] = j->y;
424 contour_points[idx++] = j->x;
425 }
426
427 double total = 0, maxlat = -90;
428 int pl = idx - 2;
429 double x0 = contour_points[0] - contour_points[pl + 0];
430 double y0 = contour_points[1] - contour_points[pl + 1];
431 // determine winding direction of this contour
432 for (int p = 0; p < idx; p += 2) {
433 maxlat = wxMax(maxlat, contour_points[p]);
434 int pn = p < idx - 2 ? p + 2 : 0;
435 double x1 = contour_points[pn + 0] - contour_points[p + 0];
436 double y1 = contour_points[pn + 1] - contour_points[p + 1];
437 total += x1 * y0 - x0 * y1;
438 x0 = x1, y0 = y1;
439 }
440
442 s.maxlat = maxlat;
443 s.subtract = total < 0;
444 s.r = GetVPRegionIntersect(region, i->size(), contour_points,
445 chart_native_scale, NULL);
446 delete[] contour_points;
447
448 std::list<ContourRegion>::iterator k = cregions.begin();
449 while (k != cregions.end()) {
450 if (k->maxlat < s.maxlat) break;
451 k++;
452 }
453 cregions.insert(k, s);
454 }
455
456 OCPNRegion r;
457 for (std::list<ContourRegion>::iterator k = cregions.begin();
458 k != cregions.end(); k++) {
459 if (k->r.Ok()) {
460 if (k->subtract)
461 r.Subtract(k->r);
462 else
463 r.Union(k->r);
464 }
465 }
466
467 rotation = rotation_save;
468 return r;
469}
470
472 float *llpoints,
473 int chart_native_scale,
474 wxPoint *ppoints) {
475 // Calculate the intersection between a given OCPNRegion (Region) and a
476 // polygon specified by lat/lon points.
477
478 // If the viewpoint is highly overzoomed wrt to chart native scale, the
479 // polygon region may be huge. This can be very expensive, and lead to
480 // crashes on some platforms (gtk in particular) So, look for this case and
481 // handle appropriately with respect to the given Region
482
483 if (chart_scale < chart_native_scale / 10) {
484 // Scan the points one-by-one, so that we can get min/max to make a bbox
485 float *pfp = llpoints;
486 float lon_max = -10000.;
487 float lon_min = 10000.;
488 float lat_max = -10000.;
489 float lat_min = 10000.;
490
491 for (int ip = 0; ip < nPoints; ip++) {
492 lon_max = wxMax(lon_max, pfp[1]);
493 lon_min = wxMin(lon_min, pfp[1]);
494 lat_max = wxMax(lat_max, pfp[0]);
495 lat_min = wxMin(lat_min, pfp[0]);
496
497 pfp += 2;
498 }
499
500 LLBBox chart_box;
501 chart_box.Set(lat_min, lon_min, lat_max, lon_max);
502
503 // Case: vpBBox is completely outside the chart box, or vice versa
504 // Return an empty region
505 if (chart_box.IntersectOut(vpBBox)) return OCPNRegion();
506
507 // Case: vpBBox is completely inside the chart box
508 // Note that this test is not perfect, and will fail for some charts.
509 // The chart coverage may be essentially triangular, and the viewport
510 // box may be in the "cut off" segment of the chart_box, and not
511 // actually exhibit any true overlap. Results will be reported
512 // incorrectly. How to fix: maybe scrub the chart points and see if it
513 // is likely that a region may be safely built and intersection tested.
514
515 if (chart_box.IntersectIn(vpBBox)) return Region;
516
517 wxPoint p1 = GetPixFromLL(lat_max, lon_min); // upper left
518 wxPoint p2 = GetPixFromLL(lat_min, lon_max); // lower right
519
520 OCPNRegion r(p1, p2);
521 r.Intersect(Region);
522 return r;
523 }
524
525 // More "normal" case
526
527 wxPoint *pp;
528
529 // Use the passed point buffer if available
530 if (ppoints == NULL)
531 pp = new wxPoint[nPoints];
532 else
533 pp = ppoints;
534
535 float *pfp = llpoints;
536
537 wxPoint p = GetPixFromLL(pfp[0], pfp[1]);
538 int poly_x_max = INVALID_COORD, poly_y_max = INVALID_COORD,
539 poly_x_min = INVALID_COORD, poly_y_min = INVALID_COORD;
540
541 bool valid = false;
542 int npPoints = 0;
543 for (int ip = 0; ip < nPoints; ip++) {
544 wxPoint p = GetPixFromLL(pfp[0], pfp[1]);
545 if (p.x == INVALID_COORD) continue;
546
547 pp[npPoints++] = p;
548
549 if (valid) {
550 poly_x_max = wxMax(poly_x_max, p.x);
551 poly_y_max = wxMax(poly_y_max, p.y);
552 poly_x_min = wxMin(poly_x_min, p.x);
553 poly_y_min = wxMin(poly_y_min, p.y);
554 } else {
555 poly_x_max = p.x;
556 poly_y_max = p.y;
557 poly_x_min = p.x;
558 poly_y_min = p.y;
559 valid = true;
560 }
561 pfp += 2;
562 }
563
564 if (!valid) {
565 if (ppoints == NULL) delete[] pp;
566 return OCPNRegion(); // empty;
567 }
568
569 // We want to avoid processing regions with very large rectangle counts,
570 // so make some tests for special cases
571
572 float_2Dpt p0, p1, p2, p3;
573
574 // First, calculate whether any segment of the input polygon intersects the
575 // specified Region
576 int nrect = 0;
577 bool b_intersect = false;
578 OCPNRegionIterator screen_region_it1(Region);
579 while (screen_region_it1.HaveRects()) {
580 wxRect rect = screen_region_it1.GetRect();
581
582 double lat, lon;
583
584 // The screen region corners
585 GetLLFromPix(wxPoint(rect.x, rect.y), &lat, &lon);
586 p0.y = lat;
587 p0.x = lon;
588
589 GetLLFromPix(wxPoint(rect.x + rect.width, rect.y), &lat, &lon);
590 p1.y = lat;
591 p1.x = lon;
592
593 GetLLFromPix(wxPoint(rect.x + rect.width, rect.y + rect.height), &lat,
594 &lon);
595 p2.y = lat;
596 p2.x = lon;
597
598 GetLLFromPix(wxPoint(rect.x, rect.y + rect.height), &lat, &lon);
599 p3.y = lat;
600 p3.x = lon;
601
602 for (int i = 0; i < npPoints - 1; i++) {
603 // Quick check on y dimension
604 int y0 = pp[i].y;
605 int y1 = pp[i + 1].y;
606
607 if (((y0 < rect.y) && (y1 < rect.y)) ||
608 ((y0 > rect.y + rect.height) && (y1 > rect.y + rect.height)))
609 continue; // both ends of line outside of box, top or bottom
610
611 // Look harder
612 float_2Dpt f0;
613 f0.y = llpoints[i * 2];
614 f0.x = llpoints[(i * 2) + 1];
615 float_2Dpt f1;
616 f1.y = llpoints[(i + 1) * 2];
617 f1.x = llpoints[((i + 1) * 2) + 1];
618 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
619 if (b_intersect) break;
620 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
621 if (b_intersect) break;
622 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
623 if (b_intersect) break;
624 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
625 if (b_intersect) break;
626
627 // Must check the case where the input polygon has been pre-normalized,
628 // eg (0 < lon < 360), as cm93
629 f0.x -= 360.;
630 f1.x -= 360.;
631 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
632 if (b_intersect) break;
633 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
634 if (b_intersect) break;
635 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
636 if (b_intersect) break;
637 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
638 if (b_intersect) break;
639 }
640
641 // Check segment, last point back to first point
642 if (!b_intersect) {
643 float_2Dpt f0;
644 f0.y = llpoints[(nPoints - 1) * 2];
645 f0.x = llpoints[((nPoints - 1) * 2) + 1];
646 float_2Dpt f1;
647 f1.y = llpoints[0];
648 f1.x = llpoints[1];
649 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
650 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
651 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
652 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
653
654 f0.x -= 360.;
655 f1.x -= 360.;
656 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
657 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
658 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
659 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
660 }
661
662 screen_region_it1.NextRect();
663 nrect++;
664 }
665
666 // If there is no itersection, we need to consider the case where
667 // the subject polygon is entirely within the Region
668 bool b_contained = false;
669 if (!b_intersect) {
670 OCPNRegionIterator screen_region_it2(Region);
671 while (screen_region_it2.HaveRects()) {
672 wxRect rect = screen_region_it2.GetRect();
673
674 for (int i = 0; i < npPoints - 1; i++) {
675 int x0 = pp[i].x;
676 int y0 = pp[i].y;
677
678 if ((x0 < rect.x) || (x0 > rect.x + rect.width) || (y0 < rect.y) ||
679 (y0 > rect.y + rect.height))
680 continue;
681
682 b_contained = true;
683 break;
684 }
685 screen_region_it2.NextRect();
686 }
687 }
688
689#if 1
690 // and here is the payoff
691 if (!b_contained && !b_intersect) {
692 // Two cases to consider
693 wxRect rpoly(poly_x_min, poly_y_min, poly_x_max - poly_x_min,
694 poly_y_max - poly_y_min);
695 wxRect rRegion = Region.GetBox();
696 if (rpoly.Contains(rRegion)) {
697 // subject poygon may be large enough to fully encompass the target
698 // Region, but it might not, especially for irregular or concave charts.
699 // So we cannot directly shortcut here
700 // Better check....
701
702#if 1
703 if (nrect == 1) { // most common case
704 // If the subject polygon contains the center of the target rectangle,
705 // then the intersection must be the target rectangle
706 float rlat = (p0.y + p2.y) / 2.;
707 float rlon = (p0.x + p1.x) / 2.;
708
709 if (G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
710 if (NULL == ppoints) delete[] pp;
711 return Region;
712 }
713 rlon += 360.;
714 if (G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
715 if (NULL == ppoints) delete[] pp;
716 return Region;
717 }
718
719 // otherwise, there is no intersection
720 else {
721 if (NULL == ppoints) delete[] pp;
722 wxRegion r;
723 return r;
724 }
725 }
726
727#endif
728
729 } else {
730 // Subject polygon is entirely outside of target Region
731 // so the intersection must be empty.
732 if (NULL == ppoints) delete[] pp;
733 wxRegion r;
734 return r;
735 }
736 } else if (b_contained && !b_intersect) {
737 // subject polygon is entirely withing the target Region,
738 // so the intersection is the subject polygon
739 OCPNRegion r = OCPNRegion(npPoints, pp);
740 if (NULL == ppoints) delete[] pp;
741 return r;
742 }
743
744#endif
745
746#ifdef __WXGTK__
747 sigaction(SIGSEGV, NULL,
748 &sa_all_old); // save existing action for this signal
749
750 struct sigaction temp;
751 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
752
753 temp.sa_handler = catch_signals; // point to my handler
754 sigemptyset(&temp.sa_mask); // make the blocking set
755 // empty, so that all
756 // other signals will be
757 // unblocked during my handler
758 temp.sa_flags = 0;
759 sigaction(SIGSEGV, &temp, NULL);
760
761 if (sigsetjmp(env, 1)) // Something in the below code block faulted....
762 {
763 sigaction(SIGSEGV, &sa_all_old, NULL); // reset signal handler
764
765 return Region;
766
767 }
768
769 else {
770 OCPNRegion r = OCPNRegion(npPoints, pp);
771 if (NULL == ppoints) delete[] pp;
772
773 sigaction(SIGSEGV, &sa_all_old, NULL); // reset signal handler
774 r.Intersect(Region);
775 return r;
776 }
777
778#else
779 OCPNRegion r = OCPNRegion(npPoints, pp);
780
781 if (NULL == ppoints) delete[] pp;
782
783 r.Intersect(Region);
784 return r;
785
786#endif
787}
788
789wxRect ViewPort::GetVPRectIntersect(size_t n, float *llpoints) {
790 // Calculate the intersection between the currect VP screen
791 // and the bounding box of a polygon specified by lat/lon points.
792
793 float *pfp = llpoints;
794
795 BoundingBox point_box;
796 for (unsigned int ip = 0; ip < n; ip++) {
797 point_box.Expand(pfp[1], pfp[0]);
798 pfp += 2;
799 }
800
801 wxPoint pul = GetPixFromLL(point_box.GetMaxY(), point_box.GetMinX());
802 wxPoint plr = GetPixFromLL(point_box.GetMinY(), point_box.GetMaxX());
803
804 OCPNRegion r(pul, plr);
805 OCPNRegion rs(rv_rect);
806
807 r.Intersect(rs);
808
809 return r.GetBox();
810}
811
813 // In the case where canvas rotation is applied, we need to define a larger
814 // "virtual" pixel window size to ensure that enough chart data is fatched
815 // and available to fill the rotated screen.
816 rv_rect = wxRect(0, 0, pix_width, pix_height);
817
818 // Specify the minimum required rectangle in unrotated screen space which
819 // will supply full screen data after specified rotation
820 if ((fabs(skew) > .0001) || (fabs(rotation) > .0001)) {
821 double rotator = rotation;
822 double lpixh = pix_height;
823 double lpixw = pix_width;
824
825 lpixh = wxMax(lpixh,
826 (fabs(pix_height * cos(skew)) + fabs(pix_width * sin(skew))));
827 lpixw = wxMax(lpixw,
828 (fabs(pix_width * cos(skew)) + fabs(pix_height * sin(skew))));
829
830 int dy = wxRound(fabs(lpixh * cos(rotator)) + fabs(lpixw * sin(rotator)));
831 int dx = wxRound(fabs(lpixw * cos(rotator)) + fabs(lpixh * sin(rotator)));
832
833 // It is important for MSW build that viewport pixel dimensions be
834 // multiples of 4.....
835 if (dy % 4) dy += 4 - (dy % 4);
836 if (dx % 4) dx += 4 - (dx % 4);
837
838 int inflate_x = wxMax((dx - pix_width) / 2, 0);
839 int inflate_y = wxMax((dy - pix_height) / 2, 0);
840
841 // Grow the source rectangle appropriately
842 rv_rect.Inflate(inflate_x, inflate_y);
843 }
844
845 // Compute Viewport lat/lon reference points for co-ordinate hit testing
846
847 // This must be done in unrotated space with respect to full unrotated screen
848 // space calculated above
849 double rotation_save = rotation;
850 SetRotationAngle(0.0);
851
852 wxPoint ul(rv_rect.x, rv_rect.y); // Upper left.
853 wxPoint lr(rv_rect.x + rv_rect.width,
854 rv_rect.y + rv_rect.height); // Lower right.
855 double dlat_min, dlat_max, dlon_min, dlon_max;
856
857 bool hourglass = false;
858 switch (m_projection_type) {
859 case PROJECTION_TRANSVERSE_MERCATOR:
860 case PROJECTION_STEREOGRAPHIC:
861 case PROJECTION_GNOMONIC:
862 hourglass = true;
863 // fall through
864 case PROJECTION_POLYCONIC:
865 case PROJECTION_POLAR:
866 case PROJECTION_ORTHOGRAPHIC: {
867 double d;
868
869 if (clat > 0) { // north polar
870 wxPoint u(rv_rect.x + rv_rect.width / 2, rv_rect.y);
871 wxPoint ur(rv_rect.x + rv_rect.width, rv_rect.y);
872 GetLLFromPix(ul, &d, &dlon_min);
873 GetLLFromPix(ur, &d, &dlon_max);
874 GetLLFromPix(lr, &dlat_min, &d);
875 GetLLFromPix(u, &dlat_max, &d);
876
877 if (fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
878 dlat_max = 90;
879 dlon_min = -180;
880 dlon_max = 180;
881 } else if (std::isnan(dlat_max))
882 dlat_max = 90;
883
884 if (hourglass) {
885 // near equator, center may be less
886 wxPoint l(rv_rect.x + rv_rect.width / 2, rv_rect.y + rv_rect.height);
887 double dlat_min2;
888 GetLLFromPix(l, &dlat_min2, &d);
889 dlat_min = wxMin(dlat_min, dlat_min2);
890 }
891
892 if (std::isnan(dlat_min)) // world is off-screen
893 dlat_min = clat - 90;
894 } else { // south polar
895 wxPoint l(rv_rect.x + rv_rect.width / 2, rv_rect.y + rv_rect.height);
896 wxPoint ll(rv_rect.x, rv_rect.y + rv_rect.height);
897 GetLLFromPix(ul, &dlat_max, &d);
898 GetLLFromPix(lr, &d, &dlon_max);
899 GetLLFromPix(ll, &d, &dlon_min);
900 GetLLFromPix(l, &dlat_min, &d);
901
902 if (fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
903 dlat_min = -90;
904 dlon_min = -180;
905 dlon_max = 180;
906 } else if (std::isnan(dlat_min))
907 dlat_min = -90;
908
909 if (hourglass) {
910 // near equator, center may be less
911 wxPoint u(rv_rect.x + rv_rect.width / 2, rv_rect.y);
912 double dlat_max2;
913 GetLLFromPix(u, &dlat_max2, &d);
914 dlat_max = wxMax(dlat_max, dlat_max2);
915 }
916
917 if (std::isnan(dlat_max)) // world is off-screen
918 dlat_max = clat + 90;
919 }
920
921 if (std::isnan(dlon_min)) {
922 // if neither pole is visible, but left and right of the screen are in
923 // space we can avoid drawing the far side of the earth
924 if (dlat_max < 90 && dlat_min > -90) {
925 dlon_min =
926 clon - 90 -
927 fabs(clat); // this logic is not optimal, is it always correct?
928 dlon_max = clon + 90 + fabs(clat);
929 } else {
930 dlon_min = -180;
931 dlon_max = 180;
932 }
933 }
934 } break;
935
936 default: // works for mercator and equirectangular
937 {
938 GetLLFromPix(ul, &dlat_max, &dlon_min);
939 GetLLFromPix(lr, &dlat_min, &dlon_max);
940 }
941 }
942
943 if (clon < dlon_min)
944 dlon_min -= 360;
945 else if (clon > dlon_max)
946 dlon_max += 360;
947
948 // Set the viewport lat/lon bounding box appropriately
949 vpBBox.Set(dlat_min, dlon_min, dlat_max, dlon_max);
950
951 // Restore the rotation angle
952 SetRotationAngle(rotation_save);
953}
954
955void ViewPort::SetBBoxDirect(double latmin, double lonmin, double latmax,
956 double lonmax) {
957 vpBBox.Set(latmin, lonmin, latmax, lonmax);
958}
959bool ViewPort::ContainsIDL() {
960 if ((vpBBox.GetMinLon() <= -180.) && (vpBBox.GetMaxLon() > -180.))
961 return true;
962 if ((vpBBox.GetMinLon() <= 180.) && (vpBBox.GetMaxLon() > 180.)) return true;
963 return false;
964}
965
966ViewPort ViewPort::BuildExpandedVP(int width, int height) {
967 ViewPort new_vp = *this;
968
969 new_vp.pix_width = width;
970 new_vp.pix_height = height;
971 new_vp.SetBoxes();
972
973 return new_vp;
974}
975
976void ViewPort::SetVPTransformMatrix() {
977 mat4x4 m;
978 mat4x4_identity(m);
979 mat4x4_scale_aniso((float(*)[4])vp_matrix_transform, m,
980 2.0 / (float)pix_width, -2.0 / (float)pix_height, 1.0);
981 mat4x4_translate_in_place((float(*)[4])vp_matrix_transform, -pix_width / 2,
982 -pix_height / 2, 0);
983}
Class AisDecoder and helpers.
Class AISTargetAlertDialog and helpers.
AIS target definitions.
Chart info panel.
Charts database management
BSB chart management.
Generic Chart canvas base.
An iterator class for OCPNRegion.
A wrapper class for wxRegion with additional functionality.
Definition ocpn_region.h:37
ViewPort - Core geographic projection and coordinate transformation engine.
Definition viewport.h:56
void SetBoxes()
Computes the bounding box coordinates for the current viewport.
Definition viewport.cpp:812
double view_scale_ppm
Requested view scale in physical pixels per meter (ppm), before applying projections.
Definition viewport.h:204
int pix_height
Height of the viewport in physical pixels.
Definition viewport.h:233
double rotation
Rotation angle of the viewport in radians.
Definition viewport.h:214
void SetPixelScale(double scale)
Set the physical to logical pixel ratio for the display.
Definition viewport.cpp:124
wxRect GetVPRectIntersect(size_t n, float *llpoints)
Get the viewport rectangle intersecting with a set of lat/lon points.
Definition viewport.cpp:789
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:136
double tilt
Tilt angle for perspective view in radians.
Definition viewport.h:216
double skew
Angular distortion (shear transform) applied to the viewport in radians.
Definition viewport.h:212
void GetLLFromPix(const wxPoint &p, double *lat, double *lon)
Convert physical pixel coordinates on the ViewPort to latitude and longitude.
Definition viewport.h:80
OCPNRegion GetVPRegionIntersect(const OCPNRegion &region, const LLRegion &llregion, int chart_native_scale)
Get the intersection of the viewport with a given region.
Definition viewport.cpp:412
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
wxPoint GetPixFromLL(double lat, double lon)
Convert latitude and longitude on the ViewPort to physical pixel coordinates.
Definition viewport.cpp:127
double chart_scale
Chart scale denominator (e.g., 50000 for a 1:50000 scale).
Definition viewport.h:219
Class cm93chart and helpers – CM93 chart state.
Primary navigation console display for route and vessel tracking.
Extern C linked utilities.
Font list manager.
OpenGL chart rendering canvas.
Platform independent GL includes.
OpenGL texture container.
GSHHS Chart Object (Global Self-consistent, Hierarchical, High-resolution Shoreline) Derived from htt...
Read and write KML Format.
Multiplexer class and helpers.
Utility functions.
Optimized wxBitmap Object.
OpenCPN Platform specific support utilities.
OpenCPN region handling.
Layer to use wxDC or opengl.
PlugInManager and helper classes – Mostly gui parts (dialogs) and plugin API stuff.
Chart quilt support.
Route Manager.
Manage routes dialog.
S57 Chart Object.
Selected route, segment, waypoint, etc.
A single, selected generic item.
Send route/waypoint to GPS dialog.
Chart Symbols.
Tide and currents window.
Tide and Current Manager @TODO Add original author copyright.
Chart thumbnail object.
Timer identification constants.
Track Properties Dialog.
Framework for Undo features.