OpenCPN Partial API docs
select.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2013 by David S. Register *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the *
20  * Free Software Foundation, Inc., *
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22  ***************************************************************************
23  */
24 
25 #include <wx/list.h>
26 #include <wx/gdicmn.h>
27 
28 #include "model/base_platform.h"
29 #include "model/georef.h"
30 #include "model/nav_object_database.h"
31 #include "model/route.h"
32 #include "model/select.h"
33 #include "model/track.h"
34 
35 #include "vector2D.h"
36 
37 Select* pSelect;
38 
39 Select::Select() {
40  pSelectList = new SelectableItemList;
41  pixelRadius = g_BasePlatform->GetSelectRadiusPix();
42 }
43 
44 Select::~Select() {
45  pSelectList->DeleteContents(true);
46  pSelectList->Clear();
47  delete pSelectList;
48 }
49 
50 bool Select::IsSelectableRoutePointValid(RoutePoint *pRoutePoint) {
51  SelectItem *pFindSel;
52 
53  // Iterate on the select list
54  wxSelectableItemListNode *node = pSelectList->GetFirst();
55 
56  while (node) {
57  pFindSel = node->GetData();
58  if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT &&
59  (RoutePoint *)pFindSel->m_pData1 == pRoutePoint)
60  return true;
61  node = node->GetNext();
62  }
63  return false;
64 }
65 
66 bool Select::AddSelectableRoutePoint(float slat, float slon,
67  RoutePoint *pRoutePointAdd) {
68  SelectItem *pSelItem = new SelectItem;
69  pSelItem->m_slat = slat;
70  pSelItem->m_slon = slon;
71  pSelItem->m_seltype = SELTYPE_ROUTEPOINT;
72  pSelItem->m_bIsSelected = false;
73  pSelItem->m_pData1 = pRoutePointAdd;
74 
75  wxSelectableItemListNode *node;
76 
77  if (pRoutePointAdd->m_bIsInLayer)
78  node = pSelectList->Append(pSelItem);
79  else
80  node = pSelectList->Insert(pSelItem);
81 
82  pRoutePointAdd->SetSelectNode(node);
83 
84  return true;
85 }
86 
87 bool Select::AddSelectableRouteSegment(float slat1, float slon1, float slat2,
88  float slon2, RoutePoint *pRoutePointAdd1,
89  RoutePoint *pRoutePointAdd2,
90  Route *pRoute) {
91  SelectItem *pSelItem = new SelectItem;
92  pSelItem->m_slat = slat1;
93  pSelItem->m_slon = slon1;
94  pSelItem->m_slat2 = slat2;
95  pSelItem->m_slon2 = slon2;
96  pSelItem->m_seltype = SELTYPE_ROUTESEGMENT;
97  pSelItem->m_bIsSelected = false;
98  pSelItem->m_pData1 = pRoutePointAdd1;
99  pSelItem->m_pData2 = pRoutePointAdd2;
100  pSelItem->m_pData3 = pRoute;
101 
102  if (pRoute->m_bIsInLayer)
103  pSelectList->Append(pSelItem);
104  else
105  pSelectList->Insert(pSelItem);
106 
107  return true;
108 }
109 
110 bool Select::DeleteAllSelectableRouteSegments(Route *pr) {
111  SelectItem *pFindSel;
112 
113  // Iterate on the select list
114  wxSelectableItemListNode *node = pSelectList->GetFirst();
115 
116  while (node) {
117  pFindSel = node->GetData();
118  if (pFindSel->m_seltype == SELTYPE_ROUTESEGMENT &&
119  (Route *)pFindSel->m_pData3 == pr) {
120  delete pFindSel;
121  wxSelectableItemListNode *d = node;
122  node = node->GetNext();
123  pSelectList->DeleteNode(d); // delete node;
124  } else
125  node = node->GetNext();
126  }
127 
128  return true;
129 }
130 
131 bool Select::DeleteAllSelectableRoutePoints(Route *pr) {
132  SelectItem *pFindSel;
133 
134  // Iterate on the select list
135  wxSelectableItemListNode *node = pSelectList->GetFirst();
136 
137  while (node) {
138  pFindSel = node->GetData();
139  if (pFindSel->m_seltype == SELTYPE_ROUTEPOINT) {
140  RoutePoint *ps = (RoutePoint *)pFindSel->m_pData1;
141 
142  // inner loop iterates on the route's point list
143  wxRoutePointListNode *pnode = (pr->pRoutePointList)->GetFirst();
144  while (pnode) {
145  RoutePoint *prp = pnode->GetData();
146 
147  if (prp == ps) {
148  delete pFindSel;
149  pSelectList->DeleteNode(node); // delete node;
150  prp->SetSelectNode(NULL);
151 
152  node = pSelectList->GetFirst();
153 
154  goto got_next_outer_node;
155  }
156  pnode = pnode->GetNext();
157  }
158  }
159 
160  node = node->GetNext();
161  got_next_outer_node:
162  continue;
163  }
164  return true;
165 }
166 
167 bool Select::AddAllSelectableRoutePoints(Route *pr) {
168  if (pr->pRoutePointList->GetCount()) {
169  wxRoutePointListNode *node = (pr->pRoutePointList)->GetFirst();
170 
171  while (node) {
172  RoutePoint *prp = node->GetData();
173  AddSelectableRoutePoint(prp->m_lat, prp->m_lon, prp);
174  node = node->GetNext();
175  }
176  return true;
177  } else
178  return false;
179 }
180 
181 bool Select::AddAllSelectableRouteSegments(Route *pr) {
182  wxPoint rpt, rptn;
183  float slat1, slon1, slat2, slon2;
184 
185  if (pr->pRoutePointList->GetCount()) {
186  wxRoutePointListNode *node = (pr->pRoutePointList)->GetFirst();
187 
188  RoutePoint *prp0 = node->GetData();
189  slat1 = prp0->m_lat;
190  slon1 = prp0->m_lon;
191 
192  node = node->GetNext();
193 
194  while (node) {
195  RoutePoint *prp = node->GetData();
196  slat2 = prp->m_lat;
197  slon2 = prp->m_lon;
198 
199  AddSelectableRouteSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
200 
201  slat1 = slat2;
202  slon1 = slon2;
203  prp0 = prp;
204 
205  node = node->GetNext();
206  }
207  return true;
208  } else
209  return false;
210 }
211 
212 bool Select::AddAllSelectableTrackSegments(Track *pr) {
213  wxPoint rpt, rptn;
214  float slat1, slon1, slat2, slon2;
215 
216  if (pr->GetnPoints()) {
217  TrackPoint *prp0 = pr->GetPoint(0);
218  slat1 = prp0->m_lat;
219  slon1 = prp0->m_lon;
220 
221  for (int i = 1; i < pr->GetnPoints(); i++) {
222  TrackPoint *prp = pr->GetPoint(i);
223  slat2 = prp->m_lat;
224  slon2 = prp->m_lon;
225 
226  AddSelectableTrackSegment(slat1, slon1, slat2, slon2, prp0, prp, pr);
227 
228  slat1 = slat2;
229  slon1 = slon2;
230  prp0 = prp;
231  }
232  return true;
233  } else
234  return false;
235 }
236 
237 bool Select::UpdateSelectableRouteSegments(RoutePoint *prp) {
238  SelectItem *pFindSel;
239  bool ret = false;
240 
241  // Iterate on the select list
242  wxSelectableItemListNode *node = pSelectList->GetFirst();
243 
244  while (node) {
245  pFindSel = node->GetData();
246  if (pFindSel->m_seltype == SELTYPE_ROUTESEGMENT) {
247  if (pFindSel->m_pData1 == prp) {
248  pFindSel->m_slat = prp->m_lat;
249  pFindSel->m_slon = prp->m_lon;
250  ret = true;
251  ;
252  }
253 
254  else if (pFindSel->m_pData2 == prp) {
255  pFindSel->m_slat2 = prp->m_lat;
256  pFindSel->m_slon2 = prp->m_lon;
257  ret = true;
258  }
259  }
260  node = node->GetNext();
261  }
262 
263  return ret;
264 }
265 
266 SelectItem *Select::AddSelectablePoint(float slat, float slon,
267  const void *pdata, int fseltype) {
268  SelectItem *pSelItem = new SelectItem;
269  if (pSelItem) {
270  pSelItem->m_slat = slat;
271  pSelItem->m_slon = slon;
272  pSelItem->m_seltype = fseltype;
273  pSelItem->m_bIsSelected = false;
274  pSelItem->m_pData1 = pdata;
275 
276  pSelectList->Append(pSelItem);
277  }
278 
279  return pSelItem;
280 }
281 
282 /*
283 bool Select::DeleteAllPoints( void )
284 {
285  pSelectList->DeleteContents( true );
286  pSelectList->Clear();
287  return true;
288 }
289 */
290 
291 bool Select::DeleteSelectablePoint(void *pdata, int SeltypeToDelete) {
292  SelectItem *pFindSel;
293 
294  if (NULL != pdata) {
295  // Iterate on the list
296  wxSelectableItemListNode *node = pSelectList->GetFirst();
297 
298  while (node) {
299  pFindSel = node->GetData();
300  if (pFindSel->m_seltype == SeltypeToDelete) {
301  if (pdata == pFindSel->m_pData1) {
302  delete pFindSel;
303  delete node;
304 
305  if (SELTYPE_ROUTEPOINT == SeltypeToDelete) {
306  RoutePoint *prp = (RoutePoint *)pdata;
307  prp->SetSelectNode(NULL);
308  }
309 
310  return true;
311  }
312  }
313  node = node->GetNext();
314  }
315  }
316  return false;
317 }
318 
319 bool Select::DeleteAllSelectableTypePoints(int SeltypeToDelete) {
320  SelectItem *pFindSel;
321 
322  // Iterate on the list
323  wxSelectableItemListNode *node = pSelectList->GetFirst();
324 
325  while (node) {
326  pFindSel = node->GetData();
327  if (pFindSel->m_seltype == SeltypeToDelete) {
328  delete node;
329 
330  if (SELTYPE_ROUTEPOINT == SeltypeToDelete) {
331  RoutePoint *prp = (RoutePoint *)pFindSel->m_pData1;
332  prp->SetSelectNode(NULL);
333  }
334  delete pFindSel;
335 
336  node = pSelectList->GetFirst();
337  goto got_next_node;
338  }
339 
340  node = node->GetNext();
341  got_next_node:
342  continue;
343  }
344  return true;
345 }
346 
347 bool Select::DeleteSelectableRoutePoint(RoutePoint *prp) {
348  if (NULL != prp) {
349  wxSelectableItemListNode *node =
350  (wxSelectableItemListNode *)prp->GetSelectNode();
351  if (node) {
352  SelectItem *pFindSel = node->GetData();
353  if (pFindSel) {
354  delete pFindSel;
355  delete node; // automatically removes from list
356  prp->SetSelectNode(NULL);
357  return true;
358  }
359  } else
360  return DeleteSelectablePoint(prp, SELTYPE_ROUTEPOINT);
361  }
362  return false;
363 }
364 
365 bool Select::ModifySelectablePoint(float lat, float lon, void *data,
366  int SeltypeToModify) {
367  SelectItem *pFindSel;
368 
369  // Iterate on the list
370  wxSelectableItemListNode *node = pSelectList->GetFirst();
371 
372  while (node) {
373  pFindSel = node->GetData();
374  if (pFindSel->m_seltype == SeltypeToModify) {
375  if (data == pFindSel->m_pData1) {
376  pFindSel->m_slat = lat;
377  pFindSel->m_slon = lon;
378  return true;
379  }
380  }
381 
382  node = node->GetNext();
383  }
384  return false;
385 }
386 
387 bool Select::AddSelectableTrackSegment(float slat1, float slon1, float slat2,
388  float slon2, TrackPoint *pTrackPointAdd1,
389  TrackPoint *pTrackPointAdd2,
390  Track *pTrack) {
391  SelectItem *pSelItem = new SelectItem;
392  pSelItem->m_slat = slat1;
393  pSelItem->m_slon = slon1;
394  pSelItem->m_slat2 = slat2;
395  pSelItem->m_slon2 = slon2;
396  pSelItem->m_seltype = SELTYPE_TRACKSEGMENT;
397  pSelItem->m_bIsSelected = false;
398  pSelItem->m_pData1 = pTrackPointAdd1;
399  pSelItem->m_pData2 = pTrackPointAdd2;
400  pSelItem->m_pData3 = pTrack;
401 
402  if (pTrack->m_bIsInLayer)
403  pSelectList->Append(pSelItem);
404  else
405  pSelectList->Insert(pSelItem);
406 
407  return true;
408 }
409 
410 bool Select::DeleteAllSelectableTrackSegments(Track *pt) {
411  SelectItem *pFindSel;
412 
413  // Iterate on the select list
414  wxSelectableItemListNode *node = pSelectList->GetFirst();
415 
416  while (node) {
417  pFindSel = node->GetData();
418  if (pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
419  (Track *)pFindSel->m_pData3 == pt) {
420  delete pFindSel;
421  wxSelectableItemListNode *d = node;
422  node = node->GetNext();
423  pSelectList->DeleteNode(d); // delete node;
424  } else
425  node = node->GetNext();
426  }
427  return true;
428 }
429 
430 bool Select::DeletePointSelectableTrackSegments(TrackPoint *pt) {
431  SelectItem *pFindSel;
432 
433  // Iterate on the select list
434  wxSelectableItemListNode *node = pSelectList->GetFirst();
435 
436  while (node) {
437  pFindSel = node->GetData();
438  if (pFindSel->m_seltype == SELTYPE_TRACKSEGMENT &&
439  ((TrackPoint *)pFindSel->m_pData1 == pt ||
440  (TrackPoint *)pFindSel->m_pData2 == pt)) {
441  delete pFindSel;
442  wxSelectableItemListNode *d = node;
443  node = node->GetNext();
444  pSelectList->DeleteNode(d); // delete node;
445  } else
446  node = node->GetNext();
447  }
448  return true;
449 }
450 
451 bool Select::IsSegmentSelected(float a, float b, float c, float d, float slat,
452  float slon) {
453  double adder = 0.;
454 
455  // Track segments for some reason can have longitude values > 180.
456  // Therefore, we normalize all the lat/lon values here.
457  if (a > 90.0) a -= 180.0;
458  if (b > 90.0) b -= 180.0;
459  if (c > 180.0) c -= 360.0;
460  if (d > 180.0) d -= 360.0;
461  if (slat > 90.0) slat -= 180.0;
462  if (slon > 180.0) slon -= 360.0;
463 
464  if ((c * d) < 0.) {
465  // Arrange for points to be increasing longitude, c to d
466  double dist, brg;
467  DistanceBearingMercator(a, c, b, d, &brg, &dist);
468  if (brg < 180.) // swap points?
469  {
470  double tmp;
471  tmp = c;
472  c = d;
473  d = tmp;
474  tmp = a;
475  a = b;
476  b = tmp;
477  }
478  if (d < 0.) // idl?
479  {
480  d += 360.;
481  if (slon < 0.) adder = 360.;
482  }
483  }
484 
485  // As a course test, use segment bounding box test
486  if ((slat >= (fmin(a, b) - selectRadius)) &&
487  (slat <= (fmax(a, b) + selectRadius)) &&
488  ((slon + adder) >= (fmin(c, d) - selectRadius)) &&
489  ((slon + adder) <= (fmax(c, d) + selectRadius))) {
490  // Use vectors to do hit test....
491  vector2D va, vb, vn;
492 
493  // Assuming a Mercator projection
494  double ap, cp;
495  toSM(a, c, 0., 0., &cp, &ap);
496  double bp, dp;
497  toSM(b, d, 0., 0., &dp, &bp);
498  double slatp, slonp;
499  toSM(slat, slon + adder, 0., 0., &slonp, &slatp);
500 
501  va.x = slonp - cp;
502  va.y = slatp - ap;
503  vb.x = dp - cp;
504  vb.y = bp - ap;
505 
506  double delta = vGetLengthOfNormal(&va, &vb, &vn);
507  if (fabs(delta) < (selectRadius * 1852 * 60)) return true;
508  }
509  return false;
510 }
511 
512 void Select::CalcSelectRadius(SelectCtx& ctx) {
513  selectRadius = pixelRadius / (ctx.scale * 1852 * 60);
514 }
515 
516 SelectItem *Select::FindSelection(SelectCtx& ctx, float slat, float slon,
517  int fseltype) {
518  float a, b, c, d;
519  SelectItem *pFindSel;
520 
521  CalcSelectRadius(ctx);
522 
523  // Iterate on the list
524  wxSelectableItemListNode *node = pSelectList->GetFirst();
525 
526  while (node) {
527  pFindSel = node->GetData();
528  if (pFindSel->m_seltype == fseltype) {
529  switch (fseltype) {
530  case SELTYPE_ROUTEPOINT:
531  case SELTYPE_TIDEPOINT:
532  case SELTYPE_CURRENTPOINT:
533  case SELTYPE_AISTARGET:
534  if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
535  (fabs(slon - pFindSel->m_slon) < selectRadius)) {
536  if(fseltype == SELTYPE_ROUTEPOINT) {
537  if (((RoutePoint *)pFindSel->m_pData1)->IsVisibleSelectable(ctx.chart_scale))
538  goto find_ok;
539  } else {
540  goto find_ok;
541  }
542  }
543  break;
544  case SELTYPE_ROUTESEGMENT:
545  case SELTYPE_TRACKSEGMENT: {
546  a = pFindSel->m_slat;
547  b = pFindSel->m_slat2;
548  c = pFindSel->m_slon;
549  d = pFindSel->m_slon2;
550 
551  if (IsSegmentSelected(a, b, c, d, slat, slon)) goto find_ok;
552  break;
553  }
554  default:
555  break;
556  }
557  }
558 
559  node = node->GetNext();
560  }
561 
562  return NULL;
563 find_ok:
564  return pFindSel;
565 }
566 
567 bool Select::IsSelectableSegmentSelected(SelectCtx& ctx, float slat,
568  float slon, SelectItem *pFindSel) {
569  bool valid = false;
570  wxSelectableItemListNode *node = pSelectList->GetFirst();
571 
572  while (node) {
573  if (pFindSel == node->GetData()) {
574  valid = true;
575  break;
576  }
577  node = node->GetNext();
578  }
579 
580  if (valid == false) {
581  // not in the list anymore
582  return false;
583  }
584  CalcSelectRadius(ctx);
585 
586  float a = pFindSel->m_slat;
587  float b = pFindSel->m_slat2;
588  float c = pFindSel->m_slon;
589  float d = pFindSel->m_slon2;
590 
591  return IsSegmentSelected(a, b, c, d, slat, slon);
592 }
593 
594 static bool is_selectable_wp(SelectCtx ctx, RoutePoint *wp) {
595  if (ctx.show_nav_objects) return true;
596 
597  if (wp->m_bIsActive) return true;
598 
599  Route *rte;
600  rte = FindRouteContainingWaypoint(wp);
601  if (rte && rte->IsActive()) return true;
602 
603  return false;
604 }
605 
606 SelectableItemList Select::FindSelectionList(SelectCtx& ctx, float slat,
607  float slon, int fseltype) {
608  float a, b, c, d;
609  SelectItem *pFindSel;
610  SelectableItemList ret_list;
611 
612  CalcSelectRadius(ctx);
613 
614  // Iterate on the list
615  wxSelectableItemListNode *node = pSelectList->GetFirst();
616 
617  while (node) {
618  pFindSel = node->GetData();
619  if (pFindSel->m_seltype == fseltype) {
620  switch (fseltype) {
621  case SELTYPE_ROUTEPOINT:
622  if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
623  (fabs(slon - pFindSel->m_slon) < selectRadius))
624  if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
625  if (((RoutePoint *)pFindSel->m_pData1)->IsVisibleSelectable(ctx.chart_scale))
626  ret_list.Append(pFindSel);
627  break;
628  case SELTYPE_TIDEPOINT:
629  case SELTYPE_CURRENTPOINT:
630  case SELTYPE_AISTARGET:
631  case SELTYPE_DRAGHANDLE:
632  if ((fabs(slat - pFindSel->m_slat) < selectRadius) &&
633  (fabs(slon - pFindSel->m_slon) < selectRadius)) {
634  if (is_selectable_wp(ctx, (RoutePoint *)pFindSel->m_pData1))
635  ret_list.Append(pFindSel);
636  }
637  break;
638  case SELTYPE_ROUTESEGMENT:
639  case SELTYPE_TRACKSEGMENT: {
640  a = pFindSel->m_slat;
641  b = pFindSel->m_slat2;
642  c = pFindSel->m_slon;
643  d = pFindSel->m_slon2;
644 
645  if (IsSegmentSelected(a, b, c, d, slat, slon)) {
646  if (ctx.show_nav_objects ||
647  (fseltype == SELTYPE_ROUTESEGMENT &&
648  ((Route *)pFindSel->m_pData3)->m_bRtIsActive)) {
649  ret_list.Append(pFindSel);
650  }
651  }
652 
653  break;
654  }
655  default:
656  break;
657  }
658  }
659 
660  node = node->GetNext();
661  }
662 
663  return ret_list;
664 }
Definition: route.h:75
Definition: select.h:54
Definition: track.h:78