Skip to content

Commit d3972a3

Browse files
authored
dart: use floor and integer tests (#728)
1 parent ae0f176 commit d3972a3

2 files changed

Lines changed: 64 additions & 12 deletions

File tree

dart/lib/src/open_location_code.dart

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,6 @@ bool isFull(String code) {
254254
/// * [codeLength]: The number of significant digits in the output code, not
255255
/// including any separator characters.
256256
String encode(num latitude, num longitude, {int codeLength = pairCodeLength}) {
257-
if (codeLength < minDigitCount ||
258-
(codeLength < pairCodeLength && codeLength.isOdd)) {
259-
throw ArgumentError('Invalid Open Location Code length: $codeLength');
260-
}
261-
codeLength = min(maxDigitCount, codeLength);
262257
var integers = locationToIntegers(latitude, longitude);
263258
return encodeIntegers(integers[0], integers[1], codeLength);
264259
}
@@ -269,7 +264,7 @@ List<int> locationToIntegers(num latitude, num longitude) {
269264
// Convert latitude into a positive integer clipped into the range 0-(just
270265
// under 180*2.5e7). Latitude 90 needs to be adjusted to be just less, so the
271266
// returned code can also be decoded.
272-
var latVal = (latitude * finalLatPrecision).round().toInt();
267+
var latVal = (latitude * finalLatPrecision).floor().toInt();
273268
latVal += latitudeMax * finalLatPrecision;
274269
if (latVal < 0) {
275270
latVal = 0;
@@ -278,7 +273,7 @@ List<int> locationToIntegers(num latitude, num longitude) {
278273
}
279274
// Convert longitude into a positive integer and normalise it into the range
280275
// 0-360*8.192e6.
281-
var lngVal = (longitude * finalLngPrecision).round().toInt();
276+
var lngVal = (longitude * finalLngPrecision).floor().toInt();
282277
lngVal += longitudeMax * finalLngPrecision;
283278
if (lngVal < 0) {
284279
// Dart's % operator differs from other languages in that it returns the
@@ -293,6 +288,11 @@ List<int> locationToIntegers(num latitude, num longitude) {
293288

294289
/// Encode a location into an Open Location Code.
295290
String encodeIntegers(int latVal, int lngVal, int codeLength) {
291+
if (codeLength < minDigitCount ||
292+
(codeLength < pairCodeLength && codeLength.isOdd)) {
293+
throw ArgumentError('Invalid Open Location Code length: $codeLength');
294+
}
295+
codeLength = min(maxDigitCount, codeLength);
296296
List<String> code = List<String>.filled(maxDigitCount + 1, '');
297297
code[separatorPosition] = separator;
298298

dart/test/encode_test.dart

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,71 @@ import 'package:test/test.dart';
1919
import 'utils.dart';
2020

2121
// code,lat,lng,latLo,lngLo,latHi,lngHi
22-
void checkEncodeDecode(String csvLine) {
22+
void checkEncodeDegrees(String csvLine) {
2323
var elements = csvLine.split(',');
2424
num lat = double.parse(elements[0]);
2525
num lng = double.parse(elements[1]);
26-
int len = int.parse(elements[2]);
27-
var want = elements[3];
26+
int len = int.parse(elements[4]);
27+
var want = elements[5];
2828
var got = olc.encode(lat, lng, codeLength: len);
2929
expect(got, equals(want));
3030
}
3131

32+
void checkEncodeIntegers(String csvLine) {
33+
var elements = csvLine.split(',');
34+
int lat = int.parse(elements[2]);
35+
int lng = int.parse(elements[3]);
36+
int len = int.parse(elements[4]);
37+
var want = elements[5];
38+
var got = olc.encodeIntegers(lat, lng, len);
39+
expect(got, equals(want));
40+
}
41+
42+
void checkLocationToIntegers(String csvLine) {
43+
var elements = csvLine.split(',');
44+
num latDegrees = double.parse(elements[0]);
45+
num lngDegrees = double.parse(elements[1]);
46+
int latInteger = int.parse(elements[2]);
47+
int lngInteger = int.parse(elements[3]);
48+
var got = olc.locationToIntegers(latDegrees, lngDegrees);
49+
// Due to floating point precision limitations, we may get values 1 less than expected.
50+
expect(got[0], lessThanOrEqualTo(latInteger));
51+
expect(got[0] + 1, greaterThanOrEqualTo(latInteger));
52+
expect(got[1], lessThanOrEqualTo(lngInteger));
53+
expect(got[1] + 1, greaterThanOrEqualTo(lngInteger));
54+
}
55+
3256
void main() {
33-
test('Check encode decode', () {
34-
csvLinesFromFile('encoding.csv').forEach(checkEncodeDecode);
57+
// Encoding from degrees permits a small percentage of errors.
58+
// This is due to floating point precision limitations.
59+
test('Check encode from degrees', () {
60+
// The proportion of tests that we will accept generating a different code.
61+
// This should not be significantly different from any other implementation.
62+
num allowedErrRate = 0.05;
63+
int errors = 0;
64+
int tests = 0;
65+
csvLinesFromFile('encoding.csv').forEach((csvLine) {
66+
tests++;
67+
var elements = csvLine.split(',');
68+
num lat = double.parse(elements[0]);
69+
num lng = double.parse(elements[1]);
70+
int len = int.parse(elements[4]);
71+
var want = elements[5];
72+
var got = olc.encode(lat, lng, codeLength: len);
73+
if (got != want) {
74+
print("ENCODING DIFFERENCE: Got '$got', expected '$want'");
75+
errors++;
76+
}
77+
});
78+
expect(errors / tests, lessThanOrEqualTo(allowedErrRate));
79+
});
80+
81+
test('Check encode from integers', () {
82+
csvLinesFromFile('encoding.csv').forEach(checkEncodeIntegers);
83+
});
84+
85+
test('Check conversion of degrees to integers', () {
86+
csvLinesFromFile('encoding.csv').forEach(checkLocationToIntegers);
3587
});
3688

3789
test('MaxCodeLength', () {

0 commit comments

Comments
 (0)