~foxtrotgps-team/foxtrotgps/trunk

« back to all changes in this revision

Viewing changes to src/route.c

  • Committer: Joshua Judson Rosen
  • Date: 2010-08-08 21:26:27 UTC
  • Revision ID: rozzin@geekspace.com-20100808212627-33l17je2wcdeza2n
Install & distribute the foxtrotgps(1) man page.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#include <stdio.h>
2
 
#include <stdlib.h>
3
 
#include <string.h>
4
 
#include <time.h>
5
 
#include <errno.h>
6
 
#include <math.h>
7
 
 
8
 
#include <gtk/gtk.h>
9
 
#include <glib.h>
10
 
#include <glib/gstdio.h>
11
 
 
12
 
#include <sys/types.h>
13
 
#include <sys/stat.h>
14
 
#include <fcntl.h>
15
 
 
16
 
#include <libxml/parser.h>
17
 
#include <libxml/tree.h>
18
 
#include <libxml/encoding.h>
19
 
#include <libxml/xmlwriter.h>
20
 
 
21
 
#include "globals.h"
22
 
#include "tile_management.h"
23
 
#include "route.h"
24
 
#include "wp.h"
25
 
#include "interface.h"
26
 
#include "support.h"
27
 
#include "converter.h"
28
 
#include "map_management.h"
29
 
#include "util.h"
30
 
#include "tracks.h"
31
 
 
32
 
#define GPX_ENCODING "utf-8"
33
 
 
34
 
/**
35
 
 * This is a GSList containing the waypoints of the route
36
 
 * which we are planing. It is the central data structure for route.c
37
 
 */
38
 
static GSList *route = NULL;
39
 
 
40
 
static GdkPixbuf        *wp_icon = NULL;
41
 
static int              wp_icon_width = 0;
42
 
static int              wp_icon_height = 0;
43
 
 
44
 
/**
45
 
 * Clear the route.
46
 
 */
47
 
void
48
 
reset_route ()
49
 
{
50
 
        route = NULL;
51
 
}
52
 
 
53
 
/**
54
 
 * Append a new waypoint at the end of the route.
55
 
 */
56
 
void
57
 
append_waypoint_to_route (double lat, double lon)
58
 
{
59
 
        waypoint_t *tp = g_new0 (waypoint_t,1);
60
 
 
61
 
        tp->lat = lat;
62
 
        tp->lon = lon;
63
 
 
64
 
        route = g_slist_append (route, tp);
65
 
}
66
 
 
67
 
/**
68
 
 * Change the position of the given waypoint to the new position.
69
 
 */
70
 
void
71
 
change_waypoint_of_route (waypoint_t *tp, double lat, double lon)
72
 
{
73
 
        tp->lat = lat;
74
 
        tp->lon = lon;
75
 
}
76
 
 
77
 
/**
78
 
 * Delete the given waypoint from the route.
79
 
 */
80
 
void
81
 
delete_waypoint_of_route (waypoint_t *wp)
82
 
{
83
 
        route = g_slist_remove (route, wp);
84
 
        g_free (wp);
85
 
}
86
 
 
87
 
/**
88
 
 * Insert a new waypoint before the given waypoint. This waypoint lies
89
 
 * between the given waypoint and the waypoint before this waypoint.
90
 
 */
91
 
void
92
 
insert_waypoint_before_of_route (waypoint_t *wp)
93
 
{
94
 
        waypoint_t *previous_wp;
95
 
        int position;
96
 
 
97
 
        position = g_slist_index (route, wp);
98
 
        if (position > 0) {
99
 
                previous_wp = g_slist_nth_data (route, position - 1);
100
 
 
101
 
                waypoint_t *new_wp = g_new0 (waypoint_t,1);
102
 
 
103
 
                new_wp->lat = previous_wp->lat - (previous_wp->lat - wp->lat) / 2;
104
 
                new_wp->lon = previous_wp->lon - (previous_wp->lon - wp->lon) / 2;
105
 
 
106
 
                route = g_slist_insert (route, new_wp, position + 0);
107
 
        }
108
 
}
109
 
 
110
 
/**
111
 
 * Find the waypoint which wp_icon is at the given mouse position.
112
 
 * Return that waypoint or NULL if none was found.
113
 
 */
114
 
waypoint_t *
115
 
find_routepoint (int mouse_x, int mouse_y)
116
 
{
117
 
        GSList *list;
118
 
        double lat;
119
 
        double lon;
120
 
        int pixel_x, pixel_y, x,y;
121
 
 
122
 
        for (list = route; list != NULL; list = list->next)
123
 
        {
124
 
                waypoint_t *tp = list->data;
125
 
 
126
 
                lat = tp->lat;
127
 
                lon = tp->lon;
128
 
 
129
 
                pixel_x = lon2pixel (global_zoom, lon);
130
 
                pixel_y = lat2pixel (global_zoom, lat);
131
 
 
132
 
                x = pixel_x - global_x + wp_icon_width / 2;
133
 
                y = pixel_y - global_y - wp_icon_height / 2;
134
 
 
135
 
                if (abs (x - mouse_x) < wp_icon_width / 2 &&
136
 
                    abs (y - mouse_y) < wp_icon_height / 2)
137
 
                {
138
 
                        return tp;
139
 
                }
140
 
        }
141
 
 
142
 
        return NULL;
143
 
}
144
 
 
145
 
static
146
 
void
147
 
draw_arrow (GdkGC *gc, int start_x, int start_y, int end_x, int end_y)
148
 
{
149
 
        int x1,y1,x2,y2;
150
 
        double angle = atan2 (end_y - start_y, end_x - start_x) + M_PI;
151
 
        double arrow_length = 20;
152
 
        double arrow_degrees = M_PI / 10;
153
 
 
154
 
        /* https://kapo-cpp.blogspot.com/2008/10/drawing-arrows-with-cairo.html */
155
 
 
156
 
        x1 = end_x + arrow_length * cos (angle - arrow_degrees);
157
 
        y1 = end_y + arrow_length * sin (angle - arrow_degrees);
158
 
        x2 = end_x + arrow_length * cos (angle + arrow_degrees);
159
 
        y2 = end_y + arrow_length * sin (angle + arrow_degrees);
160
 
 
161
 
        gdk_draw_line (pixmap, gc, start_x, start_y, end_x, end_y);
162
 
        gdk_draw_line (pixmap, gc, end_x, end_y, x1, y1);
163
 
        gdk_draw_line (pixmap, gc, x1, y1, x2, y2);
164
 
        gdk_draw_line (pixmap, gc, x2, y2, end_x, end_y);
165
 
}
166
 
 
167
 
static
168
 
void
169
 
draw_line_of_route (GdkGC *gc, int x1, int y1, int x2, int y2)
170
 
{
171
 
        if (abs (x1-x2) > 30 || abs (y1-y2) > 30) {
172
 
                /* Line is long enough. Draw arrow */
173
 
                draw_arrow (gc, x1, y1, x2, y2);
174
 
        } else {
175
 
                /* short line. Omit arrow. */
176
 
                gdk_draw_line (pixmap, gc, x1, y1, x2, y2);
177
 
        }
178
 
}
179
 
 
180
 
/**
181
 
 * This function draws the current route on the screen.
182
 
 */
183
 
void
184
 
paint_route ()
185
 
{
186
 
        GSList *list;
187
 
        int pixel_x, pixel_y, x,y, last_x = 0, last_y = 0;
188
 
        float lat, lon;
189
 
        GdkColor color;
190
 
        GdkGC *gc;
191
 
        gboolean is_line = FALSE;
192
 
 
193
 
        /* Load icon if not already loaded: */
194
 
        if (!wp_icon) {
195
 
                wp_icon        = load_wp_icon ();
196
 
                wp_icon_width  = gdk_pixbuf_get_width (wp_icon);
197
 
                wp_icon_height = gdk_pixbuf_get_height (wp_icon);
198
 
        }
199
 
 
200
 
        /* Create GC for drawing the route line */
201
 
        gc = gdk_gc_new (pixmap);
202
 
        color.green = 0;
203
 
        color.blue = 0;
204
 
        color.red = 50000;
205
 
        gdk_gc_set_rgb_fg_color (gc, &color);
206
 
        gdk_gc_set_line_attributes (gc, 5, GDK_LINE_SOLID,
207
 
                                    GDK_CAP_ROUND, GDK_JOIN_ROUND);
208
 
 
209
 
        /* [1] paint line first */
210
 
        for (list = route; list != NULL; list = list->next)
211
 
        {
212
 
                waypoint_t *tp = list->data;
213
 
 
214
 
                lat = tp->lat;
215
 
                lon = tp->lon;
216
 
 
217
 
                pixel_x = lon2pixel (global_zoom, lon);
218
 
                pixel_y = lat2pixel (global_zoom, lat);
219
 
 
220
 
                x = pixel_x - global_x;
221
 
                y = pixel_y - global_y;
222
 
 
223
 
                if (is_line)
224
 
                {
225
 
                        draw_line_of_route (gc, last_x, last_y, x, y);
226
 
                        gtk_widget_queue_draw_area (map_drawable,
227
 
                                                    x-4, y-4, 8, 8);
228
 
                }
229
 
 
230
 
                last_x = x;
231
 
                last_y = y;
232
 
 
233
 
                is_line = TRUE;
234
 
        }
235
 
 
236
 
        /* [2] paint flags after line */
237
 
        for (list = route; list != NULL; list = list->next)
238
 
        {
239
 
                waypoint_t *tp = list->data;
240
 
 
241
 
                lat = tp->lat;
242
 
                lon = tp->lon;
243
 
 
244
 
                pixel_x = lon2pixel (global_zoom, lon);
245
 
                pixel_y = lat2pixel (global_zoom, lat);
246
 
 
247
 
                x = pixel_x - global_x;
248
 
                y = pixel_y - global_y;
249
 
 
250
 
                if (!wp_icon) {
251
 
                        gdk_draw_arc (pixmap,
252
 
                                      gc,
253
 
                                      TRUE,
254
 
                                      x-4, y-4,
255
 
                                      8,8,
256
 
                                      0,23040);
257
 
                } else {
258
 
                        gdk_draw_pixbuf (pixmap,
259
 
                                         NULL,
260
 
                                         wp_icon,
261
 
                                         0,0,
262
 
                                         x,y-wp_icon_height,
263
 
                                         wp_icon_width,wp_icon_height,
264
 
                                         GDK_RGB_DITHER_NONE, 0, 0);
265
 
 
266
 
                        gtk_widget_queue_draw_area (map_drawable,
267
 
                                                    x, y-wp_icon_height,
268
 
                                                    wp_icon_width,
269
 
                                                    wp_icon_height);
270
 
                }
271
 
        }
272
 
}
273
 
 
274
 
/**
275
 
 * Find the bounding box of the given GSList containin waypoints.
276
 
 */
277
 
bbox_t
278
 
get_way_bbox (GSList *ways)
279
 
{
280
 
        GSList *list;
281
 
        bbox_t bbox;
282
 
        double lat, lon;
283
 
 
284
 
        bbox.lat1 =  -90;
285
 
        bbox.lon1 =  180;
286
 
        bbox.lat2 =   90;
287
 
        bbox.lon2 = -180;
288
 
 
289
 
        for (list = ways; list != NULL; list = list->next)
290
 
        {
291
 
                waypoint_t *tp = list->data;
292
 
 
293
 
                lat = tp->lat;
294
 
                lon = tp->lon;
295
 
                bbox.lat1 = (lat > bbox.lat1) ? lat : bbox.lat1;
296
 
                bbox.lat2 = (lat < bbox.lat2) ? lat : bbox.lat2;
297
 
                bbox.lon1 = (lon < bbox.lon1) ? lon : bbox.lon1;
298
 
                bbox.lon2 = (lon > bbox.lon2) ? lon : bbox.lon2;
299
 
        }
300
 
 
301
 
        return bbox;
302
 
}
303
 
 
304
 
/**
305
 
 * Write a single route point to the XML file in GPX format.
306
 
 */
307
 
static
308
 
void
309
 
write_rtept (xmlTextWriterPtr writer, waypoint_t *wp, int no)
310
 
{
311
 
        xmlTextWriterStartElement (writer, BAD_CAST "rtept");
312
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "lat",
313
 
                                           "%f", rad2deg (wp->lat));
314
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "lon",
315
 
                                           "%f", rad2deg (wp->lon));
316
 
        xmlTextWriterEndElement (writer); /* </rtept> */
317
 
}
318
 
 
319
 
/**
320
 
 * Save the route in GPX format to the given URI.
321
 
 */
322
 
void
323
 
save_route_as_gpx (const char *uri)
324
 
{
325
 
        int rc;
326
 
        xmlTextWriterPtr writer;
327
 
        int no;
328
 
        GSList *list;
329
 
        char now_as_string[200];
330
 
        struct tm now_as_tm;
331
 
        time_t now;
332
 
 
333
 
        if (uri == NULL) return;
334
 
 
335
 
        bbox_t bbox = get_way_bbox (route);
336
 
 
337
 
        /*
338
 
         * this initialize the library and check potential ABI mismatches
339
 
         * between the version it was compiled for and the actual shared
340
 
         * library used.
341
 
         */
342
 
        LIBXML_TEST_VERSION
343
 
 
344
 
        /* Create a new XmlWriter for uri, with no compression. */
345
 
        writer = xmlNewTextWriterFilename (uri, 0);
346
 
        if (writer == NULL) {
347
 
                printf ("testXmlwriterFilename: Error creating the xml writer\n");
348
 
                return;
349
 
        }
350
 
 
351
 
        /* We would like to indent the elements for better readability. */
352
 
        xmlTextWriterSetIndent (writer, 1);
353
 
 
354
 
        /* Start the document with the xml default for the version,
355
 
         * encoding and the default for the standalone
356
 
         * declaration. */
357
 
        rc = xmlTextWriterStartDocument (writer, "1.0", GPX_ENCODING, "no");
358
 
        if (rc < 0) {
359
 
                printf ("testXmlwriterFilename: Error at xmlTextWriterStartDocument\n");
360
 
                return;
361
 
        }
362
 
 
363
 
        xmlTextWriterStartElement (writer, BAD_CAST "gpx");
364
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "version",
365
 
                                             BAD_CAST "1.1");
366
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "creator",
367
 
                                             BAD_CAST PACKAGE_STRING);
368
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "xmlns:xsi",
369
 
                                             BAD_CAST "http://www.w3.org/2001/XMLSchema-instance");
370
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "xmlns:topografix",
371
 
                                             BAD_CAST "http://www.topografix.com/GPX/Private/TopoGrafix/0/1");
372
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "xmlns",
373
 
                                             BAD_CAST "http://www.topografix.com/GPX/1/1");
374
 
        xmlTextWriterWriteAttribute (writer, BAD_CAST "xsi:schemaLocation",
375
 
                                             BAD_CAST "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd");
376
 
 
377
 
        xmlTextWriterStartElement (writer, BAD_CAST "metadata");
378
 
 
379
 
        /* save current time into GPX file: */
380
 
        time (&now);
381
 
        gmtime_r (&now, &now_as_tm);
382
 
        strftime (now_as_string, sizeof (now_as_string), "%Y-%m-%dT%H:%M:%SZ",
383
 
                  &now_as_tm);
384
 
        xmlTextWriterWriteElement (writer, BAD_CAST "time",
385
 
                                           BAD_CAST now_as_string);
386
 
 
387
 
        xmlTextWriterStartElement (writer, BAD_CAST "bounds");
388
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "minlat",
389
 
                                           "%f", rad2deg (bbox.lat2));
390
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "minlon",
391
 
                                           "%f", rad2deg (bbox.lon1));
392
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "maxlat",
393
 
                                           "%f", rad2deg (bbox.lat1));
394
 
        xmlTextWriterWriteFormatAttribute (writer, BAD_CAST "maxlon",
395
 
                                           "%f", rad2deg (bbox.lon2));
396
 
        xmlTextWriterEndElement (writer); /* </bounds> */
397
 
 
398
 
        xmlTextWriterEndElement (writer); /* </metadata> */
399
 
 
400
 
        xmlTextWriterStartElement (writer, BAD_CAST "rte");
401
 
 
402
 
        for (list = route, no = 1; list != NULL; list = list->next, no++)
403
 
        {
404
 
                waypoint_t *wp = list->data;
405
 
                write_rtept (writer, wp, no);
406
 
        }
407
 
 
408
 
        xmlTextWriterEndElement (writer); /* </rte> */
409
 
        xmlTextWriterEndElement (writer); /* </gpx> */
410
 
 
411
 
        xmlTextWriterEndDocument (writer);
412
 
 
413
 
        xmlFreeTextWriter (writer);
414
 
}
415
 
 
416
 
/**
417
 
 * Save a route to a TomTom ITN file format.
418
 
 */
419
 
void
420
 
save_route_as_tomtom_itn (const char *uri)
421
 
{
422
 
        FILE *tomtom;
423
 
        int no;
424
 
        GSList *list;
425
 
 
426
 
        if (uri == NULL) return;
427
 
 
428
 
        tomtom = fopen (uri, "w");
429
 
        if (tomtom == NULL) {
430
 
                perror ("opening file to save tomtom.");
431
 
        } else {
432
 
                for (list = route, no = 1;
433
 
                     list != NULL;
434
 
                     list = list->next, no++)
435
 
                {
436
 
                        waypoint_t *wp = list->data;
437
 
                        fprintf (tomtom, "%d|%d|WP%d|0\n",
438
 
                                 (int) (rad2deg (wp->lon) * 100000),
439
 
                                 (int) (rad2deg (wp->lat) * 100000),
440
 
                                 no);
441
 
                }
442
 
 
443
 
                fclose (tomtom);
444
 
        }
445
 
}
446
 
 
447
 
/**
448
 
 * Take all routepoints from a DOM tree containing GPX nodes.
449
 
 */
450
 
static
451
 
GSList *
452
 
parse_gpx_routepoints (xmlNode *node)
453
 
{
454
 
        xmlNode *cur_node = NULL;
455
 
        GSList *list = NULL;
456
 
 
457
 
        for (cur_node = node; cur_node; cur_node = cur_node->next)
458
 
        {
459
 
                if (cur_node->type == XML_ELEMENT_NODE)
460
 
                {
461
 
                        if (xmlStrEqual (cur_node->name, BAD_CAST "rtept"))
462
 
                        {
463
 
                                double lat, lon;
464
 
                                waypoint_t *tp = g_new0 (waypoint_t, 1);
465
 
 
466
 
                                lat = atof ((char *) xmlGetProp (cur_node,
467
 
                                                                 BAD_CAST "lat"));
468
 
                                lon = atof ((char *) xmlGetProp (cur_node,
469
 
                                                                 BAD_CAST "lon"));
470
 
 
471
 
                                tp->lat = deg2rad (lat);
472
 
                                tp->lon = deg2rad (lon);
473
 
 
474
 
                                list = g_slist_append (list, tp);
475
 
                        }
476
 
                }
477
 
                list = g_slist_concat (list,
478
 
                                       parse_gpx_routepoints (cur_node->children));
479
 
        }
480
 
 
481
 
        return list;
482
 
}
483
 
 
484
 
/**
485
 
 * Load a route from a given GPX file.
486
 
 */
487
 
GSList *
488
 
load_route_as_gpx (const char *file)
489
 
{
490
 
        GSList *list = NULL;
491
 
        xmlDoc *doc = NULL;
492
 
        xmlNode *root_element = NULL;
493
 
 
494
 
        LIBXML_TEST_VERSION
495
 
 
496
 
        doc = xmlReadFile (file, NULL, 0);
497
 
        if (doc == NULL)
498
 
        {
499
 
                printf ("error: could not parse file %s\n", file);
500
 
        }
501
 
 
502
 
        root_element = xmlDocGetRootElement (doc);
503
 
        list = parse_gpx_routepoints (root_element);
504
 
        xmlFreeDoc (doc);
505
 
        xmlCleanupParser ();
506
 
 
507
 
        return list;
508
 
}
509
 
 
510
 
/**
511
 
 * Load a route from a file with a supported file format.
512
 
 */
513
 
void
514
 
load_route (const char *filename)
515
 
{
516
 
        bbox_t bbox;
517
 
 
518
 
        if (filename == NULL) return;
519
 
 
520
 
        route = load_route_as_gpx (filename);
521
 
        if (route != NULL)
522
 
        {
523
 
                bbox = get_way_bbox (route);
524
 
                show_bbox (bbox);
525
 
        }
526
 
}
527
 
 
528
 
 
529
 
char *
530
 
choose_save_file (char *currentName)
531
 
{
532
 
        char *filename = NULL;
533
 
        GtkWidget *dialog;
534
 
        dialog = gtk_file_chooser_dialog_new ("Save File",
535
 
                                              NULL,
536
 
                                              GTK_FILE_CHOOSER_ACTION_SAVE,
537
 
                                              GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
538
 
                                              GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
539
 
                                              NULL);
540
 
        gtk_file_chooser_set_do_overwrite_confirmation
541
 
                (GTK_FILE_CHOOSER (dialog), TRUE);
542
 
        gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog),
543
 
                                           currentName);
544
 
 
545
 
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
546
 
        {
547
 
                filename = gtk_file_chooser_get_filename
548
 
                        (GTK_FILE_CHOOSER (dialog));
549
 
        }
550
 
 
551
 
        gtk_widget_destroy (dialog);
552
 
 
553
 
        return filename;
554
 
}
555
 
 
556
 
char *
557
 
choose_load_file ()
558
 
{
559
 
        char *filename = NULL;
560
 
        GtkWidget *dialog;
561
 
        dialog = gtk_file_chooser_dialog_new ("Open File", NULL,
562
 
                                              GTK_FILE_CHOOSER_ACTION_OPEN,
563
 
                                              GTK_STOCK_CANCEL,
564
 
                                              GTK_RESPONSE_CANCEL,
565
 
                                              GTK_STOCK_OPEN,
566
 
                                              GTK_RESPONSE_ACCEPT,
567
 
                                              NULL);
568
 
 
569
 
        if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_ACCEPT)
570
 
        {
571
 
                filename = gtk_file_chooser_get_filename
572
 
                        (GTK_FILE_CHOOSER (dialog));
573
 
        }
574
 
        gtk_widget_destroy (dialog);
575
 
 
576
 
        return filename;
577
 
}