/* 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.geotools.referencing.GeodeticCalculator;
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 static DistanceLibrary distanceLibrary = SphericalDistanceLibrary.getInstance();
/* this is used to calculate angles on a sphere */
private GeodeticCalculator geodeticCalculator;
private DirectionUtils() {
// TODO(flamholz): Is constructing GeodeticCalculator really so
// heavyweight that we need this synchronization?
geodeticCalculator = new GeodeticCalculator();
}
private static synchronized DirectionUtils getInstance() {
if (instance == null) {
instance = new DirectionUtils();
}
return instance;
}
/**
* Returns the azimuth in decimal degrees from (-180° to +180°) between
* Coordinates A and B.
*
* @param a
* @param b
* @return
*/
public static synchronized double getAzimuth(Coordinate a, Coordinate b) {
DirectionUtils utils = getInstance();
utils.geodeticCalculator.setStartingGeographicPoint(a.x, a.y);
utils.geodeticCalculator.setDestinationGeographicPoint(b.x, b.y);
return utils.geodeticCalculator.getAzimuth();
}
/**
* Computes the angle of the last segment of a LineString or MultiLineString
*
* @param geometry
* a LineString or a MultiLineString
* @return
*/
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 (distanceLibrary.fastDistance(coord0, coord1) < minDistance && i >= 0) {
coord0 = line.getCoordinateN(i--);
}
DirectionUtils utils = getInstance();
utils.geodeticCalculator.setStartingGeographicPoint(coord0.x, coord0.y);
utils.geodeticCalculator.setDestinationGeographicPoint(coord1.x, coord1.y);
double az = utils.geodeticCalculator.getAzimuth();
return az * Math.PI / 180;
}
/**
* Computes the angle of the first segment of a LineString or MultiLineString
*
* @param geometry
* a LineString or a MultiLineString
* @return
*/
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 (distanceLibrary.fastDistance(coord0, coord1) < minDistance
&& i < line.getNumPoints()) {
coord1 = line.getCoordinateN(i++);
}
DirectionUtils utils = getInstance();
utils.geodeticCalculator.setStartingGeographicPoint(coord0.x, coord0.y);
utils.geodeticCalculator.setDestinationGeographicPoint(coord1.x, coord1.y);
double az = utils.geodeticCalculator.getAzimuth();
return az * Math.PI / 180;
}
}