OpenCPN Partial API docs
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 "model/geodesic.h"
53 #include "styles.h"
54 #include "model/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 "model/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 "model/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 "model/select_item.h"
76 #include "model/select.h"
77 #include "FontMgr.h"
78 #include "model/ais_decoder.h"
79 #include "model/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 
107 extern struct sigaction sa_all_old;
108 
109 extern sigjmp_buf env; // the context saved by sigsetjmp();
110 #endif
111 
112 #include <vector>
113 
114 // ----------------------------------------------------------------------------
115 // Useful Prototypes
116 // ----------------------------------------------------------------------------
117 
118 extern void catch_signals(int signo);
119 
120 //------------------------------------------------------------------------------
121 // ViewPort Implementation
122 //------------------------------------------------------------------------------
123 ViewPort::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 
136 void 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
143 wxPoint 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 
152 wxPoint2DDouble 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 
267 void 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 
342 LLRegion 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 
418 OCPNRegion 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 
449  ContourRegion s;
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 
479 OCPNRegion 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 
797 wxRect 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 
820 void 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 
962 void ViewPort::SetBBoxDirect(double latmin, double lonmin, double latmax,
963  double lonmax) {
964  vpBBox.Set(latmin, lonmin, latmax, lonmax);
965 }
966 bool ViewPort::ContainsIDL(){
967  if ((vpBBox.GetMinLon() <= -180.) && (vpBBox.GetMaxLon() > -180.))
968  return true;
969  if ((vpBBox.GetMinLon() <= 180.) && (vpBBox.GetMaxLon() > 180.))
970  return true;
971  return false;
972 }
973 
974 ViewPort ViewPort::BuildExpandedVP(int width, int height) {
975  ViewPort new_vp = *this;
976 
977  new_vp.pix_width = width;
978  new_vp.pix_height = height;
979  new_vp.SetBoxes();
980 
981  return new_vp;
982 }
983 
984 void ViewPort::SetVPTransformMatrix() {
985  mat4x4 m;
986  mat4x4_identity(m);
987  mat4x4_scale_aniso((float(*)[4])vp_matrix_transform, m,
988  2.0 / (float)pix_width, -2.0 / (float)pix_height,
989  1.0);
990  mat4x4_translate_in_place((float(*)[4])vp_matrix_transform, -pix_width / 2,
991  -pix_height / 2, 0);
992 
993 }
Definition: Quilt.cpp:867