13 #include <glib/gstdio.h>
16 #include <libgnomevfs/gnome-vfs.h>
17 #include <curl/multi.h>
22 #include "ui-common.h"
24 #include "mapper-types.h"
29 #include "map-download.h"
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)))
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);
43 typedef struct _OriginToggleInfo OriginToggleInfo;
44 struct _OriginToggleInfo {
45 GtkWidget *rad_use_gps;
46 GtkWidget *rad_use_route;
47 GtkWidget *rad_use_text;
56 memset(&_route, 0, sizeof(_route));
57 MACRO_PATH_INIT(_route);
63 MACRO_PATH_FREE(_route);
71 confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), _("Really clear the route?"));
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();
81 gtk_widget_destroy(confirm);
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);
95 MACRO_BANNER_SHOW_INFO(_window, _("Route Opened"));
96 /* Find the nearest route point, if we're connected. */
97 route_find_nearest_point();
99 route_set_destination_from_last();
102 popup_error(_window, _("Error parsing GPX file."));
111 * Ask user to save route
116 GnomeVFSHandle *handle;
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"));
122 popup_error(_window, _("Error writing GPX file."));
124 gnome_vfs_close(handle);
134 origin_type_selected_cb(GtkWidget * toggle, OriginToggleInfo * oti)
136 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))) {
137 if (toggle == oti->rad_use_gps) {
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) {
152 /* Use last non-zero route point. */
153 for (p = _route.tail; !p->unity; p--) {
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);
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);
169 * Cancel the current auto-route.
172 cancel_autoroute(gboolean temporary)
174 if (_autoroute_data.enabled) {
176 _autoroute_data.enabled = FALSE;
178 g_free(_autoroute_data.dest);
179 _autoroute_data.dest = NULL;
181 g_free(_autoroute_data.src_str);
182 _autoroute_data.src_str = NULL;
185 if (_autoroute_data.curl_easy) {
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;
192 g_free(_autoroute_data.rdl_data.bytes);
193 _autoroute_data.rdl_data.bytes = NULL;
194 _autoroute_data.rdl_data.bytes_read = 0;
196 _autoroute_data.in_progress = FALSE;
201 * Read the data provided by the given handle as GPX data, updating the
202 * auto-route with that data.
205 route_dl_cb_read(void *ptr, size_t size, size_t nmemb, RouteDownloadData * rdl_data)
207 size_t old_size = rdl_data->bytes_read;
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);
213 return (size * nmemb);
219 gchar latstr[32], lonstr[32], *latlonstr;
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);
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);
233 /* Initialize CURL. */
234 _curl_multi = curl_multi_init();
235 /*curl_multi_setopt(_curl_multi, CURLMOPT_PIPELINING, 1); */
237 curl_multi_add_handle(_curl_multi, _autoroute_data.curl_easy);
239 if (iap_is_connected() && !_curl_sid)
240 _curl_sid = g_timeout_add(100, (GSourceFunc) map_download_timeout, NULL);
242 _autoroute_data.in_progress = TRUE;
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.
258 route_download(gchar * to)
263 GtkWidget *txt_source_url;
265 OriginToggleInfo oti;
266 GtkEntryCompletion *from_comp;
267 GtkEntryCompletion *to_comp;
271 dialog = gtk_dialog_new_with_buttons(_("Download Route"),
273 GTK_DIALOG_MODAL, GTK_STOCK_OK,
276 GTK_RESPONSE_REJECT, NULL);
278 help_dialog_help_enable(GTK_DIALOG(dialog), HELP_ID_DOWNROUTE);
280 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
281 table = gtk_table_new(2, 5, FALSE), TRUE, TRUE, 0);
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);
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);
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")),
303 gtk_box_pack_start(GTK_BOX(hbox), oti.chk_auto =
304 gtk_check_button_new_with_label(_("Auto-Update")),
306 gtk_widget_set_sensitive(oti.chk_auto, FALSE);
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),
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);
320 gtk_table_attach(GTK_TABLE(table),
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,
328 gtk_entry_set_completion(GTK_ENTRY(oti.txt_from), from_comp);
329 gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_from), 25);
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);
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);
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);
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 : ""));
354 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
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);
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);
368 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
369 gtk_widget_grab_focus(oti.txt_from);
372 gtk_widget_show_all(dialog);
374 while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
376 RouteDownloadData rdl_data = { 0, 0 };
377 gchar buffer[BUFFER_SIZE];
378 const gchar *source_url, *from, *to;
379 gchar *from_escaped, *to_escaped;
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."));
386 g_free(_route_dl_url);
387 _route_dl_url = g_strdup(source_url);
390 from = gtk_entry_get_text(GTK_ENTRY(oti.txt_from));
392 popup_error(dialog, _("Please specify a start location."));
396 to = gtk_entry_get_text(GTK_ENTRY(oti.txt_to));
398 popup_error(dialog, _("Please specify an end location."));
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);
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 */
420 curl_easy_cleanup(curl_easy);
422 if (strncmp(rdl_data.bytes, "<?xml", strlen("<?xml"))) {
423 /* Not an XML document - must be bad locations. */
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. */
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))) {
436 /* Find the nearest route point, if we're connected. */
437 route_find_nearest_point();
439 /* Cancel any autoroute that might be occurring. */
440 cancel_autoroute(FALSE);
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;
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);
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);
464 MACRO_BANNER_SHOW_INFO(_window, _("Route Downloaded"));
465 g_free(rdl_data.bytes);
466 route_set_destination_from_last();
468 /* Success! Get out of the while loop. */
471 popup_error(dialog, _("Error parsing GPX file."));
472 g_free(rdl_data.bytes);
473 /* Let them try again. */
477 gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */
483 find_nearest_waypoint(guint unitx, guint unity)
487 guint64 nearest_squared;
488 Point pos = { unitx, unity, 0, NAN };
490 wcurr = wnear = _route.whead;
491 if (wcurr && wcurr != _route.wtail) {
492 nearest_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
494 while (wcurr++ != _route.wtail) {
495 guint64 test_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
496 if (test_squared < nearest_squared) {
498 nearest_squared = test_squared;
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))
513 MACRO_BANNER_SHOW_INFO(_window, _("There are no waypoints."));
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.
525 route_update_nears(gboolean quick)
527 gboolean ret = FALSE;
529 WayPoint *wcurr, *wnext;
530 guint64 near_dist_squared;
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. */
537 /* First, set near_dist_squared with the new distance from
540 near_dist_squared = DISTANCE_SQUARED(*_gps, *near);
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.
545 for (curr = _near_point; curr++ != _route.tail;) {
547 guint dist_squared = DISTANCE_SQUARED(*_gps, *curr);
548 if (dist_squared <= near_dist_squared) {
550 near_dist_squared = dist_squared;
556 /* Update _near_point. */
558 _near_point_dist_squared = near_dist_squared;
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. */
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)))))
583 _next_way_dist_squared = -1;
584 _next_wpt_dist_squared = -1;
587 /* Only update _next_way (and consequently _next_wpt) if _next_way is
588 * different, and record that fact for return. */
590 if (!quick || _next_way != wnext) {
592 _next_wpt = wnext->point;
593 if (_next_wpt == _route.tail)
596 while (!(++_next_wpt)->unity) {
597 if (_next_wpt == _route.tail) {
605 _next_way_dist_squared = DISTANCE_SQUARED(*_gps, *wnext->point);
607 _next_wpt_dist_squared = DISTANCE_SQUARED(*_gps, *_next_wpt);
614 * Reset the _near_point data by searching the entire route for the nearest
615 * route point and waypoint.
618 route_find_nearest_point()
620 /* Initialize _near_point to first non-zero point. */
621 _near_point = _route.head;
622 while (!_near_point->unity && _near_point != _route.tail)
625 /* Initialize _next_way. */
626 if (_route.wtail == _route.whead - 1 || (_autoroute_data.enabled && _route.wtail == _route.whead))
629 /* We have at least one waypoint. */
630 _next_way = (_autoroute_data.enabled ? _route.whead + 1 : _route.whead);
632 _next_way_dist_squared = -1;
634 /* Initialize _next_wpt. */
636 _next_wpt_dist_squared = -1;
638 route_update_nears(FALSE);
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
647 route_show_distance_to(Point *point)
650 gdouble lat1, lon1, lat2, lon2;
653 /* If point is NULL, use the next waypoint. */
654 if (point == NULL && _next_way)
655 point = _next_way->point;
657 /* If point is still NULL, return an error. */
661 unit2latlon(_gps->unitx, _gps->unity, lat1, lon1);
662 if (point > _near_point) {
664 /* Skip _near_point in case we have already passed it. */
665 for (curr = _near_point + 1; curr <= point; ++curr) {
667 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
668 sum += calculate_distance(lat1, lon1, lat2, lon2);
673 } else if (point < _near_point) {
675 /* Skip _near_point in case we have already passed it. */
676 for (curr = _near_point - 1; curr >= point; --curr) {
678 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
679 sum += calculate_distance(lat1, lon1, lat2, lon2);
685 /* Waypoint _is_ the nearest point. */
686 unit2latlon(_near_point->unitx, _near_point->unity, lat2, lon2);
687 sum += calculate_distance(lat1, lon1, lat2, lon2);
690 g_snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"), sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
691 MACRO_BANNER_SHOW_INFO(_window, buffer);
697 route_show_distance_to_next()
699 if (!route_show_distance_to(NULL)) {
700 MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
705 route_set_destination_from_last(void)
709 if (_route.head == _route.tail)
712 /* Find last non-zero point. */
713 for (p = _route.tail; !p->unity; p--) {
715 unit2latlon(p->unitx, p->unity, lat, lon);
722 route_show_distance_to_last(void)
726 if (_route.head != _route.tail) {
727 /* Find last non-zero point. */
728 for (p = _route.tail; !p->unity; p--) {
730 route_show_distance_to(p);
732 MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));
739 route_generate_store(Path *route)
746 gdouble lat1, lon1, lat2, lon2;
749 if (route->whead==route->wtail)
757 unit2latlon(wcurr->point->unitx, wcurr->point->unity, lat1, lon1);
759 store = gtk_list_store_new(ROUTE_NUM_COLUMNS,G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
761 while (wcurr!=route->wtail) {
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]);
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,