From d74d7e4d401eb6088480e473cd639530cd8e1d57 Mon Sep 17 00:00:00 2001 From: Doug Rinckes Date: Fri, 13 Jun 2025 15:14:56 +0200 Subject: [PATCH 1/4] ruby: use floor for conversion, add integer tests --- ruby/lib/plus_codes/open_location_code.rb | 13 +++--- ruby/test/plus_codes_test.rb | 50 ++++++++++++++++++++++- 2 files changed, 54 insertions(+), 9 deletions(-) diff --git a/ruby/lib/plus_codes/open_location_code.rb b/ruby/lib/plus_codes/open_location_code.rb index decae2db..752a46e3 100644 --- a/ruby/lib/plus_codes/open_location_code.rb +++ b/ruby/lib/plus_codes/open_location_code.rb @@ -44,14 +44,14 @@ def full?(code) # @return [Array] with the latitude and longitude integer # values. def location_to_integers(latitude, longitude) - lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).round + lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).floor lat_val += 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION if lat_val.negative? lat_val = 0 elsif lat_val >= 2 * 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION lat_val = 2 * 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION - 1 end - lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).round + lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).floor lng_val += 180 * PAIR_CODE_PRECISION * LNG_GRID_PRECISION if lng_val.negative? # Ruby's % operator differs from other languages in that it returns @@ -72,11 +72,6 @@ def location_to_integers(latitude, longitude) # excludes the separator # @return [String] a plus+codes def encode(latitude, longitude, code_length = PAIR_CODE_LENGTH) - if invalid_length?(code_length) - raise ArgumentError, 'Invalid Open Location Code(Plus+Codes) length' - end - - code_length = MAX_CODE_LENGTH if code_length > MAX_CODE_LENGTH lat_val, lng_val = location_to_integers(latitude, longitude) encode_integers(lat_val, lng_val, code_length) end @@ -91,6 +86,10 @@ def encode(latitude, longitude, code_length = PAIR_CODE_LENGTH) # excludes the separator # @return [String] a plus+codes def encode_integers(lat_val, lng_val, code_length = PAIR_CODE_LENGTH) + if invalid_length?(code_length) + raise ArgumentError, 'Invalid Open Location Code(Plus+Codes) length' + end + code_length = MAX_CODE_LENGTH if code_length > MAX_CODE_LENGTH # Initialise the code using an Array. Array.join is more efficient that # string addition. code = Array.new(MAX_CODE_LENGTH + 1, '') diff --git a/ruby/test/plus_codes_test.rb b/ruby/test/plus_codes_test.rb index c28dd546..06e819bd 100644 --- a/ruby/test/plus_codes_test.rb +++ b/ruby/test/plus_codes_test.rb @@ -42,12 +42,58 @@ def test_decode end def test_encode + # Allow a 5% error rate encoding from degree coordinates (because of floating point precision). + allowedErrorRate = 0.05 + errors = 0 + tests = 0 read_csv_lines('encoding.csv').each do |line| next if line.empty? + tests += 1 cols = line.split(',') - code = @olc.encode(cols[0].to_f, cols[1].to_f, cols[2].to_i) - assert_equal(cols[3], code) + lat_degrees = cols[0].to_f + lng_degrees = cols[1].to_f + code_length = cols[4].to_i + want = cols[5] + + code = @olc.encode(lat_degrees, lng_degrees, code_length) + if want != code + errors += 1 + puts "ENCODING DIFFERENCE: want #{want}, got #{code}" + end + end + assert_compare(errors.to_f / tests.to_f, "<=", allowedErrorRate) + end + + def test_location_to_integers + read_csv_lines('encoding.csv').each do |line| + next if line.empty? + + cols = line.split(',') + lat_degrees = cols[0].to_f + lng_degrees = cols[1].to_f + lat_integer = cols[2].to_i + lng_integer = cols[3].to_i + + got_lat, got_lng = @olc.location_to_integers(lat_degrees, lng_degrees) + # Due to floating point precision limitations, we may get values 1 less than expected. + assert_include([lat_integer - 1, lat_integer], got_lat) + assert_include([lng_integer - 1, lng_integer], got_lng) + end + end + + def test_encode_integers + read_csv_lines('encoding.csv').each do |line| + next if line.empty? + + cols = line.split(',') + lat_integer = cols[2].to_i + lng_integer = cols[3].to_i + code_length = cols[4].to_i + want = cols[5] + + code = @olc.encode_integers(lat_integer, lng_integer, code_length) + assert_equal(want, code) end end From cc26f48e9b862e4bc8931eda4a7844742bcf0a89 Mon Sep 17 00:00:00 2001 From: Doug Rinckes Date: Fri, 13 Jun 2025 15:14:56 +0200 Subject: [PATCH 2/4] Fix rubocop errors --- ruby/lib/plus_codes/open_location_code.rb | 2 ++ ruby/test/plus_codes_test.rb | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ruby/lib/plus_codes/open_location_code.rb b/ruby/lib/plus_codes/open_location_code.rb index 752a46e3..0cf15668 100644 --- a/ruby/lib/plus_codes/open_location_code.rb +++ b/ruby/lib/plus_codes/open_location_code.rb @@ -44,6 +44,7 @@ def full?(code) # @return [Array] with the latitude and longitude integer # values. def location_to_integers(latitude, longitude) + lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).floor lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).floor lat_val += 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION if lat_val.negative? @@ -52,6 +53,7 @@ def location_to_integers(latitude, longitude) lat_val = 2 * 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION - 1 end lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).floor + lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).floor lng_val += 180 * PAIR_CODE_PRECISION * LNG_GRID_PRECISION if lng_val.negative? # Ruby's % operator differs from other languages in that it returns diff --git a/ruby/test/plus_codes_test.rb b/ruby/test/plus_codes_test.rb index 06e819bd..db6b939a 100644 --- a/ruby/test/plus_codes_test.rb +++ b/ruby/test/plus_codes_test.rb @@ -42,8 +42,9 @@ def test_decode end def test_encode - # Allow a 5% error rate encoding from degree coordinates (because of floating point precision). - allowedErrorRate = 0.05 + # Allow a 5% error rate encoding from degree coordinates (because of + # floating point precision). + allowed_error_rate = 0.05 errors = 0 tests = 0 read_csv_lines('encoding.csv').each do |line| @@ -62,7 +63,7 @@ def test_encode puts "ENCODING DIFFERENCE: want #{want}, got #{code}" end end - assert_compare(errors.to_f / tests.to_f, "<=", allowedErrorRate) + assert_compare(errors.to_f / tests, '<=', allowed_error_rate) end def test_location_to_integers @@ -76,7 +77,8 @@ def test_location_to_integers lng_integer = cols[3].to_i got_lat, got_lng = @olc.location_to_integers(lat_degrees, lng_degrees) - # Due to floating point precision limitations, we may get values 1 less than expected. + # Due to floating point precision limitations, we may get values 1 less + # than expected. assert_include([lat_integer - 1, lat_integer], got_lat) assert_include([lng_integer - 1, lng_integer], got_lng) end From 2049fc972e425650b8e9e7fbced13a9bf4cc31e6 Mon Sep 17 00:00:00 2001 From: Doug Rinckes Date: Fri, 13 Jun 2025 15:24:59 +0200 Subject: [PATCH 3/4] weird merge artifact --- ruby/lib/plus_codes/open_location_code.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/ruby/lib/plus_codes/open_location_code.rb b/ruby/lib/plus_codes/open_location_code.rb index 0cf15668..752a46e3 100644 --- a/ruby/lib/plus_codes/open_location_code.rb +++ b/ruby/lib/plus_codes/open_location_code.rb @@ -44,7 +44,6 @@ def full?(code) # @return [Array] with the latitude and longitude integer # values. def location_to_integers(latitude, longitude) - lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).floor lat_val = (latitude * PAIR_CODE_PRECISION * LAT_GRID_PRECISION).floor lat_val += 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION if lat_val.negative? @@ -53,7 +52,6 @@ def location_to_integers(latitude, longitude) lat_val = 2 * 90 * PAIR_CODE_PRECISION * LAT_GRID_PRECISION - 1 end lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).floor - lng_val = (longitude * PAIR_CODE_PRECISION * LNG_GRID_PRECISION).floor lng_val += 180 * PAIR_CODE_PRECISION * LNG_GRID_PRECISION if lng_val.negative? # Ruby's % operator differs from other languages in that it returns From 5f8d6b2be0846909be23b58a5eeeaf03abc4f1ab Mon Sep 17 00:00:00 2001 From: Doug Rinckes Date: Fri, 13 Jun 2025 15:26:43 +0200 Subject: [PATCH 4/4] add blank line after guard --- ruby/lib/plus_codes/open_location_code.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/ruby/lib/plus_codes/open_location_code.rb b/ruby/lib/plus_codes/open_location_code.rb index 752a46e3..b693da00 100644 --- a/ruby/lib/plus_codes/open_location_code.rb +++ b/ruby/lib/plus_codes/open_location_code.rb @@ -89,6 +89,7 @@ def encode_integers(lat_val, lng_val, code_length = PAIR_CODE_LENGTH) if invalid_length?(code_length) raise ArgumentError, 'Invalid Open Location Code(Plus+Codes) length' end + code_length = MAX_CODE_LENGTH if code_length > MAX_CODE_LENGTH # Initialise the code using an Array. Array.join is more efficient that # string addition.