use std::io::Write;
use std::path::Path;
use std::process::ExitCode;
use geopackage::core::tiles::TileCoord;
use geopackage::{Coverage, GeoPackage};
use crate::error::Result;
pub fn info(path: &Path, coverage_name: Option<&str>, out: &mut impl Write) -> Result<ExitCode> {
let gpkg = GeoPackage::open_read_only_lenient(path)?;
let coverages = match coverage_name {
Some(name) => vec![gpkg.coverage(name)?],
None => gpkg.coverages()?,
};
if coverages.is_empty() {
writeln!(out, "{}: no tiled gridded coverages", path.display())?;
return Ok(ExitCode::SUCCESS);
}
for coverage in &coverages {
print_coverage(&gpkg, coverage, out)?;
}
Ok(ExitCode::SUCCESS)
}
fn print_coverage(gpkg: &GeoPackage, coverage: &Coverage<'_>, out: &mut impl Write) -> Result<()> {
let set = coverage.matrix_set();
let ancillary = coverage.ancillary();
writeln!(out, "coverage {:?}", coverage.table_name())?;
writeln!(out, " tiles: {}", coverage.tile_count()?)?;
writeln!(
out,
" samples: {}{}",
ancillary.datatype,
match ancillary.uom.as_deref() {
Some(uom) => format!(" in {uom}"),
None => String::new(),
}
)?;
if let Some(field_name) = &ancillary.field_name {
writeln!(out, " field: {field_name}")?;
}
writeln!(
out,
" scale: {} offset: {}",
ancillary.scale, ancillary.offset
)?;
match ancillary.data_null {
Some(null) => writeln!(out, " null: {null}")?,
None => writeln!(out, " null: none declared")?,
}
if let Some(encoding) = &ancillary.grid_cell_encoding {
writeln!(out, " cells: {encoding}")?;
}
if let Some(srs) = gpkg.srs(set.srs_id)? {
writeln!(
out,
" srs: {} ({}:{})",
srs.name, srs.organization, srs.organization_coordsys_id
)?;
}
writeln!(
out,
" extent: {} {} {} {}",
set.min_x, set.min_y, set.max_x, set.max_y
)?;
writeln!(out, " zoom levels:")?;
for matrix in coverage.matrices() {
writeln!(
out,
" {:<3} {:>6} x {:<6} grid, {} x {} px, {} stored",
matrix.zoom_level,
matrix.matrix_width,
matrix.matrix_height,
matrix.tile_width,
matrix.tile_height,
coverage.tile_count_at(matrix.zoom_level)?
)?;
}
if let Some(matrix) = coverage.matrices().first() {
let mut cursor = coverage.cursor_at(matrix.zoom_level)?;
let mut stream = cursor.tiles()?;
if let Some(tile) = stream.next()? {
let described = match coverage.check_payload(tile.data()) {
Ok(payload) => format!(
"{} {:?} {}x{}",
payload.mime_type(),
payload.sample_type(),
payload.width(),
payload.height()
),
Err(error) => format!("unusable: {error}"),
};
writeln!(out, " payloads: {described} (first tile)")?;
}
}
Ok(())
}
pub fn get(
path: &Path,
coverage_name: &str,
coord: TileCoord,
out_path: Option<&Path>,
out: &mut impl Write,
) -> Result<ExitCode> {
let gpkg = GeoPackage::open_read_only_lenient(path)?;
let coverage = gpkg.coverage(coverage_name)?;
let Some(bytes) = coverage.get_tile(coord)? else {
eprintln!(
"gpkg: no tile at {}/{}/{} in {coverage_name}",
coord.zoom_level, coord.column, coord.row
);
return Ok(ExitCode::FAILURE);
};
match out_path {
Some(out_path) => {
std::fs::write(out_path, &bytes)?;
let described = match coverage.check_payload(&bytes) {
Ok(payload) => format!(
"{} {:?} {}x{}",
payload.mime_type(),
payload.sample_type(),
payload.width(),
payload.height()
),
Err(error) => format!("unusable payload: {error}"),
};
writeln!(
out,
"wrote {} bytes to {} ({described})",
bytes.len(),
out_path.display()
)?;
if let Some(stats) = coverage.tile_ancillary(coord)?
&& let (Some(min), Some(max)) = (stats.min, stats.max)
{
writeln!(
out,
" recorded range {} to {} (values {} to {})",
min,
max,
coverage.value(&stats, min),
coverage.value(&stats, max)
)?;
}
}
None => {
out.write_all(&bytes)?;
}
}
Ok(ExitCode::SUCCESS)
}