/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.common.geometry; import org.apache.commons.math3.util.FastMath; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; public class DirectionUtils { public static DirectionUtils instance; private DirectionUtils() { } /** * Returns the approximate azimuth from coordinate A to B in decimal degrees clockwise from North, * in the range (-180° to +180°). The computation is exact for small delta between A and B. */ public static synchronized double getAzimuth(Coordinate a, Coordinate b) { double cosLat = FastMath.cos(FastMath.toRadians((a.y + b.y) / 2.0)); double dY = (b.y - a.y); // in degrees, we do not care about the units double dX = (b.x - a.x) * cosLat; // same if (Math.abs(dX) < 1e-10 && Math.abs(dY) < 1e-10) return 180; double az = FastMath.toDegrees(FastMath.atan2(dX, dY)); return az; } /** * Computes the angle of the last segment of a LineString or MultiLineString in radians clockwise from North * in the range (-PI, PI). * @param geometry a LineString or a MultiLineString */ public static synchronized double getLastAngle(Geometry geometry) { LineString line; if (geometry instanceof MultiLineString) { line = (LineString) geometry.getGeometryN(geometry.getNumGeometries() - 1); } else { assert geometry instanceof LineString; line = (LineString) geometry; } int numPoints = line.getNumPoints(); Coordinate coord0 = line.getCoordinateN(numPoints - 2); Coordinate coord1 = line.getCoordinateN(numPoints - 1); int i = numPoints - 3; int minDistance = 10; // Meters while (SphericalDistanceLibrary.fastDistance(coord0, coord1) < minDistance && i >= 0) { coord0 = line.getCoordinateN(i--); } double az = getAzimuth(coord0, coord1); return az * Math.PI / 180; } /** * Computes the angle of the first segment of a LineString or MultiLineString in radians clockwise from North * in the range (-PI, PI). * @param geometry a LineString or a MultiLineString */ public static synchronized double getFirstAngle(Geometry geometry) { LineString line; if (geometry instanceof MultiLineString) { line = (LineString) geometry.getGeometryN(0); } else { assert geometry instanceof LineString; line = (LineString) geometry; } Coordinate coord0 = line.getCoordinateN(0); Coordinate coord1 = line.getCoordinateN(1); int i = 2; int minDistance = 10; // Meters while (SphericalDistanceLibrary.fastDistance(coord0, coord1) < minDistance && i < line.getNumPoints()) { coord1 = line.getCoordinateN(i++); } double az = getAzimuth(coord0, coord1); return az * Math.PI / 180; } }