e40f75022d5349681fb0b06345ea4c1689cb4b5f
[routino] / src / output.c
1 /***************************************
2  $Header: /home/amb/routino/src/RCS/output.c,v 1.33 2010/07/07 17:31:06 amb Exp $
3
4  Routing output generator.
5
6  Part of the Routino routing software.
7  ******************/ /******************
8  This file Copyright 2008-2010 Andrew M. Bishop
9
10  This program is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Affero General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU Affero General Public License for more details.
19
20  You should have received a copy of the GNU Affero General Public License
21  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  ***************************************/
23
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <stdio.h>
29 #include <math.h>
30 #include <errno.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34
35 #include "types.h"
36 #include "functions.h"
37 #include "translations.h"
38 #include "nodes.h"
39 #include "segments.h"
40 #include "ways.h"
41 #include "results.h"
42 #include "xmlparse.h"
43
44
45 /* Global variables */
46
47 /*+ The option to calculate the quickest route insted of the shortest. +*/
48 extern int option_quickest;
49
50 /*+ The options to select the format of the output. +*/
51 extern int option_html,option_gpx_track,option_gpx_route,option_text,option_text_all;
52
53 /* Local variables */
54
55 /*+ Heuristics for determining if a junction is important. +*/
56 static char junction_other_way[Way_Count][Way_Count]=
57  { /* M, T, P, S, T, U, R, S, T, C, P, S = Way type of route not taken */
58   {   1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Motorway     */
59   {   1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Trunk        */
60   {   1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Primary      */
61   {   1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 }, /* Secondary    */
62   {   1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 }, /* Tertiary     */
63   {   1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0 }, /* Unclassified */
64   {   1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 }, /* Residential  */
65   {   1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0 }, /* Service      */
66   {   1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 }, /* Track        */
67   {   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0 }, /* Cycleway     */
68   {   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* Path         */
69   {   1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }, /* Steps        */
70  };
71
72
73 /* Local functions */
74
75 static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node);
76 static int bearing_angle(Nodes *nodes,Segment *segment,index_t node);
77
78
79 /*++++++++++++++++++++++++++++++++++++++
80   Print the optimum route between two nodes.
81
82   Results **results The set of results to print (some may be NULL - ignore them).
83
84   int nresults The number of results in the list.
85
86   Nodes *nodes The list of nodes.
87
88   Segments *segments The set of segments to use.
89
90   Ways *ways The list of ways.
91
92   Profile *profile The profile containing the transport type, speeds and allowed highways.
93   ++++++++++++++++++++++++++++++++++++++*/
94
95 void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile)
96 {
97  FILE *htmlfile=NULL,*gpxtrackfile=NULL,*gpxroutefile=NULL,*textfile=NULL,*textallfile=NULL;
98
99  int point=1;
100  distance_t cum_distance=0;
101  duration_t cum_duration=0;
102  double finish_lat,finish_lon;
103  int segment_count=0;
104  int route_count=0;
105
106  /* Open the files */
107
108  if(option_quickest==0)
109    {
110     /* Print the result for the shortest route */
111
112     if(option_html)
113        htmlfile    =fopen("shortest.html","w");
114     if(option_gpx_track)
115        gpxtrackfile=fopen("shortest-track.gpx","w");
116     if(option_gpx_route)
117        gpxroutefile=fopen("shortest-route.gpx","w");
118     if(option_text)
119        textfile    =fopen("shortest.txt","w");
120     if(option_text_all)
121        textallfile =fopen("shortest-all.txt","w");
122
123     if(option_html && !htmlfile)
124        fprintf(stderr,"Warning: Cannot open file 'shortest.html' for writing [%s].\n",strerror(errno));
125     if(option_gpx_track && !gpxtrackfile)
126        fprintf(stderr,"Warning: Cannot open file 'shortest-track.gpx' for writing [%s].\n",strerror(errno));
127     if(option_gpx_route && !gpxroutefile)
128        fprintf(stderr,"Warning: Cannot open file 'shortest-route.gpx' for writing [%s].\n",strerror(errno));
129     if(option_text && !textfile)
130        fprintf(stderr,"Warning: Cannot open file 'shortest.txt' for writing [%s].\n",strerror(errno));
131     if(option_text_all && !textallfile)
132        fprintf(stderr,"Warning: Cannot open file 'shortest-all.txt' for writing [%s].\n",strerror(errno));
133    }
134  else
135    {
136     /* Print the result for the quickest route */
137
138     if(option_html)
139        htmlfile    =fopen("quickest.html","w");
140     if(option_gpx_track)
141        gpxtrackfile=fopen("quickest-track.gpx","w");
142     if(option_gpx_route)
143        gpxroutefile=fopen("quickest-route.gpx","w");
144     if(option_text)
145        textfile    =fopen("quickest.txt","w");
146     if(option_text_all)
147        textallfile =fopen("quickest-all.txt","w");
148
149     if(option_html && !htmlfile)
150        fprintf(stderr,"Warning: Cannot open file 'quickest.html' for writing [%s].\n",strerror(errno));
151     if(option_gpx_track && !gpxtrackfile)
152        fprintf(stderr,"Warning: Cannot open file 'quickest-track.gpx' for writing [%s].\n",strerror(errno));
153     if(option_gpx_route && !gpxroutefile)
154        fprintf(stderr,"Warning: Cannot open file 'quickest-route.gpx' for writing [%s].\n",strerror(errno));
155     if(option_text && !textfile)
156        fprintf(stderr,"Warning: Cannot open file 'quickest.txt' for writing [%s].\n",strerror(errno));
157     if(option_text_all && !textallfile)
158        fprintf(stderr,"Warning: Cannot open file 'quickest-all.txt' for writing [%s].\n",strerror(errno));
159    }
160
161  /* Print the head of the files */
162
163  if(htmlfile)
164    {
165     fprintf(htmlfile,"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
166     fprintf(htmlfile,"<HTML>\n");
167     if(translate_copyright_creator[0] && translate_copyright_creator[1])
168        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_copyright_creator[0],translate_copyright_creator[1]);
169     if(translate_copyright_source[0] && translate_copyright_source[1])
170        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_copyright_source[0],translate_copyright_source[1]);
171     if(translate_copyright_license[0] && translate_copyright_license[1])
172        fprintf(htmlfile,"<!-- %s : %s -->\n",translate_copyright_license[0],translate_copyright_license[1]);
173     fprintf(htmlfile,"<HEAD>\n");
174     fprintf(htmlfile,"<TITLE>");
175     fprintf(htmlfile,translate_html_title,option_quickest?translate_route_quickest:translate_route_shortest);
176     fprintf(htmlfile,"</TITLE>\n");
177     fprintf(htmlfile,"<STYLE type='text/css'>\n");
178     fprintf(htmlfile,"<!--\n");
179     fprintf(htmlfile,"   table   {table-layout: fixed; border: none; border-collapse: collapse;}\n");
180     fprintf(htmlfile,"   table.c {color: grey; font-size: x-small;} /* copyright */\n");
181     fprintf(htmlfile,"   tr      {border: 0px;}\n");
182     fprintf(htmlfile,"   tr.c    {display: none;} /* coords */\n");
183     fprintf(htmlfile,"   tr.n    {} /* node */\n");
184     fprintf(htmlfile,"   tr.s    {} /* segment */\n");
185     fprintf(htmlfile,"   tr.t    {font-weight: bold;} /* total */\n");
186     fprintf(htmlfile,"   td.l    {font-weight: bold;}\n");
187     fprintf(htmlfile,"   td.r    {}\n");
188     fprintf(htmlfile,"   span.w  {font-weight: bold;} /* waypoint */\n");
189     fprintf(htmlfile,"   span.h  {text-decoration: underline;} /* highway */\n");
190     fprintf(htmlfile,"   span.d  {} /* segment distance */\n");
191     fprintf(htmlfile,"   span.j  {font-style: italic;} /* total journey distance */\n");
192     fprintf(htmlfile,"   span.t  {font-variant: small-caps;} /* turn */\n");
193     fprintf(htmlfile,"   span.b  {font-variant: small-caps;} /* bearing */\n");
194     fprintf(htmlfile,"-->\n");
195     fprintf(htmlfile,"</STYLE>\n");
196     fprintf(htmlfile,"</HEAD>\n");
197     fprintf(htmlfile,"<BODY>\n");
198     fprintf(htmlfile,"<H1>");
199     fprintf(htmlfile,translate_html_title,option_quickest?translate_route_quickest:translate_route_shortest);
200     fprintf(htmlfile,"</H1>\n");
201     fprintf(htmlfile,"<table>\n");
202    }
203
204  if(gpxtrackfile)
205    {
206     fprintf(gpxtrackfile,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
207     fprintf(gpxtrackfile,"<gpx version=\"1.1\" creator=\"Routino\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/1\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n");
208
209     fprintf(gpxtrackfile,"<metadata>\n");
210     fprintf(gpxtrackfile,"<desc>%s : %s</desc>\n",translate_copyright_creator[0],translate_copyright_creator[1]);
211     if(translate_copyright_source[1])
212       {
213        fprintf(gpxtrackfile,"<copyright author=\"%s\">\n",translate_copyright_source[1]);
214
215        if(translate_copyright_license[1])
216           fprintf(gpxtrackfile,"<license>%s</license>\n",translate_copyright_license[1]);
217
218        fprintf(gpxtrackfile,"</copyright>\n");
219       }
220     fprintf(gpxtrackfile,"</metadata>\n");
221
222     fprintf(gpxtrackfile,"<trk>\n");
223     fprintf(gpxtrackfile,"<name>");
224     fprintf(gpxtrackfile,translate_gpx_name,option_quickest?translate_route_quickest:translate_route_shortest);
225     fprintf(gpxtrackfile,"</name>\n");
226     fprintf(gpxtrackfile,"<desc>");
227     fprintf(gpxtrackfile,translate_gpx_desc,option_quickest?translate_route_quickest:translate_route_shortest);
228     fprintf(gpxtrackfile,"</desc>\n");
229    }
230
231  if(gpxroutefile)
232    {
233     fprintf(gpxroutefile,"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
234     fprintf(gpxroutefile,"<gpx version=\"1.1\" creator=\"Routino\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/1\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n");
235
236     fprintf(gpxroutefile,"<metadata>\n");
237     fprintf(gpxroutefile,"<desc>%s : %s</desc>\n",translate_copyright_creator[0],translate_copyright_creator[1]);
238     if(translate_copyright_source[1])
239       {
240        fprintf(gpxroutefile,"<copyright author=\"%s\">\n",translate_copyright_source[1]);
241
242        if(translate_copyright_license[1])
243           fprintf(gpxroutefile,"<license>%s</license>\n",translate_copyright_license[1]);
244
245        fprintf(gpxroutefile,"</copyright>\n");
246       }
247     fprintf(gpxroutefile,"</metadata>\n");
248
249     fprintf(gpxroutefile,"<rte>\n");
250     fprintf(gpxroutefile,"<name>");
251     fprintf(gpxroutefile,translate_gpx_name,option_quickest?translate_route_quickest:translate_route_shortest);
252     fprintf(gpxroutefile,"</name>\n");
253     fprintf(gpxroutefile,"<desc>");
254     fprintf(gpxroutefile,translate_gpx_desc,option_quickest?translate_route_quickest:translate_route_shortest);
255     fprintf(gpxroutefile,"</desc>\n");
256    }
257
258  if(textfile)
259    {
260     if(translate_copyright_creator[0] && translate_copyright_creator[1])
261        fprintf(textfile,"# %s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]);
262     if(translate_copyright_source[0] && translate_copyright_source[1])
263        fprintf(textfile,"# %s : %s\n",translate_copyright_source[0],translate_copyright_source[1]);
264     if(translate_copyright_license[0] && translate_copyright_license[1])
265        fprintf(textfile,"# %s : %s\n",translate_copyright_license[0],translate_copyright_license[1]);
266     if((translate_copyright_creator[0] && translate_copyright_creator[1]) ||
267        (translate_copyright_source[0]  && translate_copyright_source[1]) ||
268        (translate_copyright_license[0] && translate_copyright_license[1]))
269        fprintf(textfile,"#\n");
270
271     fprintf(textfile,"#Latitude\tLongitude\tSection \tSection \tTotal   \tTotal   \tPoint\tTurn\tBearing\tHighway\n");
272     fprintf(textfile,"#        \t         \tDistance\tDuration\tDistance\tDuration\tType \t    \t       \t       \n");
273                      /* "%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t %+d\t %+d\t%s\n" */
274    }
275
276  if(textallfile)
277    {
278     if(translate_copyright_creator[0] && translate_copyright_creator[1])
279        fprintf(textallfile,"# %s : %s\n",translate_copyright_creator[0],translate_copyright_creator[1]);
280     if(translate_copyright_source[0] && translate_copyright_source[1])
281        fprintf(textallfile,"# %s : %s\n",translate_copyright_source[0],translate_copyright_source[1]);
282     if(translate_copyright_license[0] && translate_copyright_license[1])
283        fprintf(textallfile,"# %s : %s\n",translate_copyright_license[0],translate_copyright_license[1]);
284     if((translate_copyright_creator[0] && translate_copyright_creator[1]) ||
285        (translate_copyright_source[0]  && translate_copyright_source[1]) ||
286        (translate_copyright_license[0] && translate_copyright_license[1]))
287        fprintf(textallfile,"#\n");
288
289     fprintf(textallfile,"#Latitude\tLongitude\t    Node\tType\tSegment\tSegment\tTotal\tTotal  \tSpeed\tBearing\tHighway\n");
290     fprintf(textallfile,"#        \t         \t        \t    \tDist   \tDurat'n\tDist \tDurat'n\t     \t       \t       \n");
291                         /* "%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t%3d\t%4d\t%s\n" */
292    }
293
294  /* Loop through the segments of the route and print it */
295
296  while(!results[point])
297     point++;
298
299  while(point<=nresults)
300    {
301     int nextpoint=point;
302     double start_lat,start_lon;
303     distance_t junc_distance=0;
304     duration_t junc_duration=0;
305     Result *result;
306
307     if(gpxtrackfile)
308        fprintf(gpxtrackfile,"<trkseg>\n");
309
310     if(IsFakeNode(results[point]->start))
311        GetFakeLatLong(results[point]->start,&start_lat,&start_lon);
312     else
313        GetLatLong(nodes,results[point]->start,&start_lat,&start_lon);
314
315     if(IsFakeNode(results[point]->finish))
316        GetFakeLatLong(results[point]->finish,&finish_lat,&finish_lon);
317     else
318        GetLatLong(nodes,results[point]->finish,&finish_lat,&finish_lon);
319
320     result=FindResult(results[point],results[point]->start);
321
322     do
323       {
324        double latitude,longitude;
325        Result *nextresult;
326
327        if(result->node==results[point]->start)
328          {latitude=start_lat; longitude=start_lon;}
329        else if(result->node==results[point]->finish)
330          {latitude=finish_lat; longitude=finish_lon;}
331        else
332           GetLatLong(nodes,result->node,&latitude,&longitude);
333
334        if(gpxtrackfile)
335           fprintf(gpxtrackfile,"<trkpt lat=\"%.6f\" lon=\"%.6f\"/>\n",
336                   radians_to_degrees(latitude),radians_to_degrees(longitude));
337
338        nextresult=FindResult(results[point],result->next);
339
340        if(!nextresult)
341           for(nextpoint=point+1;nextpoint<=nresults;nextpoint++)
342              if(results[nextpoint])
343                {
344                 nextresult=FindResult(results[nextpoint],results[nextpoint]->start);
345                 nextresult=FindResult(results[nextpoint],nextresult->next);
346                 break;
347                }
348
349        if(result->node!=results[point]->start)
350          {
351           distance_t seg_distance=0;
352           duration_t seg_duration=0;
353           Way *resultway;
354           int important=0;
355
356           /* Cache the values to be printed rather than calculating them repeatedly for each output format */
357
358           char *waynameraw=NULL,*wayname=NULL,*waynamexml=NULL;
359           int bearing_int=0,bearing_next_int=0,turn_int=0;
360           char *bearing_str=NULL,*bearing_next_str=NULL,*turn_str=NULL;
361
362           /* Get the properties of this segment */
363
364           resultway=LookupWay(ways,result->segment->way);
365
366           seg_distance+=DISTANCE(result->segment->distance);
367           seg_duration+=Duration(result->segment,resultway,profile);
368           junc_distance+=seg_distance;
369           junc_duration+=seg_duration;
370           cum_distance+=seg_distance;
371           cum_duration+=seg_duration;
372
373           /* Decide if this is an important junction */
374
375           if(result->node==results[point]->finish)
376              important=10;
377           else
378             {
379              Segment *segment=FirstSegment(segments,nodes,result->node);
380
381              do
382                {
383                 index_t othernode=OtherNode(segment,result->node);
384
385                 if(othernode!=result->prev && segment!=result->segment)
386                    if(IsNormalSegment(segment) && (!profile->oneway || !IsOnewayTo(segment,result->node)))
387                      {
388                       Way *way=LookupWay(ways,segment->way);
389
390                       if(othernode==result->next) /* the next segment that we follow */
391                         {
392                          if(HIGHWAY(way->type)!=HIGHWAY(resultway->type))
393                             if(important<2)
394                                important=2;
395                         }
396                       else /* a segment that we don't follow */
397                         {
398                          if(junction_other_way[HIGHWAY(resultway->type)-1][HIGHWAY(way->type)-1])
399                             if(important<3)
400                                important=3;
401
402                          if(important<1)
403                             important=1;
404                         }
405                      }
406
407                 segment=NextSegment(segments,segment,result->node);
408                }
409              while(segment);
410             }
411
412           /* Print out the important points (junctions / waypoints) */
413
414           if(important>1)
415             {
416              /* Print the intermediate finish points (because they have correct junction distances) */
417
418              if(htmlfile)
419                {
420                 char *type;
421
422                 if(important==10)
423                    type=translate_html_waypoint;
424                 else
425                    type=translate_html_junction;
426
427                 if(!waynameraw)
428                   {
429                    waynameraw=WayNameRaw(ways,resultway);
430                    if(!*waynameraw)
431                       waynameraw=translate_highway[HIGHWAY(resultway->type)];
432                   }
433
434                 if(!waynamexml)
435                    waynamexml=ParseXML_Encode_Safe_XML(waynameraw);
436
437                 fprintf(htmlfile,"<tr class='s'><td class='l'>%s:<td class='r'>",translate_html_segment[0]);
438                 fprintf(htmlfile,translate_html_segment[1],
439                                   waynamexml,
440                                   distance_to_km(junc_distance),duration_to_minutes(junc_duration));
441                 fprintf(htmlfile," [<span class='j'>");
442                 fprintf(htmlfile,translate_html_total[1],
443                                   distance_to_km(cum_distance),duration_to_minutes(cum_duration));
444                 fprintf(htmlfile,"</span>]\n");
445
446                 fprintf(htmlfile,"<tr class='c'><td class='l'><td class='r'>%.6f %.6f\n",
447                                  radians_to_degrees(latitude),radians_to_degrees(longitude));
448
449                 if(nextresult)
450                   {
451                    if(!turn_str)
452                      {
453                       turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node);
454                       turn_str=translate_turn[(4+(22+turn_int)/45)%8];
455                      }
456
457                    if(!bearing_next_str)
458                      {
459                       bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node);
460                       bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
461                      }
462
463                    fprintf(htmlfile,"<tr class='n'><td class='l'>%s:<td class='r'>",translate_html_node[0]);
464                    fprintf(htmlfile,translate_html_node[1],
465                                     type,
466                                     turn_str,
467                                     bearing_next_str);
468                    fprintf(htmlfile,"\n");
469                   }
470                 else
471                   {
472                    fprintf(htmlfile,"<tr class='n'><td class='l'>%s:<td class='r'>",translate_html_stop[0]);
473                    fprintf(htmlfile,translate_html_stop[1],
474                                     translate_html_waypoint);
475                    fprintf(htmlfile,"\n");
476                    fprintf(htmlfile,"<tr class='t'><td class='l'>%s:<td class='r'><span class='j'>",translate_html_total[0]);
477                    fprintf(htmlfile,translate_html_total[1],
478                                     distance_to_km(cum_distance),duration_to_minutes(cum_duration));
479                    fprintf(htmlfile,"</span>\n");
480                   }
481                }
482
483              if(gpxroutefile)
484                {
485                 if(!waynameraw)
486                   {
487                    waynameraw=WayNameRaw(ways,resultway);
488                    if(!*waynameraw)
489                       waynameraw=translate_highway[HIGHWAY(resultway->type)];
490                   }
491
492                 if(!waynamexml)
493                    waynamexml=ParseXML_Encode_Safe_XML(waynameraw);
494
495                 if(!bearing_str)
496                   {
497                    bearing_int=bearing_angle(nodes,result->segment,result->node);
498                    bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
499                   }
500
501                 fprintf(gpxroutefile,"<desc>");
502                 fprintf(gpxroutefile,translate_gpx_step,
503                                      bearing_str,
504                                      waynamexml,
505                                      distance_to_km(junc_distance),duration_to_minutes(junc_duration));
506                 fprintf(gpxroutefile,"</desc></rtept>\n");
507
508                 if(!nextresult)
509                   {
510                    fprintf(gpxroutefile,"<rtept lat=\"%.6f\" lon=\"%.6f\"><name>%s</name>\n",
511                                         radians_to_degrees(finish_lat),radians_to_degrees(finish_lon),
512                                         translate_gpx_finish);
513                    fprintf(gpxroutefile,"<desc>");
514                    fprintf(gpxroutefile,translate_gpx_final,
515                                         distance_to_km(cum_distance),duration_to_minutes(cum_duration));
516                    fprintf(gpxroutefile,"</desc></rtept>\n");
517                   }
518                 else if(important==10)
519                    fprintf(gpxroutefile,"<rtept lat=\"%.6f\" lon=\"%.6f\"><name>%s%d</name>\n",
520                                         radians_to_degrees(latitude),radians_to_degrees(longitude),
521                                         translate_gpx_inter,++segment_count);
522                 else
523                    fprintf(gpxroutefile,"<rtept lat=\"%.6f\" lon=\"%.6f\"><name>%s%03d</name>\n",
524                                         radians_to_degrees(latitude),radians_to_degrees(longitude),
525                                         translate_gpx_trip,++route_count);
526                }
527
528              if(textfile)
529                {
530                 char *type;
531
532                 if(important==10)
533                    type="Waypt";
534                 else
535                    type="Junct";
536
537                 if(!wayname)
538                    wayname=(char*)WayNameHighway(ways,resultway);
539
540                 if(nextresult)
541                   {
542                    if(!turn_str)
543                      {
544                       turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node);
545                       turn_str=translate_turn[(4+(22+turn_int)/45)%8];
546                      }
547
548                    if(!bearing_next_str)
549                      {
550                       bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node);
551                       bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
552                      }
553
554                    fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t %+d\t %+d\t%s\n",
555                                     radians_to_degrees(latitude),radians_to_degrees(longitude),
556                                     distance_to_km(junc_distance),duration_to_minutes(junc_duration),
557                                     distance_to_km(cum_distance),duration_to_minutes(cum_duration),
558                                     type,
559                                     (22+turn_int)/45,
560                                     ((22+bearing_next_int)/45+4)%8-4,
561                                     wayname);
562                   }
563                 else
564                    fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t\t\t%s\n",
565                                     radians_to_degrees(latitude),radians_to_degrees(longitude),
566                                     distance_to_km(junc_distance),duration_to_minutes(junc_duration),
567                                     distance_to_km(cum_distance),duration_to_minutes(cum_duration),
568                                     type,
569                                     wayname);
570                }
571
572              junc_distance=0;
573              junc_duration=0;
574             }
575
576           /* Print out all of the results */
577
578           if(textallfile)
579             {
580              char *type;
581
582              if(important==10)
583                 type="Waypt";
584              else if(important==2)
585                 type="Change";
586              else if(important>=1)
587                 type="Junct";
588              else
589                 type="Inter";
590
591              if(!wayname)
592                 wayname=(char*)WayNameHighway(ways,resultway);
593
594              if(!bearing_str)
595                {
596                 bearing_int=bearing_angle(nodes,result->segment,result->node);
597                 bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
598                }
599
600              fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t%3d\t%4d\t%s\n",
601                                  radians_to_degrees(latitude),radians_to_degrees(longitude),
602                                  IsFakeNode(result->node)?-(result->node&(~NODE_SUPER)):result->node,
603                                  (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',type,
604                                  distance_to_km(seg_distance),duration_to_minutes(seg_duration),
605                                  distance_to_km(cum_distance),duration_to_minutes(cum_duration),
606                                  profile->speed[HIGHWAY(resultway->type)],
607                                  bearing_int,
608                                  wayname);
609             }
610
611           if(waynamexml && waynamexml!=waynameraw)
612              free(waynamexml);
613          }
614        else if(!cum_distance)
615          {
616           int   bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node);
617           char *bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
618
619           /* Print out the very first start point */
620
621           if(htmlfile)
622             {
623              fprintf(htmlfile,"<tr class='c'><td class='l'><td class='r'>%.6f %.6f\n",
624                               radians_to_degrees(latitude),radians_to_degrees(longitude));
625              fprintf(htmlfile,"<tr class='n'><td class='l'>%s:<td class='r'>",translate_html_start[0]);
626              fprintf(htmlfile,translate_html_start[1],
627                               translate_html_waypoint,
628                               bearing_next_str);
629              fprintf(htmlfile,"\n");
630             }
631
632           if(gpxroutefile)
633              fprintf(gpxroutefile,"<rtept lat=\"%.6f\" lon=\"%.6f\"><name>%s</name>\n",
634                                   radians_to_degrees(latitude),radians_to_degrees(longitude),
635                                   translate_gpx_start);
636
637           if(textfile)
638              fprintf(textfile,"%10.6f\t%11.6f\t%6.3f km\t%4.1f min\t%5.1f km\t%4.0f min\t%s\t\t +%d\t\n",
639                               radians_to_degrees(latitude),radians_to_degrees(longitude),
640                               0.0,0.0,0.0,0.0,
641                               "Waypt",
642                               (22+bearing_next_int)/45);
643
644           if(textallfile)
645              fprintf(textallfile,"%10.6f\t%11.6f\t%8d%c\t%s\t%5.3f\t%5.2f\t%5.2f\t%5.1f\t\t\t\n",
646                                  radians_to_degrees(latitude),radians_to_degrees(longitude),
647                                  IsFakeNode(result->node)?-(result->node&(~NODE_SUPER)):result->node,
648                                  (!IsFakeNode(result->node) && IsSuperNode(nodes,result->node))?'*':' ',"Waypt",
649                                  0.0,0.0,0.0,0.0);
650          }
651
652        result=nextresult;
653       }
654     while(point==nextpoint);
655
656     if(gpxtrackfile)
657        fprintf(gpxtrackfile,"</trkseg>\n");
658
659     point=nextpoint;
660    }
661
662  /* Print the tail of the files */
663
664  if(htmlfile)
665    {
666     fprintf(htmlfile,"</table>\n");
667
668     if((translate_copyright_creator[0] && translate_copyright_creator[1]) ||
669        (translate_copyright_source[0]  && translate_copyright_source[1]) ||
670        (translate_copyright_license[0] && translate_copyright_license[1]))
671       {
672        fprintf(htmlfile,"<p>\n");
673        fprintf(htmlfile,"<table class='c'>\n");
674        if(translate_copyright_creator[0] && translate_copyright_creator[1])
675           fprintf(htmlfile,"<tr><td class='l'>%s:<td class='r'>%s\n",translate_copyright_creator[0],translate_copyright_creator[1]);
676        if(translate_copyright_source[0] && translate_copyright_source[1])
677           fprintf(htmlfile,"<tr><td class='l'>%s:<td class='r'>%s\n",translate_copyright_source[0],translate_copyright_source[1]);
678        if(translate_copyright_license[0] && translate_copyright_license[1])
679           fprintf(htmlfile,"<tr><td class='l'>%s:<td class='r'>%s\n",translate_copyright_license[0],translate_copyright_license[1]);
680        fprintf(htmlfile,"</table>\n");
681       }
682
683     fprintf(htmlfile,"</BODY>\n");
684     fprintf(htmlfile,"</HTML>\n");
685    }
686
687  if(gpxtrackfile)
688    {
689     fprintf(gpxtrackfile,"</trk>\n");
690     fprintf(gpxtrackfile,"</gpx>\n");
691    }
692
693  if(gpxroutefile)
694    {
695     fprintf(gpxroutefile,"</rte>\n");
696     fprintf(gpxroutefile,"</gpx>\n");
697    }
698
699  /* Close the files */
700
701  if(htmlfile)
702     fclose(htmlfile);
703  if(gpxtrackfile)
704     fclose(gpxtrackfile);
705  if(gpxroutefile)
706     fclose(gpxroutefile);
707  if(textfile)
708     fclose(textfile);
709  if(textallfile)
710     fclose(textallfile);
711 }
712
713
714 /*++++++++++++++++++++++++++++++++++++++
715   Calculate the angle to turn at a junction from segment1 to segment2 at node.
716
717   int turn_angle Returns a value in the range -4 to +4 indicating the angle to turn.
718
719   Nodes *nodes The set of nodes.
720
721   Segment *segment1 The current segment.
722
723   Segment *segment2 The next segment.
724
725   index_t node The node at which they join.
726
727   Straight ahead is zero, turning to the right is positive (90 degrees) and turning to the left is negative.
728   Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
729   ++++++++++++++++++++++++++++++++++++++*/
730
731 static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node)
732 {
733  double lat1,latm,lat2;
734  double lon1,lonm,lon2;
735  double angle1,angle2,angle;
736  index_t node1,node2;
737
738  node1=OtherNode(segment1,node);
739  node2=OtherNode(segment2,node);
740
741  if(IsFakeNode(node1))
742     GetFakeLatLong(node1,&lat1,&lon1);
743  else
744     GetLatLong(nodes,node1,&lat1,&lon1);
745
746  if(IsFakeNode(node))
747     GetFakeLatLong(node,&latm,&lonm);
748  else
749     GetLatLong(nodes,node,&latm,&lonm);
750
751  if(IsFakeNode(node2))
752     GetFakeLatLong(node2,&lat2,&lon2);
753  else
754     GetLatLong(nodes,node2,&lat2,&lon2);
755
756  angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1));
757  angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm));
758
759  angle=angle2-angle1;
760
761  angle=radians_to_degrees(angle);
762
763  angle=round(angle);
764
765  if(angle<-180) angle+=360;
766  if(angle> 180) angle-=360;
767
768  return((int)angle);
769 }
770
771
772 /*++++++++++++++++++++++++++++++++++++++
773   Calculate the bearing of a segment from the given node.
774
775   int bearing_angle Returns a value in the range 0 to 359 indicating the bearing.
776
777   Nodes *nodes The set of nodes.
778
779   Segment *segment The segment.
780
781   index_t node The node to start.
782
783   Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
784   ++++++++++++++++++++++++++++++++++++++*/
785
786 static int bearing_angle(Nodes *nodes,Segment *segment,index_t node)
787 {
788  double lat1,lat2;
789  double lon1,lon2;
790  double angle;
791  index_t node1,node2;
792
793  node1=node;
794  node2=OtherNode(segment,node);
795
796  if(IsFakeNode(node1))
797     GetFakeLatLong(node1,&lat1,&lon1);
798  else
799     GetLatLong(nodes,node1,&lat1,&lon1);
800
801  if(IsFakeNode(node2))
802     GetFakeLatLong(node2,&lat2,&lon2);
803  else
804     GetLatLong(nodes,node2,&lat2,&lon2);
805
806  angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1));
807
808  angle=radians_to_degrees(angle);
809
810  angle=round(270-angle);
811
812  if(angle<  0) angle+=360;
813  if(angle>360) angle-=360;
814
815  return((int)angle);
816 }