// Maidenhead locators (also called QTH locators): alternating letter and
// digit pairs that divide the world into fields, squares, subsquares and
// extended squares. Amateur radio uses four or six characters most often;
// eight and ten are supported here as well.
///|
/// Encode a point as a Maidenhead locator. `precision` is the number of
/// characters and must be even, from 2 to 10.
pub fn maidenhead_encode(point : LatLng, precision? : Int = 6) -> String? {
if precision < 2 || precision > 10 || precision % 2 == 1 {
return None
}
let lng_widths : Array[Double] = [
20.0,
2.0,
2.0 / 24.0,
2.0 / 240.0,
2.0 / 2400.0,
]
let lat_widths : Array[Double] = [
10.0,
1.0,
1.0 / 24.0,
1.0 / 240.0,
1.0 / 2400.0,
]
let mut lng = point.lng() + 180.0
let mut lat = point.lat() + 90.0
let out : Array[Char] = []
let pairs = precision / 2
for pair in 0.. BoundingBox? {
let s = ascii_upper(trim_ascii(locator))
let chars = chars_of(s)
if chars.length() < 2 || chars.length() > 10 || chars.length() % 2 == 1 {
return None
}
let lng_widths : Array[Double] = [
20.0,
2.0,
2.0 / 24.0,
2.0 / 240.0,
2.0 / 2400.0,
]
let lat_widths : Array[Double] = [
10.0,
1.0,
1.0 / 24.0,
1.0 / 240.0,
1.0 / 2400.0,
]
let mut lng = -180.0
let mut lat = -90.0
let mut i = 0
while i < chars.length() {
let pair = i / 2
let lng_char = chars[i]
let lat_char = chars[i + 1]
let mut lng_index = -1
let mut lat_index = -1
if pair % 2 == 0 {
if lng_char >= 'A' && lng_char <= 'X' {
lng_index = lng_char.to_int() - 'A'.to_int()
}
if lat_char >= 'A' && lat_char <= 'X' {
lat_index = lat_char.to_int() - 'A'.to_int()
}
if lng_index > (if pair == 0 { 17 } else { 23 }) ||
lat_index > (if pair == 0 { 17 } else { 23 }) {
return None
}
} else {
if lng_char >= '0' && lng_char <= '9' {
lng_index = lng_char.to_int() - '0'.to_int()
}
if lat_char >= '0' && lat_char <= '9' {
lat_index = lat_char.to_int() - '0'.to_int()
}
}
if lng_index < 0 || lat_index < 0 {
return None
}
lng = lng + lng_index.to_double() * lng_widths[pair]
lat = lat + lat_index.to_double() * lat_widths[pair]
i = i + 2
}
let width = lng_widths[chars.length() / 2 - 1]
let height = lat_widths[chars.length() / 2 - 1]
BoundingBox::new(lat, lng, lat + height, lng + width)
}
///|
/// The centre of a Maidenhead locator.
pub fn maidenhead_decode(locator : String) -> LatLng? {
match maidenhead_bounds(locator) {
None => None
Some(b) => b.center()
}
}
///|
/// Clamp a cell index into `[0, max]`. Only the exact north/east edge of the
/// world can overflow, and there the last cell is the right answer.
fn clamp_index(value : Int, max : Int) -> Int {
if value < 0 {
0
} else if value > max {
max
} else {
value
}
}