Skip to content

Commit 2bee6e0

Browse files
authored
Merge branch 'main' into ruby_674_717
2 parents d74d7e4 + 2afe39f commit 2bee6e0

3 files changed

Lines changed: 137 additions & 76 deletions

File tree

js/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Execute the tests with `npm test`. This will install the
3434
dependencies, run `eslint` and then run the tests as long as there were no
3535
eslint errors.
3636

37+
Note: Run `checks.sh` first to build the JSON files in `test/`.
38+
3739
Unit tests are automatically run on pull and push requests and visible at
3840
<https://github.com/google/open-location-code/actions>.
3941

js/src/openlocationcode.js

Lines changed: 17 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -312,18 +312,6 @@
312312
longitude, codeLength) {
313313
latitude = Number(latitude);
314314
longitude = Number(longitude);
315-
if (typeof codeLength == 'undefined') {
316-
codeLength = OpenLocationCode.CODE_PRECISION_NORMAL;
317-
} else {
318-
codeLength = Math.min(MAX_DIGIT_COUNT_, Number(codeLength));
319-
}
320-
if (isNaN(latitude) || isNaN(longitude) || isNaN(codeLength)) {
321-
throw new Error('ValueError: Parameters are not numbers');
322-
}
323-
if (codeLength < MIN_DIGIT_COUNT_ ||
324-
(codeLength < PAIR_CODE_LENGTH_ && codeLength % 2 == 1)) {
325-
throw new Error('IllegalArgumentException: Invalid Open Location Code length');
326-
}
327315

328316
const locationIntegers = locationToIntegers(latitude, longitude);
329317

@@ -345,15 +333,15 @@
345333
* @param {number} longitude
346334
* @return {Array<number>} A tuple of the latitude integer and longitude integer.
347335
*/
348-
var locationToIntegers = function(latitude, longitude) {
349-
var latVal = roundAwayFromZero(latitude * FINAL_LAT_PRECISION_);
336+
var locationToIntegers = OpenLocationCode.locationToIntegers = function(latitude, longitude) {
337+
var latVal = Math.floor(latitude * FINAL_LAT_PRECISION_);
350338
latVal += LATITUDE_MAX_ * FINAL_LAT_PRECISION_;
351339
if (latVal < 0) {
352340
latVal = 0;
353341
} else if (latVal >= 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_) {
354342
latVal = 2 * LATITUDE_MAX_ * FINAL_LAT_PRECISION_ - 1;
355343
}
356-
var lngVal = roundAwayFromZero(longitude * FINAL_LNG_PRECISION_);
344+
var lngVal = Math.floor(longitude * FINAL_LNG_PRECISION_);
357345
lngVal += LONGITUDE_MAX_ * FINAL_LNG_PRECISION_;
358346
if (lngVal < 0) {
359347
lngVal =
@@ -376,8 +364,21 @@
376364
* code, not including any separator characters.
377365
* @return {string} A code of the specified length or the default length if not
378366
* specified.
367+
* @throws {Exception} if any of the input values are not numbers.
379368
*/
380-
var encodeIntegers = function(latInt, lngInt, codeLength) {
369+
var encodeIntegers = OpenLocationCode.encodeIntegers = function(latInt, lngInt, codeLength) {
370+
if (typeof codeLength == 'undefined') {
371+
codeLength = OpenLocationCode.CODE_PRECISION_NORMAL;
372+
} else {
373+
codeLength = Math.min(MAX_DIGIT_COUNT_, Number(codeLength));
374+
}
375+
if (isNaN(latInt) || isNaN(lngInt) || isNaN(codeLength)) {
376+
throw new Error('ValueError: Parameters are not numbers');
377+
}
378+
if (codeLength < MIN_DIGIT_COUNT_ ||
379+
(codeLength < PAIR_CODE_LENGTH_ && codeLength % 2 == 1)) {
380+
throw new Error('IllegalArgumentException: Invalid Open Location Code length');
381+
}
381382
// Javascript strings are immutable and it doesn't have a native
382383
// StringBuilder, so we'll use an array.
383384
const code = new Array(MAX_DIGIT_COUNT_ + 1);
@@ -626,20 +627,6 @@
626627
return code;
627628
};
628629

629-
/**
630-
* Round numbers like C does. This implements rounding away from zero (see
631-
* https://en.wikipedia.org/wiki/Rounding).
632-
*
633-
* @param {number} num A number to round.
634-
* @return {number} The rounded value.
635-
*/
636-
var roundAwayFromZero = function(num) {
637-
if (num >= 0) {
638-
return Math.round(num);
639-
}
640-
return -1 * Math.round(Math.abs(num));
641-
};
642-
643630
/**
644631
* Clip a latitude into the range -90 to 90.
645632
*

js/test/jasmine-tests.js

Lines changed: 118 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,136 @@
11
describe("Open Location Code", function() {
22
var precision = 1e-10;
33

4-
it("Encoding Tests", function() {
5-
jasmine.getFixtures().fixturesPath = 'base/';
6-
var encTests = JSON.parse(jasmine.getFixtures().read("encoding.json"));
7-
expect(encTests.length).toBeGreaterThan(1);
8-
for (var i = 0; i < encTests.length; i++) {
9-
var test = encTests[i];
10-
var code = OpenLocationCode.encode(test[0], test[1], test[2]);
11-
// Did we get the same code?
12-
expect(code).toBe(test[3]);
4+
jasmine.getFixtures().fixturesPath = "base/";
5+
6+
const encodingTests = JSON.parse(jasmine.getFixtures().read("encoding.json"));
7+
it("has encoding tests", function() {
8+
expect(encodingTests.length).toBeGreaterThan(1);
9+
});
10+
11+
const decodingTests = JSON.parse(jasmine.getFixtures().read("decoding.json"));
12+
it("has decoding tests", function() {
13+
expect(decodingTests.length).toBeGreaterThan(1);
14+
});
15+
16+
const validityTests = JSON.parse(jasmine.getFixtures().read("validityTests.json"));
17+
it("has validity tests", function() {
18+
expect(validityTests.length).toBeGreaterThan(1);
19+
});
20+
21+
const shortenTests = JSON.parse(jasmine.getFixtures().read("shortCodeTests.json"));
22+
it("has shorten tests", function() {
23+
expect(shortenTests.length).toBeGreaterThan(1);
24+
});
25+
26+
describe("locationToIntegers Tests", function() {
27+
for (let i = 0; i < encodingTests.length; i++) {
28+
const test = encodingTests[i];
29+
const lat = test[0];
30+
const lng = test[1];
31+
const wantLat = test[2];
32+
const wantLng = test[3];
33+
const got = OpenLocationCode.locationToIntegers(lat, lng);
34+
// Due to floating point precision limitations, we may get values 1 less
35+
// than expected.
36+
it("converting latitude " + lat + " to integer, want " + wantLat + ", got " + got[0], function() {
37+
expect(got[0] == wantLat || got[0] == wantLat - 1).toBe(true);
38+
});
39+
it("converting longitude " + lng + " to integer, want " + wantLng + ", got " + got[1], function() {
40+
expect(got[1] == wantLng || got[1] == wantLng - 1).toBe(true);
41+
});
42+
}
43+
});
44+
45+
describe("encode (degrees) Tests", function() {
46+
// Allow a 5% error rate encoding from degree coordinates (because of floating
47+
// point precision).
48+
const allowedErrorRate = 0.05;
49+
let errors = 0;
50+
for (let i = 0; i < encodingTests.length; i++) {
51+
const test = encodingTests[i];
52+
const lat = test[0];
53+
const lng = test[1];
54+
const codeLength = test[4];
55+
const want = test[5];
56+
const got = OpenLocationCode.encode(lat, lng, codeLength);
57+
if (got !== want) {
58+
console.log('ENCODING DIFFERENCE: Expected code ' + want +', got ' + got);
59+
errors ++;
60+
}
61+
}
62+
it("Encoding degrees error rate too high", function() {
63+
expect(errors / encodingTests.length).toBeLessThan(allowedErrorRate);
64+
});
65+
});
66+
67+
describe("encodeIntegers Tests", function() {
68+
for (let i = 0; i < encodingTests.length; i++) {
69+
const test = encodingTests[i];
70+
const lat = test[2];
71+
const lng = test[3];
72+
const codeLength = test[4];
73+
const want = test[5];
74+
it("Encoding integers " + lat + "," + lng + " with length " + codeLength, function() {
75+
const got = OpenLocationCode.encodeIntegers(lat, lng, codeLength);
76+
expect(got).toBe(want);
77+
});
1378
}
1479
});
1580

16-
it("Decoding Tests", function() {
17-
jasmine.getFixtures().fixturesPath = 'base/';
18-
var encTests = JSON.parse(jasmine.getFixtures().read("decoding.json"));
19-
expect(encTests.length).toBeGreaterThan(1);
20-
for (var i = 0; i < encTests.length; i++) {
21-
var test = encTests[i];
22-
var area = OpenLocationCode.decode(test[0]);
23-
// Did we get the same values?
24-
expect(area.codeLength).toBe(test[1]);
25-
expect(area.latitudeLo).toBeCloseTo(test[2], precision, test[0]);
26-
expect(area.longitudeLo).toBeCloseTo(test[3], precision, test[0]);
27-
expect(area.latitudeHi).toBeCloseTo(test[4], precision, test[0]);
28-
expect(area.longitudeHi).toBeCloseTo(test[5], precision, test[0]);
81+
describe("Decoding Tests", function() {
82+
for (let i = 0; i < decodingTests.length; i++) {
83+
const test = decodingTests[i];
84+
const area = OpenLocationCode.decode(test[0]);
85+
it("Decoding code " + test[0] + ", checking codelength", function() {
86+
expect(area.codeLength).toBe(test[1]);
87+
})
88+
it("Decoding code " + test[0] + ", checking latitudeLo", function() {
89+
expect(area.latitudeLo).toBeCloseTo(test[2], precision, test[0]);
90+
})
91+
it("Decoding code " + test[0] + ", checking longitudeLo", function() {
92+
expect(area.longitudeLo).toBeCloseTo(test[3], precision, test[0]);
93+
})
94+
it("Decoding code " + test[0] + ", checking latitudeHi", function() {
95+
expect(area.latitudeHi).toBeCloseTo(test[4], precision, test[0]);
96+
})
97+
it("Decoding code " + test[0] + ", checking longitudeHi", function() {
98+
expect(area.longitudeHi).toBeCloseTo(test[5], precision, test[0]);
99+
})
29100
}
30101
});
31102

32-
it("Validity Tests", function() {
33-
jasmine.getFixtures().fixturesPath = 'base/';
34-
var encTests = JSON.parse(jasmine.getFixtures().read("validityTests.json"));
35-
expect(encTests.length).toBeGreaterThan(1);
36-
for (var i = 0; i < encTests.length; i++) {
37-
var test = encTests[i];
38-
expect(OpenLocationCode.isValid(test[0])).toBe(test[1] === 'true', test[0]);
39-
expect(OpenLocationCode.isShort(test[0])).toBe(test[2] === 'true', test[0]);
40-
expect(OpenLocationCode.isFull(test[0])).toBe(test[3] === 'true', test[0]);
103+
describe("Validity Tests", function() {
104+
for (let i = 0; i < validityTests.length; i++) {
105+
const test = validityTests[i];
106+
it("isValid(" + test[0] + ")", function() {
107+
expect(OpenLocationCode.isValid(test[0])).toBe(test[1] === "true", test[0]);
108+
});
109+
it("isShort(" + test[0] + ")", function() {
110+
expect(OpenLocationCode.isShort(test[0])).toBe(test[2] === "true", test[0]);
111+
});
112+
it("isFull(" + test[0] + ")", function() {
113+
expect(OpenLocationCode.isFull(test[0])).toBe(test[3] === "true", test[0]);
114+
});
41115
}
42116
});
43117

44-
it("Short Code Tests", function() {
45-
jasmine.getFixtures().fixturesPath = 'base/';
46-
var encTests = JSON.parse(jasmine.getFixtures().read("shortCodeTests.json"));
47-
expect(encTests.length).toBeGreaterThan(1);
48-
for (var i = 0; i < encTests.length; i++) {
49-
var test = encTests[i];
118+
describe("Short Code Tests", function() {
119+
for (let i = 0; i < shortenTests.length; i++) {
120+
const test = shortenTests[i];
50121
if (test[4] == "B" || test[4] == "S") {
51122
// Shorten the full length code.
52-
var shorten = OpenLocationCode.shorten(
53-
test[0], test[1], test[2]);
54-
// Confirm we got what we expected.
55-
expect(shorten).toBe(test[3], test[0]);
123+
it("shorten(" + test[0] + ", " + test[1] + ", " + test[2], function() {
124+
const got = OpenLocationCode.shorten(test[0], test[1], test[2]);
125+
expect(got).toBe(test[3], test[0]);
126+
});
56127
}
57128
if (test[4] == "B" || test[4] == "R") {
58129
// Now try expanding the shortened code.
59-
var expanded = OpenLocationCode.recoverNearest(
60-
test[3], test[1], test[2]);
61-
expect(expanded).toBe(test[0]);
130+
it("recoverNearest(" + test[3] + ", " + test[1] + ", " + test[2], function() {
131+
const got = OpenLocationCode.recoverNearest(test[3], test[1], test[2]);
132+
expect(got).toBe(test[0]);
133+
});
62134
}
63135
}
64136
});
@@ -73,7 +145,7 @@ describe("Open Location Code", function() {
73145
lng = Math.round(lng * Math.pow(10, decimals)) / Math.pow(10, decimals);
74146
var length = 2 + Math.round(Math.random() * 13);
75147
if (length < 10 && length % 2 === 1) {
76-
length += 1;
148+
length += 1;
77149
}
78150
input.push([lat, lng, length]);
79151
}
@@ -97,7 +169,7 @@ describe("Open Location Code", function() {
97169
lng = Math.round(lng * Math.pow(10, decimals)) / Math.pow(10, decimals);
98170
var length = 2 + Math.round(Math.random() * 13);
99171
if (length < 10 && length % 2 === 1) {
100-
length += 1;
172+
length += 1;
101173
}
102174
input.push(OpenLocationCode.encode(lat, lng, length));
103175
}

0 commit comments

Comments
 (0)