1 /***************************************
2 $Header: /home/amb/routino/src/RCS/output.c,v 1.33 2010/07/07 17:31:06 amb Exp $
4 Routing output generator.
6 Part of the Routino routing software.
7 ******************/ /******************
8 This file Copyright 2008-2010 Andrew M. Bishop
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.
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.
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 ***************************************/
31 #include <sys/types.h>
36 #include "functions.h"
37 #include "translations.h"
45 /* Global variables */
47 /*+ The option to calculate the quickest route insted of the shortest. +*/
48 extern int option_quickest;
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;
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 */
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);
79 /*++++++++++++++++++++++++++++++++++++++
80 Print the optimum route between two nodes.
82 Results **results The set of results to print (some may be NULL - ignore them).
84 int nresults The number of results in the list.
86 Nodes *nodes The list of nodes.
88 Segments *segments The set of segments to use.
90 Ways *ways The list of ways.
92 Profile *profile The profile containing the transport type, speeds and allowed highways.
93 ++++++++++++++++++++++++++++++++++++++*/
95 void PrintRoute(Results **results,int nresults,Nodes *nodes,Segments *segments,Ways *ways,Profile *profile)
97 FILE *htmlfile=NULL,*gpxtrackfile=NULL,*gpxroutefile=NULL,*textfile=NULL,*textallfile=NULL;
100 distance_t cum_distance=0;
101 duration_t cum_duration=0;
102 double finish_lat,finish_lon;
108 if(option_quickest==0)
110 /* Print the result for the shortest route */
113 htmlfile =fopen("shortest.html","w");
115 gpxtrackfile=fopen("shortest-track.gpx","w");
117 gpxroutefile=fopen("shortest-route.gpx","w");
119 textfile =fopen("shortest.txt","w");
121 textallfile =fopen("shortest-all.txt","w");
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));
136 /* Print the result for the quickest route */
139 htmlfile =fopen("quickest.html","w");
141 gpxtrackfile=fopen("quickest-track.gpx","w");
143 gpxroutefile=fopen("quickest-route.gpx","w");
145 textfile =fopen("quickest.txt","w");
147 textallfile =fopen("quickest-all.txt","w");
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));
161 /* Print the head of the files */
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");
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");
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])
213 fprintf(gpxtrackfile,"<copyright author=\"%s\">\n",translate_copyright_source[1]);
215 if(translate_copyright_license[1])
216 fprintf(gpxtrackfile,"<license>%s</license>\n",translate_copyright_license[1]);
218 fprintf(gpxtrackfile,"</copyright>\n");
220 fprintf(gpxtrackfile,"</metadata>\n");
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");
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");
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])
240 fprintf(gpxroutefile,"<copyright author=\"%s\">\n",translate_copyright_source[1]);
242 if(translate_copyright_license[1])
243 fprintf(gpxroutefile,"<license>%s</license>\n",translate_copyright_license[1]);
245 fprintf(gpxroutefile,"</copyright>\n");
247 fprintf(gpxroutefile,"</metadata>\n");
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");
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");
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" */
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");
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" */
294 /* Loop through the segments of the route and print it */
296 while(!results[point])
299 while(point<=nresults)
302 double start_lat,start_lon;
303 distance_t junc_distance=0;
304 duration_t junc_duration=0;
308 fprintf(gpxtrackfile,"<trkseg>\n");
310 if(IsFakeNode(results[point]->start))
311 GetFakeLatLong(results[point]->start,&start_lat,&start_lon);
313 GetLatLong(nodes,results[point]->start,&start_lat,&start_lon);
315 if(IsFakeNode(results[point]->finish))
316 GetFakeLatLong(results[point]->finish,&finish_lat,&finish_lon);
318 GetLatLong(nodes,results[point]->finish,&finish_lat,&finish_lon);
320 result=FindResult(results[point],results[point]->start);
324 double latitude,longitude;
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;}
332 GetLatLong(nodes,result->node,&latitude,&longitude);
335 fprintf(gpxtrackfile,"<trkpt lat=\"%.6f\" lon=\"%.6f\"/>\n",
336 radians_to_degrees(latitude),radians_to_degrees(longitude));
338 nextresult=FindResult(results[point],result->next);
341 for(nextpoint=point+1;nextpoint<=nresults;nextpoint++)
342 if(results[nextpoint])
344 nextresult=FindResult(results[nextpoint],results[nextpoint]->start);
345 nextresult=FindResult(results[nextpoint],nextresult->next);
349 if(result->node!=results[point]->start)
351 distance_t seg_distance=0;
352 duration_t seg_duration=0;
356 /* Cache the values to be printed rather than calculating them repeatedly for each output format */
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;
362 /* Get the properties of this segment */
364 resultway=LookupWay(ways,result->segment->way);
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;
373 /* Decide if this is an important junction */
375 if(result->node==results[point]->finish)
379 Segment *segment=FirstSegment(segments,nodes,result->node);
383 index_t othernode=OtherNode(segment,result->node);
385 if(othernode!=result->prev && segment!=result->segment)
386 if(IsNormalSegment(segment) && (!profile->oneway || !IsOnewayTo(segment,result->node)))
388 Way *way=LookupWay(ways,segment->way);
390 if(othernode==result->next) /* the next segment that we follow */
392 if(HIGHWAY(way->type)!=HIGHWAY(resultway->type))
396 else /* a segment that we don't follow */
398 if(junction_other_way[HIGHWAY(resultway->type)-1][HIGHWAY(way->type)-1])
407 segment=NextSegment(segments,segment,result->node);
412 /* Print out the important points (junctions / waypoints) */
416 /* Print the intermediate finish points (because they have correct junction distances) */
423 type=translate_html_waypoint;
425 type=translate_html_junction;
429 waynameraw=WayNameRaw(ways,resultway);
431 waynameraw=translate_highway[HIGHWAY(resultway->type)];
435 waynamexml=ParseXML_Encode_Safe_XML(waynameraw);
437 fprintf(htmlfile,"<tr class='s'><td class='l'>%s:<td class='r'>",translate_html_segment[0]);
438 fprintf(htmlfile,translate_html_segment[1],
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");
446 fprintf(htmlfile,"<tr class='c'><td class='l'><td class='r'>%.6f %.6f\n",
447 radians_to_degrees(latitude),radians_to_degrees(longitude));
453 turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node);
454 turn_str=translate_turn[(4+(22+turn_int)/45)%8];
457 if(!bearing_next_str)
459 bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node);
460 bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
463 fprintf(htmlfile,"<tr class='n'><td class='l'>%s:<td class='r'>",translate_html_node[0]);
464 fprintf(htmlfile,translate_html_node[1],
468 fprintf(htmlfile,"\n");
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");
487 waynameraw=WayNameRaw(ways,resultway);
489 waynameraw=translate_highway[HIGHWAY(resultway->type)];
493 waynamexml=ParseXML_Encode_Safe_XML(waynameraw);
497 bearing_int=bearing_angle(nodes,result->segment,result->node);
498 bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
501 fprintf(gpxroutefile,"<desc>");
502 fprintf(gpxroutefile,translate_gpx_step,
505 distance_to_km(junc_distance),duration_to_minutes(junc_duration));
506 fprintf(gpxroutefile,"</desc></rtept>\n");
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");
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);
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);
538 wayname=(char*)WayNameHighway(ways,resultway);
544 turn_int=turn_angle(nodes,result->segment,nextresult->segment,result->node);
545 turn_str=translate_turn[(4+(22+turn_int)/45)%8];
548 if(!bearing_next_str)
550 bearing_next_int=bearing_angle(nodes,nextresult->segment,nextresult->node);
551 bearing_next_str=translate_heading[(4+(22+bearing_next_int)/45)%8];
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),
560 ((22+bearing_next_int)/45+4)%8-4,
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),
576 /* Print out all of the results */
584 else if(important==2)
586 else if(important>=1)
592 wayname=(char*)WayNameHighway(ways,resultway);
596 bearing_int=bearing_angle(nodes,result->segment,result->node);
597 bearing_str=translate_heading[(4+(22+bearing_int)/45)%8];
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)],
611 if(waynamexml && waynamexml!=waynameraw)
614 else if(!cum_distance)
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];
619 /* Print out the very first start point */
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,
629 fprintf(htmlfile,"\n");
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);
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),
642 (22+bearing_next_int)/45);
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",
654 while(point==nextpoint);
657 fprintf(gpxtrackfile,"</trkseg>\n");
662 /* Print the tail of the files */
666 fprintf(htmlfile,"</table>\n");
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]))
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");
683 fprintf(htmlfile,"</BODY>\n");
684 fprintf(htmlfile,"</HTML>\n");
689 fprintf(gpxtrackfile,"</trk>\n");
690 fprintf(gpxtrackfile,"</gpx>\n");
695 fprintf(gpxroutefile,"</rte>\n");
696 fprintf(gpxroutefile,"</gpx>\n");
699 /* Close the files */
704 fclose(gpxtrackfile);
706 fclose(gpxroutefile);
714 /*++++++++++++++++++++++++++++++++++++++
715 Calculate the angle to turn at a junction from segment1 to segment2 at node.
717 int turn_angle Returns a value in the range -4 to +4 indicating the angle to turn.
719 Nodes *nodes The set of nodes.
721 Segment *segment1 The current segment.
723 Segment *segment2 The next segment.
725 index_t node The node at which they join.
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 ++++++++++++++++++++++++++++++++++++++*/
731 static int turn_angle(Nodes *nodes,Segment *segment1,Segment *segment2,index_t node)
733 double lat1,latm,lat2;
734 double lon1,lonm,lon2;
735 double angle1,angle2,angle;
738 node1=OtherNode(segment1,node);
739 node2=OtherNode(segment2,node);
741 if(IsFakeNode(node1))
742 GetFakeLatLong(node1,&lat1,&lon1);
744 GetLatLong(nodes,node1,&lat1,&lon1);
747 GetFakeLatLong(node,&latm,&lonm);
749 GetLatLong(nodes,node,&latm,&lonm);
751 if(IsFakeNode(node2))
752 GetFakeLatLong(node2,&lat2,&lon2);
754 GetLatLong(nodes,node2,&lat2,&lon2);
756 angle1=atan2((lonm-lon1)*cos(latm),(latm-lat1));
757 angle2=atan2((lon2-lonm)*cos(latm),(lat2-latm));
761 angle=radians_to_degrees(angle);
765 if(angle<-180) angle+=360;
766 if(angle> 180) angle-=360;
772 /*++++++++++++++++++++++++++++++++++++++
773 Calculate the bearing of a segment from the given node.
775 int bearing_angle Returns a value in the range 0 to 359 indicating the bearing.
777 Nodes *nodes The set of nodes.
779 Segment *segment The segment.
781 index_t node The node to start.
783 Angles are calculated using flat Cartesian lat/long grid approximation (after scaling longitude due to latitude).
784 ++++++++++++++++++++++++++++++++++++++*/
786 static int bearing_angle(Nodes *nodes,Segment *segment,index_t node)
794 node2=OtherNode(segment,node);
796 if(IsFakeNode(node1))
797 GetFakeLatLong(node1,&lat1,&lon1);
799 GetLatLong(nodes,node1,&lat1,&lon1);
801 if(IsFakeNode(node2))
802 GetFakeLatLong(node2,&lat2,&lon2);
804 GetLatLong(nodes,node2,&lat2,&lon2);
806 angle=atan2((lat2-lat1),(lon2-lon1)*cos(lat1));
808 angle=radians_to_degrees(angle);
810 angle=round(270-angle);
812 if(angle< 0) angle+=360;
813 if(angle>360) angle-=360;