rewrite calculation of bounding circle using coslat-transformed lon-values.

This commit is contained in:
ntruchsess 2018-01-22 15:39:38 +01:00
parent c1048ab62a
commit 1fd973daf2
2 changed files with 322 additions and 241 deletions

View file

@ -1,19 +1,25 @@
/* /**********************************************************************************************
* Copyright 2018 Norbert Truchsess <norbert.truchsess@t-online.de> Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
*
* this code is based on work of Dan Sunday published at: This program is free software: you can redistribute it and/or modify
* http://geomalgorithms.com/a03-_inclusion.html it under the terms of the GNU General Public License as published by
* (implementation of winding number algorithm in c) the Free Software Foundation, either version 3 of the License, or
* http://geomalgorithms.com/a08-_containers.html (at your option) any later version.
* (fast computation of bounding circly in c)
* This program is distributed in the hope that it will be useful,
* Copyright 2001 softSurfer, 2012 Dan Sunday but WITHOUT ANY WARRANTY; without even the implied warranty of
* This code may be freely used and modified for any purpose providing that MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* this copyright notice is included with it. SoftSurfer makes no warranty for GNU General Public License for more details.
* this code, and cannot be held liable for any real or imagined damage
* resulting from its use. Users of this code must verify correctness for You should have received a copy of the GNU General Public License
* their application. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
The following methods are based on work of Dan Sunday published at:
http://geomalgorithms.com/a03-_inclusion.html
cn_PnPoly, wn_PnPoly, inSegment, intersect2D_2Segments
**********************************************************************************************/
package btools.router; package btools.router;
import java.util.ArrayList; import java.util.ArrayList;
@ -21,266 +27,297 @@ import java.util.List;
public class OsmNogoPolygon extends OsmNodeNamed public class OsmNogoPolygon extends OsmNodeNamed
{ {
public final class Point { private final static class Point
/** {
* The latitude final int y;
*/ final int x;
public final int y;
/** Point(final int lon, final int lat)
* The longitude
*/
public final int x;
public Point(final int lon, final int lat)
{ {
x = lon; x = lon;
y = lat; y = lat;
} }
} }
public List<Point> P = new ArrayList<Point>(); private final List<Point> points = new ArrayList<Point>();
public void addVertex(int lon, int lat) public final void addVertex(int lon, int lat)
{ {
P.add(new Point(lon, lat)); points.add(new Point(lon, lat));
} }
private final static double coslat(double lat)
{
final double l = (lat - 90000000) * 0.00000001234134; // 0.01234134 = Pi/(sqrt(2)*180)
final double l2 = l*l;
final double l4 = l2*l2;
// final double l6 = l4*l2;
return 1.- l2 + l4 / 6.; // - l6 / 90;
}
/**
* method calcBoundingCircle is inspired by the algorithm described on
* http://geomalgorithms.com/a08-_containers.html
* (fast computation of bounding circly in c). It is not as fast (the original
* algorithm runs in linear time), as it may do more iterations but it takes
* into account the coslat-factor being used for the linear approximation that
* is also used in other places of brouter does change when moving the centerpoint
* with each iteration.
* This is done to ensure the calculated radius being used
* in RoutingContext.calcDistance will actually contain the whole polygon.
*
* For reasonable distributed vertices the implemented algorithm runs in O(n*ln(n)).
* As this is only run once on initialization of OsmNogoPolygon this methods
* overall usage of cpu is neglegible in comparism to the cpu-usage of the
* actual routing algoritm.
*/
public void calcBoundingCircle() public void calcBoundingCircle()
{ {
double Cx, Cy; // Center of ball int cxmin, cxmax, cymin, cymax;
double rad, rad2; // radius and radius squared cxmin = cymin = Integer.MAX_VALUE;
double xmin, xmax, ymin, ymax; // bounding box extremes cxmax = cymax = Integer.MIN_VALUE;
int i_xmin, i_xmax, i_ymin, i_ymax; // index of P[] at box extreme
// find a large diameter to start with
// first get the bounding box and P[] extreme points for it
xmin = xmax = P.get(0).x;
ymin = ymax = P.get(0).y;
i_xmin = i_xmax = i_ymin = i_ymax = 0;
for (int i = 1; i < P.size(); i++)
{
Point Pi = P.get(i);
if (Pi.x < xmin)
{
xmin = Pi.x;
i_xmin = i;
}
else if (Pi.x > xmax)
{
xmax = Pi.x;
i_xmax = i;
}
if (Pi.y < ymin)
{
ymin = Pi.y;
i_ymin = i;
}
else if (Pi.y > ymax)
{
ymax = Pi.y;
i_ymax = i;
}
}
// select the largest extent as an initial diameter for the ball
Point Pi_xmax = P.get(i_xmax);
Point Pi_xmin = P.get(i_xmin);
Point Pi_ymax = P.get(i_ymax);
Point Pi_ymin = P.get(i_ymin);
int dPx_x = (Pi_xmax.x - Pi_xmin.x); // diff of Px max and min // first calculate a starting center point as center of boundingbox
int dPx_y = Pi_xmax.y - Pi_xmin.y; for (int i = 0; i < points.size(); i++)
int dPy_x = Pi_ymax.x - Pi_ymin.x; // diff of Py max and min
int dPy_y = Pi_ymax.y - Pi_ymin.y;
int dx2 = dPx_x * dPx_x + dPx_y * dPx_y; // Px diff squared
int dy2 = dPy_x * dPy_x + dPy_y * dPy_y; // Py diff squared
if (dx2 >= dy2) // x direction is largest extent
{ {
Cx = Pi_xmin.x + dPx_x / 2.0; // Center = midpoint of extremes final Point p = points.get(i);
Cy = Pi_xmin.y + dPx_x / 2.0; if (p.x < cxmin)
double dPC_x = Pi_xmax.x - Cx;
double dPC_y = Pi_xmax.y - Cy;
rad2 = dPC_x * dPC_x + dPC_y * dPC_y; // radius squared
}
else // y direction is largest extent
{
Cx = Pi_ymin.x + dPy_x / 2.0; // Center = midpoint of extremes
Cy = Pi_ymin.y + dPy_y / 2.0;
double dPC_x = Pi_ymax.x - Cx;
double dPC_y = Pi_ymax.y - Cy;
rad2 = dPC_x * dPC_x + dPC_y * dPC_y; // radius squared
}
rad = Math.sqrt(rad2);
// now check that all points P[i] are in the ball
// and if not, expand the ball just enough to include them
double dist, dist2;
for (int i = 0; i < P.size(); i++)
{
Point Pi = P.get(i);
double dPC_x = Pi.x - Cx;
double dPC_y = Pi.y - Cy;
dist2 = dPC_x * dPC_x + dPC_y * dPC_y;
if (dist2 <= rad2) // P[i] is inside the ball already
{ {
continue; cxmin = p.x;
}
else if (p.x > cxmax)
{
cxmax = p.x;
}
if (p.y < cymin)
{
cymin = p.y;
}
else if (p.y > cymax)
{
cymax = p.y;
} }
// P[i] not in ball, so expand ball to include it
dist = Math.sqrt(dist2);
rad = (rad + dist) / 2.0; // enlarge radius just enough
rad2 = rad * rad;
double dd = (dist - rad) / dist;
Cx = Cx + dd * dPC_x; // shift Center toward
Cy = Cy + dd * dPC_y;
} }
ilon = (int) Math.round(Cx);
ilat = (int) Math.round(Cy); double cx = (cxmax+cxmin) / 2.0; // center of circle
double cy = (cymax+cymin) / 2.0;
double ccoslat = coslat(cy); // cosin at latitude of center
double rad = 0; // radius
double rad2 = 0; // radius squared;
double dpx = 0; // x-xomponent of vector from center to point
double dpy = 0; // y-component
double dmax2 = 0; // squared lenght of vector from center to point
int i_max = -1;
do
{ // now identify the point outside of the circle that has the greatest distance
for (int i = 0; i < points.size();i++)
{
final Point p = points.get(i);
final double dpix = (p.x - cx) * ccoslat;
final double dpiy = p.y-cy;
final double dist2 = dpix * dpix + dpiy * dpiy;
if (dist2 <= rad2)
{
continue;
}
if (dist2 > dmax2)
{
dmax2 = dist2; // new maximum distance found
dpx = dpix;
dpy = dpiy;
i_max = i;
}
}
if (i_max < 0)
{
break; // leave loop when no point outside the circle is found any more.
}
final double dist = Math.sqrt(dmax2);
final double dd = 0.5 * (dist - rad) / dist;
cx = cx + dd * dpx; // shift center toward point
cy = cy + dd * dpy;
ccoslat = coslat(cy);
final Point p = points.get(i_max); // calculate new radius to just include this point
final double dpix = (p.x - cx) * ccoslat;
final double dpiy = p.y-cy;
dmax2 = rad2 = dpix * dpix + dpiy * dpiy;
rad = Math.sqrt(rad2);
i_max = -1;
}
while (true);
ilon = (int) Math.round(cx);
ilat = (int) Math.round(cy);
dpx = cx - ilon; // rounding error
dpy = cy - ilat;
// compensate rounding error of center-point // compensate rounding error of center-point
radius = rad + Math.max(Math.abs(Cx - ilon), Math.abs(Cy - ilat)); radius = rad + Math.sqrt(dpx * dpx + dpy * dpy);
return; return;
} }
public boolean intersectsOrIsWithin(int lon0, int lat0, int lon1, int lat1) public boolean intersectsOrIsWithin(int lon0, int lat0, int lon1, int lat1)
{ {
Point P0 = new Point (lon0,lat0); Point p0 = new Point (lon0,lat0);
Point P1 = new Point (lon1,lat1); Point p1 = new Point (lon1,lat1);
// is start or endpoint within polygon? // is start or endpoint within polygon?
if ((wn_PnPoly(P0, P) > 0) || (wn_PnPoly(P1, P) > 0)) if ((wn_PnPoly(p0, points) > 0) || (wn_PnPoly(p1, points) > 0))
{ {
return true; return true;
} }
Point P2 = P.get(0); int i_last = points.size()-1;
for (int i = 1; i < P.size(); i++) Point p2 = points.get(i_last);
for (int i = 0; i <= i_last; i++)
{ {
Point P3 = P.get(i); Point p3 = points.get(i);
// does it intersect with at least one of the polygon's segments? // does it intersect with at least one of the polygon's segments?
if (intersect2D_2Segments(P0,P1,P2,P3) > 0) if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
{ {
return true; return true;
} }
P2 = P3; p2 = p3;
} }
return false; return false;
} }
/** /**
* isLeft(): tests if a point is Left|On|Right of an infinite line. Input: * Copyright 2001 softSurfer, 2012 Dan Sunday
* three points P0, P1, and P2 Return: >0 for P2 left of the line through P0 * This code may be freely used and modified for any purpose providing that
* and P1 =0 for P2 on the line <0 for P2 right of the line See: Algorithm 1 * this copyright notice is included with it. SoftSurfer makes no warranty for
* "Area of Triangles and Polygons" * this code, and cannot be held liable for any real or imagined damage
*/ * resulting from its use. Users of this code must verify correctness for
* their application.
private static int isLeft(Point P0, Point P1, Point P2) { *
return ((P1.x - P0.x) * (P2.y - P0.y) - (P2.x - P0.x) * (P1.y - P0.y)); * cn_PnPoly(): crossing number test for a point in a polygon Input: P = a
} * point, V[] = vertex points of a polygon V[n+1] with V[n]=V[0] Return: 0 =
* outside, 1 = inside This code is patterned after [Franklin, 2000]
/** */
* cn_PnPoly(): crossing number test for a point in a polygon Input: P = a private static boolean cn_PnPoly(final Point p, final List<Point> v)
* point, V[] = vertex points of a polygon V[n+1] with V[n]=V[0] Return: 0 =
* outside, 1 = inside This code is patterned after [Franklin, 2000]
*/
private static boolean cn_PnPoly(Point P, List<Point> V)
{ {
int cn = 0; // the crossing number counter int cn = 0; // the crossing number counter
// loop through all edges of the polygon // loop through all edges of the polygon
int last = V.size()-1; int last = v.size()-1;
Point Vi = V.get(last); Point v0 = v.get(last);
for (int i = 0; i <= last; i++) // edge from V[i] to V[i+1] for (int i = 0; i <= last; i++) // edge from V[i] to V[i+1]
{ {
Point Vi1 = V.get(i); Point v1 = v.get(i);
if (((Vi.y <= P.y) && (Vi1.y > P.y)) // an upward crossing if (((v0.y <= p.y) && (v1.y > p.y)) // an upward crossing
|| ((Vi.y > P.y) && (Vi1.y <= P.y))) // a downward crossing || ((v0.y > p.y) && (v1.y <= p.y))) // a downward crossing
{ {
// compute the actual edge-ray intersect x-coordinate // compute the actual edge-ray intersect x-coordinate
float vt = (float) (P.y - Vi.y) / (Vi1.y - Vi.y); double vt = (double) (p.y - v0.y) / (v1.y - v0.y);
if (P.x < Vi.x + vt * (Vi1.x - Vi.x)) // P.x < intersect if (p.x < v0.x + vt * (v1.x - v0.x)) // P.x < intersect
{ {
++cn; // a valid crossing of y=P.y right of P.x ++cn; // a valid crossing of y=P.y right of P.x
} }
} }
Vi = Vi1; v0 = v1;
} }
return ((cn & 1) > 0); // 0 if even (out), and 1 if odd (in) return ((cn & 1) > 0); // 0 if even (out), and 1 if odd (in)
} }
/** /**
* wn_PnPoly(): winding number test for a point in a polygon Input: P = a * Copyright 2001 softSurfer, 2012 Dan Sunday
* point, V = vertex points of a polygon V[n+1] with V[n]=V[0] Return: wn = * This code may be freely used and modified for any purpose providing that
* the winding number (=0 only when P is outside) * this copyright notice is included with it. SoftSurfer makes no warranty for
*/ * this code, and cannot be held liable for any real or imagined damage
* resulting from its use. Users of this code must verify correctness for
* their application.
*
* wn_PnPoly(): winding number test for a point in a polygon Input: P = a
* point, V = vertex points of a polygon V[n+1] with V[n]=V[0] Return: wn =
* the winding number (=0 only when P is outside)
*/
private static int wn_PnPoly(Point P, List<Point> V) { private static int wn_PnPoly(final Point p, final List<Point> v) {
int wn = 0; // the winding number counter int wn = 0; // the winding number counter
final int px = p.x;
final int py = p.y;
// loop through all edges of the polygon // loop through all edges of the polygon
int last = V.size()-1; final int i_last = v.size()-1;
Point Vi = V.get(last); final Point p0 = v.get(i_last);
for (int i = 0; i <= last; i++) // edge from V[i] to V[i+1] long p0x = p0.x; // need to use long to avoid overflow in products
long p0y = p0.y;
for (int i = 0; i <= i_last; i++) // edge from v[i] to v[i+1]
{ {
Point Vi1 = V.get(i); final Point p1 = v.get(i);
final long p1x = p1.x;
final long p1y = p1.y;
if (Vi.y <= P.y) { // start y <= P.y if (p0y <= py) // start y <= p.y
if (Vi1.y > P.y) { // an upward crossing {
if (isLeft(Vi, Vi1, P) > 0) { // P left of edge if (p1y > py) // an upward crossing
++wn; // have a valid up intersect { // p left of edge
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) > 0)
{
++wn; // have a valid up intersect
} }
} }
} else { // start y > P.y (no test needed) } else { // start y > p.y (no test needed)
if (Vi1.y <= P.y) { // a downward crossing if (p1y <= py) // a downward crossing
if (isLeft(Vi, Vi1, P) < 0) { // P right of edge { // p right of edge
--wn; // have a valid down intersect if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
{
--wn; // have a valid down intersect
} }
} }
} }
Vi = Vi1; p0x = p1x;
p0y = p1y;
} }
return wn; return wn;
} }
/** /**
* inSegment(): determine if a point is inside a segment * Copyright 2001 softSurfer, 2012 Dan Sunday
* Input: a point P, and a collinear segment S * This code may be freely used and modified for any purpose providing that
* Return: 1 = P is inside S * this copyright notice is included with it. SoftSurfer makes no warranty for
* 0 = P is not inside S * this code, and cannot be held liable for any real or imagined damage
*/ * resulting from its use. Users of this code must verify correctness for
* their application.
private static boolean inSegment( Point P, Point SP0, Point SP1) *
* inSegment(): determine if a point is inside a segment
* Input: a point P, and a collinear segment S
* Return: 1 = P is inside S
* 0 = P is not inside S
*/
private static boolean inSegment( final Point p, final Point seg_p0, final Point seg_p1)
{ {
if (SP0.x != SP1.x) // S is not vertical final int sp0x = seg_p0.x;
final int sp1x = seg_p1.x;
if (sp0x != sp1x) // S is not vertical
{ {
if (SP0.x <= P.x && P.x <= SP1.x) final int px = p.x;
if (sp0x <= px && px <= sp1x)
{ {
return true; return true;
} }
if (SP0.x >= P.x && P.x >= SP1.x) if (sp0x >= px && px >= sp1x)
{ {
return true; return true;
} }
} }
else // S is vertical, so test y coordinate else // S is vertical, so test y coordinate
{ {
if (SP0.y <= P.y && P.y <= SP1.y) final int sp0y = seg_p0.y;
final int sp1y = seg_p1.y;
final int py = p.y;
if (sp0y <= py && py <= sp1y)
{ {
return true; return true;
} }
if (SP0.y >= P.y && P.y >= SP1.y) if (sp0y >= py && py >= sp1y)
{ {
return true; return true;
} }
@ -288,26 +325,33 @@ public class OsmNogoPolygon extends OsmNodeNamed
return false; return false;
} }
/** /**
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments * Copyright 2001 softSurfer, 2012 Dan Sunday
* Input: two finite segments S1 and S2 * This code may be freely used and modified for any purpose providing that
* Return: 0=disjoint (no intersect) * this copyright notice is included with it. SoftSurfer makes no warranty for
* 1=intersect in unique point I0 * this code, and cannot be held liable for any real or imagined damage
* 2=overlap in segment from I0 to I1 * resulting from its use. Users of this code must verify correctness for
*/ * their application.
private static int intersect2D_2Segments( Point S1P0, Point S1P1, Point S2P0, Point S2P1 ) *
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
* Input: two finite segments S1 and S2
* Return: 0=disjoint (no intersect)
* 1=intersect in unique point I0
* 2=overlap in segment from I0 to I1
*/
private static int intersect2D_2Segments( final Point s1p0, final Point s1p1, final Point s2p0, final Point s2p1 )
{ {
int ux = S1P1.x - S1P0.x; // vector u = S1P1-S1P0 (segment 1) final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
int uy = S1P1.y - S1P0.y; final long uy = s1p1.y - s1p0.y;
int vx = S2P1.x - S2P0.x; // vector v = S2P1-S2P0 (segment 2) final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
int vy = S2P1.y - S2P0.y; final long vy = s2p1.y - s2p0.y;
int wx = S1P0.x - S2P0.x; // vector w = S1P0-S2P0 (from start of segment 2 to start of segment 1 final long wx = s1p0.x - s2p0.x; // vector w = S1P0-S2P0 (from start of segment 2 to start of segment 1
int wy = S1P0.y - S2P0.y; final long wy = s1p0.y - s2p0.y;
int D = ux * vy - uy * vx; final double d = ux * vy - uy * vx;
// test if they are parallel (includes either being a point) // test if they are parallel (includes either being a point)
if (D == 0) // S1 and S2 are parallel if (d == 0) // S1 and S2 are parallel
{ {
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0) if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
{ {
@ -316,24 +360,24 @@ public class OsmNogoPolygon extends OsmNodeNamed
// they are collinear or degenerate // they are collinear or degenerate
// check if they are degenerate points // check if they are degenerate points
boolean du = ux == 0 && uy == 0; final boolean du = ((ux == 0) && (uy == 0));
boolean dv = vx == 0 && vy == 0; final boolean dv = ((vx == 0) && (vy == 0));
if (du && dv) // both segments are points if (du && dv) // both segments are points
{ {
return (wx == 0 && wy == 0) ? 0 : 1; // return 0 if they are distinct points return (wx == 0 && wy == 0) ? 0 : 1; // return 0 if they are distinct points
} }
if (du) // S1 is a single point if (du) // S1 is a single point
{ {
return inSegment(S1P0, S2P0, S2P1) ? 1 : 0; // is it part of S2? return inSegment(s1p0, s2p0, s2p1) ? 1 : 0; // is it part of S2?
} }
if (dv) // S2 a single point if (dv) // S2 a single point
{ {
return inSegment(S2P0, S1P0, S1P1) ? 1 : 0; // is it part of S1? return inSegment(s2p0, s1p0, s1p1) ? 1 : 0; // is it part of S1?
} }
// they are collinear segments - get overlap (or not) // they are collinear segments - get overlap (or not)
float t0, t1; // endpoints of S1 in eqn for S2 double t0, t1; // endpoints of S1 in eqn for S2
int w2x = S1P1.x - S2P0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1) final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
int w2y = S1P1.y - S2P0.y; final int w2y = s1p1.y - s2p0.y;
if (vx != 0) if (vx != 0)
{ {
t0 = wx / vx; t0 = wx / vx;
@ -346,7 +390,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
} }
if (t0 > t1) // must have t0 smaller than t1 if (t0 > t1) // must have t0 smaller than t1
{ {
float t=t0; // swap if not final double t=t0; // swap if not
t0=t1; t0=t1;
t1=t; t1=t;
} }
@ -363,14 +407,14 @@ public class OsmNogoPolygon extends OsmNodeNamed
// the segments are skew and may intersect in a point // the segments are skew and may intersect in a point
// get the intersect parameter for S1 // get the intersect parameter for S1
double sI = (vx * wy - vy * wx) / D; final double sI = (vx * wy - vy * wx) / d;
if (sI < 0 || sI > 1) // no intersect with S1 if (sI < 0 || sI > 1) // no intersect with S1
{ {
return 0; return 0;
} }
// get the intersect parameter for S2 // get the intersect parameter for S2
double tI = (ux * wy - uy * wx) / D; final double tI = (ux * wy - uy * wx) / d;
return (tI < 0 || tI > 1) ? 0 : 1; // return 0 if no intersect with S2 return (tI < 0 || tI > 1) ? 0 : 1; // return 0 if no intersect with S2
} }
} }

View file

@ -1,3 +1,20 @@
/**********************************************************************************************
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
**********************************************************************************************/
package btools.router; package btools.router;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -8,40 +25,60 @@ import org.junit.Test;
public class OsmNogoPolygonTest { public class OsmNogoPolygonTest {
OsmNogoPolygon p; OsmNogoPolygon p;
final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.0, -1.0 };
final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.0, -1.0 };
int toOsmLon(double lon) {
return (int)( ( lon + 180. ) *1000000. + 0.5); // see ServerHandler.readPosition()
}
int toOsmLat(double lat) {
return (int)( ( lat + 90. ) *1000000. + 0.5);
}
double coslat(int lat) // see RoutingContext.calcDistance()
{
final double l = (lat - 90000000) * 0.00000001234134; // 0.01234134 = Pi/(sqrt(2)*180)
final double l2 = l*l;
final double l4 = l2*l2;
// final double l6 = l4*l2;
return 1.- l2 + l4 / 6.; // - l6 / 90;
}
@Before @Before
public void setUp() throws Exception { public void setUp() throws Exception {
p = new OsmNogoPolygon(); p = new OsmNogoPolygon();
p.addVertex(1000, 1000); for (int i = 0; i<lons.length; i++) {
p.addVertex(2001, 1000); p.addVertex(toOsmLon(lons[i]),toOsmLat(lats[i]));
p.addVertex(2001, 1250); }
p.addVertex(1750, 1250);
p.addVertex(1750, 1750);
p.addVertex(2001, 1750);
p.addVertex(2001, 2001);
p.addVertex(1000, 2001);
} }
@Test @Test
public void testCalcBoundingCircle() { public void testCalcBoundingCircle() {
p.calcBoundingCircle(); p.calcBoundingCircle();
assertEquals(1501,p.ilat); double r = p.radius;
assertEquals(1501,p.ilon); for (int i=0; i<lons.length; i++) {
assertEquals(707.813887968,p.radius,0.5); double py = toOsmLat(lats[i]);
double dpx = (toOsmLon(lons[i]) - p.ilon) * coslat(p.ilat);
double dpy = py - p.ilat;
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
}
} }
@Test @Test
public void testIntersectsOrIsWithin() { public void testIntersectsOrIsWithin() {
assertFalse(p.intersectsOrIsWithin(0,0, 0,0)); double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, };
assertFalse(p.intersectsOrIsWithin(1800,1500, 1800,1500)); double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, };
assertFalse(p.intersectsOrIsWithin(1500,2002, 1500,2002)); double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, };
assertTrue(p.intersectsOrIsWithin(1750, 1500, 1800,1500)); double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, };
assertTrue(p.intersectsOrIsWithin(1500, 2001, 1500,2002)); boolean[] within = { true, false, true, true, true, true, false, true, };
assertTrue(p.intersectsOrIsWithin(1100, 1000, 1900, 1000));
assertTrue(p.intersectsOrIsWithin(0, 0, 1500,1500)); for (int i=0; i<p0lons.length; i++) {
assertTrue(p.intersectsOrIsWithin(500, 1500, 1500, 1500)); assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],p.intersectsOrIsWithin(toOsmLon(p0lons[i]), toOsmLat(p0lats[i]), toOsmLon(p1lons[i]), toOsmLat(p1lats[i])));
assertTrue(p.intersectsOrIsWithin(500, 1500, 2000, 1500)); }
assertTrue(p.intersectsOrIsWithin(1400, 1500, 1500, 1500));
} }
} }