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