Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions rust/src/interface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,15 @@ pub fn is_full(code: &str) -> bool {
pub fn point_to_integers(pt: Point<f64>) -> (i64, i64) {
let (lng, lat) = pt.x_y();

let mut lat_val = (lat * LAT_INTEGER_MULTIPLIER as f64).round() as i64;
let mut lat_val = (lat * LAT_INTEGER_MULTIPLIER as f64).floor() as i64;
lat_val += LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER;
if lat_val < 0 {
lat_val = 0
} else if lat_val >= 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER {
lat_val = 2 * LATITUDE_MAX as i64 * LAT_INTEGER_MULTIPLIER - 1;
}

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

encode_integers(lat_val, lng_val, trimmed_code_length)
encode_integers(lat_val, lng_val, code_length)
}

/// Encode an integer location into an Open Location Code.
///
/// This function is only exposed for testing and should not be called directly.
pub fn encode_integers(mut lat_val: i64, mut lng_val: i64, code_length: usize) -> String {
pub fn encode_integers(mut lat_val: i64, mut lng_val: i64, code_length_raw: usize) -> String {
let code_length = min(max(code_length_raw, MIN_CODE_LENGTH), MAX_CODE_LENGTH);
// Compute the code digits. This largely ignores the requested length - it
// generates either a 10 digit code, or a 15 digit code, and then truncates
// it to the requested length.
Expand Down
80 changes: 76 additions & 4 deletions rust/tests/all_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ use std::time::Instant;

use csv_reader::CSVReader;
use geo::Point;
use open_location_code::{decode, encode, is_full, is_short, is_valid, recover_nearest, shorten};
use open_location_code::{
decode, encode, encode_integers, is_full, is_short, is_valid, point_to_integers,
recover_nearest, shorten,
};
use rand::random_range;

/// CSVReader is written to swallow errors; as such, we might "pass" tests because we didn't
Expand Down Expand Up @@ -59,18 +62,87 @@ fn decode_test() {
#[test]
fn encode_test() {
let mut tested = 0;
let mut errors = 0;
// Allow a small proportion of errors due to floating point.
let allowed_error_rate = 0.05;
for line in CSVReader::new("encoding.csv") {
if line.chars().count() == 0 {
continue;
}
let cols: Vec<&str> = line.split(',').collect();
let lat = cols[0].parse::<f64>().unwrap();
let lng = cols[1].parse::<f64>().unwrap();
let len = cols[2].parse::<usize>().unwrap();
let code = cols[3];
let len = cols[4].parse::<usize>().unwrap();
let code = cols[5];

let got = encode(Point::new(lng, lat), len);
if got != code {
errors += 1;
println!(
"encode(Point::new({}, {}), {}) want {}, got {}",
lng, lat, len, code, got
);
}

tested += 1;
}
assert!(
errors as f32 / tested as f32 <= allowed_error_rate,
"too many encoding errors ({})",
errors
);
assert!(tested > 0);
}

#[test]
fn point_to_integers_test() {
let mut tested = 0;
for line in CSVReader::new("encoding.csv") {
if line.chars().count() == 0 {
continue;
}
let cols: Vec<&str> = line.split(',').collect();
let lat_deg = cols[0].parse::<f64>().unwrap();
let lng_deg = cols[1].parse::<f64>().unwrap();
let lat_int = cols[2].parse::<i64>().unwrap();
let lng_int = cols[3].parse::<i64>().unwrap();

let (got_lat, got_lng) = point_to_integers(Point::new(lng_deg, lat_deg));
assert!(
got_lat >= lat_int - 1 && got_lat <= lat_int,
"converting lat={}, want={}, got={}",
lat_deg,
lat_int,
got_lat
);
assert!(
got_lng >= lng_int - 1 && got_lng <= lng_int,
"converting lng={}, want={}, got={}",
lng_deg,
lng_int,
got_lng
);

tested += 1;
}
assert!(tested > 0);
}

#[test]
fn encode_integers_test() {
let mut tested = 0;
for line in CSVReader::new("encoding.csv") {
if line.chars().count() == 0 {
continue;
}
let cols: Vec<&str> = line.split(',').collect();
let lat = cols[2].parse::<i64>().unwrap();
let lng = cols[3].parse::<i64>().unwrap();
let len = cols[4].parse::<usize>().unwrap();
let code = cols[5];

assert_eq!(
encode(Point::new(lng, lat), len),
encode_integers(lat, lng, len),
code,
"encoding lat={},lng={},len={}",
lat,
Expand Down
Loading