/// <summary> /// Convert a latitude and longitude into Cartesian coordinates /// </summary> /// <param name="lon">Geodetic longitude in radians</param> /// <param name="lat">Geodetic latitude in radians</param> /// <param name="geodeticHeight">Above ellipsoid in metres</param> /// <returns>Cartesian position on the spheriod in metres</returns> public DVec3 ToVector(double lon, double lat, double geodeticHeight) { //work out normal position on surface of unit sphere using Euler formula and lat/lon //The latitude in this case is a geodetic latitude, so it's defined as the angle between the equatorial plane and the surface normal. //This is why the following works. double CosLat = Math.Cos(lat); DVec3 n = new DVec3(CosLat * Math.Cos(lon), CosLat * Math.Sin(lon), Math.Sin(lat)); //so (nx,ny,nz) is the geodetic surface normal i.e. the normal to the surface at lat,lon //using |ns|=gamma * ns, find gamma ( where |ns| is normalised ns) //with ns=Xs/a^2 i + Ys/b^2 j + Zs/c^2 k //equation of ellipsoid Xs^2/a^2 + Ys^2/b^2 + Zs^2/c^2 = 1 //So, basically, I've got two equations for the geodetic surface normal that are related by a linear factor gamma DVec3 k = new DVec3(a2, b2, c2); k = k * n; double gamma = Math.Sqrt(k.x * n.x + k.y * n.y + k.z * n.z); DVec3 rSurface = new DVec3(k.x / gamma, k.y / gamma, k.z / gamma); //NOTE: you do rSurface = rSurface + (geodetic.height * n) to add the height on if you need it rSurface = rSurface + new DVec3(geodeticHeight * n.x, geodeticHeight * n.y, geodeticHeight * n.z); //Relative to Centre rendering: rSurface.x -= CX; rSurface.y -= CY; rSurface.z -= CZ; return(rSurface); }
/// <summary> /// Convert a point on the surface of the ellipsoid into the Geodetic surface normal at that point /// </summary> DVec3 GeodeticSurfaceNormal(DVec3 P) { DVec3 Normal = new DVec3(P.x * ra2, P.y * rb2, P.z * rc2); Normal = DVec3.normalize(Normal); return(Normal); }
/// <summary> /// Scale a geocentric point to the surface of the ellipsoid (along the geocentric normal) /// </summary> DVec3 ScaleToGeocentricSurface(DVec3 P) { double beta = 1.0 / Math.Sqrt( (P.x * P.x) * ra2 + (P.y * P.y) * rb2 + (P.z * P.z) * rc2 ); return(beta * P); }
/// <summary> /// Cozzi and Ring method using Newton Raphson from 3D Engine Design for Virtual Globes book /// </summary DVec3 ScaleToGeodeticSurface(DVec3 P) { double beta = 1.0 / Math.Sqrt( (P.x * P.x) * ra2 + (P.y * P.y) * rb2 + (P.z * P.z) * rc2 ); double n = DVec3.length(new DVec3(beta * P.x * ra2, beta * P.y * rb2, beta * P.z * rc2)); double alpha = (1.0 - beta) * (DVec3.length(P) / n); double x2 = P.x * P.x; double y2 = P.y * P.y; double z2 = P.z * P.z; double da = 0.0; double db = 0.0; double dc = 0.0; double s = 0.0; double dSdA = 1.0; do { alpha -= (s / dSdA); da = 1.0 + (alpha * ra2); db = 1.0 + (alpha * rb2); dc = 1.0 + (alpha * rc2); double da2 = da * da; double db2 = db * db; double dc2 = dc * dc; double da3 = da * da2; double db3 = db * db2; double dc3 = dc * dc2; s = x2 / (a2 * da2) + y2 / (b2 * db2) + z2 / (c2 * dc2) - 1.0; dSdA = -2.0 * (x2 / (a4 * da3) + y2 / (b4 * db3) + z2 / (c4 * dc3)); } while (Math.Abs(s) > 1e-10); return(new DVec3(P.x / da, P.y / db, P.z / dc)); }
/// <summary> /// Normalise the vector and return the unit vector pointing in the same direction /// </summary> /// <param name="v"></param> /// <returns></returns> public static DVec3 normalize(DVec3 v) { double mag = DVec3.length(v); return(new DVec3(v.x / mag, v.y / mag, v.z / mag)); }
/// <summary> /// Scalar length, or magnitude of vector /// </summary> /// <param name="v"></param> /// <returns>A double value which is the length along the vector</returns> public static double length(DVec3 v) { return(Math.Sqrt(v.x * v.x + v.y * v.y + v.z * v.z)); }