Skip to content

Commit

Permalink
Increase latitude accuracy level (#102)
Browse files Browse the repository at this point in the history
  • Loading branch information
hambsch authored Apr 27, 2024
1 parent 5162646 commit 58c42d9
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 8 deletions.
7 changes: 7 additions & 0 deletions .changeset/giant-elephants-pull.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"react-three-map": patch
---

- Improve accuracy at long distances for `coordsToVector3`.
- Improve translation accuracy for `NearCoordinates`, but scale is still ignored.
- 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).
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,11 @@ import { Canvas, Coordinates } from 'react-three-map'

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

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

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

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

### useMap

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

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

Similar to `NearCoordinates` it has a relatively good precision at city distances, but is not recommended if your distances are too big.
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).


| Parameter | Description |
Expand All @@ -240,7 +240,9 @@ 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` and maintains the same level of precision. It is not recommended for use with very large distances.
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.

| Parameter | Description |
| ------------------------ | --------------------------------------------------------------- |
Expand Down
30 changes: 28 additions & 2 deletions src/api/coords-to-vector-3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,40 @@ import { MathUtils, Vector3Tuple } from 'three';
import { Coords } from './coords';
import { earthRadius } from "../core/earth-radius";


const mercatorScaleLookup: { [key: number]: number } = {};

function getMercatorScale(lat: number): number {
const index = Math.round(lat * 1000);
if (mercatorScaleLookup[index] === undefined) {
mercatorScaleLookup[index] = 1 / Math.cos(lat * MathUtils.DEG2RAD);
}
return mercatorScaleLookup[index];
}

export function averageMercatorScale(originLat: number, pointLat: number, steps = 10): number {
let totalScale = 0;
const latStep = (pointLat - originLat) / steps;
for (let i = 0; i <= steps; i++) {
const lat = originLat + latStep * i;
totalScale += getMercatorScale(lat);
}
return totalScale / (steps + 1);
}

export function coordsToVector3(point: Coords, origin: Coords): Vector3Tuple {
const latitudeDiff = (point.latitude - origin.latitude) * MathUtils.DEG2RAD;
const longitudeDiff = (point.longitude - origin.longitude) * MathUtils.DEG2RAD;
const altitudeDiff = (point.altitude || 0) - (origin.altitude || 0);

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

// dynamic step size based on latitude difference. calculate the mercator unit scale at origin
// and the scale average along the line to the point for better accuracy far from origin
const steps = Math.ceil(Math.abs(point.latitude - origin.latitude)) * 100 + 1;
const avgScale = averageMercatorScale(origin.latitude, point.latitude, steps);

const z = ((-latitudeDiff * earthRadius) / getMercatorScale(origin.latitude)) * avgScale;
return [x, y, z] as Vector3Tuple;
}
}
2 changes: 1 addition & 1 deletion src/core/earth-radius.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const earthRadius = 6378137;
export const earthRadius = 6371008.8;

0 comments on commit 58c42d9

Please sign in to comment.