Routino SVN Repository Browser

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

ViewVC logotype

Contents of /branches/destination-access/src/relationsx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1969 - (show annotations) (download) (as text)
Wed Mar 13 13:31:03 2019 UTC (6 years ago) by amb
File MIME type: text/x-csrc
File size: 43313 byte(s)
Merge from trunk.

1 /***************************************
2 Extended Relation data type functions.
3
4 Part of the Routino routing software.
5 ******************/ /******************
6 This file Copyright 2010-2015, 2018, 2019 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 <stdlib.h>
24 #include <string.h>
25
26 #include "types.h"
27 #include "segments.h"
28 #include "relations.h"
29
30 #include "nodesx.h"
31 #include "segmentsx.h"
32 #include "waysx.h"
33 #include "relationsx.h"
34
35 #include "files.h"
36 #include "logging.h"
37 #include "sorting.h"
38
39
40 /* Global variables */
41
42 /*+ The command line '--tmpdir' option or its default value. +*/
43 extern char *option_tmpdirname;
44
45 /* Local variables */
46
47 /*+ Temporary file-local variables for use by the sort functions (re-initialised for each sort). +*/
48 static SegmentsX *sortsegmentsx;
49 static NodesX *sortnodesx;
50
51 /* Local functions */
52
53 static int sort_route_by_id(RouteRelX *a,RouteRelX *b);
54 static int deduplicate_route_by_id(RouteRelX *relationx,index_t index);
55
56 static int sort_turn_by_id(TurnRelX *a,TurnRelX *b);
57 static int deduplicate_turn_by_id(TurnRelX *relationx,index_t index);
58
59 static int geographically_index(TurnRelX *relationx,index_t index);
60 static int geographically_index_convert_segments(TurnRelX *relationx,index_t index);
61 static int sort_by_via(TurnRelX *a,TurnRelX *b);
62
63
64 /*++++++++++++++++++++++++++++++++++++++
65 Allocate a new relation list (create a new file or open an existing one).
66
67 RelationsX *NewRelationList Returns the relation list.
68
69 int append Set to 1 if the file is to be opened for appending.
70
71 int readonly Set to 1 if the file is to be opened for reading.
72 ++++++++++++++++++++++++++++++++++++++*/
73
74 RelationsX *NewRelationList(int append,int readonly)
75 {
76 RelationsX *relationsx;
77
78 relationsx=(RelationsX*)calloc(1,sizeof(RelationsX));
79
80 logassert(relationsx,"Failed to allocate memory (try using slim mode?)"); /* Check calloc() worked */
81
82
83 /* Route Relations */
84
85 relationsx->rrfilename =(char*)malloc(strlen(option_tmpdirname)+32);
86 relationsx->rrfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+48); /* allow %p to be up to 20 bytes */
87
88 sprintf(relationsx->rrfilename ,"%s/relationsx.route.parsed.mem",option_tmpdirname);
89 sprintf(relationsx->rrfilename_tmp,"%s/relationsx.route.%p.tmp" ,option_tmpdirname,(void*)relationsx);
90
91 if(append || readonly)
92 if(ExistsFile(relationsx->rrfilename))
93 {
94 FILESORT_VARINT relationsize;
95 int rrfd;
96
97 rrfd=ReOpenFileBuffered(relationsx->rrfilename);
98
99 while(!ReadFileBuffered(rrfd,&relationsize,FILESORT_VARSIZE))
100 {
101 SkipFileBuffered(rrfd,relationsize);
102
103 relationsx->rrnumber++;
104 }
105
106 CloseFileBuffered(rrfd);
107
108 RenameFile(relationsx->rrfilename,relationsx->rrfilename_tmp);
109 }
110
111 if(append)
112 relationsx->rrfd=OpenFileBufferedAppend(relationsx->rrfilename_tmp);
113 else if(!readonly)
114 relationsx->rrfd=OpenFileBufferedNew(relationsx->rrfilename_tmp);
115 else
116 relationsx->rrfd=-1;
117
118
119 /* Turn Restriction Relations */
120
121 relationsx->trfilename =(char*)malloc(strlen(option_tmpdirname)+32);
122 relationsx->trfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+48); /* allow %p to be up to 20 bytes */
123
124 sprintf(relationsx->trfilename ,"%s/relationsx.turn.parsed.mem",option_tmpdirname);
125 sprintf(relationsx->trfilename_tmp,"%s/relationsx.turn.%p.tmp" ,option_tmpdirname,(void*)relationsx);
126
127 if(append || readonly)
128 if(ExistsFile(relationsx->trfilename))
129 {
130 offset_t size;
131
132 size=SizeFile(relationsx->trfilename);
133
134 relationsx->trnumber=size/sizeof(TurnRelX);
135
136 RenameFile(relationsx->trfilename,relationsx->trfilename_tmp);
137 }
138
139 if(append)
140 relationsx->trfd=OpenFileBufferedAppend(relationsx->trfilename_tmp);
141 else if(!readonly)
142 relationsx->trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
143 else
144 relationsx->trfd=-1;
145
146 return(relationsx);
147 }
148
149
150 /*++++++++++++++++++++++++++++++++++++++
151 Free a relation list.
152
153 RelationsX *relationsx The set of relations to be freed.
154
155 int keep If set then the results file is to be kept.
156 ++++++++++++++++++++++++++++++++++++++*/
157
158 void FreeRelationList(RelationsX *relationsx,int keep)
159 {
160 /* Route relations */
161
162 if(keep)
163 RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
164 else
165 DeleteFile(relationsx->rrfilename_tmp);
166
167 free(relationsx->rrfilename);
168 free(relationsx->rrfilename_tmp);
169
170 if(relationsx->rridata)
171 {
172 log_free(relationsx->rridata);
173 free(relationsx->rridata);
174 }
175
176 if(relationsx->rrodata)
177 {
178 log_free(relationsx->rrodata);
179 free(relationsx->rrodata);
180 }
181
182
183 /* Turn Restriction relations */
184
185 if(keep)
186 RenameFile(relationsx->trfilename_tmp,relationsx->trfilename);
187 else
188 DeleteFile(relationsx->trfilename_tmp);
189
190 free(relationsx->trfilename);
191 free(relationsx->trfilename_tmp);
192
193 if(relationsx->tridata)
194 {
195 log_free(relationsx->tridata);
196 free(relationsx->tridata);
197 }
198
199
200 free(relationsx);
201 }
202
203
204 /*++++++++++++++++++++++++++++++++++++++
205 Append a single relation to an unsorted route relation list.
206
207 RelationsX* relationsx The set of relations to process.
208
209 relation_t id The ID of the relation.
210
211 transports_t routes The types of routes that this relation is for.
212
213 node_t *nodes The array of nodes that are members of the relation.
214
215 int nnodes The number of nodes that are members of the relation.
216
217 way_t *ways The array of ways that are members of the relation.
218
219 int nways The number of ways that are members of the relation.
220
221 relation_t *relations The array of relations that are members of the relation.
222
223 int nrelations The number of relations that are members of the relation.
224 ++++++++++++++++++++++++++++++++++++++*/
225
226 void AppendRouteRelationList(RelationsX* relationsx,relation_t id,
227 transports_t routes,
228 node_t *nodes,int nnodes,
229 way_t *ways,int nways,
230 relation_t *relations,int nrelations)
231 {
232 RouteRelX relationx={0};
233 unsigned long longsize;
234 FILESORT_VARINT size;
235 node_t nonode=NO_NODE_ID;
236 way_t noway=NO_WAY_ID;
237 relation_t norelation=NO_RELATION_ID;
238
239 relationx.id=id;
240 relationx.routes=routes;
241
242 longsize=sizeof(RouteRelX)+(nnodes+1)*sizeof(node_t)+(nways+1)*sizeof(way_t)+(nrelations+1)*sizeof(relation_t);
243
244 logassert(longsize<FILESORT_MAXINT,"Route relation contains too much data (change FILESORT_VARINT to 32-bits?)"); /* Ensure no overflow of FILESORT_VARINT integer */
245
246 size=longsize;
247
248 WriteFileBuffered(relationsx->rrfd,&size ,FILESORT_VARSIZE);
249 WriteFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
250
251 WriteFileBuffered(relationsx->rrfd,nodes ,nnodes*sizeof(node_t));
252 WriteFileBuffered(relationsx->rrfd,&nonode, sizeof(node_t));
253
254 WriteFileBuffered(relationsx->rrfd,ways ,nways*sizeof(way_t));
255 WriteFileBuffered(relationsx->rrfd,&noway, sizeof(way_t));
256
257 WriteFileBuffered(relationsx->rrfd,relations ,nrelations*sizeof(relation_t));
258 WriteFileBuffered(relationsx->rrfd,&norelation, sizeof(relation_t));
259
260 relationsx->rrnumber++;
261
262 logassert(relationsx->rrnumber!=0,"Too many route relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
263 }
264
265
266 /*++++++++++++++++++++++++++++++++++++++
267 Append a single relation to an unsorted turn restriction relation list.
268
269 RelationsX* relationsx The set of relations to process.
270
271 relation_t id The ID of the relation.
272
273 way_t from The way that the turn restriction starts from.
274
275 way_t to The way that the restriction finished on.
276
277 node_t via The node that the turn restriction passes through.
278
279 TurnRestriction restriction The type of restriction.
280
281 transports_t except The set of transports allowed to bypass the restriction.
282 ++++++++++++++++++++++++++++++++++++++*/
283
284 void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
285 way_t from,way_t to,node_t via,
286 TurnRestriction restriction,transports_t except)
287 {
288 TurnRelX relationx={0};
289
290 relationx.id=id;
291 relationx.from=from;
292 relationx.to=to;
293 relationx.via=via;
294 relationx.restriction=restriction;
295 relationx.except=except;
296
297 WriteFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
298
299 relationsx->trnumber++;
300
301 logassert(relationsx->trnumber!=0,"Too many turn relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
302 }
303
304
305 /*++++++++++++++++++++++++++++++++++++++
306 Finish appending relations and change the filename over.
307
308 RelationsX *relationsx The relations that have been appended.
309 ++++++++++++++++++++++++++++++++++++++*/
310
311 void FinishRelationList(RelationsX *relationsx)
312 {
313 if(relationsx->rrfd!=-1)
314 relationsx->rrfd =CloseFileBuffered(relationsx->rrfd);
315
316 if(relationsx->trfd!=-1)
317 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
318 }
319
320
321 /*++++++++++++++++++++++++++++++++++++++
322 Find a particular route relation index.
323
324 index_t IndexRouteRelX Returns the index of the route relation with the specified id.
325
326 RelationsX *relationsx The set of relations to process.
327
328 relation_t id The relation id to look for.
329 ++++++++++++++++++++++++++++++++++++++*/
330
331 index_t IndexRouteRelX(RelationsX *relationsx,relation_t id)
332 {
333 index_t start=0;
334 index_t end=relationsx->rrnumber-1;
335 index_t mid;
336
337 if(relationsx->rrnumber==0) /* There are no route relations */
338 return(NO_RELATION);
339
340 if(id<relationsx->rridata[start]) /* Key is before start */
341 return(NO_RELATION);
342
343 if(id>relationsx->rridata[end]) /* Key is after end */
344 return(NO_RELATION);
345
346 /* Binary search - search key exact match only is required.
347 *
348 * # <- start | Check mid and move start or end if it doesn't match
349 * # |
350 * # | Since an exact match is wanted we can set end=mid-1
351 * # <- mid | or start=mid+1 because we know that mid doesn't match.
352 * # |
353 * # | Eventually either end=start or end=start+1 and one of
354 * # <- end | start or end is the wanted one.
355 */
356
357 do
358 {
359 mid=start+(end-start)/2; /* Choose mid point (avoid overflow) */
360
361 if(relationsx->rridata[mid]<id) /* Mid point is too low */
362 start=mid+1;
363 else if(relationsx->rridata[mid]>id) /* Mid point is too high */
364 end=mid?(mid-1):mid;
365 else /* Mid point is correct */
366 return(mid);
367 }
368 while((end-start)>1);
369
370 if(relationsx->rridata[start]==id) /* Start is correct */
371 return(start);
372
373 if(relationsx->rridata[end]==id) /* End is correct */
374 return(end);
375
376 return(NO_RELATION);
377 }
378
379
380 /*++++++++++++++++++++++++++++++++++++++
381 Find a particular route relation index.
382
383 index_t IndexTurnRelX Returns the index of the turn relation with the specified id.
384
385 RelationsX *relationsx The set of relations to process.
386
387 relation_t id The relation id to look for.
388 ++++++++++++++++++++++++++++++++++++++*/
389
390 index_t IndexTurnRelX(RelationsX *relationsx,relation_t id)
391 {
392 index_t start=0;
393 index_t end=relationsx->trnumber-1;
394 index_t mid;
395
396 if(relationsx->trnumber==0) /* There are no route relations */
397 return(NO_RELATION);
398
399 if(id<relationsx->tridata[start]) /* Key is before start */
400 return(NO_RELATION);
401
402 if(id>relationsx->tridata[end]) /* Key is after end */
403 return(NO_RELATION);
404
405 /* Binary search - search key exact match only is required.
406 *
407 * # <- start | Check mid and move start or end if it doesn't match
408 * # |
409 * # | Since an exact match is wanted we can set end=mid-1
410 * # <- mid | or start=mid+1 because we know that mid doesn't match.
411 * # |
412 * # | Eventually either end=start or end=start+1 and one of
413 * # <- end | start or end is the wanted one.
414 */
415
416 do
417 {
418 mid=start+(end-start)/2; /* Choose mid point (avoid overflow) */
419
420 if(relationsx->tridata[mid]<id) /* Mid point is too low */
421 start=mid+1;
422 else if(relationsx->tridata[mid]>id) /* Mid point is too high */
423 end=mid?(mid-1):mid;
424 else /* Mid point is correct */
425 return(mid);
426 }
427 while((end-start)>1);
428
429 if(relationsx->tridata[start]==id) /* Start is correct */
430 return(start);
431
432 if(relationsx->tridata[end]==id) /* End is correct */
433 return(end);
434
435 return(NO_RELATION);
436 }
437
438
439 /*++++++++++++++++++++++++++++++++++++++
440 Sort the list of relations.
441
442 RelationsX* relationsx The set of relations to process.
443 ++++++++++++++++++++++++++++++++++++++*/
444
445 void SortRelationList(RelationsX* relationsx)
446 {
447 /* Route Relations */
448
449 if(relationsx->rrnumber)
450 {
451 index_t rrxnumber;
452 int rrfd;
453
454 /* Print the start message */
455
456 printf_first("Sorting Route Relations");
457
458 /* Re-open the file read-only and a new file writeable */
459
460 rrfd=ReplaceFileBuffered(relationsx->rrfilename_tmp,&relationsx->rrfd);
461
462 /* Sort the relations */
463
464 rrxnumber=relationsx->rrnumber;
465
466 relationsx->rrnumber=filesort_vary(relationsx->rrfd,rrfd,NULL,
467 (int (*)(const void*,const void*))sort_route_by_id,
468 (int (*)(void*,index_t))deduplicate_route_by_id);
469
470 relationsx->rrknumber=relationsx->rrnumber;
471
472 /* Close the files */
473
474 relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
475 CloseFileBuffered(rrfd);
476
477 /* Print the final message */
478
479 printf_last("Sorted Route Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,rrxnumber,rrxnumber-relationsx->rrnumber);
480 }
481
482 /* Turn Restriction Relations. */
483
484 if(relationsx->trnumber)
485 {
486 index_t trxnumber;
487 int trfd;
488
489 /* Print the start message */
490
491 printf_first("Sorting Turn Relations");
492
493 /* Re-open the file read-only and a new file writeable */
494
495 trfd=ReplaceFileBuffered(relationsx->trfilename_tmp,&relationsx->trfd);
496
497 /* Sort the relations */
498
499 trxnumber=relationsx->trnumber;
500
501 relationsx->trnumber=filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRelX),NULL,
502 (int (*)(const void*,const void*))sort_turn_by_id,
503 (int (*)(void*,index_t))deduplicate_turn_by_id);
504
505 relationsx->trknumber=relationsx->trnumber;
506
507 /* Close the files */
508
509 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
510 CloseFileBuffered(trfd);
511
512 /* Print the final message */
513
514 printf_last("Sorted Turn Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,trxnumber,trxnumber-relationsx->trnumber);
515 }
516 }
517
518
519 /*++++++++++++++++++++++++++++++++++++++
520 Sort the route relations into id order.
521
522 int sort_route_by_id Returns the comparison of the id fields.
523
524 RouteRelX *a The first extended relation.
525
526 RouteRelX *b The second extended relation.
527 ++++++++++++++++++++++++++++++++++++++*/
528
529 static int sort_route_by_id(RouteRelX *a,RouteRelX *b)
530 {
531 relation_t a_id=a->id;
532 relation_t b_id=b->id;
533
534 if(a_id<b_id)
535 return(-1);
536 else if(a_id>b_id)
537 return(1);
538 else
539 return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
540 }
541
542
543 /*++++++++++++++++++++++++++++++++++++++
544 Deduplicate the route relations using the id after sorting.
545
546 int deduplicate_route_by_id Return 1 if the value is to be kept, otherwise 0.
547
548 RouteRelX *relationx The extended relation.
549
550 index_t index The number of sorted relations that have already been written to the output file.
551 ++++++++++++++++++++++++++++++++++++++*/
552
553 static int deduplicate_route_by_id(RouteRelX *relationx,index_t index)
554 {
555 static relation_t previd; /* internal variable (reset by first call in each sort; index==0) */
556
557 if(index==0 || relationx->id!=previd)
558 {
559 previd=relationx->id;
560
561 if(relationx->routes==RELATION_DELETED)
562 return(0);
563 else
564 return(1);
565 }
566 else
567 return(0);
568 }
569
570
571 /*++++++++++++++++++++++++++++++++++++++
572 Sort the turn restriction relations into id order.
573
574 int sort_turn_by_id Returns the comparison of the id fields.
575
576 TurnRelX *a The first extended relation.
577
578 TurnRelX *b The second extended relation.
579 ++++++++++++++++++++++++++++++++++++++*/
580
581 static int sort_turn_by_id(TurnRelX *a,TurnRelX *b)
582 {
583 relation_t a_id=a->id;
584 relation_t b_id=b->id;
585
586 if(a_id<b_id)
587 return(-1);
588 else if(a_id>b_id)
589 return(1);
590 else
591 return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
592 }
593
594
595 /*++++++++++++++++++++++++++++++++++++++
596 Deduplicate the turn restriction relations using the id after sorting.
597
598 int deduplicate_turn_by_id Return 1 if the value is to be kept, otherwise 0.
599
600 TurnRelX *relationx The extended relation.
601
602 index_t index The number of sorted relations that have already been written to the output file.
603 ++++++++++++++++++++++++++++++++++++++*/
604
605 static int deduplicate_turn_by_id(TurnRelX *relationx,index_t index)
606 {
607 static relation_t previd; /* internal variable (reset by first call in each sort; index==0) */
608
609 if(index==0 || relationx->id!=previd)
610 {
611 previd=relationx->id;
612
613 if(relationx->except==RELATION_DELETED)
614 return(0);
615 else
616 return(1);
617 }
618 else
619 return(0);
620 }
621
622
623 /*++++++++++++++++++++++++++++++++++++++
624 Process the route relations and apply the information to the ways.
625
626 RelationsX *relationsx The set of relations to use.
627
628 WaysX *waysx The set of ways to modify.
629
630 int keep If set to 1 then keep the old data file otherwise delete it.
631 ++++++++++++++++++++++++++++++++++++++*/
632
633 void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
634 {
635 RouteRelX *unmatched=NULL,*lastunmatched=NULL;
636 int nunmatched=0,lastnunmatched=0,iteration=1;
637
638 if(waysx->number==0)
639 return;
640
641 /* Map into memory / open the files */
642
643 #if !SLIM
644 waysx->data=MapFileWriteable(waysx->filename_tmp);
645 #else
646 waysx->fd=SlimMapFileWriteable(waysx->filename_tmp);
647
648 InvalidateWayXCache(waysx->cache);
649 #endif
650
651 /* Re-open the file read-only */
652
653 relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename_tmp);
654
655 /* Read through the file. */
656
657 do
658 {
659 int ways=0,relations=0;
660 index_t i;
661
662 /* Print the start message */
663
664 printf_first("Processing Route Relations (%d): Relations=0 Modified Ways=0",iteration);
665
666 SeekFileBuffered(relationsx->rrfd,0);
667
668 for(i=0;i<relationsx->rrnumber;i++)
669 {
670 FILESORT_VARINT size;
671 RouteRelX relationx;
672 way_t wayid;
673 node_t nodeid;
674 relation_t relationid;
675 transports_t routes=Transports_None;
676
677 /* Read each route relation */
678
679 ReadFileBuffered(relationsx->rrfd,&size,FILESORT_VARSIZE);
680 ReadFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
681
682 /* Decide what type of route it is */
683
684 if(iteration==1)
685 {
686 relations++;
687 routes=relationx.routes;
688 }
689 else
690 {
691 int j;
692
693 for(j=0;j<lastnunmatched;j++)
694 if(lastunmatched[j].id==relationx.id)
695 {
696 relations++;
697
698 if((lastunmatched[j].routes|relationx.routes)==relationx.routes)
699 routes=0; /* Nothing new to add */
700 else
701 routes=lastunmatched[j].routes;
702
703 break;
704 }
705 }
706
707 /* Skip the nodes */
708
709 while(!ReadFileBuffered(relationsx->rrfd,&nodeid,sizeof(node_t)) && nodeid!=NO_NODE_ID)
710 ;
711
712 /* Loop through the ways */
713
714 while(!ReadFileBuffered(relationsx->rrfd,&wayid,sizeof(way_t)) && wayid!=NO_WAY_ID)
715 {
716 /* Update the ways that are listed for the relation */
717
718 if(routes)
719 {
720 index_t way=IndexWayX(waysx,wayid);
721
722 if(way!=NO_WAY)
723 {
724 WayX *wayx=LookupWayX(waysx,way,1);
725
726 if(routes&Transports_Foot)
727 {
728 if(!(wayx->way.allow&Transports_Foot))
729 {
730 logerror("Route Relation %"Prelation_t" for Foot contains Way %"Pway_t" that does not allow Foot transport; overriding.\n",logerror_relation(relationx.id),logerror_way(wayid));
731 wayx->way.allow|=Transports_Foot;
732 }
733 wayx->way.props|=Properties_FootRoute;
734 }
735
736 if(routes&Transports_Bicycle)
737 {
738 if(!(wayx->way.allow&Transports_Bicycle))
739 {
740 logerror("Route Relation %"Prelation_t" for Bicycle contains Way %"Pway_t" that does not allow Bicycle transport; overriding.\n",logerror_relation(relationx.id),logerror_way(wayid));
741 wayx->way.allow|=Transports_Bicycle;
742 }
743 wayx->way.props|=Properties_BicycleRoute;
744 }
745
746 PutBackWayX(waysx,wayx);
747
748 ways++;
749 }
750 else
751 logerror("Route Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database.\n",logerror_relation(relationx.id),logerror_way(wayid));
752 }
753 }
754
755 /* Loop through the relations */
756
757 while(!ReadFileBuffered(relationsx->rrfd,&relationid,sizeof(relation_t)) && relationid!=NO_RELATION_ID)
758 {
759 /* Add the relations that are listed for this relation to the list for next time */
760
761 if(relationid==relationx.id)
762 logerror("Relation %"Prelation_t" contains itself.\n",logerror_relation(relationx.id));
763 else if(routes)
764 {
765 if(nunmatched%256==0)
766 unmatched=(RouteRelX*)realloc((void*)unmatched,(nunmatched+256)*sizeof(RouteRelX));
767
768 unmatched[nunmatched].id=relationid;
769 unmatched[nunmatched].routes=routes;
770
771 nunmatched++;
772 }
773 }
774
775 if(!((i+1)%1000))
776 printf_middle("Processing Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
777 }
778
779 if(lastunmatched)
780 free(lastunmatched);
781
782 lastunmatched=unmatched;
783 lastnunmatched=nunmatched;
784
785 unmatched=NULL;
786 nunmatched=0;
787
788 /* Print the final message */
789
790 printf_last("Processed Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
791 }
792 while(lastnunmatched && iteration++<8);
793
794 if(lastunmatched)
795 free(lastunmatched);
796
797 /* Close the file */
798
799 relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
800
801 if(keep)
802 RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
803
804 /* Unmap from memory / close the files */
805
806 #if !SLIM
807 waysx->data=UnmapFile(waysx->data);
808 #else
809 waysx->fd=SlimUnmapFile(waysx->fd);
810 #endif
811 }
812
813
814 /*++++++++++++++++++++++++++++++++++++++
815 Process the turn relations to update them with node/segment information.
816
817 RelationsX *relationsx The set of relations to modify.
818
819 NodesX *nodesx The set of nodes to use.
820
821 SegmentsX *segmentsx The set of segments to use.
822
823 WaysX *waysx The set of ways to use.
824
825 int keep If set to 1 then keep the old data file otherwise delete it.
826 ++++++++++++++++++++++++++++++++++++++*/
827
828 void ProcessTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int keep)
829 {
830 int trfd;
831 index_t i,total=0,deleted=0;
832
833 if(nodesx->number==0 || segmentsx->number==0)
834 return;
835
836 /* Print the start message */
837
838 printf_first("Processing Turn Relations: Relations=0 Deleted=0 Added=0");
839
840 /* Map into memory / open the files */
841
842 #if !SLIM
843 nodesx->data=MapFileWriteable(nodesx->filename_tmp);
844 segmentsx->data=MapFile(segmentsx->filename_tmp);
845 waysx->data=MapFile(waysx->filename_tmp);
846 #else
847 nodesx->fd=SlimMapFileWriteable(nodesx->filename_tmp);
848 segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
849 waysx->fd=SlimMapFile(waysx->filename_tmp);
850
851 InvalidateNodeXCache(nodesx->cache);
852 InvalidateSegmentXCache(segmentsx->cache);
853 InvalidateWayXCache(waysx->cache);
854 #endif
855
856 /* Re-open the file read-only and a new file writeable */
857
858 if(keep)
859 {
860 RenameFile(relationsx->trfilename_tmp,relationsx->trfilename);
861
862 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename);
863
864 trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
865 }
866 else
867 trfd=ReplaceFileBuffered(relationsx->trfilename_tmp,&relationsx->trfd);
868
869 /* Process all of the relations */
870
871 for(i=0;i<relationsx->trnumber;i++)
872 {
873 TurnRelX relationx;
874 NodeX *nodex;
875 SegmentX *segmentx;
876 index_t via,from,to;
877
878 ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
879
880 via =IndexNodeX(nodesx,relationx.via);
881 from=IndexWayX(waysx,relationx.from);
882 to =IndexWayX(waysx,relationx.to);
883
884 if(via==NO_NODE)
885 {
886 logerror("Turn Relation %"Prelation_t" contains Node %"Pnode_t" that does not exist in the Routino database.\n",logerror_relation(relationx.id),logerror_node(relationx.via));
887 deleted++;
888 goto endloop;
889 }
890
891 if(from==NO_WAY)
892 {
893 logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database.\n",logerror_relation(relationx.id),logerror_way(relationx.from));
894 deleted++;
895 goto endloop;
896 }
897
898 if(to==NO_WAY)
899 {
900 logerror("Turn Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database.\n",logerror_relation(relationx.id),logerror_way(relationx.to));
901 deleted++;
902 goto endloop;
903 }
904
905 relationx.via =via;
906 relationx.from=from;
907 relationx.to =to;
908
909 if(relationx.restriction==TurnRestrict_no_right_turn ||
910 relationx.restriction==TurnRestrict_no_left_turn ||
911 relationx.restriction==TurnRestrict_no_u_turn ||
912 relationx.restriction==TurnRestrict_no_straight_on)
913 {
914 index_t node_from=NO_NODE,node_to=NO_NODE;
915 int oneway_from=0,oneway_to=0,vehicles_from=1,vehicles_to=1;
916
917 /* Find the segments that join the node 'via' */
918
919 segmentx=FirstSegmentX(segmentsx,relationx.via,1);
920
921 while(segmentx)
922 {
923 if(segmentx->way==relationx.from)
924 {
925 WayX *wayx=LookupWayX(waysx,segmentx->way,1);
926
927 if(node_from!=NO_NODE) /* Only one segment can be on the 'from' way */
928 {
929 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",logerror_relation(relationx.id));
930 deleted++;
931 goto endloop;
932 }
933
934 node_from=OtherNode(segmentx,relationx.via);
935
936 if(IsOnewayFrom(segmentx,relationx.via))
937 oneway_from=1; /* not allowed */
938
939 if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
940 vehicles_from=0; /* not allowed */
941 }
942
943 if(segmentx->way==relationx.to)
944 {
945 WayX *wayx=LookupWayX(waysx,segmentx->way,1);
946
947 if(node_to!=NO_NODE) /* Only one segment can be on the 'to' way */
948 {
949 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",logerror_relation(relationx.id));
950 deleted++;
951 goto endloop;
952 }
953
954 node_to=OtherNode(segmentx,relationx.via);
955
956 if(IsOnewayTo(segmentx,relationx.via))
957 oneway_to=1; /* not allowed */
958
959 if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
960 vehicles_to=0; /* not allowed */
961 }
962
963 segmentx=NextSegmentX(segmentsx,segmentx,relationx.via);
964 }
965
966 if(node_from==NO_NODE)
967 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",logerror_relation(relationx.id));
968
969 if(node_to==NO_NODE)
970 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",logerror_relation(relationx.id));
971
972 if(oneway_from)
973 logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way is oneway away from the 'via' node.\n",logerror_relation(relationx.id));
974
975 if(oneway_to)
976 logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way is oneway towards the 'via' node.\n",logerror_relation(relationx.id));
977
978 if(!vehicles_from)
979 logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way does not allow vehicles.\n",logerror_relation(relationx.id));
980
981 if(!vehicles_to)
982 logerror("Turn Relation %"Prelation_t" is not needed because the 'to' way does not allow vehicles.\n",logerror_relation(relationx.id));
983
984 if(oneway_from || oneway_to || !vehicles_from || !vehicles_to || node_from==NO_NODE || node_to==NO_NODE)
985 {
986 deleted++;
987 goto endloop;
988 }
989
990 /* Write the results */
991
992 relationx.from=node_from;
993 relationx.to =node_to;
994
995 WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
996
997 total++;
998 }
999 else
1000 {
1001 index_t node_from=NO_NODE,node_to=NO_NODE,node_other[MAX_SEG_PER_NODE];
1002 int nnodes_other=0,i;
1003 int oneway_from=0,vehicles_from=1;
1004
1005 /* Find the segments that join the node 'via' */
1006
1007 segmentx=FirstSegmentX(segmentsx,relationx.via,1);
1008
1009 while(segmentx)
1010 {
1011 if(segmentx->way==relationx.from)
1012 {
1013 WayX *wayx=LookupWayX(waysx,segmentx->way,1);
1014
1015 if(node_from!=NO_NODE) /* Only one segment can be on the 'from' way */
1016 {
1017 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'from' way.\n",logerror_relation(relationx.id));
1018 deleted++;
1019 goto endloop;
1020 }
1021
1022 node_from=OtherNode(segmentx,relationx.via);
1023
1024 if(IsOnewayFrom(segmentx,relationx.via))
1025 oneway_from=1; /* not allowed */
1026
1027 if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
1028 vehicles_from=0; /* not allowed */
1029 }
1030
1031 if(segmentx->way==relationx.to)
1032 {
1033 if(node_to!=NO_NODE) /* Only one segment can be on the 'to' way */
1034 {
1035 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not at the end of the 'to' way.\n",logerror_relation(relationx.id));
1036 deleted++;
1037 goto endloop;
1038 }
1039
1040 node_to=OtherNode(segmentx,relationx.via);
1041 }
1042
1043 if(segmentx->way!=relationx.from && segmentx->way!=relationx.to)
1044 {
1045 WayX *wayx=LookupWayX(waysx,segmentx->way,1);
1046
1047 if(IsOnewayTo(segmentx,relationx.via))
1048 ; /* not allowed */
1049 else if(!(wayx->way.allow&(Transports_Bicycle|Transports_Moped|Transports_Motorcycle|Transports_Motorcar|Transports_Goods|Transports_HGV|Transports_PSV)))
1050 ; /* not allowed */
1051 else
1052 {
1053 logassert(nnodes_other<MAX_SEG_PER_NODE,"Too many segments for one node (increase MAX_SEG_PER_NODE?)"); /* Only a limited amount of information stored. */
1054
1055 node_other[nnodes_other++]=OtherNode(segmentx,relationx.via);
1056 }
1057 }
1058
1059 segmentx=NextSegmentX(segmentsx,segmentx,relationx.via);
1060 }
1061
1062 if(node_from==NO_NODE)
1063 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'from' way.\n",logerror_relation(relationx.id));
1064
1065 if(node_to==NO_NODE)
1066 logerror("Turn Relation %"Prelation_t" is not stored because the 'via' node is not part of the 'to' way.\n",logerror_relation(relationx.id));
1067
1068 if(nnodes_other==0)
1069 logerror("Turn Relation %"Prelation_t" is not needed because the only allowed exit from the 'via' node is the 'to' way.\n",logerror_relation(relationx.id));
1070
1071 if(oneway_from)
1072 logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way is oneway away from the 'via' node.\n",logerror_relation(relationx.id));
1073
1074 if(!vehicles_from)
1075 logerror("Turn Relation %"Prelation_t" is not needed because the 'from' way does not allow vehicles.\n",logerror_relation(relationx.id));
1076
1077 if(oneway_from || !vehicles_from || node_from==NO_NODE || node_to==NO_NODE || nnodes_other==0)
1078 {
1079 deleted++;
1080 goto endloop;
1081 }
1082
1083 /* Write the results */
1084
1085 for(i=0;i<nnodes_other;i++)
1086 {
1087 relationx.from=node_from;
1088 relationx.to =node_other[i];
1089
1090 WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
1091
1092 total++;
1093 }
1094 }
1095
1096 /* Force super nodes on via node and adjacent nodes */
1097
1098 nodex=LookupNodeX(nodesx,relationx.via,1);
1099 nodex->flags|=NODE_TURNRSTRCT;
1100 PutBackNodeX(nodesx,nodex);
1101
1102 segmentx=FirstSegmentX(segmentsx,relationx.via,1);
1103
1104 while(segmentx)
1105 {
1106 index_t othernode=OtherNode(segmentx,relationx.via);
1107
1108 nodex=LookupNodeX(nodesx,othernode,1);
1109 nodex->flags|=NODE_TURNRSTRCT2;
1110 PutBackNodeX(nodesx,nodex);
1111
1112 segmentx=NextSegmentX(segmentsx,segmentx,relationx.via);
1113 }
1114
1115 endloop:
1116
1117 if(!((i+1)%1000))
1118 printf_middle("Processing Turn Relations: Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,i+1,deleted,total-relationsx->trnumber+deleted);
1119 }
1120
1121 /* Close the files */
1122
1123 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1124 CloseFileBuffered(trfd);
1125
1126 /* Free the now-unneeded indexes */
1127
1128 log_free(nodesx->idata);
1129 free(nodesx->idata);
1130 nodesx->idata=NULL;
1131
1132 log_free(waysx->idata);
1133 free(waysx->idata);
1134 waysx->idata=NULL;
1135
1136 log_free(segmentsx->firstnode);
1137 free(segmentsx->firstnode);
1138 segmentsx->firstnode=NULL;
1139
1140 /* Unmap from memory / close the files */
1141
1142 #if !SLIM
1143 nodesx->data=UnmapFile(nodesx->data);
1144 segmentsx->data=UnmapFile(segmentsx->data);
1145 waysx->data=UnmapFile(waysx->data);
1146 #else
1147 nodesx->fd=SlimUnmapFile(nodesx->fd);
1148 segmentsx->fd=SlimUnmapFile(segmentsx->fd);
1149 waysx->fd=SlimUnmapFile(waysx->fd);
1150 #endif
1151
1152 /* Print the final message */
1153
1154 printf_last("Processed Turn Relations: Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
1155
1156 relationsx->trnumber=total;
1157 }
1158
1159
1160 /*++++++++++++++++++++++++++++++++++++++
1161 Remove pruned turn relations and update the node indexes after pruning nodes.
1162
1163 RelationsX *relationsx The set of relations to modify.
1164
1165 NodesX *nodesx The set of nodes to use.
1166 ++++++++++++++++++++++++++++++++++++++*/
1167
1168 void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx)
1169 {
1170 TurnRelX relationx;
1171 index_t total=0,pruned=0,notpruned=0;
1172 int trfd;
1173
1174 if(relationsx->trnumber==0)
1175 return;
1176
1177 /* Print the start message */
1178
1179 printf_first("Deleting Pruned Turn Relations: Relations=0 Pruned=0");
1180
1181 /* Re-open the file read-only and a new file writeable */
1182
1183 trfd=ReplaceFileBuffered(relationsx->trfilename_tmp,&relationsx->trfd);
1184
1185 /* Process all of the relations */
1186
1187 while(!ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX)))
1188 {
1189 relationx.from=nodesx->pdata[relationx.from];
1190 relationx.via =nodesx->pdata[relationx.via];
1191 relationx.to =nodesx->pdata[relationx.to];
1192
1193 if(relationx.from==NO_NODE || relationx.via==NO_NODE || relationx.to==NO_NODE)
1194 pruned++;
1195 else
1196 {
1197 WriteFileBuffered(trfd,&relationx,sizeof(TurnRelX));
1198
1199 notpruned++;
1200 }
1201
1202 total++;
1203
1204 if(!(total%1000))
1205 printf_middle("Deleting Pruned Turn Relations: Relations=%"Pindex_t" Pruned=%"Pindex_t,total,pruned);
1206 }
1207
1208 relationsx->trnumber=notpruned;
1209
1210 /* Close the files */
1211
1212 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1213 CloseFileBuffered(trfd);
1214
1215 /* Print the final message */
1216
1217 printf_last("Deleted Pruned Turn Relations: Relations=%"Pindex_t" Pruned=%"Pindex_t,total,pruned);
1218 }
1219
1220
1221 /*++++++++++++++++++++++++++++++++++++++
1222 Sort the turn relations geographically after updating the node indexes.
1223
1224 RelationsX *relationsx The set of relations to modify.
1225
1226 NodesX *nodesx The set of nodes to use.
1227
1228 SegmentsX *segmentsx The set of segments to use.
1229
1230 int convert Set to 1 to convert the segments as well as sorting them (the second time it is called).
1231 ++++++++++++++++++++++++++++++++++++++*/
1232
1233 void SortTurnRelationListGeographically(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,int convert)
1234 {
1235 int trfd;
1236
1237 if(segmentsx->number==0)
1238 return;
1239
1240 /* Print the start message */
1241
1242 printf_first("Sorting Turn Relations Geographically");
1243
1244 /* Map into memory / open the files */
1245
1246 #if !SLIM
1247 segmentsx->data=MapFile(segmentsx->filename_tmp);
1248 #else
1249 segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
1250
1251 InvalidateSegmentXCache(segmentsx->cache);
1252 #endif
1253
1254 /* Re-open the file read-only and a new file writeable */
1255
1256 trfd=ReplaceFileBuffered(relationsx->trfilename_tmp,&relationsx->trfd);
1257
1258 /* Update the segments with geographically sorted node indexes and sort them */
1259
1260 sortnodesx=nodesx;
1261 sortsegmentsx=segmentsx;
1262
1263 if(!convert)
1264 filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRelX),(int (*)(void*,index_t))geographically_index,
1265 (int (*)(const void*,const void*))sort_by_via,
1266 NULL);
1267 else
1268 filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRelX),(int (*)(void*,index_t))geographically_index_convert_segments,
1269 (int (*)(const void*,const void*))sort_by_via,
1270 NULL);
1271
1272 /* Close the files */
1273
1274 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1275 CloseFileBuffered(trfd);
1276
1277 /* Unmap from memory / close the files */
1278
1279 #if !SLIM
1280 segmentsx->data=UnmapFile(segmentsx->data);
1281 #else
1282 segmentsx->fd=SlimUnmapFile(segmentsx->fd);
1283 #endif
1284
1285 /* Free the memory */
1286
1287 if(nodesx->gdata)
1288 {
1289 log_free(nodesx->gdata);
1290 free(nodesx->gdata);
1291 nodesx->gdata=NULL;
1292 }
1293
1294 /* Print the final message */
1295
1296 printf_last("Sorted Turn Relations Geographically: Turn Relations=%"Pindex_t,relationsx->trnumber);
1297 }
1298
1299
1300 /*++++++++++++++++++++++++++++++++++++++
1301 Update the turn relation indexes.
1302
1303 int geographically_index Return 1 if the value is to be kept, otherwise 0.
1304
1305 TurnRelX *relationx The extended turn relation.
1306
1307 index_t index The number of unsorted turn relations that have been read from the input file.
1308 ++++++++++++++++++++++++++++++++++++++*/
1309
1310 static int geographically_index(TurnRelX *relationx,index_t index)
1311 {
1312 relationx->from=sortnodesx->gdata[relationx->from];
1313 relationx->via =sortnodesx->gdata[relationx->via];
1314 relationx->to =sortnodesx->gdata[relationx->to];
1315
1316 return(1);
1317 }
1318
1319
1320 /*++++++++++++++++++++++++++++++++++++++
1321 Update the turn relation indexes and replace them with segments.
1322
1323 int geographically_index_convert_segments Return 1 if the value is to be kept, otherwise 0.
1324
1325 TurnRelX *relationx The extended turn relation.
1326
1327 index_t index The number of unsorted turn relations that have been read from the input file.
1328 ++++++++++++++++++++++++++++++++++++++*/
1329
1330 static int geographically_index_convert_segments(TurnRelX *relationx,index_t index)
1331 {
1332 SegmentX *segmentx;
1333 index_t from_node,via_node,to_node;
1334
1335 from_node=sortnodesx->gdata[relationx->from];
1336 via_node =sortnodesx->gdata[relationx->via];
1337 to_node =sortnodesx->gdata[relationx->to];
1338
1339 segmentx=FirstSegmentX(sortsegmentsx,via_node,1);
1340
1341 do
1342 {
1343 if(OtherNode(segmentx,via_node)==from_node)
1344 relationx->from=IndexSegmentX(sortsegmentsx,segmentx);
1345
1346 if(OtherNode(segmentx,via_node)==to_node)
1347 relationx->to=IndexSegmentX(sortsegmentsx,segmentx);
1348
1349 segmentx=NextSegmentX(sortsegmentsx,segmentx,via_node);
1350 }
1351 while(segmentx);
1352
1353 relationx->via=via_node;
1354
1355 return(1);
1356 }
1357
1358
1359 /*++++++++++++++++++++++++++++++++++++++
1360 Sort the turn restriction relations into via index order (then by from and to segments).
1361
1362 int sort_by_via Returns the comparison of the via, from and to fields.
1363
1364 TurnRelX *a The first extended relation.
1365
1366 TurnRelX *b The second extended relation.
1367 ++++++++++++++++++++++++++++++++++++++*/
1368
1369 static int sort_by_via(TurnRelX *a,TurnRelX *b)
1370 {
1371 index_t a_id=a->via;
1372 index_t b_id=b->via;
1373
1374 if(a_id<b_id)
1375 return(-1);
1376 else if(a_id>b_id)
1377 return(1);
1378 else
1379 {
1380 index_t a_id=a->from;
1381 index_t b_id=b->from;
1382
1383 if(a_id<b_id)
1384 return(-1);
1385 else if(a_id>b_id)
1386 return(1);
1387 else
1388 {
1389 index_t a_id=a->to;
1390 index_t b_id=b->to;
1391
1392 if(a_id<b_id)
1393 return(-1);
1394 else if(a_id>b_id)
1395 return(1);
1396 else
1397 return(FILESORT_PRESERVE_ORDER(a,b));
1398 }
1399 }
1400 }
1401
1402
1403 /*++++++++++++++++++++++++++++++++++++++
1404 Save the relation list to a file.
1405
1406 RelationsX* relationsx The set of relations to save.
1407
1408 const char *filename The name of the file to save.
1409 ++++++++++++++++++++++++++++++++++++++*/
1410
1411 void SaveRelationList(RelationsX* relationsx,const char *filename)
1412 {
1413 index_t i;
1414 int fd;
1415 RelationsFile relationsfile={0};
1416
1417 /* Print the start message */
1418
1419 printf_first("Writing Relations: Turn Relations=0");
1420
1421 /* Re-open the file read-only */
1422
1423 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
1424
1425 /* Write out the relations data */
1426
1427 fd=OpenFileBufferedNew(filename);
1428
1429 SeekFileBuffered(fd,sizeof(RelationsFile));
1430
1431 for(i=0;i<relationsx->trnumber;i++)
1432 {
1433 TurnRelX relationx;
1434 TurnRelation relation={0};
1435
1436 ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
1437
1438 relation.from=relationx.from;
1439 relation.via=relationx.via;
1440 relation.to=relationx.to;
1441 relation.except=relationx.except;
1442
1443 WriteFileBuffered(fd,&relation,sizeof(TurnRelation));
1444
1445 if(!((i+1)%1000))
1446 printf_middle("Writing Relations: Turn Relations=%"Pindex_t,i+1);
1447 }
1448
1449 /* Write out the header structure */
1450
1451 relationsfile.trnumber=relationsx->trnumber;
1452
1453 SeekFileBuffered(fd,0);
1454 WriteFileBuffered(fd,&relationsfile,sizeof(RelationsFile));
1455
1456 CloseFileBuffered(fd);
1457
1458 /* Close the file */
1459
1460 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1461
1462 /* Print the final message */
1463
1464 printf_last("Wrote Relations: Turn Relations=%"Pindex_t,relationsx->trnumber);
1465 }