Skip to content

Commit 58c42d9

Browse files
authored
Increase latitude accuracy level (#102)
1 parent 5162646 commit 58c42d9

4 files changed

Lines changed: 43 additions & 8 deletions

File tree

.changeset/giant-elephants-pull.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"react-three-map": patch
3+
---
4+
5+
- Improve accuracy at long distances for `coordsToVector3`.
6+
- Improve translation accuracy for `NearCoordinates`, but scale is still ignored.
7+
- Update `earthRadius` from `6378137` to `6371008.8` to match [Mapbox](https://github.com/maplibre/maplibre-gl-js/blob/8ea76118210dd18fa52fdb83f2cbdd1229807346/src/geo/lng_lat.ts#L8) and [Maplibre](https://github.com/maplibre/maplibre-gl-js/blob/main/src/geo/lng_lat.ts#L8).

README.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ import { Canvas, Coordinates } from 'react-three-map'
198198

199199
[![](https://img.shields.io/badge/-demo-%23ff69b4)](https://rodrigohamuy.github.io/react-three-map/?story=multi-coordinates--default)
200200

201-
Same as `Coordinates`, but with an error margin that increases the further you are from the origin.
201+
Same as `Coordinates`, but scale is ignored in exchange of being able to be rendered under the same scene.
202202

203-
Recommended to use at city level distances, but margin errors will be noticeable at country level distances.
203+
Works well at city level distances, but since scale remains unchanged, is not recommended at country level distances.
204204

205-
Check the story to see the difference between the two.
205+
Check the story to see the difference between the two or check #102 for more info.
206206

207207
### useMap
208208

@@ -224,7 +224,7 @@ const Component = () => {
224224

225225
This utility function converts geographic coordinates into a `Vector3Tuple`, which represents a 3D vector in meters.
226226

227-
Similar to `NearCoordinates` it has a relatively good precision at city distances, but is not recommended if your distances are too big.
227+
Similar to `NearCoordinates`, remember that this only updates positions (translation) but that scale is not taken into account, which has an important factor at very long distances (country level).
228228

229229

230230
| Parameter | Description |
@@ -240,7 +240,9 @@ Returns a `Vector3Tuple` representing the 3D position of the point relative to t
240240

241241
This utility function converts a `Vector3Tuple`, which represents a 3D vector in meters, back into geographic coordinates.
242242

243-
It is the inverse of `coordsToVector3` and maintains the same level of precision. It is not recommended for use with very large distances.
243+
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.
244+
245+
Recommended to use at city level distances, but margin errors will be noticeable at country level distances.
244246

245247
| Parameter | Description |
246248
| ------------------------ | --------------------------------------------------------------- |

src/api/coords-to-vector-3.ts

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,40 @@ import { MathUtils, Vector3Tuple } from 'three';
22
import { Coords } from './coords';
33
import { earthRadius } from "../core/earth-radius";
44

5+
6+
const mercatorScaleLookup: { [key: number]: number } = {};
7+
8+
function getMercatorScale(lat: number): number {
9+
const index = Math.round(lat * 1000);
10+
if (mercatorScaleLookup[index] === undefined) {
11+
mercatorScaleLookup[index] = 1 / Math.cos(lat * MathUtils.DEG2RAD);
12+
}
13+
return mercatorScaleLookup[index];
14+
}
15+
16+
export function averageMercatorScale(originLat: number, pointLat: number, steps = 10): number {
17+
let totalScale = 0;
18+
const latStep = (pointLat - originLat) / steps;
19+
for (let i = 0; i <= steps; i++) {
20+
const lat = originLat + latStep * i;
21+
totalScale += getMercatorScale(lat);
22+
}
23+
return totalScale / (steps + 1);
24+
}
25+
526
export function coordsToVector3(point: Coords, origin: Coords): Vector3Tuple {
627
const latitudeDiff = (point.latitude - origin.latitude) * MathUtils.DEG2RAD;
728
const longitudeDiff = (point.longitude - origin.longitude) * MathUtils.DEG2RAD;
829
const altitudeDiff = (point.altitude || 0) - (origin.altitude || 0);
930

1031
const x = longitudeDiff * earthRadius * Math.cos(origin.latitude * MathUtils.DEG2RAD);
1132
const y = altitudeDiff;
12-
const z = -latitudeDiff * earthRadius;
1333

34+
// dynamic step size based on latitude difference. calculate the mercator unit scale at origin
35+
// and the scale average along the line to the point for better accuracy far from origin
36+
const steps = Math.ceil(Math.abs(point.latitude - origin.latitude)) * 100 + 1;
37+
const avgScale = averageMercatorScale(origin.latitude, point.latitude, steps);
38+
39+
const z = ((-latitudeDiff * earthRadius) / getMercatorScale(origin.latitude)) * avgScale;
1440
return [x, y, z] as Vector3Tuple;
15-
}
41+
}

src/core/earth-radius.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const earthRadius = 6378137;
1+
export const earthRadius = 6371008.8;

0 commit comments

Comments
 (0)