1 /***************************************
2 $Header: /home/amb/routino/src/RCS/nodes.c,v 1.44 2010/07/26 18:17:20 amb Exp $
4 Node data type functions.
6 Part of the Routino routing software.
7 ******************/ /******************
8 This file Copyright 2008-2010 Andrew M. Bishop
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.
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.
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 ***************************************/
25 #include <sys/types.h>
37 /*++++++++++++++++++++++++++++++++++++++
38 Load in a node list from a file.
40 Nodes* LoadNodeList Returns the node list.
42 const char *filename The name of the file to load.
43 ++++++++++++++++++++++++++++++++++++++*/
45 Nodes *LoadNodeList(const char *filename)
49 nodes=(Nodes*)malloc(sizeof(Nodes));
53 nodes->data=MapFile(filename);
55 /* Copy the NodesFile header structure from the loaded data */
57 nodes->file=*((NodesFile*)nodes->data);
59 /* Set the pointers in the Nodes structure. */
61 nodes->offsets=(index_t*)(nodes->data+sizeof(NodesFile));
62 nodes->nodes =(Node* )(nodes->data+sizeof(NodesFile)+(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t));
66 nodes->fd=ReOpenFile(filename);
68 /* Copy the NodesFile header structure from the loaded data */
70 ReadFile(nodes->fd,&nodes->file,sizeof(NodesFile));
72 nodes->nodesoffset=sizeof(NodesFile)+(nodes->file.latbins*nodes->file.lonbins+1)*sizeof(index_t);
74 nodes->incache[0]=NO_NODE;
75 nodes->incache[1]=NO_NODE;
76 nodes->incache[2]=NO_NODE;
84 /*++++++++++++++++++++++++++++++++++++++
85 Find the closest node given its latitude, longitude and optionally profile.
87 index_t FindClosestNode Returns the closest node.
89 Nodes* nodes The set of nodes to search.
91 Segments *segments The set of segments to use.
93 Ways *ways The set of ways to use.
95 double latitude The latitude to look for.
97 double longitude The longitude to look for.
99 distance_t distance The maximum distance to look.
101 Profile *profile The profile of the mode of transport (or NULL).
103 distance_t *bestdist Returns the distance to the best node.
104 ++++++++++++++++++++++++++++++++++++++*/
106 index_t FindClosestNode(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
107 distance_t distance,Profile *profile,distance_t *bestdist)
109 ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->file.latzero;
110 ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->file.lonzero;
112 index_t i,index1,index2;
113 index_t bestn=NO_NODE;
114 distance_t bestd=INF_DISTANCE;
116 /* Start with the bin containing the location, then spiral outwards. */
124 for(latb=latbin-delta;latb<=latbin+delta;latb++)
126 if(latb<0 || latb>=nodes->file.latbins)
129 for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++)
131 if(lonb<0 || lonb>=nodes->file.lonbins)
134 if(abs(latb-latbin)<delta && abs(lonb-lonbin)<delta)
137 llbin=lonb*nodes->file.latbins+latb;
139 /* Check if this grid square has any hope of being close enough */
143 double lat1=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb));
144 double lon1=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb));
145 double lat2=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb+1));
146 double lon2=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb+1));
150 distance_t dist1=Distance(latitude,lon1,latitude,longitude);
151 distance_t dist2=Distance(latitude,lon2,latitude,longitude);
153 if(dist1>distance && dist2>distance)
156 else if(lonb==lonbin)
158 distance_t dist1=Distance(lat1,longitude,latitude,longitude);
159 distance_t dist2=Distance(lat2,longitude,latitude,longitude);
161 if(dist1>distance && dist2>distance)
166 distance_t dist1=Distance(lat1,lon1,latitude,longitude);
167 distance_t dist2=Distance(lat2,lon1,latitude,longitude);
168 distance_t dist3=Distance(lat2,lon2,latitude,longitude);
169 distance_t dist4=Distance(lat1,lon2,latitude,longitude);
171 if(dist1>distance && dist2>distance && dist3>distance && dist4>distance)
176 /* Check every node in this grid square. */
178 index1=LookupNodeOffset(nodes,llbin);
179 index2=LookupNodeOffset(nodes,llbin+1);
181 for(i=index1;i<index2;i++)
183 Node *node=LookupNode(nodes,i,1);
184 double lat=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb)+off_to_latlong(node->latoffset));
185 double lon=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb)+off_to_latlong(node->lonoffset));
187 distance_t dist=Distance(lat,lon,latitude,longitude);
195 /* Decide if this is node is valid for the profile */
197 segment=FirstSegment(segments,nodes,i);
201 Way *way=LookupWay(ways,segment->way,1);
203 if(way->allow&profile->allow)
206 segment=NextSegment(segments,segment,i);
214 bestn=i; bestd=distance=dist;
232 /*++++++++++++++++++++++++++++++++++++++
233 Find the closest segment to a latitude, longitude and optionally profile.
235 index_t FindClosestSegment Returns the closest segment index.
237 Nodes* nodes The set of nodes to search.
239 Segments *segments The set of segments to use.
241 Ways *ways The set of ways to use.
243 double latitude The latitude to look for.
245 double longitude The longitude to look for.
247 distance_t distance The maximum distance to look.
249 Profile *profile The profile of the mode of transport (or NULL).
251 distance_t *bestdist Returns the distance to the best segment.
253 index_t *bestnode1 Returns the best node at one end.
255 index_t *bestnode2 Returns the best node at the other end.
257 distance_t *bestdist1 Returns the distance to the best node at one end.
259 distance_t *bestdist2 Returns the distance to the best node at the other end.
260 ++++++++++++++++++++++++++++++++++++++*/
262 index_t FindClosestSegment(Nodes* nodes,Segments *segments,Ways *ways,double latitude,double longitude,
263 distance_t distance,Profile *profile, distance_t *bestdist,
264 index_t *bestnode1,index_t *bestnode2,distance_t *bestdist1,distance_t *bestdist2)
266 ll_bin_t latbin=latlong_to_bin(radians_to_latlong(latitude ))-nodes->file.latzero;
267 ll_bin_t lonbin=latlong_to_bin(radians_to_latlong(longitude))-nodes->file.lonzero;
269 index_t i,index1,index2;
270 index_t bestn1=NO_NODE,bestn2=NO_NODE;
271 distance_t bestd=INF_DISTANCE,bestd1=INF_DISTANCE,bestd2=INF_DISTANCE;
272 index_t bests=NO_SEGMENT;
274 /* Start with the bin containing the location, then spiral outwards. */
282 for(latb=latbin-delta;latb<=latbin+delta;latb++)
284 if(latb<0 || latb>=nodes->file.latbins)
287 for(lonb=lonbin-delta;lonb<=lonbin+delta;lonb++)
289 if(lonb<0 || lonb>=nodes->file.lonbins)
292 if(abs(latb-latbin)<delta && abs(lonb-lonbin)<delta)
295 llbin=lonb*nodes->file.latbins+latb;
297 /* Check if this grid square has any hope of being close enough */
301 double lat1=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb));
302 double lon1=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb));
303 double lat2=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb+1));
304 double lon2=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb+1));
308 distance_t dist1=Distance(latitude,lon1,latitude,longitude);
309 distance_t dist2=Distance(latitude,lon2,latitude,longitude);
311 if(dist1>distance && dist2>distance)
314 else if(lonb==lonbin)
316 distance_t dist1=Distance(lat1,longitude,latitude,longitude);
317 distance_t dist2=Distance(lat2,longitude,latitude,longitude);
319 if(dist1>distance && dist2>distance)
324 distance_t dist1=Distance(lat1,lon1,latitude,longitude);
325 distance_t dist2=Distance(lat2,lon1,latitude,longitude);
326 distance_t dist3=Distance(lat2,lon2,latitude,longitude);
327 distance_t dist4=Distance(lat1,lon2,latitude,longitude);
329 if(dist1>distance && dist2>distance && dist3>distance && dist4>distance)
334 /* Check every node in this grid square. */
336 index1=LookupNodeOffset(nodes,llbin);
337 index2=LookupNodeOffset(nodes,llbin+1);
339 for(i=index1;i<index2;i++)
341 Node *node=LookupNode(nodes,i,1);
342 double lat1=latlong_to_radians(bin_to_latlong(nodes->file.latzero+latb)+off_to_latlong(node->latoffset));
343 double lon1=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonb)+off_to_latlong(node->lonoffset));
346 dist1=Distance(lat1,lon1,latitude,longitude);
352 /* Check each segment for closeness and if valid for the profile */
354 segment=FirstSegment(segments,nodes,i);
358 if(IsNormalSegment(segment))
363 way=LookupWay(ways,segment->way,1);
365 if(!profile || way->allow&profile->allow)
367 distance_t dist2,dist3;
368 double lat2,lon2,dist3a,dist3b,distp;
370 GetLatLong(nodes,OtherNode(segment,i),&lat2,&lon2);
372 dist2=Distance(lat2,lon2,latitude,longitude);
374 dist3=Distance(lat1,lon1,lat2,lon2);
376 /* Use law of cosines (assume flat Earth) */
378 dist3a=((double)dist1*(double)dist1-(double)dist2*(double)dist2+(double)dist3*(double)dist3)/(2.0*(double)dist3);
379 dist3b=(double)dist3-dist3a;
381 if(dist3a>=0 && dist3b>=0)
382 distp=sqrt((double)dist1*(double)dist1-dist3a*dist3a);
389 else /* if(dist3b>0) */
396 if(distp<(double)bestd)
398 bests=IndexSegment(segments,segment);
400 bestn2=OtherNode(segment,i);
401 bestd1=(distance_t)dist3a;
402 bestd2=(distance_t)dist3b;
403 bestd=(distance_t)distp;
408 segment=NextSegment(segments,segment,i);
413 } /* dist1 < distance */
434 /*++++++++++++++++++++++++++++++++++++++
435 Get the latitude and longitude associated with a node.
437 Nodes *nodes The set of nodes.
439 index_t index The node index.
441 double *latitude Returns the latitude.
443 double *longitude Returns the logitude.
444 ++++++++++++++++++++++++++++++++++++++*/
446 void GetLatLong(Nodes *nodes,index_t index,double *latitude,double *longitude)
448 Node *node=LookupNode(nodes,index,2);
449 int latbin=-1,lonbin=-1;
453 /* Binary search - search key closest below is required.
455 * # <- start | Check mid and move start or end if it doesn't match
457 * # | Since an inexact match is wanted we must set end=mid-1
458 * # <- mid | or start=mid because we know that mid doesn't match.
460 * # | Eventually either end=start or end=start+1 and one of
461 * # <- end | start or end is the wanted one.
464 /* Search for longitude */
467 end=nodes->file.lonbins-1;
471 mid=(start+end)/2; /* Choose mid point */
473 offset=LookupNodeOffset(nodes,nodes->file.latbins*mid);
475 if(offset<index) /* Mid point is too low */
477 else if(offset>index) /* Mid point is too high */
479 else /* Mid point is correct */
482 while((end-start)>1);
486 offset=LookupNodeOffset(nodes,nodes->file.latbins*end);
494 while(lonbin<nodes->file.lonbins &&
495 LookupNodeOffset(nodes,lonbin*nodes->file.latbins)==LookupNodeOffset(nodes,(lonbin+1)*nodes->file.latbins))
498 /* Search for latitude */
501 end=nodes->file.latbins-1;
505 mid=(start+end)/2; /* Choose mid point */
507 offset=LookupNodeOffset(nodes,lonbin*nodes->file.latbins+mid);
509 if(offset<index) /* Mid point is too low */
511 else if(offset>index) /* Mid point is too high */
513 else /* Mid point is correct */
516 while((end-start)>1);
520 offset=LookupNodeOffset(nodes,lonbin*nodes->file.latbins+end);
528 while(latbin<nodes->file.latbins &&
529 LookupNodeOffset(nodes,lonbin*nodes->file.latbins+latbin)==LookupNodeOffset(nodes,lonbin*nodes->file.latbins+latbin+1))
532 /* Return the values */
534 *latitude =latlong_to_radians(bin_to_latlong(nodes->file.latzero+latbin)+off_to_latlong(node->latoffset));
535 *longitude=latlong_to_radians(bin_to_latlong(nodes->file.lonzero+lonbin)+off_to_latlong(node->lonoffset));