summaryrefslogtreecommitdiff
path: root/navigation.c
diff options
context:
space:
mode:
Diffstat (limited to 'navigation.c')
-rw-r--r--navigation.c2156
1 files changed, 2156 insertions, 0 deletions
diff --git a/navigation.c b/navigation.c
new file mode 100644
index 00000000..827e09d4
--- /dev/null
+++ b/navigation.c
@@ -0,0 +1,2156 @@
+/**
+ * Navit, a modular navigation system.
+ * Copyright (C) 2005-2008 Navit Team
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+#include <glib.h>
+#include "debug.h"
+#include "profile.h"
+#include "navigation.h"
+#include "coord.h"
+#include "item.h"
+#include "route.h"
+#include "transform.h"
+#include "mapset.h"
+#include "projection.h"
+#include "map.h"
+#include "navit.h"
+#include "callback.h"
+#include "plugin.h"
+#include "navit_nls.h"
+
+#define DEBUG
+
+struct suffix {
+ char *fullname;
+ char *abbrev;
+ int sex;
+} suffixes[]= {
+ {"weg",NULL,1},
+ {"platz","pl.",1},
+ {"ring",NULL,1},
+ {"allee",NULL,2},
+ {"gasse",NULL,2},
+ {"straße","str.",2},
+ {"strasse",NULL,2},
+};
+
+struct navigation {
+ struct route *route;
+ struct map *map;
+ struct item_hash *hash;
+ struct navigation_itm *first;
+ struct navigation_itm *last;
+ struct navigation_command *cmd_first;
+ struct navigation_command *cmd_last;
+ struct callback_list *callback_speech;
+ struct callback_list *callback;
+ struct navit *navit;
+ int level_last;
+ struct item item_last;
+ int turn_around;
+ int turn_around_limit;
+ int distance_turn;
+ struct callback *route_cb;
+ int announce[route_item_last-route_item_first+1][3];
+};
+
+
+struct navigation_command {
+ struct navigation_itm *itm;
+ struct navigation_command *next;
+ struct navigation_command *prev;
+ int delta;
+ int roundabout_delta;
+ int length;
+};
+
+static void navigation_flush(struct navigation *this_);
+
+/**
+ * @brief Calculates the delta between two angles
+ * @param angle1 The first angle
+ * @param angle2 The second angle
+ * @return The difference between the angles: -179..-1=angle2 is left of angle1,0=same,1..179=angle2 is right of angle1,180=angle1 is opposite of angle2
+ */
+
+static int
+angle_delta(int angle1, int angle2)
+{
+ int delta=angle2-angle1;
+ if (delta <= -180)
+ delta+=360;
+ if (delta > 180)
+ delta-=360;
+ return delta;
+}
+
+static int
+angle_median(int angle1, int angle2)
+{
+ int delta=angle_delta(angle1, angle2);
+ int ret=angle1+delta/2;
+ if (ret < 0)
+ ret+=360;
+ if (ret > 360)
+ ret-=360;
+ return ret;
+}
+
+static int
+angle_opposite(int angle)
+{
+ return ((angle+180)%360);
+}
+
+int
+navigation_get_attr(struct navigation *this_, enum attr_type type, struct attr *attr, struct attr_iter *iter)
+{
+ dbg(0,"enter %s\n", attr_to_name(type));
+ switch (type) {
+ case attr_map:
+ attr->u.map=this_->map;
+ break;
+ default:
+ return 0;
+ }
+ attr->type=type;
+ return 1;
+}
+
+
+struct navigation *
+navigation_new(struct attr *parent, struct attr **attrs)
+{
+ int i,j;
+ struct navigation *ret=g_new0(struct navigation, 1);
+ ret->hash=item_hash_new();
+ ret->callback=callback_list_new();
+ ret->callback_speech=callback_list_new();
+ ret->level_last=-2;
+ ret->distance_turn=50;
+ ret->turn_around_limit=3;
+ ret->navit=parent->u.navit;
+
+ for (j = 0 ; j <= route_item_last-route_item_first ; j++) {
+ for (i = 0 ; i < 3 ; i++) {
+ ret->announce[j][i]=-1;
+ }
+ }
+
+ return ret;
+}
+
+int
+navigation_set_announce(struct navigation *this_, enum item_type type, int *level)
+{
+ int i;
+ if (type < route_item_first || type > route_item_last) {
+ dbg(0,"street type %d out of range [%d,%d]", type, route_item_first, route_item_last);
+ return 0;
+ }
+ for (i = 0 ; i < 3 ; i++)
+ this_->announce[type-route_item_first][i]=level[i];
+ return 1;
+}
+
+static int
+navigation_get_announce_level(struct navigation *this_, enum item_type type, int dist)
+{
+ int i;
+
+ if (type < route_item_first || type > route_item_last)
+ return -1;
+ for (i = 0 ; i < 3 ; i++) {
+ if (dist <= this_->announce[type-route_item_first][i])
+ return i;
+ }
+ return i;
+}
+
+/**
+ * @brief Holds a way that one could possibly drive from a navigation item
+ */
+struct navigation_way {
+ struct navigation_way *next; /**< Pointer to a linked-list of all navigation_ways from this navigation item */
+ short dir; /**< The direction -1 or 1 of the way */
+ short angle2; /**< The angle one has to steer to drive from the old item to this street */
+ int flags; /**< The flags of the way */
+ struct item item; /**< The item of the way */
+ char *name1;
+ char *name2;
+};
+
+struct navigation_itm {
+ char *name1;
+ char *name2;
+ struct item item;
+ int direction;
+ int angle_start;
+ int angle_end;
+ struct coord start,end;
+ int time;
+ int length;
+ int dest_time;
+ int dest_length;
+ int told; /**< Indicates if this item's announcement has been told earlier and should not be told again*/
+ int streetname_told; /**< Indicates if this item's streetname has been told in speech navigation*/
+ int dest_count;
+ int flags;
+ struct navigation_itm *next;
+ struct navigation_itm *prev;
+ struct navigation_way *ways; /**< Pointer to all ways one could drive from here */
+};
+
+/* 0=N,90=E */
+static int
+road_angle(struct coord *c1, struct coord *c2, int dir)
+{
+ int ret=transform_get_angle_delta(c1, c2, dir);
+ dbg(1, "road_angle(0x%x,0x%x - 0x%x,0x%x)=%d\n", c1->x, c1->y, c2->x, c2->y, ret);
+ return ret;
+}
+
+static char
+*get_count_str(int n)
+{
+ switch (n) {
+ case 0:
+ return _("zeroth"); // Not shure if this exists, neither if it will ever be needed
+ case 1:
+ return _("first");
+ case 2:
+ return _("second");
+ case 3:
+ return _("third");
+ case 4:
+ return _("fourth");
+ case 5:
+ return _("fifth");
+ case 6:
+ return _("sixth");
+ default:
+ return NULL;
+ }
+}
+
+static char
+*get_exit_count_str(int n)
+{
+ switch (n) {
+ case 0:
+ return _("zeroth exit"); // Not shure if this exists, neither if it will ever be needed
+ case 1:
+ return _("first exit");
+ case 2:
+ return _("second exit");
+ case 3:
+ return _("third exit");
+ case 4:
+ return _("fourth exit");
+ case 5:
+ return _("fifth exit");
+ case 6:
+ return _("sixth exit");
+ default:
+ return NULL;
+ }
+}
+static int
+round_distance(int dist)
+{
+ if (dist < 100) {
+ dist=(dist+5)/10;
+ return dist*10;
+ }
+ if (dist < 250) {
+ dist=(dist+13)/25;
+ return dist*25;
+ }
+ if (dist < 500) {
+ dist=(dist+25)/50;
+ return dist*50;
+ }
+ if (dist < 1000) {
+ dist=(dist+50)/100;
+ return dist*100;
+ }
+ if (dist < 5000) {
+ dist=(dist+50)/100;
+ return dist*100;
+ }
+ if (dist < 100000) {
+ dist=(dist+500)/1000;
+ return dist*1000;
+ }
+ dist=(dist+5000)/10000;
+ return dist*10000;
+}
+
+static char *
+get_distance(int dist, enum attr_type type, int is_length)
+{
+ if (type == attr_navigation_long) {
+ if (is_length)
+ return g_strdup_printf(_("%d m"), dist);
+ else
+ return g_strdup_printf(_("in %d m"), dist);
+ }
+ if (dist < 1000) {
+ if (is_length)
+ return g_strdup_printf(_("%d meters"), dist);
+ else
+ return g_strdup_printf(_("in %d meters"), dist);
+ }
+ if (dist < 5000) {
+ int rem=(dist/100)%10;
+ if (rem) {
+ if (is_length)
+ return g_strdup_printf(_("%d.%d kilometer"), dist/1000, rem);
+ else
+ return g_strdup_printf(_("in %d.%d kilometers"), dist/1000, rem);
+ }
+ }
+ if (is_length)
+ return g_strdup_printf(ngettext("one kilometer","%d kilometers", dist/1000), dist/1000);
+ else
+ return g_strdup_printf(ngettext("in one kilometer","in %d kilometers", dist/1000), dist/1000);
+}
+
+
+/**
+ * @brief This calculates the angle with which an item starts or ends
+ *
+ * This function can be used to get the angle an item (from a route graph map)
+ * starts or ends with. Note that the angle will point towards the inner of
+ * the item.
+ *
+ * This is meant to be used with items from a route graph map
+ * With other items this will probably not be optimal...
+ *
+ * @param w The way which should be calculated
+ */
+static void
+calculate_angle(struct navigation_way *w)
+{
+ struct coord cbuf[2];
+ struct item *ritem; // the "real" item
+ struct coord c;
+ struct map_rect *mr;
+ struct attr attr;
+
+ w->angle2=361;
+ mr = map_rect_new(w->item.map, NULL);
+ if (!mr)
+ return;
+
+ ritem = map_rect_get_item_byid(mr, w->item.id_hi, w->item.id_lo);
+ if (!ritem) {
+ dbg(1,"Item from segment not found on map!\n");
+ map_rect_destroy(mr);
+ return;
+ }
+
+ if (ritem->type < type_line || ritem->type >= type_area) {
+ map_rect_destroy(mr);
+ return;
+ }
+ if (item_attr_get(ritem, attr_flags, &attr))
+ w->flags=attr.u.num;
+ else
+ w->flags=0;
+ if (item_attr_get(ritem, attr_street_name, &attr))
+ w->name1=map_convert_string(ritem->map,attr.u.str);
+ else
+ w->name1=NULL;
+ if (item_attr_get(ritem, attr_street_name_systematic, &attr))
+ w->name2=map_convert_string(ritem->map,attr.u.str);
+ else
+ w->name2=NULL;
+
+ if (w->dir < 0) {
+ if (item_coord_get(ritem, cbuf, 2) != 2) {
+ dbg(1,"Using calculate_angle() with a less-than-two-coords-item?\n");
+ map_rect_destroy(mr);
+ return;
+ }
+
+ while (item_coord_get(ritem, &c, 1)) {
+ cbuf[0] = cbuf[1];
+ cbuf[1] = c;
+ }
+
+ } else {
+ if (item_coord_get(ritem, cbuf, 2) != 2) {
+ dbg(1,"Using calculate_angle() with a less-than-two-coords-item?\n");
+ map_rect_destroy(mr);
+ return;
+ }
+ c = cbuf[0];
+ cbuf[0] = cbuf[1];
+ cbuf[1] = c;
+ }
+
+ map_rect_destroy(mr);
+
+ w->angle2=road_angle(&cbuf[1],&cbuf[0],0);
+}
+
+/**
+ * @brief Returns the time (in seconds) one will drive between two navigation items
+ *
+ * This function returns the time needed to drive between two items, including both of them,
+ * in seconds.
+ *
+ * @param from The first item
+ * @param to The last item
+ * @return The travel time in seconds, or -1 on error
+ */
+static int
+navigation_time(struct navigation_itm *from, struct navigation_itm *to)
+{
+ struct navigation_itm *cur;
+ int time;
+
+ time = 0;
+ cur = from;
+ while (cur) {
+ time += cur->time;
+
+ if (cur == to) {
+ break;
+ }
+ cur = cur->next;
+ }
+
+ if (!cur) {
+ return -1;
+ }
+
+ return time;
+}
+
+/**
+ * @brief Clears the ways one can drive from itm
+ *
+ * @param itm The item that should have its ways cleared
+ */
+static void
+navigation_itm_ways_clear(struct navigation_itm *itm)
+{
+ struct navigation_way *c,*n;
+
+ c = itm->ways;
+ while (c) {
+ n = c->next;
+ map_convert_free(c->name1);
+ map_convert_free(c->name2);
+ g_free(c);
+ c = n;
+ }
+
+ itm->ways = NULL;
+}
+
+/**
+ * @brief Updates the ways one can drive from itm
+ *
+ * This updates the list of possible ways to drive to from itm. The item "itm" is on
+ * and the next navigation item are excluded.
+ *
+ * @param itm The item that should be updated
+ * @param graph_map The route graph's map that these items are on
+ */
+static void
+navigation_itm_ways_update(struct navigation_itm *itm, struct map *graph_map)
+{
+ struct map_selection coord_sel;
+ struct map_rect *g_rect; // Contains a map rectangle from the route graph's map
+ struct item *i,*sitem;
+ struct attr sitem_attr,direction_attr;
+ struct navigation_way *w,*l;
+
+ navigation_itm_ways_clear(itm);
+
+ // These values cause the code in route.c to get us only the route graph point and connected segments
+ coord_sel.next = NULL;
+ coord_sel.u.c_rect.lu = itm->start;
+ coord_sel.u.c_rect.rl = itm->start;
+ // the selection's order is ignored
+
+ g_rect = map_rect_new(graph_map, &coord_sel);
+
+ i = map_rect_get_item(g_rect);
+ if (!i || i->type != type_rg_point) { // probably offroad?
+ return ;
+ }
+
+ w = NULL;
+
+ while (1) {
+ i = map_rect_get_item(g_rect);
+
+ if (!i) {
+ break;
+ }
+
+ if (i->type != type_rg_segment) {
+ continue;
+ }
+
+ if (!item_attr_get(i,attr_street_item,&sitem_attr)) {
+ dbg(1, "Got no street item for route graph item in entering_straight()\n");
+ continue;
+ }
+
+ if (!item_attr_get(i,attr_direction,&direction_attr)) {
+ continue;
+ }
+
+ sitem = sitem_attr.u.item;
+ if (item_is_equal(itm->item,*sitem) || ((itm->prev) && item_is_equal(itm->prev->item,*sitem))) {
+ continue;
+ }
+
+ l = w;
+ w = g_new(struct navigation_way, 1);
+ w->dir = direction_attr.u.num;
+ w->item = *sitem;
+ w->next = l;
+ calculate_angle(w);
+ }
+
+ map_rect_destroy(g_rect);
+
+ itm->ways = w;
+}
+
+static void
+navigation_destroy_itms_cmds(struct navigation *this_, struct navigation_itm *end)
+{
+ struct navigation_itm *itm;
+ struct navigation_command *cmd;
+ dbg(2,"enter this_=%p this_->first=%p this_->cmd_first=%p end=%p\n", this_, this_->first, this_->cmd_first, end);
+ if (this_->cmd_first)
+ dbg(2,"this_->cmd_first->itm=%p\n", this_->cmd_first->itm);
+ while (this_->first && this_->first != end) {
+ itm=this_->first;
+ dbg(3,"destroying %p\n", itm);
+ item_hash_remove(this_->hash, &itm->item);
+ this_->first=itm->next;
+ if (this_->first)
+ this_->first->prev=NULL;
+ if (this_->cmd_first && this_->cmd_first->itm == itm->next) {
+ cmd=this_->cmd_first;
+ this_->cmd_first=cmd->next;
+ if (cmd->next) {
+ cmd->next->prev = NULL;
+ }
+ g_free(cmd);
+ }
+ map_convert_free(itm->name1);
+ map_convert_free(itm->name2);
+ navigation_itm_ways_clear(itm);
+ g_free(itm);
+ }
+ if (! this_->first)
+ this_->last=NULL;
+ if (! this_->first && end)
+ dbg(0,"end wrong\n");
+ dbg(2,"ret this_->first=%p this_->cmd_first=%p\n",this_->first, this_->cmd_first);
+}
+
+static void
+navigation_itm_update(struct navigation_itm *itm, struct item *ritem)
+{
+ struct attr length, time;
+
+ if (! item_attr_get(ritem, attr_length, &length)) {
+ dbg(0,"no length\n");
+ return;
+ }
+ if (! item_attr_get(ritem, attr_time, &time)) {
+ dbg(0,"no time\n");
+ return;
+ }
+
+ dbg(1,"length=%d time=%d\n", length.u.num, time.u.num);
+ itm->length=length.u.num;
+ itm->time=time.u.num;
+}
+
+/**
+ * @brief This check if an item is part of a roundabout
+ *
+ * @param itm The item to be checked
+ * @return True if the item is part of a roundabout
+ */
+static int
+check_roundabout(struct navigation_itm *itm, struct map *graph_map)
+{
+ struct map_selection coord_sel;
+ struct map_rect *g_rect; // Contains a map rectangle from the route graph's map
+ struct item *i,*sitem;
+ struct attr sitem_attr,flags_attr;
+
+ // These values cause the code in route.c to get us only the route graph point and connected segments
+ coord_sel.next = NULL;
+ coord_sel.u.c_rect.lu = itm->start;
+ coord_sel.u.c_rect.rl = itm->start;
+ // the selection's order is ignored
+
+ g_rect = map_rect_new(graph_map, &coord_sel);
+
+ i = map_rect_get_item(g_rect);
+ if (!i || i->type != type_rg_point) { // probably offroad?
+ return 0;
+ }
+
+ while (1) {
+ i = map_rect_get_item(g_rect);
+
+ if (!i) {
+ break;
+ }
+
+ if (i->type != type_rg_segment) {
+ continue;
+ }
+
+ if (!item_attr_get(i,attr_street_item,&sitem_attr)) {
+ continue;
+ }
+
+ sitem = sitem_attr.u.item;
+ if (item_is_equal(itm->item,*sitem)) {
+ if (item_attr_get(i,attr_flags,&flags_attr) && (flags_attr.u.num & AF_ROUNDABOUT)) {
+ map_rect_destroy(g_rect);
+ return 1;
+ }
+ }
+ }
+
+ map_rect_destroy(g_rect);
+ return 0;
+}
+
+static struct navigation_itm *
+navigation_itm_new(struct navigation *this_, struct item *ritem)
+{
+ struct navigation_itm *ret=g_new0(struct navigation_itm, 1);
+ int i=0;
+ struct item *sitem;
+ struct map *graph_map = NULL;
+ struct attr street_item,direction,route_attr;
+ struct map_rect *mr;
+ struct attr attr;
+ struct coord c[5];
+
+ if (ritem) {
+ ret->streetname_told=0;
+ if (! item_attr_get(ritem, attr_street_item, &street_item)) {
+ dbg(1, "no street item\n");
+ return NULL;
+ }
+ if (item_attr_get(ritem, attr_direction, &direction))
+ ret->direction=direction.u.num;
+ else
+ ret->direction=0;
+
+ sitem=street_item.u.item;
+ ret->item=*sitem;
+ item_hash_insert(this_->hash, sitem, ret);
+ mr=map_rect_new(sitem->map, NULL);
+ sitem=map_rect_get_item_byid(mr, sitem->id_hi, sitem->id_lo);
+ if (item_attr_get(sitem, attr_street_name, &attr))
+ ret->name1=map_convert_string(sitem->map,attr.u.str);
+ if (item_attr_get(sitem, attr_street_name_systematic, &attr))
+ ret->name2=map_convert_string(sitem->map,attr.u.str);
+ navigation_itm_update(ret, ritem);
+
+ while (item_coord_get(ritem, &c[i], 1)) {
+ dbg(1, "coord %d 0x%x 0x%x\n", i, c[i].x ,c[i].y);
+
+ if (i < 4)
+ i++;
+ else {
+ c[2]=c[3];
+ c[3]=c[4];
+ }
+ }
+ dbg(1,"count=%d\n", i);
+ i--;
+
+ ret->angle_start=road_angle(&c[0], &c[1], 0);
+ ret->angle_end=road_angle(&c[i-1], &c[i], 0);
+
+ ret->start=c[0];
+ ret->end=c[i];
+
+ item_attr_get(ritem, attr_route, &route_attr);
+ graph_map = route_get_graph_map(route_attr.u.route);
+ if (check_roundabout(ret, graph_map)) {
+ ret->flags |= AF_ROUNDABOUT;
+ }
+
+ dbg(1,"i=%d start %d end %d '%s' '%s'\n", i, ret->angle_start, ret->angle_end, ret->name1, ret->name2);
+ map_rect_destroy(mr);
+ } else {
+ if (this_->last)
+ ret->start=ret->end=this_->last->end;
+ }
+ if (! this_->first)
+ this_->first=ret;
+ if (this_->last) {
+ this_->last->next=ret;
+ ret->prev=this_->last;
+ if (graph_map) {
+ navigation_itm_ways_update(ret,graph_map);
+ }
+ }
+ dbg(1,"ret=%p\n", ret);
+ this_->last=ret;
+ return ret;
+}
+
+/**
+ * @brief Counts how many times a driver could turn right/left
+ *
+ * This function counts how many times the driver theoretically could
+ * turn right/left between two navigation items, not counting the final
+ * turn itself.
+ *
+ * @param from The navigation item which should form the start
+ * @param to The navigation item which should form the end
+ * @param direction Set to < 0 to count turns to the left >= 0 for turns to the right
+ * @return The number of possibilities to turn or -1 on error
+ */
+static int
+count_possible_turns(struct navigation_itm *from, struct navigation_itm *to, int direction)
+{
+ int count;
+ struct navigation_itm *curr;
+ struct navigation_way *w;
+
+ count = 0;
+ curr = from->next;
+ while (curr && (curr != to)) {
+ w = curr->ways;
+
+ while (w) {
+ if (direction < 0) {
+ if (angle_delta(curr->prev->angle_end, w->angle2) < 0) {
+ count++;
+ break;
+ }
+ } else {
+ if (angle_delta(curr->prev->angle_end, w->angle2) > 0) {
+ count++;
+ break;
+ }
+ }
+ w = w->next;
+ }
+ curr = curr->next;
+ }
+
+ if (!curr) { // from does not lead to to?
+ return -1;
+ }
+
+ return count;
+}
+
+/**
+ * @brief Calculates distance and time to the destination
+ *
+ * This function calculates the distance and the time to the destination of a
+ * navigation. If incr is set, this is only calculated for the first navigation
+ * item, which is a lot faster than re-calculation the whole destination, but works
+ * only if the rest of the navigation already has been calculated.
+ *
+ * @param this_ The navigation whose destination / time should be calculated
+ * @param incr Set this to true to only calculate the first item. See description.
+ */
+static void
+calculate_dest_distance(struct navigation *this_, int incr)
+{
+ int len=0, time=0, count=0;
+ struct navigation_itm *next,*itm=this_->last;
+ dbg(1, "enter this_=%p, incr=%d\n", this_, incr);
+ if (incr) {
+ if (itm)
+ dbg(2, "old values: (%p) time=%d lenght=%d\n", itm, itm->dest_length, itm->dest_time);
+ else
+ dbg(2, "old values: itm is null\n");
+ itm=this_->first;
+ next=itm->next;
+ dbg(2, "itm values: time=%d lenght=%d\n", itm->length, itm->time);
+ dbg(2, "next values: (%p) time=%d lenght=%d\n", next, next->dest_length, next->dest_time);
+ itm->dest_length=next->dest_length+itm->length;
+ itm->dest_count=next->dest_count+1;
+ itm->dest_time=next->dest_time+itm->time;
+ dbg(2, "new values: time=%d lenght=%d\n", itm->dest_length, itm->dest_time);
+ return;
+ }
+ while (itm) {
+ len+=itm->length;
+ time+=itm->time;
+ itm->dest_length=len;
+ itm->dest_time=time;
+ itm->dest_count=count++;
+ itm=itm->prev;
+ }
+ dbg(1,"len %d time %d\n", len, time);
+}
+
+/**
+ * @brief Checks if two navigation items are on the same street
+ *
+ * This function checks if two navigation items are on the same street. It returns
+ * true if either their name or their "systematic name" (e.g. "A6" or "B256") are the
+ * same.
+ *
+ * @param old The first item to be checked
+ * @param new The second item to be checked
+ * @return True if both old and new are on the same street
+ */
+static int
+is_same_street2(char *old_name1, char *old_name2, char *new_name1, char *new_name2)
+{
+ if (old_name1 && new_name1 && !strcmp(old_name1, new_name1)) {
+ dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' yes (1.)\n", old_name2, new_name2, old_name1, new_name1);
+ return 1;
+ }
+ if (old_name2 && new_name2 && !strcmp(old_name2, new_name2)) {
+ dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' yes (2.)\n", old_name2, new_name2, old_name1, new_name1);
+ return 1;
+ }
+ dbg(1,"is_same_street: '%s' '%s' vs '%s' '%s' no\n", old_name2, new_name2, old_name1, new_name1);
+ return 0;
+}
+
+#if 0
+/**
+ * @brief Checks if two navigation items are on the same street
+ *
+ * This function checks if two navigation items are on the same street. It returns
+ * true if the first part of their "systematic name" is equal. If the "systematic name" is
+ * for example "A352/E3" (a german highway which at the same time is part of the international
+ * E-road network), it would only search for "A352" in the second item's systematic name.
+ *
+ * @param old The first item to be checked
+ * @param new The second item to be checked
+ * @return True if the "systematic name" of both items matches. See description.
+ */
+static int
+is_same_street_systematic(struct navigation_itm *old, struct navigation_itm *new)
+{
+ int slashold,slashnew;
+ if (!old->name2 || !new->name2)
+ return 1;
+ slashold=strcspn(old->name2, "/");
+ slashnew=strcspn(new->name2, "/");
+ if (slashold != slashnew || strncmp(old->name2, new->name2, slashold))
+ return 0;
+ return 1;
+}
+
+
+/**
+ * @brief Check if there are multiple possibilities to drive from old
+ *
+ * This function checks, if there are multiple streets connected to the exit of "old".
+ * Sometimes it happens that an item on a map is just segmented, without any other streets
+ * being connected there, and it is not useful if navit creates a maneuver there.
+ *
+ * @param new The navigation item we're driving to
+ * @return True if there are multiple streets
+ */
+static int
+maneuver_multiple_streets(struct navigation_itm *new)
+{
+ if (new->ways) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+
+/**
+ * @brief Check if the new item is entered "straight"
+ *
+ * This function checks if the new item is entered "straight" from the old item, i.e. if there
+ * is no other street one could take from the old item on with less steering.
+ *
+ * @param new The navigation item we're driving to
+ * @param diff The absolute angle one needs to steer to drive to this item
+ * @return True if the new item is entered "straight"
+ */
+static int
+maneuver_straight(struct navigation_itm *new, int diff)
+{
+ int curr_diff;
+ struct navigation_way *w;
+
+ w = new->ways;
+ dbg(1,"diff=%d\n", diff);
+ while (w) {
+ curr_diff=abs(angle_delta(new->prev->angle_end, w->angle2));
+ dbg(1,"curr_diff=%d\n", curr_diff);
+ if (curr_diff < diff) {
+ return 0;
+ }
+ w = w->next;
+ }
+ return 1;
+}
+#endif
+
+static int maneuver_category(enum item_type type)
+{
+ switch (type) {
+ case type_street_0:
+ return 1;
+ case type_street_1_city:
+ return 2;
+ case type_street_2_city:
+ return 3;
+ case type_street_3_city:
+ return 4;
+ case type_street_4_city:
+ return 5;
+ case type_highway_city:
+ return 7;
+ case type_street_1_land:
+ return 2;
+ case type_street_2_land:
+ return 3;
+ case type_street_3_land:
+ return 4;
+ case type_street_4_land:
+ return 5;
+ case type_street_n_lanes:
+ return 6;
+ case type_highway_land:
+ return 7;
+ case type_ramp:
+ return 0;
+ case type_roundabout:
+ return 0;
+ case type_ferry:
+ return 0;
+ default:
+ return 0;
+ }
+
+
+}
+
+static int
+is_way_allowed(struct navigation_way *way)
+{
+ if (way->dir > 0) {
+ if (way->flags & AF_ONEWAYREV)
+ return 0;
+ } else {
+ if (way->flags & AF_ONEWAY)
+ return 0;
+ }
+ return 1;
+}
+
+/**
+ * @brief Checks if navit has to create a maneuver to drive from old to new
+ *
+ * This function checks if it has to create a "maneuver" - i.e. guide the user - to drive
+ * from "old" to "new".
+ *
+ * @param old The old navigation item, where we're coming from
+ * @param new The new navigation item, where we're going to
+ * @param delta The angle the user has to steer to navigate from old to new
+ * @param reason A text string explaining how the return value resulted
+ * @return True if navit should guide the user, false otherwise
+ */
+static int
+maneuver_required2(struct navigation_itm *old, struct navigation_itm *new, int *delta, char **reason)
+{
+ int ret=0,d,dw,dlim;
+ char *r=NULL;
+ struct navigation_way *w;
+ int cat,ncat,wcat,maxcat,left=-180,right=180,is_unambigous=0,is_same_street;
+
+ dbg(1,"enter %p %p %p\n",old, new, delta);
+ d=angle_delta(old->angle_end, new->angle_start);
+ if (!new->ways) {
+ /* No announcement necessary */
+ r="no: Only one possibility";
+ } else if (!new->ways->next && new->ways->item.type == type_ramp && !is_way_allowed(new->ways)) {
+ /* If the other way is only a ramp and it is one-way in the wrong direction, no announcement necessary */
+ r="no: Only ramp";
+ }
+ if (! r) {
+ if ((old->flags & AF_ROUNDABOUT) && ! (new->flags & AF_ROUNDABOUT)) {
+ r="yes: leaving roundabout";
+ ret=1;
+ } else if (!(old->flags & AF_ROUNDABOUT) && (new->flags & AF_ROUNDABOUT))
+ r="no: entering roundabout";
+ else if ((old->flags & AF_ROUNDABOUT) && (new->flags & AF_ROUNDABOUT))
+ r="no: staying in roundabout";
+ }
+ if (!r && abs(d) > 75) {
+ /* always make an announcement if you have to make a sharp turn */
+ r="yes: delta over 75";
+ ret=1;
+ }
+ cat=maneuver_category(old->item.type);
+ ncat=maneuver_category(new->item.type);
+ if (!r) {
+ /* Check whether the street keeps its name */
+ is_same_street=is_same_street2(old->name1, old->name2, new->name1, new->name2);
+ w = new->ways;
+ maxcat=-1;
+ while (w) {
+ dw=angle_delta(old->angle_end, w->angle2);
+ if (dw < 0) {
+ if (dw > left)
+ left=dw;
+ } else {
+ if (dw < right)
+ right=dw;
+ }
+ wcat=maneuver_category(w->item.type);
+ /* If any other street has the same name but isn't a highway (a highway might split up temporarily), then
+ we can't use the same name criterium */
+ if (is_same_street && is_same_street2(old->name1, old->name2, w->name1, w->name2) && (cat != 7 || wcat != 7) && is_way_allowed(w))
+ is_same_street=0;
+ /* Mark if the street has a higher or the same category */
+ if (wcat > maxcat)
+ maxcat=wcat;
+ w = w->next;
+ }
+ /* get the delta limit for checking for other streets. It is lower if the street has no other
+ streets of the same or higher category */
+ if (ncat < cat)
+ dlim=80;
+ else
+ dlim=120;
+ /* if the street is really straight, the others might be closer to straight */
+ if (abs(d) < 20)
+ dlim/=2;
+ if ((maxcat == ncat && maxcat == cat) || (ncat == 0 && cat == 0))
+ dlim=abs(d)*620/256;
+ else if (maxcat < ncat && maxcat < cat)
+ dlim=abs(d)*128/256;
+ if (left < -dlim && right > dlim)
+ is_unambigous=1;
+ if (!is_same_street && is_unambigous < 1) {
+ ret=1;
+ r="yes: not same street or ambigous";
+ } else
+ r="no: same street and unambigous";
+#ifdef DEBUG
+ r=g_strdup_printf("yes: d %d left %d right %d dlim=%d cat old:%d new:%d max:%d unambigous=%d same_street=%d", d, left, right, dlim, cat, ncat, maxcat, is_unambigous, is_same_street);
+#endif
+ }
+ *delta=d;
+ if (reason)
+ *reason=r;
+ return ret;
+
+
+#if 0
+ if (new->item.type == old->item.type || (new->item.type != type_ramp && old->item.type != type_ramp)) {
+ if (is_same_street2(old, new)) {
+ if (! entering_straight(new, abs(*delta))) {
+ dbg(1, "maneuver_required: Not driving straight: yes\n");
+ if (reason)
+ *reason="yes: Not driving straight";
+ return 1;
+ }
+
+ if (check_multiple_streets(new)) {
+ if (entering_straight(new,abs(*delta)*2)) {
+ if (reason)
+ *reason="no: delta < ext_limit for same name";
+ return 0;
+ }
+ if (reason)
+ *reason="yes: delta > ext_limit for same name";
+ return 1;
+ } else {
+ dbg(1, "maneuver_required: Staying on the same street: no\n");
+ if (reason)
+ *reason="no: Staying on same street";
+ return 0;
+ }
+ }
+ } else
+ dbg(1, "maneuver_required: old or new is ramp\n");
+#if 0
+ if (old->item.type == type_ramp && (new->item.type == type_highway_city || new->item.type == type_highway_land)) {
+ dbg(1, "no_maneuver_required: old is ramp new is highway\n");
+ if (reason)
+ *reason="no: old is ramp new is highway";
+ return 0;
+ }
+#endif
+#if 0
+ if (old->crossings_end == 2) {
+ dbg(1, "maneuver_required: only 2 connections: no\n");
+ return 0;
+ }
+#endif
+ dbg(1,"delta=%d-%d=%d\n", new->angle_start, old->angle_end, *delta);
+ if ((new->item.type == type_highway_land || new->item.type == type_highway_city || old->item.type == type_highway_land || old->item.type == type_highway_city) && (!is_same_street_systematic(old, new) || (old->name2 != NULL && new->name2 == NULL))) {
+ dbg(1, "maneuver_required: highway changed name\n");
+ if (reason)
+ *reason="yes: highway changed name";
+ return 1;
+ }
+ if (abs(*delta) < straight_limit) {
+ if (! entering_straight(new,abs(*delta))) {
+ if (reason)
+ *reason="yes: not straight";
+ dbg(1, "maneuver_required: not driving straight: yes\n");
+ return 1;
+ }
+
+ dbg(1, "maneuver_required: delta(%d) < %d: no\n", *delta, straight_limit);
+ if (reason)
+ *reason="no: delta < limit";
+ return 0;
+ }
+ if (abs(*delta) < ext_straight_limit) {
+ if (entering_straight(new,abs(*delta)*2)) {
+ if (reason)
+ *reason="no: delta < ext_limit";
+ return 0;
+ }
+ }
+
+ if (! check_multiple_streets(new)) {
+ dbg(1, "maneuver_required: only one possibility: no\n");
+ if (reason)
+ *reason="no: only one possibility";
+ return 0;
+ }
+
+ dbg(1, "maneuver_required: delta=%d: yes\n", *delta);
+ if (reason)
+ *reason="yes: delta >= limit";
+ return 1;
+#endif
+}
+
+static struct navigation_command *
+command_new(struct navigation *this_, struct navigation_itm *itm, int delta)
+{
+ struct navigation_command *ret=g_new0(struct navigation_command, 1);
+ dbg(1,"enter this_=%p itm=%p delta=%d\n", this_, itm, delta);
+ ret->delta=delta;
+ ret->itm=itm;
+ if (itm && itm->prev && itm->ways && itm->prev->ways && !(itm->flags & AF_ROUNDABOUT) && (itm->prev->flags & AF_ROUNDABOUT)) {
+ int len=0;
+ int angle=0;
+ int entry_angle;
+ struct navigation_itm *itm2=itm->prev;
+ int exit_angle=angle_median(itm->prev->angle_end, itm->ways->angle2);
+ dbg(1,"exit %d median from %d,%d\n", exit_angle,itm->prev->angle_end, itm->ways->angle2);
+ while (itm2 && (itm2->flags & AF_ROUNDABOUT)) {
+ len+=itm2->length;
+ angle=itm2->angle_end;
+ itm2=itm2->prev;
+ }
+ if (itm2 && itm2->next && itm2->next->ways) {
+ itm2=itm2->next;
+ entry_angle=angle_median(angle_opposite(itm2->angle_start), itm2->ways->angle2);
+ dbg(1,"entry %d median from %d(%d),%d\n", entry_angle,angle_opposite(itm2->angle_start), itm2->angle_start, itm2->ways->angle2);
+ } else {
+ entry_angle=angle_opposite(angle);
+ }
+ dbg(0,"entry %d exit %d\n", entry_angle, exit_angle);
+ ret->roundabout_delta=angle_delta(entry_angle, exit_angle);
+ ret->length=len;
+
+ }
+ if (this_->cmd_last) {
+ this_->cmd_last->next=ret;
+ ret->prev = this_->cmd_last;
+ }
+ this_->cmd_last=ret;
+
+ if (!this_->cmd_first)
+ this_->cmd_first=ret;
+ return ret;
+}
+
+static void
+make_maneuvers(struct navigation *this_, struct route *route)
+{
+ struct navigation_itm *itm, *last=NULL, *last_itm=NULL;
+ int delta;
+ itm=this_->first;
+ this_->cmd_last=NULL;
+ this_->cmd_first=NULL;
+ while (itm) {
+ if (last) {
+ if (maneuver_required2(last_itm, itm,&delta,NULL)) {
+ command_new(this_, itm, delta);
+ }
+ } else
+ last=itm;
+ last_itm=itm;
+ itm=itm->next;
+ }
+ command_new(this_, last_itm, 0);
+}
+
+static int
+contains_suffix(char *name, char *suffix)
+{
+ if (!suffix)
+ return 0;
+ if (strlen(name) < strlen(suffix))
+ return 0;
+ return !strcasecmp(name+strlen(name)-strlen(suffix), suffix);
+}
+
+static char *
+replace_suffix(char *name, char *search, char *replace)
+{
+ int len=strlen(name)-strlen(search);
+ char *ret=g_malloc(len+strlen(replace)+1);
+ strncpy(ret, name, len);
+ strcpy(ret+len, replace);
+ if (isupper(name[len])) {
+ ret[len]=toupper(ret[len]);
+ }
+
+ return ret;
+}
+
+static char *
+navigation_item_destination(struct navigation_itm *itm, struct navigation_itm *next, char *prefix)
+{
+ char *ret=NULL,*name1,*sep,*name2;
+ int i,sex;
+ if (! prefix)
+ prefix="";
+ if(!itm->name1 && !itm->name2 && itm->item.type == type_ramp) {
+ dbg(1,">> Next is ramp %lx current is %lx \n", itm->item.type, next->item.type);
+
+ if(next->item.type == type_ramp)
+ return NULL;
+ if(itm->item.type == type_highway_city || itm->item.type == type_highway_land )
+ return g_strdup_printf("%s%s",prefix,_("exit")); /* %FIXME Can this even be reached? */
+ else
+ return g_strdup_printf("%s%s",prefix,_("into the ramp"));
+
+ }
+ if (!itm->name1 && !itm->name2)
+ return NULL;
+ if (itm->name1) {
+ sex=-1;
+ name1=NULL;
+ for (i = 0 ; i < sizeof(suffixes)/sizeof(suffixes[0]) ; i++) {
+ if (contains_suffix(itm->name1,suffixes[i].fullname)) {
+ sex=suffixes[i].sex;
+ name1=g_strdup(itm->name1);
+ break;
+ }
+ if (contains_suffix(itm->name1,suffixes[i].abbrev)) {
+ sex=suffixes[i].sex;
+ name1=replace_suffix(itm->name1, suffixes[i].abbrev, suffixes[i].fullname);
+ break;
+ }
+ }
+ if (itm->name2) {
+ name2=itm->name2;
+ sep=" ";
+ } else {
+ name2="";
+ sep="";
+ }
+ switch (sex) {
+ case -1:
+ /* TRANSLATORS: Arguments: 1: Prefix (Space if required) 2: Street Name 3: Separator (Space if required), 4: Systematic Street Name */
+ ret=g_strdup_printf(_("%sinto the street %s%s%s"),prefix,itm->name1, sep, name2);
+ break;
+ case 1:
+ /* 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 */
+ ret=g_strdup_printf(_("%sinto the %s%s%s|male form"),prefix,name1, sep, name2);
+ break;
+ case 2:
+ /* 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 */
+ ret=g_strdup_printf(_("%sinto the %s%s%s|female form"),prefix,name1, sep, name2);
+ break;
+ case 3:
+ /* 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 */
+ ret=g_strdup_printf(_("%sinto the %s%s%s|neutral form"),prefix,name1, sep, name2);
+ break;
+ }
+ g_free(name1);
+
+ } else
+ /* TRANSLATORS: gives the name of the next road to turn into (into the E17) */
+ ret=g_strdup_printf(_("%sinto the %s"),prefix,itm->name2);
+ name1=ret;
+ while (name1 && *name1) {
+ switch (*name1) {
+ case '|':
+ *name1='\0';
+ break;
+ case '/':
+ *name1++=' ';
+ break;
+ default:
+ name1++;
+ }
+ }
+ return ret;
+}
+
+static char *
+show_maneuver(struct navigation *nav, struct navigation_itm *itm, struct navigation_command *cmd, enum attr_type type, int connect)
+{
+ /* TRANSLATORS: right, as in 'Turn right' */
+ char *dir=_("right"),*strength="";
+ int distance=itm->dest_length-cmd->itm->dest_length;
+ char *d,*ret=NULL;
+ int delta=cmd->delta;
+ int level;
+ int strength_needed;
+ int skip_roads;
+ int count_roundabout;
+ struct navigation_itm *cur;
+ struct navigation_way *w;
+
+ if (connect) {
+ level = -2; // level = -2 means "connect to another maneuver via 'then ...'"
+ } else {
+ level=1;
+ }
+
+ w = itm->next->ways;
+ strength_needed = 0;
+ if (angle_delta(itm->next->angle_start,itm->angle_end) < 0) {
+ while (w) {
+ if (angle_delta(w->angle2,itm->angle_end) < 0) {
+ strength_needed = 1;
+ break;
+ }
+ w = w->next;
+ }
+ } else {
+ while (w) {
+ if (angle_delta(w->angle2,itm->angle_end) > 0) {
+ strength_needed = 1;
+ break;
+ }
+ w = w->next;
+ }
+ }
+
+ if (delta < 0) {
+ /* TRANSLATORS: left, as in 'Turn left' */
+ dir=_("left");
+ delta=-delta;
+ }
+
+ if (strength_needed) {
+ if (delta < 45) {
+ /* TRANSLATORS: Don't forget the ending space */
+ strength=_("easily ");
+ } else if (delta < 105) {
+ strength="";
+ } else if (delta < 165) {
+ /* TRANSLATORS: Don't forget the ending space */
+ strength=_("strongly ");
+ } else if (delta < 180) {
+ /* TRANSLATORS: Don't forget the ending space */
+ strength=_("really strongly ");
+ } else {
+ dbg(1,"delta=%d\n", delta);
+ /* TRANSLATORS: Don't forget the ending space */
+ strength=_("unknown ");
+ }
+ }
+ if (type != attr_navigation_long_exact)
+ distance=round_distance(distance);
+ if (type == attr_navigation_speech) {
+ if (nav->turn_around && nav->turn_around == nav->turn_around_limit)
+ return g_strdup(_("When possible, please turn around"));
+ if (!connect) {
+ level=navigation_get_announce_level(nav, itm->item.type, distance-cmd->length);
+ }
+ dbg(1,"distance=%d level=%d type=0x%x\n", distance, level, itm->item.type);
+ }
+
+ if (cmd->itm->prev->flags & AF_ROUNDABOUT) {
+ switch (level) {
+ case 2:
+ return g_strdup(_("Enter the roundabout soon"));
+ case 1:
+ d = get_distance(distance, type, 1);
+ /* TRANSLATORS: %s is the distance to the roundabout */
+ ret = g_strdup_printf(_("In %s, enter the roundabout"), d);
+ g_free(d);
+ return ret;
+ case -2:
+ case 0:
+ cur = cmd->itm->prev;
+ count_roundabout = 0;
+ while (cur && (cur->flags & AF_ROUNDABOUT)) {
+ if (cur->next->ways && is_way_allowed(cur->next->ways)) { // If the next segment has no exit or the exit isn't allowed, don't count it
+ count_roundabout++;
+ }
+ cur = cur->prev;
+ }
+ switch (level) {
+ case 0:
+ ret = g_strdup_printf(_("Leave the roundabout at the %s"), get_exit_count_str(count_roundabout));
+ break;
+ case -2:
+ ret = g_strdup_printf(_("then leave the roundabout at the %s"), get_exit_count_str(count_roundabout));
+ break;
+ }
+ return ret;
+ }
+ }
+
+ switch(level) {
+ case 3:
+ d=get_distance(distance, type, 1);
+ ret=g_strdup_printf(_("Follow the road for the next %s"), d);
+ g_free(d);
+ return ret;
+ case 2:
+ d=g_strdup(_("soon"));
+ break;
+ case 1:
+ d=get_distance(distance, type, 0);
+ break;
+ case 0:
+ skip_roads = count_possible_turns(nav->first,cmd->itm,cmd->delta);
+ if (skip_roads > 0) {
+ if (get_count_str(skip_roads+1)) {
+ /* TRANSLATORS: First argument is the how manieth street to take, second the direction */
+ ret = g_strdup_printf(_("Take the %1$s road to the %2$s"), get_count_str(skip_roads+1), dir);
+ return ret;
+ } else {
+ d = g_strdup_printf(_("after %i roads"), skip_roads);
+ }
+ } else {
+ d=g_strdup(_("now"));
+ }
+ break;
+ case -2:
+ skip_roads = count_possible_turns(cmd->prev->itm,cmd->itm,cmd->delta);
+ if (skip_roads > 0) {
+ /* TRANSLATORS: First argument is the how manieth street to take, second the direction */
+ if (get_count_str(skip_roads+1)) {
+ ret = g_strdup_printf(_("then take the %1$s road to the %2$s"), get_count_str(skip_roads+1), dir);
+ return ret;
+ } else {
+ d = g_strdup_printf(_("after %i roads"), skip_roads);
+ }
+
+ } else {
+ d = g_strdup("");
+ }
+ break;
+ default:
+ d=g_strdup(_("error"));
+ }
+ if (cmd->itm->next) {
+ int tellstreetname = 0;
+ char *destination = NULL;
+
+ if(type == attr_navigation_speech) { // In voice mode
+ // In Voice Mode only tell the street name in level 1 or in level 0 if level 1
+ // was skipped
+
+ if (level == 1) { // we are close to the intersection
+ cmd->itm->streetname_told = 1; // remeber to be checked when we turn
+ tellstreetname = 1; // Ok so we tell the name of the street
+ }
+
+ if (level == 0) {
+ if(cmd->itm->streetname_told == 0) // we are right at the intersection
+ tellstreetname = 1;
+ else
+ cmd->itm->streetname_told = 0; // reset just in case we come to the same street again
+ }
+
+ }
+ else
+ tellstreetname = 1;
+
+ if(tellstreetname)
+ destination=navigation_item_destination(cmd->itm, itm, " ");
+ if (level != -2) {
+ /* 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' */
+ ret=g_strdup_printf(_("Turn %1$s%2$s %3$s%4$s"), strength, dir, d, destination ? destination:"");
+ } else {
+ /* TRANSLATORS: First argument is strength, second direction, third how many roads to skip, fourth destination */
+ ret=g_strdup_printf(_("then turn %1$s%2$s %3$s%4$s"), strength, dir, d, destination ? destination:"");
+ }
+ g_free(destination);
+ } else {
+ if (!connect) {
+ ret=g_strdup_printf(_("You have reached your destination %s"), d);
+ } else {
+ ret=g_strdup_printf(_("then you have reached your destination."));
+ }
+ }
+ g_free(d);
+ return ret;
+}
+
+/**
+ * @brief Creates announcements for maneuvers, plus maneuvers immediately following the next maneuver
+ *
+ * This function does create an announcement for the current maneuver and for maneuvers
+ * immediately following that maneuver, if these are too close and we're in speech navigation.
+ *
+ * @return An announcement that should be made
+ */
+static char *
+show_next_maneuvers(struct navigation *nav, struct navigation_itm *itm, struct navigation_command *cmd, enum attr_type type)
+{
+ struct navigation_command *cur,*prev;
+ int distance=itm->dest_length-cmd->itm->dest_length;
+ int level, dist, i, time;
+ int speech_time,time2nav;
+ char *ret,*old,*buf,*next;
+
+ if (type != attr_navigation_speech) {
+ return show_maneuver(nav, itm, cmd, type, 0); // We accumulate maneuvers only in speech navigation
+ }
+
+ level=navigation_get_announce_level(nav, itm->item.type, distance-cmd->length);
+
+ if (level > 1) {
+ return show_maneuver(nav, itm, cmd, type, 0); // We accumulate maneuvers only if they are close
+ }
+
+ if (cmd->itm->told) {
+ return g_strdup("");
+ }
+
+ ret = show_maneuver(nav, itm, cmd, type, 0);
+ time2nav = navigation_time(itm,cmd->itm->prev);
+ old = NULL;
+
+ cur = cmd->next;
+ prev = cmd;
+ i = 0;
+ while (cur && cur->itm) {
+ // We don't merge more than 3 announcements...
+ if (i > 1) { // if you change this, please also change the value below, that is used to terminate the loop
+ break;
+ }
+
+ next = show_maneuver(nav,prev->itm, cur, type, 0);
+ speech_time = navit_speech_estimate(nav->navit,next);
+ g_free(next);
+
+ if (speech_time == -1) { // user didn't set cps
+ speech_time = 30; // assume 3 seconds
+ }
+
+ dist = prev->itm->dest_length - cur->itm->dest_length;
+ time = navigation_time(prev->itm,cur->itm->prev);
+
+ if (time >= (speech_time + 30)) { // 3 seconds for understanding what has been said
+ break;
+ }
+
+ old = ret;
+ buf = show_maneuver(nav, prev->itm, cur, type, 1);
+ ret = g_strdup_printf("%s, %s", old, buf);
+ g_free(buf);
+ if (navit_speech_estimate(nav->navit,ret) > time2nav) {
+ g_free(ret);
+ ret = old;
+ i = 2; // This will terminate the loop
+ } else {
+ g_free(old);
+ }
+
+ // If the two maneuvers are *really* close, we shouldn't tell the second one again, because TTS won't be fast enough
+ if (time <= speech_time) {
+ cur->itm->told = 1;
+ }
+
+ prev = cur;
+ cur = cur->next;
+ i++;
+ }
+
+ return ret;
+}
+
+static void
+navigation_call_callbacks(struct navigation *this_, int force_speech)
+{
+ int distance, level = 0, level2;
+ void *p=this_;
+ if (!this_->cmd_first)
+ return;
+ callback_list_call(this_->callback, 1, &p);
+ dbg(1,"force_speech=%d turn_around=%d turn_around_limit=%d\n", force_speech, this_->turn_around, this_->turn_around_limit);
+ distance=round_distance(this_->first->dest_length-this_->cmd_first->itm->dest_length);
+ if (this_->turn_around_limit && this_->turn_around == this_->turn_around_limit) {
+ dbg(1,"distance=%d distance_turn=%d\n", distance, this_->distance_turn);
+ while (distance > this_->distance_turn) {
+ this_->level_last=4;
+ level=4;
+ force_speech=1;
+ if (this_->distance_turn >= 500)
+ this_->distance_turn*=2;
+ else
+ this_->distance_turn=500;
+ }
+ } else if (!this_->turn_around_limit || this_->turn_around == -this_->turn_around_limit+1) {
+ this_->distance_turn=50;
+ distance-=this_->cmd_first->length;
+ level=navigation_get_announce_level(this_, this_->first->item.type, distance);
+ if (this_->cmd_first->itm->prev) {
+ level2=navigation_get_announce_level(this_, this_->cmd_first->itm->prev->item.type, distance);
+ if (level2 > level)
+ level=level2;
+ }
+ if (level < this_->level_last) {
+ dbg(1,"level %d < %d\n", level, this_->level_last);
+ this_->level_last=level;
+ force_speech=1;
+ }
+ if (!item_is_equal(this_->cmd_first->itm->item, this_->item_last)) {
+ dbg(1,"item different\n");
+ this_->item_last=this_->cmd_first->itm->item;
+ force_speech=1;
+ }
+ }
+ if (force_speech) {
+ this_->level_last=level;
+ dbg(1,"distance=%d level=%d type=0x%x\n", distance, level, this_->first->item.type);
+ callback_list_call(this_->callback_speech, 1, &p);
+ }
+}
+
+static void
+navigation_update(struct navigation *this_, struct route *route, struct attr *attr)
+{
+ struct map *map;
+ struct map_rect *mr;
+ struct item *ritem; /* Holds an item from the route map */
+ struct item *sitem; /* Holds the corresponding item from the actual map */
+ struct attr street_item,street_direction;
+ struct navigation_itm *itm;
+ int mode=0, incr=0, first=1;
+ if (attr->type != attr_route_status)
+ return;
+
+ dbg(1,"enter %d\n", mode);
+ if (attr->u.num == route_status_no_destination || attr->u.num == route_status_not_found || attr->u.num == route_status_path_done_new)
+ navigation_flush(this_);
+ if (attr->u.num != route_status_path_done_new && attr->u.num != route_status_path_done_incremental)
+ return;
+
+ if (! this_->route)
+ return;
+ map=route_get_map(this_->route);
+ if (! map)
+ return;
+ mr=map_rect_new(map, NULL);
+ if (! mr)
+ return;
+ dbg(1,"enter\n");
+ while ((ritem=map_rect_get_item(mr))) {
+ if (ritem->type == type_route_start && this_->turn_around > -this_->turn_around_limit+1)
+ this_->turn_around--;
+ if (ritem->type == type_route_start_reverse && this_->turn_around < this_->turn_around_limit)
+ this_->turn_around++;
+ if (ritem->type != type_street_route)
+ continue;
+ if (first && item_attr_get(ritem, attr_street_item, &street_item)) {
+ first=0;
+ if (!item_attr_get(ritem, attr_direction, &street_direction))
+ street_direction.u.num=0;
+ sitem=street_item.u.item;
+ dbg(1,"sitem=%p\n", sitem);
+ itm=item_hash_lookup(this_->hash, sitem);
+ dbg(2,"itm for item with id (0x%x,0x%x) is %p\n", sitem->id_hi, sitem->id_lo, itm);
+ if (itm && itm->direction != street_direction.u.num) {
+ dbg(2,"wrong direction\n");
+ itm=NULL;
+ }
+ navigation_destroy_itms_cmds(this_, itm);
+ if (itm) {
+ navigation_itm_update(itm, ritem);
+ break;
+ }
+ dbg(1,"not on track\n");
+ }
+ navigation_itm_new(this_, ritem);
+ }
+ dbg(2,"turn_around=%d\n", this_->turn_around);
+ if (first)
+ navigation_destroy_itms_cmds(this_, NULL);
+ else {
+ if (! ritem) {
+ navigation_itm_new(this_, NULL);
+ make_maneuvers(this_,this_->route);
+ }
+ calculate_dest_distance(this_, incr);
+ profile(0,"end");
+ navigation_call_callbacks(this_, FALSE);
+ }
+ map_rect_destroy(mr);
+}
+
+static void
+navigation_flush(struct navigation *this_)
+{
+ navigation_destroy_itms_cmds(this_, NULL);
+}
+
+
+void
+navigation_destroy(struct navigation *this_)
+{
+ navigation_flush(this_);
+ item_hash_destroy(this_->hash);
+ callback_list_destroy(this_->callback);
+ callback_list_destroy(this_->callback_speech);
+ g_free(this_);
+}
+
+int
+navigation_register_callback(struct navigation *this_, enum attr_type type, struct callback *cb)
+{
+ if (type == attr_navigation_speech)
+ callback_list_add(this_->callback_speech, cb);
+ else
+ callback_list_add(this_->callback, cb);
+ return 1;
+}
+
+void
+navigation_unregister_callback(struct navigation *this_, enum attr_type type, struct callback *cb)
+{
+ if (type == attr_navigation_speech)
+ callback_list_remove_destroy(this_->callback_speech, cb);
+ else
+ callback_list_remove_destroy(this_->callback, cb);
+}
+
+struct map *
+navigation_get_map(struct navigation *this_)
+{
+ if (! this_->map)
+ this_->map=map_new(NULL, (struct attr*[]){
+ &(struct attr){attr_type,{"navigation"}},
+ &(struct attr){attr_navigation,.u.navigation=this_},
+ &(struct attr){attr_data,{""}},
+ &(struct attr){attr_description,{"Navigation"}},
+ NULL});
+ return this_->map;
+}
+
+struct map_priv {
+ struct navigation *navigation;
+};
+
+struct map_rect_priv {
+ struct navigation *nav;
+ struct navigation_command *cmd;
+ struct navigation_command *cmd_next;
+ struct navigation_itm *itm;
+ struct navigation_itm *itm_next;
+ struct navigation_itm *cmd_itm;
+ struct navigation_itm *cmd_itm_next;
+ struct item item;
+ enum attr_type attr_next;
+ int ccount;
+ int debug_idx;
+ struct navigation_way *ways;
+ int show_all;
+ char *str;
+};
+
+static int
+navigation_map_item_coord_get(void *priv_data, struct coord *c, int count)
+{
+ struct map_rect_priv *this=priv_data;
+ if (this->ccount || ! count)
+ return 0;
+ *c=this->itm->start;
+ this->ccount=1;
+ return 1;
+}
+
+static int
+navigation_map_item_attr_get(void *priv_data, enum attr_type attr_type, struct attr *attr)
+{
+ struct map_rect_priv *this_=priv_data;
+ attr->type=attr_type;
+ struct navigation_command *cmd=this_->cmd;
+ struct navigation_itm *itm=this_->itm;
+ struct navigation_itm *prev=itm->prev;
+
+ if (this_->str) {
+ g_free(this_->str);
+ this_->str=NULL;
+ }
+
+ if (cmd) {
+ if (cmd->itm != itm)
+ cmd=NULL;
+ }
+ switch(attr_type) {
+ case attr_navigation_short:
+ this_->attr_next=attr_navigation_long;
+ if (cmd) {
+ this_->str=attr->u.str=show_next_maneuvers(this_->nav, this_->cmd_itm, cmd, attr_type);
+ return 1;
+ }
+ return 0;
+ case attr_navigation_long:
+ this_->attr_next=attr_navigation_long_exact;
+ if (cmd) {
+ this_->str=attr->u.str=show_next_maneuvers(this_->nav, this_->cmd_itm, cmd, attr_type);
+ return 1;
+ }
+ return 0;
+ case attr_navigation_long_exact:
+ this_->attr_next=attr_navigation_speech;
+ if (cmd) {
+ this_->str=attr->u.str=show_next_maneuvers(this_->nav, this_->cmd_itm, cmd, attr_type);
+ return 1;
+ }
+ return 0;
+ case attr_navigation_speech:
+ this_->attr_next=attr_length;
+ if (cmd) {
+ this_->str=attr->u.str=show_next_maneuvers(this_->nav, this_->cmd_itm, this_->cmd, attr_type);
+ return 1;
+ }
+ return 0;
+ case attr_length:
+ this_->attr_next=attr_time;
+ if (cmd) {
+ attr->u.num=this_->cmd_itm->dest_length-cmd->itm->dest_length;
+ return 1;
+ }
+ return 0;
+ case attr_time:
+ this_->attr_next=attr_destination_length;
+ if (cmd) {
+ attr->u.num=this_->cmd_itm->dest_time-cmd->itm->dest_time;
+ return 1;
+ }
+ return 0;
+ case attr_destination_length:
+ attr->u.num=itm->dest_length;
+ this_->attr_next=attr_destination_time;
+ return 1;
+ case attr_destination_time:
+ attr->u.num=itm->dest_time;
+ this_->attr_next=attr_street_name;
+ return 1;
+ case attr_street_name:
+ attr->u.str=itm->name1;
+ this_->attr_next=attr_street_name_systematic;
+ if (attr->u.str)
+ return 1;
+ return 0;
+ case attr_street_name_systematic:
+ attr->u.str=itm->name2;
+ this_->attr_next=attr_debug;
+ if (attr->u.str)
+ return 1;
+ return 0;
+ case attr_debug:
+ switch(this_->debug_idx) {
+ case 0:
+ this_->debug_idx++;
+ this_->str=attr->u.str=g_strdup_printf("angle:%d (- %d)", itm->angle_start, itm->angle_end);
+ return 1;
+ case 1:
+ this_->debug_idx++;
+ this_->str=attr->u.str=g_strdup_printf("item type:%s", item_to_name(itm->item.type));
+ return 1;
+ case 2:
+ this_->debug_idx++;
+ if (cmd) {
+ this_->str=attr->u.str=g_strdup_printf("delta:%d", cmd->delta);
+ return 1;
+ }
+ case 3:
+ this_->debug_idx++;
+ if (prev) {
+ this_->str=attr->u.str=g_strdup_printf("prev street_name:%s", prev->name1);
+ return 1;
+ }
+ case 4:
+ this_->debug_idx++;
+ if (prev) {
+ this_->str=attr->u.str=g_strdup_printf("prev street_name_systematic:%s", prev->name2);
+ return 1;
+ }
+ case 5:
+ this_->debug_idx++;
+ if (prev) {
+ this_->str=attr->u.str=g_strdup_printf("prev angle:(%d -) %d", prev->angle_start, prev->angle_end);
+ return 1;
+ }
+ case 6:
+ this_->debug_idx++;
+ this_->ways=itm->ways;
+ if (prev) {
+ this_->str=attr->u.str=g_strdup_printf("prev item type:%s", item_to_name(prev->item.type));
+ return 1;
+ }
+ case 7:
+ if (this_->ways && prev) {
+ this_->str=attr->u.str=g_strdup_printf("other item angle:%d delta:%d flags:%d dir:%d type:%s id:(0x%x,0x%x)", this_->ways->angle2, angle_delta(prev->angle_end, this_->ways->angle2), this_->ways->flags, this_->ways->dir, item_to_name(this_->ways->item.type), this_->ways->item.id_hi, this_->ways->item.id_lo);
+ this_->ways=this_->ways->next;
+ return 1;
+ }
+ this_->debug_idx++;
+ case 8:
+ this_->debug_idx++;
+ if (prev) {
+ int delta=0;
+ char *reason=NULL;
+ maneuver_required2(prev, itm, &delta, &reason);
+ this_->str=attr->u.str=g_strdup_printf("reason:%s",reason);
+ return 1;
+ }
+
+ default:
+ this_->attr_next=attr_none;
+ return 0;
+ }
+ case attr_any:
+ while (this_->attr_next != attr_none) {
+ if (navigation_map_item_attr_get(priv_data, this_->attr_next, attr))
+ return 1;
+ }
+ return 0;
+ default:
+ attr->type=attr_none;
+ return 0;
+ }
+}
+
+static struct item_methods navigation_map_item_methods = {
+ NULL,
+ navigation_map_item_coord_get,
+ NULL,
+ navigation_map_item_attr_get,
+};
+
+
+static void
+navigation_map_destroy(struct map_priv *priv)
+{
+ g_free(priv);
+}
+
+static void
+navigation_map_rect_init(struct map_rect_priv *priv)
+{
+ priv->cmd_next=priv->nav->cmd_first;
+ priv->cmd_itm_next=priv->itm_next=priv->nav->first;
+}
+
+static struct map_rect_priv *
+navigation_map_rect_new(struct map_priv *priv, struct map_selection *sel)
+{
+ struct navigation *nav=priv->navigation;
+ struct map_rect_priv *ret=g_new0(struct map_rect_priv, 1);
+ ret->nav=nav;
+ navigation_map_rect_init(ret);
+ ret->item.meth=&navigation_map_item_methods;
+ ret->item.priv_data=ret;
+#ifdef DEBUG
+ ret->show_all=1;
+#endif
+ return ret;
+}
+
+static void
+navigation_map_rect_destroy(struct map_rect_priv *priv)
+{
+ g_free(priv);
+}
+
+static struct item *
+navigation_map_get_item(struct map_rect_priv *priv)
+{
+ struct item *ret=&priv->item;
+ int delta;
+ if (!priv->itm_next)
+ return NULL;
+ priv->itm=priv->itm_next;
+ priv->cmd=priv->cmd_next;
+ priv->cmd_itm=priv->cmd_itm_next;
+ if (!priv->cmd)
+ return NULL;
+ if (!priv->show_all && priv->itm->prev != NULL)
+ priv->itm=priv->cmd->itm;
+ priv->itm_next=priv->itm->next;
+ if (priv->itm->prev)
+ ret->type=type_nav_none;
+ else
+ ret->type=type_nav_position;
+ if (priv->cmd->itm == priv->itm) {
+ priv->cmd_itm_next=priv->cmd->itm;
+ priv->cmd_next=priv->cmd->next;
+ if (priv->cmd_itm_next && !priv->cmd_itm_next->next)
+ ret->type=type_nav_destination;
+ else {
+ if (priv->itm && priv->itm->prev && !(priv->itm->flags & AF_ROUNDABOUT) && (priv->itm->prev->flags & AF_ROUNDABOUT)) {
+
+ switch (((180+22)-priv->cmd->roundabout_delta)/45) {
+ case 0:
+ case 1:
+ ret->type=type_nav_roundabout_r1;
+ break;
+ case 2:
+ ret->type=type_nav_roundabout_r2;
+ break;
+ case 3:
+ ret->type=type_nav_roundabout_r3;
+ break;
+ case 4:
+ ret->type=type_nav_roundabout_r4;
+ break;
+ case 5:
+ ret->type=type_nav_roundabout_r5;
+ break;
+ case 6:
+ ret->type=type_nav_roundabout_r6;
+ break;
+ case 7:
+ ret->type=type_nav_roundabout_r7;
+ break;
+ case 8:
+ ret->type=type_nav_roundabout_r8;
+ break;
+ }
+ } else {
+ delta=priv->cmd->delta;
+ if (delta < 0) {
+ delta=-delta;
+ if (delta < 45)
+ ret->type=type_nav_left_1;
+ else if (delta < 105)
+ ret->type=type_nav_left_2;
+ else if (delta < 165)
+ ret->type=type_nav_left_3;
+ else
+ ret->type=type_none;
+ } else {
+ if (delta < 45)
+ ret->type=type_nav_right_1;
+ else if (delta < 105)
+ ret->type=type_nav_right_2;
+ else if (delta < 165)
+ ret->type=type_nav_right_3;
+ else
+ ret->type=type_none;
+ }
+ }
+ }
+ }
+ priv->ccount=0;
+ priv->debug_idx=0;
+ priv->attr_next=attr_navigation_short;
+
+ ret->id_lo=priv->itm->dest_count;
+ dbg(1,"type=%d\n", ret->type);
+ return ret;
+}
+
+static struct item *
+navigation_map_get_item_byid(struct map_rect_priv *priv, int id_hi, int id_lo)
+{
+ struct item *ret;
+ navigation_map_rect_init(priv);
+ while ((ret=navigation_map_get_item(priv))) {
+ if (ret->id_hi == id_hi && ret->id_lo == id_lo)
+ return ret;
+ }
+ return NULL;
+}
+
+static struct map_methods navigation_map_meth = {
+ projection_mg,
+ "utf-8",
+ navigation_map_destroy,
+ navigation_map_rect_new,
+ navigation_map_rect_destroy,
+ navigation_map_get_item,
+ navigation_map_get_item_byid,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static struct map_priv *
+navigation_map_new(struct map_methods *meth, struct attr **attrs)
+{
+ struct map_priv *ret;
+ struct attr *navigation_attr;
+
+ navigation_attr=attr_search(attrs, NULL, attr_navigation);
+ if (! navigation_attr)
+ return NULL;
+ ret=g_new0(struct map_priv, 1);
+ *meth=navigation_map_meth;
+ ret->navigation=navigation_attr->u.navigation;
+
+ return ret;
+}
+
+void
+navigation_set_route(struct navigation *this_, struct route *route)
+{
+ struct attr callback;
+ this_->route=route;
+ this_->route_cb=callback_new_attr_1(callback_cast(navigation_update), attr_route_status, this_);
+ callback.type=attr_callback;
+ callback.u.callback=this_->route_cb;
+ route_add_attr(route, &callback);
+}
+
+void
+navigation_init(void)
+{
+ plugin_register_map_type("navigation", navigation_map_new);
+}