1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Expose [`CellIndex::to_local_ij`]

use anyhow::{Context, Result as AnyResult};
use clap::{Parser, ValueEnum};
use h3o::{CellIndex, LocalIJ};
use serde::Serialize;

/// Converts indexes to local IJ coordinates.
///
/// The command reads H3 indexes from stdin and outputs the corresponding IJ
/// coordinates to stdout, until EOF is encountered. `NA` is printed if the IJ
/// coordinates could not be obtained.
#[derive(Parser, Debug)]
pub struct Args {
    /// The origin (or anchoring) index for the IJ coordinate.
    #[arg(short, long)]
    origin: CellIndex,

    /// Cell index.
    #[arg(short, long)]
    index: Option<CellIndex>,

    /// Output format.
    #[arg(short, long, value_enum, default_value_t = Format::Text)]
    format: Format,

    /// Prettify the output (JSON only).
    #[arg(short, long, default_value_t = false)]
    pretty: bool,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
enum Format {
    Text,
    Json,
}

/// Run the `cellToLocalIj` command.
pub fn run(args: &Args) -> AnyResult<()> {
    let indexes = crate::utils::get_cell_indexes(args.index);
    let coords = indexes
        .map(|input| input.map(|index| index.to_local_ij(args.origin).ok()));

    match args.format {
        Format::Text => local_ij_to_text(coords),
        Format::Json => local_ij_to_json(coords, args.pretty),
    }
    .context("cellToLocalIj")?;

    Ok(())
}

/// Print local IJ coordinates as plain text.
fn local_ij_to_text(
    coords: impl IntoIterator<Item = AnyResult<Option<LocalIJ>>>,
) -> AnyResult<()> {
    for coord in coords {
        coord?.map_or_else(
            || println!("NA"),
            |coord| println!("{} {}", coord.coord.i, coord.coord.j),
        );
    }

    Ok(())
}

/// Print local IJ coordinates as JSON.
fn local_ij_to_json(
    coords: impl IntoIterator<Item = AnyResult<Option<LocalIJ>>>,
    pretty: bool,
) -> AnyResult<()> {
    #[derive(Serialize)]
    struct CoordIJ {
        i: i32,
        j: i32,
    }
    let coords = coords
        .into_iter()
        .map(|result| {
            result.map(|value| {
                value.map(|coord| CoordIJ {
                    i: coord.coord.i,
                    j: coord.coord.j,
                })
            })
        })
        .collect::<AnyResult<Vec<_>>>()?;

    crate::json::print(&coords, pretty)
}