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