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