1 /***************************************
2  OSM router.
3 
4  Part of the Routino routing software.
5  ******************/ /******************
6  This file Copyright 2008-2016, 2018 Andrew M. Bishop
7 
8  This program is free software: you can redistribute it and/or modify
9  it under the terms of the GNU Affero General Public License as published by
10  the Free Software Foundation, either version 3 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU Affero General Public License for more details.
17 
18  You should have received a copy of the GNU Affero General Public License
19  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  ***************************************/
21 
22 
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include <ctype.h>
27 
28 #include "version.h"
29 
30 #include "types.h"
31 #include "nodes.h"
32 #include "segments.h"
33 #include "ways.h"
34 #include "relations.h"
35 
36 #include "files.h"
37 #include "logging.h"
38 #include "functions.h"
39 #include "fakes.h"
40 #include "translations.h"
41 #include "profiles.h"
42 
43 
44 /*+ The maximum distance from the specified point to search for a node or segment (in km). +*/
45 #define MAXSEARCH  1
46 
47 
48 /* Global variables */
49 
50 /*+ The option not to print any progress information. +*/
51 int option_quiet=0;
52 
53 /*+ The option to calculate the quickest route insted of the shortest. +*/
54 extern int option_quickest;
55 
56 /*+ The options to select the format of the file output. +*/
57 extern int option_file_html,option_file_gpx_track,option_file_gpx_route,option_file_text,option_file_text_all,option_file_stdout;
58 int option_file_none=0;
59 
60 
61 /* Local functions */
62 
63 static void print_usage(int detail,const char *argerr,const char *err);
64 
65 
66 /*++++++++++++++++++++++++++++++++++++++
67   The main program for the router.
68   ++++++++++++++++++++++++++++++++++++++*/
69 
main(int argc,char ** argv)70 int main(int argc,char** argv)
71 {
72  Nodes       *OSMNodes;
73  Segments    *OSMSegments;
74  Ways        *OSMWays;
75  Relations   *OSMRelations;
76  Results     *results[NWAYPOINTS+1]={NULL};
77  int          point_used[NWAYPOINTS+1]={0};
78  double       point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
79  index_t      point_node[NWAYPOINTS+1]={NO_NODE};
80  double       heading=-999;
81  int          help_profile=0,help_profile_xml=0,help_profile_json=0,help_profile_pl=0;
82  char        *dirname=NULL,*prefix=NULL;
83  char        *profiles=NULL,*profilename=NULL;
84  char        *translations=NULL,*language=NULL;
85  int          exactnodes=0,reverse=0,loop=0;
86  Transport    transport=Transport_None;
87  Profile     *profile=NULL;
88  Translation *translation=NULL;
89  index_t      start_node,finish_node=NO_NODE;
90  index_t      join_segment=NO_SEGMENT;
91  int          arg,nresults=0;
92  waypoint_t   start_waypoint,finish_waypoint=NO_WAYPOINT;
93  waypoint_t   first_waypoint=NWAYPOINTS,last_waypoint=1,waypoint;
94  int          inc_dec_waypoint=1;
95 
96  printf_program_start();
97 
98  /* Parse the command line arguments */
99 
100  if(argc<2)
101     print_usage(0,NULL,NULL);
102 
103  /* Get the non-routing, general program options */
104 
105  for(arg=1;arg<argc;arg++)
106    {
107     if(!strcmp(argv[arg],"--version"))
108        print_usage(-1,NULL,NULL);
109     else if(!strcmp(argv[arg],"--help"))
110        print_usage(1,NULL,NULL);
111     else if(!strcmp(argv[arg],"--help-profile"))
112        help_profile=1;
113     else if(!strcmp(argv[arg],"--help-profile-xml"))
114        help_profile_xml=1;
115     else if(!strcmp(argv[arg],"--help-profile-json"))
116        help_profile_json=1;
117     else if(!strcmp(argv[arg],"--help-profile-perl"))
118        help_profile_pl=1;
119     else if(!strncmp(argv[arg],"--dir=",6))
120        dirname=&argv[arg][6];
121     else if(!strncmp(argv[arg],"--prefix=",9))
122        prefix=&argv[arg][9];
123     else if(!strncmp(argv[arg],"--profiles=",11))
124        profiles=&argv[arg][11];
125     else if(!strncmp(argv[arg],"--translations=",15))
126        translations=&argv[arg][15];
127     else if(!strcmp(argv[arg],"--exact-nodes-only"))
128        exactnodes=1;
129     else if(!strncmp(argv[arg],"--reverse",9))
130       {
131        if(argv[arg][9]=='=')
132           reverse=atoi(&argv[arg][10]);
133        else
134           reverse=1;
135       }
136     else if(!strncmp(argv[arg],"--loop",6))
137       {
138        if(argv[arg][6]=='=')
139           loop=atoi(&argv[arg][7]);
140        else
141           loop=1;
142       }
143     else if(!strcmp(argv[arg],"--quiet"))
144        option_quiet=1;
145     else if(!strcmp(argv[arg],"--loggable"))
146        option_loggable=1;
147     else if(!strcmp(argv[arg],"--logtime"))
148        option_logtime=2;
149     else if(!strcmp(argv[arg],"--logmemory"))
150        option_logmemory=1;
151     else if(!strcmp(argv[arg],"--output-html"))
152        option_file_html=1;
153     else if(!strcmp(argv[arg],"--output-gpx-track"))
154        option_file_gpx_track=1;
155     else if(!strcmp(argv[arg],"--output-gpx-route"))
156        option_file_gpx_route=1;
157     else if(!strcmp(argv[arg],"--output-text"))
158        option_file_text=1;
159     else if(!strcmp(argv[arg],"--output-text-all"))
160        option_file_text_all=1;
161     else if(!strcmp(argv[arg],"--output-none"))
162        option_file_none=1;
163     else if(!strcmp(argv[arg],"--output-stdout"))
164       { option_file_stdout=1; option_quiet=1; }
165     else if(!strncmp(argv[arg],"--profile=",10))
166        profilename=&argv[arg][10];
167     else if(!strncmp(argv[arg],"--language=",11))
168        language=&argv[arg][11];
169     else if(!strcmp(argv[arg],"--shortest"))
170        option_quickest=0;
171     else if(!strcmp(argv[arg],"--quickest"))
172        option_quickest=1;
173     else if(!strncmp(argv[arg],"--lon",5) && isdigit(argv[arg][5]))
174       {
175        int point;
176        char *p=&argv[arg][6];
177 
178        while(isdigit(*p)) p++;
179        if(*p++!='=')
180           print_usage(0,argv[arg],NULL);
181 
182        point=atoi(&argv[arg][5]);
183        if(point>NWAYPOINTS || point_used[point]&1)
184           print_usage(0,argv[arg],NULL);
185 
186        point_lon[point]=degrees_to_radians(atof(p));
187        point_used[point]+=1;
188 
189        if(point<first_waypoint)
190           first_waypoint=point;
191        if(point>last_waypoint)
192           last_waypoint=point;
193       }
194     else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
195       {
196        int point;
197        char *p=&argv[arg][6];
198 
199        while(isdigit(*p)) p++;
200        if(*p++!='=')
201           print_usage(0,argv[arg],NULL);
202 
203        point=atoi(&argv[arg][5]);
204        if(point>NWAYPOINTS || point_used[point]&2)
205           print_usage(0,argv[arg],NULL);
206 
207        point_lat[point]=degrees_to_radians(atof(p));
208        point_used[point]+=2;
209 
210        if(point<first_waypoint)
211           first_waypoint=point;
212        if(point>last_waypoint)
213           last_waypoint=point;
214       }
215     else if(!strncmp(argv[arg],"--heading=",10))
216       {
217        double h=atof(&argv[arg][10]);
218 
219        if(h>=-360 && h<=360)
220          {
221           heading=h;
222 
223           if(heading<0) heading+=360;
224          }
225       }
226     else if(!strncmp(argv[arg],"--transport=",12))
227       {
228        transport=TransportType(&argv[arg][12]);
229 
230        if(transport==Transport_None)
231           print_usage(0,argv[arg],NULL);
232       }
233     else
234        continue;
235 
236     argv[arg]=NULL;
237    }
238 
239  /* Check the specified command line options */
240 
241  if(option_file_stdout && (option_file_html+option_file_gpx_track+option_file_gpx_route+option_file_text+option_file_text_all)!=1)
242    {
243     fprintf(stderr,"Error: The '--output-stdout' option requires exactly one other output option (but not '--output-none').\n");
244     exit(EXIT_FAILURE);
245    }
246 
247  if(option_file_html==0 && option_file_gpx_track==0 && option_file_gpx_route==0 && option_file_text==0 && option_file_text_all==0 && option_file_none==0)
248     option_file_html=option_file_gpx_track=option_file_gpx_route=option_file_text=option_file_text_all=1;
249 
250  /* Load in the selected profiles */
251 
252  if(transport==Transport_None)
253     transport=Transport_Motorcar;
254 
255  if(profiles)
256    {
257     if(!ExistsFile(profiles))
258       {
259        fprintf(stderr,"Error: The '--profiles' option specifies a file '%s' that does not exist.\n",profiles);
260        exit(EXIT_FAILURE);
261       }
262    }
263  else
264    {
265     profiles=FileName(dirname,prefix,"profiles.xml");
266 
267     if(!ExistsFile(profiles))
268       {
269        char *defaultprofiles=FileName(ROUTINO_DATADIR,NULL,"profiles.xml");
270 
271        if(!ExistsFile(defaultprofiles))
272          {
273           fprintf(stderr,"Error: The '--profiles' option was not used and the files '%s' and '%s' do not exist.\n",profiles,defaultprofiles);
274           exit(EXIT_FAILURE);
275          }
276 
277        free(profiles);
278        profiles=defaultprofiles;
279       }
280    }
281 
282  if(!profilename)
283     profilename=(char*)TransportName(transport);
284 
285  if(ParseXMLProfiles(profiles,profilename,(help_profile_xml|help_profile_json|help_profile_pl)))
286    {
287     fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
288     exit(EXIT_FAILURE);
289    }
290 
291  profile=GetProfile(profilename);
292 
293  if(!profile)
294    {
295     fprintf(stderr,"Error: Cannot find a profile called '%s' in the file '%s'.\n",profilename,profiles);
296 
297     profile=(Profile*)calloc(1,sizeof(Profile));
298     profile->transport=transport;
299    }
300 
301  /* Parse the other command line arguments that modify the profile */
302 
303  for(arg=1;arg<argc;arg++)
304    {
305     if(!argv[arg])
306        continue;
307     else if(!strncmp(argv[arg],"--highway-",10))
308       {
309        Highway highway;
310        char *equal=strchr(argv[arg],'=');
311        char *string;
312        double p;
313 
314        if(!equal)
315            print_usage(0,argv[arg],NULL);
316 
317        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+10);
318        string[equal-argv[arg]-10]=0;
319 
320        highway=HighwayType(string);
321 
322        if(highway==Highway_None)
323           print_usage(0,argv[arg],NULL);
324 
325        p=atof(equal+1);
326 
327        if(p<0 || p>100)
328           print_usage(0,argv[arg],NULL);
329 
330        profile->highway[highway]=(score_t)(p/100);
331 
332        free(string);
333       }
334     else if(!strncmp(argv[arg],"--speed-",8))
335       {
336        Highway highway;
337        char *equal=strchr(argv[arg],'=');
338        char *string;
339        double s;
340 
341        if(!equal)
342           print_usage(0,argv[arg],NULL);
343 
344        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+8);
345        string[equal-argv[arg]-8]=0;
346 
347        highway=HighwayType(string);
348 
349        if(highway==Highway_None)
350           print_usage(0,argv[arg],NULL);
351 
352        s=atof(equal+1);
353 
354        if(s<0)
355           print_usage(0,argv[arg],NULL);
356 
357        profile->speed[highway]=kph_to_speed(s);
358 
359        free(string);
360       }
361     else if(!strncmp(argv[arg],"--property-",11))
362       {
363        Property property;
364        char *equal=strchr(argv[arg],'=');
365        char *string;
366        double p;
367 
368        if(!equal)
369           print_usage(0,argv[arg],NULL);
370 
371        string=strcpy((char*)malloc(strlen(argv[arg])),argv[arg]+11);
372        string[equal-argv[arg]-11]=0;
373 
374        property=PropertyType(string);
375 
376        if(property==Property_None)
377           print_usage(0,argv[arg],NULL);
378 
379        p=atof(equal+1);
380 
381        if(p<0 || p>100)
382           print_usage(0,argv[arg],NULL);
383 
384        profile->props[property]=(score_t)(p/100);
385 
386        free(string);
387       }
388     else if(!strncmp(argv[arg],"--oneway=",9))
389        profile->oneway=!!atoi(&argv[arg][9]);
390     else if(!strncmp(argv[arg],"--turns=",8))
391        profile->turns=!!atoi(&argv[arg][8]);
392     else if(!strncmp(argv[arg],"--weight=",9))
393        profile->weight=tonnes_to_weight(atof(&argv[arg][9]));
394     else if(!strncmp(argv[arg],"--height=",9))
395        profile->height=metres_to_height(atof(&argv[arg][9]));
396     else if(!strncmp(argv[arg],"--width=",8))
397        profile->width=metres_to_width(atof(&argv[arg][8]));
398     else if(!strncmp(argv[arg],"--length=",9))
399        profile->length=metres_to_length(atof(&argv[arg][9]));
400     else
401        print_usage(0,argv[arg],NULL);
402    }
403 
404  /* Print one of the profiles if requested */
405 
406  if(help_profile)
407    {
408     PrintProfile(profile);
409 
410     exit(EXIT_SUCCESS);
411    }
412  else if(help_profile_xml)
413    {
414     PrintProfilesXML();
415 
416     exit(EXIT_SUCCESS);
417    }
418  else if(help_profile_json)
419    {
420     PrintProfilesJSON();
421 
422     exit(EXIT_SUCCESS);
423    }
424  else if(help_profile_pl)
425    {
426     PrintProfilesPerl();
427 
428     exit(EXIT_SUCCESS);
429    }
430 
431  /* Load in the selected translation */
432 
433  if(option_file_html || option_file_gpx_route || option_file_gpx_track || option_file_text || option_file_text_all)
434    {
435     if(translations)
436       {
437        if(!ExistsFile(translations))
438          {
439           fprintf(stderr,"Error: The '--translations' option specifies a file '%s' that does not exist.\n",translations);
440           exit(EXIT_FAILURE);
441          }
442       }
443     else
444       {
445        translations=FileName(dirname,prefix,"translations.xml");
446 
447        if(!ExistsFile(translations))
448          {
449           char *defaulttranslations=FileName(ROUTINO_DATADIR,NULL,"translations.xml");
450 
451           if(!ExistsFile(defaulttranslations))
452             {
453              fprintf(stderr,"Error: The '--translations' option was not used and the files '%s' and '%s' do not exist.\n",translations,defaulttranslations);
454              exit(EXIT_FAILURE);
455             }
456 
457           free(translations);
458           translations=defaulttranslations;
459          }
460       }
461 
462     if(ParseXMLTranslations(translations,language,0))
463       {
464        fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
465        exit(EXIT_FAILURE);
466       }
467 
468     if(language)
469       {
470        translation=GetTranslation(language);
471 
472        if(!translation)
473          {
474           fprintf(stderr,"Error: Cannot find a translation called '%s' in the file '%s'.\n",language,translations);
475           exit(EXIT_FAILURE);
476          }
477       }
478     else
479       {
480        translation=GetTranslation("");
481 
482        if(!translation)
483          {
484           fprintf(stderr,"Error: No translations in '%s'.\n",translations);
485           exit(EXIT_FAILURE);
486          }
487       }
488    }
489 
490  /* Check the waypoints are valid */
491 
492  for(waypoint=1;waypoint<=NWAYPOINTS;waypoint++)
493     if(point_used[waypoint]==1 || point_used[waypoint]==2)
494        print_usage(0,NULL,"All waypoints must have latitude and longitude.");
495 
496  if(first_waypoint>=last_waypoint)
497     print_usage(0,NULL,"At least two waypoints must be specified.");
498 
499  /* Load in the data - Note: No error checking because Load*List() will call exit() in case of an error. */
500 
501  if(!option_quiet)
502     printf_first("Loading Files:");
503 
504  OSMNodes=LoadNodeList(FileName(dirname,prefix,"nodes.mem"));
505 
506  OSMSegments=LoadSegmentList(FileName(dirname,prefix,"segments.mem"));
507 
508  OSMWays=LoadWayList(FileName(dirname,prefix,"ways.mem"));
509 
510  OSMRelations=LoadRelationList(FileName(dirname,prefix,"relations.mem"));
511 
512  if(!option_quiet)
513     printf_last("Loaded Files: nodes, segments, ways & relations");
514 
515  /* Check the profile is valid for use with this database */
516 
517  if(UpdateProfile(profile,OSMWays))
518    {
519     fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
520     exit(EXIT_FAILURE);
521    }
522 
523  /* Find all waypoints */
524 
525  for(waypoint=first_waypoint;waypoint<=last_waypoint;waypoint++)
526    {
527     distance_t distmax=km_to_distance(MAXSEARCH);
528     distance_t distmin;
529     index_t segment=NO_SEGMENT;
530     index_t node1,node2,node=NO_NODE;
531 
532     if(point_used[waypoint]!=3)
533        continue;
534 
535     /* Find the closest point */
536 
537     if(!option_quiet)
538        printf_first("Finding Closest Point: Waypoint %d",waypoint);
539 
540     if(exactnodes)
541        node=FindClosestNode(OSMNodes,OSMSegments,OSMWays,point_lat[waypoint],point_lon[waypoint],distmax,profile,&distmin);
542     else
543       {
544        distance_t dist1,dist2;
545 
546        segment=FindClosestSegment(OSMNodes,OSMSegments,OSMWays,point_lat[waypoint],point_lon[waypoint],distmax,profile,&distmin,&node1,&node2,&dist1,&dist2);
547 
548        if(segment!=NO_SEGMENT)
549           node=CreateFakes(OSMNodes,OSMSegments,waypoint,LookupSegment(OSMSegments,segment,1),node1,node2,dist1,dist2);
550       }
551 
552     if(!option_quiet)
553        printf_last("Found Closest Point: Waypoint %d",waypoint);
554 
555     if(node==NO_NODE)
556       {
557        fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",waypoint);
558        exit(EXIT_FAILURE);
559       }
560 
561     if(!option_quiet)
562       {
563        double lat,lon;
564 
565        if(IsFakeNode(node))
566           GetFakeLatLong(node,&lat,&lon);
567        else
568           GetLatLong(OSMNodes,node,NULL,&lat,&lon);
569 
570        if(IsFakeNode(node))
571           printf("Waypoint %d is segment %"Pindex_t" (node %"Pindex_t" -> %"Pindex_t"): %3.6f %4.6f = %2.3f km\n",waypoint,segment,node1,node2,
572                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
573        else
574           printf("Waypoint %d is node %"Pindex_t": %3.6f %4.6f = %2.3f km\n",waypoint,node,
575                  radians_to_degrees(lon),radians_to_degrees(lat),distance_to_km(distmin));
576       }
577 
578     point_node[waypoint]=node;
579    }
580 
581  /* Check for reverse direction */
582 
583  if(reverse)
584    {
585     waypoint_t temp;
586 
587     temp=first_waypoint;
588     first_waypoint=last_waypoint;
589     last_waypoint=temp;
590 
591     inc_dec_waypoint=-1;
592    }
593 
594  /* Loop through all pairs of waypoints */
595 
596  if(loop && reverse)
597    {
598     finish_node=point_node[last_waypoint];
599 
600     finish_waypoint=last_waypoint;
601    }
602 
603  for(waypoint=first_waypoint;waypoint!=(last_waypoint+inc_dec_waypoint);waypoint+=inc_dec_waypoint)
604    {
605     if(point_used[waypoint]!=3)
606        continue;
607 
608     start_node=finish_node;
609     finish_node=point_node[waypoint];
610 
611     start_waypoint=finish_waypoint;
612     finish_waypoint=waypoint;
613 
614     if(start_node==NO_NODE)
615        continue;
616 
617     if(heading!=-999 && join_segment==NO_SEGMENT)
618        join_segment=FindClosestSegmentHeading(OSMNodes,OSMSegments,OSMWays,start_node,heading,profile);
619 
620     /* Calculate the route */
621 
622     if(!option_quiet)
623        printf("Routing from waypoint %d to waypoint %d\n",start_waypoint,finish_waypoint);
624 
625     results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,start_waypoint,finish_waypoint);
626 
627     if(!results[nresults])
628        exit(EXIT_FAILURE);
629 
630     join_segment=results[nresults]->last_segment;
631 
632     nresults++;
633    }
634 
635  if(loop && !reverse)
636    {
637     start_node=finish_node;
638     finish_node=point_node[first_waypoint];
639 
640     start_waypoint=finish_waypoint;
641     finish_waypoint=first_waypoint;
642 
643     /* Calculate the route */
644 
645     if(!option_quiet)
646        printf("Routing from waypoint %d to waypoint %d\n",start_waypoint,finish_waypoint);
647 
648     results[nresults]=CalculateRoute(OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,start_node,join_segment,finish_node,start_waypoint,finish_waypoint);
649 
650     if(!results[nresults])
651        exit(EXIT_FAILURE);
652 
653     nresults++;
654    }
655 
656  if(!option_quiet)
657    {
658     printf("Routed OK\n");
659     fflush(stdout);
660    }
661 
662  /* Print out the combined route */
663 
664  if(!option_quiet)
665     printf_first("Generating Result Outputs");
666 
667  if(!option_file_none)
668     PrintRoute(results,nresults,OSMNodes,OSMSegments,OSMWays,OSMRelations,profile,translation);
669 
670  if(!option_quiet)
671     printf_last("Generated Result Outputs");
672 
673  /* Destroy the remaining results lists and data structures */
674 
675 #ifdef DEBUG_MEMORY_LEAK
676 
677  for(waypoint=0;waypoint<nresults;waypoint++)
678     FreeResultsList(results[waypoint]);
679 
680  DestroyNodeList(OSMNodes);
681  DestroySegmentList(OSMSegments);
682  DestroyWayList(OSMWays);
683  DestroyRelationList(OSMRelations);
684 
685  FreeXMLProfiles();
686 
687  FreeXMLTranslations();
688 
689 #endif
690 
691  if(!option_quiet)
692     printf_program_end();
693 
694  exit(EXIT_SUCCESS);
695 }
696 
697 
698 /*++++++++++++++++++++++++++++++++++++++
699   Print out the usage information.
700 
701   int detail The level of detail to use: -1 = just version number, 0 = low detail, 1 = full details.
702 
703   const char *argerr The argument that gave the error (if there is one).
704 
705   const char *err Other error message (if there is one).
706   ++++++++++++++++++++++++++++++++++++++*/
707 
print_usage(int detail,const char * argerr,const char * err)708 static void print_usage(int detail,const char *argerr,const char *err)
709 {
710  if(detail<0)
711    {
712     fprintf(stderr,
713             "Routino version " ROUTINO_VERSION " " ROUTINO_URL ".\n"
714             );
715    }
716 
717  if(detail>=0)
718    {
719     fprintf(stderr,
720             "Usage: router [--version]\n"
721             "              [--help | --help-profile | --help-profile-xml |\n"
722             "                        --help-profile-json | --help-profile-perl ]\n"
723             "              [--dir=<dirname>] [--prefix=<name>]\n"
724             "              [--profiles=<filename>] [--translations=<filename>]\n"
725             "              [--exact-nodes-only]\n"
726             "              [--quiet | [--loggable] [--logtime] [--logmemory]]\n"
727             "              [--language=<lang>]\n"
728             "              [--output-html]\n"
729             "              [--output-gpx-track] [--output-gpx-route]\n"
730             "              [--output-text] [--output-text-all]\n"
731             "              [--output-none] [--output-stdout]\n"
732             "              [--profile=<name>]\n"
733             "              [--transport=<transport>]\n"
734             "              [--shortest | --quickest]\n"
735             "              --lon1=<longitude> --lat1=<latitude>\n"
736             "              --lon2=<longitude> --lon2=<latitude>\n"
737             "              [ ... --lon99=<longitude> --lon99=<latitude>]\n"
738             "              [--reverse] [--loop]\n"
739             "              [--highway-<highway>=<preference> ...]\n"
740             "              [--speed-<highway>=<speed> ...]\n"
741             "              [--property-<property>=<preference> ...]\n"
742             "              [--oneway=(0|1)] [--turns=(0|1)]\n"
743             "              [--weight=<weight>]\n"
744             "              [--height=<height>] [--width=<width>] [--length=<length>]\n");
745 
746     if(argerr)
747        fprintf(stderr,
748                "\n"
749                "Error with command line parameter: %s\n",argerr);
750 
751     if(err)
752        fprintf(stderr,
753                "\n"
754                "Error: %s\n",err);
755    }
756 
757  if(detail==1)
758     fprintf(stderr,
759             "\n"
760             "--version               Print the version of Routino.\n"
761             "\n"
762             "--help                  Prints this information.\n"
763             "--help-profile          Prints the information about the selected profile.\n"
764             "--help-profile-xml      Prints all loaded profiles in XML format.\n"
765             "--help-profile-json     Prints all loaded profiles in JSON format.\n"
766             "--help-profile-perl     Prints all loaded profiles in Perl format.\n"
767             "\n"
768             "--dir=<dirname>         The directory containing the routing database.\n"
769             "--prefix=<name>         The filename prefix for the routing database.\n"
770             "--profiles=<filename>   The name of the XML file containing the profiles\n"
771             "                        (defaults to 'profiles.xml' with '--dir' and\n"
772             "                         '--prefix' options or the file installed in\n"
773             "                         '" ROUTINO_DATADIR "').\n"
774             "--translations=<fname>  The name of the XML file containing the translations\n"
775             "                        (defaults to 'translations.xml' with '--dir' and\n"
776             "                         '--prefix' options or the file installed in\n"
777             "                         '" ROUTINO_DATADIR "').\n"
778             "\n"
779             "--exact-nodes-only      Only route between nodes (don't find closest segment).\n"
780             "\n"
781             "--quiet                 Don't print any screen output when running.\n"
782             "--loggable              Print progress messages suitable for logging to file.\n"
783             "--logtime               Print the elapsed time for each processing step.\n"
784             "--logmemory             Print the max allocated/mapped memory for each step.\n"
785             "\n"
786             "--language=<lang>       Use the translations for specified language.\n"
787             "--output-html           Write an HTML description of the route.\n"
788             "--output-gpx-track      Write a GPX track file with all route points.\n"
789             "--output-gpx-route      Write a GPX route file with interesting junctions.\n"
790             "--output-text           Write a plain text file with interesting junctions.\n"
791             "--output-text-all       Write a plain text file with all route points.\n"
792             "--output-none           Don't write any output files or read any translations.\n"
793             "                        (If no output option is given then all are written.)\n"
794             "--output-stdout         Write to stdout instead of a file (requires exactly\n"
795             "                        one output format option, implies '--quiet').\n"
796             "\n"
797             "--profile=<name>        Select the loaded profile with this name.\n"
798             "--transport=<transport> Select the transport to use (selects the profile\n"
799             "                        named after the transport if '--profile' is not used.)\n"
800             "\n"
801             "--shortest              Find the shortest route between the waypoints.\n"
802             "--quickest              Find the quickest route between the waypoints.\n"
803             "\n"
804             "--lon<n>=<longitude>    Specify the longitude of the n'th waypoint.\n"
805             "--lat<n>=<latitude>     Specify the latitude of the n'th waypoint.\n"
806             "\n"
807             "--reverse               Find a route between the waypoints in reverse order.\n"
808             "--loop                  Find a route that returns to the first waypoint.\n"
809             "\n"
810             "--heading=<bearing>     Initial compass bearing at lowest numbered waypoint.\n"
811             "\n"
812             "                                   Routing preference options\n"
813             "--highway-<highway>=<preference>   * preference for highway type (%%).\n"
814             "--speed-<highway>=<speed>          * speed for highway type (km/h).\n"
815             "--property-<property>=<preference> * preference for proprty type (%%).\n"
816             "--oneway=(0|1)                     * oneway restrictions are to be obeyed.\n"
817             "--turns=(0|1)                      * turn restrictions are to be obeyed.\n"
818             "--weight=<weight>                  * maximum weight limit (tonnes).\n"
819             "--height=<height>                  * maximum height limit (metres).\n"
820             "--width=<width>                    * maximum width limit (metres).\n"
821             "--length=<length>                  * maximum length limit (metres).\n"
822             "\n"
823             "<transport> defaults to motorcar but can be set to:\n"
824             "%s"
825             "\n"
826             "<highway> can be selected from:\n"
827             "%s"
828             "\n"
829             "<property> can be selected from:\n"
830             "%s",
831             TransportList(),HighwayList(),PropertyList());
832 
833  exit(!detail);
834 }
835