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/relationsx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1598 - (show annotations) (download) (as text)
Sat Sep 27 16:35:09 2014 UTC (10 years, 5 months ago) by amb
File MIME type: text/x-csrc
File size: 41948 byte(s)
Add a '--logmemory' option to planetsplitter which will report the maximum
memory in use (allocated and mapped files) during each step of the processing.

1 /***************************************
2 Extended Relation data type functions.
3
4 Part of the Routino routing software.
5 ******************/ /******************
6 This file Copyright 2010-2014 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. +*/
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 sort_by_via(TurnRelX *a,TurnRelX *b);
61
62
63 /*++++++++++++++++++++++++++++++++++++++
64 Allocate a new relation list (create a new file or open an existing one).
65
66 RelationsX *NewRelationList Returns the relation list.
67
68 int append Set to 1 if the file is to be opened for appending.
69
70 int readonly Set to 1 if the file is to be opened for reading.
71 ++++++++++++++++++++++++++++++++++++++*/
72
73 RelationsX *NewRelationList(int append,int readonly)
74 {
75 RelationsX *relationsx;
76
77 relationsx=(RelationsX*)calloc(1,sizeof(RelationsX));
78
79 logassert(relationsx,"Failed to allocate memory (try using slim mode?)"); /* Check calloc() worked */
80
81
82 /* Route Relations */
83
84 relationsx->rrfilename =(char*)malloc(strlen(option_tmpdirname)+32);
85 relationsx->rrfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
86
87 sprintf(relationsx->rrfilename ,"%s/relationsx.route.parsed.mem",option_tmpdirname);
88 sprintf(relationsx->rrfilename_tmp,"%s/relationsx.route.%p.tmp" ,option_tmpdirname,(void*)relationsx);
89
90 if(append || readonly)
91 if(ExistsFile(relationsx->rrfilename))
92 {
93 FILESORT_VARINT relationsize;
94 int rrfd;
95
96 rrfd=ReOpenFileBuffered(relationsx->rrfilename);
97
98 while(!ReadFileBuffered(rrfd,&relationsize,FILESORT_VARSIZE))
99 {
100 SkipFileBuffered(rrfd,relationsize);
101
102 relationsx->rrnumber++;
103 }
104
105 CloseFileBuffered(rrfd);
106
107 RenameFile(relationsx->rrfilename,relationsx->rrfilename_tmp);
108 }
109
110 if(append)
111 relationsx->rrfd=OpenFileBufferedAppend(relationsx->rrfilename_tmp);
112 else if(!readonly)
113 relationsx->rrfd=OpenFileBufferedNew(relationsx->rrfilename_tmp);
114 else
115 relationsx->rrfd=-1;
116
117
118 /* Turn Restriction Relations */
119
120 relationsx->trfilename =(char*)malloc(strlen(option_tmpdirname)+32);
121 relationsx->trfilename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
122
123 sprintf(relationsx->trfilename ,"%s/relationsx.turn.parsed.mem",option_tmpdirname);
124 sprintf(relationsx->trfilename_tmp,"%s/relationsx.turn.%p.tmp" ,option_tmpdirname,(void*)relationsx);
125
126 if(append || readonly)
127 if(ExistsFile(relationsx->trfilename))
128 {
129 off_t size;
130
131 size=SizeFile(relationsx->trfilename);
132
133 relationsx->trnumber=size/sizeof(TurnRelX);
134
135 RenameFile(relationsx->trfilename,relationsx->trfilename_tmp);
136 }
137
138 if(append)
139 relationsx->trfd=OpenFileBufferedAppend(relationsx->trfilename_tmp);
140 else if(!readonly)
141 relationsx->trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
142 else
143 relationsx->trfd=-1;
144
145 return(relationsx);
146 }
147
148
149 /*++++++++++++++++++++++++++++++++++++++
150 Free a relation list.
151
152 RelationsX *relationsx The set of relations to be freed.
153
154 int keep If set then the results file is to be kept.
155 ++++++++++++++++++++++++++++++++++++++*/
156
157 void FreeRelationList(RelationsX *relationsx,int keep)
158 {
159 /* Route relations */
160
161 if(keep)
162 RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
163 else
164 DeleteFile(relationsx->rrfilename_tmp);
165
166 free(relationsx->rrfilename);
167 free(relationsx->rrfilename_tmp);
168
169 if(relationsx->rridata)
170 {
171 log_free(relationsx->rridata);
172 free(relationsx->rridata);
173 }
174
175 if(relationsx->rrodata)
176 {
177 log_free(relationsx->rrodata);
178 free(relationsx->rrodata);
179 }
180
181
182 /* Turn Restriction relations */
183
184 if(keep)
185 RenameFile(relationsx->trfilename_tmp,relationsx->trfilename);
186 else
187 DeleteFile(relationsx->trfilename_tmp);
188
189 free(relationsx->trfilename);
190 free(relationsx->trfilename_tmp);
191
192 if(relationsx->tridata)
193 {
194 log_free(relationsx->tridata);
195 free(relationsx->tridata);
196 }
197
198
199 free(relationsx);
200 }
201
202
203 /*++++++++++++++++++++++++++++++++++++++
204 Append a single relation to an unsorted route relation list.
205
206 RelationsX* relationsx The set of relations to process.
207
208 relation_t id The ID of the relation.
209
210 transports_t routes The types of routes that this relation is for.
211
212 node_t *nodes The array of nodes that are members of the relation.
213
214 int nnodes The number of nodes that are members of the relation.
215
216 way_t *ways The array of ways that are members of the relation.
217
218 int nways The number of ways that are members of the relation.
219
220 relation_t *relations The array of relations that are members of the relation.
221
222 int nrelations The number of relations that are members of the relation.
223 ++++++++++++++++++++++++++++++++++++++*/
224
225 void AppendRouteRelationList(RelationsX* relationsx,relation_t id,
226 transports_t routes,
227 node_t *nodes,int nnodes,
228 way_t *ways,int nways,
229 relation_t *relations,int nrelations)
230 {
231 RouteRelX relationx={0};
232 FILESORT_VARINT size;
233 node_t nonode=NO_NODE_ID;
234 way_t noway=NO_WAY_ID;
235 relation_t norelation=NO_RELATION_ID;
236
237 relationx.id=id;
238 relationx.routes=routes;
239
240 size=sizeof(RouteRelX)+(nnodes+1)*sizeof(node_t)+(nways+1)*sizeof(way_t)+(nrelations+1)*sizeof(relation_t);
241
242 WriteFileBuffered(relationsx->rrfd,&size,FILESORT_VARSIZE);
243 WriteFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
244
245 WriteFileBuffered(relationsx->rrfd,nodes ,nnodes*sizeof(node_t));
246 WriteFileBuffered(relationsx->rrfd,&nonode, sizeof(node_t));
247
248 WriteFileBuffered(relationsx->rrfd,ways ,nways*sizeof(way_t));
249 WriteFileBuffered(relationsx->rrfd,&noway, sizeof(way_t));
250
251 WriteFileBuffered(relationsx->rrfd,relations ,nrelations*sizeof(relation_t));
252 WriteFileBuffered(relationsx->rrfd,&norelation, sizeof(relation_t));
253
254 relationsx->rrnumber++;
255
256 logassert(relationsx->rrnumber!=0,"Too many route relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
257 }
258
259
260 /*++++++++++++++++++++++++++++++++++++++
261 Append a single relation to an unsorted turn restriction relation list.
262
263 RelationsX* relationsx The set of relations to process.
264
265 relation_t id The ID of the relation.
266
267 way_t from The way that the turn restriction starts from.
268
269 way_t to The way that the restriction finished on.
270
271 node_t via The node that the turn restriction passes through.
272
273 TurnRestriction restriction The type of restriction.
274
275 transports_t except The set of transports allowed to bypass the restriction.
276 ++++++++++++++++++++++++++++++++++++++*/
277
278 void AppendTurnRelationList(RelationsX* relationsx,relation_t id,
279 way_t from,way_t to,node_t via,
280 TurnRestriction restriction,transports_t except)
281 {
282 TurnRelX relationx={0};
283
284 relationx.id=id;
285 relationx.from=from;
286 relationx.to=to;
287 relationx.via=via;
288 relationx.restriction=restriction;
289 relationx.except=except;
290
291 WriteFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
292
293 relationsx->trnumber++;
294
295 logassert(relationsx->trnumber!=0,"Too many turn relations (change index_t to 64-bits?)"); /* Zero marks the high-water mark for relations. */
296 }
297
298
299 /*++++++++++++++++++++++++++++++++++++++
300 Finish appending relations and change the filename over.
301
302 RelationsX *relationsx The relations that have been appended.
303 ++++++++++++++++++++++++++++++++++++++*/
304
305 void FinishRelationList(RelationsX *relationsx)
306 {
307 if(relationsx->rrfd!=-1)
308 relationsx->rrfd =CloseFileBuffered(relationsx->rrfd);
309
310 if(relationsx->trfd!=-1)
311 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
312 }
313
314
315 /*++++++++++++++++++++++++++++++++++++++
316 Find a particular route relation index.
317
318 index_t IndexRouteRelX Returns the index of the route relation with the specified id.
319
320 RelationsX *relationsx The set of relations to process.
321
322 relation_t id The relation id to look for.
323 ++++++++++++++++++++++++++++++++++++++*/
324
325 index_t IndexRouteRelX(RelationsX *relationsx,relation_t id)
326 {
327 index_t start=0;
328 index_t end=relationsx->rrnumber-1;
329 index_t mid;
330
331 if(relationsx->rrnumber==0) /* There are no route relations */
332 return(NO_RELATION);
333
334 if(id<relationsx->rridata[start]) /* Key is before start */
335 return(NO_RELATION);
336
337 if(id>relationsx->rridata[end]) /* Key is after end */
338 return(NO_RELATION);
339
340 /* Binary search - search key exact match only is required.
341 *
342 * # <- start | Check mid and move start or end if it doesn't match
343 * # |
344 * # | Since an exact match is wanted we can set end=mid-1
345 * # <- mid | or start=mid+1 because we know that mid doesn't match.
346 * # |
347 * # | Eventually either end=start or end=start+1 and one of
348 * # <- end | start or end is the wanted one.
349 */
350
351 do
352 {
353 mid=(start+end)/2; /* Choose mid point */
354
355 if(relationsx->rridata[mid]<id) /* Mid point is too low */
356 start=mid+1;
357 else if(relationsx->rridata[mid]>id) /* Mid point is too high */
358 end=mid?(mid-1):mid;
359 else /* Mid point is correct */
360 return(mid);
361 }
362 while((end-start)>1);
363
364 if(relationsx->rridata[start]==id) /* Start is correct */
365 return(start);
366
367 if(relationsx->rridata[end]==id) /* End is correct */
368 return(end);
369
370 return(NO_RELATION);
371 }
372
373
374 /*++++++++++++++++++++++++++++++++++++++
375 Find a particular route relation index.
376
377 index_t IndexTurnRelX Returns the index of the turn relation with the specified id.
378
379 RelationsX *relationsx The set of relations to process.
380
381 relation_t id The relation id to look for.
382 ++++++++++++++++++++++++++++++++++++++*/
383
384 index_t IndexTurnRelX(RelationsX *relationsx,relation_t id)
385 {
386 index_t start=0;
387 index_t end=relationsx->trnumber-1;
388 index_t mid;
389
390 if(relationsx->trnumber==0) /* There are no route relations */
391 return(NO_RELATION);
392
393 if(id<relationsx->tridata[start]) /* Key is before start */
394 return(NO_RELATION);
395
396 if(id>relationsx->tridata[end]) /* Key is after end */
397 return(NO_RELATION);
398
399 /* Binary search - search key exact match only is required.
400 *
401 * # <- start | Check mid and move start or end if it doesn't match
402 * # |
403 * # | Since an exact match is wanted we can set end=mid-1
404 * # <- mid | or start=mid+1 because we know that mid doesn't match.
405 * # |
406 * # | Eventually either end=start or end=start+1 and one of
407 * # <- end | start or end is the wanted one.
408 */
409
410 do
411 {
412 mid=(start+end)/2; /* Choose mid point */
413
414 if(relationsx->tridata[mid]<id) /* Mid point is too low */
415 start=mid+1;
416 else if(relationsx->tridata[mid]>id) /* Mid point is too high */
417 end=mid?(mid-1):mid;
418 else /* Mid point is correct */
419 return(mid);
420 }
421 while((end-start)>1);
422
423 if(relationsx->tridata[start]==id) /* Start is correct */
424 return(start);
425
426 if(relationsx->tridata[end]==id) /* End is correct */
427 return(end);
428
429 return(NO_RELATION);
430 }
431
432
433 /*++++++++++++++++++++++++++++++++++++++
434 Sort the list of relations.
435
436 RelationsX* relationsx The set of relations to process.
437 ++++++++++++++++++++++++++++++++++++++*/
438
439 void SortRelationList(RelationsX* relationsx)
440 {
441 /* Route Relations */
442
443 if(relationsx->rrnumber)
444 {
445 index_t rrxnumber;
446 int rrfd;
447
448 /* Print the start message */
449
450 printf_first("Sorting Route Relations");
451
452 /* Re-open the file read-only and a new file writeable */
453
454 relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename_tmp);
455
456 DeleteFile(relationsx->rrfilename_tmp);
457
458 rrfd=OpenFileBufferedNew(relationsx->rrfilename_tmp);
459
460 /* Sort the relations */
461
462 rrxnumber=relationsx->rrnumber;
463
464 relationsx->rrnumber=filesort_vary(relationsx->rrfd,rrfd,NULL,
465 (int (*)(const void*,const void*))sort_route_by_id,
466 (int (*)(void*,index_t))deduplicate_route_by_id);
467
468 relationsx->rrknumber=relationsx->rrnumber;
469
470 /* Close the files */
471
472 relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
473 CloseFileBuffered(rrfd);
474
475 /* Print the final message */
476
477 printf_last("Sorted Route Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,rrxnumber,rrxnumber-relationsx->rrnumber);
478 }
479
480 /* Turn Restriction Relations. */
481
482 if(relationsx->trnumber)
483 {
484 index_t trxnumber;
485 int trfd;
486
487 /* Print the start message */
488
489 printf_first("Sorting Turn Relations");
490
491 /* Re-open the file read-only and a new file writeable */
492
493 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
494
495 DeleteFile(relationsx->trfilename_tmp);
496
497 trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
498
499 /* Sort the relations */
500
501 trxnumber=relationsx->trnumber;
502
503 relationsx->trnumber=filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRelX),NULL,
504 (int (*)(const void*,const void*))sort_turn_by_id,
505 (int (*)(void*,index_t))deduplicate_turn_by_id);
506
507 relationsx->trknumber=relationsx->trnumber;
508
509 /* Close the files */
510
511 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
512 CloseFileBuffered(trfd);
513
514 /* Print the final message */
515
516 printf_last("Sorted Turn Relations: Relations=%"Pindex_t" Duplicates=%"Pindex_t,trxnumber,trxnumber-relationsx->trnumber);
517 }
518 }
519
520
521 /*++++++++++++++++++++++++++++++++++++++
522 Sort the route relations into id order.
523
524 int sort_route_by_id Returns the comparison of the id fields.
525
526 RouteRelX *a The first extended relation.
527
528 RouteRelX *b The second extended relation.
529 ++++++++++++++++++++++++++++++++++++++*/
530
531 static int sort_route_by_id(RouteRelX *a,RouteRelX *b)
532 {
533 relation_t a_id=a->id;
534 relation_t b_id=b->id;
535
536 if(a_id<b_id)
537 return(-1);
538 else if(a_id>b_id)
539 return(1);
540 else
541 return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
542 }
543
544
545 /*++++++++++++++++++++++++++++++++++++++
546 Deduplicate the route relations using the id after sorting.
547
548 int deduplicate_route_by_id Return 1 if the value is to be kept, otherwise 0.
549
550 RouteRelX *relationx The extended relation.
551
552 index_t index The number of sorted relations that have already been written to the output file.
553 ++++++++++++++++++++++++++++++++++++++*/
554
555 static int deduplicate_route_by_id(RouteRelX *relationx,index_t index)
556 {
557 static relation_t previd=NO_RELATION_ID;
558
559 if(relationx->id!=previd)
560 {
561 previd=relationx->id;
562
563 if(relationx->routes==RELATION_DELETED)
564 return(0);
565 else
566 return(1);
567 }
568 else
569 return(0);
570 }
571
572
573 /*++++++++++++++++++++++++++++++++++++++
574 Sort the turn restriction relations into id order.
575
576 int sort_turn_by_id Returns the comparison of the id fields.
577
578 TurnRelX *a The first extended relation.
579
580 TurnRelX *b The second extended relation.
581 ++++++++++++++++++++++++++++++++++++++*/
582
583 static int sort_turn_by_id(TurnRelX *a,TurnRelX *b)
584 {
585 relation_t a_id=a->id;
586 relation_t b_id=b->id;
587
588 if(a_id<b_id)
589 return(-1);
590 else if(a_id>b_id)
591 return(1);
592 else
593 return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
594 }
595
596
597 /*++++++++++++++++++++++++++++++++++++++
598 Deduplicate the turn restriction relations using the id after sorting.
599
600 int deduplicate_turn_by_id Return 1 if the value is to be kept, otherwise 0.
601
602 TurnRelX *relationx The extended relation.
603
604 index_t index The number of sorted relations that have already been written to the output file.
605 ++++++++++++++++++++++++++++++++++++++*/
606
607 static int deduplicate_turn_by_id(TurnRelX *relationx,index_t index)
608 {
609 static relation_t previd=NO_RELATION_ID;
610
611 if(relationx->id!=previd)
612 {
613 previd=relationx->id;
614
615 if(relationx->except==RELATION_DELETED)
616 return(0);
617 else
618 return(1);
619 }
620 else
621 return(0);
622 }
623
624
625 /*++++++++++++++++++++++++++++++++++++++
626 Process the route relations and apply the information to the ways.
627
628 RelationsX *relationsx The set of relations to use.
629
630 WaysX *waysx The set of ways to modify.
631
632 int keep If set to 1 then keep the old data file otherwise delete it.
633 ++++++++++++++++++++++++++++++++++++++*/
634
635 void ProcessRouteRelations(RelationsX *relationsx,WaysX *waysx,int keep)
636 {
637 RouteRelX *unmatched=NULL,*lastunmatched=NULL;
638 int nunmatched=0,lastnunmatched=0,iteration=1;
639
640 if(waysx->number==0)
641 return;
642
643 /* Map into memory / open the files */
644
645 #if !SLIM
646 waysx->data=MapFileWriteable(waysx->filename_tmp);
647 #else
648 waysx->fd=SlimMapFileWriteable(waysx->filename_tmp);
649
650 InvalidateWayXCache(waysx->cache);
651 #endif
652
653 /* Re-open the file read-only */
654
655 relationsx->rrfd=ReOpenFileBuffered(relationsx->rrfilename_tmp);
656
657 /* Read through the file. */
658
659 do
660 {
661 int ways=0,relations=0;
662 index_t i;
663
664 /* Print the start message */
665
666 printf_first("Processing Route Relations (%d): Relations=0 Modified Ways=0",iteration);
667
668 SeekFileBuffered(relationsx->rrfd,0);
669
670 for(i=0;i<relationsx->rrnumber;i++)
671 {
672 FILESORT_VARINT size;
673 RouteRelX relationx;
674 way_t wayid;
675 node_t nodeid;
676 relation_t relationid;
677 transports_t routes=Transports_None;
678
679 /* Read each route relation */
680
681 ReadFileBuffered(relationsx->rrfd,&size,FILESORT_VARSIZE);
682 ReadFileBuffered(relationsx->rrfd,&relationx,sizeof(RouteRelX));
683
684 /* Decide what type of route it is */
685
686 if(iteration==1)
687 {
688 relations++;
689 routes=relationx.routes;
690 }
691 else
692 {
693 int j;
694
695 for(j=0;j<lastnunmatched;j++)
696 if(lastunmatched[j].id==relationx.id)
697 {
698 relations++;
699
700 if((lastunmatched[j].routes|relationx.routes)==relationx.routes)
701 routes=0; /* Nothing new to add */
702 else
703 routes=lastunmatched[j].routes;
704
705 break;
706 }
707 }
708
709 /* Skip the nodes */
710
711 while(!ReadFileBuffered(relationsx->rrfd,&nodeid,sizeof(node_t)) && nodeid!=NO_NODE_ID)
712 ;
713
714 /* Loop through the ways */
715
716 while(!ReadFileBuffered(relationsx->rrfd,&wayid,sizeof(way_t)) && wayid!=NO_WAY_ID)
717 {
718 /* Update the ways that are listed for the relation */
719
720 if(routes)
721 {
722 index_t way=IndexWayX(waysx,wayid);
723
724 if(way!=NO_WAY)
725 {
726 WayX *wayx=LookupWayX(waysx,way,1);
727
728 if(routes&Transports_Foot)
729 {
730 if(!(wayx->way.allow&Transports_Foot))
731 {
732 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));
733 wayx->way.allow|=Transports_Foot;
734 }
735 wayx->way.props|=Properties_FootRoute;
736 }
737
738 if(routes&Transports_Bicycle)
739 {
740 if(!(wayx->way.allow&Transports_Bicycle))
741 {
742 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));
743 wayx->way.allow|=Transports_Bicycle;
744 }
745 wayx->way.props|=Properties_BicycleRoute;
746 }
747
748 PutBackWayX(waysx,wayx);
749
750 ways++;
751 }
752 else
753 logerror("Route Relation %"Prelation_t" contains Way %"Pway_t" that does not exist in the Routino database (not a highway?).\n",logerror_relation(relationx.id),logerror_way(wayid));
754 }
755 }
756
757 /* Loop through the relations */
758
759 while(!ReadFileBuffered(relationsx->rrfd,&relationid,sizeof(relation_t)) && relationid!=NO_RELATION_ID)
760 {
761 /* Add the relations that are listed for this relation to the list for next time */
762
763 if(relationid==relationx.id)
764 logerror("Relation %"Prelation_t" contains itself.\n",logerror_relation(relationx.id));
765 else if(routes)
766 {
767 if(nunmatched%256==0)
768 unmatched=(RouteRelX*)realloc((void*)unmatched,(nunmatched+256)*sizeof(RouteRelX));
769
770 unmatched[nunmatched].id=relationid;
771 unmatched[nunmatched].routes=routes;
772
773 nunmatched++;
774 }
775 }
776
777 if(!((i+1)%1000))
778 printf_middle("Processing Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
779 }
780
781 if(lastunmatched)
782 free(lastunmatched);
783
784 lastunmatched=unmatched;
785 lastnunmatched=nunmatched;
786
787 unmatched=NULL;
788 nunmatched=0;
789
790 /* Print the final message */
791
792 printf_last("Processed Route Relations (%d): Relations=%"Pindex_t" Modified Ways=%"Pindex_t,iteration,relations,ways);
793 }
794 while(lastnunmatched && iteration++<8);
795
796 if(lastunmatched)
797 free(lastunmatched);
798
799 /* Close the file */
800
801 relationsx->rrfd=CloseFileBuffered(relationsx->rrfd);
802
803 if(keep)
804 RenameFile(relationsx->rrfilename_tmp,relationsx->rrfilename);
805
806 /* Unmap from memory / close the files */
807
808 #if !SLIM
809 waysx->data=UnmapFile(waysx->data);
810 #else
811 waysx->fd=SlimUnmapFile(waysx->fd);
812 #endif
813 }
814
815
816 /*++++++++++++++++++++++++++++++++++++++
817 Process the turn relations to update them with node/segment information.
818
819 RelationsX *relationsx The set of relations to modify.
820
821 NodesX *nodesx The set of nodes to use.
822
823 SegmentsX *segmentsx The set of segments to use.
824
825 WaysX *waysx The set of ways to use.
826
827 int keep If set to 1 then keep the old data file otherwise delete it.
828 ++++++++++++++++++++++++++++++++++++++*/
829
830 void ProcessTurnRelations(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx,WaysX *waysx,int keep)
831 {
832 int trfd;
833 index_t i,total=0,deleted=0;
834
835 if(nodesx->number==0 || segmentsx->number==0)
836 return;
837
838 /* Print the start message */
839
840 printf_first("Processing Turn Relations: Relations=0 Deleted=0 Added=0");
841
842 /* Map into memory / open the files */
843
844 #if !SLIM
845 nodesx->data=MapFileWriteable(nodesx->filename_tmp);
846 segmentsx->data=MapFile(segmentsx->filename_tmp);
847 waysx->data=MapFile(waysx->filename_tmp);
848 #else
849 nodesx->fd=SlimMapFileWriteable(nodesx->filename_tmp);
850 segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
851 waysx->fd=SlimMapFile(waysx->filename_tmp);
852
853 InvalidateNodeXCache(nodesx->cache);
854 InvalidateSegmentXCache(segmentsx->cache);
855 InvalidateWayXCache(waysx->cache);
856 #endif
857
858 /* Re-open the file read-only and a new file writeable */
859
860 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
861
862 if(keep)
863 RenameFile(relationsx->trfilename_tmp,relationsx->trfilename);
864 else
865 DeleteFile(relationsx->trfilename_tmp);
866
867 trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
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 (not a highway node?).\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 (not a highway?).\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 (not a highway?).\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 /* Unmap from memory / close the files */
1137
1138 #if !SLIM
1139 nodesx->data=UnmapFile(nodesx->data);
1140 segmentsx->data=UnmapFile(segmentsx->data);
1141 waysx->data=UnmapFile(waysx->data);
1142 #else
1143 nodesx->fd=SlimUnmapFile(nodesx->fd);
1144 segmentsx->fd=SlimUnmapFile(segmentsx->fd);
1145 waysx->fd=SlimUnmapFile(waysx->fd);
1146 #endif
1147
1148 /* Print the final message */
1149
1150 printf_last("Processed Turn Relations: Relations=%"Pindex_t" Deleted=%"Pindex_t" Added=%"Pindex_t,total,deleted,total-relationsx->trnumber+deleted);
1151
1152 relationsx->trnumber=total;
1153 }
1154
1155
1156 /*++++++++++++++++++++++++++++++++++++++
1157 Remove pruned turn relations and update the node indexes after pruning nodes.
1158
1159 RelationsX *relationsx The set of relations to modify.
1160
1161 NodesX *nodesx The set of nodes to use.
1162 ++++++++++++++++++++++++++++++++++++++*/
1163
1164 void RemovePrunedTurnRelations(RelationsX *relationsx,NodesX *nodesx)
1165 {
1166 TurnRelX relationx;
1167 index_t total=0,pruned=0,notpruned=0;
1168 int trfd;
1169
1170 if(relationsx->trnumber==0)
1171 return;
1172
1173 /* Print the start message */
1174
1175 printf_first("Deleting Pruned Turn Relations: Relations=0 Pruned=0");
1176
1177 /* Re-open the file read-only and a new file writeable */
1178
1179 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
1180
1181 DeleteFile(relationsx->trfilename_tmp);
1182
1183 trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
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
1231 void SortTurnRelationListGeographically(RelationsX *relationsx,NodesX *nodesx,SegmentsX *segmentsx)
1232 {
1233 int trfd;
1234
1235 if(segmentsx->number==0)
1236 return;
1237
1238 /* Print the start message */
1239
1240 printf_first("Sorting Turn Relations Geographically");
1241
1242 /* Map into memory / open the files */
1243
1244 #if !SLIM
1245 segmentsx->data=MapFile(segmentsx->filename_tmp);
1246 #else
1247 segmentsx->fd=SlimMapFile(segmentsx->filename_tmp);
1248
1249 InvalidateSegmentXCache(segmentsx->cache);
1250 #endif
1251
1252 /* Re-open the file read-only and a new file writeable */
1253
1254 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
1255
1256 DeleteFile(relationsx->trfilename_tmp);
1257
1258 trfd=OpenFileBufferedNew(relationsx->trfilename_tmp);
1259
1260 /* Update the segments with geographically sorted node indexes and sort them */
1261
1262 sortnodesx=nodesx;
1263 sortsegmentsx=segmentsx;
1264
1265 filesort_fixed(relationsx->trfd,trfd,sizeof(TurnRelX),(int (*)(void*,index_t))geographically_index,
1266 (int (*)(const void*,const void*))sort_by_via,
1267 NULL);
1268
1269 /* Close the files */
1270
1271 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1272 CloseFileBuffered(trfd);
1273
1274 /* Unmap from memory / close the files */
1275
1276 #if !SLIM
1277 segmentsx->data=UnmapFile(segmentsx->data);
1278 #else
1279 segmentsx->fd=SlimUnmapFile(segmentsx->fd);
1280 #endif
1281
1282 /* Free the memory */
1283
1284 if(nodesx->gdata)
1285 {
1286 log_free(nodesx->gdata);
1287 free(nodesx->gdata);
1288 nodesx->gdata=NULL;
1289 }
1290
1291 /* Print the final message */
1292
1293 printf_last("Sorted Turn Relations Geographically: Turn Relations=%"Pindex_t,relationsx->trnumber);
1294 }
1295
1296
1297 /*++++++++++++++++++++++++++++++++++++++
1298 Update the turn relation indexes.
1299
1300 int geographically_index Return 1 if the value is to be kept, otherwise 0.
1301
1302 TurnRelX *relationx The extended turn relation.
1303
1304 index_t index The number of unsorted turn relations that have been read from the input file.
1305 ++++++++++++++++++++++++++++++++++++++*/
1306
1307 static int geographically_index(TurnRelX *relationx,index_t index)
1308 {
1309 SegmentX *segmentx;
1310 index_t from_node,via_node,to_node;
1311
1312 from_node=sortnodesx->gdata[relationx->from];
1313 via_node =sortnodesx->gdata[relationx->via];
1314 to_node =sortnodesx->gdata[relationx->to];
1315
1316 segmentx=FirstSegmentX(sortsegmentsx,via_node,1);
1317
1318 do
1319 {
1320 if(OtherNode(segmentx,via_node)==from_node)
1321 relationx->from=IndexSegmentX(sortsegmentsx,segmentx);
1322
1323 if(OtherNode(segmentx,via_node)==to_node)
1324 relationx->to=IndexSegmentX(sortsegmentsx,segmentx);
1325
1326 segmentx=NextSegmentX(sortsegmentsx,segmentx,via_node);
1327 }
1328 while(segmentx);
1329
1330 relationx->via=via_node;
1331
1332 return(1);
1333 }
1334
1335
1336 /*++++++++++++++++++++++++++++++++++++++
1337 Sort the turn restriction relations into via index order (then by from and to segments).
1338
1339 int sort_by_via Returns the comparison of the via, from and to fields.
1340
1341 TurnRelX *a The first extended relation.
1342
1343 TurnRelX *b The second extended relation.
1344 ++++++++++++++++++++++++++++++++++++++*/
1345
1346 static int sort_by_via(TurnRelX *a,TurnRelX *b)
1347 {
1348 index_t a_id=a->via;
1349 index_t b_id=b->via;
1350
1351 if(a_id<b_id)
1352 return(-1);
1353 else if(a_id>b_id)
1354 return(1);
1355 else
1356 {
1357 index_t a_id=a->from;
1358 index_t b_id=b->from;
1359
1360 if(a_id<b_id)
1361 return(-1);
1362 else if(a_id>b_id)
1363 return(1);
1364 else
1365 {
1366 index_t a_id=a->to;
1367 index_t b_id=b->to;
1368
1369 if(a_id<b_id)
1370 return(-1);
1371 else if(a_id>b_id)
1372 return(1);
1373 else
1374 return(FILESORT_PRESERVE_ORDER(a,b));
1375 }
1376 }
1377 }
1378
1379
1380 /*++++++++++++++++++++++++++++++++++++++
1381 Save the relation list to a file.
1382
1383 RelationsX* relationsx The set of relations to save.
1384
1385 const char *filename The name of the file to save.
1386 ++++++++++++++++++++++++++++++++++++++*/
1387
1388 void SaveRelationList(RelationsX* relationsx,const char *filename)
1389 {
1390 index_t i;
1391 int fd;
1392 RelationsFile relationsfile={0};
1393
1394 /* Print the start message */
1395
1396 printf_first("Writing Relations: Turn Relations=0");
1397
1398 /* Re-open the file read-only */
1399
1400 relationsx->trfd=ReOpenFileBuffered(relationsx->trfilename_tmp);
1401
1402 /* Write out the relations data */
1403
1404 fd=OpenFileBufferedNew(filename);
1405
1406 SeekFileBuffered(fd,sizeof(RelationsFile));
1407
1408 for(i=0;i<relationsx->trnumber;i++)
1409 {
1410 TurnRelX relationx;
1411 TurnRelation relation={0};
1412
1413 ReadFileBuffered(relationsx->trfd,&relationx,sizeof(TurnRelX));
1414
1415 relation.from=relationx.from;
1416 relation.via=relationx.via;
1417 relation.to=relationx.to;
1418 relation.except=relationx.except;
1419
1420 WriteFileBuffered(fd,&relation,sizeof(TurnRelation));
1421
1422 if(!((i+1)%1000))
1423 printf_middle("Writing Relations: Turn Relations=%"Pindex_t,i+1);
1424 }
1425
1426 /* Write out the header structure */
1427
1428 relationsfile.trnumber=relationsx->trnumber;
1429
1430 SeekFileBuffered(fd,0);
1431 WriteFileBuffered(fd,&relationsfile,sizeof(RelationsFile));
1432
1433 CloseFileBuffered(fd);
1434
1435 /* Close the file */
1436
1437 relationsx->trfd=CloseFileBuffered(relationsx->trfd);
1438
1439 /* Print the final message */
1440
1441 printf_last("Wrote Relations: Turn Relations=%"Pindex_t,relationsx->trnumber);
1442 }