Skip to content

Commit 740b35a

Browse files
authored
Modify integer conversion for dart implementation (#700)
* Modify dart implementation * update dart workflow to use dart commands * remove change to tests to force them to fail * update checks script for new dart commands * modify integer conversion in dart library * fix % operator behaviour and formatting * update dart commands * dart format fixes
1 parent efb173d commit 740b35a

File tree

5 files changed

+72
-52
lines changed

5 files changed

+72
-52
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
- name: test
3939
run: |
4040
cd ${OLC_PATH}
41-
pub get && pub run test
41+
dart pub get && dart test
4242
bash checks.sh
4343
4444
# Go implementation. Lives in go/. Tests fail if files have not been formatted with gofmt.

dart/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
## Formatting
44

5-
Code **must** be formatted using `dartfmt`.
5+
Code **must** be formatted using `dart format`.
66

7-
To format your files, just run `format_check.sh` or:
7+
To format your files, just run `checks.sh` or:
88

9-
```
10-
dartfmt --fix --overwrite .
9+
```shell
10+
dart format .
1111
```
1212

1313
The TravisCI test **will fail if any files need formatting**.
@@ -24,5 +24,5 @@ To test the dart version first download the dart sdk from
2424
directory:
2525

2626
```
27-
~/open-location-code$ cd dart && pub run test
27+
~/open-location-code$ cd dart && dart test
2828
```

dart/checks.sh

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,23 @@
33
# with dartanalyzer.
44
# Run from within the dart directory.
55

6-
DART_FMT_CMD=dartfmt
7-
$DART_FMT_CMD --version >/dev/null 2>&1
6+
DART_CMD=dart
7+
$DART_CMD --version >/dev/null 2>&1
88
if [ $? -ne 0 ]; then
9-
DART_FMT_CMD=/usr/lib/dart/bin/dartfmt
10-
fi
11-
12-
DART_ANALYZER_CMD=dartanalyzer
13-
$DART_ANALYZER_CMD --version >/dev/null 2>&1
14-
if [ $? -ne 0 ]; then
15-
DART_ANALYZER_CMD=/usr/lib/dart/bin/dartanalyzer
9+
DART_CMD=/usr/lib/dart/bin/dart
1610
fi
1711

1812
# Define the default return code.
1913
RETURN=0
2014

2115
# For every dart file, check the formatting.
2216
for FILE in `find * | egrep "\.dart$"`; do
23-
FORMATTED=`$DART_FMT_CMD --set-exit-if-changed --fix "$FILE"`
17+
FORMATTED=`$DART_CMD format -o none --set-exit-if-changed "$FILE"`
2418
if [ $? -ne 0 ]; then
2519
if [ -z "$TRAVIS" ]; then
2620
# Running locally, we can just format the file. Use colour codes.
2721
echo -e "\e[1;34m"
28-
$DART_FMT_CMD --fix --overwrite $FILE
22+
$DART_CMD format $FILE
2923
echo -e "\e[0m"
3024
else
3125
# On TravisCI, send a comment with the diff to the pull request.
@@ -39,7 +33,7 @@ for FILE in `find * | egrep "\.dart$"`; do
3933
--commit "$TRAVIS_PULL_REQUEST_SHA"
4034
fi
4135
fi
42-
ANALYSIS=`$DART_ANALYZER_CMD "$FILE"`
36+
ANALYSIS=`$DART_CMD analyze "$FILE"`
4337
echo "$ANALYSIS" | grep "No issues found" >/dev/null
4438
if [ $? -ne 0 ]; then
4539
echo -e "\e[1;31mStatic analysis problems: $FILE\e[0m"

dart/lib/src/open_location_code.dart

Lines changed: 52 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ bool _matchesPattern(String string, Pattern pattern) =>
108108
string.contains(pattern);
109109

110110
bool isValid(String code) {
111-
if (code == null || code.length == 1) {
111+
if (code.length == 1) {
112112
return false;
113113
}
114114

@@ -254,33 +254,40 @@ 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 || (codeLength < pairCodeLength && codeLength.isOdd)) {
257+
if (codeLength < minDigitCount ||
258+
(codeLength < pairCodeLength && codeLength.isOdd)) {
258259
throw ArgumentError('Invalid Open Location Code length: $codeLength');
259260
}
260261
codeLength = min(maxDigitCount, codeLength);
261-
// Ensure that latitude and longitude are valid.
262-
latitude = clipLatitude(latitude);
263-
longitude = normalizeLongitude(longitude);
264-
// Latitude 90 needs to be adjusted to be just less, so the returned code
265-
// can also be decoded.
266-
if (latitude == 90) {
267-
latitude -= computeLatitudePrecision(codeLength).toDouble();
268-
}
269-
var code = '';
270262

271-
// Compute the code.
272263
// This approach converts each value to an integer after multiplying it by
273264
// the final precision. This allows us to use only integer operations, so
274265
// avoiding any accumulation of floating point representation errors.
275266

276-
// Multiply values by their precision and convert to positive.
277-
// Force to integers so the division operations will have integer results.
278-
// Note: Dart requires rounding before truncating to ensure precision!
279-
var latVal =
280-
((latitude + latitudeMax) * finalLatPrecision * 1e6).round() ~/ 1e6;
281-
var lngVal =
282-
((longitude + longitudeMax) * finalLngPrecision * 1e6).round() ~/ 1e6;
267+
// Convert latitude into a positive integer clipped into the range 0-(just
268+
// under 180*2.5e7). Latitude 90 needs to be adjusted to be just less, so the
269+
// returned code can also be decoded.
270+
var latVal = (latitude * finalLatPrecision).round().toInt();
271+
latVal += latitudeMax * finalLatPrecision;
272+
if (latVal < 0) {
273+
latVal = 0;
274+
} else if (latVal >= 2 * latitudeMax * finalLatPrecision) {
275+
latVal = 2 * latitudeMax * finalLatPrecision - 1;
276+
}
277+
// Convert longitude into a positive integer and normalise it into the range
278+
// 0-360*8.192e6.
279+
var lngVal = (longitude * finalLngPrecision).round().toInt();
280+
lngVal += longitudeMax * finalLngPrecision;
281+
if (lngVal < 0) {
282+
// Dart's % operator differs from other languages in that it returns the
283+
// same sign as the divisor. This means we don't need to add the range to
284+
// the result.
285+
lngVal = (lngVal % (2 * longitudeMax * finalLngPrecision));
286+
} else if (lngVal >= 2 * longitudeMax * finalLngPrecision) {
287+
lngVal = lngVal % (2 * longitudeMax * finalLngPrecision);
288+
}
283289

290+
var code = '';
284291
// Compute the grid part of the code if necessary.
285292
if (codeLength > pairCodeLength) {
286293
for (var i = 0; i < maxDigitCount - pairCodeLength; i++) {
@@ -326,7 +333,8 @@ String encode(num latitude, num longitude, {int codeLength = pairCodeLength}) {
326333
CodeArea decode(String code) {
327334
if (!isFull(code)) {
328335
throw ArgumentError(
329-
'Passed Open Location Code is not a valid full code: $code');
336+
'Passed Open Location Code is not a valid full code: $code',
337+
);
330338
}
331339
// Strip out separator character (we've already established the code is
332340
// valid so the maximum is one), padding characters and convert to upper
@@ -381,8 +389,13 @@ CodeArea decode(String code) {
381389
var lat = normalLat / pairPrecision + gridLat / finalLatPrecision;
382390
var lng = normalLng / pairPrecision + gridLng / finalLngPrecision;
383391
// Return the code area.
384-
return CodeArea(lat, lng, lat + latPrecision, lng + lngPrecision,
385-
min(code.length, maxDigitCount));
392+
return CodeArea(
393+
lat,
394+
lng,
395+
lat + latPrecision,
396+
lng + lngPrecision,
397+
min(code.length, maxDigitCount),
398+
);
386399
}
387400

388401
/// Recover the nearest matching code to a specified location.
@@ -420,7 +433,10 @@ CodeArea decode(String code) {
420433
/// passed code was not a valid short code, but was a valid full code, it is
421434
/// returned unchanged.
422435
String recoverNearest(
423-
String shortCode, num referenceLatitude, num referenceLongitude) {
436+
String shortCode,
437+
num referenceLatitude,
438+
num referenceLongitude,
439+
) {
424440
if (!isShort(shortCode)) {
425441
if (isFull(shortCode)) {
426442
return shortCode.toUpperCase();
@@ -442,9 +458,10 @@ String recoverNearest(
442458
var halfResolution = resolution / 2.0;
443459

444460
// Use the reference location to pad the supplied short code and decode it.
445-
var codeArea = decode(encode(referenceLatitude, referenceLongitude)
446-
.substring(0, paddingLength) +
447-
shortCode);
461+
var codeArea = decode(
462+
encode(referenceLatitude, referenceLongitude).substring(0, paddingLength) +
463+
shortCode,
464+
);
448465
var centerLatitude = codeArea.center.latitude;
449466
var centerLongitude = codeArea.center.longitude;
450467

@@ -470,8 +487,11 @@ String recoverNearest(
470487
centerLongitude += resolution;
471488
}
472489

473-
return encode(centerLatitude, centerLongitude,
474-
codeLength: codeArea.codeLength);
490+
return encode(
491+
centerLatitude,
492+
centerLongitude,
493+
codeLength: codeArea.codeLength,
494+
);
475495
}
476496

477497
/// Remove characters from the start of an OLC [code].
@@ -506,8 +526,10 @@ String shorten(String code, num latitude, num longitude) {
506526
latitude = clipLatitude(latitude);
507527
longitude = normalizeLongitude(longitude);
508528
// How close are the latitude and longitude to the code center.
509-
var range = max((codeArea.center.latitude - latitude).abs(),
510-
(codeArea.center.longitude - longitude).abs());
529+
var range = max(
530+
(codeArea.center.latitude - latitude).abs(),
531+
(codeArea.center.longitude - longitude).abs(),
532+
);
511533
for (var i = pairResolutions.length - 2; i >= 1; i--) {
512534
// Check if we're close enough to shorten. The range must be less than 1/2
513535
// the resolution to shorten at all, and we want to allow some safety, so

dart/test/benchmark_test.dart

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,19 @@ void main() {
2626
olc.encode(testData[i][0], testData[i][1], codeLength: testData[i][2]);
2727
}
2828
var duration = stopwatch.elapsedMicroseconds;
29-
print('Encoding benchmark ${testData.length}, duration ${duration} usec, '
30-
'average ${duration / testData.length} usec');
29+
print(
30+
'Encoding benchmark ${testData.length}, duration ${duration} usec, '
31+
'average ${duration / testData.length} usec',
32+
);
3133

3234
stopwatch = Stopwatch()..start();
3335
for (var i = 0; i < testData.length; i++) {
3436
olc.decode(testData[i][3]);
3537
}
3638
duration = stopwatch.elapsedMicroseconds;
37-
print('Decoding benchmark ${testData.length}, duration ${duration} usec, '
38-
'average ${duration / testData.length} usec');
39+
print(
40+
'Decoding benchmark ${testData.length}, duration ${duration} usec, '
41+
'average ${duration / testData.length} usec',
42+
);
3943
});
4044
}

0 commit comments

Comments
 (0)