1 /***************************************
2 $Header: /home/amb/routino/src/RCS/router.c,v 1.90 2010/11/13 14:22:28 amb Exp $
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 ***************************************/
37 #include "functions.h"
38 #include "translations.h"
42 /*+ The maximum distance from the specified point to search for a node or segment (in km). +*/
46 /*+ A set of waypoint latitudes and longitudes. +*/
47 static double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
49 /*+ The option not to print any progress information. +*/
52 /*+ The options to select the format of the output. +*/
53 int option_html=0,option_gpx_track=0,option_gpx_route=0,option_text=0,option_text_all=0,option_none=0;
55 /*+ The option to calculate the quickest route insted of the shortest. +*/
56 int option_quickest=0;
61 static void print_usage(int detail,const char *argerr,const char *err);
64 /*++++++++++++++++++++++++++++++++++++++
65 The main program for the router.
66 ++++++++++++++++++++++++++++++++++++++*/
68 int main(int argc,char** argv)
71 Segments *OSMSegments;
73 Results *results[NWAYPOINTS+1]={NULL};
74 int point_used[NWAYPOINTS+1]={0};
75 int help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0;
76 char *dirname=NULL,*prefix=NULL;
77 char *profiles=NULL,*profilename=NULL;
78 char *translations=NULL,*language=NULL;
80 Transport transport=Transport_None;
81 Profile *profile=NULL;
82 index_t start=NO_NODE,finish=NO_NODE;
85 /* Parse the command line arguments */
88 print_usage(0,NULL,NULL);
90 /* Get the non-routing, general program options */
92 for(arg=1;arg<argc;arg++)
94 if(!strcmp(argv[arg],"--help"))
95 print_usage(1,NULL,NULL);
96 else if(!strcmp(argv[arg],"--help-profile"))
98 else if(!strcmp(argv[arg],"--help-profile-xml"))
100 else if(!strcmp(argv[arg],"--help-profile-json"))
102 else if(!strcmp(argv[arg],"--help-profile-perl"))
104 else if(!strncmp(argv[arg],"--dir=",6))
105 dirname=&argv[arg][6];
106 else if(!strncmp(argv[arg],"--prefix=",9))
107 prefix=&argv[arg][9];
108 else if(!strncmp(argv[arg],"--profiles=",11))
109 profiles=&argv[arg][11];
110 else if(!strncmp(argv[arg],"--translations=",15))
111 translations=&argv[arg][15];
112 else if(!strcmp(argv[arg],"--exact-nodes-only"))
114 else if(!strcmp(argv[arg],"--quiet"))
116 else if(!strcmp(argv[arg],"--loggable"))
118 else if(!strcmp(argv[arg],"--output-html"))
120 else if(!strcmp(argv[arg],"--output-gpx-track"))
122 else if(!strcmp(argv[arg],"--output-gpx-route"))
124 else if(!strcmp(argv[arg],"--output-text"))
126 else if(!strcmp(argv[arg],"--output-text-all"))
128 else if(!strcmp(argv[arg],"--output-none"))
130 else if(!strncmp(argv[arg],"--profile=",10))
131 profilename=&argv[arg][10];
132 else if(!strncmp(argv[arg],"--language=",11))
133 language=&argv[arg][11];
134 else if(!strncmp(argv[arg],"--transport=",12))
136 transport=TransportType(&argv[arg][12]);
138 if(transport==Transport_None)
139 print_usage(0,argv[arg],NULL);
147 /* Load in the profiles */
149 if(transport==Transport_None)
150 transport=Transport_Motorcar;
154 if(!ExistsFile(profiles))
156 fprintf(stderr,"Error: The '--profiles' option specifies a file that does not exist.\n");
162 if(ExistsFile(FileName(dirname,prefix,"profiles.xml")))
163 profiles=FileName(dirname,prefix,"profiles.xml");
164 else if(ExistsFile(FileName(DATADIR,NULL,"tagging.xml")))
165 profiles=FileName(DATADIR,NULL,"profiles.xml");
168 fprintf(stderr,"Error: The '--profiles' option was not used and the default 'profiles.xml' does not exist.\n");
173 if(ParseXMLProfiles(profiles))
175 fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
181 profile=GetProfile(profilename);
185 fprintf(stderr,"Error: Cannot find a profile called '%s' in '%s'.\n",profilename,profiles);
190 profile=GetProfile(TransportName(transport));
194 profile=(Profile*)calloc(1,sizeof(Profile));
195 profile->transport=transport;
198 /* Parse the other command line arguments */
200 for(arg=1;arg<argc;arg++)
204 else if(!strcmp(argv[arg],"--shortest"))
206 else if(!strcmp(argv[arg],"--quickest"))
208 else if(isdigit(argv[arg][0]) ||
209 ((argv[arg][0]=='-' || argv[arg][0]=='+') && isdigit(argv[arg][1])))
211 for(point=1;point<=NWAYPOINTS;point++)
212 if(point_used[point]!=3)
214 if(point_used[point]==0)
216 point_lon[point]=degrees_to_radians(atof(argv[arg]));
219 else /* if(point_used[point]==1) */
221 point_lat[point]=degrees_to_radians(atof(argv[arg]));
227 else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
229 char *p=&argv[arg][6];
230 while(isdigit(*p)) p++;
232 print_usage(0,argv[arg],NULL);
234 point=atoi(&argv[arg][5]);
235 if(point>NWAYPOINTS || point_used[point]&1)
236 print_usage(0,argv[arg],NULL);
238 point_lon[point]=degrees_to_radians(atof(p));
239 point_used[point]+=1;
241 else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
243 char *p=&argv[arg][6];
244 while(isdigit(*p)) p++;
246 print_usage(0,argv[arg],NULL);
248 point=atoi(&argv[arg][5]);
249 if(point>NWAYPOINTS || point_used[point]&2)
250 print_usage(0,argv[arg],NULL);
252 point_lat[point]=degrees_to_radians(atof(p));
253 point_used[point]+=2;
255 else if(!strncmp(argv[arg],"--transport=",12))
256 ; /* Done this already */
257 else if(!strncmp(argv[arg],"--highway-",10))
260 char *equal=strchr(argv[arg],'=');
264 print_usage(0,argv[arg],NULL);
266 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+10);
267 string[equal-argv[arg]-10]=0;
269 highway=HighwayType(string);
271 if(highway==Way_Count)
272 print_usage(0,argv[arg],NULL);
274 profile->highway[highway]=atof(equal+1);
278 else if(!strncmp(argv[arg],"--speed-",8))
281 char *equal=strchr(argv[arg],'=');
285 print_usage(0,argv[arg],NULL);
287 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+8);
288 string[equal-argv[arg]-8]=0;
290 highway=HighwayType(string);
292 if(highway==Way_Count)
293 print_usage(0,argv[arg],NULL);
295 profile->speed[highway]=kph_to_speed(atof(equal+1));
299 else if(!strncmp(argv[arg],"--property-",11))
302 char *equal=strchr(argv[arg],'=');
306 print_usage(0,argv[arg],NULL);
308 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+11);
309 string[equal-argv[arg]-11]=0;
311 property=PropertyType(string);
313 if(property==Way_Count)
314 print_usage(0,argv[arg],NULL);
316 profile->props_yes[property]=atof(equal+1);
320 else if(!strncmp(argv[arg],"--oneway=",9))
321 profile->oneway=!!atoi(&argv[arg][9]);
322 else if(!strncmp(argv[arg],"--weight=",9))
323 profile->weight=tonnes_to_weight(atof(&argv[arg][9]));
324 else if(!strncmp(argv[arg],"--height=",9))
325 profile->height=metres_to_height(atof(&argv[arg][9]));
326 else if(!strncmp(argv[arg],"--width=",8))
327 profile->width=metres_to_width(atof(&argv[arg][8]));
328 else if(!strncmp(argv[arg],"--length=",9))
329 profile->length=metres_to_length(atof(&argv[arg][9]));
331 print_usage(0,argv[arg],NULL);
334 for(point=1;point<=NWAYPOINTS;point++)
335 if(point_used[point]==1 || point_used[point]==2)
336 print_usage(0,NULL,"All waypoints must have latitude and longitude.");
340 PrintProfile(profile);
344 else if(help_profile_xml)
350 else if(help_profile_json)
356 else if(help_profile_pl)
363 /* Load in the translations */
365 if(option_html==0 && option_gpx_track==0 && option_gpx_route==0 && option_text==0 && option_text_all==0 && option_none==0)
366 option_html=option_gpx_track=option_gpx_route=option_text=option_text_all=1;
368 if(option_html || option_gpx_route || option_gpx_track)
372 if(!ExistsFile(translations))
374 fprintf(stderr,"Error: The '--translations' option specifies a file that does not exist.\n");
380 if(ExistsFile(FileName(dirname,prefix,"translations.xml")))
381 translations=FileName(dirname,prefix,"translations.xml");
382 else if(ExistsFile(FileName(DATADIR,NULL,"translations.xml")))
383 translations=FileName(DATADIR,NULL,"translations.xml");
386 fprintf(stderr,"Error: The '--translations' option was not used and the default 'translations.xml' does not exist.\n");
391 if(ParseXMLTranslations(translations,language))
393 fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
398 /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
400 OSMNodes=LoadNodeList(FileName(dirname,prefix,"nodes.mem"));
402 OSMSegments=LoadSegmentList(FileName(dirname,prefix,"segments.mem"));
404 OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem"));
406 if(UpdateProfile(profile,OSMWays))
408 fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
412 /* Loop through all pairs of points */
414 for(point=1;point<=NWAYPOINTS;point++)
417 distance_t distmax=km_to_distance(MAXSEARCH);
419 index_t segment=NO_SEGMENT;
422 if(point_used[point]!=3)
425 /* Find the closest point */
431 finish=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
435 distance_t dist1,dist2;
437 segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
439 if(segment!=NO_SEGMENT)
440 finish=CreateFakes(OSMNodes,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
447 fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point);
455 if(IsFakeNode(finish))
456 GetFakeLatLong(finish,&lat,&lon);
458 GetLatLong(OSMNodes,finish,&lat,&lon);
460 if(IsFakeNode(finish))
461 printf("Point %d is segment %d (node %d -> %d): %3.6f %4.6f = %2.3f km\n",point,segment,node1,node2,
462 radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
464 printf("Point %d is node %d: %3.6f %4.6f = %2.3f km\n",point,finish,
465 radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
474 /* Calculate the beginning of the route */
476 if(!IsFakeNode(start) && IsSuperNode(OSMNodes,start))
480 begin=NewResultsList(1);
484 result=InsertResult(begin,start);
490 begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,start,profile);
494 fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
499 if(FindResult(begin,finish))
501 FixForwardRoute(begin,finish);
503 results[point]=begin;
507 printf("Routed: Super-Nodes Checked = %d\n",begin->number);
513 Results *superresults;
515 /* Calculate the end of the route */
517 if(!IsFakeNode(finish) && IsSuperNode(OSMNodes,finish))
521 end=NewResultsList(1);
525 result=InsertResult(end,finish);
531 end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,finish,profile);
535 fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
540 /* Calculate the middle of the route */
542 superresults=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,begin,end,profile);
544 FreeResultsList(begin);
545 FreeResultsList(end);
549 fprintf(stderr,"Error: Cannot find route compatible with profile.\n");
553 results[point]=CombineRoutes(superresults,OSMNodes,OSMSegments,OSMWays,profile);
555 FreeResultsList(superresults);
559 /* Print out the combined route */
562 PrintRoute(results,NWAYPOINTS,OSMNodes,OSMSegments,OSMWays,profile);
568 /*++++++++++++++++++++++++++++++++++++++
569 Print out the usage information.
571 int detail The level of detail to use - 0 = low, 1 = high.
573 const char *argerr The argument that gave the error (if there is one).
575 const char *err Other error message (if there is one).
576 ++++++++++++++++++++++++++++++++++++++*/
578 static void print_usage(int detail,const char *argerr,const char *err)
581 "Usage: router [--help | --help-profile | --help-profile-xml |\n"
582 " --help-profile-json | --help-profile-perl ]\n"
583 " [--dir=<dirname>] [--prefix=<name>]\n"
584 " [--profiles=<filename>] [--translations=<filename>]\n"
585 " [--exact-nodes-only]\n"
586 " [--loggable | --quiet]\n"
587 " [--language=<lang>]\n"
589 " [--output-gpx-track] [--output-gpx-route]\n"
590 " [--output-text] [--output-text-all]\n"
592 " [--profile=<name>]\n"
593 " [--transport=<transport>]\n"
594 " [--shortest | --quickest]\n"
595 " --lon1=<longitude> --lat1=<latitude>\n"
596 " --lon2=<longitude> --lon2=<latitude>\n"
597 " [ ... --lon99=<longitude> --lon99=<latitude>]\n"
598 " [--highway-<highway>=<preference> ...]\n"
599 " [--speed-<highway>=<speed> ...]\n"
600 " [--property-<property>=<preference> ...]\n"
601 " [--oneway=(0|1)]\n"
602 " [--weight=<weight>]\n"
603 " [--height=<height>] [--width=<width>] [--length=<length>]\n");
608 "Error with command line parameter: %s\n",argerr);
618 "--help Prints this information.\n"
619 "--help-profile Prints the information about the selected profile.\n"
620 "--help-profile-xml Prints all loaded profiles in XML format.\n"
621 "--help-profile-json Prints all loaded profiles in JSON format.\n"
622 "--help-profile-perl Prints all loaded profiles in Perl format.\n"
624 "--dir=<dirname> The directory containing the routing database.\n"
625 "--prefix=<name> The filename prefix for the routing database.\n"
626 "--profiles=<filename> The name of the XML file containing the profiles\n"
627 " (defaults to 'profiles.xml' with '--dir' and\n"
628 " '--prefix' options or the file installed in\n"
630 "--translations=<fname> The name of the XML file containing the translations\n"
631 " (defaults to 'translations.xml' with '--dir' and\n"
632 " '--prefix' options or the file installed in\n"
635 "--exact-nodes-only Only route between nodes (don't find closest segment).\n"
637 "--loggable Print progress messages suitable for logging to file.\n"
638 "--quiet Don't print any screen output when running.\n"
640 "--language=<lang> Use the translations for specified language.\n"
641 "--output-html Write an HTML description of the route.\n"
642 "--output-gpx-track Write a GPX track file with all route points.\n"
643 "--output-gpx-route Write a GPX route file with interesting junctions.\n"
644 "--output-text Write a plain text file with interesting junctions.\n"
645 "--output-text-all Write a plain test file with all route points.\n"
646 "--output-none Don't write any output files or read any translations.\n"
647 " (If no output option is given then all are written.)\n"
649 "--profile=<name> Select the loaded profile with this name.\n"
650 "--transport=<transport> Select the transport to use (selects the profile\n"
651 " named after the transport if '--profile' is not used.)\n"
653 "--shortest Find the shortest route between the waypoints.\n"
654 "--quickest Find the quickest route between the waypoints.\n"
656 "--lon<n>=<longitude> Specify the longitude of the n'th waypoint.\n"
657 "--lat<n>=<latitude> Specify the latitude of the n'th waypoint.\n"
659 " Routing preference options\n"
660 "--highway-<highway>=<preference> * preference for highway type (%%).\n"
661 "--speed-<highway>=<speed> * speed for highway type (km/h).\n"
662 "--property-<property>=<preference> * preference for proprty type (%%).\n"
663 "--oneway=(0|1) * oneway streets are to be obeyed.\n"
664 "--weight=<weight> * maximum weight limit (tonnes).\n"
665 "--height=<height> * maximum height limit (metres).\n"
666 "--width=<width> * maximum width limit (metres).\n"
667 "--length=<length> * maximum length limit (metres).\n"
669 "<transport> defaults to motorcar but can be set to:\n"
672 "<highway> can be selected from:\n"
675 "<property> can be selected from:\n"
677 TransportList(),HighwayList(),PropertyList());