Routino SVN Repository Browser

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

ViewVC logotype

Annotation of /trunk/src/segmentsx.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1297 - (hide annotations) (download) (as text)
Tue May 7 14:41:11 2013 UTC (11 years, 10 months ago) by amb
File MIME type: text/x-csrc
File size: 31903 byte(s)
Add cache functions for NodesX, SegmentsX and WaysX to speed up the
planetsplitter in slim mode.

1 amb 110 /***************************************
2     Extended Segment data type functions.
3 amb 151
4     Part of the Routino routing software.
5 amb 110 ******************/ /******************
6 amb 1297 This file Copyright 2008-2013 Andrew M. Bishop
7 amb 110
8 amb 151 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 amb 110 ***************************************/
21    
22    
23     #include <math.h>
24     #include <stdlib.h>
25 amb 955 #include <string.h>
26 amb 110
27     #include "types.h"
28 amb 228 #include "segments.h"
29     #include "ways.h"
30 amb 110
31 amb 955 #include "typesx.h"
32 amb 449 #include "nodesx.h"
33     #include "segmentsx.h"
34     #include "waysx.h"
35 amb 110
36 amb 449 #include "files.h"
37 amb 519 #include "logging.h"
38 amb 532 #include "sorting.h"
39 amb 449
40    
41 amb 680 /* Global variables */
42 amb 110
43 amb 289 /*+ The command line '--tmpdir' option or its default value. +*/
44 amb 284 extern char *option_tmpdirname;
45 amb 110
46 amb 1100 /* Local variables */
47    
48 amb 1107 /*+ Temporary file-local variables for use by the sort functions. +*/
49 amb 1132 static NodesX *sortnodesx;
50 amb 1100 static SegmentsX *sortsegmentsx;
51 amb 1132 static WaysX *sortwaysx;
52 amb 1100
53 amb 680 /* Local functions */
54 amb 110
55 amb 1140 static int sort_by_way_id(SegmentX *a,SegmentX *b);
56     static int apply_changes(SegmentX *segmentx,index_t index);
57 amb 1160
58 amb 275 static int sort_by_id(SegmentX *a,SegmentX *b);
59 amb 1155 static int deduplicate(SegmentX *segmentx,index_t index);
60 amb 1160
61 amb 949 static int delete_pruned(SegmentX *segmentx,index_t index);
62 amb 1160
63 amb 1132 static int deduplicate_super(SegmentX *segmentx,index_t index);
64 amb 275
65 amb 1160 static int geographically_index(SegmentX *segmentx,index_t index);
66    
67 amb 1168 static distance_t DistanceX(NodeX *nodex1,NodeX *nodex2);
68 amb 110
69    
70     /*++++++++++++++++++++++++++++++++++++++
71 amb 326 Allocate a new segment list (create a new file or open an existing one).
72 amb 110
73     SegmentsX *NewSegmentList Returns the segment list.
74 amb 326
75 amb 1123 int append Set to 1 if the file is to be opened for appending.
76    
77 amb 1139 int readonly Set to 1 if the file is to be opened for reading.
78 amb 110 ++++++++++++++++++++++++++++++++++++++*/
79    
80 amb 1123 SegmentsX *NewSegmentList(int append,int readonly)
81 amb 110 {
82     SegmentsX *segmentsx;
83    
84 amb 213 segmentsx=(SegmentsX*)calloc(1,sizeof(SegmentsX));
85 amb 110
86 amb 1166 logassert(segmentsx,"Failed to allocate memory (try using slim mode?)"); /* Check calloc() worked */
87 amb 243
88 amb 1120 segmentsx->filename =(char*)malloc(strlen(option_tmpdirname)+32);
89     segmentsx->filename_tmp=(char*)malloc(strlen(option_tmpdirname)+32);
90 amb 216
91 amb 1120 sprintf(segmentsx->filename ,"%s/segmentsx.parsed.mem",option_tmpdirname);
92     sprintf(segmentsx->filename_tmp,"%s/segmentsx.%p.tmp" ,option_tmpdirname,(void*)segmentsx);
93 amb 256
94 amb 1123 if(append || readonly)
95     if(ExistsFile(segmentsx->filename))
96     {
97     off_t size;
98 amb 326
99 amb 1123 size=SizeFile(segmentsx->filename);
100 amb 326
101 amb 1123 segmentsx->number=size/sizeof(SegmentX);
102 amb 1139
103     RenameFile(segmentsx->filename,segmentsx->filename_tmp);
104 amb 1123 }
105 amb 326
106 amb 1123 if(append)
107 amb 1139 segmentsx->fd=OpenFileAppend(segmentsx->filename_tmp);
108 amb 1123 else if(!readonly)
109 amb 1139 segmentsx->fd=OpenFileNew(segmentsx->filename_tmp);
110 amb 326 else
111 amb 1123 segmentsx->fd=-1;
112 amb 326
113 amb 1297 #if SLIM
114     segmentsx->cache=NewSegmentXCache();
115     #endif
116    
117 amb 110 return(segmentsx);
118     }
119    
120    
121     /*++++++++++++++++++++++++++++++++++++++
122 amb 226 Free a segment list.
123 amb 110
124 amb 681 SegmentsX *segmentsx The set of segments to be freed.
125 amb 1151
126 amb 1167 int keep If set then the results file is to be kept.
127 amb 110 ++++++++++++++++++++++++++++++++++++++*/
128    
129 amb 1167 void FreeSegmentList(SegmentsX *segmentsx,int keep)
130 amb 110 {
131 amb 1167 if(keep)
132 amb 1151 RenameFile(segmentsx->filename_tmp,segmentsx->filename);
133     else
134     DeleteFile(segmentsx->filename_tmp);
135 amb 1120
136 amb 283 free(segmentsx->filename);
137 amb 1120 free(segmentsx->filename_tmp);
138 amb 256
139 amb 949 if(segmentsx->firstnode)
140     free(segmentsx->firstnode);
141    
142     if(segmentsx->next1)
143     free(segmentsx->next1);
144    
145 amb 643 if(segmentsx->usednode)
146     free(segmentsx->usednode);
147    
148 amb 1297 #if SLIM
149     DeleteSegmentXCache(segmentsx->cache);
150     #endif
151    
152 amb 110 free(segmentsx);
153     }
154    
155    
156     /*++++++++++++++++++++++++++++++++++++++
157 amb 493 Append a single segment to an unsorted segment list.
158 amb 110
159 amb 681 SegmentsX *segmentsx The set of segments to modify.
160 amb 110
161 amb 285 way_t way The way that the segment belongs to.
162    
163     node_t node1 The first node in the segment.
164    
165     node_t node2 The second node in the segment.
166    
167 amb 1168 distance_t distance The distance between the nodes (or just the flags).
168 amb 110 ++++++++++++++++++++++++++++++++++++++*/
169    
170 amb 1168 void AppendSegmentList(SegmentsX *segmentsx,way_t way,node_t node1,node_t node2,distance_t distance)
171 amb 110 {
172 amb 285 SegmentX segmentx;
173 amb 110
174 amb 643 if(node1>node2)
175     {
176     node_t temp;
177    
178     temp=node1;
179     node1=node2;
180     node2=temp;
181    
182 amb 1168 if(distance&(ONEWAY_2TO1|ONEWAY_1TO2))
183     distance^=ONEWAY_2TO1|ONEWAY_1TO2;
184 amb 643 }
185    
186 amb 285 segmentx.node1=node1;
187     segmentx.node2=node2;
188 amb 643 segmentx.next2=NO_SEGMENT;
189 amb 285 segmentx.way=way;
190 amb 1168 segmentx.distance=distance;
191 amb 110
192 amb 285 WriteFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
193 amb 281
194 amb 650 segmentsx->number++;
195 amb 466
196 amb 1166 logassert(segmentsx->number<SEGMENT_FAKE,"Too many segments (change index_t to 64-bits?)"); /* SEGMENT_FAKE marks the high-water mark for real segments. */
197 amb 285 }
198 amb 227
199 amb 281
200 amb 285 /*++++++++++++++++++++++++++++++++++++++
201 amb 1120 Finish appending segments and change the filename over.
202    
203     SegmentsX *segmentsx The segments that have been appended.
204     ++++++++++++++++++++++++++++++++++++++*/
205    
206 amb 1151 void FinishSegmentList(SegmentsX *segmentsx)
207 amb 1120 {
208 amb 1136 if(segmentsx->fd!=-1)
209     segmentsx->fd=CloseFile(segmentsx->fd);
210 amb 1120 }
211    
212    
213     /*++++++++++++++++++++++++++++++++++++++
214 amb 1160 Find the first extended segment with a particular starting node index.
215    
216     SegmentX *FirstSegmentX Returns a pointer to the first extended segment with the specified id.
217    
218     SegmentsX *segmentsx The set of segments to use.
219    
220     index_t nodeindex The node index to look for.
221    
222     int position A flag to pass through.
223     ++++++++++++++++++++++++++++++++++++++*/
224    
225     SegmentX *FirstSegmentX(SegmentsX *segmentsx,index_t nodeindex,int position)
226     {
227     index_t index=segmentsx->firstnode[nodeindex];
228     SegmentX *segmentx;
229    
230     if(index==NO_SEGMENT)
231     return(NULL);
232    
233     segmentx=LookupSegmentX(segmentsx,index,position);
234    
235     return(segmentx);
236     }
237    
238    
239     /*++++++++++++++++++++++++++++++++++++++
240     Find the next segment with a particular starting node index.
241    
242     SegmentX *NextSegmentX Returns a pointer to the next segment with the same id.
243    
244     SegmentsX *segmentsx The set of segments to use.
245    
246     SegmentX *segmentx The current segment.
247    
248     index_t nodeindex The node index.
249     ++++++++++++++++++++++++++++++++++++++*/
250    
251     SegmentX *NextSegmentX(SegmentsX *segmentsx,SegmentX *segmentx,index_t nodeindex)
252     {
253     #if SLIM
254     int position=1+(segmentx-&segmentsx->cached[0]);
255     #endif
256    
257     if(segmentx->node1==nodeindex)
258     {
259     if(segmentsx->next1)
260     {
261     index_t index=IndexSegmentX(segmentsx,segmentx);
262    
263     if(segmentsx->next1[index]==NO_SEGMENT)
264     return(NULL);
265    
266     segmentx=LookupSegmentX(segmentsx,segmentsx->next1[index],position);
267    
268     return(segmentx);
269     }
270     else
271     {
272     #if SLIM
273     index_t index=IndexSegmentX(segmentsx,segmentx);
274     index++;
275    
276     if(index>=segmentsx->number)
277     return(NULL);
278    
279     segmentx=LookupSegmentX(segmentsx,index,position);
280     #else
281     segmentx++;
282    
283     if(IndexSegmentX(segmentsx,segmentx)>=segmentsx->number)
284     return(NULL);
285     #endif
286    
287     if(segmentx->node1!=nodeindex)
288     return(NULL);
289    
290     return(segmentx);
291     }
292     }
293     else
294     {
295     if(segmentx->next2==NO_SEGMENT)
296     return(NULL);
297    
298     return(LookupSegmentX(segmentsx,segmentx->next2,position));
299     }
300     }
301    
302    
303     /*++++++++++++++++++++++++++++++++++++++
304 amb 1140 Apply the changes to the segments (no unique id to use).
305    
306     SegmentsX *segmentsx The set of segments to sort and modify.
307     ++++++++++++++++++++++++++++++++++++++*/
308    
309     void ApplySegmentChanges(SegmentsX *segmentsx)
310     {
311     int fd;
312     index_t xnumber;
313    
314     /* Print the start message */
315    
316     printf_first("Applying Segment Changes");
317    
318     /* Re-open the file read-only and a new file writeable */
319    
320     segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
321    
322     DeleteFile(segmentsx->filename_tmp);
323    
324     fd=OpenFileNew(segmentsx->filename_tmp);
325    
326     /* Sort by node indexes */
327    
328     xnumber=segmentsx->number;
329    
330     segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),NULL,
331     (int (*)(const void*,const void*))sort_by_way_id,
332     (int (*)(void*,index_t))apply_changes);
333    
334     /* Close the files */
335    
336     segmentsx->fd=CloseFile(segmentsx->fd);
337     CloseFile(fd);
338    
339     /* Print the final message */
340    
341     printf_last("Applying Segment Changes: Segments=%"Pindex_t" Changed=%"Pindex_t,xnumber,xnumber-segmentsx->number);
342     }
343    
344    
345     /*++++++++++++++++++++++++++++++++++++++
346 amb 1160 Sort the segments into way id order.
347 amb 232
348 amb 1160 int sort_by_way_id Returns the comparison of the way fields.
349    
350     SegmentX *a The first segment.
351    
352     SegmentX *b The second segment.
353 amb 1100 ++++++++++++++++++++++++++++++++++++++*/
354 amb 949
355 amb 1160 static int sort_by_way_id(SegmentX *a,SegmentX *b)
356 amb 1100 {
357 amb 1160 way_t a_id=a->way;
358     way_t b_id=b->way;
359 amb 1100
360 amb 1160 if(a_id<b_id)
361     return(-1);
362     else if(a_id>b_id)
363     return(1);
364     else /* if(a_id==b_id) */
365     return(-FILESORT_PRESERVE_ORDER(a,b)); /* latest version first */
366     }
367 amb 1100
368    
369 amb 1160 /*++++++++++++++++++++++++++++++++++++++
370     Apply the changes to the segments.
371 amb 1100
372 amb 1160 int apply_changes Return 1 if the value is to be kept, otherwise 0.
373 amb 1100
374 amb 1160 SegmentX *segmentx The extended segment.
375 amb 1100
376 amb 1160 index_t index The number of sorted segments that have already been written to the output file.
377     ++++++++++++++++++++++++++++++++++++++*/
378 amb 1100
379 amb 1160 static int apply_changes(SegmentX *segmentx,index_t index)
380     {
381     static way_t prevway=NO_WAY_ID;
382     static int deleted=0;
383 amb 1100
384 amb 1160 if(prevway!=segmentx->way)
385     {
386     prevway=segmentx->way;
387     deleted=0;
388     }
389 amb 1131
390 amb 1160 if(!deleted)
391     if(segmentx->node1==NO_NODE_ID)
392     deleted=1;
393 amb 1131
394 amb 1160 if(deleted)
395     return(0);
396     else
397     return(1);
398 amb 1131 }
399    
400    
401     /*++++++++++++++++++++++++++++++++++++++
402 amb 1160 Sort the segment list and deduplicate it.
403 amb 1100
404     SegmentsX *segmentsx The set of segments to sort and modify.
405 amb 285 ++++++++++++++++++++++++++++++++++++++*/
406 amb 110
407 amb 1160 void SortSegmentList(SegmentsX *segmentsx)
408 amb 285 {
409     int fd;
410 amb 1132 index_t xnumber;
411 amb 243
412 amb 285 /* Print the start message */
413 amb 232
414 amb 1160 printf_first("Sorting Segments");
415 amb 110
416 amb 555 /* Re-open the file read-only and a new file writeable */
417    
418 amb 1120 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
419 amb 110
420 amb 1120 DeleteFile(segmentsx->filename_tmp);
421 amb 110
422 amb 1120 fd=OpenFileNew(segmentsx->filename_tmp);
423 amb 110
424 amb 285 /* Sort by node indexes */
425 amb 132
426 amb 1132 xnumber=segmentsx->number;
427    
428 amb 1160 segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),NULL,
429 amb 1132 (int (*)(const void*,const void*))sort_by_id,
430 amb 1160 (int (*)(void*,index_t))deduplicate);
431 amb 1100
432 amb 555 /* Close the files */
433 amb 285
434 amb 612 segmentsx->fd=CloseFile(segmentsx->fd);
435 amb 281 CloseFile(fd);
436    
437     /* Print the final message */
438    
439 amb 1160 printf_last("Sorted Segments: Segments=%"Pindex_t" Duplicates=%"Pindex_t,xnumber,xnumber-segmentsx->number);
440 amb 110 }
441    
442    
443     /*++++++++++++++++++++++++++++++++++++++
444 amb 680 Sort the segments into id order, first by node1 then by node2, finally by distance.
445 amb 110
446 amb 285 int sort_by_id Returns the comparison of the node fields.
447 amb 110
448 amb 285 SegmentX *a The first segment.
449 amb 110
450 amb 285 SegmentX *b The second segment.
451 amb 256 ++++++++++++++++++++++++++++++++++++++*/
452    
453 amb 285 static int sort_by_id(SegmentX *a,SegmentX *b)
454 amb 256 {
455 amb 285 node_t a_id1=a->node1;
456     node_t b_id1=b->node1;
457 amb 256
458 amb 285 if(a_id1<b_id1)
459     return(-1);
460     else if(a_id1>b_id1)
461     return(1);
462     else /* if(a_id1==b_id1) */
463 amb 256 {
464 amb 285 node_t a_id2=a->node2;
465     node_t b_id2=b->node2;
466 amb 256
467 amb 285 if(a_id2<b_id2)
468     return(-1);
469     else if(a_id2>b_id2)
470     return(1);
471     else
472     {
473 amb 1168 distance_t a_distance=DISTANCE(a->distance);
474     distance_t b_distance=DISTANCE(b->distance);
475 amb 256
476 amb 1168 if(a_distance<b_distance)
477 amb 285 return(-1);
478 amb 1168 else if(a_distance>b_distance)
479 amb 285 return(1);
480     else
481 amb 1153 {
482 amb 1168 distance_t a_distflag=DISTFLAG(a->distance);
483     distance_t b_distflag=DISTFLAG(b->distance);
484 amb 1153
485 amb 1168 if(a_distflag<b_distflag)
486 amb 1153 return(-1);
487 amb 1168 else if(a_distflag>b_distflag)
488 amb 1153 return(1);
489     else
490     return(FILESORT_PRESERVE_ORDER(a,b)); /* preserve order */
491     }
492 amb 285 }
493 amb 256 }
494     }
495    
496    
497     /*++++++++++++++++++++++++++++++++++++++
498 amb 1155 Discard duplicate segments.
499 amb 1131
500 amb 1155 int deduplicate Return 1 if the value is to be kept, otherwise 0.
501 amb 1131
502     SegmentX *segmentx The extended segment.
503    
504     index_t index The number of sorted segments that have already been written to the output file.
505     ++++++++++++++++++++++++++++++++++++++*/
506    
507 amb 1155 static int deduplicate(SegmentX *segmentx,index_t index)
508 amb 1131 {
509     static node_t prevnode1=NO_NODE_ID,prevnode2=NO_NODE_ID;
510 amb 1155 static way_t prevway=NO_WAY_ID;
511 amb 1168 static distance_t prevdist=0;
512 amb 1131
513 amb 1168 if(prevnode1!=segmentx->node1 || prevnode2!=segmentx->node2 || prevway!=segmentx->way || prevdist!=segmentx->distance)
514 amb 1131 {
515     prevnode1=segmentx->node1;
516     prevnode2=segmentx->node2;
517 amb 1155 prevway=segmentx->way;
518 amb 1168 prevdist=segmentx->distance;
519 amb 1131
520     return(1);
521     }
522     else
523     return(0);
524     }
525    
526    
527     /*++++++++++++++++++++++++++++++++++++++
528 amb 680 Remove bad segments (duplicated, zero length or with missing nodes).
529 amb 110
530 amb 1136 SegmentsX *segmentsx The set of segments to modify.
531    
532 amb 681 NodesX *nodesx The set of nodes to use.
533 amb 195
534 amb 1140 WaysX *waysx The set of ways to use.
535    
536 amb 1167 int keep If set to 1 then keep the old data file otherwise delete it.
537 amb 110 ++++++++++++++++++++++++++++++++++++++*/
538    
539 amb 1167 void RemoveBadSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx,int keep)
540 amb 110 {
541 amb 1231 index_t noway=0,nonode=0,duplicate=0,good=0,total=0;
542 amb 1155 node_t prevnode1=NO_NODE_ID,prevnode2=NO_NODE_ID;
543 amb 1173 way_t prevway=NO_WAY_ID;
544 amb 1168 distance_t prevdist=0;
545 amb 275 SegmentX segmentx;
546     int fd;
547 amb 110
548 amb 275 /* Print the start message */
549    
550 amb 1231 printf_first("Checking Segments: Segments=0 No-Way=0 No-Node=0 Duplicate=0");
551 amb 227
552 amb 1100 /* Allocate the node usage bitmask */
553 amb 643
554 amb 950 segmentsx->usednode=AllocBitMask(nodesx->number);
555 amb 643
556 amb 1166 logassert(segmentsx->usednode,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
557 amb 643
558 amb 555 /* Re-open the file read-only and a new file writeable */
559 amb 275
560 amb 1120 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
561 amb 555
562 amb 1167 if(keep)
563 amb 1136 RenameFile(segmentsx->filename_tmp,segmentsx->filename);
564     else
565     DeleteFile(segmentsx->filename_tmp);
566 amb 275
567 amb 1120 fd=OpenFileNew(segmentsx->filename_tmp);
568 amb 275
569 amb 555 /* Modify the on-disk image */
570    
571 amb 275 while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
572 amb 110 {
573 amb 643 index_t index1=IndexNodeX(nodesx,segmentx.node1);
574     index_t index2=IndexNodeX(nodesx,segmentx.node2);
575 amb 1140 index_t indexw=IndexWayX(waysx,segmentx.way);
576 amb 643
577 amb 1140 if(indexw==NO_WAY)
578 amb 812 {
579 amb 1228 logerror("Segment belongs to way %"Pway_t" that does not exist in the Routino database (not a highway?).\n",segmentx.way);
580 amb 1140
581     noway++;
582     }
583 amb 643 else if(index1==NO_NODE || index2==NO_NODE)
584 amb 812 {
585     if(index1==NO_NODE && index2==NO_NODE)
586 amb 1228 logerror("Segment connects nodes %"Pnode_t" and %"Pnode_t" that do not exist in the Routino database (not highway nodes?).\n",segmentx.node1,segmentx.node2);
587 amb 812
588     if(index1==NO_NODE && index2!=NO_NODE)
589 amb 1228 logerror("Segment contains node %"Pnode_t" that does not exist in the Routino database (not a highway node?).\n",segmentx.node1);
590 amb 812
591     if(index1!=NO_NODE && index2==NO_NODE)
592 amb 1231 logerror("Segment contains node %"Pnode_t" that does not exist in the Routino database (not a highway node?).\n",segmentx.node2);
593 amb 812
594 amb 761 nonode++;
595 amb 812 }
596 amb 1155 else if(prevnode1==segmentx.node1 && prevnode2==segmentx.node2)
597     {
598 amb 1173 if(prevway==segmentx.way)
599     ; /* already logged an error - only possible to get here for oneway opposite direction segments */
600     else
601     {
602     if(!(prevdist&SEGMENT_AREA) && !(segmentx.distance&SEGMENT_AREA))
603     logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated.\n",segmentx.node1,segmentx.node2);
604 amb 1155
605 amb 1173 if(!(prevdist&SEGMENT_AREA) && (segmentx.distance&SEGMENT_AREA))
606     logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the area).\n",segmentx.node1,segmentx.node2);
607 amb 1155
608 amb 1173 if((prevdist&SEGMENT_AREA) && !(segmentx.distance&SEGMENT_AREA))
609     logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (discarded the non-area).\n",segmentx.node1,segmentx.node2);
610 amb 1155
611 amb 1173 if((prevdist&SEGMENT_AREA) && (segmentx.distance&SEGMENT_AREA))
612     logerror("Segment connecting nodes %"Pnode_t" and %"Pnode_t" is duplicated (both are areas).\n",segmentx.node1,segmentx.node2);
613     }
614 amb 1155
615     duplicate++;
616     }
617 amb 275 else
618 amb 257 {
619 amb 275 WriteFile(fd,&segmentx,sizeof(SegmentX));
620    
621 amb 655 SetBit(segmentsx->usednode,index1);
622     SetBit(segmentsx->usednode,index2);
623 amb 643
624 amb 1155 prevnode1=segmentx.node1;
625     prevnode2=segmentx.node2;
626 amb 1173 prevway=segmentx.way;
627 amb 1168 prevdist=DISTANCE(segmentx.distance);
628 amb 1155
629 amb 275 good++;
630 amb 110 }
631    
632 amb 275 total++;
633 amb 256
634 amb 275 if(!(total%10000))
635 amb 1231 printf_middle("Checking Segments: Segments=%"Pindex_t" No-Way=%"Pindex_t" No-Node=%"Pindex_t" Duplicate=%"Pindex_t,total,noway,nonode,duplicate);
636 amb 110 }
637    
638 amb 555 segmentsx->number=good;
639 amb 275
640 amb 555 /* Close the files */
641    
642 amb 612 segmentsx->fd=CloseFile(segmentsx->fd);
643 amb 275 CloseFile(fd);
644    
645     /* Print the final message */
646    
647 amb 1231 printf_last("Checked Segments: Segments=%"Pindex_t" No-Way=%"Pindex_t" No-Node=%"Pindex_t" Duplicate=%"Pindex_t,total,noway,nonode,duplicate);
648 amb 110 }
649    
650    
651     /*++++++++++++++++++++++++++++++++++++++
652 amb 285 Measure the segments and replace node/way ids with indexes.
653 amb 110
654 amb 681 SegmentsX *segmentsx The set of segments to process.
655 amb 110
656 amb 680 NodesX *nodesx The set of nodes to use.
657 amb 279
658 amb 680 WaysX *waysx The set of ways to use.
659 amb 110 ++++++++++++++++++++++++++++++++++++++*/
660    
661 amb 681 void MeasureSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
662 amb 110 {
663 amb 279 index_t index=0;
664 amb 643 int fd;
665 amb 256 SegmentX segmentx;
666 amb 110
667 amb 1208 if(segmentsx->number==0)
668     return;
669    
670 amb 275 /* Print the start message */
671    
672 amb 519 printf_first("Measuring Segments: Segments=0");
673 amb 227
674 amb 555 /* Map into memory / open the file */
675 amb 257
676 amb 452 #if !SLIM
677 amb 1120 nodesx->data=MapFile(nodesx->filename_tmp);
678 amb 555 #else
679 amb 1120 nodesx->fd=ReOpenFile(nodesx->filename_tmp);
680 amb 1297
681     InvalidateNodeXCache(nodesx->cache);
682 amb 452 #endif
683 amb 258
684 amb 1100 /* Allocate the way usage bitmask */
685    
686     segmentsx->usedway=AllocBitMask(waysx->number);
687    
688 amb 1166 logassert(segmentsx->usedway,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
689 amb 1100
690 amb 555 /* Re-open the file read-only and a new file writeable */
691 amb 275
692 amb 1120 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
693 amb 555
694 amb 1120 DeleteFile(segmentsx->filename_tmp);
695 amb 256
696 amb 1120 fd=OpenFileNew(segmentsx->filename_tmp);
697 amb 256
698 amb 555 /* Modify the on-disk image */
699    
700 amb 256 while(!ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX)))
701 amb 110 {
702 amb 279 index_t node1=IndexNodeX(nodesx,segmentx.node1);
703     index_t node2=IndexNodeX(nodesx,segmentx.node2);
704     index_t way =IndexWayX (waysx ,segmentx.way);
705 amb 110
706 amb 279 NodeX *nodex1=LookupNodeX(nodesx,node1,1);
707     NodeX *nodex2=LookupNodeX(nodesx,node2,2);
708    
709     /* Replace the node and way ids with their indexes */
710    
711     segmentx.node1=node1;
712     segmentx.node2=node2;
713     segmentx.way =way;
714    
715 amb 1100 SetBit(segmentsx->usedway,segmentx.way);
716    
717 amb 1167 /* Set the distance but keep the other flags except for area */
718 amb 275
719 amb 1168 segmentx.distance=DISTANCE(DistanceX(nodex1,nodex2))|DISTFLAG(segmentx.distance);
720 amb 1169 segmentx.distance&=~SEGMENT_AREA;
721 amb 275
722 amb 279 /* Write the modified segment */
723    
724 amb 275 WriteFile(fd,&segmentx,sizeof(SegmentX));
725    
726 amb 279 index++;
727 amb 275
728 amb 279 if(!(index%10000))
729 amb 790 printf_middle("Measuring Segments: Segments=%"Pindex_t,index);
730 amb 275 }
731 amb 110
732 amb 555 /* Close the files */
733 amb 257
734 amb 612 segmentsx->fd=CloseFile(segmentsx->fd);
735 amb 275 CloseFile(fd);
736    
737 amb 280 /* Free the other now-unneeded indexes */
738 amb 279
739     free(nodesx->idata);
740     nodesx->idata=NULL;
741    
742     free(waysx->idata);
743     waysx->idata=NULL;
744    
745 amb 555 /* Unmap from memory / close the file */
746 amb 275
747 amb 452 #if !SLIM
748 amb 1122 nodesx->data=UnmapFile(nodesx->data);
749 amb 555 #else
750 amb 612 nodesx->fd=CloseFile(nodesx->fd);
751 amb 452 #endif
752 amb 275
753     /* Print the final message */
754    
755 amb 790 printf_last("Measured Segments: Segments=%"Pindex_t,segmentsx->number);
756 amb 275 }
757    
758    
759     /*++++++++++++++++++++++++++++++++++++++
760 amb 1160 Index the segments by creating the firstnode index and filling in the segment next2 parameter.
761    
762     SegmentsX *segmentsx The set of segments to modify.
763    
764     NodesX *nodesx The set of nodes to use.
765    
766     WaysX *waysx The set of ways to use.
767     ++++++++++++++++++++++++++++++++++++++*/
768    
769     void IndexSegments(SegmentsX *segmentsx,NodesX *nodesx,WaysX *waysx)
770     {
771     index_t index,i;
772    
773     if(segmentsx->number==0)
774     return;
775    
776     /* Print the start message */
777    
778     printf_first("Indexing Segments: Segments=0");
779    
780     /* Allocate the array of indexes */
781    
782     if(segmentsx->firstnode)
783     free(segmentsx->firstnode);
784    
785     segmentsx->firstnode=(index_t*)malloc(nodesx->number*sizeof(index_t));
786    
787 amb 1166 logassert(segmentsx->firstnode,"Failed to allocate memory (try using slim mode?)"); /* Check malloc() worked */
788 amb 1160
789     for(i=0;i<nodesx->number;i++)
790     segmentsx->firstnode[i]=NO_SEGMENT;
791    
792     /* Map into memory / open the files */
793    
794     #if !SLIM
795     segmentsx->data=MapFileWriteable(segmentsx->filename_tmp);
796     #else
797     segmentsx->fd=ReOpenFileWriteable(segmentsx->filename_tmp);
798 amb 1297
799     InvalidateSegmentXCache(segmentsx->cache);
800 amb 1160 #endif
801    
802     /* Read through the segments in reverse order */
803    
804     for(index=segmentsx->number-1;index!=NO_SEGMENT;index--)
805     {
806     SegmentX *segmentx=LookupSegmentX(segmentsx,index,1);
807    
808     if(nodesx->pdata)
809     {
810     segmentx->node1=nodesx->pdata[segmentx->node1];
811     segmentx->node2=nodesx->pdata[segmentx->node2];
812     }
813    
814     if(waysx->cdata)
815     segmentx->way=waysx->cdata[segmentx->way];
816    
817     segmentx->next2=segmentsx->firstnode[segmentx->node2];
818    
819     PutBackSegmentX(segmentsx,segmentx);
820    
821     segmentsx->firstnode[segmentx->node1]=index;
822     segmentsx->firstnode[segmentx->node2]=index;
823    
824     if(!(index%10000))
825     printf_middle("Indexing Segments: Segments=%"Pindex_t,segmentsx->number-index);
826     }
827    
828     /* Unmap from memory / close the files */
829    
830     #if !SLIM
831     segmentsx->data=UnmapFile(segmentsx->data);
832     #else
833     segmentsx->fd=CloseFile(segmentsx->fd);
834     #endif
835    
836     /* Free the memory */
837    
838     if(nodesx->pdata)
839     {
840     free(nodesx->pdata);
841     nodesx->pdata=NULL;
842     }
843    
844     if(waysx->cdata)
845     {
846     free(waysx->cdata);
847     waysx->cdata=NULL;
848     }
849    
850     /* Print the final message */
851    
852     printf_last("Indexed Segments: Segments=%"Pindex_t,segmentsx->number);
853     }
854    
855    
856     /*++++++++++++++++++++++++++++++++++++++
857     Prune the deleted segments while resorting the list.
858    
859     SegmentsX *segmentsx The set of segments to sort and modify.
860    
861     WaysX *waysx The set of ways to check.
862     ++++++++++++++++++++++++++++++++++++++*/
863    
864     void RemovePrunedSegments(SegmentsX *segmentsx,WaysX *waysx)
865     {
866     int fd;
867     index_t xnumber;
868    
869 amb 1208 if(segmentsx->number==0)
870     return;
871    
872 amb 1160 /* Print the start message */
873    
874     printf_first("Sorting and Pruning Segments");
875    
876     /* Allocate the way usage bitmask */
877    
878     segmentsx->usedway=AllocBitMask(waysx->number);
879    
880 amb 1166 logassert(segmentsx->usedway,"Failed to allocate memory (try using slim mode?)"); /* Check AllocBitMask() worked */
881 amb 1160
882     /* Re-open the file read-only and a new file writeable */
883    
884     segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
885    
886     DeleteFile(segmentsx->filename_tmp);
887    
888     fd=OpenFileNew(segmentsx->filename_tmp);
889    
890     /* Sort by node indexes */
891    
892     xnumber=segmentsx->number;
893    
894     sortsegmentsx=segmentsx;
895    
896     segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),(int (*)(void*,index_t))delete_pruned,
897     (int (*)(const void*,const void*))sort_by_id,
898     NULL);
899    
900     /* Close the files */
901    
902     segmentsx->fd=CloseFile(segmentsx->fd);
903     CloseFile(fd);
904    
905     /* Print the final message */
906    
907     printf_last("Sorted and Pruned Segments: Segments=%"Pindex_t" Deleted=%"Pindex_t,xnumber,xnumber-segmentsx->number);
908     }
909    
910    
911     /*++++++++++++++++++++++++++++++++++++++
912     Delete the pruned segments.
913    
914     int delete_pruned Return 1 if the value is to be kept, otherwise 0.
915    
916     SegmentX *segmentx The extended segment.
917    
918     index_t index The number of unsorted segments that have been read from the input file.
919     ++++++++++++++++++++++++++++++++++++++*/
920    
921     static int delete_pruned(SegmentX *segmentx,index_t index)
922     {
923     if(IsPrunedSegmentX(segmentx))
924     return(0);
925    
926     SetBit(sortsegmentsx->usedway,segmentx->way);
927    
928     return(1);
929     }
930    
931    
932     /*++++++++++++++++++++++++++++++++++++++
933 amb 1132 Remove the duplicate super-segments.
934 amb 110
935 amb 1132 SegmentsX *segmentsx The set of super-segments to modify.
936 amb 110
937 amb 680 WaysX *waysx The set of ways to use.
938 amb 110 ++++++++++++++++++++++++++++++++++++++*/
939    
940 amb 1132 void DeduplicateSuperSegments(SegmentsX *segmentsx,WaysX *waysx)
941 amb 110 {
942 amb 1132 int fd;
943     index_t xnumber;
944 amb 110
945 amb 1208 if(waysx->number==0)
946     return;
947    
948 amb 275 /* Print the start message */
949    
950 amb 1132 printf_first("Sorting and Deduplicating Super-Segments");
951 amb 227
952 amb 555 /* Map into memory / open the file */
953 amb 256
954 amb 452 #if !SLIM
955 amb 1120 waysx->data=MapFile(waysx->filename_tmp);
956 amb 555 #else
957 amb 1120 waysx->fd=ReOpenFile(waysx->filename_tmp);
958 amb 1297
959     InvalidateWayXCache(waysx->cache);
960 amb 452 #endif
961 amb 275
962 amb 555 /* Re-open the file read-only and a new file writeable */
963 amb 275
964 amb 1120 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
965 amb 555
966 amb 1120 DeleteFile(segmentsx->filename_tmp);
967 amb 275
968 amb 1120 fd=OpenFileNew(segmentsx->filename_tmp);
969 amb 275
970 amb 1132 /* Sort by node indexes */
971 amb 555
972 amb 1132 xnumber=segmentsx->number;
973 amb 256
974 amb 1132 sortsegmentsx=segmentsx;
975     sortwaysx=waysx;
976 amb 110
977 amb 1132 segmentsx->number=filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),NULL,
978     (int (*)(const void*,const void*))sort_by_id,
979     (int (*)(void*,index_t))deduplicate_super);
980 amb 264
981 amb 555 /* Close the files */
982    
983 amb 612 segmentsx->fd=CloseFile(segmentsx->fd);
984 amb 275 CloseFile(fd);
985    
986 amb 555 /* Unmap from memory / close the file */
987 amb 275
988 amb 452 #if !SLIM
989 amb 1122 waysx->data=UnmapFile(waysx->data);
990 amb 555 #else
991 amb 612 waysx->fd=CloseFile(waysx->fd);
992 amb 452 #endif
993 amb 275
994     /* Print the final message */
995    
996 amb 1132 printf_last("Sorted and Deduplicated Super-Segments: Super-Segments=%"Pindex_t" Duplicate=%"Pindex_t,xnumber,xnumber-segmentsx->number);
997 amb 110 }
998    
999    
1000     /*++++++++++++++++++++++++++++++++++++++
1001 amb 1132 De-duplicate super-segments.
1002    
1003     int deduplicate_super Return 1 if the value is to be kept, otherwise 0.
1004    
1005     SegmentX *segmentx The extended super-segment.
1006    
1007     index_t index The number of sorted super-segments that have already been written to the output file.
1008     ++++++++++++++++++++++++++++++++++++++*/
1009    
1010     static int deduplicate_super(SegmentX *segmentx,index_t index)
1011     {
1012     static int nprev=0;
1013     static index_t prevnode1=NO_NODE,prevnode2=NO_NODE;
1014     static SegmentX prevsegx[MAX_SEG_PER_NODE];
1015     static Way prevway[MAX_SEG_PER_NODE];
1016    
1017     WayX *wayx=LookupWayX(sortwaysx,segmentx->way,1);
1018     int isduplicate=0;
1019    
1020     if(index==0 || segmentx->node1!=prevnode1 || segmentx->node2!=prevnode2)
1021     {
1022     nprev=1;
1023     prevnode1=segmentx->node1;
1024     prevnode2=segmentx->node2;
1025     prevsegx[0]=*segmentx;
1026     prevway[0] =wayx->way;
1027     }
1028     else
1029     {
1030     int offset;
1031    
1032     for(offset=0;offset<nprev;offset++)
1033     {
1034 amb 1168 if(DISTFLAG(segmentx->distance)==DISTFLAG(prevsegx[offset].distance))
1035 amb 1132 if(!WaysCompare(&prevway[offset],&wayx->way))
1036     {
1037     isduplicate=1;
1038     break;
1039     }
1040     }
1041    
1042     if(isduplicate)
1043     {
1044     nprev--;
1045    
1046     for(;offset<nprev;offset++)
1047     {
1048     prevsegx[offset]=prevsegx[offset+1];
1049     prevway[offset] =prevway[offset+1];
1050     }
1051     }
1052     else
1053     {
1054 amb 1166 logassert(nprev<MAX_SEG_PER_NODE,"Too many segments for one node (increase MAX_SEG_PER_NODE?)"); /* Only a limited amount of information stored. */
1055 amb 1132
1056     prevsegx[nprev]=*segmentx;
1057     prevway[nprev] =wayx->way;
1058    
1059     nprev++;
1060     }
1061     }
1062    
1063     return(!isduplicate);
1064     }
1065    
1066    
1067     /*++++++++++++++++++++++++++++++++++++++
1068 amb 1160 Sort the segments geographically after updating the node indexes.
1069 amb 209
1070 amb 681 SegmentsX *segmentsx The set of segments to modify.
1071 amb 209
1072 amb 1100 NodesX *nodesx The set of nodes to use.
1073 amb 209 ++++++++++++++++++++++++++++++++++++++*/
1074    
1075 amb 1160 void SortSegmentListGeographically(SegmentsX *segmentsx,NodesX *nodesx)
1076 amb 209 {
1077 amb 1160 int fd;
1078 amb 209
1079 amb 1208 if(segmentsx->number==0)
1080     return;
1081    
1082 amb 275 /* Print the start message */
1083    
1084 amb 1160 printf_first("Sorting Segments Geographically");
1085 amb 227
1086 amb 1160 /* Re-open the file read-only and a new file writeable */
1087 amb 643
1088 amb 1160 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
1089 amb 643
1090 amb 1160 DeleteFile(segmentsx->filename_tmp);
1091 amb 643
1092 amb 1160 fd=OpenFileNew(segmentsx->filename_tmp);
1093 amb 1102
1094 amb 1160 /* Update the segments with geographically sorted node indexes and sort them */
1095 amb 643
1096 amb 1160 sortnodesx=nodesx;
1097 amb 209
1098 amb 1160 filesort_fixed(segmentsx->fd,fd,sizeof(SegmentX),(int (*)(void*,index_t))geographically_index,
1099     (int (*)(const void*,const void*))sort_by_id,
1100     NULL);
1101     /* Close the files */
1102 amb 275
1103 amb 1160 segmentsx->fd=CloseFile(segmentsx->fd);
1104     CloseFile(fd);
1105 amb 280
1106 amb 1160 /* Print the final message */
1107 amb 209
1108 amb 1160 printf_last("Sorted Segments Geographically: Segments=%"Pindex_t,segmentsx->number);
1109     }
1110 amb 1098
1111 amb 1100
1112 amb 1160 /*++++++++++++++++++++++++++++++++++++++
1113     Update the segment indexes.
1114 amb 209
1115 amb 1160 int geographically_index Return 1 if the value is to be kept, otherwise 0.
1116 amb 448
1117 amb 1160 SegmentX *segmentx The extended segment.
1118 amb 558
1119 amb 1160 index_t index The number of unsorted segments that have been read from the input file.
1120     ++++++++++++++++++++++++++++++++++++++*/
1121 amb 209
1122 amb 1160 static int geographically_index(SegmentX *segmentx,index_t index)
1123     {
1124     segmentx->node1=sortnodesx->gdata[segmentx->node1];
1125     segmentx->node2=sortnodesx->gdata[segmentx->node2];
1126 amb 275
1127 amb 1160 if(segmentx->node1>segmentx->node2)
1128     {
1129     index_t temp;
1130 amb 275
1131 amb 1160 temp=segmentx->node1;
1132     segmentx->node1=segmentx->node2;
1133     segmentx->node2=temp;
1134 amb 1100
1135 amb 1168 if(segmentx->distance&(ONEWAY_2TO1|ONEWAY_1TO2))
1136     segmentx->distance^=ONEWAY_2TO1|ONEWAY_1TO2;
1137 amb 1100 }
1138    
1139 amb 1160 return(1);
1140 amb 209 }
1141    
1142    
1143     /*++++++++++++++++++++++++++++++++++++++
1144 amb 285 Save the segment list to a file.
1145    
1146 amb 681 SegmentsX *segmentsx The set of segments to save.
1147 amb 285
1148     const char *filename The name of the file to save.
1149     ++++++++++++++++++++++++++++++++++++++*/
1150    
1151 amb 681 void SaveSegmentList(SegmentsX *segmentsx,const char *filename)
1152 amb 285 {
1153     index_t i;
1154     int fd;
1155 amb 500 SegmentsFile segmentsfile={0};
1156 amb 780 index_t super_number=0,normal_number=0;
1157 amb 285
1158     /* Print the start message */
1159    
1160 amb 519 printf_first("Writing Segments: Segments=0");
1161 amb 285
1162 amb 558 /* Re-open the file */
1163    
1164 amb 1120 segmentsx->fd=ReOpenFile(segmentsx->filename_tmp);
1165 amb 558
1166 amb 461 /* Write out the segments data */
1167 amb 285
1168 amb 502 fd=OpenFileNew(filename);
1169 amb 461
1170     SeekFile(fd,sizeof(SegmentsFile));
1171    
1172 amb 285 for(i=0;i<segmentsx->number;i++)
1173     {
1174 amb 643 SegmentX segmentx;
1175 amb 944 Segment segment={0};
1176 amb 448
1177 amb 643 ReadFile(segmentsx->fd,&segmentx,sizeof(SegmentX));
1178 amb 558
1179 amb 1168 segment.node1 =segmentx.node1;
1180     segment.node2 =segmentx.node2;
1181     segment.next2 =segmentx.next2;
1182     segment.way =segmentx.way;
1183     segment.distance=segmentx.distance;
1184 amb 643
1185 amb 558 if(IsSuperSegment(&segment))
1186 amb 285 super_number++;
1187 amb 558 if(IsNormalSegment(&segment))
1188 amb 285 normal_number++;
1189    
1190 amb 558 WriteFile(fd,&segment,sizeof(Segment));
1191 amb 448
1192 amb 285 if(!((i+1)%10000))
1193 amb 790 printf_middle("Writing Segments: Segments=%"Pindex_t,i+1);
1194 amb 285 }
1195    
1196 amb 461 /* Write out the header structure */
1197    
1198     segmentsfile.number=segmentsx->number;
1199     segmentsfile.snumber=super_number;
1200     segmentsfile.nnumber=normal_number;
1201    
1202     SeekFile(fd,0);
1203     WriteFile(fd,&segmentsfile,sizeof(SegmentsFile));
1204    
1205 amb 285 CloseFile(fd);
1206    
1207 amb 558 /* Close the file */
1208    
1209 amb 643 segmentsx->fd=CloseFile(segmentsx->fd);
1210 amb 558
1211 amb 285 /* Print the final message */
1212    
1213 amb 790 printf_last("Wrote Segments: Segments=%"Pindex_t,segmentsx->number);
1214 amb 285 }
1215    
1216    
1217     /*++++++++++++++++++++++++++++++++++++++
1218 amb 110 Calculate the distance between two nodes.
1219    
1220 amb 1168 distance_t DistanceX Returns the distance between the extended nodes.
1221 amb 110
1222     NodeX *nodex1 The starting node.
1223    
1224     NodeX *nodex2 The end node.
1225     ++++++++++++++++++++++++++++++++++++++*/
1226    
1227 amb 1168 static distance_t DistanceX(NodeX *nodex1,NodeX *nodex2)
1228 amb 110 {
1229 amb 223 double dlon = latlong_to_radians(nodex1->longitude) - latlong_to_radians(nodex2->longitude);
1230     double dlat = latlong_to_radians(nodex1->latitude) - latlong_to_radians(nodex2->latitude);
1231     double lat1 = latlong_to_radians(nodex1->latitude);
1232     double lat2 = latlong_to_radians(nodex2->latitude);
1233 amb 110
1234 amb 219 double a1,a2,a,sa,c,d;
1235 amb 110
1236     if(dlon==0 && dlat==0)
1237     return 0;
1238    
1239 amb 219 a1 = sin (dlat / 2);
1240     a2 = sin (dlon / 2);
1241     a = (a1 * a1) + cos (lat1) * cos (lat2) * a2 * a2;
1242     sa = sqrt (a);
1243 amb 110 if (sa <= 1.0)
1244 amb 219 {c = 2 * asin (sa);}
1245 amb 110 else
1246 amb 219 {c = 2 * asin (1.0);}
1247 amb 110 d = 6378.137 * c;
1248    
1249     return km_to_distance(d);
1250     }

Properties

Name Value
cvs:description Extended segments functions.