Imported Upstream version 1.5.1
[routino] / src / nodesx.c
1 /***************************************
2  $Header: /home/amb/routino/src/RCS/nodesx.c,v 1.76 2010/11/13 14:22:28 amb Exp $
3
4  Extented Node data type functions.
5
6  Part of the Routino routing software.
7  ******************/ /******************
8  This file Copyright 2008-2010 Andrew M. Bishop
9
10  This program is free software: you can redistribute it and/or modify
11  it under the terms of the GNU Affero General Public License as published by
12  the Free Software Foundation, either version 3 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU Affero General Public License for more details.
19
20  You should have received a copy of the GNU Affero General Public License
21  along with this program.  If not, see <http://www.gnu.org/licenses/>.
22  ***************************************/
23
24
25 #include <assert.h>
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <sys/stat.h>
30
31 #include "types.h"
32 #include "nodes.h"
33 #include "segments.h"
34
35 #include "nodesx.h"
36 #include "segmentsx.h"
37 #include "waysx.h"
38
39 #include "types.h"
40
41 #include "files.h"
42 #include "logging.h"
43 #include "functions.h"
44
45
46 /* Variables */
47
48 /*+ The command line '--tmpdir' option or its default value. +*/
49 extern char *option_tmpdirname;
50
51 /*+ A temporary file-local variable for use by the sort functions. +*/
52 static NodesX *sortnodesx;
53
54 /* Functions */
55
56 static int sort_by_id(NodeX *a,NodeX *b);
57 static int index_by_id(NodeX *nodex,index_t index);
58
59 static int sort_by_lat_long(NodeX *a,NodeX *b);
60 static int index_by_lat_long(NodeX *nodex,index_t index);
61
62
63 /*++++++++++++++++++++++++++++++++++++++
64   Allocate a new node list (create a new file or open an existing one).
65
66   NodesX *NewNodeList Returns the node list.
67
68   int append Set to 1 if the file is to be opened for appending (now or later).
69   ++++++++++++++++++++++++++++++++++++++*/
70
71 NodesX *NewNodeList(int append)
72 {
73  NodesX *nodesx;
74
75  nodesx=(NodesX*)calloc(1,sizeof(NodesX));
76
77  assert(nodesx); /* Check calloc() worked */
78
79  nodesx->filename=(char*)malloc(strlen(option_tmpdirname)+32);
80
81  if(append)
82     sprintf(nodesx->filename,"%s/nodesx.input.tmp",option_tmpdirname);
83  else
84     sprintf(nodesx->filename,"%s/nodesx.%p.tmp",option_tmpdirname,nodesx);
85
86 #if SLIM
87  nodesx->nfilename=(char*)malloc(strlen(option_tmpdirname)+32);
88
89  sprintf(nodesx->nfilename,"%s/nodes.%p.tmp",option_tmpdirname,nodesx);
90 #endif
91
92  if(append)
93    {
94     off_t size;
95
96     nodesx->fd=OpenFileAppend(nodesx->filename);
97
98     size=SizeFile(nodesx->filename);
99
100     nodesx->xnumber=size/sizeof(NodeX);
101    }
102  else
103     nodesx->fd=OpenFileNew(nodesx->filename);
104
105  return(nodesx);
106 }
107
108
109 /*++++++++++++++++++++++++++++++++++++++
110   Free a node list.
111
112   NodesX *nodesx The list to be freed.
113
114   int keep Set to 1 if the file is to be kept.
115   ++++++++++++++++++++++++++++++++++++++*/
116
117 void FreeNodeList(NodesX *nodesx,int keep)
118 {
119  if(!keep)
120     DeleteFile(nodesx->filename);
121
122  free(nodesx->filename);
123
124  if(nodesx->idata)
125     free(nodesx->idata);
126
127 #if !SLIM
128  if(nodesx->ndata)
129     free(nodesx->ndata);
130 #endif
131
132 #if SLIM
133  DeleteFile(nodesx->nfilename);
134
135  free(nodesx->nfilename);
136 #endif
137
138  if(nodesx->super)
139     free(nodesx->super);
140
141  if(nodesx->offsets)
142     free(nodesx->offsets);
143
144  free(nodesx);
145 }
146
147
148 /*++++++++++++++++++++++++++++++++++++++
149   Append a single node to an unsorted node list.
150
151   NodesX* nodesx The set of nodes to process.
152
153   node_t id The node identification.
154
155   double latitude The latitude of the node.
156
157   double longitude The longitude of the node.
158
159   allow_t allow The allowed traffic types through the node.
160   ++++++++++++++++++++++++++++++++++++++*/
161
162 void AppendNode(NodesX* nodesx,node_t id,double latitude,double longitude,allow_t allow)
163 {
164  NodeX nodex;
165
166  nodex.id=id;
167  nodex.latitude =radians_to_latlong(latitude);
168  nodex.longitude=radians_to_latlong(longitude);
169  nodex.allow=allow;
170
171  WriteFile(nodesx->fd,&nodex,sizeof(NodeX));
172
173  nodesx->xnumber++;
174
175  assert(nodesx->xnumber<NODE_FAKE); /* NODE_FAKE marks the high-water mark for real nodes. */
176 }
177
178
179 /*++++++++++++++++++++++++++++++++++++++
180   Sort the node list (i.e. create the sortable indexes).
181
182   NodesX* nodesx The set of nodes to process.
183   ++++++++++++++++++++++++++++++++++++++*/
184
185 void SortNodeList(NodesX* nodesx)
186 {
187  int fd;
188
189  /* Print the start message */
190
191  printf_first("Sorting Nodes");
192
193  /* Close the files and re-open them (finished appending) */
194
195  CloseFile(nodesx->fd);
196  nodesx->fd=ReOpenFile(nodesx->filename);
197
198  DeleteFile(nodesx->filename);
199
200  fd=OpenFileNew(nodesx->filename);
201
202  /* Allocate the array of indexes */
203
204  nodesx->idata=(node_t*)malloc(nodesx->xnumber*sizeof(node_t));
205
206  assert(nodesx->idata); /* Check malloc() worked */
207
208  /* Sort by node indexes */
209
210  sortnodesx=nodesx;
211
212  filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_id,(int (*)(void*,index_t))index_by_id);
213
214  /* Close the files and re-open them */
215
216  CloseFile(nodesx->fd);
217  CloseFile(fd);
218
219  nodesx->fd=ReOpenFile(nodesx->filename);
220
221  /* Print the final message */
222
223  printf_last("Sorted Nodes: Nodes=%d Duplicates=%d",nodesx->xnumber,nodesx->xnumber-nodesx->number);
224 }
225
226
227 /*++++++++++++++++++++++++++++++++++++++
228   Sort the nodes into id order.
229
230   int sort_by_id Returns the comparison of the id fields.
231
232   NodeX *a The first extended node.
233
234   NodeX *b The second extended node.
235   ++++++++++++++++++++++++++++++++++++++*/
236
237 static int sort_by_id(NodeX *a,NodeX *b)
238 {
239  node_t a_id=a->id;
240  node_t b_id=b->id;
241
242  if(a_id<b_id)
243     return(-1);
244  else if(a_id>b_id)
245     return(1);
246  else
247     return(0);
248 }
249
250
251 /*++++++++++++++++++++++++++++++++++++++
252   Index the nodes after sorting.
253
254   int index_by_id Return 1 if the value is to be kept, otherwise zero.
255
256   NodeX *nodex The extended node.
257
258   index_t index The index of this node in the total.
259   ++++++++++++++++++++++++++++++++++++++*/
260
261 static int index_by_id(NodeX *nodex,index_t index)
262 {
263  if(index==0 || sortnodesx->idata[index-1]!=nodex->id)
264    {
265     sortnodesx->idata[index]=nodex->id;
266
267     sortnodesx->number++;
268
269     return(1);
270    }
271
272  return(0);
273 }
274
275
276 /*++++++++++++++++++++++++++++++++++++++
277   Sort the node list geographically.
278
279   NodesX* nodesx The set of nodes to process.
280   ++++++++++++++++++++++++++++++++++++++*/
281
282 void SortNodeListGeographically(NodesX* nodesx)
283 {
284  int fd;
285
286  /* Print the start message */
287
288  printf_first("Sorting Nodes Geographically");
289
290  /* Allocate the memory for the geographical offsets array */
291
292  nodesx->offsets=(index_t*)malloc((nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
293
294  nodesx->latlonbin=0;
295
296  /* Close the files and re-open them */
297
298  CloseFile(nodesx->fd);
299  nodesx->fd=ReOpenFile(nodesx->filename);
300
301  DeleteFile(nodesx->filename);
302
303  fd=OpenFileNew(nodesx->filename);
304
305  /* Sort geographically */
306
307  sortnodesx=nodesx;
308
309  filesort_fixed(nodesx->fd,fd,sizeof(NodeX),(int (*)(const void*,const void*))sort_by_lat_long,(int (*)(void*,index_t))index_by_lat_long);
310
311  /* Close the files and re-open them */
312
313  CloseFile(nodesx->fd);
314  CloseFile(fd);
315
316  nodesx->fd=ReOpenFile(nodesx->filename);
317
318  /* Finish off the indexing */
319
320  for(;nodesx->latlonbin<=(nodesx->latbins*nodesx->lonbins);nodesx->latlonbin++)
321     nodesx->offsets[nodesx->latlonbin]=nodesx->number;
322
323  /* Print the final message */
324
325  printf_last("Sorted Nodes Geographically");
326 }
327
328
329 /*++++++++++++++++++++++++++++++++++++++
330   Sort the nodes into latitude and longitude order.
331
332   int sort_by_lat_long Returns the comparison of the latitude and longitude fields.
333
334   NodeX *a The first extended node.
335
336   NodeX *b The second extended node.
337   ++++++++++++++++++++++++++++++++++++++*/
338
339 static int sort_by_lat_long(NodeX *a,NodeX *b)
340 {
341  ll_bin_t a_lon=latlong_to_bin(a->longitude);
342  ll_bin_t b_lon=latlong_to_bin(b->longitude);
343
344  if(a_lon<b_lon)
345     return(-1);
346  else if(a_lon>b_lon)
347     return(1);
348  else
349    {
350     ll_bin_t a_lat=latlong_to_bin(a->latitude);
351     ll_bin_t b_lat=latlong_to_bin(b->latitude);
352
353     if(a_lat<b_lat)
354        return(-1);
355     else if(a_lat>b_lat)
356        return(1);
357     else
358       {
359 #ifdef REGRESSION_TESTING
360        // Need this for regression testing because filesort_heapsort() is not order
361        // preserving like qsort() is (or was when tested).
362
363        index_t a_id=a->id;
364        index_t b_id=b->id;
365
366        if(a_id<b_id)
367           return(-1);
368        else if(a_id>b_id)
369           return(1);
370        else
371 #endif
372           return(0);
373       }
374    }
375 }
376
377
378 /*++++++++++++++++++++++++++++++++++++++
379   Index the nodes after sorting.
380
381   int index_by_lat_long Return 1 if the value is to be kept, otherwise zero.
382
383   NodeX *nodex The extended node.
384
385   index_t index The index of this node in the total.
386   ++++++++++++++++++++++++++++++++++++++*/
387
388 static int index_by_lat_long(NodeX *nodex,index_t index)
389 {
390  /* Work out the offsets */
391
392  ll_bin_t latbin=latlong_to_bin(nodex->latitude )-sortnodesx->latzero;
393  ll_bin_t lonbin=latlong_to_bin(nodex->longitude)-sortnodesx->lonzero;
394  int llbin=lonbin*sortnodesx->latbins+latbin;
395
396  for(;sortnodesx->latlonbin<=llbin;sortnodesx->latlonbin++)
397     sortnodesx->offsets[sortnodesx->latlonbin]=index;
398
399  return(1);
400 }
401
402
403 /*++++++++++++++++++++++++++++++++++++++
404   Find a particular node index.
405
406   index_t IndexNodeX Returns the index of the extended node with the specified id.
407
408   NodesX* nodesx The set of nodes to process.
409
410   node_t id The node id to look for.
411   ++++++++++++++++++++++++++++++++++++++*/
412
413 index_t IndexNodeX(NodesX* nodesx,node_t id)
414 {
415  int start=0;
416  int end=nodesx->number-1;
417  int mid;
418
419  /* Binary search - search key exact match only is required.
420   *
421   *  # <- start  |  Check mid and move start or end if it doesn't match
422   *  #           |
423   *  #           |  Since an exact match is wanted we can set end=mid-1
424   *  # <- mid    |  or start=mid+1 because we know that mid doesn't match.
425   *  #           |
426   *  #           |  Eventually either end=start or end=start+1 and one of
427   *  # <- end    |  start or end is the wanted one.
428   */
429
430  if(end<start)                        /* There are no nodes */
431     return(NO_NODE);
432  else if(id<nodesx->idata[start])     /* Check key is not before start */
433     return(NO_NODE);
434  else if(id>nodesx->idata[end])       /* Check key is not after end */
435     return(NO_NODE);
436  else
437    {
438     do
439       {
440        mid=(start+end)/2;             /* Choose mid point */
441
442        if(nodesx->idata[mid]<id)      /* Mid point is too low */
443           start=mid+1;
444        else if(nodesx->idata[mid]>id) /* Mid point is too high */
445           end=mid-1;
446        else                           /* Mid point is correct */
447           return(mid);
448       }
449     while((end-start)>1);
450
451     if(nodesx->idata[start]==id)      /* Start is correct */
452        return(start);
453
454     if(nodesx->idata[end]==id)        /* End is correct */
455        return(end);
456    }
457
458  return(NO_NODE);
459 }
460
461
462 /*++++++++++++++++++++++++++++++++++++++
463   Remove any nodes that are not part of a highway.
464
465   NodesX *nodesx The complete node list.
466
467   SegmentsX *segmentsx The list of segments.
468   ++++++++++++++++++++++++++++++++++++++*/
469
470 void RemoveNonHighwayNodes(NodesX *nodesx,SegmentsX *segmentsx)
471 {
472  NodeX nodex;
473  int total=0,highway=0,nothighway=0;
474  ll_bin_t lat_min_bin,lat_max_bin,lon_min_bin,lon_max_bin;
475  latlong_t lat_min,lat_max,lon_min,lon_max;
476  int fd;
477
478  /* Print the start message */
479
480  printf_first("Checking: Nodes=0");
481
482  /* While we are here we can work out the range of data */
483
484  lat_min=radians_to_latlong( 2);
485  lat_max=radians_to_latlong(-2);
486  lon_min=radians_to_latlong( 4);
487  lon_max=radians_to_latlong(-4);
488
489  /* Modify the on-disk image */
490
491  DeleteFile(nodesx->filename);
492
493  fd=OpenFileNew(nodesx->filename);
494  SeekFile(nodesx->fd,0);
495
496  while(!ReadFile(nodesx->fd,&nodex,sizeof(NodeX)))
497    {
498     if(IndexFirstSegmentX(segmentsx,nodex.id)==NO_SEGMENT)
499        nothighway++;
500     else
501       {
502        nodex.id=highway;
503
504        WriteFile(fd,&nodex,sizeof(NodeX));
505
506        nodesx->idata[highway]=nodesx->idata[total];
507        highway++;
508
509        if(nodex.latitude<lat_min)
510           lat_min=nodex.latitude;
511        if(nodex.latitude>lat_max)
512           lat_max=nodex.latitude;
513        if(nodex.longitude<lon_min)
514           lon_min=nodex.longitude;
515        if(nodex.longitude>lon_max)
516           lon_max=nodex.longitude;
517       }
518
519     total++;
520
521     if(!(total%10000))
522        printf_middle("Checking: Nodes=%d Highway=%d not-Highway=%d",total,highway,nothighway);
523    }
524
525  /* Close the files and re-open them */
526
527  CloseFile(nodesx->fd);
528  CloseFile(fd);
529
530  nodesx->fd=ReOpenFile(nodesx->filename);
531
532  nodesx->number=highway;
533
534  /* Work out the number of bins */
535
536  lat_min_bin=latlong_to_bin(lat_min);
537  lon_min_bin=latlong_to_bin(lon_min);
538  lat_max_bin=latlong_to_bin(lat_max);
539  lon_max_bin=latlong_to_bin(lon_max);
540
541  nodesx->latzero=lat_min_bin;
542  nodesx->lonzero=lon_min_bin;
543
544  nodesx->latbins=(lat_max_bin-lat_min_bin)+1;
545  nodesx->lonbins=(lon_max_bin-lon_min_bin)+1;
546
547  /* Allocate and clear the super-node markers */
548
549  nodesx->super=(uint8_t*)calloc(nodesx->number,sizeof(uint8_t));
550
551  assert(nodesx->super); /* Check calloc() worked */
552
553  /* Print the final message */
554
555  printf_last("Checked: Nodes=%d Highway=%d not-Highway=%d",total,highway,nothighway);
556 }
557
558
559 /*++++++++++++++++++++++++++++++++++++++
560   Create the real node data.
561
562   NodesX *nodesx The set of nodes to use.
563
564   int iteration The final super-node iteration.
565   ++++++++++++++++++++++++++++++++++++++*/
566
567 void CreateRealNodes(NodesX *nodesx,int iteration)
568 {
569  index_t i;
570
571  /* Print the start message */
572
573  printf_first("Creating Real Nodes: Nodes=0");
574
575  /* Map into memory */
576
577 #if !SLIM
578  nodesx->xdata=MapFile(nodesx->filename);
579 #endif
580
581  /* Allocate the memory (or open the file) */
582
583 #if !SLIM
584  nodesx->ndata=(Node*)malloc(nodesx->number*sizeof(Node));
585
586  assert(nodesx->ndata); /* Check malloc() worked */
587 #else
588  nodesx->nfd=OpenFileNew(nodesx->nfilename);
589 #endif
590
591  /* Loop through and allocate. */
592
593  for(i=0;i<nodesx->number;i++)
594    {
595     NodeX *nodex=LookupNodeX(nodesx,i,1);
596     Node  *node =LookupNodeXNode(nodesx,nodex->id,1);
597
598     node->latoffset=latlong_to_off(nodex->latitude);
599     node->lonoffset=latlong_to_off(nodex->longitude);
600     node->firstseg=NO_SEGMENT;
601     node->allow=nodex->allow;
602     node->flags=0;
603
604     if(nodesx->super[nodex->id]==iteration)
605        node->flags|=NODE_SUPER;
606
607 #if SLIM
608     PutBackNodeXNode(nodesx,nodex->id,1);
609 #endif
610
611     if(!((i+1)%10000))
612        printf_middle("Creating Real Nodes: Nodes=%d",i+1);
613    }
614
615  /* Free the unneeded memory */
616
617  free(nodesx->super);
618  nodesx->super=NULL;
619
620  /* Unmap from memory */
621
622 #if !SLIM
623  nodesx->xdata=UnmapFile(nodesx->filename);
624 #endif
625
626  /* Print the final message */
627
628  printf_last("Creating Real Nodes: Nodes=%d",nodesx->number);
629 }
630
631
632 /*++++++++++++++++++++++++++++++++++++++
633   Assign the segment indexes to the nodes.
634
635   NodesX *nodesx The list of nodes to process.
636
637   SegmentsX* segmentsx The set of segments to use.
638   ++++++++++++++++++++++++++++++++++++++*/
639
640 void IndexNodes(NodesX *nodesx,SegmentsX *segmentsx)
641 {
642  index_t i;
643
644  if(nodesx->number==0 || segmentsx->number==0)
645     return;
646
647  /* Print the start message */
648
649  printf_first("Indexing Segments: Segments=0");
650
651  /* Map into memory */
652
653 #if !SLIM
654  nodesx->xdata=MapFile(nodesx->filename);
655  segmentsx->xdata=MapFile(segmentsx->filename);
656 #endif
657
658  /* Index the nodes */
659
660  for(i=0;i<segmentsx->number;i++)
661    {
662     SegmentX *segmentx=LookupSegmentX(segmentsx,i,1);
663     node_t id1=segmentx->node1;
664     node_t id2=segmentx->node2;
665     Node *node1=LookupNodeXNode(nodesx,id1,1);
666     Node *node2=LookupNodeXNode(nodesx,id2,2);
667
668     /* Check node1 */
669
670     if(node1->firstseg==NO_SEGMENT)
671       {
672        node1->firstseg=i;
673
674 #if SLIM
675        PutBackNodeXNode(nodesx,id1,1);
676 #endif
677       }
678     else
679       {
680        index_t index=node1->firstseg;
681
682        do
683          {
684           segmentx=LookupSegmentX(segmentsx,index,1);
685
686           if(segmentx->node1==id1)
687             {
688              index++;
689
690              if(index>=segmentsx->number)
691                 break;
692
693              segmentx=LookupSegmentX(segmentsx,index,1);
694
695              if(segmentx->node1!=id1)
696                 break;
697             }
698           else
699             {
700              Segment *segment=LookupSegmentXSegment(segmentsx,index,1);
701
702              if(segment->next2==NO_NODE)
703                {
704                 segment->next2=i;
705
706 #if SLIM
707                 PutBackSegmentXSegment(segmentsx,index,1);
708 #endif
709
710                 break;
711                }
712              else
713                 index=segment->next2;
714             }
715          }
716        while(1);
717       }
718
719     /* Check node2 */
720
721     if(node2->firstseg==NO_SEGMENT)
722       {
723        node2->firstseg=i;
724
725 #if SLIM
726        PutBackNodeXNode(nodesx,id2,2);
727 #endif
728       }
729     else
730       {
731        index_t index=node2->firstseg;
732
733        do
734          {
735           segmentx=LookupSegmentX(segmentsx,index,1);
736
737           if(segmentx->node1==id2)
738             {
739              index++;
740
741              if(index>=segmentsx->number)
742                 break;
743
744              segmentx=LookupSegmentX(segmentsx,index,1);
745
746              if(segmentx->node1!=id2)
747                 break;
748             }
749           else
750             {
751              Segment *segment=LookupSegmentXSegment(segmentsx,index,1);
752
753              if(segment->next2==NO_NODE)
754                {
755                 segment->next2=i;
756
757 #if SLIM
758                 PutBackSegmentXSegment(segmentsx,index,1);
759 #endif
760
761                 break;
762                }
763              else
764                 index=segment->next2;
765             }
766          }
767        while(1);
768       }
769
770     if(!((i+1)%10000))
771        printf_middle("Indexing Segments: Segments=%d",i+1);
772    }
773
774  /* Unmap from memory */
775
776 #if !SLIM
777  nodesx->xdata=UnmapFile(nodesx->filename);
778  segmentsx->xdata=UnmapFile(segmentsx->filename);
779 #endif
780
781  /* Print the final message */
782
783  printf_last("Indexed Segments: Segments=%d ",segmentsx->number);
784 }
785
786
787 /*++++++++++++++++++++++++++++++++++++++
788   Save the node list to a file.
789
790   NodesX* nodesx The set of nodes to save.
791
792   const char *filename The name of the file to save.
793   ++++++++++++++++++++++++++++++++++++++*/
794
795 void SaveNodeList(NodesX* nodesx,const char *filename)
796 {
797  index_t i;
798  int fd;
799  NodesFile nodesfile={0};
800  int super_number=0;
801
802  /* Print the start message */
803
804  printf_first("Writing Nodes: Nodes=0");
805
806  /* Map into memory */
807
808 #if !SLIM
809  nodesx->xdata=MapFile(nodesx->filename);
810 #endif
811
812  /* Write out the nodes data */
813
814  fd=OpenFileNew(filename);
815
816  SeekFile(fd,sizeof(NodesFile));
817  WriteFile(fd,nodesx->offsets,(nodesx->latbins*nodesx->lonbins+1)*sizeof(index_t));
818
819  for(i=0;i<nodesx->number;i++)
820    {
821     NodeX *nodex=LookupNodeX(nodesx,i,1);
822     Node *node=LookupNodeXNode(nodesx,nodex->id,1);
823
824     if(node->flags&NODE_SUPER)
825        super_number++;
826
827     WriteFile(fd,node,sizeof(Node));
828
829     if(!((i+1)%10000))
830        printf_middle("Writing Nodes: Nodes=%d",i+1);
831    }
832
833  /* Write out the header structure */
834
835  nodesfile.number=nodesx->number;
836  nodesfile.snumber=super_number;
837
838  nodesfile.latbins=nodesx->latbins;
839  nodesfile.lonbins=nodesx->lonbins;
840
841  nodesfile.latzero=nodesx->latzero;
842  nodesfile.lonzero=nodesx->lonzero;
843
844  SeekFile(fd,0);
845  WriteFile(fd,&nodesfile,sizeof(NodesFile));
846
847  CloseFile(fd);
848
849  /* Unmap from memory */
850
851 #if !SLIM
852  nodesx->xdata=UnmapFile(nodesx->filename);
853 #endif
854
855  /* Print the final message */
856
857  printf_last("Wrote Nodes: Nodes=%d",nodesx->number);
858 }