1 /***************************************
2 $Header: /home/amb/routino/src/RCS/router.c,v 1.89 2010/09/15 18:19:36 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 ***************************************/
36 #include "functions.h"
37 #include "translations.h"
41 /*+ The maximum distance from the specified point to search for a node or segment (in km). +*/
45 /*+ A set of waypoint latitudes and longitudes. +*/
46 static double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
48 /*+ The option not to print any progress information. +*/
51 /*+ The options to select the format of the output. +*/
52 int option_html=0,option_gpx_track=0,option_gpx_route=0,option_text=0,option_text_all=0,option_none=0;
54 /*+ The option to calculate the quickest route insted of the shortest. +*/
55 int option_quickest=0;
60 static void print_usage(int detail,const char *argerr,const char *err);
63 /*++++++++++++++++++++++++++++++++++++++
64 The main program for the router.
65 ++++++++++++++++++++++++++++++++++++++*/
67 int main(int argc,char** argv)
70 Segments *OSMSegments;
72 Results *results[NWAYPOINTS+1]={NULL};
73 int point_used[NWAYPOINTS+1]={0};
74 int help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0;
75 char *dirname=NULL,*prefix=NULL;
76 char *profiles=NULL,*profilename=NULL;
77 char *translations=NULL,*language=NULL;
79 Transport transport=Transport_None;
80 Profile *profile=NULL;
81 index_t start=NO_NODE,finish=NO_NODE;
84 /* Parse the command line arguments */
87 print_usage(0,NULL,NULL);
89 /* Get the non-routing, general program options */
91 for(arg=1;arg<argc;arg++)
93 if(!strcmp(argv[arg],"--help"))
94 print_usage(1,NULL,NULL);
95 else if(!strcmp(argv[arg],"--help-profile"))
97 else if(!strcmp(argv[arg],"--help-profile-xml"))
99 else if(!strcmp(argv[arg],"--help-profile-json"))
101 else if(!strcmp(argv[arg],"--help-profile-perl"))
103 else if(!strncmp(argv[arg],"--dir=",6))
104 dirname=&argv[arg][6];
105 else if(!strncmp(argv[arg],"--prefix=",9))
106 prefix=&argv[arg][9];
107 else if(!strncmp(argv[arg],"--profiles=",11))
108 profiles=&argv[arg][11];
109 else if(!strncmp(argv[arg],"--translations=",15))
110 translations=&argv[arg][15];
111 else if(!strcmp(argv[arg],"--exact-nodes-only"))
113 else if(!strcmp(argv[arg],"--quiet"))
115 else if(!strcmp(argv[arg],"--output-html"))
117 else if(!strcmp(argv[arg],"--output-gpx-track"))
119 else if(!strcmp(argv[arg],"--output-gpx-route"))
121 else if(!strcmp(argv[arg],"--output-text"))
123 else if(!strcmp(argv[arg],"--output-text-all"))
125 else if(!strcmp(argv[arg],"--output-none"))
127 else if(!strncmp(argv[arg],"--profile=",10))
128 profilename=&argv[arg][10];
129 else if(!strncmp(argv[arg],"--language=",11))
130 language=&argv[arg][11];
131 else if(!strncmp(argv[arg],"--transport=",12))
133 transport=TransportType(&argv[arg][12]);
135 if(transport==Transport_None)
136 print_usage(0,argv[arg],NULL);
144 /* Load in the profiles */
146 if(transport==Transport_None)
147 transport=Transport_Motorcar;
151 if(!ExistsFile(profiles))
153 fprintf(stderr,"Error: The '--profiles' option specifies a file that does not exist.\n");
159 if(ExistsFile(FileName(dirname,prefix,"profiles.xml")))
160 profiles=FileName(dirname,prefix,"profiles.xml");
161 else if(ExistsFile(FileName(DATADIR,NULL,"tagging.xml")))
162 profiles=FileName(DATADIR,NULL,"profiles.xml");
165 fprintf(stderr,"Error: The '--profiles' option was not used and the default 'profiles.xml' does not exist.\n");
170 if(ParseXMLProfiles(profiles))
172 fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
178 profile=GetProfile(profilename);
182 fprintf(stderr,"Error: Cannot find a profile called '%s' in '%s'.\n",profilename,profiles);
187 profile=GetProfile(TransportName(transport));
191 profile=(Profile*)calloc(1,sizeof(Profile));
192 profile->transport=transport;
195 /* Parse the other command line arguments */
197 for(arg=1;arg<argc;arg++)
201 else if(!strcmp(argv[arg],"--shortest"))
203 else if(!strcmp(argv[arg],"--quickest"))
205 else if(isdigit(argv[arg][0]) ||
206 ((argv[arg][0]=='-' || argv[arg][0]=='+') && isdigit(argv[arg][1])))
208 for(point=1;point<=NWAYPOINTS;point++)
209 if(point_used[point]!=3)
211 if(point_used[point]==0)
213 point_lon[point]=degrees_to_radians(atof(argv[arg]));
216 else /* if(point_used[point]==1) */
218 point_lat[point]=degrees_to_radians(atof(argv[arg]));
224 else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
226 char *p=&argv[arg][6];
227 while(isdigit(*p)) p++;
229 print_usage(0,argv[arg],NULL);
231 point=atoi(&argv[arg][5]);
232 if(point>NWAYPOINTS || point_used[point]&1)
233 print_usage(0,argv[arg],NULL);
235 point_lon[point]=degrees_to_radians(atof(p));
236 point_used[point]+=1;
238 else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
240 char *p=&argv[arg][6];
241 while(isdigit(*p)) p++;
243 print_usage(0,argv[arg],NULL);
245 point=atoi(&argv[arg][5]);
246 if(point>NWAYPOINTS || point_used[point]&2)
247 print_usage(0,argv[arg],NULL);
249 point_lat[point]=degrees_to_radians(atof(p));
250 point_used[point]+=2;
252 else if(!strncmp(argv[arg],"--transport=",12))
253 ; /* Done this already */
254 else if(!strncmp(argv[arg],"--highway-",10))
257 char *equal=strchr(argv[arg],'=');
261 print_usage(0,argv[arg],NULL);
263 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+10);
264 string[equal-argv[arg]-10]=0;
266 highway=HighwayType(string);
268 if(highway==Way_Count)
269 print_usage(0,argv[arg],NULL);
271 profile->highway[highway]=atof(equal+1);
275 else if(!strncmp(argv[arg],"--speed-",8))
278 char *equal=strchr(argv[arg],'=');
282 print_usage(0,argv[arg],NULL);
284 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+8);
285 string[equal-argv[arg]-8]=0;
287 highway=HighwayType(string);
289 if(highway==Way_Count)
290 print_usage(0,argv[arg],NULL);
292 profile->speed[highway]=kph_to_speed(atof(equal+1));
296 else if(!strncmp(argv[arg],"--property-",11))
299 char *equal=strchr(argv[arg],'=');
303 print_usage(0,argv[arg],NULL);
305 string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+11);
306 string[equal-argv[arg]-11]=0;
308 property=PropertyType(string);
310 if(property==Way_Count)
311 print_usage(0,argv[arg],NULL);
313 profile->props_yes[property]=atof(equal+1);
317 else if(!strncmp(argv[arg],"--oneway=",9))
318 profile->oneway=!!atoi(&argv[arg][9]);
319 else if(!strncmp(argv[arg],"--weight=",9))
320 profile->weight=tonnes_to_weight(atof(&argv[arg][9]));
321 else if(!strncmp(argv[arg],"--height=",9))
322 profile->height=metres_to_height(atof(&argv[arg][9]));
323 else if(!strncmp(argv[arg],"--width=",8))
324 profile->width=metres_to_width(atof(&argv[arg][8]));
325 else if(!strncmp(argv[arg],"--length=",9))
326 profile->length=metres_to_length(atof(&argv[arg][9]));
328 print_usage(0,argv[arg],NULL);
331 for(point=1;point<=NWAYPOINTS;point++)
332 if(point_used[point]==1 || point_used[point]==2)
333 print_usage(0,NULL,"All waypoints must have latitude and longitude.");
337 PrintProfile(profile);
341 else if(help_profile_xml)
347 else if(help_profile_json)
353 else if(help_profile_pl)
360 /* Load in the translations */
362 if(option_html==0 && option_gpx_track==0 && option_gpx_route==0 && option_text==0 && option_text_all==0 && option_none==0)
363 option_html=option_gpx_track=option_gpx_route=option_text=option_text_all=1;
365 if(option_html || option_gpx_route || option_gpx_track)
369 if(!ExistsFile(translations))
371 fprintf(stderr,"Error: The '--translations' option specifies a file that does not exist.\n");
377 if(ExistsFile(FileName(dirname,prefix,"translations.xml")))
378 translations=FileName(dirname,prefix,"translations.xml");
379 else if(ExistsFile(FileName(DATADIR,NULL,"translations.xml")))
380 translations=FileName(DATADIR,NULL,"translations.xml");
383 fprintf(stderr,"Error: The '--translations' option was not used and the default 'translations.xml' does not exist.\n");
388 if(ParseXMLTranslations(translations,language))
390 fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
395 /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
397 OSMNodes=LoadNodeList(FileName(dirname,prefix,"nodes.mem"));
399 OSMSegments=LoadSegmentList(FileName(dirname,prefix,"segments.mem"));
401 OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem"));
403 if(UpdateProfile(profile,OSMWays))
405 fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
409 /* Loop through all pairs of points */
411 for(point=1;point<=NWAYPOINTS;point++)
414 distance_t distmax=km_to_distance(MAXSEARCH);
416 index_t segment=NO_SEGMENT;
419 if(point_used[point]!=3)
422 /* Find the closest point */
428 finish=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
432 distance_t dist1,dist2;
434 segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
436 if(segment!=NO_SEGMENT)
437 finish=CreateFakes(OSMNodes,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
444 fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point);
452 if(IsFakeNode(finish))
453 GetFakeLatLong(finish,&lat,&lon);
455 GetLatLong(OSMNodes,finish,&lat,&lon);
457 if(IsFakeNode(finish))
458 printf("Point %d is segment %d (node %d -> %d): %3.6f %4.6f = %2.3f km\n",point,segment,node1,node2,
459 radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
461 printf("Point %d is node %d: %3.6f %4.6f = %2.3f km\n",point,finish,
462 radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
471 /* Calculate the beginning of the route */
473 if(!IsFakeNode(start) && IsSuperNode(OSMNodes,start))
477 begin=NewResultsList(1);
481 result=InsertResult(begin,start);
487 begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,start,profile);
491 fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
496 if(FindResult(begin,finish))
498 FixForwardRoute(begin,finish);
500 results[point]=begin;
504 printf("\rRouted: Super-Nodes Checked = %d\n",begin->number);
510 Results *superresults;
512 /* Calculate the end of the route */
514 if(!IsFakeNode(finish) && IsSuperNode(OSMNodes,finish))
518 end=NewResultsList(1);
522 result=InsertResult(end,finish);
528 end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,finish,profile);
532 fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
537 /* Calculate the middle of the route */
539 superresults=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,begin,end,profile);
541 FreeResultsList(begin);
542 FreeResultsList(end);
546 fprintf(stderr,"Error: Cannot find route compatible with profile.\n");
550 results[point]=CombineRoutes(superresults,OSMNodes,OSMSegments,OSMWays,profile);
552 FreeResultsList(superresults);
556 /* Print out the combined route */
559 PrintRoute(results,NWAYPOINTS,OSMNodes,OSMSegments,OSMWays,profile);
565 /*++++++++++++++++++++++++++++++++++++++
566 Print out the usage information.
568 int detail The level of detail to use - 0 = low, 1 = high.
570 const char *argerr The argument that gave the error (if there is one).
572 const char *err Other error message (if there is one).
573 ++++++++++++++++++++++++++++++++++++++*/
575 static void print_usage(int detail,const char *argerr,const char *err)
578 "Usage: router [--help | --help-profile | --help-profile-xml |\n"
579 " --help-profile-json | --help-profile-perl ]\n"
580 " [--dir=<dirname>] [--prefix=<name>]\n"
581 " [--profiles=<filename>] [--translations=<filename>]\n"
582 " [--exact-nodes-only]\n"
584 " [--language=<lang>]\n"
586 " [--output-gpx-track] [--output-gpx-route]\n"
587 " [--output-text] [--output-text-all]\n"
589 " [--profile=<name>]\n"
590 " [--transport=<transport>]\n"
591 " [--shortest | --quickest]\n"
592 " --lon1=<longitude> --lat1=<latitude>\n"
593 " --lon2=<longitude> --lon2=<latitude>\n"
594 " [ ... --lon99=<longitude> --lon99=<latitude>]\n"
595 " [--highway-<highway>=<preference> ...]\n"
596 " [--speed-<highway>=<speed> ...]\n"
597 " [--property-<property>=<preference> ...]\n"
598 " [--oneway=(0|1)]\n"
599 " [--weight=<weight>]\n"
600 " [--height=<height>] [--width=<width>] [--length=<length>]\n");
605 "Error with command line parameter: %s\n",argerr);
615 "--help Prints this information.\n"
616 "--help-profile Prints the information about the selected profile.\n"
617 "--help-profile-xml Prints all loaded profiles in XML format.\n"
618 "--help-profile-json Prints all loaded profiles in JSON format.\n"
619 "--help-profile-perl Prints all loaded profiles in Perl format.\n"
621 "--dir=<dirname> The directory containing the routing database.\n"
622 "--prefix=<name> The filename prefix for the routing database.\n"
623 "--profiles=<filename> The name of the XML file containing the profiles\n"
624 " (defaults to 'profiles.xml' with '--dir' and\n"
625 " '--prefix' options or the file installed in\n"
627 "--translations=<fname> The name of the XML file containing the translations\n"
628 " (defaults to 'translations.xml' with '--dir' and\n"
629 " '--prefix' options or the file installed in\n"
632 "--exact-nodes-only Only route between nodes (don't find closest segment).\n"
634 "--quiet Don't print any screen output when running.\n"
635 "--language=<lang> Use the translations for specified language.\n"
636 "--output-html Write an HTML description of the route.\n"
637 "--output-gpx-track Write a GPX track file with all route points.\n"
638 "--output-gpx-route Write a GPX route file with interesting junctions.\n"
639 "--output-text Write a plain text file with interesting junctions.\n"
640 "--output-text-all Write a plain test file with all route points.\n"
641 "--output-none Don't write any output files or read any translations.\n"
642 " (If no output option is given then all are written.)\n"
644 "--profile=<name> Select the loaded profile with this name.\n"
645 "--transport=<transport> Select the transport to use (selects the profile\n"
646 " named after the transport if '--profile' is not used.)\n"
648 "--shortest Find the shortest route between the waypoints.\n"
649 "--quickest Find the quickest route between the waypoints.\n"
651 "--lon<n>=<longitude> Specify the longitude of the n'th waypoint.\n"
652 "--lat<n>=<latitude> Specify the latitude of the n'th waypoint.\n"
654 " Routing preference options\n"
655 "--highway-<highway>=<preference> * preference for highway type (%%).\n"
656 "--speed-<highway>=<speed> * speed for highway type (km/h).\n"
657 "--property-<property>=<preference> * preference for proprty type (%%).\n"
658 "--oneway=(0|1) * oneway streets are to be obeyed.\n"
659 "--weight=<weight> * maximum weight limit (tonnes).\n"
660 "--height=<height> * maximum height limit (metres).\n"
661 "--width=<width> * maximum width limit (metres).\n"
662 "--length=<length> * maximum length limit (metres).\n"
664 "<transport> defaults to motorcar but can be set to:\n"
667 "<highway> can be selected from:\n"
670 "<property> can be selected from:\n"
672 TransportList(),HighwayList(),PropertyList());