diff --git a/src/uk/me/parabola/mkgmap/general/MapShape.java b/src/uk/me/parabola/mkgmap/general/MapShape.java index 8845db3..02f1918 100644 --- a/src/uk/me/parabola/mkgmap/general/MapShape.java +++ b/src/uk/me/parabola/mkgmap/general/MapShape.java @@ -1,5 +1,6 @@ /** * Copyright (C) 2006 Steve Ratcliffe + * Copyright (C) 2009 Ben Konrath - point in shape code * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -96,17 +97,16 @@ public class MapShape extends MapLine {// So top code can link objects from here pointsTemp.add(new Coord(start.getLatitude(), start.getLongitude())); points = pointsTemp; } - - int xtarget = target.getLatitude(); - int ytarget = target.getLongitude(); + int xtarget = target.getLongitude(); + int ytarget = target.getLatitude(); for (int i = 0; i < points.size() - 1; i++) { // apply transformation points to change target point to (0,0) - int x0 = points.get(i).getLatitude() - xtarget; - int y0 = points.get(i).getLongitude() - ytarget; - int x1 = points.get(i+1).getLatitude() - xtarget; - int y1 = points.get(i+1).getLongitude() - ytarget; + int x0 = points.get(i).getLongitude() - xtarget; + int y0 = points.get(i).getLatitude() - ytarget; + int x1 = points.get(i+1).getLongitude() - xtarget; + int y1 = points.get(i+1).getLatitude() - ytarget; // ensure that x0 is smaller than x1 so that we can just check to see if the line intersects the y axis easily if (x0 > x1) { @@ -118,27 +118,33 @@ public class MapShape extends MapLine {// So top code can link objects from here y1 = ytemp; } - // use (0,0) as target because points already transformed + // use (0,0) as the target point because the edge points are already transformed if (isPointOnLine(x0, y0, x1, y1, 0, 0)) return onLineIsInside; // explanation of if statement // - // (x0 < 0 && x1 >= 0): - // are the x values between the y axis? only include points from the right - // with this check so that corners aren't counted twice + // (x0 < 0 && x1 >= 0) + // ~~~~~~~~~~~~~~~~~~~ + // + // We transformed the polygon edge points to make the target point (0,0) and + // we know that x1 >= x0 from 'if (x0 > x1)' above, therefore we only need + // to check if the x values are between the y-axis. We only include points + // from the right (x0 < 0 as opposed to x0 <= 0) with this check so that + // corners aren't counted twice and the count will be odd for this case. // - // (y0 * (x1 - x0) > (y1 - y0) * x0): + // (y0 * (x1 - x0) > (y1 - y0) * x0) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // // from y = mx + b: - // => b = y0 ((y1 - y0) / (x1 - x0)) * x0 - // for intersection, b > 0 - // from y = mx + b, b = y - mx - // => y - mx > 0 - // => y0 - ((y1 - y0) / (x1 - x0)) * x0 > 0 - // => y0 > ((y1 - y0) / (x1 - x0)) * x0 - // from 'if (x0 > x1)', x1 >= x0 - // => x1 - x0 >=0 - // => y0 * (x1 - x0) > (y1 - y0) * x0 + // (1) => b = y0 - ((y1 - y0) / (x1 - x0)) * x0 + // + // for intersection of y-axis and polygon edge, we need b > 0: + // from (1) => y0 - ((y1 - y0) / (x1 - x0)) * x0 > 0 + // (2) => y0 > ((y1 - y0) / (x1 - x0)) * x0 + // from 'if (x0 > x1)' above, we know that x1 >= x0: + // => x1 - x0 >= 0 + // from (2) => y0 * (x1 - x0) > (y1 - y0) * x0 if ((x0 < 0 && x1 >= 0) && (y0 * (x1 - x0)) > ((y1 - y0) * x0)) inside = !inside; } diff --git a/src/uk/me/parabola/mkgmap/main/MapMaker.java b/src/uk/me/parabola/mkgmap/main/MapMaker.java index cd36079..7eaa23f 100644 --- a/src/uk/me/parabola/mkgmap/main/MapMaker.java +++ b/src/uk/me/parabola/mkgmap/main/MapMaker.java @@ -168,7 +168,7 @@ public class MapMaker implements MapProcessor { int pointType = shape.getPoiType(); - // only make a point if the shape has a name and we know what type of point to make + // only make a point if we know what type of point to make if (pointType == 0) continue; @@ -178,7 +178,6 @@ public class MapMaker implements MapProcessor { continue; // check if there is not already a poi in that shape - if(poiMap.findPointInShape(shape, pointType, shapeName) == null) { MapPoint newPoint = new MapPoint(); diff --git a/test/uk/me/parabola/mkgmap/general/PointInShapeTest.java b/test/uk/me/parabola/mkgmap/general/PointInShapeTest.java index 54585e2..e6f9db0 100644 --- a/test/uk/me/parabola/mkgmap/general/PointInShapeTest.java +++ b/test/uk/me/parabola/mkgmap/general/PointInShapeTest.java @@ -1,5 +1,14 @@ /** - * + * Copyright (C) 2009 Ben Konrath + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. */ package uk.me.parabola.mkgmap.general; @@ -19,6 +28,7 @@ import org.junit.Test; public class PointInShapeTest { private MapShape square; + private MapShape unclosedSquare; private MapShape triangle; private MapShape line; private final int squareSize = 4; @@ -38,7 +48,17 @@ public class PointInShapeTest { ); square = new MapShape(); square.setPoints(points); - + + // Unclosed Square + points = Arrays.asList( + new Coord(0, 0), + new Coord(0, squareSize), + new Coord(squareSize, squareSize), + new Coord(squareSize, 0) + ); + unclosedSquare = new MapShape(); + unclosedSquare.setPoints(points); + // Triangle points = Arrays.asList( new Coord(0,0), @@ -143,7 +163,7 @@ public class PointInShapeTest { @Test public void testCornerPointsOutsideSquare() { - // tests above / below corner points, outside squate + // tests above / below corner points, outside square for (Coord cornerpoint : square.getPoints()) { int yadd = cornerpoint.getLongitude() > 0 ? 1 : -1; int x = cornerpoint.getLatitude(); @@ -172,7 +192,6 @@ public class PointInShapeTest { } } - @Test public void testLinePointsInsideTriangle() { // inside triangle, above / below lines @@ -283,4 +302,44 @@ public class PointInShapeTest { assertFalse("point (" + co.getLatitude() + ", " + co.getLongitude() + ") should be outside line", line.contains(co)); } + + @Test + public void testUnclosedShape() { + + // inside unclosed square, 1 unit from corners + List points = Arrays.asList( + new Coord(1, squareSize/2), + new Coord(squareSize/2, squareSize - 1), + new Coord(squareSize - 1, squareSize/2), + new Coord(squareSize/2, 1) + ); + for (Coord coord : points) { + assertTrue("point (" + coord.getLatitude() + ", " + coord.getLongitude() + ") should be inside unclosed square", + unclosedSquare.contains(coord)); + } + + // on the line + points = Arrays.asList( + new Coord(0, squareSize/2), + new Coord(squareSize/2, squareSize), + new Coord(squareSize, squareSize/2), + new Coord(squareSize/2, 0) + ); + for (Coord coord : points) { + assertTrue("point (" + coord.getLatitude() + ", " + coord.getLongitude() + ") should be outside unclosed square", + unclosedSquare.contains(coord)); + } + + // outside unclosed square, 1 unit from line + points = Arrays.asList( + new Coord(-1, squareSize/2), + new Coord(squareSize/2, squareSize + 1), + new Coord(squareSize + 1, squareSize/2), + new Coord(squareSize/2, -1) + ); + for (Coord coord : points) { + assertFalse("point (" + coord.getLatitude() + ", " + coord.getLongitude() + ") should be outside unclosed square", + unclosedSquare.contains(coord)); + } + } }