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//------------------------------------------------------------------------------
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 (auto i = llregion.contours.begin(); i != llregion.contours.end(); i++) {
417 float *contour_points = new float[2 * i->size()];
418 int idx = 0;
419 for (auto j = i->begin(); j != i->end(); j++) {
420 contour_points[idx++] = j->y;
421 contour_points[idx++] = j->x;
422 }
423
424 double total = 0, maxlat = -90;
425 int pl = idx - 2;
426 double x0 = contour_points[0] - contour_points[pl + 0];
427 double y0 = contour_points[1] - contour_points[pl + 1];
428 // determine winding direction of this contour
429 for (int p = 0; p < idx; p += 2) {
430 maxlat = wxMax(maxlat, contour_points[p]);
431 int pn = p < idx - 2 ? p + 2 : 0;
432 double x1 = contour_points[pn + 0] - contour_points[p + 0];
433 double y1 = contour_points[pn + 1] - contour_points[p + 1];
434 total += x1 * y0 - x0 * y1;
435 x0 = x1, y0 = y1;
436 }
437
439 s.maxlat = maxlat;
440 s.subtract = total < 0;
441 s.r = GetVPRegionIntersect(region, i->size(), contour_points,
442 chart_native_scale, NULL);
443 delete[] contour_points;
444
445 std::list<ContourRegion>::iterator k = cregions.begin();
446 while (k != cregions.end()) {
447 if (k->maxlat < s.maxlat) break;
448 k++;
449 }
450 cregions.insert(k, s);
451 }
452
453 OCPNRegion r;
454 for (std::list<ContourRegion>::iterator k = cregions.begin();
455 k != cregions.end(); k++) {
456 if (k->r.Ok()) {
457 if (k->subtract)
458 r.Subtract(k->r);
459 else
460 r.Union(k->r);
461 }
462 }
463
464 rotation = rotation_save;
465 return r;
466}
467
469 float *llpoints,
470 int chart_native_scale,
471 wxPoint *ppoints) {
472 // Calculate the intersection between a given OCPNRegion (Region) and a
473 // polygon specified by lat/lon points.
474
475 // If the viewpoint is highly overzoomed wrt to chart native scale, the
476 // polygon region may be huge. This can be very expensive, and lead to
477 // crashes on some platforms (gtk in particular) So, look for this case and
478 // handle appropriately with respect to the given Region
479
480 if (chart_scale < chart_native_scale / 10) {
481 // Scan the points one-by-one, so that we can get min/max to make a bbox
482 float *pfp = llpoints;
483 float lon_max = -10000.;
484 float lon_min = 10000.;
485 float lat_max = -10000.;
486 float lat_min = 10000.;
487
488 for (int ip = 0; ip < nPoints; ip++) {
489 lon_max = wxMax(lon_max, pfp[1]);
490 lon_min = wxMin(lon_min, pfp[1]);
491 lat_max = wxMax(lat_max, pfp[0]);
492 lat_min = wxMin(lat_min, pfp[0]);
493
494 pfp += 2;
495 }
496
497 LLBBox chart_box;
498 chart_box.Set(lat_min, lon_min, lat_max, lon_max);
499
500 // Case: vpBBox is completely outside the chart box, or vice versa
501 // Return an empty region
502 if (chart_box.IntersectOut(vpBBox)) return OCPNRegion();
503
504 // Case: vpBBox is completely inside the chart box
505 // Note that this test is not perfect, and will fail for some charts.
506 // The chart coverage may be essentially triangular, and the viewport
507 // box may be in the "cut off" segment of the chart_box, and not
508 // actually exhibit any true overlap. Results will be reported
509 // incorrectly. How to fix: maybe scrub the chart points and see if it
510 // is likely that a region may be safely built and intersection tested.
511
512 if (chart_box.IntersectIn(vpBBox)) return Region;
513
514 wxPoint p1 = GetPixFromLL(lat_max, lon_min); // upper left
515 wxPoint p2 = GetPixFromLL(lat_min, lon_max); // lower right
516
517 OCPNRegion r(p1, p2);
518 r.Intersect(Region);
519 return r;
520 }
521
522 // More "normal" case
523
524 wxPoint *pp;
525
526 // Use the passed point buffer if available
527 if (ppoints == NULL)
528 pp = new wxPoint[nPoints];
529 else
530 pp = ppoints;
531
532 float *pfp = llpoints;
533
534 wxPoint p = GetPixFromLL(pfp[0], pfp[1]);
535 int poly_x_max = INVALID_COORD, poly_y_max = INVALID_COORD,
536 poly_x_min = INVALID_COORD, poly_y_min = INVALID_COORD;
537
538 bool valid = false;
539 int npPoints = 0;
540 for (int ip = 0; ip < nPoints; ip++) {
541 wxPoint p = GetPixFromLL(pfp[0], pfp[1]);
542 if (p.x == INVALID_COORD) continue;
543
544 pp[npPoints++] = p;
545
546 if (valid) {
547 poly_x_max = wxMax(poly_x_max, p.x);
548 poly_y_max = wxMax(poly_y_max, p.y);
549 poly_x_min = wxMin(poly_x_min, p.x);
550 poly_y_min = wxMin(poly_y_min, p.y);
551 } else {
552 poly_x_max = p.x;
553 poly_y_max = p.y;
554 poly_x_min = p.x;
555 poly_y_min = p.y;
556 valid = true;
557 }
558 pfp += 2;
559 }
560
561 if (!valid) {
562 if (ppoints == NULL) delete[] pp;
563 return OCPNRegion(); // empty;
564 }
565
566 // We want to avoid processing regions with very large rectangle counts,
567 // so make some tests for special cases
568
569 float_2Dpt p0, p1, p2, p3;
570
571 // First, calculate whether any segment of the input polygon intersects the
572 // specified Region
573 int nrect = 0;
574 bool b_intersect = false;
575 OCPNRegionIterator screen_region_it1(Region);
576 while (screen_region_it1.HaveRects()) {
577 wxRect rect = screen_region_it1.GetRect();
578
579 double lat, lon;
580
581 // The screen region corners
582 GetLLFromPix(wxPoint(rect.x, rect.y), &lat, &lon);
583 p0.y = lat;
584 p0.x = lon;
585
586 GetLLFromPix(wxPoint(rect.x + rect.width, rect.y), &lat, &lon);
587 p1.y = lat;
588 p1.x = lon;
589
590 GetLLFromPix(wxPoint(rect.x + rect.width, rect.y + rect.height), &lat,
591 &lon);
592 p2.y = lat;
593 p2.x = lon;
594
595 GetLLFromPix(wxPoint(rect.x, rect.y + rect.height), &lat, &lon);
596 p3.y = lat;
597 p3.x = lon;
598
599 for (int i = 0; i < npPoints - 1; i++) {
600 // Quick check on y dimension
601 int y0 = pp[i].y;
602 int y1 = pp[i + 1].y;
603
604 if (((y0 < rect.y) && (y1 < rect.y)) ||
605 ((y0 > rect.y + rect.height) && (y1 > rect.y + rect.height)))
606 continue; // both ends of line outside of box, top or bottom
607
608 // Look harder
609 float_2Dpt f0;
610 f0.y = llpoints[i * 2];
611 f0.x = llpoints[(i * 2) + 1];
612 float_2Dpt f1;
613 f1.y = llpoints[(i + 1) * 2];
614 f1.x = llpoints[((i + 1) * 2) + 1];
615 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
616 if (b_intersect) break;
617 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
618 if (b_intersect) break;
619 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
620 if (b_intersect) break;
621 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
622 if (b_intersect) break;
623
624 // Must check the case where the input polygon has been pre-normalized,
625 // eg (0 < lon < 360), as cm93
626 f0.x -= 360.;
627 f1.x -= 360.;
628 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
629 if (b_intersect) break;
630 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
631 if (b_intersect) break;
632 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
633 if (b_intersect) break;
634 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
635 if (b_intersect) break;
636 }
637
638 // Check segment, last point back to first point
639 if (!b_intersect) {
640 float_2Dpt f0;
641 f0.y = llpoints[(nPoints - 1) * 2];
642 f0.x = llpoints[((nPoints - 1) * 2) + 1];
643 float_2Dpt f1;
644 f1.y = llpoints[0];
645 f1.x = llpoints[1];
646 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
647 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
648 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
649 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
650
651 f0.x -= 360.;
652 f1.x -= 360.;
653 b_intersect |= Intersect_FL(p0, p1, f0, f1) != 0;
654 b_intersect |= Intersect_FL(p1, p2, f0, f1) != 0;
655 b_intersect |= Intersect_FL(p2, p3, f0, f1) != 0;
656 b_intersect |= Intersect_FL(p3, p0, f0, f1) != 0;
657 }
658
659 screen_region_it1.NextRect();
660 nrect++;
661 }
662
663 // If there is no itersection, we need to consider the case where
664 // the subject polygon is entirely within the Region
665 bool b_contained = false;
666 if (!b_intersect) {
667 OCPNRegionIterator screen_region_it2(Region);
668 while (screen_region_it2.HaveRects()) {
669 wxRect rect = screen_region_it2.GetRect();
670
671 for (int i = 0; i < npPoints - 1; i++) {
672 int x0 = pp[i].x;
673 int y0 = pp[i].y;
674
675 if ((x0 < rect.x) || (x0 > rect.x + rect.width) || (y0 < rect.y) ||
676 (y0 > rect.y + rect.height))
677 continue;
678
679 b_contained = true;
680 break;
681 }
682 screen_region_it2.NextRect();
683 }
684 }
685
686#if 1
687 // and here is the payoff
688 if (!b_contained && !b_intersect) {
689 // Two cases to consider
690 wxRect rpoly(poly_x_min, poly_y_min, poly_x_max - poly_x_min,
691 poly_y_max - poly_y_min);
692 wxRect rRegion = Region.GetBox();
693 if (rpoly.Contains(rRegion)) {
694 // subject poygon may be large enough to fully encompass the target
695 // Region, but it might not, especially for irregular or concave charts.
696 // So we cannot directly shortcut here
697 // Better check....
698
699#if 1
700 if (nrect == 1) { // most common case
701 // If the subject polygon contains the center of the target rectangle,
702 // then the intersection must be the target rectangle
703 float rlat = (p0.y + p2.y) / 2.;
704 float rlon = (p0.x + p1.x) / 2.;
705
706 if (G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
707 if (NULL == ppoints) delete[] pp;
708 return Region;
709 }
710 rlon += 360.;
711 if (G_PtInPolygon_FL((float_2Dpt *)llpoints, nPoints, rlon, rlat)) {
712 if (NULL == ppoints) delete[] pp;
713 return Region;
714 }
715
716 // otherwise, there is no intersection
717 else {
718 if (NULL == ppoints) delete[] pp;
719 wxRegion r;
720 return r;
721 }
722 }
723
724#endif
725
726 } else {
727 // Subject polygon is entirely outside of target Region
728 // so the intersection must be empty.
729 if (NULL == ppoints) delete[] pp;
730 wxRegion r;
731 return r;
732 }
733 } else if (b_contained && !b_intersect) {
734 // subject polygon is entirely withing the target Region,
735 // so the intersection is the subject polygon
736 OCPNRegion r = OCPNRegion(npPoints, pp);
737 if (NULL == ppoints) delete[] pp;
738 return r;
739 }
740
741#endif
742
743#ifdef __WXGTK__
744 sigaction(SIGSEGV, NULL,
745 &sa_all_old); // save existing action for this signal
746
747 struct sigaction temp;
748 sigaction(SIGSEGV, NULL, &temp); // inspect existing action for this signal
749
750 temp.sa_handler = catch_signals; // point to my handler
751 sigemptyset(&temp.sa_mask); // make the blocking set
752 // empty, so that all
753 // other signals will be
754 // unblocked during my handler
755 temp.sa_flags = 0;
756 sigaction(SIGSEGV, &temp, NULL);
757
758 if (sigsetjmp(env, 1)) // Something in the below code block faulted....
759 {
760 sigaction(SIGSEGV, &sa_all_old, NULL); // reset signal handler
761
762 return Region;
763
764 }
765
766 else {
767 OCPNRegion r = OCPNRegion(npPoints, pp);
768 if (NULL == ppoints) delete[] pp;
769
770 sigaction(SIGSEGV, &sa_all_old, NULL); // reset signal handler
771 r.Intersect(Region);
772 return r;
773 }
774
775#else
776 OCPNRegion r = OCPNRegion(npPoints, pp);
777
778 if (NULL == ppoints) delete[] pp;
779
780 r.Intersect(Region);
781 return r;
782
783#endif
784}
785
786wxRect ViewPort::GetVPRectIntersect(size_t n, float *llpoints) {
787 // Calculate the intersection between the currect VP screen
788 // and the bounding box of a polygon specified by lat/lon points.
789
790 float *pfp = llpoints;
791
792 BoundingBox point_box;
793 for (unsigned int ip = 0; ip < n; ip++) {
794 point_box.Expand(pfp[1], pfp[0]);
795 pfp += 2;
796 }
797
798 wxPoint pul = GetPixFromLL(point_box.GetMaxY(), point_box.GetMinX());
799 wxPoint plr = GetPixFromLL(point_box.GetMinY(), point_box.GetMaxX());
800
801 OCPNRegion r(pul, plr);
802 OCPNRegion rs(rv_rect);
803
804 r.Intersect(rs);
805
806 return r.GetBox();
807}
808
810 // In the case where canvas rotation is applied, we need to define a larger
811 // "virtual" pixel window size to ensure that enough chart data is fatched
812 // and available to fill the rotated screen.
813 rv_rect = wxRect(0, 0, pix_width, pix_height);
814
815 // Specify the minimum required rectangle in unrotated screen space which
816 // will supply full screen data after specified rotation
817 if ((fabs(skew) > .0001) || (fabs(rotation) > .0001)) {
818 double rotator = rotation;
819 double lpixh = pix_height;
820 double lpixw = pix_width;
821
822 lpixh = wxMax(lpixh,
823 (fabs(pix_height * cos(skew)) + fabs(pix_width * sin(skew))));
824 lpixw = wxMax(lpixw,
825 (fabs(pix_width * cos(skew)) + fabs(pix_height * sin(skew))));
826
827 int dy = wxRound(fabs(lpixh * cos(rotator)) + fabs(lpixw * sin(rotator)));
828 int dx = wxRound(fabs(lpixw * cos(rotator)) + fabs(lpixh * sin(rotator)));
829
830 // It is important for MSW build that viewport pixel dimensions be
831 // multiples of 4.....
832 if (dy % 4) dy += 4 - (dy % 4);
833 if (dx % 4) dx += 4 - (dx % 4);
834
835 int inflate_x = wxMax((dx - pix_width) / 2, 0);
836 int inflate_y = wxMax((dy - pix_height) / 2, 0);
837
838 // Grow the source rectangle appropriately
839 rv_rect.Inflate(inflate_x, inflate_y);
840 }
841
842 // Compute Viewport lat/lon reference points for co-ordinate hit testing
843
844 // This must be done in unrotated space with respect to full unrotated screen
845 // space calculated above
846 double rotation_save = rotation;
847 SetRotationAngle(0.0);
848
849 wxPoint ul(rv_rect.x, rv_rect.y); // Upper left.
850 wxPoint lr(rv_rect.x + rv_rect.width,
851 rv_rect.y + rv_rect.height); // Lower right.
852 double dlat_min, dlat_max, dlon_min, dlon_max;
853
854 bool hourglass = false;
855 switch (m_projection_type) {
856 case PROJECTION_TRANSVERSE_MERCATOR:
857 case PROJECTION_STEREOGRAPHIC:
858 case PROJECTION_GNOMONIC:
859 hourglass = true;
860 // fall through
861 case PROJECTION_POLYCONIC:
862 case PROJECTION_POLAR:
863 case PROJECTION_ORTHOGRAPHIC: {
864 double d;
865
866 if (clat > 0) { // north polar
867 wxPoint u(rv_rect.x + rv_rect.width / 2, rv_rect.y);
868 wxPoint ur(rv_rect.x + rv_rect.width, rv_rect.y);
869 GetLLFromPix(ul, &d, &dlon_min);
870 GetLLFromPix(ur, &d, &dlon_max);
871 GetLLFromPix(lr, &dlat_min, &d);
872 GetLLFromPix(u, &dlat_max, &d);
873
874 if (fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
875 dlat_max = 90;
876 dlon_min = -180;
877 dlon_max = 180;
878 } else if (std::isnan(dlat_max))
879 dlat_max = 90;
880
881 if (hourglass) {
882 // near equator, center may be less
883 wxPoint l(rv_rect.x + rv_rect.width / 2, rv_rect.y + rv_rect.height);
884 double dlat_min2;
885 GetLLFromPix(l, &dlat_min2, &d);
886 dlat_min = wxMin(dlat_min, dlat_min2);
887 }
888
889 if (std::isnan(dlat_min)) // world is off-screen
890 dlat_min = clat - 90;
891 } else { // south polar
892 wxPoint l(rv_rect.x + rv_rect.width / 2, rv_rect.y + rv_rect.height);
893 wxPoint ll(rv_rect.x, rv_rect.y + rv_rect.height);
894 GetLLFromPix(ul, &dlat_max, &d);
895 GetLLFromPix(lr, &d, &dlon_max);
896 GetLLFromPix(ll, &d, &dlon_min);
897 GetLLFromPix(l, &dlat_min, &d);
898
899 if (fabs(fabs(d - clon) - 180) < 1) { // the pole is onscreen
900 dlat_min = -90;
901 dlon_min = -180;
902 dlon_max = 180;
903 } else if (std::isnan(dlat_min))
904 dlat_min = -90;
905
906 if (hourglass) {
907 // near equator, center may be less
908 wxPoint u(rv_rect.x + rv_rect.width / 2, rv_rect.y);
909 double dlat_max2;
910 GetLLFromPix(u, &dlat_max2, &d);
911 dlat_max = wxMax(dlat_max, dlat_max2);
912 }
913
914 if (std::isnan(dlat_max)) // world is off-screen
915 dlat_max = clat + 90;
916 }
917
918 if (std::isnan(dlon_min)) {
919 // if neither pole is visible, but left and right of the screen are in
920 // space we can avoid drawing the far side of the earth
921 if (dlat_max < 90 && dlat_min > -90) {
922 dlon_min =
923 clon - 90 -
924 fabs(clat); // this logic is not optimal, is it always correct?
925 dlon_max = clon + 90 + fabs(clat);
926 } else {
927 dlon_min = -180;
928 dlon_max = 180;
929 }
930 }
931 } break;
932
933 default: // works for mercator and equirectangular
934 {
935 GetLLFromPix(ul, &dlat_max, &dlon_min);
936 GetLLFromPix(lr, &dlat_min, &dlon_max);
937 }
938 }
939
940 if (clon < dlon_min)
941 dlon_min -= 360;
942 else if (clon > dlon_max)
943 dlon_max += 360;
944
945 // Set the viewport lat/lon bounding box appropriately
946 vpBBox.Set(dlat_min, dlon_min, dlat_max, dlon_max);
947
948 // Restore the rotation angle
949 SetRotationAngle(rotation_save);
950}
951
952void ViewPort::SetBBoxDirect(double latmin, double lonmin, double latmax,
953 double lonmax) {
954 vpBBox.Set(latmin, lonmin, latmax, lonmax);
955}
956bool ViewPort::ContainsIDL() {
957 if ((vpBBox.GetMinLon() <= -180.) && (vpBBox.GetMaxLon() > -180.))
958 return true;
959 if ((vpBBox.GetMinLon() <= 180.) && (vpBBox.GetMaxLon() > 180.)) return true;
960 return false;
961}
962
963ViewPort ViewPort::BuildExpandedVP(int width, int height) {
964 ViewPort new_vp = *this;
965
966 new_vp.pix_width = width;
967 new_vp.pix_height = height;
968 new_vp.SetBoxes();
969
970 return new_vp;
971}
972
973void ViewPort::SetVPTransformMatrix() {
974 mat4x4 m;
975 mat4x4_identity(m);
976 mat4x4_scale_aniso((float(*)[4])vp_matrix_transform, m,
977 2.0 / (float)pix_width, -2.0 / (float)pix_height, 1.0);
978 mat4x4_translate_in_place((float(*)[4])vp_matrix_transform, -pix_width / 2,
979 -pix_height / 2, 0);
980}
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:809
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:786
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.
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.