h3o-cli 0.2.10

A CLI app that exposes most of the h3o API for scripting.
Documentation
//! Expose [`CellIndex::boundary`]

use anyhow::{Context, Result as AnyResult};
use clap::{Parser, ValueEnum};
use geojson::{FeatureCollection, GeoJson};
use h3o::CellIndex;
use kml::Kml;

/// Converts indexes to latitude/longitude cell boundaries in degrees.
///
/// This command reads H3 indexes from stdin and outputs the corresponding cell
/// boundaries to stdout, until EOF is encountered.
#[derive(Parser, Debug)]
pub struct Args {
    /// 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 (`GeoJSON` only).
    #[arg(short, long, default_value_t = false)]
    pretty: bool,
}

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

/// Run the `cellToBoundary` command.
pub fn run(args: &Args) -> AnyResult<()> {
    let indexes = crate::utils::get_cell_indexes(args.index);

    match args.format {
        Format::Text => boundaries_to_text(indexes),
        Format::Geojson => boundaries_to_geojson(indexes, args.pretty),
        Format::Kml => boundaries_to_kml(indexes),
    }
    .context("cellToBoundary")?;

    Ok(())
}

/// Print boundaries as plain text.
fn boundaries_to_text(
    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
) -> AnyResult<()> {
    for index in indexes {
        let index = index?;
        println!("{index}");
        println!("{{");
        for ll in &*index.boundary() {
            println!("   {:.9} {:.9}", ll.lat(), ll.lng());
        }
        println!("}}");
    }

    Ok(())
}

/// Print boundaries as geojson.
fn boundaries_to_geojson(
    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
    pretty: bool,
) -> AnyResult<()> {
    let indexes = indexes.into_iter().collect::<AnyResult<Vec<_>>>()?;
    let features = crate::geojson::boundaries(&indexes);
    let geojson = GeoJson::FeatureCollection(FeatureCollection {
        bbox: None,
        features,
        foreign_members: None,
    });

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

/// Print boundaries as KML.
fn boundaries_to_kml(
    indexes: impl IntoIterator<Item = AnyResult<CellIndex>>,
) -> AnyResult<()> {
    let indexes = indexes.into_iter().collect::<AnyResult<Vec<_>>>()?;
    let style_id = "lineStyle1";
    let style = kml::types::Style {
        id: Some(style_id.to_owned()),
        line: Some(kml::types::LineStyle {
            id: Some("lineStyle2".to_owned()),
            color: "ff0000ff".to_owned(),
            width: 2.,
            ..kml::types::LineStyle::default()
        }),
        ..kml::types::Style::default()
    };

    let mut elements = vec![Kml::Style(style)];
    elements.append(&mut crate::kml::boundaries(&indexes, style_id));

    crate::kml::print_document(
        "H3 Geometry".to_owned(),
        "Generated by cellToBoundary".to_owned(),
        elements,
    )
}