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