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