rusqlite-gpkg 0.0.8

GeoPackage reader/writer built on top of rusqlite
Documentation
use rusqlite_gpkg::{ColumnSpec, ColumnType, Gpkg, params};
use std::f64::consts::PI;
use std::str::FromStr;
use wkt::Wkt;

fn main() {
    if let Err(err) = run() {
        eprintln!("write_gpkg failed: {err}");
        std::process::exit(1);
    }
}

fn run() -> Result<(), Box<dyn std::error::Error>> {
    let path = std::env::args()
        .nth(1)
        .ok_or("Usage: write_gpkg <output.gpkg>")?;

    let gpkg = Gpkg::open(path)?;

    let columns = vec![
        ColumnSpec {
            name: "name".to_string(),
            column_type: ColumnType::Varchar,
        },
        ColumnSpec {
            name: "region".to_string(),
            column_type: ColumnType::Varchar,
        },
        ColumnSpec {
            name: "center_lat".to_string(),
            column_type: ColumnType::Double,
        },
        ColumnSpec {
            name: "center_lon".to_string(),
            column_type: ColumnType::Double,
        },
        ColumnSpec {
            name: "points".to_string(),
            column_type: ColumnType::Integer,
        },
        ColumnSpec {
            name: "note".to_string(),
            column_type: ColumnType::Varchar,
        },
    ];

    let layer = gpkg.create_layer(
        "stars",
        "geom",
        wkb::reader::GeometryType::Polygon,
        wkb::reader::Dimension::Xy,
        4326,
        &columns,
    )?;

    let tokyo_center = (139.767, 35.681);
    let tokyo_star = star_polygon_wkt(tokyo_center.0, tokyo_center.1, 1.4, 0.6, 5)?;
    let tokyo_name = "Tokyo Star".to_string();
    let tokyo_region = "Tokyo".to_string();
    let tokyo_points = 5_i64;
    let tokyo_note = "Star polygon around Tokyo".to_string();
    layer.insert(
        tokyo_star,
        params![
            tokyo_name,
            tokyo_region,
            tokyo_center.1,
            tokyo_center.0,
            tokyo_points,
            tokyo_note
        ],
    )?;

    let hokkaido_center = (141.3468, 43.0642);
    let hokkaido_star = star_polygon_wkt(hokkaido_center.0, hokkaido_center.1, 2.2, 0.9, 5)?;
    let hokkaido_name = "Hokkaido Star".to_string();
    let hokkaido_region = "Hokkaido".to_string();
    let hokkaido_points = 5_i64;
    let hokkaido_note = "Star polygon around Hokkaido".to_string();
    layer.insert(
        hokkaido_star,
        params![
            hokkaido_name,
            hokkaido_region,
            hokkaido_center.1,
            hokkaido_center.0,
            hokkaido_points,
            hokkaido_note
        ],
    )?;

    Ok(())
}

fn star_polygon_wkt(
    center_lon: f64,
    center_lat: f64,
    outer_radius: f64,
    inner_radius: f64,
    points: usize,
) -> Result<Wkt<f64>, Box<dyn std::error::Error>> {
    let mut coords = Vec::with_capacity(points * 2 + 1);
    let total_vertices = points * 2;
    let start_angle = -PI / 2.0;

    for i in 0..total_vertices {
        let radius = if i % 2 == 0 {
            outer_radius
        } else {
            inner_radius
        };
        let angle = start_angle + (i as f64) * (2.0 * PI / total_vertices as f64);
        let lon = center_lon + radius * angle.cos();
        let lat = center_lat + radius * angle.sin();
        coords.push((lon, lat));
    }

    if let Some(first) = coords.first().copied() {
        coords.push(first);
    }

    let mut ring = String::new();
    for (idx, (lon, lat)) in coords.iter().copied().enumerate() {
        if idx > 0 {
            ring.push_str(", ");
        }
        ring.push_str(&format!("{lon} {lat}"));
    }

    let wkt = format!("POLYGON (({ring}))");
    Ok(Wkt::from_str(&wkt)?)
}