From a13899b6a7e281524db41f2a0f0b2a95d2cb3e4b Mon Sep 17 00:00:00 2001 From: Joseph Gardner Date: Thu, 11 May 2017 14:40:24 +1200 Subject: [PATCH] Implemented and tested to check if a point is inside or outside of a boundary using a ray tracing algorithm. The function is public static for a GPSCoordinate and takes a list of GPSCoordinates and the comparing coordinate. #story[873] --- .../java/seng302/Model/GPSCoordinate.java | 88 +++++++++++++ .../java/seng302/Model/GPSCoordinateTest.java | 121 ++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 mock/src/test/java/seng302/Model/GPSCoordinateTest.java diff --git a/mock/src/main/java/seng302/Model/GPSCoordinate.java b/mock/src/main/java/seng302/Model/GPSCoordinate.java index 020ac0dd..9dc03fd6 100644 --- a/mock/src/main/java/seng302/Model/GPSCoordinate.java +++ b/mock/src/main/java/seng302/Model/GPSCoordinate.java @@ -1,5 +1,8 @@ package seng302.Model; +import java.util.Comparator; +import java.util.List; + /** * GPS Coordinate for the world map. * Created by esa46 on 15/03/17. @@ -68,5 +71,90 @@ public class GPSCoordinate { result = 31 * result + (int) (temp ^ (temp >>> 32)); return result; } + + /** + * Calculates min and max values and passed it to calculate if coordinate is in the boundary + * @param coordinate coordinate of interest + * @param boundary List of points which make a boundry + * @return true if coordinate is in the boundary + */ + public static boolean isInsideBoundary(GPSCoordinate coordinate, List boundary) { + double maxLatitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude(); + double maxLongitude = boundary.stream().max(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude(); + double minLatitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLatitude)).get().getLatitude(); + double minLongitude = boundary.stream().min(Comparator.comparingDouble(GPSCoordinate::getLongitude)).get().getLongitude(); + return isInsideBoundary(coordinate, boundary, new GPSCoordinate(minLatitude, minLongitude), + new GPSCoordinate(maxLatitude, maxLongitude)); + } + + /** + * Calculates if the coordinate is in the boundary + * @param coordinate coordinate of interest + * @param boundary List of points which make a boundary + * @param minValues max GPS + * @param maxValues min GPS + * @return true if coordinate is in the boundary + */ + public static boolean isInsideBoundary(GPSCoordinate coordinate, List boundary, GPSCoordinate minValues, GPSCoordinate maxValues) { + double minLat = minValues.getLatitude(); + double minLon = minValues.getLongitude(); + double maxLat = maxValues.getLatitude(); + double maxLon = maxValues.getLongitude(); + double coordinateLat = coordinate.getLatitude(); + double coordinateLon = coordinate.getLongitude(); + int length = boundary.size(); + + boolean inside = false; + + // End computation early + if (coordinateLat <= minLat || coordinateLat >= maxLat || coordinateLon <= minLon || coordinateLon >= maxLon) { + return false; + } + + // Check if inside using ray casting algorithm + for (int i = 0, j = length - 1; i < length; j = i++) { + if (intersects(boundary.get(i), boundary.get(j), coordinate)) { + inside = !inside; + } + + + } + return inside; + } + + /** + * Helper function to find if a point is in a boundary + * @param boundaryA + * @param boundaryB + * @param coordinate + * @return true if a line from the point intersects the two boundary points + */ + private static boolean intersects(GPSCoordinate boundaryA, GPSCoordinate boundaryB, GPSCoordinate coordinate) { + double boundaryALat = boundaryA.getLatitude(); + double boundaryALon = boundaryA.getLongitude(); + double boundaryBLat = boundaryB.getLatitude(); + double boundaryBLon = boundaryB.getLongitude(); + double coordinateLat = coordinate.getLatitude(); + double coordinateLon = coordinate.getLongitude(); + + if (boundaryALat > boundaryBLat) { + return intersects(boundaryB, boundaryA, coordinate); + } + if (coordinateLat == boundaryALat || coordinateLat == boundaryBLat) { + // Move coordinate off intersection line + coordinateLat += 1e-9; + } + if (coordinateLat > boundaryBLat || coordinateLat < boundaryALat || coordinateLon > Double.max(boundaryALon, boundaryBLon)) { + return false; + } + if (coordinateLon < Double.min(boundaryALon, boundaryBLon)) { + return true; + } + + double aRatio = (coordinateLat - boundaryALat) / (coordinateLon - boundaryALon); + double bRatio = (coordinateLat - boundaryBLat) / (coordinateLon - boundaryBLon); + return aRatio >= bRatio; + + } } diff --git a/mock/src/test/java/seng302/Model/GPSCoordinateTest.java b/mock/src/test/java/seng302/Model/GPSCoordinateTest.java new file mode 100644 index 00000000..be0dafea --- /dev/null +++ b/mock/src/test/java/seng302/Model/GPSCoordinateTest.java @@ -0,0 +1,121 @@ +package seng302.Model; + +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.TestCase.assertTrue; +import static junit.framework.TestCase.assertFalse; + +/** + * Created by jjg64 on 11/05/17. + */ +public class GPSCoordinateTest { + List boundary; + + @Before + public void init() { + boundary = new ArrayList<>(); + } + + /** + * ------- + * | | + * | * | + * ------- + */ + @Test + public void insideSquareTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(10, 0)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(0, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(2, 8); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertTrue(inside); + } + + /** + * ------- + * | | + * * | | + * ------- + */ + @Test + public void outsideSquareTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(10, 0)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(0, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(-2, 8); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertFalse(inside); + } + + /** + * ------- + * | | + * | | + * *------ + */ + @Test + public void edgeSquareTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(10, 0)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(0, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(0, 0); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertFalse(inside); + } + + @Test + public void insideShapeWithObtuseAnglesTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(4, 4)); + boundary.add(new GPSCoordinate(7, 2)); + boundary.add(new GPSCoordinate(9, 5)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(6, 6)); + boundary.add(new GPSCoordinate(2, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(5, 5); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertTrue(inside); + } + + @Test + public void outsideShapeWithObtuseAnglesTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(4, 4)); + boundary.add(new GPSCoordinate(7, 2)); + boundary.add(new GPSCoordinate(9, 5)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(6, 6)); + boundary.add(new GPSCoordinate(2, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(4, 3); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertFalse(inside); + } + + @Test + public void edgeOfShapeWithObtuseAnglesTest() { + boundary.add(new GPSCoordinate(0, 0)); + boundary.add(new GPSCoordinate(4, 4)); + boundary.add(new GPSCoordinate(7, 2)); + boundary.add(new GPSCoordinate(9, 5)); + boundary.add(new GPSCoordinate(10, 10)); + boundary.add(new GPSCoordinate(6, 6)); + boundary.add(new GPSCoordinate(2, 10)); + + GPSCoordinate coordinate = new GPSCoordinate(2, 10); + boolean inside = GPSCoordinate.isInsideBoundary(coordinate, boundary); + assertFalse(inside); + } +}