package de.blau.android.util.mapbox.turf; import java.util.ArrayList; import java.util.List; import de.blau.android.util.mapbox.geojson.MultiPolygon; import de.blau.android.util.mapbox.geojson.Point; import de.blau.android.util.mapbox.geojson.Polygon; import de.blau.android.util.mapbox.models.Position; /** * Class contains methods that can determine if points lie within a polygon or not. * * @see <a href="http://turfjs.org/docs/">Turf documentation</a> * @since 1.3.0 */ class TurfJoins { /** * Takes a {@link Position} and a List of Positions making up a polygon and determines if the * point resides inside the polygon. The polygon can be convex or concave. The function accounts * for holes. * * @param point input position. * @param polygon input list of positions making up the polygon. * @return True if the Point is inside the Polygon; false if the Point is not inside the Polygon. * @throws TurfException Signals that a Turf exception of some sort has occurred. * @see <a href="http://turfjs.org/docs/#inside">Turf Inside documentation</a> * @since 1.3.0 */ public static boolean inside(Position point, List<Position> polygon) throws TurfException { List<List<Position>> polygons = new ArrayList<>(); polygons.add(polygon); return inside(Point.fromCoordinates(point), Polygon.fromCoordinates(polygons)); } /** * Takes a {@link Point} and a {@link Polygon} and determines if the point resides inside the * polygon. The polygon can be convex or concave. The function accounts for holes. * * @param point input point. * @param polygon input polygon. * @return True if the Point is inside the Polygon; false if the Point is not inside the Polygon. * @throws TurfException Signals that a Turf exception of some sort has occurred. * @see <a href="http://turfjs.org/docs/#inside">Turf Inside documentation</a> * @since 1.3.0 */ public static boolean inside(Point point, Polygon polygon) throws TurfException { // This API needs to get better List<List<Position>> coordinates = polygon.getCoordinates(); ArrayList<List<List<Position>>> multiCoordinates = new ArrayList<>(); multiCoordinates.add(coordinates); return inside(point, MultiPolygon.fromCoordinates(multiCoordinates)); } /** * Takes a {@link Point} and a {@link MultiPolygon} and determines if the point resides inside * the polygon. The polygon can be convex or concave. The function accounts for holes. * * @param point input point. * @param polygon input multipolygon. * @return True if the Point is inside the MultiPolygon; false if the Point is not inside the MultiPolygon. * @throws TurfException Signals that a Turf exception of some sort has occurred. * @see <a href="http://turfjs.org/docs/#inside">Turf Inside documentation</a> * @since 1.3.0 */ private static boolean inside(Point point, MultiPolygon polygon) throws TurfException { Position pt = TurfInvariant.getCoord(point); List<List<List<Position>>> polys = polygon.getCoordinates(); boolean insidePoly = false; for (int i = 0; i < polys.size() && !insidePoly; i++) { // check if it is in the outer ring first if (inRing(pt, polys.get(i).get(0))) { boolean inHole = false; int k = 1; // check for the point in any of the holes while (k < polys.get(i).size() && !inHole) { if (inRing(pt, polys.get(i).get(k))) { inHole = true; } k++; } if (!inHole) { insidePoly = true; } } } return insidePoly; } // pt is [x,y] and ring is [[x,y], [x,y],..] private static boolean inRing(Position pt, List<Position> ring) { boolean isInside = false; for (int i = 0, j = ring.size() - 1; i < ring.size(); j = i++) { double xi = ring.get(i).getLongitude(); double yi = ring.get(i).getLatitude(); double xj = ring.get(j).getLongitude(); double yj = ring.get(j).getLatitude(); boolean intersect = ( (yi > pt.getLatitude()) != (yj > pt.getLatitude())) && (pt.getLongitude() < (xj - xi) * (pt.getLatitude() - yi) / (yj - yi) + xi); if (intersect) { isInside = !isInside; } } return isInside; } }