use ndarray::Array2;
use super::*;
fn apply_base_adjustment(
idx: usize,
ab: &Array1<u8>,
cd: &Array1<u8>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
lat[idx] = ab[idx] as f64 * UNIT_LAT_LV1;
lon[idx] = cd[idx] as f64 * UNIT_LON_LV1 + 100.0;
}
fn apply_level_40000(idx: usize, e: &Array1<u8>, lat: &mut Array1<f64>, lon: &mut Array1<f64>) {
if e[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_40000;
}
if e[idx] % 2 == 0 {
lon[idx] += UNIT_LON_40000;
}
}
fn apply_level_2(
idx: usize,
e: &Array1<u8>,
f: &Array1<u8>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
lat[idx] += e[idx] as f64 * UNIT_LAT_LV2;
lon[idx] += f[idx] as f64 * UNIT_LON_LV2;
}
fn apply_level_3(
idx: usize,
e: &Array1<u8>,
f: &Array1<u8>,
g: &Array1<u8>,
h: &Array1<u8>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
apply_level_2(idx, e, f, lat, lon);
lat[idx] += g[idx] as f64 * UNIT_LAT_LV3;
lon[idx] += h[idx] as f64 * UNIT_LON_LV3;
}
fn apply_level_4(
idx: usize,
e: &Array1<u8>,
f: &Array1<u8>,
g: &Array1<u8>,
h: &Array1<u8>,
i: &Array1<u8>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
apply_level_3(idx, e, f, g, h, lat, lon);
if i[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_LV4;
}
if i[idx] % 2 == 0 {
lon[idx] += UNIT_LON_LV4;
}
}
fn apply_level_5(
idx: usize,
e: &Array1<u8>,
f: &Array1<u8>,
g: &Array1<u8>,
h: &Array1<u8>,
i: &Array1<u8>,
j: &Array1<u8>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
apply_level_4(idx, e, f, g, h, i, lat, lon);
if j[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_LV5;
}
if j[idx] % 2 == 0 {
lon[idx] += UNIT_LON_LV5;
}
}
fn apply_multipliers(
idx: usize,
level: MeshLevel,
lat_multiplier: &Array1<f64>,
lon_multiplier: &Array1<f64>,
lat: &mut Array1<f64>,
lon: &mut Array1<f64>,
) {
lat[idx] += unit_lat(level) * lat_multiplier[idx.min(lat_multiplier.len() - 1)];
lon[idx] += unit_lon(level) * lon_multiplier[idx.min(lon_multiplier.len() - 1)];
}
pub fn to_meshpoint(
meshcode: Array1<u64>,
lat_multiplier: Array1<f64>,
lon_multiplier: Array1<f64>,
) -> Result<Array2<f64>> {
let meshcode_len = meshcode.len();
let level = to_meshlevel(&meshcode)?;
let ab = slice(&meshcode, 0, 2);
let cd = slice(&meshcode, 2, 4);
let e = slice(&meshcode, 4, 5);
let f = slice(&meshcode, 5, 6);
let g = slice(&meshcode, 6, 7);
let h = slice(&meshcode, 7, 8);
let i = slice(&meshcode, 8, 9);
let j = slice(&meshcode, 9, 10);
let k = slice(&meshcode, 10, 11);
let mut lat = Array1::zeros(meshcode_len);
let mut lon = Array1::zeros(meshcode_len);
for idx in 0..meshcode_len {
apply_base_adjustment(idx, &ab, &cd, &mut lat, &mut lon);
match level[idx] {
MeshLevel::Lv1 => {}
MeshLevel::X40 => {
apply_level_40000(idx, &e, &mut lat, &mut lon);
}
MeshLevel::X20 => {
apply_level_40000(idx, &e, &mut lat, &mut lon);
if f[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_20000;
}
if f[idx] % 2 == 0 {
lon[idx] += UNIT_LON_20000;
}
}
MeshLevel::X16 => {
lat[idx] += (e[idx] / 2) as f64 * UNIT_LAT_16000;
lon[idx] += (f[idx] / 2) as f64 * UNIT_LON_16000;
}
MeshLevel::X8 => {
lat[idx] += e[idx] as f64 * UNIT_LAT_8000;
lon[idx] += f[idx] as f64 * UNIT_LON_8000;
}
MeshLevel::X4 => {
lat[idx] += e[idx] as f64 * UNIT_LAT_8000;
lon[idx] += f[idx] as f64 * UNIT_LON_8000;
if h[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_4000;
}
if h[idx] % 2 == 0 {
lon[idx] += UNIT_LON_4000;
}
}
MeshLevel::Lv2 => {
apply_level_2(idx, &e, &f, &mut lat, &mut lon);
}
MeshLevel::X5 => {
apply_level_2(idx, &e, &f, &mut lat, &mut lon);
if g[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_5000;
}
if g[idx] % 2 == 0 {
lon[idx] += UNIT_LON_5000;
}
}
MeshLevel::X2_5 => {
apply_level_2(idx, &e, &f, &mut lat, &mut lon);
if g[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_5000;
}
if g[idx] % 2 == 0 {
lon[idx] += UNIT_LON_5000;
}
if h[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_2500;
}
if h[idx] % 2 == 0 {
lon[idx] += UNIT_LON_2500;
}
}
MeshLevel::X2 => {
apply_level_2(idx, &e, &f, &mut lat, &mut lon);
lat[idx] += (g[idx] / 2) as f64 * UNIT_LAT_2000;
lon[idx] += (h[idx] / 2) as f64 * UNIT_LON_2000;
}
MeshLevel::Lv3 => {
apply_level_3(idx, &e, &f, &g, &h, &mut lat, &mut lon);
}
MeshLevel::Lv4 => {
apply_level_4(idx, &e, &f, &g, &h, &i, &mut lat, &mut lon);
}
MeshLevel::Lv5 => {
apply_level_5(idx, &e, &f, &g, &h, &i, &j, &mut lat, &mut lon);
}
MeshLevel::Lv6 => {
apply_level_5(idx, &e, &f, &g, &h, &i, &j, &mut lat, &mut lon);
if k[idx] / 3 == 1 {
lat[idx] += UNIT_LAT_LV6;
}
if k[idx] % 2 == 0 {
lon[idx] += UNIT_LON_LV6;
}
}
}
apply_multipliers(
idx,
level[idx],
&lat_multiplier,
&lon_multiplier,
&mut lat,
&mut lon,
);
}
let mut result = Array2::zeros((2, meshcode_len));
for idx in 0..meshcode_len {
result[[0, idx]] = lat[idx];
result[[1, idx]] = lon[idx];
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use ndarray::array;
#[test]
fn test_to_meshpoint() {
let test_cases = vec![
(5339u64, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(53391, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(5339115, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(5339007, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(533900, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(5339006, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(5339001, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(533900617, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(533900116, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(533900005, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(53390000, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(533900001, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(5339000011, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(53390000111, 0.0, 0.0, 35.0 + 1.0 / 3.0, 139.0),
(53393599212, 0.5, 0.5, 35.6588542, 139.74609375),
];
for (meshcode, lat_multiplier, lon_multiplier, expected_lat, expected_lon) in test_cases {
let meshcode_array = array![meshcode as u64];
let lat_multiplier_array = array![lat_multiplier];
let lon_multiplier_array = array![lon_multiplier];
let result =
to_meshpoint(meshcode_array, lat_multiplier_array, lon_multiplier_array).unwrap();
assert_relative_eq!(result[[0, 0]], expected_lat, epsilon = 1e-7);
assert_relative_eq!(result[[1, 0]], expected_lon, epsilon = 1e-7);
}
}
#[test]
fn test_to_meshpoint_vector() {
let num_elements = 10;
let meshcode_value = 53390000111;
let expected_lat = 35.0 + 1.0 / 3.0;
let expected_lon = 139.0;
let meshcode_array = Array1::from_elem(num_elements, meshcode_value);
let lat_multiplier_array = Array1::zeros(num_elements);
let lon_multiplier_array = Array1::zeros(num_elements);
let result =
to_meshpoint(meshcode_array, lat_multiplier_array, lon_multiplier_array).unwrap();
for i in 0..num_elements {
assert_relative_eq!(result[[0, i]], expected_lat, epsilon = 1e-7);
assert_relative_eq!(result[[1, i]], expected_lon, epsilon = 1e-7);
}
}
}