Routino SVN Repository Browser

Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino

ViewVC logotype

Contents of /trunk/src/routino.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1795 - (show annotations) (download) (as text)
Thu Sep 3 18:12:28 2015 UTC (9 years, 6 months ago) by amb
File MIME type: text/x-csrc
File size: 20890 byte(s)
Add in an HTML-all linked list formats that includes the full set of
points and the HTML directions for the important ones.

1 /***************************************
2 Routino library functions file.
3
4 Part of the Routino routing software.
5 ******************/ /******************
6 This file Copyright 2015 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 #include <stdlib.h>
23
24 #include "routino.h"
25
26 #include "types.h"
27 #include "nodes.h"
28 #include "segments.h"
29 #include "ways.h"
30 #include "relations.h"
31
32 #include "fakes.h"
33 #include "results.h"
34 #include "functions.h"
35 #include "profiles.h"
36 #include "translations.h"
37
38
39 /* Global variables */
40
41 /*+ Contains the libroutino API version number. +*/
42 DLL_PUBLIC int Routino_APIVersion=ROUTINO_API_VERSION;
43
44 /*+ Contains the error number of the most recent Routino function (one of the ROUTINO_ERROR_* values). +*/
45 DLL_PUBLIC int Routino_errno=ROUTINO_ERROR_NONE;
46
47 /*+ The function to be called to report on the routing progress. +*/
48 Routino_ProgressFunc progress_func=NULL;
49
50 /*+ The current state of the routing progress. +*/
51 double progress_value=0;
52
53 /*+ Set when the progress callback returns false in the routing function. +*/
54 int progress_abort=0;
55
56 /*+ The option to calculate the quickest route insted of the shortest. +*/
57 extern int option_quickest;
58
59 /*+ The options to select the format of the file output. +*/
60 extern int option_file_html,option_file_gpx_track,option_file_gpx_route,option_file_text,option_file_text_all,option_file_stdout;
61
62 /*+ The options to select the format of the linked list output. +*/
63 extern int option_list_html,option_list_html_all,option_list_text,option_list_text_all;
64
65
66 /* Static variables */
67
68 static distance_t distmax=km_to_distance(1);
69
70
71 /* Local types */
72
73 struct _Routino_Database
74 {
75 Nodes *nodes;
76 Segments *segments;
77 Ways *ways;
78 Relations *relations;
79 };
80
81 struct _Routino_Waypoint
82 {
83 index_t segment;
84 index_t node1,node2;
85 distance_t dist1,dist2;
86 };
87
88
89 /*++++++++++++++++++++++++++++++++++++++
90 Check the version of the library used by the caller against the library version
91
92 int Routino_Check_API_Version Returns ROUTINO_ERROR_NONE if OK or ROUTINO_ERROR_WRONG_VERSION if there is an error.
93
94 int caller_version The version of the API used in the caller.
95
96 This function should not be called directly, use the macro Routino_CheckAPIVersion() which takes no arguments.
97 ++++++++++++++++++++++++++++++++++++++*/
98
99 DLL_PUBLIC int Routino_Check_API_Version(int caller_version)
100 {
101 if(caller_version==Routino_APIVersion)
102 return(ROUTINO_ERROR_NONE);
103 else
104 return(ROUTINO_ERROR_WRONG_API_VERSION);
105 }
106
107
108 /*++++++++++++++++++++++++++++++++++++++
109 Load a database of files for Routino to use for routing.
110
111 Routino_Database *Routino_LoadDatabase Returns a pointer to the database.
112
113 const char *dirname The pathname of the directory containing the database files.
114
115 const char *prefix The prefix of the database files.
116 ++++++++++++++++++++++++++++++++++++++*/
117
118 DLL_PUBLIC Routino_Database *Routino_LoadDatabase(const char *dirname,const char *prefix)
119 {
120 char *nodes_filename;
121 char *segments_filename;
122 char *ways_filename;
123 char *relations_filename;
124 Routino_Database *database=NULL;
125
126 nodes_filename =FileName(dirname,prefix,"nodes.mem");
127 segments_filename =FileName(dirname,prefix,"segments.mem");
128 ways_filename =FileName(dirname,prefix,"ways.mem");
129 relations_filename=FileName(dirname,prefix,"relations.mem");
130
131 if(!ExistsFile(nodes_filename) || !ExistsFile(nodes_filename) || !ExistsFile(nodes_filename) || !ExistsFile(nodes_filename))
132 Routino_errno=ROUTINO_ERROR_NO_DATABASE_FILES;
133 else
134 {
135 database=calloc(sizeof(Routino_Database),1);
136
137 database->nodes =LoadNodeList (nodes_filename);
138 database->segments =LoadSegmentList (segments_filename);
139 database->ways =LoadWayList (ways_filename);
140 database->relations=LoadRelationList(relations_filename);
141 }
142
143 free(nodes_filename);
144 free(segments_filename);
145 free(ways_filename);
146 free(relations_filename);
147
148 if(!database->nodes || !database->segments || !database->ways || !database->relations)
149 {
150 Routino_UnloadDatabase(database);
151 database=NULL;
152
153 Routino_errno=ROUTINO_ERROR_BAD_DATABASE_FILES;
154 }
155
156 if(database)
157 {
158 Routino_errno=ROUTINO_ERROR_NONE;
159 return(database);
160 }
161 else
162 return(NULL);
163 }
164
165
166 /*++++++++++++++++++++++++++++++++++++++
167 Close the database files that were opened by a call to Routino_LoadDatabase().
168
169 Routino_Database *database The database to close.
170 ++++++++++++++++++++++++++++++++++++++*/
171
172 DLL_PUBLIC void Routino_UnloadDatabase(Routino_Database *database)
173 {
174 if(!database)
175 Routino_errno=ROUTINO_ERROR_NO_DATABASE;
176 else
177 {
178 if(database->nodes) DestroyNodeList (database->nodes);
179 if(database->segments) DestroySegmentList (database->segments);
180 if(database->ways) DestroyWayList (database->ways);
181 if(database->relations) DestroyRelationList(database->relations);
182
183 free(database);
184
185 Routino_errno=ROUTINO_ERROR_NONE;
186 }
187 }
188
189
190 /*++++++++++++++++++++++++++++++++++++++
191 Parse a Routino XML file containing profiles, must be called before selecting a profile.
192
193 int Routino_ParseXMLProfiles Returns non-zero in case of an error or zero if there was no error.
194
195 const char *filename The full pathname of the file to read.
196 ++++++++++++++++++++++++++++++++++++++*/
197
198 DLL_PUBLIC int Routino_ParseXMLProfiles(const char *filename)
199 {
200 int retval;
201
202 retval=ParseXMLProfiles(filename,NULL,1);
203
204 if(retval==1)
205 retval=ROUTINO_ERROR_NO_PROFILES_XML;
206 else if(retval==2)
207 retval=ROUTINO_ERROR_BAD_PROFILES_XML;
208
209 Routino_errno=retval;
210 return(retval);
211 }
212
213
214 /*++++++++++++++++++++++++++++++++++++++
215 Return a list of the profile names that have been loaded from the XML file.
216
217 char **Routino_GetProfileNames Returns a NULL terminated list of strings - all allocated.
218 ++++++++++++++++++++++++++++++++++++++*/
219
220 DLL_PUBLIC char **Routino_GetProfileNames(void)
221 {
222 Routino_errno=ROUTINO_ERROR_NONE;
223
224 return(GetProfileNames());
225 }
226
227
228 /*++++++++++++++++++++++++++++++++++++++
229 Select a specific routing profile from the set of Routino profiles that have been loaded from the XML file or NULL in case of an error.
230
231 Routino_Profile *Routino_GetProfile Returns a pointer to an internal data structure - do not free.
232
233 const char *name The name of the profile to select.
234 ++++++++++++++++++++++++++++++++++++++*/
235
236 DLL_PUBLIC Routino_Profile *Routino_GetProfile(const char *name)
237 {
238 Profile *profile=GetProfile(name);
239
240 if(profile)
241 Routino_errno=ROUTINO_ERROR_NONE;
242 else
243 Routino_errno=ROUTINO_ERROR_NO_SUCH_PROFILE;
244
245 return(profile);
246 }
247
248
249 /*++++++++++++++++++++++++++++++++++++++
250 Free the internal memory that was allocated for the Routino profiles loaded from the XML file.
251 ++++++++++++++++++++++++++++++++++++++*/
252
253 DLL_PUBLIC void Routino_FreeXMLProfiles(void)
254 {
255 Routino_errno=ROUTINO_ERROR_NONE;
256
257 FreeXMLProfiles();
258 }
259
260
261 /*++++++++++++++++++++++++++++++++++++++
262 Parse a Routino XML file containing translations, must be called before selecting a translation.
263
264 int Routino_ParseXMLTranslations Returns non-zero in case of an error or zero if there was no error.
265
266 const char *filename The full pathname of the file to read.
267 ++++++++++++++++++++++++++++++++++++++*/
268
269 DLL_PUBLIC int Routino_ParseXMLTranslations(const char *filename)
270 {
271 int retval;
272
273 retval=ParseXMLTranslations(filename,NULL,1);
274
275 if(retval==1)
276 retval=ROUTINO_ERROR_NO_TRANSLATIONS_XML;
277 else if(retval==2)
278 retval=ROUTINO_ERROR_BAD_TRANSLATIONS_XML;
279
280 Routino_errno=retval;
281 return(retval);
282 }
283
284
285 /*++++++++++++++++++++++++++++++++++++++
286 Return a list of the translation languages that have been loaded from the XML file.
287
288 char **Routino_GetTranslationLanguages Returns a NULL terminated list of strings - all allocated.
289 ++++++++++++++++++++++++++++++++++++++*/
290
291 DLL_PUBLIC char **Routino_GetTranslationLanguages(void)
292 {
293 Routino_errno=ROUTINO_ERROR_NONE;
294
295 return(GetTranslationLanguages());
296 }
297
298
299 /*++++++++++++++++++++++++++++++++++++++
300 Return a list of the full names of the translation languages that have been loaded from the XML file.
301
302 char **Routino_GetTranslationLanguageFullNames Returns a NULL terminated list of strings - all allocated.
303 ++++++++++++++++++++++++++++++++++++++*/
304
305 DLL_PUBLIC char **Routino_GetTranslationLanguageFullNames(void)
306 {
307 Routino_errno=ROUTINO_ERROR_NONE;
308
309 return(GetTranslationLanguageFullNames());
310 }
311
312
313 /*++++++++++++++++++++++++++++++++++++++
314 Select a specific translation from the set of Routino translations that have been loaded from the XML file or NULL in case of an error.
315
316 Routino_Translation *Routino_GetTranslation Returns a pointer to an internal data structure - do not free.
317
318 const char *language The language to select (as a country code, e.g. 'en', 'de') or an empty string for the first in the file or NULL for the built-in English version.
319 ++++++++++++++++++++++++++++++++++++++*/
320
321 DLL_PUBLIC Routino_Translation *Routino_GetTranslation(const char *language)
322 {
323 Translation *translation=GetTranslation(language);
324
325 if(translation)
326 Routino_errno=ROUTINO_ERROR_NONE;
327 else
328 Routino_errno=ROUTINO_ERROR_NO_SUCH_TRANSLATION;
329
330 return(translation);
331 }
332
333
334 /*++++++++++++++++++++++++++++++++++++++
335 Free the internal memory that was allocated for the Routino translations loaded from the XML file.
336 ++++++++++++++++++++++++++++++++++++++*/
337
338 DLL_PUBLIC void Routino_FreeXMLTranslations(void)
339 {
340 Routino_errno=ROUTINO_ERROR_NONE;
341
342 FreeXMLTranslations();
343 }
344
345
346 /*++++++++++++++++++++++++++++++++++++++
347 Create a fully formed Routino Profile from a Routino User Profile.
348
349 Routino_Profile *Routino_CreateProfileFromUserProfile Returns an allocated Routino Profile.
350
351 Routino_UserProfile *profile The user specified profile to convert (not modified by this).
352 ++++++++++++++++++++++++++++++++++++++*/
353
354 DLL_PUBLIC Routino_Profile *Routino_CreateProfileFromUserProfile(Routino_UserProfile *profile)
355 {
356 Routino_Profile *rprofile=calloc(1,sizeof(Routino_Profile));
357 int i;
358
359 Routino_errno=ROUTINO_ERROR_NONE;
360
361 if(profile->transport<=0 || profile->transport>=Transport_Count)
362 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
363 else
364 rprofile->transport=profile->transport;
365
366 for(i=1;i<Highway_Count;i++)
367 {
368 if(profile->highway[i]<0 || profile->highway[i]>1)
369 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
370 else
371 rprofile->highway[i]=profile->highway[i];
372
373 if(profile->speed[i]<=0)
374 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
375 else
376 rprofile->speed[i]=kph_to_speed(profile->speed[i]);
377 }
378
379 for(i=1;i<Property_Count;i++)
380 {
381 if(profile->props[i]<0 || profile->props[i]>1)
382 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
383 else
384 rprofile->props[i]=profile->props[i];
385 }
386
387 if(profile->weight<=0)
388 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
389 else
390 rprofile->weight=tonnes_to_weight(profile->weight);
391
392 if(profile->height<=0)
393 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
394 else
395 rprofile->height=metres_to_height(profile->height);
396
397 if(profile->width<=0)
398 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
399 else
400 rprofile->width=metres_to_width(profile->width);
401
402 if(profile->length<=0)
403 Routino_errno=ROUTINO_ERROR_BAD_USER_PROFILE;
404 else
405 rprofile->length=metres_to_length(profile->length);
406
407 if(Routino_errno==ROUTINO_ERROR_NONE)
408 return(rprofile);
409
410 free(rprofile);
411 return(NULL);
412 }
413
414
415 /*++++++++++++++++++++++++++++++++++++++
416 Create a Routino User Profile from a Routino Profile loaded from an XML file.
417
418 Routino_UserProfile *Routino_CreateUserProfileFromProfile Returns an allocated Routino User Profile.
419
420 Routino_Profile *profile The Routino Profile to convert (not modified by this).
421 ++++++++++++++++++++++++++++++++++++++*/
422
423 DLL_PUBLIC Routino_UserProfile *Routino_CreateUserProfileFromProfile(Routino_Profile *profile)
424 {
425 Routino_UserProfile *uprofile=calloc(1,sizeof(Routino_UserProfile));
426 int i;
427
428 Routino_errno=ROUTINO_ERROR_NONE;
429
430 uprofile->transport=profile->transport;
431
432 for(i=1;i<Highway_Count;i++)
433 {
434 uprofile->highway[i]=profile->highway[i];
435
436 uprofile->speed[i]=speed_to_kph(profile->speed[i]);
437 }
438
439 for(i=1;i<Property_Count;i++)
440 uprofile->props[i]=profile->props[i];
441
442 uprofile->weight=weight_to_tonnes(profile->weight);
443
444 uprofile->height=height_to_metres(profile->height);
445
446 uprofile->width=width_to_metres(profile->width);
447
448 uprofile->length=length_to_metres(profile->length);
449
450 return(uprofile);
451 }
452
453
454 /*++++++++++++++++++++++++++++++++++++++
455 Validates that a selected routing profile is valid for use with the selected routing database.
456
457 int Routino_ValidateProfile Returns zero if OK or something else in case of an error.
458
459 Routino_Database *database The Routino database to use.
460
461 Routino_Profile *profile The Routino profile to validate.
462 ++++++++++++++++++++++++++++++++++++++*/
463
464 DLL_PUBLIC int Routino_ValidateProfile(Routino_Database *database,Routino_Profile *profile)
465 {
466 Routino_errno=ROUTINO_ERROR_NONE;
467
468 if(UpdateProfile(profile,database->ways))
469 Routino_errno=ROUTINO_ERROR_PROFILE_DATABASE_ERR;
470
471 return(Routino_errno);
472 }
473
474
475 /*++++++++++++++++++++++++++++++++++++++
476 Finds the nearest point in the database to the specified latitude and longitude.
477
478 Routino_Waypoint *Routino_FindWaypoint Returns a pointer to a newly allocated Routino waypoint or NULL if none could be found.
479
480 Routino_Database *database The Routino database to use.
481
482 Routino_Profile *profile The Routino profile to use.
483
484 double latitude The latitude in degrees of the point.
485
486 double longitude The longitude in degrees of the point.
487 ++++++++++++++++++++++++++++++++++++++*/
488
489 DLL_PUBLIC Routino_Waypoint *Routino_FindWaypoint(Routino_Database *database,Routino_Profile *profile,double latitude,double longitude)
490 {
491 distance_t dist;
492 Routino_Waypoint *waypoint;
493
494 if(!database)
495 {
496 Routino_errno=ROUTINO_ERROR_NO_DATABASE;
497 return(NULL);
498 }
499
500 if(!profile)
501 {
502 Routino_errno=ROUTINO_ERROR_NO_PROFILE;
503 return(NULL);
504 }
505
506 if(!profile->allow)
507 {
508 Routino_errno=ROUTINO_ERROR_NOTVALID_PROFILE;
509 return(NULL);
510 }
511
512 waypoint=calloc(sizeof(Routino_Waypoint),1);
513
514 waypoint->segment=FindClosestSegment(database->nodes,database->segments,database->ways,
515 degrees_to_radians(latitude),degrees_to_radians(longitude),distmax,profile,
516 &dist,&waypoint->node1,&waypoint->node2,&waypoint->dist1,&waypoint->dist2);
517
518 if(waypoint->segment==NO_SEGMENT)
519 {
520 free(waypoint);
521
522 Routino_errno=ROUTINO_ERROR_NO_NEARBY_HIGHWAY;
523 return(NULL);
524 }
525
526 Routino_errno=ROUTINO_ERROR_NONE;
527 return(waypoint);
528 }
529
530
531 /*++++++++++++++++++++++++++++++++++++++
532 Calculate a route using a loaded database, chosen profile, chosen translation and set of waypoints.
533
534 Routino_Output *Routino_CalculateRoute Returns the head of a linked list of route data (if requested) or NULL.
535
536 Routino_Database *database The loaded database to use.
537
538 Routino_Profile *profile The chosen routing profile to use.
539
540 Routino_Translation *translation The chosen translation information to use.
541
542 Routino_Waypoint **waypoints The set of waypoints.
543
544 int nwaypoints The number of waypoints.
545
546 int options The set of routing options (ROUTINO_ROUTE_*) ORed together.
547
548 Routino_ProgressFunc progress A function to be called occasionally to report progress or NULL.
549 ++++++++++++++++++++++++++++++++++++++*/
550
551 DLL_PUBLIC Routino_Output *Routino_CalculateRoute(Routino_Database *database,Routino_Profile *profile,Routino_Translation *translation,
552 Routino_Waypoint **waypoints,int nwaypoints,int options,Routino_ProgressFunc progress)
553 {
554 int waypoint;
555 index_t start_node,finish_node=NO_NODE;
556 index_t join_segment=NO_SEGMENT;
557 Results **results;
558 Routino_Output *output=NULL;
559
560 /* Check the input data */
561
562 if(!database)
563 {
564 Routino_errno=ROUTINO_ERROR_NO_DATABASE;
565 return(NULL);
566 }
567
568 if(!profile)
569 {
570 Routino_errno=ROUTINO_ERROR_NO_PROFILE;
571 return(NULL);
572 }
573
574 if(!profile->allow)
575 {
576 Routino_errno=ROUTINO_ERROR_NOTVALID_PROFILE;
577 return(NULL);
578 }
579
580 if(!translation)
581 {
582 Routino_errno=ROUTINO_ERROR_NO_TRANSLATION;
583 return(NULL);
584 }
585
586 /* Extract the options */
587
588 if(options&ROUTINO_ROUTE_QUICKEST) option_quickest=1; else option_quickest=0;
589
590 if(options&ROUTINO_ROUTE_FILE_HTML) option_file_html=1; else option_file_html=0;
591 if(options&ROUTINO_ROUTE_FILE_GPX_TRACK) option_file_gpx_track=1; else option_file_gpx_track=0;
592 if(options&ROUTINO_ROUTE_FILE_GPX_ROUTE) option_file_gpx_route=1; else option_file_gpx_route=0;
593 if(options&ROUTINO_ROUTE_FILE_TEXT) option_file_text=1; else option_file_text=0;
594 if(options&ROUTINO_ROUTE_FILE_TEXT_ALL) option_file_text_all=1; else option_file_text_all=0;
595
596 if(options&ROUTINO_ROUTE_FILE_STDOUT) option_file_stdout=1; else option_file_stdout=0;
597
598 if(option_file_stdout && (option_file_html+option_file_gpx_track+option_file_gpx_route+option_file_text+option_file_text_all)!=1)
599 {
600 Routino_errno=ROUTINO_ERROR_BAD_OPTIONS;
601 return(NULL);
602 }
603
604 if(options&ROUTINO_ROUTE_LIST_HTML) option_list_html=1; else option_list_html=0;
605 if(options&ROUTINO_ROUTE_LIST_HTML_ALL) option_list_html_all=1; else option_list_html_all=0;
606 if(options&ROUTINO_ROUTE_LIST_TEXT) option_list_text=1; else option_list_text=0;
607 if(options&ROUTINO_ROUTE_LIST_TEXT_ALL) option_list_text_all=1; else option_list_text_all=0;
608
609 if((option_list_html+option_list_html_all+option_list_text+option_list_text_all)>1)
610 {
611 Routino_errno=ROUTINO_ERROR_BAD_OPTIONS;
612 return(NULL);
613 }
614
615 /* Set up the progress callback */
616
617 progress_func=progress;
618 progress_value=0.0;
619 progress_abort=0;
620
621 /* Loop through all pairs of waypoints */
622
623 results=calloc(sizeof(Results*),nwaypoints);
624
625 for(waypoint=0;waypoint<nwaypoints;waypoint++)
626 {
627 if(progress_func)
628 {
629 progress_value=(double)waypoint/(double)(nwaypoints+1);
630
631 if(!progress_func(progress_value))
632 {
633 Routino_errno=ROUTINO_ERROR_PROGRESS_ABORTED;
634 goto tidy_and_exit;
635 }
636 }
637
638 start_node=finish_node;
639
640 finish_node=CreateFakes(database->nodes,database->segments,waypoint+1,
641 LookupSegment(database->segments,waypoints[waypoint]->segment,1),
642 waypoints[waypoint]->node1,waypoints[waypoint]->node2,waypoints[waypoint]->dist1,waypoints[waypoint]->dist2);
643
644 if(waypoint==0)
645 continue;
646
647 results[waypoint-1]=CalculateRoute(database->nodes,database->segments,database->ways,database->relations,
648 profile,start_node,join_segment,finish_node,waypoint,waypoint+1);
649
650 if(!results[waypoint-1])
651 {
652 if(progress_func && progress_abort)
653 Routino_errno=ROUTINO_ERROR_PROGRESS_ABORTED;
654 else
655 Routino_errno=ROUTINO_ERROR_NO_ROUTE_1+waypoint-1;
656
657 goto tidy_and_exit;
658 }
659
660 join_segment=results[waypoint-1]->last_segment;
661 }
662
663 if(progress_func)
664 {
665 progress_value=(double)waypoint/(double)(nwaypoints+1);
666
667 if(!progress_func(progress_value))
668 {
669 Routino_errno=ROUTINO_ERROR_PROGRESS_ABORTED;
670 goto tidy_and_exit;
671 }
672 }
673
674 /* Print the route */
675
676 output=PrintRoute(results,nwaypoints-1,database->nodes,database->segments,database->ways,profile,translation);
677
678 if(progress_func && !progress_func(1.0))
679 {
680 Routino_errno=ROUTINO_ERROR_PROGRESS_ABORTED;
681 goto tidy_and_exit;
682 }
683
684 /* Tidy up and exit */
685
686 tidy_and_exit:
687
688 DeleteFakeNodes();
689
690 for(waypoint=0;waypoint<nwaypoints;waypoint++)
691 if(results[waypoint])
692 FreeResultsList(results[waypoint]);
693
694 free(results);
695
696 return(output);
697 }
698
699
700 /*++++++++++++++++++++++++++++++++++++++
701 Delete the linked list created by Routino_CalculateRoute.
702
703 Routino_Output *output The output to be deleted.
704 ++++++++++++++++++++++++++++++++++++++*/
705
706 DLL_PUBLIC void Routino_DeleteRoute(Routino_Output *output)
707 {
708 while(output)
709 {
710 Routino_Output *next=output->next;
711
712 if(output->name)
713 free(output->name);
714
715 if(output->desc1)
716 free(output->desc1);
717
718 if(output->desc2)
719 free(output->desc2);
720
721 if(output->desc3)
722 free(output->desc3);
723
724 free(output);
725
726 output=next;
727 }
728 }