Skip to content

Commit c30b548

Browse files
committed
Use lon, lat order instead of lat, lon
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.
1 parent 74c3444 commit c30b548

17 files changed

+66
-66
lines changed

README.md

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ test("Example 1", () => {
103103
// Step 1
104104
//
105105
// First, the given latitudes and longitudes are converted to n-vectors:
106-
const a = fromGeodeticCoordinates(radians(aLat), radians(aLon));
107-
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
106+
const a = fromGeodeticCoordinates(radians(aLon), radians(aLat));
107+
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));
108108

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

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

234234
expect(degrees(lat)).toBeCloseTo(53.32637826433107, 13);
@@ -284,7 +284,7 @@ test("Example 3", () => {
284284
// Step 2
285285
//
286286
// Find latitude, longitude and height:
287-
const [lat, lon] = toGeodeticCoordinates(b);
287+
const [lon, lat] = toGeodeticCoordinates(b);
288288
const height = -bDepth;
289289

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

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

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

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

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

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

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

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

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

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

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

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

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

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

635635
// Path A is given by a1 and a2:
636-
const a1 = fromGeodeticCoordinates(radians(50), radians(180));
637-
const a2 = fromGeodeticCoordinates(radians(90), radians(180));
636+
const a1 = fromGeodeticCoordinates(radians(180), radians(50));
637+
const a2 = fromGeodeticCoordinates(radians(180), radians(90));
638638

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

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

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

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

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

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

709709
// Find the cross track distance between the path A (i.e. the great circle
710710
// through a1 and a2) and the position B (i.e. the shortest distance at the

src/coords.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import { transform } from "./vector.js";
99
*
1010
* @see https://github.com/FFI-no/n-vector/blob/82d749a67cc9f332f48c51aa969cdc277b4199f2/nvector/lat_long2n_E.m
1111
*
12-
* @param latitude - Geodetic latitude in radians.
1312
* @param longitude - Geodetic longitude in radians.
13+
* @param latitude - Geodetic latitude in radians.
1414
* @param frame - Coordinate frame in which the n-vector is decomposed.
1515
*
1616
* @returns An n-vector.
1717
*/
1818
export function fromGeodeticCoordinates(
19-
latitude: number,
2019
longitude: number,
20+
latitude: number,
2121
frame: Matrix = Z_AXIS_NORTH,
2222
): Vector {
2323
// Equation (3) from Gade (2010):
@@ -39,12 +39,12 @@ export function fromGeodeticCoordinates(
3939
* @param vector - An n-vector.
4040
* @param frame - Coordinate frame in which the n-vector is decomposed.
4141
*
42-
* @returns Geodetic latitude and longitude in radians.
42+
* @returns Geodetic longitude and latitude in radians.
4343
*/
4444
export function toGeodeticCoordinates(
4545
vector: Vector,
4646
frame: Matrix = Z_AXIS_NORTH,
47-
): [latitude: number, longitude: number] {
47+
): [longitude: number, latitude: number] {
4848
// Equation (5) in Gade (2010):
4949
const [x, y, z] = transform(frame, vector);
5050
const longitude = Math.atan2(y, -z);
@@ -59,5 +59,5 @@ export function toGeodeticCoordinates(
5959
// ill-conditioned which may lead to numerical inaccuracies (and it will give
6060
// imaginary results for norm(vector)>1)
6161

62-
return [latitude, longitude];
62+
return [longitude, latitude];
6363
}

src/rotation-matrix.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ export function toRotationMatrixUsingWanderAzimuth(
9494
wanderAzimuth: number,
9595
frame: Matrix = Z_AXIS_NORTH,
9696
): Matrix {
97-
const [latitude, longitude] = toGeodeticCoordinates(vector, frame);
97+
const [longitude, latitude] = toGeodeticCoordinates(vector, frame);
9898

9999
// Longitude, -latitude, and wander azimuth are the x-y-z Euler angles (about
100100
// new axes) for rotation. See also the second paragraph of Section 5.2 in

test/arbitrary.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,16 +70,16 @@ export function arbitraryEllipsoidECEFVector({
7070
return arbitrary3dVector({ min: a - b, max: a + b, noNaN: true });
7171
}
7272

73-
export function arbitraryLatLon(): fc.Arbitrary<[number, number]> {
73+
export function arbitraryGeodeticCoordinates(): fc.Arbitrary<[number, number]> {
7474
return fc.tuple(
7575
fc.double({
76-
min: -90 * RADIAN,
77-
max: 90 * RADIAN,
76+
min: -180 * RADIAN,
77+
max: 180 * RADIAN,
7878
noNaN: true,
7979
}),
8080
fc.double({
81-
min: -180 * RADIAN,
82-
max: 180 * RADIAN,
81+
min: -90 * RADIAN,
82+
max: 90 * RADIAN,
8383
noNaN: true,
8484
}),
8585
);

test/nvector-test-api.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,11 @@ export async function createNvectorTestClient(): Promise<NvectorTestClient> {
4747
});
4848

4949
return {
50-
async fromGeodeticCoordinates(latitude, longitude, frame) {
50+
async fromGeodeticCoordinates(longitude, latitude, frame) {
5151
return unwrapVector3(
5252
await call<WrappedVector3>("lat_lon2n_E", {
53-
latitude,
5453
longitude,
54+
latitude,
5555
R_Ee: frame,
5656
}),
5757
);
@@ -66,15 +66,15 @@ export async function createNvectorTestClient(): Promise<NvectorTestClient> {
6666
},
6767

6868
async toGeodeticCoordinates(vector, frame) {
69-
const { latitude, longitude } = await call<{
70-
latitude: number;
69+
const { longitude, latitude } = await call<{
7170
longitude: number;
71+
latitude: number;
7272
}>("n_E2lat_lon", {
7373
n_E: wrapVector3(vector),
7474
R_Ee: frame,
7575
});
7676

77-
return [latitude, longitude];
77+
return [longitude, latitude];
7878
},
7979

8080
async toRotationMatrix(vector, frame) {

test/vitest/coords.spec.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { afterAll, beforeAll, describe, expect } from "vitest";
77
import {
88
arbitrary3dRotationMatrix,
99
arbitrary3dUnitVector,
10-
arbitraryLatLon,
10+
arbitraryGeodeticCoordinates,
1111
} from "../arbitrary.js";
1212
import type { NvectorTestClient } from "../nvector-test-api.js";
1313
import { createNvectorTestClient } from "../nvector-test-api.js";
@@ -27,16 +27,16 @@ describe("fromGeodeticCoordinates()", () => {
2727

2828
it.prop(
2929
[
30-
arbitraryLatLon(),
30+
arbitraryGeodeticCoordinates(),
3131
fc.option(arbitrary3dRotationMatrix(), { nil: undefined }),
3232
],
3333
{ interruptAfterTimeLimit: TEST_DURATION, numRuns: Infinity },
3434
)(
3535
"matches the reference implementation",
36-
async ([latitude, longitude], frame) => {
36+
async ([longitude, latitude], frame) => {
3737
const expected = await nvectorTestClient.fromGeodeticCoordinates(
38-
latitude,
3938
longitude,
39+
latitude,
4040
frame,
4141
);
4242

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

49-
const actual = fromGeodeticCoordinates(latitude, longitude, frame);
49+
const actual = fromGeodeticCoordinates(longitude, latitude, frame);
5050

5151
expect(actual).toMatchObject([
5252
expect.any(Number),

test/vitest/examples/example-01.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ test("Example 1", () => {
4646
// Step 1
4747
//
4848
// First, the given latitudes and longitudes are converted to n-vectors:
49-
const a = fromGeodeticCoordinates(radians(aLat), radians(aLon));
50-
const b = fromGeodeticCoordinates(radians(bLat), radians(bLon));
49+
const a = fromGeodeticCoordinates(radians(aLon), radians(aLat));
50+
const b = fromGeodeticCoordinates(radians(bLon), radians(bLat));
5151

5252
// Step 2
5353
//

test/vitest/examples/example-02.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ test("Example 2", () => {
7272
const [c, cDepth] = destination(b, bcE, bDepth, e);
7373

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

7878
expect(degrees(lat)).toBeCloseTo(53.32637826433107, 13);

test/vitest/examples/example-03.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ test("Example 3", () => {
3535
// Step 2
3636
//
3737
// Find latitude, longitude and height:
38-
const [lat, lon] = toGeodeticCoordinates(b);
38+
const [lon, lat] = toGeodeticCoordinates(b);
3939
const height = -bDepth;
4040

4141
expect(degrees(lat)).toBeCloseTo(5.685075734513181, 14);

test/vitest/examples/example-04.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ test("Example 4", () => {
2222
// SOLUTION:
2323

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

2727
// Step 2: Convert to an ECEF-vector:
2828
const pb = toECEF(b, -bHeight);

0 commit comments

Comments
 (0)