h3o_cli/commands/
cell_to_latlng.rs

1//! Expose [`LatLng::from`](./struct.LatLng.html#impl-From<CellIndex>-for-LatLng)
2
3use anyhow::{Context, Result as AnyResult};
4use clap::{Parser, ValueEnum};
5use geojson::{FeatureCollection, GeoJson};
6use h3o::{CellIndex, LatLng};
7use kml::Kml;
8
9/// Converts indexes to latitude/longitude center coordinates in degrees.
10///
11/// The command reads H3 indexes from stdin and outputs the corresponding cell
12/// center points to stdout, until EOF is encountered.
13#[derive(Parser, Debug)]
14pub struct Args {
15    /// Cell index.
16    #[arg(short, long)]
17    index: Option<CellIndex>,
18
19    /// Output format.
20    #[arg(short, long, value_enum, default_value_t = Format::Text)]
21    format: Format,
22
23    /// Prettify the output.
24    #[arg(short, long, default_value_t = false)]
25    pretty: bool,
26}
27
28#[derive(Debug, Copy, Clone, PartialEq, Eq, ValueEnum)]
29enum Format {
30    Text,
31    Json,
32    Geojson,
33    Kml,
34}
35
36/// Run the `cellToLatLng` command.
37pub fn run(args: &Args) -> AnyResult<()> {
38    let indexes = crate::utils::get_cell_indexes(args.index);
39
40    match args.format {
41        Format::Text => latlng_to_text(indexes),
42        Format::Json => latlng_to_json(indexes, args.pretty),
43        Format::Geojson => latlng_to_geojson(indexes, args.pretty),
44        Format::Kml => latlng_to_kml(indexes),
45    }
46    .context("cellToLatLng")?;
47
48    Ok(())
49}
50
51/// Print lat/lng as plain text.
52fn latlng_to_text(
53    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
54) -> AnyResult<()> {
55    for ll in indexes.into_iter().map(|input| input.map(LatLng::from)) {
56        let ll = ll?;
57        println!("{:.9} {:.9}", ll.lat(), ll.lng());
58    }
59
60    Ok(())
61}
62
63/// Print lat/lng as JSON.
64fn latlng_to_json(
65    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
66    pretty: bool,
67) -> AnyResult<()> {
68    let coords = indexes
69        .into_iter()
70        .map(|input| input.map(LatLng::from))
71        .collect::<AnyResult<Vec<_>>>()?;
72
73    crate::json::print(&coords, pretty)
74}
75
76/// Print lat/lng as geojson.
77fn latlng_to_geojson(
78    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
79    pretty: bool,
80) -> AnyResult<()> {
81    let indexes = indexes.into_iter().collect::<AnyResult<Vec<_>>>()?;
82    let features = crate::geojson::centers(&indexes);
83    let geojson = GeoJson::FeatureCollection(FeatureCollection {
84        bbox: None,
85        features,
86        foreign_members: None,
87    });
88
89    crate::json::print(&geojson, pretty)
90}
91
92/// Print lat/lng as KML.
93fn latlng_to_kml(
94    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
95) -> AnyResult<()> {
96    let indexes = indexes.into_iter().collect::<AnyResult<Vec<_>>>()?;
97    // Define styles.
98    let style = kml::types::Style {
99        id: Some("s_circle".to_owned()),
100        icon: Some(kml::types::IconStyle{
101            scale: 1.1,
102            icon: kml::types::Icon {
103                href: "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png".to_owned(),
104                ..kml::types::Icon::default()
105            },
106            hot_spot: Some(kml::types::Vec2 {
107                x: 20.,
108                y: 2.,
109                xunits: kml::types::Units::Pixels,
110                yunits: kml::types::Units::Pixels,
111            }),
112            ..kml::types::IconStyle::default()
113        }),
114        label: Some(kml::types::LabelStyle{
115            color: "ff0000ff".to_owned(),
116            scale: 2.,
117            ..kml::types::LabelStyle::default()
118        }),
119        ..kml::types::Style::default()
120    };
121    let style_hl = kml::types::Style {
122        id: Some("s_circle_hl".to_owned()),
123        icon: Some(kml::types::IconStyle{
124            scale: 1.3,
125            icon: kml::types::Icon {
126                href: "http://maps.google.com/mapfiles/kml/shapes/placemark_circle.png".to_owned(),
127                ..kml::types::Icon::default()
128            },
129            hot_spot: Some(kml::types::Vec2 {
130                x: 20.,
131                y: 2.,
132                xunits: kml::types::Units::Pixels,
133                yunits: kml::types::Units::Pixels,
134            }),
135            ..kml::types::IconStyle::default()
136        }),
137        label: Some(kml::types::LabelStyle{
138            color: "ff0000ff".to_owned(),
139            scale: 2.,
140            ..kml::types::LabelStyle::default()
141        }),
142        ..kml::types::Style::default()
143    };
144    let style_id = "m_ylw-pushpin";
145    let style_map = kml::types::StyleMap {
146        id: Some(style_id.to_owned()),
147        pairs: vec![
148            kml::types::Pair {
149                key: "normal".to_owned(),
150                style_url: "#s_circle".to_owned(),
151                ..kml::types::Pair::default()
152            },
153            kml::types::Pair {
154                key: "highlight".to_owned(),
155                style_url: "#s_circle_hl".to_owned(),
156                ..kml::types::Pair::default()
157            },
158        ],
159        ..kml::types::StyleMap::default()
160    };
161    let mut elements = vec![
162        Kml::Style(style),
163        Kml::Style(style_hl),
164        Kml::StyleMap(style_map),
165    ];
166    elements.append(&mut crate::kml::centers(&indexes, style_id));
167
168    crate::kml::print_document(
169        "H3 Geometry".to_owned(),
170        "Generated by cellToLatLng".to_owned(),
171        elements,
172    )
173}