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