//Dstl (c) Crown Copyright 2017 package uk.gov.dstl.common.geo.osgb; /** * <b>Convert between LatLon and Cartesian coordinate systems</b> * <p> * This code uses an approximate Helmert transformation, with an error of up to 5 metres (both horizontally and vertically) * Equations taken from http://www.ordnancesurvey.co.uk/docs/support/guide-coordinate-systems-great-britain.pdf * * */ public class CartesianConversion { private CartesianConversion(){ //Utility class - private constructor } /** * @param inputCoordinates Array of coordinates [lat, lon, ellipsoidHeight] to convert * @param a Semi-major axis of ellipsoid * @param b Semi-minor axis of the ellipsoid * @return Array of cartesian coordinates [x, y, z] */ public static double[] fromLatLon(double[] inputCoordinates, double a, double b){ double lat = Math.toRadians(inputCoordinates[0]); double lon = Math.toRadians(inputCoordinates[1]); double height = inputCoordinates[2]; double e2 = (Math.pow(a, 2) - Math.pow(b, 2))/Math.pow(a, 2); double v = a / Math.sqrt(1 - e2*Math.pow(Math.sin(lat), 2)); double x = (v + height)*Math.cos(lat)*Math.cos(lon); double y = (v + height)*Math.cos(lat)*Math.sin(lon); double z = ((1 - e2)*v + height)*Math.sin(lat); return new double[]{x, y, z}; } /** * @param inputCoordinates Array of cartesian coordinates [x, y, z] to convert * @param a Semi-major axis of ellipsoid * @param b Semi-minor axis of the ellipsoid * @param precision Precision to calculate the latitude to * @return Array of coordinates [lat, lon, ellipsoidHeight] */ public static double[] toLatLon(double[] inputCoordinates, double a, double b, double precision){ double x = inputCoordinates[0]; double y = inputCoordinates[1]; double z = inputCoordinates[2]; double e2 = (Math.pow(a, 2) - Math.pow(b, 2))/Math.pow(a, 2); double lon = Math.atan(y / x); double p = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); double lat = Math.atan(z / p*(1 - e2)); double v = 0; double delta = 2*precision; while(delta > precision){ v = a / Math.sqrt(1 - e2*Math.pow(Math.sin(lat), 2)); double newLat = Math.atan((z + e2*v*Math.sin(lat))/p); delta = Math.abs(Math.toDegrees(lat - newLat)); lat = newLat; } double height = (p/Math.cos(lat)) - v; return new double[]{Math.toDegrees(lat), Math.toDegrees(lon), height}; } /** * @param inputCoordinates Array of coordinates [x, y, z] to transform using the specified transformation parameters * @param tX Translation along the x axis (in metres) * @param tY Translation along the y axis (in metres) * @param tZ Translation along the z azies (in metres) * @param s Scale factor * @param rX Rotation about the x axis (in radians) * @param rY Rotation about the y axis (in radians) * @param rZ Rotation about the z axis (in radians) * @return Array of coordinates [x, y, z] that have been transformed */ public static double[] helmertTransformation(double[] inputCoordinates, double tX, double tY, double tZ, double s, double rX, double rY, double rZ){ double aX = inputCoordinates[0]; double aY = inputCoordinates[1]; double aZ = inputCoordinates[2]; double bX = tX + ((1 + s)*aX) + (-rZ*aY) + (rY*aZ); double bY = tY + (rZ*aX) + ((1 + s)*aY) + (-rX*aZ); double bZ = tZ + (-rY*aX) + (rX*aY) + ((1 + s)*aZ); return new double[]{bX, bY, bZ}; } }