Check out the latest version of Routino: svn co http://routino.org/svn/trunk routino
Contents of /branches/destination-access/src/nodes.c
Parent Directory
|
Revision Log
Revision 1807 -
(show annotations)
(download)
(as text)
Wed Sep 23 18:20:13 2015 UTC (9 years, 6 months ago) by amb
File MIME type: text/x-csrc
File size: 18983 byte(s)
Wed Sep 23 18:20:13 2015 UTC (9 years, 6 months ago) by amb
File MIME type: text/x-csrc
File size: 18983 byte(s)
Merge the trunk changes back into the destination-access branch.
1 | /*************************************** |
2 | Node data type functions. |
3 | |
4 | Part of the Routino routing software. |
5 | ******************/ /****************** |
6 | This file Copyright 2008-2015 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 <math.h> |
25 | |
26 | #include "types.h" |
27 | #include "nodes.h" |
28 | #include "segments.h" |
29 | #include "ways.h" |
30 | |
31 | #include "files.h" |
32 | #include "profiles.h" |
33 | |
34 | |
35 | /* Local functions */ |
36 | |
37 | static int valid_segment_for_profile(Ways *ways,Segment *segmentp,Profile *profile,int allow_destination); |
38 | |
39 | |
40 | /*++++++++++++++++++++++++++++++++++++++ |
41 | Load in a node list from a file. |
42 | |
43 | Nodes *LoadNodeList Returns the node list. |
44 | |
45 | const char *filename The name of the file to load. |
46 | ++++++++++++++++++++++++++++++++++++++*/ |
47 | |
48 | Nodes *LoadNodeList(const char *filename) |
49 | { |
50 | Nodes *nodes; |
51 | #if SLIM |
52 | size_t sizeoffsets; |
53 | #endif |
54 | |
55 | nodes=(Nodes*)malloc(sizeof(Nodes)); |
56 | |
57 | #if !SLIM |
58 | |
59 | nodes->data=MapFile(filename); |
60 | |
61 | /* Copy the NodesFile header structure from the loaded data */ |
62 | |
63 | nodes->file=*((NodesFile*)nodes->data); |
64 | |
65 | /* Set the pointers in the Nodes structure. */ |
66 | |
67 | nodes->offsets=(index_t*)(nodes->data+sizeof(NodesFile)); |
68 | nodes->nodes =(Node* )(nodes->data+sizeof(NodesFile)+(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t)); |
69 | |
70 | #else |
71 | |
72 | nodes->fd=SlimMapFile(filename); |
73 | |
74 | /* Copy the NodesFile header structure from the loaded data */ |
75 | |
76 | SlimFetch(nodes->fd,&nodes->file,sizeof(NodesFile),0); |
77 | |
78 | sizeoffsets=(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t); |
79 | |
80 | nodes->offsets=(index_t*)malloc(sizeoffsets); |
81 | #ifndef LIBROUTINO |
82 | log_malloc(nodes->offsets,sizeoffsets); |
83 | #endif |
84 | |
85 | SlimFetch(nodes->fd,nodes->offsets,sizeoffsets,sizeof(NodesFile)); |
86 | |
87 | nodes->nodesoffset=(offset_t)(sizeof(NodesFile)+sizeoffsets); |
88 | |
89 | nodes->cache=NewNodeCache(); |
90 | #ifndef LIBROUTINO |
91 | log_malloc(nodes->cache,sizeof(*nodes->cache)); |
92 | #endif |
93 | |
94 | #endif |
95 | |
96 | return(nodes); |
97 | } |
98 | |
99 | |
100 | /*++++++++++++++++++++++++++++++++++++++ |
101 | Destroy the node list. |
102 | |
103 | Nodes *nodes The node list to destroy. |
104 | ++++++++++++++++++++++++++++++++++++++*/ |
105 | |
106 | void DestroyNodeList(Nodes *nodes) |
107 | { |
108 | #if !SLIM |
109 | |
110 | nodes->data=UnmapFile(nodes->data); |
111 | |
112 | #else |
113 | |
114 | nodes->fd=SlimUnmapFile(nodes->fd); |
115 | |
116 | #ifndef LIBROUTINO |
117 | log_free(nodes->offsets); |
118 | #endif |
119 | free(nodes->offsets); |
120 | |
121 | #ifndef LIBROUTINO |
122 | log_free(nodes->cache); |
123 | #endif |
124 | DeleteNodeCache(nodes->cache); |
125 | |
126 | #endif |
127 | |
128 | free(nodes); |
129 | } |
130 | |
131 | |
132 | /*++++++++++++++++++++++++++++++++++++++ |
133 | Find the closest node given its latitude, longitude and the profile of the |
134 | mode of transport that must be able to move to/from this node. |
135 | |
136 | index_t FindClosestNode Returns the closest node. |
137 | |
138 | Nodes *nodes The set of nodes to search. |
139 | |
140 | Segments *segments The set of segments to use. |
141 | |
142 | Ways *ways The set of ways to use. |
143 | |
144 | double latitude The latitude to look for. |
145 | |
146 | double longitude The longitude to look for. |
147 | |
148 | distance_t distance The maximum distance to look from the specified coordinates. |
149 | |
150 | Profile *profile The profile of the mode of transport. |
151 | |
152 | distance_t *bestdist Returns the distance to the best node. |
153 | |
154 | int allow_destination The option to allow routing to follow highways tagged as 'destination'. |
155 | ++++++++++++++++++++++++++++++++++++++*/ |
156 | |
157 | index_t FindClosestNode(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude, |
158 | distance_t distance,Profile *profile,distance_t *bestdist,int allow_destination) |
159 | { |
160 | ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->file.latzero; |
161 | ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->file.lonzero; |
162 | int delta=0,count; |
163 | index_t i,index1,index2; |
164 | index_t bestn=NO_NODE; |
165 | distance_t bestd=INF_DISTANCE; |
166 | |
167 | /* Find the maximum distance to search */ |
168 | |
169 | double dlat=DeltaLat(longitude,distance); |
170 | double dlon=DeltaLon(latitude ,distance); |
171 | |
172 | double minlat=latitude -dlat; |
173 | double maxlat=latitude +dlat; |
174 | double minlon=longitude-dlon; |
175 | double maxlon=longitude+dlon; |
176 | |
177 | ll_bin_t minlatbin=latlong_to_bin(radians_to_latlong(minlat))-nodes->file.latzero; |
178 | ll_bin_t maxlatbin=latlong_to_bin(radians_to_latlong(maxlat))-nodes->file.latzero; |
179 | ll_bin_t minlonbin=latlong_to_bin(radians_to_latlong(minlon))-nodes->file.lonzero; |
180 | ll_bin_t maxlonbin=latlong_to_bin(radians_to_latlong(maxlon))-nodes->file.lonzero; |
181 | |
182 | ll_off_t minlatoff=latlong_to_off(radians_to_latlong(minlat)); |
183 | ll_off_t maxlatoff=latlong_to_off(radians_to_latlong(maxlat)); |
184 | ll_off_t minlonoff=latlong_to_off(radians_to_latlong(minlon)); |
185 | ll_off_t maxlonoff=latlong_to_off(radians_to_latlong(maxlon)); |
186 | |
187 | /* Start with the bin containing the location, then spiral outwards. */ |
188 | |
189 | do |
190 | { |
191 | ll_bin_t latb,lonb; |
192 | ll_bin2_t llbin; |
193 | |
194 | count=0; |
195 | |
196 | for(latb=latbin-delta;latb<=latbin+delta;latb++) |
197 | { |
198 | if(latb<0 || latb>=nodes->file.latbins || latb<minlatbin || latb>maxlatbin) |
199 | continue; |
200 | |
201 | for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++) |
202 | { |
203 | if(lonb<0 || lonb>=nodes->file.lonbins || lonb<minlonbin || lonb>maxlonbin) |
204 | continue; |
205 | |
206 | if(abs(latb-latbin)<delta && abs(lonb-lonbin)<delta) |
207 | continue; |
208 | |
209 | /* Check every node in this grid square. */ |
210 | |
211 | llbin=lonb*nodes->file.latbins+latb; |
212 | |
213 | index1=LookupNodeOffset(nodes,llbin); |
214 | index2=LookupNodeOffset(nodes,llbin+1); |
215 | |
216 | for(i=index1;i<index2;i++) |
217 | { |
218 | Node *nodep=LookupNode(nodes,i,3); |
219 | double lat,lon; |
220 | distance_t dist; |
221 | |
222 | if(latb==minlatbin && nodep->latoffset<minlatoff) |
223 | continue; |
224 | |
225 | if(latb==maxlatbin && nodep->latoffset>maxlatoff) |
226 | continue; |
227 | |
228 | if(lonb==minlonbin && nodep->lonoffset<minlonoff) |
229 | continue; |
230 | |
231 | if(lonb==maxlonbin && nodep->lonoffset>maxlonoff) |
232 | continue; |
233 | |
234 | lat=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb)+off_to_latlong(nodep->latoffset)); |
235 | lon=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb)+off_to_latlong(nodep->lonoffset)); |
236 | |
237 | dist=Distance(lat,lon,latitude,longitude); |
238 | |
239 | if(dist<bestd) |
240 | { |
241 | Segment *segmentp; |
242 | |
243 | /* Check that at least one segment is valid for the profile */ |
244 | |
245 | segmentp=FirstSegment(segments,nodep,1); |
246 | |
247 | do |
248 | { |
249 | if(IsNormalSegment(segmentp) && valid_segment_for_profile(ways,segmentp,profile,allow_destination)) |
250 | { |
251 | bestn=i; |
252 | bestd=dist; |
253 | |
254 | break; |
255 | } |
256 | |
257 | segmentp=NextSegment(segments,segmentp,i); |
258 | } |
259 | while(segmentp); |
260 | } |
261 | } |
262 | |
263 | count++; |
264 | } |
265 | } |
266 | |
267 | delta++; |
268 | } |
269 | while(count); |
270 | |
271 | *bestdist=bestd; |
272 | |
273 | return(bestn); |
274 | } |
275 | |
276 | |
277 | /*++++++++++++++++++++++++++++++++++++++ |
278 | Find the closest point on the closest segment given its latitude, longitude |
279 | and the profile of the mode of transport that must be able to move along this |
280 | segment. |
281 | |
282 | index_t FindClosestSegment Returns the closest segment index. |
283 | |
284 | Nodes *nodes The set of nodes to use. |
285 | |
286 | Segments *segments The set of segments to search. |
287 | |
288 | Ways *ways The set of ways to use. |
289 | |
290 | double latitude The latitude to look for. |
291 | |
292 | double longitude The longitude to look for. |
293 | |
294 | distance_t distance The maximum distance to look from the specified coordinates. |
295 | |
296 | Profile *profile The profile of the mode of transport. |
297 | |
298 | distance_t *bestdist Returns the distance to the closest point on the best segment. |
299 | |
300 | index_t *bestnode1 Returns the index of the node at one end of the closest segment. |
301 | |
302 | index_t *bestnode2 Returns the index of the node at the other end of the closest segment. |
303 | |
304 | distance_t *bestdist1 Returns the distance along the segment to the node at one end. |
305 | |
306 | distance_t *bestdist2 Returns the distance along the segment to the node at the other end. |
307 | |
308 | int allow_destination The option to allow routing to follow highways tagged as 'destination'. |
309 | ++++++++++++++++++++++++++++++++++++++*/ |
310 | |
311 | index_t FindClosestSegment(Nodes *nodes,Segments *segments,Ways *ways,double latitude,double longitude, |
312 | distance_t distance,Profile *profile, distance_t *bestdist, |
313 | index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2, |
314 | int allow_destination) |
315 | { |
316 | ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->file.latzero; |
317 | ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->file.lonzero; |
318 | int delta=0,count; |
319 | index_t i,index1,index2; |
320 | index_t bestn1=NO_NODE,bestn2=NO_NODE; |
321 | distance_t bestd=INF_DISTANCE,bestd1=INF_DISTANCE,bestd2=INF_DISTANCE; |
322 | index_t bests=NO_SEGMENT; |
323 | |
324 | /* Find the maximum distance to search */ |
325 | |
326 | double dlat=DeltaLat(longitude,distance); |
327 | double dlon=DeltaLon(latitude ,distance); |
328 | |
329 | double minlat=latitude -dlat; |
330 | double maxlat=latitude +dlat; |
331 | double minlon=longitude-dlon; |
332 | double maxlon=longitude+dlon; |
333 | |
334 | ll_bin_t minlatbin=latlong_to_bin(radians_to_latlong(minlat))-nodes->file.latzero; |
335 | ll_bin_t maxlatbin=latlong_to_bin(radians_to_latlong(maxlat))-nodes->file.latzero; |
336 | ll_bin_t minlonbin=latlong_to_bin(radians_to_latlong(minlon))-nodes->file.lonzero; |
337 | ll_bin_t maxlonbin=latlong_to_bin(radians_to_latlong(maxlon))-nodes->file.lonzero; |
338 | |
339 | ll_off_t minlatoff=latlong_to_off(radians_to_latlong(minlat)); |
340 | ll_off_t maxlatoff=latlong_to_off(radians_to_latlong(maxlat)); |
341 | ll_off_t minlonoff=latlong_to_off(radians_to_latlong(minlon)); |
342 | ll_off_t maxlonoff=latlong_to_off(radians_to_latlong(maxlon)); |
343 | |
344 | /* Start with the bin containing the location, then spiral outwards. */ |
345 | |
346 | do |
347 | { |
348 | ll_bin_t latb,lonb; |
349 | ll_bin2_t llbin; |
350 | |
351 | count=0; |
352 | |
353 | for(latb=latbin-delta;latb<=latbin+delta;latb++) |
354 | { |
355 | if(latb<0 || latb>=nodes->file.latbins || latb<minlatbin || latb>maxlatbin) |
356 | continue; |
357 | |
358 | for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++) |
359 | { |
360 | if(lonb<0 || lonb>=nodes->file.lonbins || lonb<minlonbin || lonb>maxlonbin) |
361 | continue; |
362 | |
363 | if(abs(latb-latbin)<delta && abs(lonb-lonbin)<delta) |
364 | continue; |
365 | |
366 | /* Check every node in this grid square. */ |
367 | |
368 | llbin=lonb*nodes->file.latbins+latb; |
369 | |
370 | index1=LookupNodeOffset(nodes,llbin); |
371 | index2=LookupNodeOffset(nodes,llbin+1); |
372 | |
373 | for(i=index1;i<index2;i++) |
374 | { |
375 | Node *nodep=LookupNode(nodes,i,3); |
376 | double lat1,lon1; |
377 | distance_t dist1; |
378 | |
379 | if(latb==minlatbin && nodep->latoffset<minlatoff) |
380 | continue; |
381 | |
382 | if(latb==maxlatbin && nodep->latoffset>maxlatoff) |
383 | continue; |
384 | |
385 | if(lonb==minlonbin && nodep->lonoffset<minlonoff) |
386 | continue; |
387 | |
388 | if(lonb==maxlonbin && nodep->lonoffset>maxlonoff) |
389 | continue; |
390 | |
391 | lat1=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb)+off_to_latlong(nodep->latoffset)); |
392 | lon1=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb)+off_to_latlong(nodep->lonoffset)); |
393 | |
394 | dist1=Distance(lat1,lon1,latitude,longitude); |
395 | |
396 | if(dist1<distance) |
397 | { |
398 | Segment *segmentp; |
399 | |
400 | /* Check each segment for closeness and if valid for the profile */ |
401 | |
402 | segmentp=FirstSegment(segments,nodep,1); |
403 | |
404 | do |
405 | { |
406 | if(IsNormalSegment(segmentp) && valid_segment_for_profile(ways,segmentp,profile,allow_destination)) |
407 | { |
408 | distance_t dist2,dist3; |
409 | double lat2,lon2,dist3a,dist3b,distp; |
410 | |
411 | GetLatLong(nodes,OtherNode(segmentp,i),NULL,&lat2,&lon2); |
412 | |
413 | dist2=Distance(lat2,lon2,latitude,longitude); |
414 | |
415 | dist3=Distance(lat1,lon1,lat2,lon2); |
416 | |
417 | /* Use law of cosines (assume flat Earth) */ |
418 | |
419 | if(dist3==0) |
420 | { |
421 | distp=dist1; /* == dist2 */ |
422 | dist3a=dist1; /* == dist2 */ |
423 | dist3b=dist2; /* == dist1 */ |
424 | } |
425 | else if((dist1+dist2)<dist3) |
426 | { |
427 | distp=0; |
428 | dist3a=dist1; |
429 | dist3b=dist2; |
430 | } |
431 | else |
432 | { |
433 | dist3a=((double)dist1*(double)dist1-(double)dist2*(double)dist2+(double)dist3*(double)dist3)/(2.0*(double)dist3); |
434 | dist3b=(double)dist3-dist3a; |
435 | |
436 | if(dist3a>=0 && dist3b>=0) |
437 | distp=sqrt((double)dist1*(double)dist1-dist3a*dist3a); |
438 | else if(dist3a>0) |
439 | { |
440 | distp=dist2; |
441 | dist3a=dist3; |
442 | dist3b=0; |
443 | } |
444 | else /* if(dist3b>0) */ |
445 | { |
446 | distp=dist1; |
447 | dist3a=0; |
448 | dist3b=dist3; |
449 | } |
450 | } |
451 | |
452 | if(distp<(double)bestd) |
453 | { |
454 | bests=IndexSegment(segments,segmentp); |
455 | |
456 | if(segmentp->node1==i) |
457 | { |
458 | bestn1=i; |
459 | bestn2=OtherNode(segmentp,i); |
460 | bestd1=(distance_t)dist3a; |
461 | bestd2=(distance_t)dist3b; |
462 | } |
463 | else |
464 | { |
465 | bestn1=OtherNode(segmentp,i); |
466 | bestn2=i; |
467 | bestd1=(distance_t)dist3b; |
468 | bestd2=(distance_t)dist3a; |
469 | } |
470 | |
471 | bestd=(distance_t)distp; |
472 | } |
473 | } |
474 | |
475 | segmentp=NextSegment(segments,segmentp,i); |
476 | } |
477 | while(segmentp); |
478 | |
479 | } /* dist1 < distance */ |
480 | } |
481 | |
482 | count++; |
483 | } |
484 | } |
485 | |
486 | delta++; |
487 | } |
488 | while(count); |
489 | |
490 | *bestdist=bestd; |
491 | |
492 | *bestnode1=bestn1; |
493 | *bestnode2=bestn2; |
494 | *bestdist1=bestd1; |
495 | *bestdist2=bestd2; |
496 | |
497 | return(bests); |
498 | } |
499 | |
500 | |
501 | /*++++++++++++++++++++++++++++++++++++++ |
502 | Check if the transport defined by the profile is allowed on the segment. |
503 | |
504 | int valid_segment_for_profile Return 1 if it is or 0 if not. |
505 | |
506 | Ways *ways The set of ways to use. |
507 | |
508 | Segment *segmentp The segment to check. |
509 | |
510 | Profile *profile The profile to check. |
511 | |
512 | int allow_destination The option to allow routing to follow highways tagged as 'destination'. |
513 | ++++++++++++++++++++++++++++++++++++++*/ |
514 | |
515 | static int valid_segment_for_profile(Ways *ways,Segment *segmentp,Profile *profile,int allow_destination) |
516 | { |
517 | Way *wayp=LookupWay(ways,segmentp->way,1); |
518 | score_t segment_pref; |
519 | int i; |
520 | |
521 | /* mode of transport must be allowed on the highway (optionally as destination only) */ |
522 | if(!(wayp->allow&profile->allow) && !(allow_destination && wayp->destination&profile->allow)) |
523 | return(0); |
524 | |
525 | /* must obey weight restriction (if exists) */ |
526 | if(wayp->weight && wayp->weight<profile->weight) |
527 | return(0); |
528 | |
529 | /* must obey height/width/length restriction (if exists) */ |
530 | if((wayp->height && wayp->height<profile->height) || |
531 | (wayp->width && wayp->width <profile->width ) || |
532 | (wayp->length && wayp->length<profile->length)) |
533 | return(0); |
534 | |
535 | segment_pref=profile->highway[HIGHWAY(wayp->type)]; |
536 | |
537 | for(i=1;i<Property_Count;i++) |
538 | if(ways->file.props & PROPERTIES(i)) |
539 | { |
540 | if(wayp->props & PROPERTIES(i)) |
541 | segment_pref*=profile->props_yes[i]; |
542 | else |
543 | segment_pref*=profile->props_no[i]; |
544 | } |
545 | |
546 | /* profile preferences must allow this highway */ |
547 | if(segment_pref==0) |
548 | return(0); |
549 | |
550 | /* Must be OK */ |
551 | return(1); |
552 | } |
553 | |
554 | |
555 | /*++++++++++++++++++++++++++++++++++++++ |
556 | Get the latitude and longitude associated with a node. |
557 | |
558 | Nodes *nodes The set of nodes to use. |
559 | |
560 | index_t index The node index. |
561 | |
562 | Node *nodep A pointer to the node if already available. |
563 | |
564 | double *latitude Returns the latitude. |
565 | |
566 | double *longitude Returns the logitude. |
567 | ++++++++++++++++++++++++++++++++++++++*/ |
568 | |
569 | void GetLatLong(Nodes *nodes,index_t index,Node *nodep,double *latitude,double *longitude) |
570 | { |
571 | ll_bin_t latbin,lonbin; |
572 | ll_bin2_t bin=-1; |
573 | ll_bin2_t start,end,mid; |
574 | index_t offset; |
575 | |
576 | /* Binary search - search key nearest match below is required. |
577 | * |
578 | * # <- start | Check mid and move start or end if it doesn't match |
579 | * # | |
580 | * # | A lower bound match is wanted we can set end=mid-1 or |
581 | * # <- mid | start=mid because we know that mid doesn't match. |
582 | * # | |
583 | * # | Eventually either end=start or end=start+1 and one of |
584 | * # <- end | start or end is the wanted one. |
585 | */ |
586 | |
587 | /* Search for offset */ |
588 | |
589 | start=0; |
590 | end=nodes->file.lonbins*nodes->file.latbins; |
591 | |
592 | do |
593 | { |
594 | mid=(start+end)/2; /* Choose mid point */ |
595 | |
596 | offset=LookupNodeOffset(nodes,mid); |
597 | |
598 | if(offset<index) /* Mid point is too low for an exact match but could be lower bound */ |
599 | start=mid; |
600 | else if(offset>index) /* Mid point is too high */ |
601 | end=mid?(mid-1):mid; |
602 | else /* Mid point is correct */ |
603 | {bin=mid;break;} |
604 | } |
605 | while((end-start)>1); |
606 | |
607 | if(bin==-1) |
608 | { |
609 | offset=LookupNodeOffset(nodes,end); |
610 | |
611 | if(offset>index) |
612 | bin=start; |
613 | else |
614 | bin=end; |
615 | } |
616 | |
617 | while(bin<=(nodes->file.lonbins*nodes->file.latbins) && |
618 | LookupNodeOffset(nodes,bin)==LookupNodeOffset(nodes,bin+1)) |
619 | bin++; |
620 | |
621 | latbin=bin%nodes->file.latbins; |
622 | lonbin=bin/nodes->file.latbins; |
623 | |
624 | /* Return the values */ |
625 | |
626 | if(nodep==NULL) |
627 | nodep=LookupNode(nodes,index,4); |
628 | |
629 | *latitude =latlong_to_radians(bin_to_latlong(nodes->file.latzero+latbin)+off_to_latlong(nodep->latoffset)); |
630 | *longitude=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonbin)+off_to_latlong(nodep->lonoffset)); |
631 | } |
Properties
Name | Value |
---|---|
cvs:description | Node data type. |