Imported Upstream version 1.5.1
[routino] / src / router.c
1 /***************************************
2  $Header: /home/amb/routino/src/RCS/router.c,v 1.90 2010/11/13 14:22:28 amb Exp $
3
4  OSM router.
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 <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <ctype.h>
29
30 #include "types.h"
31 #include "nodes.h"
32 #include "segments.h"
33 #include "ways.h"
34
35 #include "files.h"
36 #include "logging.h"
37 #include "functions.h"
38 #include "translations.h"
39 #include "profiles.h"
40
41
42 /*+ The maximum distance from the specified point to search for a node or segment (in km). +*/
43 #define MAXSEARCH  1
44
45
46 /*+ A set of waypoint latitudes and longitudes. +*/
47 static double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
48
49 /*+ The option not to print any progress information. +*/
50 int option_quiet=0;
51
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;
54
55 /*+ The option to calculate the quickest route insted of the shortest. +*/
56 int option_quickest=0;
57
58
59 /* Local functions */
60
61 static void print_usage(int detail,const char *argerr,const char *err);
62
63
64 /*++++++++++++++++++++++++++++++++++++++
65   The main program for the router.
66   ++++++++++++++++++++++++++++++++++++++*/
67
68 int main(int argc,char** argv)
69 {
70  Nodes    *OSMNodes;
71  Segments *OSMSegments;
72  Ways     *OSMWays;
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;
79  int       exactnodes=0;
80  Transport transport=Transport_None;
81  Profile  *profile=NULL;
82  index_t   start=NO_NODE,finish=NO_NODE;
83  int       arg,point;
84
85  /* Parse the command line arguments */
86
87  if(argc<2)
88     print_usage(0,NULL,NULL);
89
90  /* Get the non-routing, general program options */
91
92  for(arg=1;arg<argc;arg++)
93    {
94     if(!strcmp(argv[arg],"--help"))
95        print_usage(1,NULL,NULL);
96     else if(!strcmp(argv[arg],"--help-profile"))
97        help_profile=1;
98     else if(!strcmp(argv[arg],"--help-profile-xml"))
99        help_profile_xml=1;
100     else if(!strcmp(argv[arg],"--help-profile-json"))
101        help_profile_json=1;
102     else if(!strcmp(argv[arg],"--help-profile-perl"))
103        help_profile_pl=1;
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"))
113        exactnodes=1;
114     else if(!strcmp(argv[arg],"--quiet"))
115        option_quiet=1;
116     else if(!strcmp(argv[arg],"--loggable"))
117        option_loggable=1;
118     else if(!strcmp(argv[arg],"--output-html"))
119        option_html=1;
120     else if(!strcmp(argv[arg],"--output-gpx-track"))
121        option_gpx_track=1;
122     else if(!strcmp(argv[arg],"--output-gpx-route"))
123        option_gpx_route=1;
124     else if(!strcmp(argv[arg],"--output-text"))
125        option_text=1;
126     else if(!strcmp(argv[arg],"--output-text-all"))
127        option_text_all=1;
128     else if(!strcmp(argv[arg],"--output-none"))
129        option_none=1;
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))
135       {
136        transport=TransportType(&argv[arg][12]);
137
138        if(transport==Transport_None)
139           print_usage(0,argv[arg],NULL);
140       }
141     else
142        continue;
143
144     argv[arg]=NULL;
145    }
146
147  /* Load in the profiles */
148
149  if(transport==Transport_None)
150     transport=Transport_Motorcar;
151
152  if(profiles)
153    {
154     if(!ExistsFile(profiles))
155       {
156        fprintf(stderr,"Error: The '--profiles' option specifies a file that does not exist.\n");
157        return(1);
158       }
159    }
160  else
161    {
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");
166     else
167       {
168        fprintf(stderr,"Error: The '--profiles' option was not used and the default 'profiles.xml' does not exist.\n");
169        return(1);
170       }
171    }
172
173  if(ParseXMLProfiles(profiles))
174    {
175     fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
176     return(1);
177    }
178
179  if(profilename)
180    {
181     profile=GetProfile(profilename);
182
183     if(!profile)
184       {
185        fprintf(stderr,"Error: Cannot find a profile called '%s' in '%s'.\n",profilename,profiles);
186        return(1);
187       }
188    }
189  else
190     profile=GetProfile(TransportName(transport));
191
192  if(!profile)
193    {
194     profile=(Profile*)calloc(1,sizeof(Profile));
195     profile->transport=transport;
196    }
197
198  /* Parse the other command line arguments */
199
200  for(arg=1;arg<argc;arg++)
201    {
202     if(!argv[arg])
203        continue;
204     else if(!strcmp(argv[arg],"--shortest"))
205        option_quickest=0;
206     else if(!strcmp(argv[arg],"--quickest"))
207        option_quickest=1;
208     else if(isdigit(argv[arg][0]) ||
209        ((argv[arg][0]=='-' || argv[arg][0]=='+') && isdigit(argv[arg][1])))
210       {
211        for(point=1;point<=NWAYPOINTS;point++)
212           if(point_used[point]!=3)
213             {
214              if(point_used[point]==0)
215                {
216                 point_lon[point]=degrees_to_radians(atof(argv[arg]));
217                 point_used[point]=1;
218                }
219              else /* if(point_used[point]==1) */
220                {
221                 point_lat[point]=degrees_to_radians(atof(argv[arg]));
222                 point_used[point]=3;
223                }
224              break;
225             }
226       }
227      else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
228        {
229         char *p=&argv[arg][6];
230         while(isdigit(*p)) p++;
231         if(*p++!='=')
232            print_usage(0,argv[arg],NULL);
233  
234         point=atoi(&argv[arg][5]);
235         if(point>NWAYPOINTS || point_used[point]&1)
236            print_usage(0,argv[arg],NULL);
237  
238        point_lon[point]=degrees_to_radians(atof(p));
239        point_used[point]+=1;
240       }
241      else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
242        {
243         char *p=&argv[arg][6];
244         while(isdigit(*p)) p++;
245         if(*p++!='=')
246            print_usage(0,argv[arg],NULL);
247  
248         point=atoi(&argv[arg][5]);
249         if(point>NWAYPOINTS || point_used[point]&2)
250            print_usage(0,argv[arg],NULL);
251  
252        point_lat[point]=degrees_to_radians(atof(p));
253        point_used[point]+=2;
254       }
255     else if(!strncmp(argv[arg],"--transport=",12))
256        ; /* Done this already */
257     else if(!strncmp(argv[arg],"--highway-",10))
258       {
259        Highway highway;
260        char *equal=strchr(argv[arg],'=');
261        char *string;
262
263        if(!equal)
264            print_usage(0,argv[arg],NULL);
265
266        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+10);
267        string[equal-argv[arg]-10]=0;
268
269        highway=HighwayType(string);
270
271        if(highway==Way_Count)
272           print_usage(0,argv[arg],NULL);
273
274        profile->highway[highway]=atof(equal+1);
275
276        free(string);
277       }
278     else if(!strncmp(argv[arg],"--speed-",8))
279       {
280        Highway highway;
281        char *equal=strchr(argv[arg],'=');
282        char *string;
283
284        if(!equal)
285           print_usage(0,argv[arg],NULL);
286
287        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+8);
288        string[equal-argv[arg]-8]=0;
289
290        highway=HighwayType(string);
291
292        if(highway==Way_Count)
293           print_usage(0,argv[arg],NULL);
294
295        profile->speed[highway]=kph_to_speed(atof(equal+1));
296
297        free(string);
298       }
299     else if(!strncmp(argv[arg],"--property-",11))
300       {
301        Property property;
302        char *equal=strchr(argv[arg],'=');
303        char *string;
304
305        if(!equal)
306           print_usage(0,argv[arg],NULL);
307
308        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+11);
309        string[equal-argv[arg]-11]=0;
310
311        property=PropertyType(string);
312
313        if(property==Way_Count)
314           print_usage(0,argv[arg],NULL);
315
316        profile->props_yes[property]=atof(equal+1);
317
318        free(string);
319       }
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]));
330     else
331        print_usage(0,argv[arg],NULL);
332    }
333
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.");
337
338  if(help_profile)
339    {
340     PrintProfile(profile);
341
342     return(0);
343    }
344  else if(help_profile_xml)
345    {
346     PrintProfilesXML();
347
348     return(0);
349    }
350  else if(help_profile_json)
351    {
352     PrintProfilesJSON();
353
354     return(0);
355    }
356  else if(help_profile_pl)
357    {
358     PrintProfilesPerl();
359
360     return(0);
361    }
362
363  /* Load in the translations */
364
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;
367
368  if(option_html || option_gpx_route || option_gpx_track)
369    {
370     if(translations)
371       {
372        if(!ExistsFile(translations))
373          {
374           fprintf(stderr,"Error: The '--translations' option specifies a file that does not exist.\n");
375           return(1);
376          }
377       }
378     else
379       {
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");
384        else
385          {
386           fprintf(stderr,"Error: The '--translations' option was not used and the default 'translations.xml' does not exist.\n");
387           return(1);
388          }
389       }
390
391     if(ParseXMLTranslations(translations,language))
392       {
393        fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
394        return(1);
395       }
396    }
397
398  /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
399
400  OSMNodes=LoadNodeList(FileName(dirname,prefix,"nodes.mem"));
401
402  OSMSegments=LoadSegmentList(FileName(dirname,prefix,"segments.mem"));
403
404  OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem"));
405
406  if(UpdateProfile(profile,OSMWays))
407    {
408     fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
409     return(1);
410    }
411
412  /* Loop through all pairs of points */
413
414  for(point=1;point<=NWAYPOINTS;point++)
415    {
416     Results *begin,*end;
417     distance_t distmax=km_to_distance(MAXSEARCH);
418     distance_t distmin;
419     index_t segment=NO_SEGMENT;
420     index_t node1,node2;
421
422     if(point_used[point]!=3)
423        continue;
424
425     /* Find the closest point */
426
427     start=finish;
428
429     if(exactnodes)
430       {
431        finish=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin);
432       }
433     else
434       {
435        distance_t dist1,dist2;
436
437        segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[point],point_lon[point],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
438
439        if(segment!=NO_SEGMENT)
440           finish=CreateFakes(OSMNodes,point,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
441        else
442           finish=NO_NODE;
443       }
444
445     if(finish==NO_NODE)
446       {
447        fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",point);
448        return(1);
449       }
450
451     if(!option_quiet)
452       {
453        double lat,lon;
454
455        if(IsFakeNode(finish))
456           GetFakeLatLong(finish,&lat,&lon);
457        else
458           GetLatLong(OSMNodes,finish,&lat,&lon);
459
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));
463        else
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));
466       }
467
468     if(start==NO_NODE)
469        continue;
470
471     if(start==finish)
472        continue;
473
474     /* Calculate the beginning of the route */
475
476     if(!IsFakeNode(start) && IsSuperNode(OSMNodes,start))
477       {
478        Result *result;
479
480        begin=NewResultsList(1);
481
482        begin->start=start;
483
484        result=InsertResult(begin,start);
485
486        ZeroResult(result);
487       }
488     else
489       {
490        begin=FindStartRoutes(OSMNodes,OSMSegments,OSMWays,start,profile);
491
492        if(!begin)
493          {
494           fprintf(stderr,"Error: Cannot find initial section of route compatible with profile.\n");
495           return(1);
496          }
497       }
498
499     if(FindResult(begin,finish))
500       {
501        FixForwardRoute(begin,finish);
502
503        results[point]=begin;
504
505        if(!option_quiet)
506          {
507           printf("Routed: Super-Nodes Checked = %d\n",begin->number);
508           fflush(stdout);
509          }
510       }
511     else
512       {
513        Results *superresults;
514
515        /* Calculate the end of the route */
516
517        if(!IsFakeNode(finish) && IsSuperNode(OSMNodes,finish))
518          {
519           Result *result;
520
521           end=NewResultsList(1);
522
523           end->finish=finish;
524
525           result=InsertResult(end,finish);
526
527           ZeroResult(result);
528          }
529        else
530          {
531           end=FindFinishRoutes(OSMNodes,OSMSegments,OSMWays,finish,profile);
532
533           if(!end)
534             {
535              fprintf(stderr,"Error: Cannot find final section of route compatible with profile.\n");
536              return(1);
537             }
538          }
539
540        /* Calculate the middle of the route */
541
542        superresults=FindMiddleRoute(OSMNodes,OSMSegments,OSMWays,begin,end,profile);
543
544        FreeResultsList(begin);
545        FreeResultsList(end);
546
547        if(!superresults)
548          {
549           fprintf(stderr,"Error: Cannot find route compatible with profile.\n");
550           return(1);
551          }
552
553        results[point]=CombineRoutes(superresults,OSMNodes,OSMSegments,OSMWays,profile);
554
555        FreeResultsList(superresults);
556       }
557    }
558
559  /* Print out the combined route */
560
561  if(!option_none)
562     PrintRoute(results,NWAYPOINTS,OSMNodes,OSMSegments,OSMWays,profile);
563
564  return(0);
565 }
566
567
568 /*++++++++++++++++++++++++++++++++++++++
569   Print out the usage information.
570
571   int detail The level of detail to use - 0 = low, 1 = high.
572
573   const char *argerr The argument that gave the error (if there is one).
574
575   const char *err Other error message (if there is one).
576   ++++++++++++++++++++++++++++++++++++++*/
577
578 static void print_usage(int detail,const char *argerr,const char *err)
579 {
580  fprintf(stderr,
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"
588          "              [--output-html]\n"
589          "              [--output-gpx-track] [--output-gpx-route]\n"
590          "              [--output-text] [--output-text-all]\n"
591          "              [--output-none]\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");
604
605  if(argerr)
606     fprintf(stderr,
607             "\n"
608             "Error with command line parameter: %s\n",argerr);
609
610  if(err)
611     fprintf(stderr,
612             "\n"
613             "Error: %s\n",err);
614
615  if(detail)
616     fprintf(stderr,
617             "\n"
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"
623             "\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"
629             "                         '" DATADIR "').\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"
633             "                         '" DATADIR "').\n"
634             "\n"
635             "--exact-nodes-only      Only route between nodes (don't find closest segment).\n"
636             "\n"
637             "--loggable              Print progress messages suitable for logging to file.\n"
638             "--quiet                 Don't print any screen output when running.\n"
639             "\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"
648             "\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"
652             "\n"
653             "--shortest              Find the shortest route between the waypoints.\n"
654             "--quickest              Find the quickest route between the waypoints.\n"
655             "\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"
658             "\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"
668             "\n"
669             "<transport> defaults to motorcar but can be set to:\n"
670             "%s"
671             "\n"
672             "<highway> can be selected from:\n"
673             "%s"
674             "\n"
675             "<property> can be selected from:\n"
676             "%s",
677             TransportList(),HighwayList(),PropertyList());
678
679  exit(!detail);
680 }