]> err.no Git - mapper/blob - src/route.c
Get rid of _pos, move data to gpsdata struct.
[mapper] / src / route.c
1 #include <config.h>
2
3 #include <unistd.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <strings.h>
7 #include <stddef.h>
8 #include <libintl.h>
9 #include <locale.h>
10 #include <math.h>
11 #include <errno.h>
12 #include <sys/wait.h>
13 #include <glib/gstdio.h>
14 #include <gtk/gtk.h>
15 #include <fcntl.h>
16 #include <libgnomevfs/gnome-vfs.h>
17 #include <curl/multi.h>
18
19 #include "utils.h"
20 #include "gps.h"
21 #include "route.h"
22 #include "ui-common.h"
23 #include "settings.h"
24 #include "mapper-types.h"
25 #include "map.h"
26 #include "file.h"
27 #include "latlon.h"
28 #include "map.h"
29 #include "map-download.h"
30 #include "iap.h"
31
32 #include "help.h"
33
34 #define DISTANCE_SQUARED(a, b) \
35    ((guint64)((((gint64)(b).unitx)-(a).unitx)*(((gint64)(b).unitx)-(a).unitx))\
36   + (guint64)((((gint64)(b).unity)-(a).unity)*(((gint64)(b).unity)-(a).unity)))
37
38 void route_find_nearest_point(void);
39 void cancel_autoroute(gboolean temporary);
40 void route_show_distance_to_last(void);
41 void route_set_destination_from_last(void);
42
43 typedef struct _OriginToggleInfo OriginToggleInfo;
44 struct _OriginToggleInfo {
45         GtkWidget *rad_use_gps;
46         GtkWidget *rad_use_route;
47         GtkWidget *rad_use_text;
48         GtkWidget *chk_auto;
49         GtkWidget *txt_from;
50         GtkWidget *txt_to;
51 };
52
53 void
54 route_init(void)
55 {
56 memset(&_route, 0, sizeof(_route));
57 MACRO_PATH_INIT(_route);
58 }
59
60 void
61 route_deinit(void)
62 {
63 MACRO_PATH_FREE(_route);
64 }
65
66 void
67 route_clear(void)
68 {
69 GtkWidget *confirm;
70
71 confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), _("Really clear the route?"));
72
73 if (GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) {
74         cancel_autoroute(FALSE);
75         MACRO_PATH_FREE(_route);
76         MACRO_PATH_INIT(_route);
77         route_find_nearest_point();
78         map_force_redraw();
79 }
80 _dest.valid=FALSE;
81 gtk_widget_destroy(confirm);
82 }
83
84 gboolean 
85 route_open_file(void)
86 {
87 gchar *buffer;
88 gint size;
89
90 if (file_open_get_contents(&_route_dir_uri, &buffer, &size)) {
91         /* If auto is enabled, append the route, otherwise replace it. */
92         if (parse_gpx(&_route, buffer, size, _autoroute_data.enabled ? 0 : 1)) {
93                 cancel_autoroute(FALSE);
94
95                 MACRO_BANNER_SHOW_INFO(_window, _("Route Opened"));
96                 /* Find the nearest route point, if we're connected. */
97                 route_find_nearest_point();
98                 map_force_redraw();
99                 route_set_destination_from_last();
100                 return TRUE;
101         } else {
102                 popup_error(_window, _("Error parsing GPX file."));
103                 g_free(buffer);
104                 return FALSE;
105         }
106 }
107 return FALSE;
108 }
109
110 /**
111  * Ask user to save route
112  */
113 gboolean
114 route_save(void)
115 {
116 GnomeVFSHandle *handle;
117
118 if (file_save(&_route_dir_uri, &_route_dir_uri, &handle)) {
119         if (write_gpx(&_route, handle)) {
120                 MACRO_BANNER_SHOW_INFO(_window, _("Route Saved"));
121         } else {
122                 popup_error(_window, _("Error writing GPX file."));
123         }
124         gnome_vfs_close(handle);
125         return TRUE;
126 }
127 return FALSE;
128 }
129
130 /**
131  *
132  */
133 static gboolean 
134 origin_type_selected_cb(GtkWidget * toggle, OriginToggleInfo * oti)
135 {
136 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))) {
137         if (toggle == oti->rad_use_gps) {
138                 gchar buffer[80];
139                 gchar strlat[32];
140                 gchar strlon[32];
141                 g_ascii_formatd(strlat, 32, "%.06f", _gps->lat);
142                 g_ascii_formatd(strlon, 32, "%.06f", _gps->lon);
143                 g_snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
144                 gtk_entry_set_text(GTK_ENTRY(oti->txt_from), buffer);
145         } else if (toggle == oti->rad_use_route) {
146                 gchar buffer[80];
147                 gchar strlat[32];
148                 gchar strlon[32];
149                 Point *p;
150                 gdouble lat, lon;
151
152                 /* Use last non-zero route point. */
153                 for (p = _route.tail; !p->unity; p--) {
154                 }
155
156                 unit2latlon(p->unitx, p->unity, lat, lon);
157                 g_ascii_formatd(strlat, 32, "%.06f", lat);
158                 g_ascii_formatd(strlon, 32, "%.06f", lon);
159                 g_snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
160                 gtk_entry_set_text(GTK_ENTRY(oti->txt_from), buffer);
161         }
162         gtk_widget_set_sensitive(oti->txt_from, toggle == oti->rad_use_text);
163         gtk_widget_set_sensitive(oti->chk_auto, toggle == oti->rad_use_gps);
164 }
165 return TRUE;
166 }
167
168 /**
169  * Cancel the current auto-route.
170  */
171 void 
172 cancel_autoroute(gboolean temporary)
173 {
174 if (_autoroute_data.enabled) {
175         if (!temporary) {
176                 _autoroute_data.enabled = FALSE;
177
178                 g_free(_autoroute_data.dest);
179                 _autoroute_data.dest = NULL;
180
181                 g_free(_autoroute_data.src_str);
182                 _autoroute_data.src_str = NULL;
183         }
184
185         if (_autoroute_data.curl_easy) {
186                 if (_curl_multi)
187                         curl_multi_remove_handle(_curl_multi, _autoroute_data.curl_easy);
188                 curl_easy_cleanup(_autoroute_data.curl_easy);
189                 _autoroute_data.curl_easy = NULL;
190         }
191
192         g_free(_autoroute_data.rdl_data.bytes);
193         _autoroute_data.rdl_data.bytes = NULL;
194         _autoroute_data.rdl_data.bytes_read = 0;
195
196         _autoroute_data.in_progress = FALSE;
197 }
198 }
199
200 /**
201  * Read the data provided by the given handle as GPX data, updating the
202  * auto-route with that data.
203  */
204 size_t
205 route_dl_cb_read(void *ptr, size_t size, size_t nmemb, RouteDownloadData * rdl_data)
206 {
207 size_t old_size = rdl_data->bytes_read;
208
209 rdl_data->bytes_read += size * nmemb;
210 rdl_data->bytes = g_renew(gchar, rdl_data->bytes, rdl_data->bytes_read);
211 g_memmove(rdl_data->bytes + old_size, ptr, size * nmemb);
212
213 return (size * nmemb);
214 }
215
216 gboolean 
217 auto_route_dl_idle()
218 {
219 gchar latstr[32], lonstr[32], *latlonstr;
220
221 g_ascii_dtostr(latstr, 32, _gps->lat);
222 g_ascii_dtostr(lonstr, 32, _gps->lon);
223 latlonstr = g_strdup_printf("%s,%s", latstr, lonstr);
224 _autoroute_data.src_str =
225     g_strdup_printf(_route_dl_url, latlonstr, _autoroute_data.dest);
226 g_free(latlonstr);
227
228 MACRO_CURL_EASY_INIT(_autoroute_data.curl_easy);
229 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_URL, _autoroute_data.src_str);
230 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_WRITEFUNCTION, route_dl_cb_read);
231 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_WRITEDATA, &_autoroute_data.rdl_data);
232 if (!_curl_multi) {
233         /* Initialize CURL. */
234         _curl_multi = curl_multi_init();
235         /*curl_multi_setopt(_curl_multi, CURLMOPT_PIPELINING, 1); */
236 }
237 curl_multi_add_handle(_curl_multi, _autoroute_data.curl_easy);
238
239 if (iap_is_connected() && !_curl_sid)
240         _curl_sid = g_timeout_add(100, (GSourceFunc) map_download_timeout, NULL);
241
242 _autoroute_data.in_progress = TRUE;
243
244 return FALSE;
245 }
246
247 /**
248  * Display a dialog box to the user asking them to download a route.  The
249  * "From" and "To" textfields may be initialized using the first two
250  * parameters.  The third parameter, if set to TRUE, will cause the "Use GPS
251  * Location" checkbox to be enabled, which automatically sets the "From" to the
252  * current GPS position (this overrides any value that may have been passed as
253  * the "To" initializer).
254  * None of the passed strings are freed - that is left to the caller, and it is
255  * safe to free either string as soon as this function returns.
256  */
257 gboolean 
258 route_download(gchar * to)
259 {
260 GtkWidget *dialog;
261 GtkWidget *table;
262 GtkWidget *label;
263 GtkWidget *txt_source_url;
264 GtkWidget *hbox;
265 OriginToggleInfo oti;
266 GtkEntryCompletion *from_comp;
267 GtkEntryCompletion *to_comp;
268
269 iap_connect();
270
271 dialog = gtk_dialog_new_with_buttons(_("Download Route"),
272                              GTK_WINDOW(_window),
273                              GTK_DIALOG_MODAL, GTK_STOCK_OK,
274                              GTK_RESPONSE_ACCEPT,
275                              GTK_STOCK_CANCEL,
276                              GTK_RESPONSE_REJECT, NULL);
277
278 help_dialog_help_enable(GTK_DIALOG(dialog), HELP_ID_DOWNROUTE);
279
280 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
281            table = gtk_table_new(2, 5, FALSE), TRUE, TRUE, 0);
282
283 from_comp = gtk_entry_completion_new();
284 gtk_entry_completion_set_model(from_comp, GTK_TREE_MODEL(_loc_model));
285 gtk_entry_completion_set_text_column(from_comp, 0);
286 to_comp = gtk_entry_completion_new();
287 gtk_entry_completion_set_model(to_comp, GTK_TREE_MODEL(_loc_model));
288 gtk_entry_completion_set_text_column(to_comp, 0);
289
290 /* Source URL. */
291 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Source URL")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
292 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
293 gtk_table_attach(GTK_TABLE(table), txt_source_url = gtk_entry_new(), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
294 gtk_entry_set_width_chars(GTK_ENTRY(txt_source_url), 25);
295
296 /* Auto. */
297 gtk_table_attach(GTK_TABLE(table),
298                  hbox = gtk_hbox_new(FALSE, 6),
299                  0, 2, 1, 2, GTK_FILL, 0, 2, 4);
300 gtk_box_pack_start(GTK_BOX(hbox),
301                    oti.rad_use_gps = gtk_radio_button_new_with_label(NULL, _("Use GPS Location")),
302                    TRUE, TRUE, 0);
303 gtk_box_pack_start(GTK_BOX(hbox), oti.chk_auto =
304                    gtk_check_button_new_with_label(_("Auto-Update")),
305                    TRUE, TRUE, 0);
306 gtk_widget_set_sensitive(oti.chk_auto, FALSE);
307
308 /* Use End of Route. */
309 gtk_table_attach(GTK_TABLE(table),
310                  hbox = gtk_hbox_new(FALSE, 6),
311                  0, 2, 2, 3, GTK_FILL, 0, 2, 4);
312 gtk_box_pack_start(GTK_BOX(hbox),
313                    oti.rad_use_route =
314                    gtk_radio_button_new_with_label_from_widget
315                    (GTK_RADIO_BUTTON(oti.rad_use_gps),
316                     _("Use End of Route")), TRUE, TRUE, 0);
317 gtk_widget_set_sensitive(oti.rad_use_route, _route.head != _route.tail);
318
319 /* Origin. */
320 gtk_table_attach(GTK_TABLE(table),
321                  oti.rad_use_text =
322                  gtk_radio_button_new_with_label_from_widget
323                  (GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")), 0, 1,
324                  3, 4, GTK_FILL, 0, 2, 4);
325 gtk_table_attach(GTK_TABLE(table), oti.txt_from =
326                  gtk_entry_new(), 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0,
327                  2, 4);
328 gtk_entry_set_completion(GTK_ENTRY(oti.txt_from), from_comp);
329 gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_from), 25);
330
331 /* Destination. */
332 gtk_table_attach(GTK_TABLE(table),
333                  label = gtk_label_new(_("Destination")),
334                  0, 1, 4, 5, GTK_FILL, 0, 2, 4);
335 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
336 gtk_table_attach(GTK_TABLE(table),
337          oti.txt_to = gtk_entry_new(), 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
338 gtk_entry_set_completion(GTK_ENTRY(oti.txt_to), to_comp);
339 gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_to), 25);
340
341 g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled", G_CALLBACK(origin_type_selected_cb), &oti);
342 g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled", G_CALLBACK(origin_type_selected_cb), &oti);
343 g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled", G_CALLBACK(origin_type_selected_cb), &oti);
344
345 #if defined (WITH_HILDON) && defined (HILDON_AUTOCAP)
346 g_object_set(G_OBJECT(oti.txt_from), HILDON_AUTOCAP, FALSE, NULL);
347 g_object_set(G_OBJECT(oti.txt_to), HILDON_AUTOCAP, FALSE, NULL);
348 #endif
349
350 /* Initialize fields. */
351 gtk_entry_set_text(GTK_ENTRY(txt_source_url), _route_dl_url);
352 gtk_entry_set_text(GTK_ENTRY(oti.txt_to), (to ? to : ""));
353
354 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
355
356 /* Use "End of Route" by default if they have a route. */
357 if (_route.head != _route.tail) {
358         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
359         gtk_widget_grab_focus(oti.rad_use_route);
360 }
361 /* Else use "GPS Location" if they have GPS enabled. */
362 else if (_enable_gps) {
363         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
364         gtk_widget_grab_focus(oti.rad_use_gps);
365 }
366 /* Else use text. */
367 else {
368         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
369         gtk_widget_grab_focus(oti.txt_from);
370 }
371
372 gtk_widget_show_all(dialog);
373
374 while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
375         CURL *curl_easy;
376         RouteDownloadData rdl_data = { 0, 0 };
377         gchar buffer[BUFFER_SIZE];
378         const gchar *source_url, *from, *to;
379         gchar *from_escaped, *to_escaped;
380
381         source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
382         if (!strlen(source_url)) {
383                 popup_error(dialog, _("Please specify a source URL."));
384                 continue;
385         } else {
386                 g_free(_route_dl_url);
387                 _route_dl_url = g_strdup(source_url);
388         }
389
390         from = gtk_entry_get_text(GTK_ENTRY(oti.txt_from));
391         if (!strlen(from)) {
392                 popup_error(dialog, _("Please specify a start location."));
393                 continue;
394         }
395
396         to = gtk_entry_get_text(GTK_ENTRY(oti.txt_to));
397         if (!strlen(to)) {
398                 popup_error(dialog, _("Please specify an end location."));
399                 continue;
400         }
401
402         from_escaped = gnome_vfs_escape_string(from);
403         to_escaped = gnome_vfs_escape_string(to);
404         g_snprintf(buffer, sizeof(buffer), source_url, from_escaped, to_escaped);
405         g_free(from_escaped);
406         g_free(to_escaped);
407
408         /* Attempt to download the route from the server. */
409         MACRO_CURL_EASY_INIT(curl_easy);
410         curl_easy_setopt(curl_easy, CURLOPT_URL, buffer);
411         curl_easy_setopt(curl_easy, CURLOPT_WRITEFUNCTION, route_dl_cb_read);
412         curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, &rdl_data);
413         if (CURLE_OK != curl_easy_perform(curl_easy)) {
414                 popup_error(dialog, _("Failed to connect to GPX Directions server"));
415                 curl_easy_cleanup(curl_easy);
416                 g_free(rdl_data.bytes);
417                 /* Let them try again */
418                 continue;
419         }
420         curl_easy_cleanup(curl_easy);
421
422         if (strncmp(rdl_data.bytes, "<?xml", strlen("<?xml"))) {
423                 /* Not an XML document - must be bad locations. */
424                 popup_error(dialog,
425                             _("Could not generate directions. Make sure your "
426                              "source and destination are valid."));
427                 g_free(rdl_data.bytes);
428                 /* Let them try again. */
429         }
430         /* Else, if GPS is enabled, replace the route, otherwise append it. */
431         else if (parse_gpx(&_route, rdl_data.bytes, rdl_data.bytes_read,
432                            (gtk_toggle_button_get_active
433                             (GTK_TOGGLE_BUTTON(oti.rad_use_gps)) ? 0 : 1))) {
434                 GtkTreeIter iter;
435
436                 /* Find the nearest route point, if we're connected. */
437                 route_find_nearest_point();
438
439                 /* Cancel any autoroute that might be occurring. */
440                 cancel_autoroute(FALSE);
441
442                 map_force_redraw();
443
444                 if (gtk_toggle_button_get_active
445                     (GTK_TOGGLE_BUTTON(oti.chk_auto))) {
446                         /* Kick off a timeout to start the first update. */
447                         _autoroute_data.dest = gnome_vfs_escape_string(to);
448                         _autoroute_data.enabled = TRUE;
449                 }
450
451                 /* Save Origin in Route Locations list if not from GPS. */
452                 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps))
453                     && !g_slist_find_custom(_loc_list, from, (GCompareFunc) strcmp)) {
454                         _loc_list = g_slist_prepend(_loc_list, g_strdup(from));
455                         gtk_list_store_insert_with_values(_loc_model, &iter, INT_MAX, 0, from, -1);
456                 }
457
458                 /* Save Destination in Route Locations list. */
459                 if (!g_slist_find_custom(_loc_list, to, (GCompareFunc) strcmp)) {
460                         _loc_list = g_slist_prepend(_loc_list, g_strdup(to));
461                         gtk_list_store_insert_with_values(_loc_model, &iter, INT_MAX, 0, to, -1);
462                 }
463
464                 MACRO_BANNER_SHOW_INFO(_window, _("Route Downloaded"));
465                 g_free(rdl_data.bytes);
466                 route_set_destination_from_last();
467
468                 /* Success! Get out of the while loop. */
469                 break;
470         } else {
471                 popup_error(dialog, _("Error parsing GPX file."));
472                 g_free(rdl_data.bytes);
473                 /* Let them try again. */
474         }
475 }
476
477 gtk_widget_hide(dialog);        /* Destroying causes a crash (!?!?!??!) */
478
479 return TRUE;
480 }
481
482 WayPoint *
483 find_nearest_waypoint(guint unitx, guint unity)
484 {
485 WayPoint *wcurr;
486 WayPoint *wnear;
487 guint64 nearest_squared;
488 Point pos = { unitx, unity, 0, NAN };
489
490 wcurr = wnear = _route.whead;
491 if (wcurr && wcurr != _route.wtail) {
492         nearest_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
493
494         while (wcurr++ != _route.wtail) {
495                 guint64 test_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
496                 if (test_squared < nearest_squared) {
497                         wnear = wcurr;
498                         nearest_squared = test_squared;
499                 }
500         }
501 }
502
503 if (wnear) {
504         /* Only use the waypoint if it is within a 6*_draw_width square drawn
505          * around the position. This is consistent with select_poi(). */
506         if (abs(unitx - wnear->point->unitx) <
507             pixel2unit(3 * _draw_width)
508             && abs(unity - wnear->point->unity) <
509             pixel2unit(3 * _draw_width))
510                 return wnear;
511 }
512
513 MACRO_BANNER_SHOW_INFO(_window, _("There are no waypoints."));
514
515 return NULL;
516 }
517
518 /**
519  * Updates _near_point, _next_way, and _next_wpt.  If quick is FALSE (as
520  * it is when this function is called from route_find_nearest_point), then
521  * the entire list (starting from _near_point) is searched.  Otherwise, we
522  * stop searching when we find a point that is farther away.
523  */
524 gboolean 
525 route_update_nears(gboolean quick)
526 {
527 gboolean ret = FALSE;
528 Point *curr, *near;
529 WayPoint *wcurr, *wnext;
530 guint64 near_dist_squared;
531
532 /* If we have waypoints (_next_way != NULL), then determine the "next
533  * waypoint", which is defined as the waypoint after the nearest point,
534  * UNLESS we've passed that waypoint, in which case the waypoint after
535  * that waypoint becomes the "next" waypoint. */
536 if (_next_way) {
537         /* First, set near_dist_squared with the new distance from
538          * _near_point. */
539         near = _near_point;
540         near_dist_squared = DISTANCE_SQUARED(*_gps, *near);
541
542         /* Now, search _route for a closer point.  If quick is TRUE, then we'll
543          * only search forward, only as long as we keep finding closer points.
544          */
545         for (curr = _near_point; curr++ != _route.tail;) {
546                 if (curr->unity) {
547                         guint dist_squared = DISTANCE_SQUARED(*_gps, *curr);
548                         if (dist_squared <= near_dist_squared) {
549                                 near = curr;
550                                 near_dist_squared = dist_squared;
551                         } else if (quick)
552                                 break;
553                 }
554         }
555
556         /* Update _near_point. */
557         _near_point = near;
558         _near_point_dist_squared = near_dist_squared;
559
560         for (wnext = wcurr = _next_way; wcurr != _route.wtail; wcurr++) {
561                 if (wcurr->point < near || (wcurr->point == near && quick 
562                                 && (_next_wpt && (DISTANCE_SQUARED(*_gps, *near) > _next_way_dist_squared
563                                 && DISTANCE_SQUARED(*_gps, *_next_wpt) < _next_wpt_dist_squared))))
564                     /* Okay, this else if expression warrants explanation.  If the
565                      * nearest track point happens to be a waypoint, then we want to
566                      * check if we have "passed" that waypoint.  To check this, we
567                      * test the distance from _gps to the waypoint and from _gps to
568                      * _next_wpt, and if the former is increasing and the latter is
569                      * decreasing, then we have passed the waypoint, and thus we
570                      * should skip it.  Note that if there is no _next_wpt, then
571                      * there is no next waypoint, so we do not skip it in that case. */
572                         wnext = wcurr + 1;
573                 else
574                         break;
575         }
576
577         if (wnext == _route.wtail && (wnext->point < near || (wnext->point == near && quick
578                                           && (_next_wpt && (DISTANCE_SQUARED (*_gps, *near) > _next_way_dist_squared
579                                                && DISTANCE_SQUARED(*_gps, *_next_wpt) < _next_wpt_dist_squared)))))
580         {
581                 _next_way = NULL;
582                 _next_wpt = NULL;
583                 _next_way_dist_squared = -1;
584                 _next_wpt_dist_squared = -1;
585                 ret = TRUE;
586         }
587         /* Only update _next_way (and consequently _next_wpt) if _next_way is
588          * different, and record that fact for return. */
589         else {
590                 if (!quick || _next_way != wnext) {
591                         _next_way = wnext;
592                         _next_wpt = wnext->point;
593                         if (_next_wpt == _route.tail)
594                                 _next_wpt = NULL;
595                         else {
596                                 while (!(++_next_wpt)->unity) {
597                                         if (_next_wpt == _route.tail) {
598                                                 _next_wpt = NULL;
599                                                 break;
600                                         }
601                                 }
602                         }
603                         ret = TRUE;
604                 }
605                 _next_way_dist_squared = DISTANCE_SQUARED(*_gps, *wnext->point);
606                 if (_next_wpt)
607                         _next_wpt_dist_squared = DISTANCE_SQUARED(*_gps, *_next_wpt);
608         }
609 }
610 return ret;
611 }
612
613 /**
614  * Reset the _near_point data by searching the entire route for the nearest
615  * route point and waypoint.
616  */
617 void 
618 route_find_nearest_point()
619 {
620 /* Initialize _near_point to first non-zero point. */
621 _near_point = _route.head;
622 while (!_near_point->unity && _near_point != _route.tail)
623         _near_point++;
624
625 /* Initialize _next_way. */
626 if (_route.wtail == _route.whead - 1 || (_autoroute_data.enabled && _route.wtail == _route.whead))
627         _next_way = NULL;
628 else
629         /* We have at least one waypoint. */
630         _next_way = (_autoroute_data.enabled ? _route.whead + 1 : _route.whead);
631
632 _next_way_dist_squared = -1;
633
634 /* Initialize _next_wpt. */
635 _next_wpt = NULL;
636 _next_wpt_dist_squared = -1;
637
638 route_update_nears(FALSE);
639 }
640
641 /**
642  * Show the distance from the current GPS location to the given point,
643  * following the route.  If point is NULL, then the distance is shown to the
644  * next waypoint.
645  */
646 gboolean 
647 route_show_distance_to(Point *point)
648 {
649 gchar buffer[80];
650 gdouble lat1, lon1, lat2, lon2;
651 gdouble sum = 0.0;
652
653 /* If point is NULL, use the next waypoint. */
654 if (point == NULL && _next_way)
655         point = _next_way->point;
656
657 /* If point is still NULL, return an error. */
658 if (point == NULL)
659         return FALSE;
660
661 unit2latlon(_gps->unitx, _gps->unity, lat1, lon1);
662 if (point > _near_point) {
663         Point *curr;
664         /* Skip _near_point in case we have already passed it. */
665         for (curr = _near_point + 1; curr <= point; ++curr) {
666                 if (curr->unity) {
667                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
668                         sum += calculate_distance(lat1, lon1, lat2, lon2);
669                         lat1 = lat2;
670                         lon1 = lon2;
671                 }
672         }
673 } else if (point < _near_point) {
674         Point *curr;
675         /* Skip _near_point in case we have already passed it. */
676         for (curr = _near_point - 1; curr >= point; --curr) {
677                 if (curr->unity) {
678                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
679                         sum += calculate_distance(lat1, lon1, lat2, lon2);
680                         lat1 = lat2;
681                         lon1 = lon2;
682                 }
683         }
684 } else {
685         /* Waypoint _is_ the nearest point. */
686         unit2latlon(_near_point->unitx, _near_point->unity, lat2, lon2);
687         sum += calculate_distance(lat1, lon1, lat2, lon2);
688 }
689
690 g_snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"), sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
691 MACRO_BANNER_SHOW_INFO(_window, buffer);
692
693 return TRUE;
694 }
695
696 void 
697 route_show_distance_to_next()
698 {
699 if (!route_show_distance_to(NULL)) {
700         MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
701 }
702 }
703
704 void
705 route_set_destination_from_last(void)
706 {
707 Point *p;
708 gdouble lat,lon;
709 if (_route.head == _route.tail)
710         return;
711
712 /* Find last non-zero point. */
713 for (p = _route.tail; !p->unity; p--) {
714 }
715 unit2latlon(p->unitx, p->unity, lat, lon);
716 _dest.valid=TRUE;
717 _dest.lat=lat;
718 _dest.lon=lon;
719 }
720
721 void 
722 route_show_distance_to_last(void)
723 {
724 Point *p;
725
726 if (_route.head != _route.tail) {
727         /* Find last non-zero point. */
728         for (p = _route.tail; !p->unity; p--) {
729         }
730         route_show_distance_to(p);
731 } else {
732         MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));
733         }
734 }
735
736 /***/
737
738 GtkListStore *
739 route_generate_store(Path *route)
740 {
741 WayPoint *wcurr;
742 GtkTreeIter iter;
743 GtkListStore *store;
744 gchar buffer1[80];
745 gchar buffer2[32];
746 gdouble lat1, lon1, lat2, lon2;
747 gfloat sum=0.0;
748
749 if (route->whead==route->wtail)
750         return NULL;
751
752 wcurr=route->whead;
753
754 if (!wcurr->point)
755         return NULL;
756
757 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1);
758
759 store = gtk_list_store_new(ROUTE_NUM_COLUMNS,G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
760
761 while (wcurr!=route->wtail) {
762         if (!wcurr)
763                 break;
764
765         unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat2, lon2);
766         g_snprintf(buffer1, sizeof(buffer1), "%.05f,%.05f", lat2, lon2);
767         sum += calculate_distance(lat1, lon1, lat2, lon2);
768         g_snprintf(buffer2, sizeof(buffer2), "%.02f %s", sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
769
770         lat1=lat2;
771         lon1=lon2;
772
773         gtk_list_store_append(store, &iter);
774         gtk_list_store_set(store, &iter,
775         ROUTE_LATLON, buffer1,
776                 ROUTE_DISTANCE, buffer2,
777                 ROUTE_WAYPOINT, wcurr->desc,
778                 -1);
779
780         wcurr++;
781 }
782
783 return store;
784 }