[OSM-dev] Retrieve Nearest Way
Richard Atterer
richard at 2009.atterer.net
Thu Apr 15 19:33:16 BST 2010
Hi Ian,
On Wed, Apr 14, 2010 at 11:27:37AM -0500, Ian Dees wrote:
> I'm trying to write a mockup for simple editing tool and one of the steps
> needed is to have a fairly quick "find nearest way" call. Can anyone
> recommend some reading or pseudocode to find the nearest way given a
> lat/lon?
The following is from my "Leadme" editor/project,
<http://atterer.net/leadme>. GPL v2 or v3. All coordinates are assumed to
be in a cartesian coordinate system, so you need to do a Mercator transform
first, and arguably the code will not find the nearest way in all cases
because of that - but it should be OK unless you edit a pole. It *is*
rather accurate in that it actually calculates the distance in 3D (not 2D),
and it also takes into account the (semi-)spheres around the endpoints of
each way segment.
Cheers,
Richard
const MapWay* Map::findNearestWay(const Point *p, double maxDistance,
double* dist) const {
double d = maxDistance;
const MapWay* result = 0;
for (Ways::const_iterator i = ways.begin(), e = ways.end(); i != e; ++i) {
//cerr << "Map::findNearestWay quickcheck " << &*i << " d=" << d <<endl;
if (i->nodes.size() < 2)
continue; // Actually, such ways are forbidden
/* First create a bbox for the entire way, to quickly eliminate
* ways which are too far away. */
MapWay::Nodes::const_iterator j = i->nodes.begin(), je = i->nodes.end();
double wayminx = (*j)->x, waymaxx = wayminx;
double wayminy = (*j)->y, waymaxy = wayminy;
while (++j != je) {
wayminx = min(wayminx, (*j)->x);
waymaxx = max(waymaxx, (*j)->x);
wayminy = min(wayminy, (*j)->y);
waymaxy = max(waymaxy, (*j)->y);
}
if (p->x + d < wayminx || p->x - d > waymaxx
|| p->y + d < wayminy || p->y - d > waymaxy) continue;
//cerr << "Map::findNearestWay detailcheck " << &*i << " d=" << d << endl;
/* Bboxes overlap - do the slow, accurate check. Calculate the distance
* from each segment of the way. */
j = i->nodes.begin();
double dd = distance(*p, **j); /* Check half-sphere around first node */
if (dd < d) {
d = dd; result = &*i;
//cerr << " near 1st node d=" << d << endl;
}
while (true) {
const MapNode* o = *j; // old node
++j;
if (j == je) break;
const MapNode* c = *j; // current node
/* t is the normalized direction vector of the way segment */
double tx = c->x - o->x, ty = c->y - o->y, tz = c->z - o->z;
double segmentLen = sqrt(tx*tx + ty*ty + tz*tz);
tx /= segmentLen; ty /= segmentLen; tz /= segmentLen;
/* projLen = length of projection of p onto the segment o c */
double hesseDist = o->x * tx + o->y * ty + o->z * tz;
double projLen = p->x * tx + p->y * ty + p->z * tz - hesseDist;
//cerr << " proj=" << projLen << " segment=" << segmentLen << endl;
if (projLen < 0 || projLen > segmentLen + d/2) continue;
if (projLen > segmentLen) {
/* p may be inside half-sphere at endpoint c */
double dd = distance(*p, *c);
if (dd < d) {
d = dd; result = &*i;
//cerr << " near n-th node, dd=" << dd << endl;
}
} else { /* or inside the cylinder around the segment */
double distpo = distance(*p, *o);
double dd = sqrt(distpo*distpo - projLen*projLen);
if (dd < d) {
d = dd; result = &*i;
//cerr << " near segment, dd=" << dd << endl;
}
}
}
}
if (result != 0 && dist != 0)
*dist = d;
return result;
}
--
__ ,
| ) /| Richard Atterer | GnuPG key: 888354F7
| \/ | http://atterer.net | 08A9 7B7D 3D13 3EF2 3D25 D157 79E6 F6DC 8883 54F7
More information about the dev
mailing list