File: router.py

package info (click to toggle)
routino 3.4.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 9,148 kB
  • sloc: ansic: 22,554; javascript: 7,897; xml: 5,980; perl: 2,597; cs: 921; makefile: 828; sh: 503; cpp: 382; python: 376
file content (333 lines) | stat: -rwxr-xr-x 13,597 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#!/usr/bin/python3
##########################################
# OSM router calling libroutino library from Python.
#
# Part of the Routino routing software.
##########################################
# This file Copyright 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 <http://www.gnu.org/licenses/>.
##########################################

import argparse
import sys
import os
import math

import routino.router as routino


# Parse the command line arguments

argparser = argparse.ArgumentParser(description="Calculates a route using Routino and command line data.")

argparser.add_argument("--version",          dest="version",       action='store_false',   help="Print the version of Routino.")

argparser.add_argument("--dir",              dest="dirname",       type=str, default=None, help="The directory containing the routing database.")
argparser.add_argument("--prefix",           dest="prefix",        type=str, default=None, help="The filename prefix for the routing database.")

argparser.add_argument("--profiles",         dest="profiles",      type=str, default=None, help="The name of the XML file containing the profiles (defaults to 'profiles.xml' with '--dir' and '--prefix' options).")
argparser.add_argument("--translations",     dest="translations",  type=str, default=None, help="The name of the XML file containing the translations (defaults to 'translations.xml' with '--dir' and '--prefix' options).")

argparser.add_argument("--reverse",          dest="reverse",       action='store_true',    help="Find a route between the waypoints in reverse order.")
argparser.add_argument("--loop",             dest="loop",          action='store_true',    help="Find a route that returns to the first waypoint.")

argparser.add_argument("--output-html",      dest="html",          action='store_true',    help="Write an HTML description of the route.")
argparser.add_argument("--output-gpx-track", dest="gpx_track",     action='store_true',    help="Write a GPX track file with all route points.")
argparser.add_argument("--output-gpx-route", dest="gpx_route",     action='store_true',    help="Write a GPX route file with interesting junctions.")
argparser.add_argument("--output-text",      dest="text",          action='store_true',    help="Write a plain text file with interesting junctions.")
argparser.add_argument("--output-text-all",  dest="text_all",      action='store_true',    help="Write a plain text file with all route points.")
argparser.add_argument("--output-none",      dest="none",          action='store_true',    help="Don't write any output files or read any translations. (If no output option is given then all are written.)")
argparser.add_argument("--output-stdout",    dest="use_stdout",    action='store_true',    help="Write to stdout instead of a file (requires exactly one output format option, implies '--quiet').")

argparser.add_argument("--list-html",        dest="list_html",     action='store_true',    help="Create an HTML list of the route.")
argparser.add_argument("--list-html-all",    dest="list_html_all", action='store_true',    help="Create an HTML list of the route with all points.")
argparser.add_argument("--list-text",        dest="list_text",     action='store_true',    help="Create a plain text list with interesting junctions.")
argparser.add_argument("--list-text-all",    dest="list_text_all", action='store_true',    help="Create a plain text list with all route points.")

argparser.add_argument("--profile",          dest="profilename",   type=str, default=None, help="Select the loaded profile with this name.")
argparser.add_argument("--language",         dest="language",      type=str, default=None, help="Use the translations for specified language.")

argparser.add_argument("--quickest",         dest="shortest",      action='store_false',   help="Find the quickest route between the waypoints.")
argparser.add_argument("--shortest",         dest="shortest",      action='store_true',    help="Find the shortest route between the waypoints.")

argparser.add_argument("--lon",              dest="lons",          action='append', type=float, help="Specify the longitude of the next waypoint (can also use '--lon<n>' to specify the n'th longitude).")
argparser.add_argument("--lat",              dest="lats",          action='append', type=float, help="Specify the latitude of the next waypoint (can also use '--lat<n>' to specify the n'th latitude).")

for i in range(1,99):
    argparser.add_argument("--lon"+str(i),   dest="lon"+str(i),    type=float, help=argparse.SUPPRESS)
    argparser.add_argument("--lat"+str(i),   dest="lat"+str(i),    type=float, help=argparse.SUPPRESS)

args = argparser.parse_args()


# Check the specified command line options

if args.use_stdout and (int(args.html)+int(args.gpx_track)+int(args.gpx_route)+int(args.text)+int(args.text_all))!=1:
    print("Error: The '--output-stdout' option requires exactly one other output option (but not '--output-none').")
    sys.exit(1)

if not args.html and not args.gpx_track and not args.gpx_route and not args.text and not args.text_all and not args.none:
    args.html=True
    args.gpx_track=True
    args.gpx_route=True
    args.text=True
    args.text_all=True


# Load in the selected profiles

if args.profiles is not None:
    if not os.access(args.profiles,os.F_OK):
        print("Error: The '--profiles' option specifies a file '{:s}' that does not exist.".format(args.profiles))
        sys.exit(1)
else:
    args.profiles=routino.FileName(args.dirname,args.prefix,"profiles.xml")

    if not os.access(args.profiles,os.F_OK):
        defaultprofiles = routino.FileName("../xml/","routino","profiles.xml")

        if not os.access(defaultprofiles,os.F_OK):
            print("Error: The '--profiles' option was not used and the files '{:s}' and '{:s}' do not exist.".format(args.profiles,defaultprofiles))
            sys.exit(1)

        args.profiles=defaultprofiles

if args.profilename is None:
    print("Error: A profile name must be specified")
    sys.exit(1)

if routino.ParseXMLProfiles(args.profiles):
    print("Error: Cannot read the profiles in the file '{:s}'.".format(args.profiles))
    sys.exit(1)

profile=routino.GetProfile(args.profilename)

if profile is None:
    list = routino.GetProfileNames()

    print("Error: Cannot find a profile called '{:s}' in the file '{:s}'.".format(args.profilename,args.profiles))
    print("Profiles available are: {:s}.".format(", ".join(list)))
    sys.exit(1)


# Load in the selected translation

if args.translations is not None:
    if not os.access(args.translations,os.F_OK):
        print("Error: The '--translations' option specifies a file '{:s}' that does not exist.".format(args.translations))
        sys.exit(1)

else:
    args.translations=routino.FileName(args.dirname,args.prefix,"translations.xml")

    if not os.access(translations,os.F_OK):
        defaulttranslations = routino.FileName("../xml/","routino","translations.xml")

        if not os.access(defaulttranslations,os.F_OK):
            print("Error: The '--translations' option was not used and the files '{:s}' and '{:s}' do not exist.".format(args.translations,defaulttranslations))
            sys.exit(1)

        args.translations=defaulttranslations

if routino.ParseXMLTranslations(args.translations):
    print("Error: Cannot read the translations in the file '{:s}'.".format(args.translations))
    sys.exit(1)

if args.language is not None:
    translation = routino.GetTranslation(args.language)

    if translation is None:
        list1 = routino.GetTranslationLanguages()
        list2 = routino.GetTranslationLanguageFullNames()

        print("Error: Cannot find a translation called '{:s}' in the file '{:s}'.".format(args.language,args.translations))
        print("Languages available are: {:s}".format(", ".join([i1+" ("+i2+")" for i1,i2 in zip(list1,list2)])))
        sys.exit(1)

else:
    translation = routino.GetTranslation("") # first in file

    if translation is None:
        print("Error: No translations in '{:s}'.".format(args.translations))
        sys.exit(1)


# Create the numbered waypoints

firstlatlon = True

for i in range(1,99):

    lon = getattr(args,"lon"+str(i),None)
    lat = getattr(args,"lat"+str(i),None)

    if lon is None and lat is None:
        continue

    if lon is None or lat is None:
        print("Error: All waypoints must have latitude and longitude.")
        sys.exit(1)

    if firstlatlon:
        if args.lats is not None or args.lons is not None:
            print("Error: Mixing numbered and un-numbered waypoints is not allowed.")
            sys.exit(1)
        else:
            firstlatlon = False
            args.lons = []
            args.lats = []

    args.lons.append(lon)
    args.lats.append(lat)


# Check the waypoints are valid

if args.lats is None or len(args.lats) < 2 or args.lons is None or len(args.lons) < 2:
    print("Error: At least two waypoints must be specified.")
    sys.exit(1)

if len(args.lats) != len(args.lons):
    print("Error: Number of latitudes ({:d}) and longitudes ({:d}) do not match.".format(len(lats),len(lons)))
    sys.exit(1)


# Load in the routing database

database = routino.LoadDatabase(args.dirname,args.prefix)

if database is None:
    print("Error: Could not load Routino database.")
    sys.exit(1)


# Check the profile is valid for use with this database

if routino.ValidateProfile(database,profile)!=routino.ERROR_NONE:
    print("Error: Profile is invalid or not compatible with database.")
    sys.exit(1)


# Loop through all waypoints

nwaypoints = 0
waypoints = []

for n in range(len(args.lats)):

    waypoint = routino.FindWaypoint(database, profile, args.lats[n], args.lons[n])

    if waypoint is None:
        print("Error: Cannot find node close to specified point {:d}.",n);
        sys.exit(1)

    waypoints.append(waypoint)


# Create the route

routing_options=0

if args.shortest:
    routing_options |= routino.ROUTE_SHORTEST
else:
    routing_options |= routino.ROUTE_QUICKEST

if args.html     : routing_options |= routino.ROUTE_FILE_HTML
if args.gpx_track: routing_options |= routino.ROUTE_FILE_GPX_TRACK
if args.gpx_route: routing_options |= routino.ROUTE_FILE_GPX_ROUTE
if args.text     : routing_options |= routino.ROUTE_FILE_TEXT
if args.text_all : routing_options |= routino.ROUTE_FILE_TEXT_ALL

if args.list_html    : routing_options |= routino.ROUTE_LIST_HTML
if args.list_html_all: routing_options |= routino.ROUTE_LIST_HTML_ALL
if args.list_text    : routing_options |= routino.ROUTE_LIST_TEXT
if args.list_text_all: routing_options |= routino.ROUTE_LIST_TEXT_ALL

if args.reverse: routing_options |= routino.ROUTE_REVERSE
if args.loop   : routing_options |= routino.ROUTE_LOOP

#def progress(fraction):
#    print("Progress: {:.0f}%".format(fraction*100.0))
#    return 1
#
#route = routino.CalculateRoute(database, profile, translation, waypoints, routing_options, progress)

route = routino.CalculateRoute(database, profile, translation, waypoints, routing_options)

if routino.errno >= routino.ERROR_NO_ROUTE_1:
    print("Error: Cannot find a route between specified waypoints")
    sys.exit(1)

if routino.errno != routino.ERROR_NONE:
    print("Error: Internal error ({:d}).".format(routino.errno))
    sys.exit(1)


# Print the list output

if args.list_html or args.list_html_all or args.list_text or args.list_text_all:

    list=route
    first=True
    last=False

    while list:

        if list.next:
            last = False
        else:
            last = True

        print("----------------")
        print("Lon,Lat: {:.5f}, {:.5f}".format((180.0/math.pi)*list.lon,(180.0/math.pi)*list.lat))

        if args.list_html or args.list_html_all or args.list_text or args.list_text_all:
            print("Dist,Time: {:.3f} km, {:.1f} minutes".format(list.dist,list.time))

        if args.list_text_all and not first:
            print("Speed: {:0f} km/hr".format(list.speed))

        print("Point type: {:d}".format(list.type))

        if (args.list_html or args.list_html_all or args.list_text) and not first and not last:
            print("Turn: {:d} degrees".format(list.turn))

        if ((args.list_html or args.list_html_all or args.list_text) and not last) or (args.list_text_all and not first):
            print("Bearing: {:d} degrees".format(list.bearing))

        if ((args.list_html or args.list_text) and not last) or (args.list_html_all and list.name) or (args.list_text_all and not first):
            print("Name: {:s}".format(list.name))

        if args.list_html or (args.list_html_all and list.name):
            print("Desc1: {:s}".format(list.desc1))
            print("Desc2: {:s}".format(list.desc2))

            if not last:
                print("Desc3: {:s}".format(list.desc3))

        list = list.next
        first = False


# Tidy up and exit

routino.DeleteRoute(route)

routino.UnloadDatabase(database)

routino.FreeXMLProfiles()

routino.FreeXMLTranslations()