]> err.no Git - mapper/blob - src/route.c
Add header and cast properly.
[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         route_deinit();
76         route_init();
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->data.lat);
142                 g_ascii_formatd(strlon, 32, "%.06f", _gps->data.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->data.lat);
222 g_ascii_dtostr(lonstr, 32, _gps->data.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->point && 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 && wnear->point) {
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) < pixel2unit(3 * _draw_width) &&
507                         abs(unity - wnear->point->unity) < pixel2unit(3 * _draw_width))
508                 return wnear;
509 }
510
511 MACRO_BANNER_SHOW_INFO(_window, _("There are no waypoints."));
512
513 return NULL;
514 }
515
516 /**
517  * Updates _near_point, _next_way, and _next_wpt.  If quick is FALSE (as
518  * it is when this function is called from route_find_nearest_point), then
519  * the entire list (starting from _near_point) is searched.  Otherwise, we
520  * stop searching when we find a point that is farther away.
521  */
522 gboolean 
523 route_update_nears(gboolean quick)
524 {
525 gboolean ret = FALSE;
526 Point *curr, *near;
527 WayPoint *wcurr, *wnext;
528 guint64 near_dist_squared;
529
530 /* If we have waypoints (_next_way != NULL), then determine the "next
531  * waypoint", which is defined as the waypoint after the nearest point,
532  * UNLESS we've passed that waypoint, in which case the waypoint after
533  * that waypoint becomes the "next" waypoint. */
534 if (_next_way) {
535         /* First, set near_dist_squared with the new distance from
536          * _near_point. */
537         near = _near_point;
538         near_dist_squared = DISTANCE_SQUARED(_gps->data, *near);
539
540         /* Now, search _route for a closer point.  If quick is TRUE, then we'll
541          * only search forward, only as long as we keep finding closer points.
542          */
543         for (curr = _near_point; curr++ != _route.tail;) {
544                 if (curr->unity) {
545                         guint dist_squared = DISTANCE_SQUARED(_gps->data, *curr);
546                         if (dist_squared <= near_dist_squared) {
547                                 near = curr;
548                                 near_dist_squared = dist_squared;
549                         } else if (quick)
550                                 break;
551                 }
552         }
553
554         /* Update _near_point. */
555         _near_point = near;
556         _near_point_dist_squared = near_dist_squared;
557
558         for (wnext = wcurr = _next_way; wcurr != _route.wtail; wcurr++) {
559                 if (wcurr->point < near || (wcurr->point == near && quick 
560                                 && (_next_wpt && (DISTANCE_SQUARED(_gps->data, *near) > _next_way_dist_squared
561                                 && DISTANCE_SQUARED(_gps->data, *_next_wpt) < _next_wpt_dist_squared))))
562                     /* Okay, this else if expression warrants explanation.  If the
563                      * nearest track point happens to be a waypoint, then we want to
564                      * check if we have "passed" that waypoint.  To check this, we
565                      * test the distance from _gps to the waypoint and from _gps to
566                      * _next_wpt, and if the former is increasing and the latter is
567                      * decreasing, then we have passed the waypoint, and thus we
568                      * should skip it.  Note that if there is no _next_wpt, then
569                      * there is no next waypoint, so we do not skip it in that case. */
570                         wnext = wcurr + 1;
571                 else
572                         break;
573         }
574
575         if (wnext == _route.wtail && (wnext->point < near || (wnext->point == near && quick
576                                           && (_next_wpt && (DISTANCE_SQUARED (_gps->data, *near) > _next_way_dist_squared
577                                                && DISTANCE_SQUARED(_gps->data, *_next_wpt) < _next_wpt_dist_squared)))))
578         {
579                 _next_way = NULL;
580                 _next_wpt = NULL;
581                 _next_way_dist_squared = -1;
582                 _next_wpt_dist_squared = -1;
583                 ret = TRUE;
584         }
585         /* Only update _next_way (and consequently _next_wpt) if _next_way is
586          * different, and record that fact for return. */
587         else {
588                 if (!quick || _next_way != wnext) {
589                         _next_way = wnext;
590                         _next_wpt = wnext->point;
591                         if (_next_wpt == _route.tail)
592                                 _next_wpt = NULL;
593                         else {
594                                 while (!(++_next_wpt)->unity) {
595                                         if (_next_wpt == _route.tail) {
596                                                 _next_wpt = NULL;
597                                                 break;
598                                         }
599                                 }
600                         }
601                         ret = TRUE;
602                 }
603                 _next_way_dist_squared = DISTANCE_SQUARED(_gps->data, *wnext->point);
604                 if (_next_wpt)
605                         _next_wpt_dist_squared = DISTANCE_SQUARED(_gps->data, *_next_wpt);
606         }
607 }
608 return ret;
609 }
610
611 /**
612  * Reset the _near_point data by searching the entire route for the nearest
613  * route point and waypoint.
614  */
615 void 
616 route_find_nearest_point()
617 {
618 /* Initialize _near_point to first non-zero point. */
619 _near_point = _route.head;
620 while (!_near_point->unity && _near_point != _route.tail)
621         _near_point++;
622
623 /* Initialize _next_way. */
624 if (_route.wtail == _route.whead - 1 || (_autoroute_data.enabled && _route.wtail == _route.whead))
625         _next_way = NULL;
626 else
627         /* We have at least one waypoint. */
628         _next_way = (_autoroute_data.enabled ? _route.whead + 1 : _route.whead);
629
630 _next_way_dist_squared = -1;
631
632 /* Initialize _next_wpt. */
633 _next_wpt = NULL;
634 _next_wpt_dist_squared = -1;
635
636 route_update_nears(FALSE);
637 }
638
639 /**
640  * Show the distance from the current GPS location to the given point,
641  * following the route.  If point is NULL, then the distance is shown to the
642  * next waypoint.
643  */
644 gboolean 
645 route_show_distance_to(Point *point)
646 {
647 gchar buffer[80];
648 gdouble lat1, lon1, lat2, lon2;
649 gdouble sum = 0.0;
650
651 /* If point is NULL, use the next waypoint. */
652 if (point == NULL && _next_way)
653         point = _next_way->point;
654
655 /* If point is still NULL, return an error. */
656 if (point == NULL)
657         return FALSE;
658
659 unit2latlon(_gps->data.unitx, _gps->data.unity, lat1, lon1);
660 if (point > _near_point) {
661         Point *curr;
662         /* Skip _near_point in case we have already passed it. */
663         for (curr = _near_point + 1; curr <= point; ++curr) {
664                 if (curr->unity) {
665                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
666                         sum += calculate_distance(lat1, lon1, lat2, lon2);
667                         lat1 = lat2;
668                         lon1 = lon2;
669                 }
670         }
671 } else if (point < _near_point) {
672         Point *curr;
673         /* Skip _near_point in case we have already passed it. */
674         for (curr = _near_point - 1; curr >= point; --curr) {
675                 if (curr->unity) {
676                         unit2latlon(curr->unitx, curr->unity, lat2, lon2);
677                         sum += calculate_distance(lat1, lon1, lat2, lon2);
678                         lat1 = lat2;
679                         lon1 = lon2;
680                 }
681         }
682 } else {
683         /* Waypoint _is_ the nearest point. */
684         unit2latlon(_near_point->unitx, _near_point->unity, lat2, lon2);
685         sum += calculate_distance(lat1, lon1, lat2, lon2);
686 }
687
688 g_snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"), sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
689 MACRO_BANNER_SHOW_INFO(_window, buffer);
690
691 return TRUE;
692 }
693
694 void 
695 route_show_distance_to_next()
696 {
697 if (!route_show_distance_to(NULL)) {
698         MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
699 }
700 }
701
702 void
703 route_set_destination_from_last(void)
704 {
705 Point *p;
706 gdouble lat,lon;
707 if (_route.head == _route.tail)
708         return;
709
710 /* Find last non-zero point. */
711 for (p = _route.tail; !p->unity; p--) {
712 }
713 unit2latlon(p->unitx, p->unity, lat, lon);
714 _dest.valid=TRUE;
715 _dest.lat=lat;
716 _dest.lon=lon;
717 }
718
719 void 
720 route_show_distance_to_last(void)
721 {
722 Point *p;
723
724 if (_route.head != _route.tail) {
725         /* Find last non-zero point. */
726         for (p = _route.tail; !p->unity; p--) {
727         }
728         route_show_distance_to(p);
729 } else {
730         MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));
731         }
732 }
733
734 /***/
735