Merge pull request #97 from ntruchsess/NogoPolygons
Nogo polygons / polylines
This commit is contained in:
commit
45495d8692
11 changed files with 833 additions and 21 deletions
|
@ -32,5 +32,10 @@
|
|||
<artifactId>brouter-expressions</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
461
brouter-core/src/main/java/btools/router/OsmNogoPolygon.java
Normal file
461
brouter-core/src/main/java/btools/router/OsmNogoPolygon.java
Normal file
|
@ -0,0 +1,461 @@
|
|||
/**********************************************************************************************
|
||||
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/>.
|
||||
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class OsmNogoPolygon extends OsmNodeNamed
|
||||
{
|
||||
public final static class Point
|
||||
{
|
||||
public final int y;
|
||||
public final int x;
|
||||
|
||||
Point(final int lon, final int lat)
|
||||
{
|
||||
x = lon;
|
||||
y = lat;
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Point> points = new ArrayList<Point>();
|
||||
|
||||
public final boolean isClosed;
|
||||
|
||||
public OsmNogoPolygon(boolean closed)
|
||||
{
|
||||
this.isClosed = closed;
|
||||
this.isNogo = true;
|
||||
this.name = "";
|
||||
}
|
||||
|
||||
public final void addVertex(int lon, int 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
{
|
||||
int cxmin, cxmax, cymin, cymax;
|
||||
cxmin = cymin = Integer.MAX_VALUE;
|
||||
cxmax = cymax = Integer.MIN_VALUE;
|
||||
|
||||
// first calculate a starting center point as center of boundingbox
|
||||
for (int i = 0; i < points.size(); i++)
|
||||
{
|
||||
final Point p = points.get(i);
|
||||
if (p.x < cxmin)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
radius = (rad + Math.sqrt(dpx * dpx + dpy * dpy)) * 0.000001;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* tests whether a segment defined by lon and lat of two points does either
|
||||
* intersect the polygon or any of the endpoints (or both) are enclosed by
|
||||
* the polygon. For this test the winding-number algorithm is
|
||||
* being used. That means a point being within an overlapping region of the
|
||||
* polygon is also taken as being 'inside' the polygon.
|
||||
*
|
||||
* @param lon0 longitude of start point
|
||||
* @param lat0 latitude of start point
|
||||
* @param lon1 longitude of end point
|
||||
* @param lat1 latitude of start point
|
||||
* @return true if segment or any of it's points are 'inside' of polygon
|
||||
*/
|
||||
public boolean intersects(int lon0, int lat0, int lon1, int lat1)
|
||||
{
|
||||
final Point p0 = new Point (lon0,lat0);
|
||||
final Point p1 = new Point (lon1,lat1);
|
||||
int i_last = points.size()-1;
|
||||
Point p2 = points.get(isClosed ? i_last : 0 );
|
||||
for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
|
||||
{
|
||||
Point p3 = points.get(i);
|
||||
// does it intersect with at least one of the polygon's segments?
|
||||
if (intersect2D_2Segments(p0,p1,p2,p3) > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
p2 = p3;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOnPolyline( long px, long py )
|
||||
{
|
||||
int i_last = points.size()-1;
|
||||
Point p1 = points.get(0);
|
||||
for (int i = 1 ; i <= i_last; i++)
|
||||
{
|
||||
final Point p2 = points.get(i);
|
||||
if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
p1 = p2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isOnLine( long px, long py, long p0x, long p0y, long p1x, long p1y )
|
||||
{
|
||||
final double v10x = px-p0x;
|
||||
final double v10y = py-p0y;
|
||||
final double v12x = p1x-p0x;
|
||||
final double v12y = p1y-p0y;
|
||||
|
||||
if ( v10x == 0 ) // P0->P1 vertical?
|
||||
{
|
||||
if ( v10y == 0 ) // P0 == P1?
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if ( v12x != 0 ) // P1->P2 not vertical?
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ( v12y / v10y ) >= 1; // P1->P2 at least as long as P1->P0?
|
||||
}
|
||||
if ( v10y == 0 ) // P0->P1 horizontal?
|
||||
{
|
||||
if ( v12y != 0 ) // P1->P2 not horizontal?
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// if ( P10x == 0 ) // P0 == P1? already tested
|
||||
return ( v12x / v10x ) >= 1; // P1->P2 at least as long as P1->P0?
|
||||
}
|
||||
final double kx = v12x / v10x;
|
||||
if ( kx < 1 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return kx == v12y / v10y;
|
||||
}
|
||||
|
||||
/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
|
||||
This code may be freely used and modified for any purpose providing that
|
||||
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. */
|
||||
/**
|
||||
* winding number test for a point in a polygon
|
||||
*
|
||||
* @param p a point
|
||||
* @param v list of vertex points forming a polygon. This polygon
|
||||
* is implicitly closed connecting the last and first point.
|
||||
* @return the winding number (=0 only when P is outside)
|
||||
*/
|
||||
public boolean isWithin(final long px, final long py)
|
||||
{
|
||||
int wn = 0; // the winding number counter
|
||||
|
||||
// loop through all edges of the polygon
|
||||
final int i_last = points.size()-1;
|
||||
final Point p0 = points.get(isClosed ? i_last : 0);
|
||||
long p0x = p0.x; // need to use long to avoid overflow in products
|
||||
long p0y = p0.y;
|
||||
|
||||
for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
|
||||
{
|
||||
final Point p1 = points.get(i);
|
||||
|
||||
final long p1x = p1.x;
|
||||
final long p1y = p1.y;
|
||||
|
||||
if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (p0y <= py) // start y <= p.y
|
||||
{
|
||||
if (p1y > py) // an upward crossing
|
||||
{ // 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)
|
||||
{
|
||||
if (p1y <= py) // a downward crossing
|
||||
{ // p right of edge
|
||||
if (((p1x - p0x) * (py - p0y) - (px - p0x) * (p1y - p0y)) < 0)
|
||||
{
|
||||
--wn; // have a valid down intersect
|
||||
}
|
||||
}
|
||||
}
|
||||
p0x = p1x;
|
||||
p0y = p1y;
|
||||
}
|
||||
return wn != 0;
|
||||
}
|
||||
|
||||
/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
|
||||
This code may be freely used and modified for any purpose providing that
|
||||
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. */
|
||||
/**
|
||||
* inSegment(): determine if a point is inside a segment
|
||||
*
|
||||
* @param p a point
|
||||
* @param seg_p0 starting point of segment
|
||||
* @param seg_p1 ending point of segment
|
||||
* @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)
|
||||
{
|
||||
final int sp0x = seg_p0.x;
|
||||
final int sp1x = seg_p1.x;
|
||||
|
||||
if (sp0x != sp1x) // S is not vertical
|
||||
{
|
||||
final int px = p.x;
|
||||
if (sp0x <= px && px <= sp1x)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (sp0x >= px && px >= sp1x)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else // S is vertical, so test y coordinate
|
||||
{
|
||||
final int sp0y = seg_p0.y;
|
||||
final int sp1y = seg_p1.y;
|
||||
final int py = p.y;
|
||||
|
||||
if (sp0y <= py && py <= sp1y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (sp0y >= py && py >= sp1y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
|
||||
This code may be freely used and modified for any purpose providing that
|
||||
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. */
|
||||
/**
|
||||
* intersect2D_2Segments(): find the 2D intersection of 2 finite segments
|
||||
* @param s1p0 start point of segment 1
|
||||
* @param s1p1 end point of segment 1
|
||||
* @param s2p0 start point of segment 2
|
||||
* @param s2p1 end point of segment 2
|
||||
* @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 )
|
||||
{
|
||||
final long ux = s1p1.x - s1p0.x; // vector u = S1P1-S1P0 (segment 1)
|
||||
final long uy = s1p1.y - s1p0.y;
|
||||
final long vx = s2p1.x - s2p0.x; // vector v = S2P1-S2P0 (segment 2)
|
||||
final long vy = s2p1.y - s2p0.y;
|
||||
final long wx = s1p0.x - s2p0.x; // vector w = S1P0-S2P0 (from start of segment 2 to start of segment 1
|
||||
final long wy = s1p0.y - s2p0.y;
|
||||
|
||||
final double d = ux * vy - uy * vx;
|
||||
|
||||
// test if they are parallel (includes either being a point)
|
||||
if (d == 0) // S1 and S2 are parallel
|
||||
{
|
||||
if ((ux * wy - uy * wx) != 0 || (vx * wy - vy * wx) != 0)
|
||||
{
|
||||
return 0; // they are NOT collinear
|
||||
}
|
||||
|
||||
// they are collinear or degenerate
|
||||
// check if they are degenerate points
|
||||
final boolean du = ((ux == 0) && (uy == 0));
|
||||
final boolean dv = ((vx == 0) && (vy == 0));
|
||||
if (du && dv) // both segments are points
|
||||
{
|
||||
return (wx == 0 && wy == 0) ? 0 : 1; // return 0 if they are distinct points
|
||||
}
|
||||
if (du) // S1 is a single point
|
||||
{
|
||||
return inSegment(s1p0, s2p0, s2p1) ? 1 : 0; // is it part of S2?
|
||||
}
|
||||
if (dv) // S2 a single point
|
||||
{
|
||||
return inSegment(s2p0, s1p0, s1p1) ? 1 : 0; // is it part of S1?
|
||||
}
|
||||
// they are collinear segments - get overlap (or not)
|
||||
double t0, t1; // endpoints of S1 in eqn for S2
|
||||
final int w2x = s1p1.x - s2p0.x; // vector w2 = S1P1-S2P0 (from start of segment 2 to end of segment 1)
|
||||
final int w2y = s1p1.y - s2p0.y;
|
||||
if (vx != 0)
|
||||
{
|
||||
t0 = wx / vx;
|
||||
t1 = w2x / vx;
|
||||
}
|
||||
else
|
||||
{
|
||||
t0 = wy / vy;
|
||||
t1 = w2y / vy;
|
||||
}
|
||||
if (t0 > t1) // must have t0 smaller than t1
|
||||
{
|
||||
final double t=t0; // swap if not
|
||||
t0=t1;
|
||||
t1=t;
|
||||
}
|
||||
if (t0 > 1 || t1 < 0)
|
||||
{
|
||||
return 0; // NO overlap
|
||||
}
|
||||
t0 = t0<0? 0 : t0; // clip to min 0
|
||||
t1 = t1>1? 1 : t1; // clip to max 1
|
||||
|
||||
return (t0 == t1) ? 1 : 2; // return 1 if intersect is a point
|
||||
}
|
||||
|
||||
// the segments are skew and may intersect in a point
|
||||
// get the intersect parameter for S1
|
||||
|
||||
final double sI = (vx * wy - vy * wx) / d;
|
||||
if (sI < 0 || sI > 1) // no intersect with S1
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// get the intersect parameter for S2
|
||||
final double tI = (ux * wy - uy * wx) / d;
|
||||
return (tI < 0 || tI > 1) ? 0 : 1; // return 0 if no intersect with S2
|
||||
}
|
||||
}
|
|
@ -192,16 +192,20 @@ public final class RoutingContext
|
|||
{
|
||||
for( OsmNodeNamed nogo : nogos )
|
||||
{
|
||||
String s = nogo.name;
|
||||
int idx = s.indexOf( ' ' );
|
||||
if ( idx > 0 ) s = s.substring( 0 , idx );
|
||||
int ir = 20; // default radius
|
||||
if ( s.length() > 4 )
|
||||
{
|
||||
try { ir = Integer.parseInt( s.substring( 4 ) ); }
|
||||
catch( Exception e ) { /* ignore */ }
|
||||
}
|
||||
nogo.radius = ir / 110984.; // 6378000. / 57.3;
|
||||
if (nogo instanceof OsmNogoPolygon)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String s = nogo.name;
|
||||
int idx = s.indexOf( ' ' );
|
||||
if ( idx > 0 ) s = s.substring( 0 , idx );
|
||||
int ir = 20; // default radius
|
||||
if ( s.length() > 4 )
|
||||
{
|
||||
try { ir = Integer.parseInt( s.substring( 4 ) ); }
|
||||
catch( Exception e ) { /* ignore */ }
|
||||
}
|
||||
nogo.radius = ir / 110984.; // 6378000. / 57.3;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,7 +219,11 @@ public final class RoutingContext
|
|||
boolean goodGuy = true;
|
||||
for( OsmNodeNamed wp : waypoints )
|
||||
{
|
||||
if ( wp.calcDistance( nogo ) < radiusInMeter )
|
||||
if ( wp.calcDistance( nogo ) < radiusInMeter
|
||||
&& (!(nogo instanceof OsmNogoPolygon)
|
||||
|| (((OsmNogoPolygon)nogo).isClosed
|
||||
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
|
||||
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
|
||||
{
|
||||
goodGuy = false;
|
||||
break;
|
||||
|
@ -294,7 +302,14 @@ public final class RoutingContext
|
|||
radius = Math.sqrt( s1 < s2 ? r12 : r22 );
|
||||
if ( radius > nogo.radius ) continue; // 20m ^ 2
|
||||
}
|
||||
if ( nogo.isNogo ) nogomatch = true;
|
||||
if ( nogo.isNogo )
|
||||
{
|
||||
if (!(nogo instanceof OsmNogoPolygon)
|
||||
|| ((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
|
||||
{
|
||||
nogomatch = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
shortestmatch = true;
|
||||
|
|
153
brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java
Normal file
153
brouter-core/src/test/java/btools/router/OsmNogoPolygonTest.java
Normal file
|
@ -0,0 +1,153 @@
|
|||
/**********************************************************************************************
|
||||
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;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import btools.router.OsmNogoPolygon.Point;
|
||||
|
||||
public class OsmNogoPolygonTest {
|
||||
|
||||
static final int offset_x = 11000000;
|
||||
static final int offset_y = 50000000;
|
||||
|
||||
static OsmNogoPolygon polygon;
|
||||
static OsmNogoPolygon polyline;
|
||||
|
||||
static final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0 };
|
||||
static final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
|
||||
|
||||
static int toOsmLon(double lon) {
|
||||
return (int)( ( lon + 180. ) *1000000. + 0.5)+offset_x; // see ServerHandler.readPosition()
|
||||
}
|
||||
|
||||
static int toOsmLat(double lat) {
|
||||
return (int)( ( lat + 90. ) *1000000. + 0.5)+offset_y;
|
||||
}
|
||||
|
||||
static 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;
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setUp() throws Exception {
|
||||
polygon = new OsmNogoPolygon(true);
|
||||
for (int i = 0; i<lons.length; i++) {
|
||||
polygon.addVertex(toOsmLon(lons[i]),toOsmLat(lats[i]));
|
||||
}
|
||||
polyline = new OsmNogoPolygon(false);
|
||||
for (int i = 0; i<lons.length; i++) {
|
||||
polyline.addVertex(toOsmLon(lons[i]),toOsmLat(lats[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDown() throws Exception {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalcBoundingCircle() {
|
||||
polygon.calcBoundingCircle();
|
||||
double r = polygon.radius;
|
||||
for (int i=0; i<lons.length; i++) {
|
||||
double py = toOsmLat(lats[i]);
|
||||
double dpx = (toOsmLon(lons[i]) - polygon.ilon) * coslat(polygon.ilat);
|
||||
double dpy = py - polygon.ilat;
|
||||
double r1 = Math.sqrt(dpx * dpx + dpy * dpy) * 0.000001;
|
||||
double diff = r-r1;
|
||||
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
|
||||
}
|
||||
polyline.calcBoundingCircle();
|
||||
r = polyline.radius;
|
||||
for (int i=0; i<lons.length; i++) {
|
||||
double py = toOsmLat(lats[i]);
|
||||
double dpx = (toOsmLon(lons[i]) - polyline.ilon) * coslat(polyline.ilat);
|
||||
double dpy = py - polyline.ilat;
|
||||
double r1 = Math.sqrt(dpx * dpx + dpy * dpy) * 0.000001;
|
||||
double diff = r-r1;
|
||||
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsWithin() {
|
||||
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
|
||||
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
|
||||
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
|
||||
|
||||
for (int i=0; i<plons.length; i++) {
|
||||
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i]), toOsmLat(plats[i])));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectsPolygon() {
|
||||
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
|
||||
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
|
||||
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
|
||||
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
|
||||
boolean[] within = { false, false, false, true, true, true, false, true, true, true };
|
||||
|
||||
for (int i=0; i<p0lons.length; i++) {
|
||||
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polygon.intersects(toOsmLon(p0lons[i]), toOsmLat(p0lats[i]), toOsmLon(p1lons[i]), toOsmLat(p1lats[i])));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntersectsPolyline() {
|
||||
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
|
||||
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
|
||||
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
|
||||
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
|
||||
boolean[] within = { false, false, false, true, true, true, false, true, true, false };
|
||||
|
||||
for (int i=0; i<p0lons.length; i++) {
|
||||
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polyline.intersects(toOsmLon(p0lons[i]), toOsmLat(p0lats[i]), toOsmLon(p1lons[i]), toOsmLat(p1lats[i])));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBelongsToLine() {
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 10,20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 20,10, 10,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,20, 10,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,15, 10,10, 10,20));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15,10, 10,10, 20,10));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(20,30, 10,10, 20,30));
|
||||
assertTrue(OsmNogoPolygon.isOnLine(15,20, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 10,20));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 20,10));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15,21, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(15,19, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(0,-10, 10,10, 20,30));
|
||||
assertFalse(OsmNogoPolygon.isOnLine(30,50, 10,10, 20,30));
|
||||
}
|
||||
}
|
|
@ -32,6 +32,8 @@ import btools.expressions.BExpressionContextWay;
|
|||
import btools.expressions.BExpressionMetaData;
|
||||
import btools.mapaccess.OsmNode;
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmNogoPolygon;
|
||||
import btools.router.OsmNogoPolygon.Point;
|
||||
import btools.router.OsmTrack;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.router.RoutingEngine;
|
||||
|
@ -518,7 +520,7 @@ public class BRouterView extends View
|
|||
scaleLon = scaleLat * coslat;
|
||||
|
||||
startTime = System.currentTimeMillis();
|
||||
rc.prepareNogoPoints( nogoList );
|
||||
RoutingContext.prepareNogoPoints( nogoList );
|
||||
rc.nogopoints = nogoList;
|
||||
|
||||
rc.memoryclass = memoryClass;
|
||||
|
@ -640,6 +642,41 @@ public class BRouterView extends View
|
|||
canvas.drawCircle( (float) x, (float) y, (float) ir, paint );
|
||||
}
|
||||
}
|
||||
|
||||
private void paintLine( Canvas canvas, final int ilon0, final int ilat0, final int ilon1, final int ilat1, final Paint paint )
|
||||
{
|
||||
final int lon0 = ilon0 - centerLon;
|
||||
final int lat0 = ilat0 - centerLat;
|
||||
final int lon1 = ilon1 - centerLon;
|
||||
final int lat1 = ilat1 - centerLat;
|
||||
final int x0 = imgw / 2 + (int) ( scaleLon * lon0 );
|
||||
final int y0 = imgh / 2 - (int) ( scaleLat * lat0 );
|
||||
final int x1 = imgw / 2 + (int) ( scaleLon * lon1 );
|
||||
final int y1 = imgh / 2 - (int) ( scaleLat * lat1 );
|
||||
canvas.drawLine( (float) x0, (float) y0, (float) x1, (float) y1, paint );
|
||||
}
|
||||
|
||||
private void paintPolygon( Canvas canvas, OsmNogoPolygon p, int minradius )
|
||||
{
|
||||
final int ir = (int) ( p.radius * 1000000. * scaleLat );
|
||||
if ( ir > minradius )
|
||||
{
|
||||
Paint paint = new Paint();
|
||||
paint.setColor( Color.RED );
|
||||
paint.setStyle( Paint.Style.STROKE );
|
||||
|
||||
Point p0 = p.isClosed ? p.points.get(p.points.size()-1) : null;
|
||||
|
||||
for ( final Point p1 : p.points )
|
||||
{
|
||||
if (p0 != null)
|
||||
{
|
||||
paintLine( canvas, p0.x, p0.y, p1.x, p1.y, paint );
|
||||
}
|
||||
p0 = p1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged( int w, int h, int oldw, int oldh )
|
||||
|
@ -824,8 +861,15 @@ public class BRouterView extends View
|
|||
for ( int ngi = 0; ngi < nogoList.size(); ngi++ )
|
||||
{
|
||||
OsmNodeNamed n = nogoList.get( ngi );
|
||||
int color = 0xff0000;
|
||||
paintCircle( canvas, n, color, 4 );
|
||||
if (n instanceof OsmNogoPolygon)
|
||||
{
|
||||
paintPolygon( canvas, (OsmNogoPolygon)n, 4 );
|
||||
}
|
||||
else
|
||||
{
|
||||
int color = 0xff0000;
|
||||
paintCircle( canvas, n, color, 4 );
|
||||
}
|
||||
}
|
||||
|
||||
Paint paint = new Paint();
|
||||
|
|
|
@ -67,7 +67,7 @@ public class BRouterWorker
|
|||
}
|
||||
|
||||
readNogos( params ); // add interface provided nogos
|
||||
rc.prepareNogoPoints( nogoList );
|
||||
RoutingContext.prepareNogoPoints( nogoList );
|
||||
rc.nogopoints = nogoList;
|
||||
|
||||
waypoints = readPositions(params);
|
||||
|
|
|
@ -3,9 +3,15 @@ package btools.routingapp;
|
|||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserFactory;
|
||||
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmNogoPolygon;
|
||||
|
||||
/**
|
||||
* Read coordinates from a gpx-file
|
||||
|
@ -65,6 +71,13 @@ public class CoordinateReaderOsmAnd extends CoordinateReader
|
|||
{
|
||||
_readPointmap( osmandDir + "/favourites.gpx" );
|
||||
}
|
||||
try
|
||||
{
|
||||
_readNogoLines( basedir+tracksdir );
|
||||
}
|
||||
catch( IOException ioe )
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void _readPointmap( String filename ) throws Exception
|
||||
|
@ -107,4 +120,71 @@ public class CoordinateReaderOsmAnd extends CoordinateReader
|
|||
}
|
||||
br.close();
|
||||
}
|
||||
|
||||
private void _readNogoLines( String dirname ) throws IOException
|
||||
{
|
||||
|
||||
File dir = new File( dirname );
|
||||
|
||||
if (dir.exists() && dir.isDirectory())
|
||||
{
|
||||
for (final File file : dir.listFiles())
|
||||
{
|
||||
final String name = file.getName();
|
||||
if (name.startsWith("nogo") && name.endsWith(".gpx"))
|
||||
{
|
||||
try
|
||||
{
|
||||
_readNogoLine(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void _readNogoLine( File file ) throws Exception
|
||||
{
|
||||
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
|
||||
factory.setNamespaceAware(false);
|
||||
XmlPullParser xpp = factory.newPullParser();
|
||||
|
||||
xpp.setInput(new FileReader(file));
|
||||
OsmNogoPolygon nogo = new OsmNogoPolygon(false);
|
||||
int eventType = xpp.getEventType();
|
||||
int numSeg = 0;
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
switch(eventType) {
|
||||
case XmlPullParser.START_TAG: {
|
||||
if (xpp.getName().equals("trkpt")) {
|
||||
final String lon = xpp.getAttributeValue(null,"lon");
|
||||
final String lat = xpp.getAttributeValue(null,"lat");
|
||||
if (lon != null && lat != null) {
|
||||
nogo.addVertex(
|
||||
(int)( ( Double.parseDouble(lon) + 180. ) *1000000. + 0.5),
|
||||
(int)( ( Double.parseDouble(lat) + 90. ) *1000000. + 0.5));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case XmlPullParser.END_TAG: {
|
||||
if (xpp.getName().equals("trkseg")) {
|
||||
nogo.calcBoundingCircle();
|
||||
final String name = file.getName();
|
||||
nogo.name = name.substring(0, name.length()-4);
|
||||
if (numSeg > 0)
|
||||
{
|
||||
nogo.name += Integer.toString(numSeg+1);
|
||||
}
|
||||
numSeg++;
|
||||
checkAddPoint( "(one-for-all)", nogo );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
eventType = xpp.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public class BRouter
|
|||
}
|
||||
System.exit(0);
|
||||
}
|
||||
System.out.println("BRouter 1.4.9 / 24092017 / abrensch");
|
||||
System.out.println("BRouter 1.4.10 / 26022018 / abrensch+ntruchsess");
|
||||
if ( args.length < 6 )
|
||||
{
|
||||
System.out.println("Find routes in an OSM map");
|
||||
|
|
|
@ -157,7 +157,7 @@ public class RouteServer extends Thread
|
|||
|
||||
public static void main(String[] args) throws Exception
|
||||
{
|
||||
System.out.println("BRouter 1.4.9 / 24092017");
|
||||
System.out.println("BRouter 1.4.10 / 26022018 abrensch+ntruchsess");
|
||||
if ( args.length != 5 && args.length != 6)
|
||||
{
|
||||
System.out.println("serve BRouter protocol");
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package btools.server.request;
|
||||
|
||||
import btools.router.OsmNodeNamed;
|
||||
import btools.router.OsmNogoPolygon;
|
||||
import btools.router.OsmTrack;
|
||||
import btools.router.RoutingContext;
|
||||
import btools.server.ServiceContext;
|
||||
|
@ -55,12 +56,23 @@ public class ServerHandler extends RequestHandler {
|
|||
rc.setAlternativeIdx(Integer.parseInt(params.get( "alternativeidx" )));
|
||||
|
||||
List<OsmNodeNamed> nogoList = readNogoList();
|
||||
List<OsmNodeNamed> nogoPolygonsList = readNogoPolygons();
|
||||
|
||||
if ( nogoList != null )
|
||||
{
|
||||
rc.prepareNogoPoints( nogoList );
|
||||
RoutingContext.prepareNogoPoints( nogoList );
|
||||
rc.nogopoints = nogoList;
|
||||
}
|
||||
|
||||
if (rc.nogopoints == null)
|
||||
{
|
||||
rc.nogopoints = nogoPolygonsList;
|
||||
}
|
||||
else if ( nogoPolygonsList != null )
|
||||
{
|
||||
rc.nogopoints.addAll(nogoPolygonsList);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -224,5 +236,42 @@ public class ServerHandler extends RequestHandler {
|
|||
n.ilat = (int)( ( lat + 90. ) *1000000. + 0.5);
|
||||
n.isNogo = true;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
private List<OsmNodeNamed> readNogoPolygons()
|
||||
{
|
||||
List<OsmNodeNamed> result = new ArrayList<OsmNodeNamed>();
|
||||
parseNogoPolygons( params.get("polylines"), result, false );
|
||||
parseNogoPolygons( params.get("polygons"), result, true );
|
||||
return result.size() > 0 ? result : null;
|
||||
}
|
||||
|
||||
private static void parseNogoPolygons(String polygons, List<OsmNodeNamed> result, boolean closed )
|
||||
{
|
||||
if ( polygons != null )
|
||||
{
|
||||
String[] polygonList = polygons.split("\\|");
|
||||
for (int i = 0; i < polygonList.length; i++)
|
||||
{
|
||||
String[] lonLatList = polygonList[i].split(",");
|
||||
if ( lonLatList.length > 1 )
|
||||
{
|
||||
OsmNogoPolygon polygon = new OsmNogoPolygon(closed);
|
||||
for (int j = 0; j < lonLatList.length-1;)
|
||||
{
|
||||
String slon = lonLatList[j++];
|
||||
String slat = lonLatList[j++];
|
||||
int lon = (int)( ( Double.parseDouble(slon) + 180. ) *1000000. + 0.5);
|
||||
int lat = (int)( ( Double.parseDouble(slat) + 90. ) *1000000. + 0.5);
|
||||
polygon.addVertex(lon, lat);
|
||||
}
|
||||
if ( polygon.points.size() > 0 )
|
||||
{
|
||||
polygon.calcBoundingCircle();
|
||||
result.add(polygon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
7
pom.xml
7
pom.xml
|
@ -28,6 +28,11 @@
|
|||
<name>Arndt Brenschede</name>
|
||||
<email>Arndt.Brenschede@web.de</email>
|
||||
</developer>
|
||||
<developer>
|
||||
<id>norbert.truchsess</id>
|
||||
<name>Norbert Truchsess</name>
|
||||
<email>norbert.truchsess@t-online.de</email>
|
||||
</developer>
|
||||
</developers>
|
||||
|
||||
<properties>
|
||||
|
@ -146,7 +151,7 @@
|
|||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
|
Loading…
Reference in a new issue