/** * Copyright (C) 2008 - 2014 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * - Apache License, version 2.0 * - Apache Software License, version 1.0 * - GNU Lesser General Public License, version 3 * - Mozilla Public License, versions 1.0, 1.1 and 2.0 * - Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * icense version 2 and the aforementioned licenses. * * 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. */ package org.n52.ses.util.geometry; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; /** * Helper class provides geodesic approximation methods. * * E.g. approximate a Great Circle to a LineString representation (the * algorithm uses the intermediate point calculation documented at * {@link http://williams.best.vwh.net/avform.htm#Intermediate}). * * @author matthes rieke * */ public class GeodesicApproximationTools { private static double radTodeg(double n) { return n * Math.PI/180; } private static Coordinate intermediatePoint(Coordinate start, Coordinate end, double fraction) { double lat1 = radTodeg(start.y); double lon1 = radTodeg(start.x); double lat2 = radTodeg(end.y); double lon2 = radTodeg(end.x); double d = 2 * Math.asin( Math.sqrt(Math.pow((Math.sin((lat1 - lat2) / 2)), 2) + Math.cos(lat1) * Math.cos(lat2) * Math.pow(Math.sin((lon1-lon2) / 2), 2))); double a = Math.sin((1 - fraction) * d) / Math.sin(d); double b = Math.sin(fraction * d) / Math.sin(d); double x = a * Math.cos(lat1) * Math.cos(lon1) + b * Math.cos(lat2) * Math.cos(lon2); double y = a * Math.cos(lat1) * Math.sin(lon1) + b * Math.cos(lat2) * Math.sin(lon2); double z = a * Math.sin(lat1) + b * Math.sin(lat2); double lat = Math.atan2(z, Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))) * 180 / Math.PI; double lng = Math.atan2(y, x) * 180 / Math.PI; return new Coordinate(lng, lat); } private static Coordinate[] approximateGreatCircle(Coordinate[] result, int subListStart, int subListEnd) { Coordinate midPoint = intermediatePoint(result[subListStart], result[subListEnd], 0.5); int targetIndex = (subListEnd+subListStart)/2; if (result[targetIndex] == null) { result[targetIndex] = midPoint; approximateGreatCircle(result, subListStart, targetIndex); approximateGreatCircle(result, targetIndex, subListEnd); } return result; } public static LineString approximateGreatCircle(int segmentsPerHalf, Coordinate start, Coordinate end) { Coordinate[] result = new Coordinate[segmentsPerHalf * 2 +1]; result[0] = start; result[result.length-1] = end; return new GeometryFactory().createLineString(approximateGreatCircle(result, 0, result.length-1)); } public static void main(String[] args) { System.out.println(approximateGreatCircle(20, new Coordinate(-87.90381968189912,41.97626011616167), new Coordinate(24.8242444289068, 59.41329527536156)).toText()); } }