Skip to content

Commit 3e39f06

Browse files
authored
rust: use floor for conversion, add integer tests (#735)
1 parent a7e6e64 commit 3e39f06

File tree

2 files changed

+81
-9
lines changed

2 files changed

+81
-9
lines changed

rust/src/interface.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,15 @@ pub fn is_full(code: &str) -> bool {
102102
pub fn point_to_integers(pt: Point<f64>) -> (i64, i64) {
103103
let (lng, lat) = pt.x_y();
104104

105-
let mut lat_val = (lat * LAT_INTEGER_MULTIPLIER as f64).round() as i64;
105+
let mut lat_val = (lat * LAT_INTEGER_MULTIPLIER as f64).floor() as i64;
106106
lat_val += LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER;
107107
if lat_val < 0 {
108108
lat_val = 0
109109
} else if lat_val >= 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER {
110110
lat_val = 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER - 1;
111111
}
112112

113-
let mut lng_val = (lng * LNG_INTEGER_MULTIPLIER as f64).round() as i64;
113+
let mut lng_val = (lng * LNG_INTEGER_MULTIPLIER as f64).floor() as i64;
114114
lng_val += LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER;
115115
if lng_val < 0 {
116116
lng_val = lng_val % (2 * LONGITUDE_MAX as i64 * LNG_INTEGER_MULTIPLIER)
@@ -131,15 +131,15 @@ pub fn point_to_integers(pt: Point<f64>) -> (i64, i64) {
131131
/// 11 or 12 are probably the limit of useful codes.
132132
pub fn encode(pt: Point<f64>, code_length: usize) -> String {
133133
let (lat_val, lng_val) = point_to_integers(pt);
134-
let trimmed_code_length = min(max(code_length, MIN_CODE_LENGTH), MAX_CODE_LENGTH);
135134

136-
encode_integers(lat_val, lng_val, trimmed_code_length)
135+
encode_integers(lat_val, lng_val, code_length)
137136
}
138137

139138
/// Encode an integer location into an Open Location Code.
140139
///
141140
/// This function is only exposed for testing and should not be called directly.
142-
pub fn encode_integers(mut lat_val: i64, mut lng_val: i64, code_length: usize) -> String {
141+
pub fn encode_integers(mut lat_val: i64, mut lng_val: i64, code_length_raw: usize) -> String {
142+
let code_length = min(max(code_length_raw, MIN_CODE_LENGTH), MAX_CODE_LENGTH);
143143
// Compute the code digits. This largely ignores the requested length - it
144144
// generates either a 10 digit code, or a 15 digit code, and then truncates
145145
// it to the requested length.

rust/tests/all_test.rs

Lines changed: 76 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ use std::time::Instant;
44

55
use csv_reader::CSVReader;
66
use geo::Point;
7-
use open_location_code::{decode, encode, is_full, is_short, is_valid, recover_nearest, shorten};
7+
use open_location_code::{
8+
decode, encode, encode_integers, is_full, is_short, is_valid, point_to_integers,
9+
recover_nearest, shorten,
10+
};
811
use rand::random_range;
912

1013
/// CSVReader is written to swallow errors; as such, we might "pass" tests because we didn't
@@ -59,18 +62,87 @@ fn decode_test() {
5962
#[test]
6063
fn encode_test() {
6164
let mut tested = 0;
65+
let mut errors = 0;
66+
// Allow a small proportion of errors due to floating point.
67+
let allowed_error_rate = 0.05;
6268
for line in CSVReader::new("encoding.csv") {
6369
if line.chars().count() == 0 {
6470
continue;
6571
}
6672
let cols: Vec<&str> = line.split(',').collect();
6773
let lat = cols[0].parse::<f64>().unwrap();
6874
let lng = cols[1].parse::<f64>().unwrap();
69-
let len = cols[2].parse::<usize>().unwrap();
70-
let code = cols[3];
75+
let len = cols[4].parse::<usize>().unwrap();
76+
let code = cols[5];
77+
78+
let got = encode(Point::new(lng, lat), len);
79+
if got != code {
80+
errors += 1;
81+
println!(
82+
"encode(Point::new({}, {}), {}) want {}, got {}",
83+
lng, lat, len, code, got
84+
);
85+
}
86+
87+
tested += 1;
88+
}
89+
assert!(
90+
errors as f32 / tested as f32 <= allowed_error_rate,
91+
"too many encoding errors ({})",
92+
errors
93+
);
94+
assert!(tested > 0);
95+
}
96+
97+
#[test]
98+
fn point_to_integers_test() {
99+
let mut tested = 0;
100+
for line in CSVReader::new("encoding.csv") {
101+
if line.chars().count() == 0 {
102+
continue;
103+
}
104+
let cols: Vec<&str> = line.split(',').collect();
105+
let lat_deg = cols[0].parse::<f64>().unwrap();
106+
let lng_deg = cols[1].parse::<f64>().unwrap();
107+
let lat_int = cols[2].parse::<i64>().unwrap();
108+
let lng_int = cols[3].parse::<i64>().unwrap();
109+
110+
let (got_lat, got_lng) = point_to_integers(Point::new(lng_deg, lat_deg));
111+
assert!(
112+
got_lat >= lat_int - 1 && got_lat <= lat_int,
113+
"converting lat={}, want={}, got={}",
114+
lat_deg,
115+
lat_int,
116+
got_lat
117+
);
118+
assert!(
119+
got_lng >= lng_int - 1 && got_lng <= lng_int,
120+
"converting lng={}, want={}, got={}",
121+
lng_deg,
122+
lng_int,
123+
got_lng
124+
);
125+
126+
tested += 1;
127+
}
128+
assert!(tested > 0);
129+
}
130+
131+
#[test]
132+
fn encode_integers_test() {
133+
let mut tested = 0;
134+
for line in CSVReader::new("encoding.csv") {
135+
if line.chars().count() == 0 {
136+
continue;
137+
}
138+
let cols: Vec<&str> = line.split(',').collect();
139+
let lat = cols[2].parse::<i64>().unwrap();
140+
let lng = cols[3].parse::<i64>().unwrap();
141+
let len = cols[4].parse::<usize>().unwrap();
142+
let code = cols[5];
71143

72144
assert_eq!(
73-
encode(Point::new(lng, lat), len),
145+
encode_integers(lat, lng, len),
74146
code,
75147
"encoding lat={},lng={},len={}",
76148
lat,

0 commit comments

Comments
 (0)