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