/* * This is part of Geomajas, a GIS framework, http://www.geomajas.org/. * * Copyright 2008-2015 Geosparc nv, http://www.geosparc.com/, Belgium. * * The program is available in open source according to the GNU Affero * General Public License. All contributions in this program are covered * by the Geomajas Contributors License Agreement. For full licensing * details, see LICENSE.txt in the project root. */ package org.geomajas.gwt.client.spatial; import org.geomajas.geometry.Coordinate; import org.geomajas.gwt.client.spatial.geometry.Geometry; import org.geomajas.gwt.client.spatial.geometry.LineString; import org.geomajas.gwt.client.spatial.geometry.LinearRing; import org.geomajas.gwt.client.spatial.geometry.MultiLineString; import org.geomajas.gwt.client.spatial.geometry.MultiPolygon; import org.geomajas.gwt.client.spatial.geometry.Point; import org.geomajas.gwt.client.spatial.geometry.Polygon; /** * General math library. * * @author Pieter De Graef */ public final class Mathlib { public static final double ZERO = 0.00001; /** * Calculates whether or not 2 line-segments intersect. * * @param c1 * First coordinate of the first line-segment. * @param c2 * Second coordinate of the first line-segment. * @param c3 * First coordinate of the second line-segment. * @param c4 * Second coordinate of the second line-segment. * @return Returns true or false. */ public static boolean lineIntersects(Coordinate c1, Coordinate c2, Coordinate c3, Coordinate c4) { LineSegment ls1 = new LineSegment(c1, c2); LineSegment ls2 = new LineSegment(c3, c4); return ls1.intersects(ls2); } /** * Calculates the intersection point of 2 lines. * * @param c1 * First coordinate of the first line. * @param c2 * Second coordinate of the first line. * @param c3 * First coordinate of the second line. * @param c4 * Second coordinate of the second line. * @return Returns a coordinate. */ public static Coordinate lineIntersection(Coordinate c1, Coordinate c2, Coordinate c3, Coordinate c4) { LineSegment ls1 = new LineSegment(c1, c2); LineSegment ls2 = new LineSegment(c3, c4); return ls1.getIntersection(ls2); } /** * Calculates the intersection point of 2 line segments. * * @param c1 * Start point of the first line segment. * @param c2 * End point of the first line segment. * @param c3 * Start point of the second line segment. * @param c4 * End point of the second line segment. * @return Returns a coordinate or null if not a single intersection point. */ public Coordinate lineSegmentIntersection(Coordinate c1, Coordinate c2, Coordinate c3, Coordinate c4) { LineSegment ls1 = new LineSegment(c1, c2); LineSegment ls2 = new LineSegment(c3, c4); return ls1.getIntersectionSegments(ls2); } /** * Distance between 2 points. * * @param c1 * First coordinate * @param c2 * Second coordinate */ public static double distance(Coordinate c1, Coordinate c2) { double a = c1.getX() - c2.getX(); double b = c1.getY() - c2.getY(); return Math.sqrt(a * a + b * b); } /** * Distance between a point and a line. * * @param c1 * First coordinate of the line. * @param c2 * Second coordinate of the line. * @param c3 * Coordinate to calculate distance to line from. */ public static double distance(Coordinate c1, Coordinate c2, Coordinate c3) { LineSegment ls = new LineSegment(c1, c2); return ls.distance(c3); } /** * Does a certain coordinate touch a given geometry? * * @param geometry * The geometry to check against. * @param coordinate * The position to check. * @return Returns true if the coordinate touches the geometry. */ public static boolean touches(Geometry geometry, Coordinate coordinate) { if (geometry instanceof MultiPolygon) { for (int i = 0; i < geometry.getNumGeometries(); i++) { if (touches(geometry.getGeometryN(i), coordinate)) { return true; } } return false; } else if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; if (touchesLineString(polygon.getExteriorRing(), coordinate)) { return true; } for (int n = 0; n < polygon.getNumInteriorRing(); n++) { if (touchesLineString(polygon.getInteriorRingN(n), coordinate)) { return true; } } return false; } else if (geometry instanceof MultiLineString) { for (int i = 0; i < geometry.getNumGeometries(); i++) { if (touchesLineString((LineString) geometry.getGeometryN(i), coordinate)) { return true; } } return false; } else if (geometry instanceof LineString) { return touchesLineString((LineString) geometry, coordinate); } else if (geometry instanceof Point) { Coordinate c = geometry.getCoordinates()[0]; Vector2D v1 = new Vector2D(c.getX(), c.getY()); Vector2D v2 = new Vector2D(coordinate.getX(), coordinate.getY()); return (v1.distance(v2) < ZERO); } return false; } /** * Is a certain coordinate within a given geometry? * * @param geometry * The geometry to check against. Only if it has {@link LinearRing}'s, can the coordinate be inside. * @param coordinate * The position that is possibly within the geometry. * @return Returns true if the coordinate is within the geometry. */ public static boolean isWithin(Geometry geometry, Coordinate coordinate) { if (geometry instanceof MultiPolygon) { for (int i = 0; i < geometry.getNumGeometries(); i++) { if (isWithin(geometry.getGeometryN(i), coordinate)) { return true; } } } else if (geometry instanceof Polygon) { Polygon polygon = (Polygon) geometry; if (isWithinRing(polygon.getExteriorRing(), coordinate)) { for (int i = 0; i < polygon.getNumInteriorRing(); i++) { if (isWithinRing(polygon.getInteriorRingN(i), coordinate)) { return false; } } return true; } } else if (geometry instanceof LinearRing) { return isWithinRing((LinearRing) geometry, coordinate); } return false; } //------------------------------------------------------------------------- // Private methods: //------------------------------------------------------------------------- /** * @private */ private static boolean touchesLineString(LineString lineString, Coordinate coordinate) { // First loop over the end-points. This will be the most common case, certainly if we take snapping into // account... for (int i = 0; i < lineString.getNumPoints(); i++) { if (lineString.getCoordinateN(i).equals(coordinate)) { return true; } } // Now loop over the edges: for (int i = 1; i < lineString.getNumPoints(); i++) { LineSegment edge = new LineSegment(lineString.getCoordinateN(i - 1), lineString.getCoordinateN(i)); if (edge.distance(coordinate) < ZERO) { return true; } } return false; } /** * @private */ private static boolean isWithinRing(LinearRing linearRing, Coordinate coordinate) { int counter = 0; int num = linearRing.getNumPoints(); Coordinate c1 = linearRing.getCoordinateN(0); for (int i = 1; i <= num; i++) { Coordinate c2 = linearRing.getCoordinateN(i % num); // this way, it should work to concatenate all ring // coordinate arrays of a polygon....(if they all have an equal number of coordinates) if (coordinate.getY() > Math.min(c1.getY(), c2.getY())) { // some checks to try and avoid the expensive // intersect calculation. if (coordinate.getY() <= Math.max(c1.getY(), c2.getY())) { if (coordinate.getX() <= Math.max(c1.getX(), c2.getX())) { if (c1.getY() != c2.getY()) { double xIntercept = (coordinate.getY() - c1.getY()) * (c2.getX() - c1.getX()) / (c2.getY() - c1.getY()) + c1.getX(); if (c1.getX() == c2.getX() || coordinate.getX() <= xIntercept) { counter++; } } } } } c1 = c2; } if (counter % 2 == 0) { return false; } return true; } }