class GeographicBody3D extends AbstractGeographicProjection {

    public static LatLonAlt greatCircleEndPosition(LatLonAlt start, double greatCircleAzimuthR, double pathLengthR)
    {
        if (MathUtil.isZero(pathLengthR))
        {
            return start;
        }

        double lat = Math.toRadians(start.getLatD());
        double lon = Math.toRadians(start.getLonD());

        double sinLat = Math.sin(lat);
        double cosLat = Math.cos(lat);
        double sinDistance = Math.sin(pathLengthR);
        double cosDistance = Math.cos(pathLengthR);
        double sinAzimuth = Math.sin(greatCircleAzimuthR);
        double cosAzimuth = Math.cos(greatCircleAzimuthR);

        double sinDcosA = sinDistance * cosAzimuth;

        // Taken from "Map Projections - A Working Manual", page 31, equation
        // 5-5 and 5-6.
        double endLatRadians = Math.asin(sinLat * cosDistance + cosLat * sinDcosA);
        double endLonRadians = lon + Math.atan2(sinDistance * sinAzimuth, cosLat * cosDistance - sinLat * sinDcosA);

        if (Double.isNaN(endLatRadians) || Double.isNaN(endLonRadians))
        {
            LOGGER.error("GreatCircleEndPosition calculation produced undefined results.");
            return start;
        }

        // Check that longitude is always in the range -180 to 180
        if (Math.toDegrees(endLonRadians) > 180.)
        {
            endLonRadians -= Math.toRadians(360);
        }
        if (Math.toDegrees(endLonRadians) < -180.)
        {
            endLonRadians += Math.toRadians(360);
        }

        return LatLonAlt.createFromDegrees(Math.toDegrees(endLatRadians), Math.toDegrees(endLonRadians));
    }

    public  GeographicBody3D(String name, Shape shape);

}