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