diff --git a/README.md b/README.md index 744456b..7afe798 100644 --- a/README.md +++ b/README.md @@ -241,9 +241,7 @@ Returns a `Vector3Tuple` representing the 3D position of the point relative to t This utility function converts a `Vector3Tuple`, which represents a 3D vector in meters, back into geographic coordinates. -It is the inverse of `coordsToVector3` but it does not have a good level of precision at long distances since we haven't reverse engineered #102 fix yet. - -Recommended to use at city level distances, but margin errors will be noticeable at country level distances. +It is the inverse of `coordsToVector3` and iteratively resolves the latitude to account for Mercator scaling. This improves accuracy when converting vectors that are far from the origin. | Parameter | Description | | ------------------------ | --------------------------------------------------------------- | diff --git a/src/api/vector-3-to-coords.ts b/src/api/vector-3-to-coords.ts index 48d8923..928a47f 100644 --- a/src/api/vector-3-to-coords.ts +++ b/src/api/vector-3-to-coords.ts @@ -1,10 +1,26 @@ import { MathUtils, Vector3Tuple } from "three"; import { Coords } from "./coords"; import { earthRadius } from "../core/earth-radius"; +import { averageMercatorScale } from "./coords-to-vector-3"; export function vector3ToCoords(position: Vector3Tuple, origin: Coords): Coords { const [x, y, z] = position; - const latitude = origin.latitude + (-z / earthRadius) * MathUtils.RAD2DEG; + + const originScale = 1 / Math.cos(origin.latitude * MathUtils.DEG2RAD); + + // initial guess ignoring mercator scale correction + let latDiff = (-z / earthRadius); + + // refine latitude difference using the same scaling as `coordsToVector3` + for (let i = 0; i < 5; i++) { + const lat = origin.latitude + latDiff * MathUtils.RAD2DEG; + const avgScale = averageMercatorScale(origin.latitude, lat); + const newLatDiff = (-z * originScale) / (earthRadius * avgScale); + if (Math.abs(newLatDiff - latDiff) < 1e-12) break; + latDiff = newLatDiff; + } + + const latitude = origin.latitude + latDiff * MathUtils.RAD2DEG; const longitude = origin.longitude + (x / earthRadius) * MathUtils.RAD2DEG / Math.cos(origin.latitude * MathUtils.DEG2RAD); const altitude = (origin.altitude || 0) + y; const coords: Coords = { latitude, longitude, altitude };