Fix:Core:Fixed comments for translators
[navit-package] / navit / navigation.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <glib.h>
6 #include <libintl.h>
7 #include "debug.h"
8 #include "profile.h"
9 #include "navigation.h"
10 #include "coord.h"
11 #include "item.h"
12 #include "route.h"
13 #include "transform.h"
14 #include "mapset.h"
15 #include "projection.h"
16 #include "map.h"
17 #include "navit.h"
18 #include "callback.h"
19 #include "plugin.h"
20
21 #define _(STRING)    gettext(STRING)
22
23 struct suffix {
24         char *fullname;
25         char *abbrev;
26         int sex;
27 } suffixes[]= {
28         {"weg",NULL,1},
29         {"platz","pl.",1},
30         {"ring",NULL,1},
31         {"allee",NULL,2},
32         {"gasse",NULL,2},
33         {"straße","str.",2},
34         {"strasse",NULL,2},
35 };
36
37 struct navigation {
38         struct map *map;
39         struct item_hash *hash;
40         struct navigation_itm *first;
41         struct navigation_itm *last;
42         struct navigation_command *cmd_first;
43         struct navigation_command *cmd_last;
44         struct callback_list *callback_speech;
45         struct callback_list *callback;
46         int level_last;
47         struct item item_last;
48         int turn_around;
49         int turn_around_limit;
50         int distance_turn;
51         int distance_last;
52         int announce[route_item_last-route_item_first+1][3];
53 };
54
55
56 struct navigation_command {
57         struct navigation_itm *itm;
58         struct navigation_command *next;
59         int delta;
60 };
61
62 struct navigation *
63 navigation_new(struct attr **attrs)
64 {
65         int i,j;
66         struct navigation *ret=g_new0(struct navigation, 1);
67         ret->hash=item_hash_new();
68         ret->callback=callback_list_new();
69         ret->callback_speech=callback_list_new();
70         ret->level_last=-2;
71         ret->distance_last=-2;
72         ret->distance_turn=50;
73         ret->turn_around_limit=3;
74
75         for (j = 0 ; j <= route_item_last-route_item_first ; j++) {
76                 for (i = 0 ; i < 3 ; i++) {
77                         ret->announce[j][i]=-1;
78                 }
79         }
80
81         return ret;     
82 }
83
84 int
85 navigation_set_announce(struct navigation *this_, enum item_type type, int *level)
86 {
87         int i;
88         if (type < route_item_first || type > route_item_last) {
89                 dbg(0,"street type %d out of range [%d,%d]", type, route_item_first, route_item_last);
90                 return 0;
91         }
92         for (i = 0 ; i < 3 ; i++) 
93                 this_->announce[type-route_item_first][i]=level[i];
94         return 1;
95 }
96
97 static int
98 navigation_get_announce_level(struct navigation *this_, enum item_type type, int dist)
99 {
100         int i;
101
102         if (type < route_item_first || type > route_item_last)
103                 return -1;
104         for (i = 0 ; i < 3 ; i++) {
105                 if (dist <= this_->announce[type-route_item_first][i])
106                         return i;
107         }
108         return i;
109 }
110
111 struct navigation_itm {
112         char *name1;
113         char *name2;
114         struct item item;
115         int angle_start;
116         int angle_end;
117         int time;
118         int length;
119         int dest_time;
120         int dest_length;
121         int told;
122         struct navigation_itm *next;
123         struct navigation_itm *prev;
124 };
125
126 /* 0=N,90=E */
127 static int
128 road_angle(struct coord *c1, struct coord *c2, int dir)
129 {
130         int ret=transform_get_angle_delta(c1, c2, dir);
131         dbg(1, "road_angle(0x%x,0x%x - 0x%x,0x%x)=%d\n", c1->x, c1->y, c2->x, c2->y, ret);
132         return ret;
133 }
134
135 static int
136 round_distance(int dist)
137 {
138         if (dist < 100) {
139                 dist=(dist+5)/10;
140                 return dist*10;
141         }
142         if (dist < 250) {
143                 dist=(dist+13)/25;
144                 return dist*25;
145         }
146         if (dist < 500) {
147                 dist=(dist+25)/50;
148                 return dist*50;
149         }
150         if (dist < 1000) {
151                 dist=(dist+50)/100;
152                 return dist*100;
153         }
154         if (dist < 5000) {
155                 dist=(dist+50)/100;
156                 return dist*100;
157         }
158         if (dist < 100000) {
159                 dist=(dist+500)/1000;
160                 return dist*1000;
161         }
162         dist=(dist+5000)/10000;
163         return dist*10000;
164 }
165
166 static char *
167 get_distance(int dist, enum attr_type type, int is_length)
168 {
169         if (type == attr_navigation_long) {
170                 if (is_length)
171                         return g_strdup_printf(_("%d m"), dist);
172                 else
173                         return g_strdup_printf(_("in %d m"), dist);
174         }
175         if (dist < 1000) {
176                 if (is_length)
177                         return g_strdup_printf(_("%d meters"), dist);
178                 else
179                         return g_strdup_printf(_("in %d meters"), dist);
180         }
181         if (dist < 5000) {
182                 int rem=(dist/100)%10;
183                 if (rem) {
184                         if (is_length)
185                                 return g_strdup_printf(_("%d.%d kilometer"), dist/1000, rem);
186                         else
187                                 return g_strdup_printf(_("in %d.%d kilometers"), dist/1000, rem);
188                 }
189         }
190         if (is_length) 
191                 return g_strdup_printf(ngettext("one kilometer","%d kilometers", dist/1000), dist/1000);
192         else
193                 return g_strdup_printf(ngettext("in one kilometer","in %d kilometers", dist/1000), dist/1000);
194 }
195
196 static void
197 navigation_destroy_itms_cmds(struct navigation *this_, struct navigation_itm *end)
198 {
199         struct navigation_itm *itm;
200         struct navigation_command *cmd;
201         dbg(2,"enter this_=%p this_->first=%p this_->cmd_first=%p end=%p\n", this_, this_->first, this_->cmd_first, end);
202         if (this_->cmd_first)
203                 dbg(2,"this_->cmd_first->itm=%p\n", this_->cmd_first->itm);
204         while (this_->first && this_->first != end) {
205                 itm=this_->first;
206                 dbg(3,"destroying %p\n", itm);
207                 item_hash_remove(this_->hash, &itm->item);
208                 this_->first=itm->next;
209                 if (this_->first)
210                         this_->first->prev=NULL;
211                 if (this_->cmd_first && this_->cmd_first->itm == itm->next) {
212                         cmd=this_->cmd_first;
213                         this_->cmd_first=cmd->next;
214                         g_free(cmd);
215                 }
216                 map_convert_free(itm->name1);
217                 map_convert_free(itm->name2);
218                 g_free(itm);
219         }
220         if (! this_->first)
221                 this_->last=NULL;
222         if (! this_->first && end) 
223                 dbg(0,"end wrong\n");
224         dbg(2,"ret this_->first=%p this_->cmd_first=%p\n",this_->first, this_->cmd_first);
225 }
226
227 static void
228 navigation_itm_update(struct navigation_itm *itm, struct item *ritem)
229 {
230         struct attr length, time;
231         if (! item_attr_get(ritem, attr_length, &length)) {
232                 dbg(0,"no length\n");
233                 return;
234         }
235         if (! item_attr_get(ritem, attr_time, &time)) {
236                 dbg(0,"no time\n");
237                 return;
238         }
239         dbg(1,"length=%d time=%d\n", length.u.num, time.u.num);
240         itm->length=length.u.num;
241         itm->time=time.u.num;
242 }
243
244 static struct navigation_itm *
245 navigation_itm_new(struct navigation *this_, struct item *ritem)
246 {
247         struct navigation_itm *ret=g_new0(struct navigation_itm, 1);
248         int l,i=0;
249         struct item *sitem;
250         struct attr street_item;
251         struct map_rect *mr;
252         struct attr attr;
253         struct coord c[5];
254
255         if (ritem) {
256                 ret->told=0;
257                 if (! item_attr_get(ritem, attr_street_item, &street_item)) {
258                         dbg(0,"no street item\n");
259                         return NULL;
260                 }
261                 sitem=street_item.u.item;
262                 ret->item=*sitem;
263                 item_hash_insert(this_->hash, sitem, ret);
264                 mr=map_rect_new(sitem->map, NULL);
265                 sitem=map_rect_get_item_byid(mr, sitem->id_hi, sitem->id_lo);
266                 if (item_attr_get(sitem, attr_street_name, &attr))
267                         ret->name1=map_convert_string(sitem->map,attr.u.str);
268                 if (item_attr_get(sitem, attr_street_name_systematic, &attr))
269                         ret->name2=map_convert_string(sitem->map,attr.u.str);
270                 navigation_itm_update(ret, ritem);
271                 l=-1;
272                 while (item_coord_get(ritem, &c[i], 1)) {
273                         dbg(1, "coord %d 0x%x 0x%x\n", i, c[i].x ,c[i].y);
274                         l=i;
275                         if (i < 4) 
276                                 i++;
277                         else {
278                                 c[2]=c[3];
279                                 c[3]=c[4];
280                         }
281                 }
282                 dbg(1,"count=%d\n", l);
283                 if (l == 4)
284                         l=3;
285                 ret->angle_start=road_angle(&c[0], &c[1], 0);
286                 ret->angle_end=road_angle(&c[l-1], &c[l], 0);
287                 dbg(1,"i=%d start %d end %d '%s' '%s'\n", i, ret->angle_start, ret->angle_end, ret->name1, ret->name2);
288                 map_rect_destroy(mr);
289         }
290         if (! this_->first)
291                 this_->first=ret;
292         if (this_->last) {
293                 this_->last->next=ret;
294                 ret->prev=this_->last;
295         }
296         dbg(1,"ret=%p\n", ret);
297         this_->last=ret;
298         return ret;
299 }
300
301 static void
302 calculate_dest_distance(struct navigation *this_, int incr)
303 {
304         int len=0, time=0;
305         struct navigation_itm *next,*itm=this_->last;
306         dbg(1, "enter this_=%p, incr=%d\n", this_, incr);
307         if (incr) {
308                 if (itm)
309                         dbg(2, "old values: (%p) time=%d lenght=%d\n", itm, itm->dest_length, itm->dest_time);
310                 else
311                         dbg(2, "old values: itm is null\n");
312                 itm=this_->first;
313                 next=itm->next;
314                 dbg(2, "itm values: time=%d lenght=%d\n", itm->length, itm->time);
315                 dbg(2, "next values: (%p) time=%d lenght=%d\n", next, next->dest_length, next->dest_time);
316                 itm->dest_length=next->dest_length+itm->length;
317                 itm->dest_time=next->dest_time+itm->time;
318                 dbg(2, "new values: time=%d lenght=%d\n", itm->dest_length, itm->dest_time);
319                 return;
320         }
321         while (itm) {
322                 len+=itm->length;
323                 time+=itm->time;
324                 itm->dest_length=len;
325                 itm->dest_time=time;
326                 itm=itm->prev;
327         }
328         dbg(1,"len %d time %d\n", len, time);
329 }
330
331 static int
332 is_same_street2(struct navigation_itm *old, struct navigation_itm *new)
333 {
334         if (old->name1 && new->name1 && !strcmp(old->name1, new->name1)) {
335                 dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' yes (1.)\n", old->name2, new->name2, old->name1, new->name1);
336                 return 1;
337         }
338         if (old->name2 && new->name2 && !strcmp(old->name2, new->name2)) {
339                 dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' yes (2.)\n", old->name2, new->name2, old->name1, new->name1);
340                 return 1;
341         }
342         dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' no\n", old->name2, new->name2, old->name1, new->name1);
343         return 0;
344 }
345
346 static int
347 maneuver_required2(struct navigation_itm *old, struct navigation_itm *new, int *delta)
348 {
349         dbg(1,"enter %p %p %p\n",old, new, delta);
350         if (new->item.type == old->item.type || (new->item.type != type_ramp && old->item.type != type_ramp)) {
351                 if (is_same_street2(old, new)) {
352                         dbg(1, "maneuver_required: is_same_street: no\n");
353                         return 0;
354                 }
355         } else
356                 dbg(1, "maneuver_required: old or new is ramp\n");
357 #if 0
358         if (old->crossings_end == 2) {
359                 dbg(1, "maneuver_required: only 2 connections: no\n");
360                 return 0;
361         }
362 #endif
363         *delta=new->angle_start-old->angle_end;
364         if (*delta < -180)
365                 *delta+=360;
366         if (*delta > 180)
367                 *delta-=360;
368         dbg(1,"delta=%d-%d=%d\n", new->angle_start, old->angle_end, *delta);
369         if (*delta < 20 && *delta >-20) {
370                 dbg(1, "maneuver_required: delta(%d) < 20: no\n", *delta);
371                 return 0;
372         }
373         dbg(1, "maneuver_required: delta=%d: yes\n", *delta);
374         return 1;
375 }
376
377 static struct navigation_command *
378 command_new(struct navigation *this_, struct navigation_itm *itm, int delta)
379 {
380         struct navigation_command *ret=g_new0(struct navigation_command, 1);
381         dbg(1,"enter this_=%p itm=%p delta=%d\n", this_, itm, delta);
382         ret->delta=delta;
383         ret->itm=itm;
384         if (this_->cmd_last)
385                 this_->cmd_last->next=ret;
386         this_->cmd_last=ret;
387
388         if (!this_->cmd_first)
389                 this_->cmd_first=ret;
390         return ret;
391 }
392
393 static void
394 make_maneuvers(struct navigation *this_)
395 {
396         struct navigation_itm *itm, *last=NULL, *last_itm=NULL;
397         int delta;
398         itm=this_->first;
399         this_->cmd_last=NULL;
400         this_->cmd_first=NULL;
401         while (itm) {
402                 if (last) {
403                         if (maneuver_required2(last_itm, itm, &delta)) {
404                                 command_new(this_, itm, delta);
405                         }
406                 } else
407                         last=itm;
408                 last_itm=itm;
409                 itm=itm->next;
410         }
411 }
412
413 static int
414 contains_suffix(char *name, char *suffix)
415 {
416         if (!suffix)
417                 return 0;
418         if (strlen(name) < strlen(suffix))
419                 return 0;
420         return !strcasecmp(name+strlen(name)-strlen(suffix), suffix);
421 }
422
423 static char *
424 replace_suffix(char *name, char *search, char *replace)
425 {
426         int len=strlen(name)-strlen(search);
427         char *ret=g_malloc(len+strlen(replace)+1);
428         strncpy(ret, name, len);
429         strcpy(ret+len, replace);
430
431         return ret;
432 }
433
434 static char *
435 navigation_item_destination(struct navigation_itm *itm, struct navigation_itm *next, char *prefix)
436 {
437         char *ret=NULL,*name1,*sep,*name2;
438         int i,sex;
439         if (! prefix)
440                 prefix="";
441         if(!itm->name1 && !itm->name2 && itm->item.type == type_ramp) {
442                 dbg(1,">> Next is ramp %lx current is %lx \n", itm->item.type, next->item.type);
443                          
444                 if(next->item.type == type_ramp)
445                         return NULL;
446                 if(itm->item.type == type_highway_city || itm->item.type == type_highway_land )
447                         return g_strdup_printf("%s%s",prefix,_("exit"));                                 
448                 else
449                         return g_strdup_printf("%s%s",prefix,_("ramp"));
450                 
451         }
452         if (!itm->name1 && !itm->name2)
453                 return NULL;
454         if (itm->name1) {
455                 sex=-1;
456                 name1=NULL;
457                 for (i = 0 ; i < sizeof(suffixes)/sizeof(suffixes[0]) ; i++) {
458                         if (contains_suffix(itm->name1,suffixes[i].fullname)) {
459                                 sex=suffixes[i].sex;
460                                 name1=g_strdup(itm->name1);
461                                 break;
462                         }
463                         if (contains_suffix(itm->name1,suffixes[i].abbrev)) {
464                                 sex=suffixes[i].sex;
465                                 name1=replace_suffix(itm->name1, suffixes[i].abbrev, suffixes[i].fullname);
466                                 break;
467                         }
468                 }
469                 if (itm->name2) {
470                         name2=itm->name2;
471                         sep=" ";
472                 } else {
473                         name2="";
474                         sep="";
475                 }
476                 switch (sex) {
477                 case -1:
478                         /* TRANSLATORS: Arguments: 1: Prefix (Space if required) 2: Street Name 3: Separator (Space if required), 4: Systematic Street Name */
479                         ret=g_strdup_printf(_("%sinto the street %s%s%s"),prefix,itm->name1, sep, name2);
480                         break;
481                 case 1:
482                         /* TRANSLATORS: Arguments: 1: Prefix (Space if required) 2: Street Name 3: Separator (Space if required), 4: Systematic Street Name. Male form. The stuff after | doesn't have to be included */
483                         ret=g_strdup_printf(_("%sinto the %s%s%s|male form"),prefix,name1, sep, name2);
484                         break;
485                 case 2:
486                         /* TRANSLATORS: Arguments: 1: Prefix (Space if required) 2: Street Name 3: Separator (Space if required), 4: Systematic Street Name. Female form. The stuff after | doesn't have to be included */
487                         ret=g_strdup_printf(_("%sinto the %s%s%s|female form"),prefix,name1, sep, name2);
488                         break;
489                 case 3:
490                         /* TRANSLATORS: Arguments: 1: Prefix (Space if required) 2: Street Name 3: Separator (Space if required), 4: Systematic Street Name. Neutral form. The stuff after | doesn't have to be included */
491                         ret=g_strdup_printf(_("%sinto the %s%s%s|neutral form"),prefix,name1, sep, name2);
492                         break;
493                 }
494                 g_free(name1);
495                         
496         } else
497                 ret=g_strdup_printf(_("into the %s"),itm->name2);
498         name1=ret;
499         while (*name1) {
500                 switch (*name1) {
501                 case '|':
502                         *name1='\0';
503                         break;
504                 case '/':
505                         *name1++=' ';
506                         break;
507                 default:
508                         name1++;
509                 }
510         }
511         return ret;
512 }
513
514
515 static char *
516 show_maneuver(struct navigation *nav, struct navigation_itm *itm, struct navigation_command *cmd, enum attr_type type)
517 {
518         char *dir=_("right"),*strength="";
519         int distance=itm->dest_length-cmd->itm->dest_length;
520         char *d,*ret;
521         int delta=cmd->delta;
522         int level;
523         level=1;
524         if (delta < 0) {
525                 dir=_("left");
526                 delta=-delta;
527         }
528         if (delta < 45) {
529                 strength=_("easily ");
530         } else if (delta < 105) {
531                 strength="";
532         } else if (delta < 165) {
533                 strength=_("strongly ");
534         } else {
535                 dbg(1,"delta=%d\n", delta);
536                 strength=_("unknown ");
537         }
538         if (type != attr_navigation_long_exact) 
539                 distance=round_distance(distance);
540         if (type == attr_navigation_speech) {
541                 if (nav->turn_around && nav->turn_around == nav->turn_around_limit) 
542                         return g_strdup(_("When possible, please turn around"));
543                 level=navigation_get_announce_level(nav, itm->item.type, distance);
544                 dbg(1,"distance=%d level=%d type=0x%x\n", distance, level, itm->item.type);
545         }
546         switch(level) {
547         case 3:
548                 d=get_distance(distance, type, 1);
549                 ret=g_strdup_printf(_("Follow the road for the next %s"), d);
550                 g_free(d);
551                 return ret;
552         case 2:
553                 d=g_strdup(_("soon"));
554                 break;
555         case 1:
556                 d=get_distance(distance, type, 0);
557                 break;
558         case 0:
559                 d=g_strdup(_("now"));
560                 break;
561         default:
562                 d=g_strdup(_("error"));
563         }
564         if (cmd->itm->next) {
565                 int tellstreetname = 0;
566                 char *destination = NULL; 
567  
568                 if(type == attr_navigation_speech) { // In voice mode
569                         // In Voice Mode only tell the street name in level 1 or in level 0 if level 1
570                         // was skipped
571
572                         if (level == 1) { // we are close to the intersection
573                                 cmd->itm->told = 1; // remeber to be checked when we turn
574                                 tellstreetname = 1; // Ok so we tell the name of the street 
575                         }
576
577                         if (level == 0) {
578                                 if(cmd->itm->told == 0) // we are write at the intersection
579                                         tellstreetname = 1; 
580                                 else
581                                         cmd->itm->told = 0;  // reset just in case we come to the same street again
582                         }
583
584                 }
585                 else
586                      tellstreetname = 1;
587
588                 if(tellstreetname) 
589                         destination=navigation_item_destination(cmd->itm, itm, " ");
590                 /* TRANSLATORS: The first argument is strength, the second direction, the third distance and the fourth destination Example: 'Turn 'slightly' 'left' in '100 m' 'onto baker street' */
591                 ret=g_strdup_printf(_("Turn %1$s%2$s %3$s%4$s"), strength, dir, d, destination ? destination:"");
592                 g_free(destination);
593         } else
594                 ret=g_strdup_printf(_("You have reached your destination %s"), d);
595         g_free(d);
596         return ret;
597 }
598
599 static void
600 navigation_call_callbacks(struct navigation *this_, int force_speech)
601 {
602         int distance, level = 0;
603         void *p=this_;
604         if (!this_->cmd_first)
605                 return;
606         callback_list_call(this_->callback, 1, &p);
607         dbg(1,"force_speech=%d turn_around=%d turn_around_limit=%d\n", force_speech, this_->turn_around, this_->turn_around_limit);
608         distance=round_distance(this_->first->dest_length-this_->cmd_first->itm->dest_length);
609         if (this_->turn_around_limit && this_->turn_around == this_->turn_around_limit) {
610                 dbg(1,"distance=%d distance_turn=%d\n", distance, this_->distance_turn);
611                 while (distance > this_->distance_turn) {
612                         this_->level_last=4;
613                         level=4;
614                         force_speech=1;
615                         if (this_->distance_turn >= 500)
616                                 this_->distance_turn*=2;
617                         else
618                                 this_->distance_turn=500;
619                 }
620         } else if (!this_->turn_around_limit || this_->turn_around == -this_->turn_around_limit+1) {
621                 this_->distance_turn=50;
622                 level=navigation_get_announce_level(this_, this_->first->item.type, distance);
623                 if (level < this_->level_last) {
624                         dbg(1,"level %d < %d\n", level, this_->level_last);
625                         this_->level_last=level;
626                         force_speech=1;
627                 }
628                 if (!item_is_equal(this_->cmd_first->itm->item, this_->item_last)) {
629                         dbg(1,"item different\n");
630                         this_->item_last=this_->cmd_first->itm->item;
631                         force_speech=1;
632                 }
633         }
634         if (force_speech) {
635                 this_->level_last=level;
636                 dbg(1,"distance=%d level=%d type=0x%x\n", distance, level, this_->first->item.type);
637                 callback_list_call(this_->callback_speech, 1, &p);
638         }
639 }
640
641 void
642 navigation_update(struct navigation *this_, struct route *route)
643 {
644         struct map *map;
645         struct map_rect *mr;
646         struct item *ritem,*sitem;
647         struct attr street_item;
648         struct navigation_itm *itm;
649         int incr=0;
650
651         if (! route)
652                 return;
653         map=route_get_map(route);
654         if (! map)
655                 return;
656         mr=map_rect_new(map, NULL);
657         if (! mr)
658                 return;
659         dbg(1,"enter\n");
660         ritem=map_rect_get_item(mr);
661         if (ritem) {
662                 if (item_attr_get(ritem, attr_street_item, &street_item)) {
663                         sitem=street_item.u.item;
664                         dbg(1,"sitem=%p\n", sitem);
665                         itm=item_hash_lookup(this_->hash, sitem);
666                         dbg(2,"itm for item with id (0x%x,0x%x) is %p\n", sitem->id_hi, sitem->id_lo, itm);
667                         navigation_destroy_itms_cmds(this_, itm);
668                         if (itm) {
669                                 incr=1;
670                                 navigation_itm_update(itm, ritem);
671                         } else {
672                                 dbg(1,"not on track\n");
673                                 do {
674                                         dbg(1,"item\n");
675                                         navigation_itm_new(this_, ritem);
676                                         ritem=map_rect_get_item(mr);
677                                 } while (ritem);
678                                 itm=navigation_itm_new(this_, NULL);
679                                 make_maneuvers(this_);
680                         }
681                 } else
682                         dbg(0,"no street_item\n");
683                 calculate_dest_distance(this_, incr);
684                 dbg(2,"destination distance old=%d new=%d\n", this_->distance_last, this_->first->dest_length);
685                 if (this_->first->dest_length > this_->distance_last && this_->distance_last >= 0) 
686                         this_->turn_around++;
687                 else
688                         this_->turn_around--;
689                 if (this_->turn_around > this_->turn_around_limit)
690                         this_->turn_around=this_->turn_around_limit;
691                 else if (this_->turn_around < -this_->turn_around_limit+1)
692                         this_->turn_around=-this_->turn_around_limit+1;
693                 dbg(2,"turn_around=%d\n", this_->turn_around);
694                 this_->distance_last=this_->first->dest_length;
695                 profile(0,"end");
696                 navigation_call_callbacks(this_, FALSE);
697         } else
698                 navigation_destroy_itms_cmds(this_, NULL);
699         map_rect_destroy(mr);
700         
701 #if 0
702         struct route_path_handle *rph;
703         struct route_path_segment *s;
704         struct navigation_itm *itm;
705         struct route_info *pos,*dst;
706         struct street_data *sd;
707         int *speedlist;
708         int len,end_flag=0;
709         int incr;
710
711         profile(0,NULL);
712         pos=route_get_pos(route);
713         dst=route_get_dst(route);
714         if (! pos || ! dst)
715                 return;
716         speedlist=route_get_speedlist(route);
717         len=route_info_length(pos, dst, 0);
718         dbg(2,"len pos,dst = %d\n", len);
719         if (len == -1) {
720                 len=route_info_length(pos, NULL, 0);
721                 dbg(2,"len pos = %d\n", len);
722                 end_flag=1;
723         }
724         sd=route_info_street(pos);
725         itm=item_hash_lookup(this_->hash, &sd->item);
726         dbg(2,"itm for item with id (0x%x,0x%x) is %p\n", sd->item.id_hi, sd->item.id_lo, itm);
727         navigation_destroy_itms_cmds(this_, itm);
728         if (itm) 
729                 incr=1;
730         else {
731                 itm=navigation_itm_new(this_, &sd->item, route_info_point(pos, -1));
732                 incr=0;
733         }
734         itm->length=len;
735         itm->time=route_time(speedlist, &sd->item, len);
736         dbg(2,"%p time = %d\n", itm, itm->time);
737         if (!incr) {
738                 printf("not on track\n");
739                 rph=route_path_open(route);
740                 if (rph) {
741                         while((s=route_path_get_segment(rph))) {
742                                 itm=navigation_itm_new(this_, route_path_segment_get_item(s),route_path_segment_get_start(s));
743                                 itm->time=route_path_segment_get_time(s);
744                                 itm->length=route_path_segment_get_length(s);
745                         }
746                         route_path_close(rph);
747                 }
748                 if (end_flag) {
749                         len=route_info_length(NULL, dst, 0);
750                         dbg(1, "end %d\n", len);
751                         sd=route_info_street(dst);
752                         itm=navigation_itm_new(this_, &sd->item, route_info_point(pos, 2));
753                         itm->length=len;
754                         itm->time=route_time(speedlist, &sd->item, len);
755                 }
756                 itm=navigation_itm_new(this_, NULL, NULL);
757                 make_maneuvers(this_);
758         }
759         calculate_dest_distance(this_, incr);
760         dbg(2,"destination distance old=%d new=%d\n", this_->distance_last, this_->first->dest_length);
761         if (this_->first->dest_length > this_->distance_last && this_->distance_last >= 0) 
762                 this_->turn_around=1;
763         else
764                 this_->turn_around=0;
765         dbg(2,"turn_around=%d\n", this_->turn_around);
766         this_->distance_last=this_->first->dest_length;
767         profile(0,"end");
768         navigation_call_callbacks(this_, FALSE);
769 #endif
770 }
771
772 void
773 navigation_flush(struct navigation *this_)
774 {
775         navigation_destroy_itms_cmds(this_, NULL);
776 }
777
778
779 void
780 navigation_destroy(struct navigation *this_)
781 {
782         navigation_flush(this_);
783         item_hash_destroy(this_->hash);
784         callback_list_destroy(this_->callback);
785         callback_list_destroy(this_->callback_speech);
786         g_free(this_);
787 }
788
789 int
790 navigation_register_callback(struct navigation *this_, enum attr_type type, struct callback *cb)
791 {
792         if (type == attr_navigation_speech)
793                 callback_list_add(this_->callback_speech, cb);
794         else
795                 callback_list_add(this_->callback, cb);
796         return 1;
797 }
798
799 void
800 navigation_unregister_callback(struct navigation *this_, enum attr_type type, struct callback *cb)
801 {
802         if (type == attr_navigation_speech)
803                 callback_list_remove_destroy(this_->callback_speech, cb);
804         else
805                 callback_list_remove_destroy(this_->callback, cb);
806 }
807
808 struct map *
809 navigation_get_map(struct navigation *this_)
810 {
811         struct attr navigation_attr;
812         struct attr data_attr;
813         struct attr *attrs_navigation[]={&navigation_attr, &data_attr, NULL};
814         navigation_attr.type=attr_navigation;
815         navigation_attr.u.navigation=this_;
816         data_attr.type=attr_data;
817         data_attr.u.str="";
818
819         if (! this_->map)
820                 this_->map=map_new("navigation",attrs_navigation);
821         return this_->map;
822 }
823
824 struct map_priv {
825         struct navigation *navigation;
826 };
827
828 struct map_rect_priv {
829         struct navigation *nav;
830         struct navigation_command *cmd;
831         struct navigation_command *cmd_next;
832         struct navigation_itm *itm;
833         struct navigation_itm *itm_next;
834         struct item item;
835 };
836
837 static int
838 navigation_map_item_coord_get(void *priv_data, struct coord *c, int count)
839 {
840         return 0;
841 }
842
843 static int
844 navigation_map_item_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr)
845 {
846         struct map_rect_priv *this_=priv_data;
847         attr->type=attr_type;
848         switch(attr_type) {
849         case attr_navigation_short:
850         case attr_navigation_long:
851         case attr_navigation_long_exact:
852         case attr_navigation_speech:
853                 attr->u.str=show_maneuver(this_->nav, this_->itm, this_->cmd, attr_type);
854                 return 1;
855         case attr_length:
856                 attr->u.num=this_->itm->dest_length-this_->cmd->itm->dest_length;
857                 return 1;
858         case attr_time:
859                 attr->u.num=this_->itm->dest_time-this_->cmd->itm->dest_time;
860                 return 1;
861         case attr_destination_length:
862                 attr->u.num=this_->itm->dest_length;
863                 return 1;
864         case attr_destination_time:
865                 attr->u.num=this_->itm->dest_time;
866                 return 1;
867         default:
868                 attr->type=attr_none;
869                 return 0;
870         }
871 }
872
873 static struct item_methods navigation_map_item_methods = {
874         NULL,
875         navigation_map_item_coord_get,
876         NULL,
877         navigation_map_item_attr_get,
878 };
879
880
881 static void
882 navigation_map_destroy(struct map_priv *priv)
883 {
884         g_free(priv);
885 }
886
887 static struct map_rect_priv *
888 navigation_map_rect_new(struct map_priv *priv, struct map_selection *sel)
889 {
890         struct navigation *nav=priv->navigation;
891         struct map_rect_priv *ret=g_new0(struct map_rect_priv, 1);
892         ret->nav=nav;
893         ret->cmd_next=nav->cmd_first;
894         ret->itm_next=nav->first;
895         ret->item.meth=&navigation_map_item_methods;
896         ret->item.priv_data=ret;
897         return ret;
898 }
899
900 static void
901 navigation_map_rect_destroy(struct map_rect_priv *priv)
902 {
903         g_free(priv);
904 }
905
906 static struct item *
907 navigation_map_get_item(struct map_rect_priv *priv)
908 {
909         struct item *ret;
910         int delta;
911         if (!priv->cmd_next)
912                 return NULL;
913         ret=&priv->item;        
914         priv->cmd=priv->cmd_next;
915         priv->itm=priv->itm_next;
916         priv->itm_next=priv->cmd->itm;
917         priv->cmd_next=priv->cmd->next;
918
919         delta=priv->cmd->delta; 
920         dbg(1,"delta=%d\n", delta);
921         if (delta < 0) {
922                 delta=-delta;
923                 if (delta < 45)
924                         ret->type=type_nav_left_1;
925                 else if (delta < 105)
926                         ret->type=type_nav_left_2;
927                 else if (delta < 165) 
928                         ret->type=type_nav_left_3;
929                 else
930                         ret->type=type_none;
931         } else {
932                 if (delta < 45)
933                         ret->type=type_nav_right_1;
934                 else if (delta < 105)
935                         ret->type=type_nav_right_2;
936                 else if (delta < 165) 
937                         ret->type=type_nav_right_3;
938                 else
939                         ret->type=type_none;
940         }
941         dbg(1,"type=%d\n", ret->type);
942         return ret;
943 }
944
945 static struct item *
946 navigation_map_get_item_byid(struct map_rect_priv *priv, int id_hi, int id_lo)
947 {
948         dbg(0,"stub");
949         return NULL;
950 }
951
952 static struct map_methods navigation_map_meth = {
953         projection_mg,
954         NULL,
955         navigation_map_destroy,
956         navigation_map_rect_new,
957         navigation_map_rect_destroy,
958         navigation_map_get_item,
959         navigation_map_get_item_byid,
960         NULL,
961         NULL,
962         NULL,
963 };
964
965 static struct map_priv *
966 navigation_map_new(struct map_methods *meth, struct attr **attrs)
967 {
968         struct map_priv *ret;
969         struct attr *navigation_attr;
970
971         navigation_attr=attr_search(attrs, NULL, attr_navigation);
972         if (! navigation_attr)
973                 return NULL;
974         ret=g_new0(struct map_priv, 1);
975         *meth=navigation_map_meth;
976         ret->navigation=navigation_attr->u.navigation;
977
978         return ret;
979 }
980
981
982 void
983 navigation_init(void)
984 {
985         plugin_register_map_type("navigation", navigation_map_new);
986 }