add support for open nogo-polygons

This commit is contained in:
ntruchsess 2018-02-07 20:50:06 +01:00
parent 419cb4e78c
commit a0198e3b34
4 changed files with 180 additions and 96 deletions

View file

@ -27,7 +27,7 @@ import java.util.List;
public class OsmNogoPolygon extends OsmNodeNamed public class OsmNogoPolygon extends OsmNodeNamed
{ {
private final static class Point public final static class Point
{ {
final int y; final int y;
final int x; final int x;
@ -40,6 +40,15 @@ public class OsmNogoPolygon extends OsmNodeNamed
} }
public final List<Point> points = new ArrayList<Point>(); 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) public final void addVertex(int lon, int lat)
{ {
@ -171,11 +180,11 @@ public class OsmNogoPolygon extends OsmNodeNamed
* @param lat latitude of point * @param lat latitude of point
* @return true if point is inside of polygon, false otherwise * @return true if point is inside of polygon, false otherwise
*/ */
public boolean isWithin(int lon, int lat) // public boolean isWithin(int lon, int lat)
{ // {
return wn_PnPoly(lon,lat,points) != 0; // return wn_PnPoly(lon,lat,points) != 0;
} // }
/** /**
* tests whether a segment defined by lon and lat of two points does either * 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 * intersect the polygon or any of the endpoints (or both) are enclosed by
@ -189,18 +198,25 @@ public class OsmNogoPolygon extends OsmNodeNamed
* @param lat1 latitude of start point * @param lat1 latitude of start point
* @return true if segment or any of it's points are 'inside' of polygon * @return true if segment or any of it's points are 'inside' of polygon
*/ */
public boolean intersectsOrIsWithin(int lon0, int lat0, int lon1, int lat1) public boolean intersects(int lon0, int lat0, int lon1, int lat1)
{ {
// is start or endpoint within polygon? // is start or endpoint within closed polygon?
if ((wn_PnPoly(lon0,lat0, points) != 0) || (wn_PnPoly(lon1,lat1, points) != 0)) // if (isClosed)
{ // {
return true; // if (wn_PnPoly(lon1,lat1, points) != 0)
} // {
// return true;
// }
// check of wn_PnPoly(lon0,lat0, points) would return true only very few times.
// in the majority of cases (point within bounding-circle but outside of
// polygon both wn_PnPoly and intersect would both run, but intersect-check
// will catch all points inside the polygon as well.
// }
final Point p0 = new Point (lon0,lat0); final Point p0 = new Point (lon0,lat0);
final Point p1 = new Point (lon1,lat1); final Point p1 = new Point (lon1,lat1);
int i_last = points.size()-1; int i_last = points.size()-1;
Point p2 = points.get(i_last); Point p2 = points.get(isClosed ? i_last : 0 );
for (int i = 0; i <= i_last; i++) for (int i = isClosed ? 0 : 1 ; i <= i_last; i++)
{ {
Point p3 = points.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?
@ -213,47 +229,56 @@ public class OsmNogoPolygon extends OsmNodeNamed
return false; return false;
} }
/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess public boolean isOnPolyline( long px, long py )
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. */
/**
* cn_PnPoly(): crossing 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 0 = outside, 1 = inside.
*
* This code is patterned after [Franklin, 2000]
*/
private static boolean cn_PnPoly(final Point p, final List<Point> v)
{ {
int cn = 0; // the crossing number counter int i_last = points.size()-1;
Point p1 = points.get(0);
// loop through all edges of the polygon for (int i = 1 ; i <= i_last; i++)
int last = v.size()-1;
Point v0 = v.get(last);
for (int i = 0; i <= last; i++) // edge from V[i] to V[i+1]
{ {
Point v1 = v.get(i); final Point p2 = points.get(i);
if (OsmNogoPolygon.isOnLine(px,py,p1.x,p1.y,p2.x,p2.y))
if (((v0.y <= p.y) && (v1.y > p.y)) // an upward crossing
|| ((v0.y > p.y) && (v1.y <= p.y))) // a downward crossing
{ {
// compute the actual edge-ray intersect x-coordinate return true;
double vt = (double) (p.y - v0.y) / (v1.y - v0.y);
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
}
} }
v0 = v1; p1 = p2;
} }
return ((cn & 1) > 0); // 0 if even (out), and 1 if odd (in) 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 /* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess
@ -270,21 +295,28 @@ public class OsmNogoPolygon extends OsmNodeNamed
* is implicitly closed connecting the last and first point. * is implicitly closed connecting the last and first point.
* @return the winding number (=0 only when P is outside) * @return the winding number (=0 only when P is outside)
*/ */
private static int wn_PnPoly(final long px, final long py, final List<Point> v) { public boolean isWithin(final long px, final long py)
{
int wn = 0; // the winding number counter int wn = 0; // the winding number counter
// loop through all edges of the polygon // loop through all edges of the polygon
final int i_last = v.size()-1; final int i_last = points.size()-1;
final Point p0 = v.get(i_last); final Point p0 = points.get(isClosed ? i_last : 0);
long p0x = p0.x; // need to use long to avoid overflow in products long p0x = p0.x; // need to use long to avoid overflow in products
long p0y = p0.y; long p0y = p0.y;
for (int i = 0; i <= i_last; i++) // edge from v[i] to v[i+1] for (int i = isClosed ? 0 : 1; i <= i_last; i++) // edge from v[i] to v[i+1]
{ {
final Point p1 = v.get(i); final Point p1 = points.get(i);
final long p1x = p1.x; final long p1x = p1.x;
final long p1y = p1.y; final long p1y = p1.y;
if (OsmNogoPolygon.isOnLine(px, py, p0x, p0y, p1x, p1y))
{
return true;
}
if (p0y <= py) // start y <= p.y if (p0y <= py) // start y <= p.y
{ {
if (p1y > py) // an upward crossing if (p1y > py) // an upward crossing
@ -308,7 +340,7 @@ public class OsmNogoPolygon extends OsmNodeNamed
p0x = p1x; p0x = p1x;
p0y = p1y; p0y = p1y;
} }
return wn; return wn != 0;
} }
/* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess /* Copyright 2001 softSurfer, 2012 Dan Sunday, 2018 Norbert Truchsess

View file

@ -217,7 +217,9 @@ public final class RoutingContext
{ {
if ( wp.calcDistance( nogo ) < radiusInMeter if ( wp.calcDistance( nogo ) < radiusInMeter
&& (!(nogo instanceof OsmNogoPolygon) && (!(nogo instanceof OsmNogoPolygon)
|| ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat))) || (((OsmNogoPolygon)nogo).isClosed
? ((OsmNogoPolygon)nogo).isWithin(wp.ilon, wp.ilat)
: ((OsmNogoPolygon)nogo).isOnPolyline(wp.ilon, wp.ilat))))
{ {
goodGuy = false; goodGuy = false;
break; break;
@ -299,7 +301,7 @@ public final class RoutingContext
if ( nogo.isNogo ) if ( nogo.isNogo )
{ {
if (!(nogo instanceof OsmNogoPolygon) if (!(nogo instanceof OsmNogoPolygon)
|| ((OsmNogoPolygon)nogo).intersectsOrIsWithin(lon1, lat1, lon2, lat2)) || ((OsmNogoPolygon)nogo).intersects(lon1, lat1, lon2, lat2))
{ {
nogomatch = true; nogomatch = true;
} }

View file

@ -18,18 +18,22 @@
package btools.router; package btools.router;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import btools.router.OsmNogoPolygon.Point;
public class OsmNogoPolygonTest { public class OsmNogoPolygonTest {
static final int offset_x = 11000000; static final int offset_x = 11000000;
static final int offset_y = 50000000; static final int offset_y = 50000000;
static OsmNogoPolygon polygon; 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[] 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 final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
@ -53,10 +57,14 @@ public class OsmNogoPolygonTest {
@BeforeClass @BeforeClass
public static void setUp() throws Exception { public static void setUp() throws Exception {
polygon = new OsmNogoPolygon(); polygon = new OsmNogoPolygon(true);
for (int i = 0; i<lons.length; i++) { for (int i = 0; i<lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i]),toOsmLat(lats[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 @AfterClass
@ -75,15 +83,23 @@ public class OsmNogoPolygonTest {
double diff = r-r1; double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0); 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 @Test
public void testIsWithin() { public void testIsWithin() {
// for points exactly on the edge of the polygon the result is not the same for all directions. double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
// that doesn't have a major impact on routing though. double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, }; // 1.0, 1.0, 0.5, 0.5, 0.5, boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
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, }; // false, false, false, false, false,
for (int i=0; i<plons.length; i++) { for (int i=0; i<plons.length; i++) {
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i]), toOsmLat(plats[i]))); assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i]), toOsmLat(plats[i])));
@ -91,16 +107,47 @@ public class OsmNogoPolygonTest {
} }
@Test @Test
public void testIntersectsOrIsWithin() { public void testIntersectsPolygon() {
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, }; 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, }; 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, }; 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, }; double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
boolean[] within = { true, false, true, true, true, true, false, true, true, }; boolean[] within = { false, false, false, true, true, true, false, true, true, true };
for (int i=0; i<p0lons.length; i++) { for (int i=0; i<p0lons.length; i++) {
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polygon.intersectsOrIsWithin(toOsmLon(p0lons[i]), toOsmLat(p0lats[i]), toOsmLon(p1lons[i]), toOsmLat(p1lats[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));
}
} }

View file

@ -240,35 +240,38 @@ public class ServerHandler extends RequestHandler {
private List<OsmNodeNamed> readNogoPolygons() private List<OsmNodeNamed> readNogoPolygons()
{ {
String polygons = params.get( "polygons" ); List<OsmNodeNamed> result = new ArrayList<OsmNodeNamed>();
if ( polygons == null ) return null; parseNogoPolygons( params.get("polylines"), result, false );
parseNogoPolygons( params.get("polygons"), result, true );
String[] polygonList = polygons.split("\\|"); return result.size() > 0 ? result : null;
}
List<OsmNodeNamed> nogoPolygonList = new ArrayList<OsmNodeNamed>();
for (int i = 0; i < polygonList.length; i++) private static void parseNogoPolygons(String polygons, List<OsmNodeNamed> result, boolean closed )
{
if ( polygons != null )
{ {
String[] lonLatList = polygonList[i].split(","); String[] polygonList = polygons.split("\\|");
if ( lonLatList.length > 1 ) for (int i = 0; i < polygonList.length; i++)
{ {
OsmNogoPolygon polygon = new OsmNogoPolygon(); String[] lonLatList = polygonList[i].split(",");
for (int j = 0; j < lonLatList.length-1;) if ( lonLatList.length > 1 )
{ {
String slon = lonLatList[j++]; OsmNogoPolygon polygon = new OsmNogoPolygon(closed);
String slat = lonLatList[j++]; for (int j = 0; j < lonLatList.length-1;)
int lon = (int)( ( Double.parseDouble(slon) + 180. ) *1000000. + 0.5); {
int lat = (int)( ( Double.parseDouble(slat) + 90. ) *1000000. + 0.5); String slon = lonLatList[j++];
polygon.addVertex(lon, lat); String slat = lonLatList[j++];
} int lon = (int)( ( Double.parseDouble(slon) + 180. ) *1000000. + 0.5);
if ( polygon.points.size() > 0 ) int lat = (int)( ( Double.parseDouble(slat) + 90. ) *1000000. + 0.5);
{ polygon.addVertex(lon, lat);
polygon.name = ""; }
polygon.isNogo = true; if ( polygon.points.size() > 0 )
polygon.calcBoundingCircle(); {
nogoPolygonList.add(polygon); polygon.calcBoundingCircle();
result.add(polygon);
}
} }
} }
} }
return nogoPolygonList;
} }
} }