/***************************************
OSM router using libroutino library.
Part of the Routino routing software.
******************/ /******************
This file Copyright 2008-2016, 2018 Andrew M. Bishop
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
***************************************/
#include
#include
#include
#include
#include
#include
#include "version.h"
#include "routino.h"
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
/*+ The maximum number of waypoints +*/
#define NWAYPOINTS 99
/* Local functions */
static char *FileName(const char *dirname,const char *prefix, const char *name);
static void print_usage(int detail,const char *argerr,const char *err);
/*++++++++++++++++++++++++++++++++++++++
The main program for the router.
++++++++++++++++++++++++++++++++++++++*/
int main(int argc,char** argv)
{
Routino_Database *database;
Routino_Profile *profile;
Routino_Translation *translation;
Routino_Waypoint **waypoints;
Routino_Output *route;
int point_used[NWAYPOINTS+1]={0};
double point_lon[NWAYPOINTS+1],point_lat[NWAYPOINTS+1];
char *dirname=NULL,*prefix=NULL;
char *profiles=NULL,*profilename="motorcar";
char *translations=NULL,*language="en";
int reverse=0,loop=0;
int quickest=0;
int html=0,gpx_track=0,gpx_route=0,text=0,text_all=0,none=0,use_stdout=0;
int list_html=0,list_html_all=0,list_text=0,list_text_all=0;
int arg;
int first_waypoint=NWAYPOINTS,last_waypoint=1,waypoint,nwaypoints=0;
int routing_options;
/* Check the libroutino API version */
if(Routino_CheckAPIVersion()!=ROUTINO_ERROR_NONE)
{
fprintf(stderr,"Error: Executable version (%d) and library version (%d) do not match.\n",ROUTINO_API_VERSION,Routino_APIVersion);
exit(EXIT_FAILURE);
}
/* Parse the command line arguments */
if(argc<2)
print_usage(0,NULL,NULL);
/* Get the non-routing, general program options */
for(arg=1;argNWAYPOINTS || point_used[point]&1)
print_usage(0,argv[arg],NULL);
point_lon[point]=atof(p);
point_used[point]+=1;
if(pointlast_waypoint)
last_waypoint=point;
}
else if(!strncmp(argv[arg],"--lat",5) && isdigit(argv[arg][5]))
{
int point;
char *p=&argv[arg][6];
while(isdigit(*p)) p++;
if(*p++!='=')
print_usage(0,argv[arg],NULL);
point=atoi(&argv[arg][5]);
if(point>NWAYPOINTS || point_used[point]&2)
print_usage(0,argv[arg],NULL);
point_lat[point]=atof(p);
point_used[point]+=2;
if(pointlast_waypoint)
last_waypoint=point;
}
else
print_usage(0,argv[arg],NULL);
argv[arg]=NULL;
}
/* Check the specified command line options */
if(use_stdout && (html+gpx_track+gpx_route+text+text_all)!=1)
{
fprintf(stderr,"Error: The '--output-stdout' option requires exactly one other output option (but not '--output-none').\n");
exit(EXIT_FAILURE);
}
if(html==0 && gpx_track==0 && gpx_route==0 && text==0 && text_all==0 && none==0)
html=gpx_track=gpx_route=text=text_all=1;
/* Load in the selected profiles */
if(profiles)
{
if(access(profiles,F_OK))
{
fprintf(stderr,"Error: The '--profiles' option specifies a file '%s' that does not exist.\n",profiles);
exit(EXIT_FAILURE);
}
}
else
{
profiles=FileName(dirname,prefix,"profiles.xml");
if(access(profiles,F_OK))
{
char *defaultprofiles=FileName(ROUTINO_DATADIR,NULL,"profiles.xml");
if(access(defaultprofiles,F_OK))
{
fprintf(stderr,"Error: The '--profiles' option was not used and the files '%s' and '%s' do not exist.\n",profiles,defaultprofiles);
exit(EXIT_FAILURE);
}
free(profiles);
profiles=defaultprofiles;
}
}
if(!profilename)
{
fprintf(stderr,"Error: A profile name must be specified.\n");
exit(EXIT_FAILURE);
}
if(Routino_ParseXMLProfiles(profiles))
{
fprintf(stderr,"Error: Cannot read the profiles in the file '%s'.\n",profiles);
exit(EXIT_FAILURE);
}
profile=Routino_GetProfile(profilename);
if(!profile)
{
char **list=Routino_GetProfileNames();
fprintf(stderr,"Error: Cannot find a profile called '%s' in the file '%s'.\n",profilename,profiles);
fprintf(stderr,"Profiles available are: %s",*list++);
while(*list)
fprintf(stderr,", %s",*list++);
fprintf(stderr,"\n");
exit(EXIT_FAILURE);
}
/* Load in the selected translation */
if(translations)
{
if(access(translations,F_OK))
{
fprintf(stderr,"Error: The '--translations' option specifies a file '%s' that does not exist.\n",translations);
exit(EXIT_FAILURE);
}
}
else
{
translations=FileName(dirname,prefix,"translations.xml");
if(access(translations,F_OK))
{
char *defaulttranslations=FileName(ROUTINO_DATADIR,NULL,"translations.xml");
if(access(defaulttranslations,F_OK))
{
fprintf(stderr,"Error: The '--translations' option was not used and the files '%s' and '%s' do not exist.\n",translations,defaulttranslations);
exit(EXIT_FAILURE);
}
free(translations);
translations=defaulttranslations;
}
}
if(Routino_ParseXMLTranslations(translations))
{
fprintf(stderr,"Error: Cannot read the translations in the file '%s'.\n",translations);
exit(EXIT_FAILURE);
}
if(language)
{
translation=Routino_GetTranslation(language);
if(!translation)
{
char **list1=Routino_GetTranslationLanguages();
char **list2=Routino_GetTranslationLanguageFullNames();
fprintf(stderr,"Error: Cannot find a translation called '%s' in the file '%s'.\n",language,translations);
fprintf(stderr,"Languages available are: %s (%s)",*list1++,*list2++);
while(*list1)
fprintf(stderr,", %s (%s)",*list1++,*list2++);
fprintf(stderr,"\n");
exit(EXIT_FAILURE);
}
}
else
{
translation=Routino_GetTranslation(""); /* first in file */
if(!translation)
{
fprintf(stderr,"Error: No translations in '%s'.\n",translations);
exit(EXIT_FAILURE);
}
}
/* Check the waypoints are valid */
for(waypoint=first_waypoint;waypoint<=last_waypoint;waypoint++)
if(point_used[waypoint]==1 || point_used[waypoint]==2)
print_usage(0,NULL,"All waypoints must have latitude and longitude.");
else if(point_used[waypoint]==3)
nwaypoints++;
if(first_waypoint>=last_waypoint)
{
fprintf(stderr,"Error: At least two waypoints must be specified.\n");
exit(EXIT_FAILURE);
}
waypoints=calloc(sizeof(Routino_Waypoint*),nwaypoints+2);
/* Load in the routing database */
database=Routino_LoadDatabase(dirname,prefix);
/* Check the profile is valid for use with this database */
if(Routino_ValidateProfile(database,profile)!=ROUTINO_ERROR_NONE)
{
fprintf(stderr,"Error: Profile is invalid or not compatible with database.\n");
exit(EXIT_FAILURE);
}
/* Loop through all waypoints */
nwaypoints=0;
for(waypoint=first_waypoint;waypoint<=last_waypoint;waypoint++)
{
if(point_used[waypoint]!=3)
continue;
waypoints[nwaypoints]=Routino_FindWaypoint(database,profile,point_lat[waypoint],point_lon[waypoint]);
if(!waypoints[nwaypoints])
{
fprintf(stderr,"Error: Cannot find node close to specified point %d.\n",waypoint);
exit(EXIT_FAILURE);
}
nwaypoints++;
}
/* Create the route */
routing_options=0;
if(quickest)
routing_options|=ROUTINO_ROUTE_QUICKEST;
else
routing_options|=ROUTINO_ROUTE_SHORTEST;
if(html ) routing_options|=ROUTINO_ROUTE_FILE_HTML;
if(gpx_track) routing_options|=ROUTINO_ROUTE_FILE_GPX_TRACK;
if(gpx_route) routing_options|=ROUTINO_ROUTE_FILE_GPX_ROUTE;
if(text ) routing_options|=ROUTINO_ROUTE_FILE_TEXT;
if(text_all ) routing_options|=ROUTINO_ROUTE_FILE_TEXT_ALL;
if(list_html) routing_options|=ROUTINO_ROUTE_LIST_HTML;
if(list_html_all) routing_options|=ROUTINO_ROUTE_LIST_HTML_ALL;
if(list_text) routing_options|=ROUTINO_ROUTE_LIST_TEXT;
if(list_text_all) routing_options|=ROUTINO_ROUTE_LIST_TEXT_ALL;
if(reverse) routing_options|=ROUTINO_ROUTE_REVERSE;
if(loop) routing_options|=ROUTINO_ROUTE_LOOP;
route=Routino_CalculateRoute(database,profile,translation,waypoints,nwaypoints,routing_options,NULL);
if(Routino_errno>=ROUTINO_ERROR_NO_ROUTE_1)
{
fprintf(stderr,"Error: Cannot find a route between specified waypoints.\n");
exit(EXIT_FAILURE);
}
else if(Routino_errno!=ROUTINO_ERROR_NONE)
{
fprintf(stderr,"Error: Internal error (%d).\n",Routino_errno);
exit(EXIT_FAILURE);
}
/* Print the list output */
if(list_html || list_html_all || list_text || list_text_all)
{
Routino_Output *list=route;
int first=1,last;
while(list)
{
last=list->next?0:1;
printf("----------------\n");
printf("Lon,Lat: %.5f, %.5f\n",(180.0/M_PI)*list->lon,(180.0/M_PI)*list->lat);
if(list_html || list_html_all || list_text || list_text_all)
printf("Dist,Time: %.3f km, %.1f minutes\n",list->dist,list->time);
if(list_text_all && !first)
printf("Speed: %.0f km/hr\n",list->speed);
printf("Point type: %d\n",list->type);
if((list_html || list_html_all || list_text) && !first && !last)
printf("Turn: %d degrees\n",list->turn);
if(((list_html || list_html_all || list_text) && !last) || (list_text_all && !first))
printf("Bearing: %d degrees\n",list->bearing);
if(((list_html || list_text) && !last) || (list_html_all && list->name) || (list_text_all && !first))
printf("Name: %s\n",list->name);
if(list_html || (list_html_all && list->name))
{
printf("Desc1: %s\n",list->desc1);
printf("Desc2: %s\n",list->desc2);
if(!last)
printf("Desc3: %s\n",list->desc3);
}
list=list->next;
first=0;
}
}
/* Tidy up and exit */
Routino_DeleteRoute(route);
Routino_UnloadDatabase(database);
Routino_FreeXMLProfiles();
Routino_FreeXMLTranslations();
for(waypoint=0;waypoint=0)
{
fprintf(stderr,
"Usage: router [--version]\n"
" [--help ]\n"
" [--dir=] [--prefix=]\n"
" [--profiles=] [--translations=]\n"
" [--language=]\n"
" [--output-html]\n"
" [--output-gpx-track] [--output-gpx-route]\n"
" [--output-text] [--output-text-all]\n"
" [--output-none] [--output-stdout]\n"
" [--list-html | --list-html-all |\n"
" --list-text | --list-text-all]\n"
" [--profile=]\n"
" [--shortest | --quickest]\n"
" --lon1= --lat1=\n"
" --lon2= --lon2=\n"
" [ ... --lon99= --lon99=]\n"
" [--reverse] [--loop]\n");
if(argerr)
fprintf(stderr,
"\n"
"Error with command line parameter: %s\n",argerr);
if(err)
fprintf(stderr,
"\n"
"Error: %s\n",err);
}
if(detail==1)
fprintf(stderr,
"\n"
"--version Print the version of Routino.\n"
"\n"
"--help Prints this information.\n"
"\n"
"--dir= The directory containing the routing database.\n"
"--prefix= The filename prefix for the routing database.\n"
"--profiles= The name of the XML file containing the profiles\n"
" (defaults to 'profiles.xml' with '--dir' and\n"
" '--prefix' options or the file installed in\n"
" '" ROUTINO_DATADIR "').\n"
"--translations= The name of the XML file containing the translations\n"
" (defaults to 'translations.xml' with '--dir' and\n"
" '--prefix' options or the file installed in\n"
" '" ROUTINO_DATADIR "').\n"
"\n"
"--language= Use the translations for specified language.\n"
"--output-html Write an HTML description of the route.\n"
"--output-gpx-track Write a GPX track file with all route points.\n"
"--output-gpx-route Write a GPX route file with interesting junctions.\n"
"--output-text Write a plain text file with interesting junctions.\n"
"--output-text-all Write a plain text file with all route points.\n"
"--output-none Don't write any output files or read any translations.\n"
" (If no output option is given then all are written.)\n"
"--output-stdout Write to stdout instead of a file (requires exactly\n"
" one output format option, implies '--quiet').\n"
"\n"
"--list-html Create an HTML list of the route.\n"
"--list-html-all Create an HTML list of the route with all points.\n"
"--list-text Create a plain text list with interesting junctions.\n"
"--list-text-all Create a plain text list with all route points.\n"
"\n"
"--profile= Select the loaded profile with this name.\n"
"\n"
"--shortest Find the shortest route between the waypoints.\n"
"--quickest Find the quickest route between the waypoints.\n"
"\n"
"--lon= Specify the longitude of the n'th waypoint.\n"
"--lat= Specify the latitude of the n'th waypoint.\n"
"\n"
"--reverse Find a route between the waypoints in reverse order.\n"
"--loop Find a route that returns to the first waypoint.\n"
"\n");
exit(!detail);
}