/// <summary> /// Encodes a coordinate on the sphere to the corresponding icosahedral face and /// containing 2D hex coordinates relative to that face center. /// </summary> /// <param name="g">The spherical coordinates to encode.</param> /// <param name="res">The desired H3 resolution for the encoding.</param> /// <returns> /// Tuple /// Item1: The resulting face /// Item2: The 2D hex coordinates of the cell containing the point. /// </returns> /// <!-- /// faceijk.c /// void _geoToHex2d /// --> public static (int, Vec2d) ToHex2d(this GeoCoord g, int res) { var v3d = g.ToVec3d(); var newFace = 0; // determine the icosahedron face decimal sqd = v3d.PointSquareDistance(Constants.FaceIjk.FaceCenterPoint[0]); for (var f = 1; f < Constants.H3.NUM_ICOSA_FACES; f++) { decimal sqdT = v3d.PointSquareDistance(Constants.FaceIjk.FaceCenterPoint[f]); if (!(sqdT < sqd)) { continue; } newFace = f; sqd = sqdT; } // cos(r) = 1 - 2 * sin^2(r/2) = 1 - 2 * (sqd / 4) = 1 - sqd/2 decimal r = DecimalEx.ACos(1 - sqd / 2.0m); if (r < Constants.H3.EPSILON) { return(newFace, new Vec2d()); } // now have face and r, now find CCW theta from CII i-axis decimal theta = ( Constants.FaceIjk.FaceAxesAzRadsCii[newFace, 0] - Constants.FaceIjk.FaceCenterGeo[newFace].AzimuthRadiansTo(g) .NormalizeRadians() ).NormalizeRadians(); // adjust theta for Class III (odd resolutions) if (res.IsResClassIii()) { theta = (theta - Constants.H3.M_AP7_ROT_RADS).NormalizeRadians(); } // perform gnomonic scaling of r r = DecimalEx.Tan(r); // scale for current resolution length u r /= Constants.H3.RES0_U_GNOMONIC; for (var i = 0; i < res; i++) { r *= Constants.FaceIjk.MSqrt7; } // we now have (r, theta) in hex2d with theta ccw from x-axes // convert to local x,y return(newFace, new Vec2d ( r * DecimalEx.Cos(theta), r * DecimalEx.Sin(theta) )); }
public void Test(decimal d, decimal expected, decimal tolerance) { tolerance = Helper.GetScaledTolerance(expected, (int)tolerance, true); Assert.That(DecimalEx.ACos(d), Is.EqualTo(expected).Within(tolerance)); }