Skip to content

Commit

Permalink
Use lon, lat order instead of lat, lon
Browse files Browse the repository at this point in the history
This change will bring the library in line with GeoJSON, Mapbox, and
Turf.js. It's also more intuitive for most tech people to have the
longitude first, as it can be thought of as the x coordinate, which
usually comes before y.
  • Loading branch information
ezzatron committed May 31, 2024
1 parent 74c3444 commit c30b548
Show file tree
Hide file tree
Showing 17 changed files with 66 additions and 66 deletions.
44 changes: 22 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,8 @@ test("Example 1", () => {
// Step 1
//
// First, the given latitudes and longitudes are converted to n-vectors:
const a = fromGeodeticCoordinates(radians(aLat), radians(aLon));
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
const a = fromGeodeticCoordinates(radians(aLon), radians(aLat));
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));

// Step 2
//
Expand Down Expand Up @@ -228,7 +228,7 @@ test("Example 2", () => {
const [c, cDepth] = destination(b, bcE, bDepth, e);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(c);
const [lon, lat] = toGeodeticCoordinates(c);
const height = -cDepth;

expect(degrees(lat)).toBeCloseTo(53.32637826433107, 13);
Expand Down Expand Up @@ -284,7 +284,7 @@ test("Example 3", () => {
// Step 2
//
// Find latitude, longitude and height:
const [lat, lon] = toGeodeticCoordinates(b);
const [lon, lat] = toGeodeticCoordinates(b);
const height = -bDepth;

expect(degrees(lat)).toBeCloseTo(5.685075734513181, 14);
Expand Down Expand Up @@ -327,7 +327,7 @@ test("Example 4", () => {
// SOLUTION:

// Step 1: First, the given latitude and longitude are converted to n-vector:
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));

// Step 2: Convert to an ECEF-vector:
const pb = toECEF(b, -bHeight);
Expand Down Expand Up @@ -370,8 +370,8 @@ test("Example 5", () => {
// PROBLEM:

// Given two positions A and B as n-vectors:
const a = fromGeodeticCoordinates(radians(88), radians(0));
const b = fromGeodeticCoordinates(radians(89), radians(-170));
const a = fromGeodeticCoordinates(radians(0), radians(88));
const b = fromGeodeticCoordinates(radians(-170), radians(89));

// Find the surface distance (i.e. great circle distance). The heights of A
// and B are not relevant (i.e. if they do not have zero height, we seek the
Expand Down Expand Up @@ -430,8 +430,8 @@ test("Example 6", () => {
const t0 = 10,
t1 = 20,
ti = 16;
const pt0 = fromGeodeticCoordinates(radians(89.9), radians(-150));
const pt1 = fromGeodeticCoordinates(radians(89.9), radians(150));
const pt0 = fromGeodeticCoordinates(radians(-150), radians(89.9));
const pt1 = fromGeodeticCoordinates(radians(150), radians(89.9));

// Find an interpolated position at time ti, pti. All positions are given as
// n-vectors.
Expand All @@ -444,7 +444,7 @@ test("Example 6", () => {
);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(pti);
const [lon, lat] = toGeodeticCoordinates(pti);

expect(degrees(lat)).toBeCloseTo(89.91282199988446, 12);
expect(degrees(lon)).toBeCloseTo(173.4132244463705, 12);
Expand Down Expand Up @@ -480,9 +480,9 @@ test("Example 7", () => {
// PROBLEM:

// Three positions A, B, and C are given as n-vectors:
const a = fromGeodeticCoordinates(radians(90), radians(0));
const b = fromGeodeticCoordinates(radians(60), radians(10));
const c = fromGeodeticCoordinates(radians(50), radians(-20));
const a = fromGeodeticCoordinates(radians(0), radians(90));
const b = fromGeodeticCoordinates(radians(10), radians(60));
const c = fromGeodeticCoordinates(radians(-20), radians(50));

// Find the mean position, M. Note that the calculation is independent of the
// heights/depths of the positions.
Expand Down Expand Up @@ -534,7 +534,7 @@ test("Example 8", () => {
// PROBLEM:

// Position A is given as n-vector:
const a = fromGeodeticCoordinates(radians(80), radians(-90));
const a = fromGeodeticCoordinates(radians(-90), radians(80));

// We also have an initial direction of travel given as an azimuth (bearing)
// relative to north (clockwise), and finally the distance to travel along a
Expand Down Expand Up @@ -587,7 +587,7 @@ test("Example 8", () => {
);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(b);
const [lon, lat] = toGeodeticCoordinates(b);

expect(degrees(lat)).toBeCloseTo(79.99154867339445, 13);
expect(degrees(lon)).toBeCloseTo(-90.01769837291397, 13);
Expand Down Expand Up @@ -633,12 +633,12 @@ test("Example 9", () => {
// the two positions are not antipodal).

// Path A is given by a1 and a2:
const a1 = fromGeodeticCoordinates(radians(50), radians(180));
const a2 = fromGeodeticCoordinates(radians(90), radians(180));
const a1 = fromGeodeticCoordinates(radians(180), radians(50));
const a2 = fromGeodeticCoordinates(radians(180), radians(90));

// While path B is given by b1 and b2:
const b1 = fromGeodeticCoordinates(radians(60), radians(160));
const b2 = fromGeodeticCoordinates(radians(80), radians(-140));
const b1 = fromGeodeticCoordinates(radians(160), radians(60));
const b2 = fromGeodeticCoordinates(radians(-140), radians(80));

// Find the position C where the two paths intersect.

Expand All @@ -660,7 +660,7 @@ test("Example 9", () => {
const c = apply((n) => Math.sign(dot(cTmp, a1)) * n, cTmp);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(c);
const [lon, lat] = toGeodeticCoordinates(c);

expect(degrees(lat)).toBeCloseTo(74.16344802135536, 16);
expect(degrees(lon)).toBeCloseTo(180, 16);
Expand Down Expand Up @@ -701,10 +701,10 @@ test("Example 10", () => {
// Path A is given by the two n-vectors a1 and a2 (as in the previous
// example):
const a1 = fromGeodeticCoordinates(radians(0), radians(0));
const a2 = fromGeodeticCoordinates(radians(10), radians(0));
const a2 = fromGeodeticCoordinates(radians(0), radians(10));

// And a position B is given by b:
const b = fromGeodeticCoordinates(radians(1), radians(0.1));
const b = fromGeodeticCoordinates(radians(0.1), radians(1));

// Find the cross track distance between the path A (i.e. the great circle
// through a1 and a2) and the position B (i.e. the shortest distance at the
Expand Down
10 changes: 5 additions & 5 deletions src/coords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { transform } from "./vector.js";
*
* @see https://github.com/FFI-no/n-vector/blob/82d749a67cc9f332f48c51aa969cdc277b4199f2/nvector/lat_long2n_E.m
*
* @param latitude - Geodetic latitude in radians.
* @param longitude - Geodetic longitude in radians.
* @param latitude - Geodetic latitude in radians.
* @param frame - Coordinate frame in which the n-vector is decomposed.
*
* @returns An n-vector.
*/
export function fromGeodeticCoordinates(
latitude: number,
longitude: number,
latitude: number,
frame: Matrix = Z_AXIS_NORTH,
): Vector {
// Equation (3) from Gade (2010):
Expand All @@ -39,12 +39,12 @@ export function fromGeodeticCoordinates(
* @param vector - An n-vector.
* @param frame - Coordinate frame in which the n-vector is decomposed.
*
* @returns Geodetic latitude and longitude in radians.
* @returns Geodetic longitude and latitude in radians.
*/
export function toGeodeticCoordinates(
vector: Vector,
frame: Matrix = Z_AXIS_NORTH,
): [latitude: number, longitude: number] {
): [longitude: number, latitude: number] {
// Equation (5) in Gade (2010):
const [x, y, z] = transform(frame, vector);
const longitude = Math.atan2(y, -z);
Expand All @@ -59,5 +59,5 @@ export function toGeodeticCoordinates(
// ill-conditioned which may lead to numerical inaccuracies (and it will give
// imaginary results for norm(vector)>1)

return [latitude, longitude];
return [longitude, latitude];
}
2 changes: 1 addition & 1 deletion src/rotation-matrix.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ export function toRotationMatrixUsingWanderAzimuth(
wanderAzimuth: number,
frame: Matrix = Z_AXIS_NORTH,
): Matrix {
const [latitude, longitude] = toGeodeticCoordinates(vector, frame);
const [longitude, latitude] = toGeodeticCoordinates(vector, frame);

// Longitude, -latitude, and wander azimuth are the x-y-z Euler angles (about
// new axes) for rotation. See also the second paragraph of Section 5.2 in
Expand Down
10 changes: 5 additions & 5 deletions test/arbitrary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,16 @@ export function arbitraryEllipsoidECEFVector({
return arbitrary3dVector({ min: a - b, max: a + b, noNaN: true });
}

export function arbitraryLatLon(): fc.Arbitrary<[number, number]> {
export function arbitraryGeodeticCoordinates(): fc.Arbitrary<[number, number]> {
return fc.tuple(
fc.double({
min: -90 * RADIAN,
max: 90 * RADIAN,
min: -180 * RADIAN,
max: 180 * RADIAN,
noNaN: true,
}),
fc.double({
min: -180 * RADIAN,
max: 180 * RADIAN,
min: -90 * RADIAN,
max: 90 * RADIAN,
noNaN: true,
}),
);
Expand Down
10 changes: 5 additions & 5 deletions test/nvector-test-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ export async function createNvectorTestClient(): Promise<NvectorTestClient> {
});

return {
async fromGeodeticCoordinates(latitude, longitude, frame) {
async fromGeodeticCoordinates(longitude, latitude, frame) {
return unwrapVector3(
await call<WrappedVector3>("lat_lon2n_E", {
latitude,
longitude,
latitude,
R_Ee: frame,
}),
);
Expand All @@ -66,15 +66,15 @@ export async function createNvectorTestClient(): Promise<NvectorTestClient> {
},

async toGeodeticCoordinates(vector, frame) {
const { latitude, longitude } = await call<{
latitude: number;
const { longitude, latitude } = await call<{
longitude: number;
latitude: number;
}>("n_E2lat_lon", {
n_E: wrapVector3(vector),
R_Ee: frame,
});

return [latitude, longitude];
return [longitude, latitude];
},

async toRotationMatrix(vector, frame) {
Expand Down
10 changes: 5 additions & 5 deletions test/vitest/coords.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { afterAll, beforeAll, describe, expect } from "vitest";
import {
arbitrary3dRotationMatrix,
arbitrary3dUnitVector,
arbitraryLatLon,
arbitraryGeodeticCoordinates,
} from "../arbitrary.js";
import type { NvectorTestClient } from "../nvector-test-api.js";
import { createNvectorTestClient } from "../nvector-test-api.js";
Expand All @@ -27,16 +27,16 @@ describe("fromGeodeticCoordinates()", () => {

it.prop(
[
arbitraryLatLon(),
arbitraryGeodeticCoordinates(),
fc.option(arbitrary3dRotationMatrix(), { nil: undefined }),
],
{ interruptAfterTimeLimit: TEST_DURATION, numRuns: Infinity },
)(
"matches the reference implementation",
async ([latitude, longitude], frame) => {
async ([longitude, latitude], frame) => {
const expected = await nvectorTestClient.fromGeodeticCoordinates(
latitude,
longitude,
latitude,
frame,
);

Expand All @@ -46,7 +46,7 @@ describe("fromGeodeticCoordinates()", () => {
expect.any(Number),
]);

const actual = fromGeodeticCoordinates(latitude, longitude, frame);
const actual = fromGeodeticCoordinates(longitude, latitude, frame);

expect(actual).toMatchObject([
expect.any(Number),
Expand Down
4 changes: 2 additions & 2 deletions test/vitest/examples/example-01.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ test("Example 1", () => {
// Step 1
//
// First, the given latitudes and longitudes are converted to n-vectors:
const a = fromGeodeticCoordinates(radians(aLat), radians(aLon));
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
const a = fromGeodeticCoordinates(radians(aLon), radians(aLat));
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));

// Step 2
//
Expand Down
2 changes: 1 addition & 1 deletion test/vitest/examples/example-02.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test("Example 2", () => {
const [c, cDepth] = destination(b, bcE, bDepth, e);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(c);
const [lon, lat] = toGeodeticCoordinates(c);
const height = -cDepth;

expect(degrees(lat)).toBeCloseTo(53.32637826433107, 13);
Expand Down
2 changes: 1 addition & 1 deletion test/vitest/examples/example-03.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ test("Example 3", () => {
// Step 2
//
// Find latitude, longitude and height:
const [lat, lon] = toGeodeticCoordinates(b);
const [lon, lat] = toGeodeticCoordinates(b);
const height = -bDepth;

expect(degrees(lat)).toBeCloseTo(5.685075734513181, 14);
Expand Down
2 changes: 1 addition & 1 deletion test/vitest/examples/example-04.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test("Example 4", () => {
// SOLUTION:

// Step 1: First, the given latitude and longitude are converted to n-vector:
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));

// Step 2: Convert to an ECEF-vector:
const pb = toECEF(b, -bHeight);
Expand Down
4 changes: 2 additions & 2 deletions test/vitest/examples/example-05.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ test("Example 5", () => {
// PROBLEM:

// Given two positions A and B as n-vectors:
const a = fromGeodeticCoordinates(radians(88), radians(0));
const b = fromGeodeticCoordinates(radians(89), radians(-170));
const a = fromGeodeticCoordinates(radians(0), radians(88));
const b = fromGeodeticCoordinates(radians(-170), radians(89));

// Find the surface distance (i.e. great circle distance). The heights of A
// and B are not relevant (i.e. if they do not have zero height, we seek the
Expand Down
6 changes: 3 additions & 3 deletions test/vitest/examples/example-06.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ test("Example 6", () => {
const t0 = 10,
t1 = 20,
ti = 16;
const pt0 = fromGeodeticCoordinates(radians(89.9), radians(-150));
const pt1 = fromGeodeticCoordinates(radians(89.9), radians(150));
const pt0 = fromGeodeticCoordinates(radians(-150), radians(89.9));
const pt1 = fromGeodeticCoordinates(radians(150), radians(89.9));

// Find an interpolated position at time ti, pti. All positions are given as
// n-vectors.
Expand All @@ -37,7 +37,7 @@ test("Example 6", () => {
);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(pti);
const [lon, lat] = toGeodeticCoordinates(pti);

expect(degrees(lat)).toBeCloseTo(89.91282199988446, 12);
expect(degrees(lon)).toBeCloseTo(173.4132244463705, 12);
Expand Down
6 changes: 3 additions & 3 deletions test/vitest/examples/example-07.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ test("Example 7", () => {
// PROBLEM:

// Three positions A, B, and C are given as n-vectors:
const a = fromGeodeticCoordinates(radians(90), radians(0));
const b = fromGeodeticCoordinates(radians(60), radians(10));
const c = fromGeodeticCoordinates(radians(50), radians(-20));
const a = fromGeodeticCoordinates(radians(0), radians(90));
const b = fromGeodeticCoordinates(radians(10), radians(60));
const c = fromGeodeticCoordinates(radians(-20), radians(50));

// Find the mean position, M. Note that the calculation is independent of the
// heights/depths of the positions.
Expand Down
4 changes: 2 additions & 2 deletions test/vitest/examples/example-08.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ test("Example 8", () => {
// PROBLEM:

// Position A is given as n-vector:
const a = fromGeodeticCoordinates(radians(80), radians(-90));
const a = fromGeodeticCoordinates(radians(-90), radians(80));

// We also have an initial direction of travel given as an azimuth (bearing)
// relative to north (clockwise), and finally the distance to travel along a
Expand Down Expand Up @@ -77,7 +77,7 @@ test("Example 8", () => {
);

// Use human-friendly outputs:
const [lat, lon] = toGeodeticCoordinates(b);
const [lon, lat] = toGeodeticCoordinates(b);

expect(degrees(lat)).toBeCloseTo(79.99154867339445, 13);
expect(degrees(lon)).toBeCloseTo(-90.01769837291397, 13);
Expand Down
Loading

0 comments on commit c30b548

Please sign in to comment.