char *name2;
struct item item;
int direction;
- int straight;
int angle_start;
int angle_end;
struct coord start,end;
return;
}
- if (item_attr_get(ritem, attr_route_follow_straight, &straight)) {
- itm->straight = straight.u.num;
- } else {
- itm->straight = 1;
- }
-
dbg(1,"length=%d time=%d\n", length.u.num, time.u.num);
itm->length=length.u.num;
itm->time=time.u.num;
return 1;
}
+
+
+/**
+ * @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.
+ *
+ * This is meant to be used with items from a route graph map
+ * With other items this will probably not be optimal...
+ *
+ * @param itm The item to get the start/end angle of
+ * @param reverse Set this to true to get the end instead of the start
+ * @return The angle the item starts/ends with or 361 on error
+ */
+static int
+calculate_angle(struct item *itm, int reverse)
+{
+ struct coord cbuf[2];
+ struct item *ritem; // the "real" item
+ struct coord c;
+ struct map_rect *mr;
+
+ mr = map_rect_new(itm->map, NULL);
+ if (!mr)
+ return 361;
+
+ ritem = map_rect_get_item_byid(mr, itm->id_hi, itm->id_lo);
+ if (!ritem) {
+ dbg(1,"Item from segment not found on map!\n");
+ map_rect_destroy(mr);
+ return 361;
+ }
+
+ if (ritem->type < type_line || ritem->type >= type_area) {
+ map_rect_destroy(mr);
+ return 361;
+ }
+
+ if (reverse) {
+ 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 361;
+ }
+
+ 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 361;
+ }
+ c = cbuf[0];
+ cbuf[0] = cbuf[1];
+ cbuf[1] = c;
+ }
+
+ map_rect_destroy(mr);
+
+ return road_angle(&cbuf[0],&cbuf[1],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 graph_map An opened route graph map
+ * @param old The navigation item we're coming from
+ * @param new The navigation item we're driving to
+ * @return True if the new item is entered "straight"
+ */
+static int
+entering_straight(struct map *graph_map, struct navigation_itm *old, struct navigation_itm *new)
+{
+ 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;
+ int angle_old,angle_new,angle_curr;
+ int diff_oldnew,diff_curr,diff_min;
+
+ // We first have to calculate the angle of the old item
+ angle_old = calculate_angle(&old->item,(old->direction >= 1));
+ angle_old += 180; // the old angle is the only one pointing away from the route graph point
+ if (angle_old >= 360) {
+ angle_old -= 360;
+ }
+
+ angle_new = 361;
+ diff_min = 360;
+
+ // 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 = old->end;
+ coord_sel.u.c_rect.rl = old->end;
+ // 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 1; // don't cause a right/left navigation on every offroad item
+ }
+
+ while (1) {
+ i = map_rect_get_item(g_rect);
+
+ if (!i) {
+ break;
+ }
+
+ if (i->type != type_rg_segment) {
+ dbg(1, "Expected to get a route graph segment in entering_straight()\n");
+ 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(new->item,*sitem)) {
+ if (angle_new == 361) {
+ angle_new = calculate_angle(sitem,(direction_attr.u.num <= 0));
+ }
+ continue;
+ } else if (item_is_equal(old->item,*sitem)) {
+ continue;
+ }
+
+
+ angle_curr = calculate_angle(sitem,(direction_attr.u.num <= 0));
+ diff_curr = angle_old - angle_curr;
+ if (diff_curr < 0) {
+ diff_curr *= -1;
+ }
+ diff_curr = diff_curr % 180; // We will *never* turn more than 180 degrees
+
+ if (diff_curr < diff_min) {
+ diff_min = diff_curr;
+ }
+ }
+
+ diff_oldnew = angle_old - angle_new;
+ if (diff_oldnew < 0) {
+ diff_oldnew *= -1;
+ }
+ diff_oldnew = diff_oldnew % 180;
+
+ map_rect_destroy(g_rect);
+
+ // 10 degrees difference should make shure it's clear where to drive
+ return ((diff_oldnew+10) < diff_min);
+}
+
/**
* @brief Checks if navit has to create a maneuver 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 graph_map A pointer to an opened map for the current route graph
* @return True if navit should guide the user, false otherwise
*/
static int
-maneuver_required2(struct navigation_itm *old, struct navigation_itm *new, int *delta)
+maneuver_required2(struct navigation_itm *old, struct navigation_itm *new, int *delta, struct map *graph_map)
{
dbg(1,"enter %p %p %p\n",old, new, delta);
+ *delta=new->angle_start-old->angle_end;
+ if (*delta < -180)
+ *delta+=360;
+ if (*delta > 180)
+ *delta-=360;
+
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(graph_map, old, new)) {
+ dbg(1, "maneuver_required: Not driving straight: yes\n");
+ return 1;
+ }
+
+ dbg(1, "maneuver_required: Staying on the same street: no\n");
return 0;
}
} else
return 0;
}
#endif
- *delta=new->angle_start-old->angle_end;
- if (*delta < -180)
- *delta+=360;
- if (*delta > 180)
- *delta-=360;
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");
return 1;
}
if (*delta < 20 && *delta >-20) {
- if (! new->straight) { /* We're not entering this item straight, so have a maneuver */
+ if (! entering_straight(graph_map, old, new)) {
+ dbg(1, "maneuver_required: not driving straight: yes\n");
return 1;
}
}
static void
-make_maneuvers(struct navigation *this_)
+make_maneuvers(struct navigation *this_, struct route *route)
{
struct navigation_itm *itm, *last=NULL, *last_itm=NULL;
+ struct map *graph_map;
int delta;
itm=this_->first;
this_->cmd_last=NULL;
this_->cmd_first=NULL;
+ graph_map = route_get_graph_map(route);
while (itm) {
if (last) {
- if (maneuver_required2(last_itm, itm, &delta)) {
+ if (maneuver_required2(last_itm, itm, &delta,graph_map)) {
command_new(this_, itm, delta);
}
} else
return ret;
}
-
static char *
show_maneuver(struct navigation *nav, struct navigation_itm *itm, struct navigation_command *cmd, enum attr_type type)
{
else {
if (! ritem) {
navigation_itm_new(this_, NULL);
- make_maneuvers(this_);
+ make_maneuvers(this_,route);
}
calculate_dest_distance(this_, incr);
dbg(2,"destination distance old=%d new=%d\n", this_->distance_last, this_->first->dest_length);
#include "fib.h"
-#define DISABLE_STRAIGHT 1 /* Slows down incremental routing by factor of 10 */
-
struct map_priv {
struct route *route;
};
* coordinate of the segment is the first coordinate of the item", <=0
* means reverse. */
unsigned ncoords; /**< How many coordinates does this segment have? */
-#ifndef DISABLE_STRAIGHT
- struct attr **attrs; /**< Attributes of this route path segment */
-#endif
struct coord c[0]; /**< Pointer to the ncoords coordinates of this segment */
/* WARNING: There will be coordinates following here, so do not create new fields after c! */
};
struct route_graph *graph; /**< Pointer to the route graph */
struct route_path *path2; /**< Pointer to the route path */
- struct map *map;
+ struct map *map;
struct map *graph_map;
int destination_distance; /**< Distance to the destination at which the destination is considered "reached" */
int speedlist[route_item_last-route_item_first+1]; /**< The speedlist for this route */
#define HASHCOORD(c) ((((c)->x +(c)->y) * 2654435761UL) & (HASH_SIZE-1))
+/**
+ * @brief Iterator to iterate through all route graph segments in a route graph point
+ *
+ * This structure can be used to iterate through all route graph segments connected to a
+ * route graph point. Use this with the rp_iterator_* functions.
+ */
+struct route_graph_point_iterator {
+ struct route_graph_point *p; /**< The route graph point whose segments should be iterated */
+ int end; /**< Indicates if we have finished iterating through the "start" segments */
+ struct route_graph_segment *next; /**< The next segment to be returned */
+};
+
static struct route_info * route_find_nearest_street(struct mapset *ms, struct pcoord *c);
static struct route_graph_point *route_graph_get_point(struct route_graph *this, struct coord *c);
static void route_graph_update(struct route *this);
}
/**
+ * @brief Creates a new graph point iterator
+ *
+ * This function creates a new route graph point iterator, that can be used to
+ * iterate through all segments connected to the point.
+ *
+ * @param p The route graph point to create the iterator from
+ * @return A new iterator.
+ */
+static struct route_graph_point_iterator
+rp_iterator_new(struct route_graph_point *p)
+{
+ struct route_graph_point_iterator it;
+
+ it.p = p;
+ if (p->start) {
+ it.next = p->start;
+ it.end = 0;
+ } else {
+ it.next = p->end;
+ it.end = 1;
+ }
+
+ return it;
+}
+
+/**
+ * @brief Gets the next segment connected to a route graph point from an iterator
+ *
+ * @param it The route graph point iterator to get the segment from
+ * @return The next segment or NULL if there are no more segments
+ */
+static struct route_graph_segment
+*rp_iterator_next(struct route_graph_point_iterator *it)
+{
+ struct route_graph_segment *ret;
+
+ ret = it->next;
+ if (!ret) {
+ return NULL;
+ }
+
+ if (!it->end) {
+ if (ret->start_next) {
+ it->next = ret->start_next;
+ } else {
+ it->next = it->p->end;
+ it->end = 1;
+ }
+ } else {
+ it->next = ret->end_next;
+ }
+
+ return ret;
+}
+
+/**
* @brief Destroys a route_path
*
* @param this The route_path to be destroyed
c=this->path;
while (c) {
n=c->next;
-#ifndef DISABLE_STRAIGHT
- if (c->attrs) {
- attr_list_free(c->attrs);
- }
-#endif
g_free(c);
c=n;
}
struct route_path_segment *segment;
int i,ccnt = 0;
struct coord ca[2048];
-#ifndef DISABLE_STRAIGHT
- struct attr straight_attr;
-#endif
if (oldpath) {
ccnt = (int)item_hash_lookup(oldpath->path_hash, &rgs->item);
segment->next=NULL;
item_hash_insert(this->path_hash, &rgs->item, (void *)ccnt);
-#ifndef DISABLE_STRAIGHT
- straight_attr.type = attr_route_follow_straight;
- straight_attr.u.num = straight;
-
- segment->attrs = attr_generic_set_attr(segment->attrs, &straight_attr);
-#endif
-
route_path_add_segment(this, segment);
}
return ret;
}
-#ifndef DISABLE_STRAIGHT
-/**
- * @brief Calculates of two coordinates' connection
- *
- * This function calculates the angle between coordinates, with north = 0
- * and east = 90.
- *
- * %FIXME This is a duplicate of road_angle() in navigation.c - combine them?
- *
- * @param c1 Coordinate 1
- * @param c2 Coordinate 2
- * @param dir Set to true if c1 is the prior, and c2 the later coordinate.
- * @return The angle of the coordinate's connection
- */
-static int
-route_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;
-}
-
-/**
- * @brief Checks if entering one segment from another is a "straight" road
- *
- * This checks if one can enter seg_to from seg_from by driving "straight" - i.e.
- * if seg_to is the segment you can drive to from seg_from by steering less than
- * all to all other segments.
- *
- * This function returns true on failure, so we don't create maneuvers on every error.
- *
- * @param seg_from Segment we are driving from
- * @param seg_to Segment we are driving to
- * @return True if driving from seg_from to seg_to is "straight", false otherwise
- */
-static int
-route_check_straight(struct route_graph_segment *seg_from, struct route_graph_segment *seg_to)
-{
- struct route_graph_segment *curr;
- struct route_graph_point *conn;
- int from_angle, to_angle, curr_angle, angle_diff;
- int ccnt;
- struct coord ca[2048];
-
- if ((seg_from->end == seg_to->start) || (seg_from->end == seg_to->end)) {
- ccnt = get_item_seg_coords(&seg_from->item, ca, 2047, &seg_from->start->c, &seg_from->end->c);
- from_angle = route_road_angle(&ca[ccnt-2], &ca[ccnt-1],1);
-
- conn = seg_from->end;
- } else if ((seg_from->start == seg_to->start) || (seg_from->start == seg_to->end)) {
- ccnt = get_item_seg_coords(&seg_from->item, ca, 2, &seg_from->start->c, &seg_from->end->c);
- from_angle = route_road_angle(&ca[1], &ca[0],1);
-
- conn = seg_from->start;
- } else {
- // Not connected!
- return 1;
- }
-
-
- if (seg_to->end == conn) {
- ccnt = get_item_seg_coords(&seg_to->item, ca, 2047, &seg_to->start->c, &seg_to->end->c);
- to_angle = route_road_angle(&ca[ccnt-1], &ca[ccnt-2],1);
- } else {
- ccnt = get_item_seg_coords(&seg_to->item, ca, 2, &seg_to->start->c, &seg_to->end->c);
- to_angle = route_road_angle(&ca[0], &ca[1],1);
- }
-
-
- angle_diff = from_angle - to_angle;
- if (angle_diff < 0) {
- angle_diff *= -1;
- }
-
-
- curr = conn->start;
- while (curr != NULL) {
- if (curr==seg_to) {
- curr = curr->start_next;
- continue;
- }
-
- ccnt = get_item_seg_coords(&curr->item, ca, 2, &curr->start->c, &curr->end->c);
- curr_angle = route_road_angle(&ca[0], &ca[1], 1);
-
- curr_angle = from_angle - curr_angle;
-
- if (curr_angle < 0) {
- curr_angle *= -1;
- }
-
-
- if (curr_angle < angle_diff) {
- return 0;
- }
-
- curr = curr->start_next;
- }
-
- curr = conn->end;
- while (curr != NULL) {
- if (curr==seg_to) {
- curr = curr->end_next;
- continue;
- }
-
- ccnt = get_item_seg_coords(&curr->item, ca, 2047, &curr->start->c, &curr->end->c);
- curr_angle = route_road_angle(&ca[ccnt-1], &ca[ccnt-2], 1);
-
- curr_angle = from_angle - curr_angle;
-
- if (curr_angle < 0) {
- curr_angle *= -1;
- }
-
- if (curr_angle < angle_diff) {
- return 0;
- }
-
- curr = curr->end_next;
- }
-
- return 1;
-}
-#endif
-
/**
* @brief Creates a new route path
*
seg_len=s->len;
len+=seg_len;
-#ifndef DISABLE_STRAIGHT
- if (lastseg) {
- is_straight = route_check_straight(lastseg,s);
- } else {
- is_straight = 0;
- }
-#endif
-
if (s->start == start) {
route_path_add_item_from_graph(ret, oldpath, s, seg_len, s->offset, 1, is_straight);
start=s->end;
struct route_graph_point *point;
struct route_graph_segment *rseg;
char *str;
+ struct coord *coord_sel; /**< Set this to a coordinate if you want to filter for just a single route graph point */
+ struct route_graph_point_iterator it;
};
static void
}
return 0;
case attr_street_item:
-#ifndef DISABLE_STRAIGHT
- mr->attr_next=attr_route_follow_straight;
-#else
mr->attr_next=attr_direction;
-#endif
if (seg && seg->item.map)
attr->u.item=&seg->item;
else
return 0;
return 1;
-#ifndef DISABLE_STRAIGHT
- case attr_route_follow_straight:
- mr->attr_next=attr_direction;
- if (seg) {
- return attr_generic_get_attr(seg->attrs,NULL,attr_route_follow_straight,attr,NULL);
- }
- return 0;
-#endif
case attr_direction:
mr->attr_next=attr_length;
if (seg)
{
struct map_rect_priv *mr = priv_data;
struct route_graph_point *p = mr->point;
- if (mr->item.type != type_rg_point)
- return 0;
+ struct route_graph_segment *seg = mr->rseg;
switch (attr_type) {
- case attr_any:
+ case attr_any: // works only with rg_points for now
+ if (mr->item.type != type_rg_point)
+ return 0;
while (mr->attr_next != attr_none) {
if (rp_attr_get(priv_data, mr->attr_next, attr))
return 1;
}
return 0;
case attr_label:
+ if (mr->item.type != type_rg_point)
+ return 0;
attr->type = attr_label;
if (mr->str)
g_free(mr->str);
attr->u.str = mr->str;
mr->attr_next=attr_none;
return 1;
+ case attr_street_item:
+ if (mr->item.type != type_rg_segment)
+ return 0;
+ mr->attr_next=attr_none;
+ if (seg && seg->item.map)
+ attr->u.item=&seg->item;
+ else
+ return 0;
+ return 1;
+ case attr_direction:
+ // This only works if the map has been opened at a single point, and in that case indicates if the
+ // segment returned last is connected to this point via its start (1) or its end (-1)
+ if (!mr->coord_sel || (mr->item.type != type_rg_segment))
+ return 0;
+ if (seg->start == mr->point) {
+ attr->u.num=1;
+ } else if (seg->end == mr->point) {
+ attr->u.num=-1;
+ } else {
+ return 0;
+ }
+ return 1;
case attr_debug:
+ if (mr->item.type != type_rg_point)
+ return 0;
attr->type = attr_debug;
if (mr->str)
g_free(mr->str);
}
}
+/**
+ * @brief Returns the coordinates of a route graph item
+ *
+ * @param priv_data The route graph item's private data
+ * @param c Pointer where to store the coordinates
+ * @param count How many coordinates to get at a max?
+ * @return The number of coordinates retrieved
+ */
static int
rp_coord_get(void *priv_data, struct coord *c, int count)
{
};
static void
+rp_destroy(struct map_priv *priv)
+{
+ g_free(priv);
+}
+
+static void
rm_destroy(struct map_priv *priv)
{
g_free(priv);
return mr;
}
+/**
+ * @brief Opens a new map rectangle on the route graph's map
+ *
+ * This function opens a new map rectangle on the route graph's map.
+ * The "sel" parameter enables you to only search for a single route graph
+ * point on this map (or exactly: open a map rectangle that only contains
+ * this one point). To do this, pass here a single map selection, whose
+ * c_rect has both coordinates set to the same point. Otherwise this parameter
+ * has no effect.
+ *
+ * @param priv The route graph map's private data
+ * @param sel Here it's possible to specify a point for which to search. Please read the function's description.
+ * @return A new map rect's private data
+ */
static struct map_rect_priv *
rp_rect_new(struct map_priv *priv, struct map_selection *sel)
{
struct map_rect_priv * mr;
+
dbg(1,"enter\n");
if (! priv->route->graph || ! priv->route->graph->route_points)
return NULL;
mr->item.priv_data = mr;
mr->item.type = type_rg_point;
mr->item.meth = &methods_point_item;
+ if (sel) {
+ if ((sel->u.c_rect.lu.x == sel->u.c_rect.rl.x) && (sel->u.c_rect.lu.y == sel->u.c_rect.rl.y)) {
+ mr->coord_sel = g_malloc(sizeof(struct coord));
+ *(mr->coord_sel) = sel->u.c_rect.lu;
+ }
+ }
return mr;
}
{
if (mr->str)
g_free(mr->str);
+ if (mr->coord_sel) {
+ g_free(mr->coord_sel);
+ }
+
g_free(mr);
}
struct route_graph_segment *seg = mr->rseg;
if (mr->item.type == type_rg_point) {
- if (!p)
- p = r->graph->route_points;
- else
- p = p->next;
+ if (mr->coord_sel) {
+ // We are supposed to return only the point at one specified coordinate...
+ if (!p) {
+ p = r->graph->hash[HASHCOORD(mr->coord_sel)];
+ while ((p) && ((p->c.x != mr->coord_sel->x) || (p->c.y != mr->coord_sel->y))) {
+ p = p->hash_next;
+ }
+ if ((!p) || !((p->c.x == mr->coord_sel->x) && (p->c.y == mr->coord_sel->y))) {
+ mr->point = NULL; // This indicates that no point has been found
+ } else {
+ mr->it = rp_iterator_new(p);
+ }
+ } else {
+ p = NULL;
+ }
+ } else {
+ if (!p)
+ p = r->graph->route_points;
+ else
+ p = p->next;
+ }
if (p) {
mr->point = p;
mr->item.id_lo++;
} else
mr->item.type = type_rg_segment;
}
- if (!seg)
- seg=r->graph->route_segments;
- else
- seg=seg->next;
+
+ if (mr->coord_sel) {
+ if (!mr->point) { // This means that no point has been found
+ return NULL;
+ }
+ seg = rp_iterator_next(&(mr->it));
+ } else {
+ if (!seg)
+ seg=r->graph->route_segments;
+ else
+ seg=seg->next;
+ }
+
if (seg) {
mr->rseg = seg;
mr->item.id_lo++;
static struct map_methods route_graph_meth = {
projection_mg,
"utf-8",
- rm_destroy,
+ rp_destroy,
rp_rect_new,
rm_rect_destroy,
rp_get_item,
&(struct attr){attr_data,{""}},
&(struct attr){attr_description,{description}},
NULL});
+
return *map;
}
+/**
+ * @brief Returns a new map containing the route path
+ *
+ * This function returns a new map containing the route path.
+ *
+ * @important Do not map_destroy() this!
+ *
+ * @param this_ The route to get the map of
+ * @return A new map containing the route path
+ */
struct map *
route_get_map(struct route *this_)
{
}
+/**
+ * @brief Returns a new map containing the route graph
+ *
+ * This function returns a new map containing the route graph.
+ *
+ * @important Do not map_destroy() this!
+ *
+ * @param this_ The route to get the map of
+ * @return A new map containing the route graph
+ */
struct map *
route_get_graph_map(struct route *this_)
{