Compare commits

...

2 Commits

2 changed files with 171 additions and 143 deletions

View File

@ -1,133 +1,59 @@
use { use {
clap::Parser, clap::{Parser, Subcommand},
geo_types::Point, geo_types::Point,
image::{ImageBuffer, Luma}, image::{ImageBuffer, Luma},
las::Read as LasRead, las::Read as LasRead,
proj::Proj, proj::Proj,
std::{ std::path::PathBuf,
ops::{Add, Div, Mul, Sub},
path::PathBuf,
},
tokio::io::AsyncReadExt, tokio::io::AsyncReadExt,
}; };
mod geonb; mod geonb;
mod numeric_types;
use numeric_types::{Float, Integer, ToFloat};
#[derive(Parser, Debug)] #[derive(Parser)]
#[clap(author, version, about, long_about=None)] #[command(author, version, about, long_about=None)]
struct Args { struct Args {
// Latitude to fetch LIDAR tile at /// Print extra debug info when initializing graphics
#[clap(long)] #[arg(long)]
latitude: Option<f64>,
// Longitude to fetch LIDAR tile at
#[clap(long)]
longitude: Option<f64>,
// Print extra debug info when initializing raphics
#[clap(long)]
debug_init: bool, debug_init: bool,
#[clap(long)] #[command(subcommand)]
laz_file: Option<PathBuf>, command: Command,
#[clap(long)]
grid_cell_size: Option<f64>,
} }
trait FromFloatFloor<F> { #[derive(Subcommand)]
fn from_float_floor(f: F) -> Self; enum Command {
/// Download data from GeoNB
GeoNB {
#[command(subcommand)]
command: GeoNBCommand,
},
/// Create a grid DEM from point data
GridPoints {
#[arg(long, short = 'i')]
laz_filename: PathBuf,
#[arg(long)]
grid_cell_size: f64,
},
} }
trait ToFloat<F> { #[derive(Subcommand)]
fn to_float(self) -> F; enum GeoNBCommand {
/// Download the LIDAR tile that includes a location
DownloadLidarTile {
/// Latitude to fetch LIDAR tile at
#[arg(long, allow_hyphen_values = true)]
latitude: f64,
/// Longitude to fetch LIDAR tile at
#[arg(long, allow_hyphen_values = true)]
longitude: f64,
},
} }
trait Integer:
Copy
+ Default
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ FromFloatFloor<f32>
+ FromFloatFloor<f64>
+ ToFloat<f32>
+ ToFloat<f64>
{
const MIN: Self;
const MAX: Self;
}
macro_rules! impl_int {
( $t:ident ) => {
impl FromFloatFloor<f32> for $t {
fn from_float_floor(f: f32) -> Self {
f as $t
}
}
impl FromFloatFloor<f64> for $t {
fn from_float_floor(f: f64) -> Self {
f as $t
}
}
impl ToFloat<f32> for $t {
fn to_float(self) -> f32 {
self as f32
}
}
impl ToFloat<f64> for $t {
fn to_float(self) -> f64 {
self as f64
}
}
impl Integer for $t {
const MIN: $t = $t::MIN;
const MAX: $t = $t::MAX;
}
};
}
impl_int!(u8);
impl_int!(i8);
impl_int!(u16);
impl_int!(i16);
impl_int!(u32);
impl_int!(i32);
impl_int!(u64);
impl_int!(i64);
trait Unsigned {}
impl Unsigned for u8 {}
impl Unsigned for u16 {}
impl Unsigned for u32 {}
impl Unsigned for u64 {}
trait Float:
Copy
+ Default
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
{
fn to_int_floor<I: Integer>(self) -> I;
}
macro_rules! impl_float {
( $t:ident ) => {
impl Float for $t {
fn to_int_floor<I: Integer>(self) -> I {
I::from_float_floor(self)
}
}
};
}
impl_float!(f32);
impl_float!(f64);
struct Grid<T> { struct Grid<T> {
width: usize, width: usize,
height: usize, height: usize,
@ -231,38 +157,47 @@ where
async fn main() -> Result<(), anyhow::Error> { async fn main() -> Result<(), anyhow::Error> {
let args = Args::parse(); let args = Args::parse();
if let (Some(latitude), Some(longitude)) = (args.latitude, args.longitude) { match args.command {
let location = Proj::new_known_crs("+proj=longlat +datum=WGS84", "EPSG:2953", None) Command::GeoNB {
.unwrap() command:
.convert(Point::new(longitude, latitude)) GeoNBCommand::DownloadLidarTile {
.unwrap(); latitude,
println!("{:?}", location); longitude,
let mut las_reader = },
tokio::io::BufReader::new(geonb::get_lidar_tile_around_point(location).await?); } => {
let mut las_bytes = Vec::new(); let location = Proj::new_known_crs("+proj=longlat +datum=WGS84", "EPSG:2953", None)
let mut buffer = [0_u8; 4096]; .unwrap()
let mut byte_count = 0; .convert(Point::new(longitude, latitude))
loop { .unwrap();
let num_bytes = las_reader.read(&mut buffer).await?; println!("{:?}", location);
if num_bytes == 0 { let mut las_reader =
break; tokio::io::BufReader::new(geonb::get_lidar_tile_around_point(location).await?);
let mut las_bytes = Vec::new();
let mut buffer = [0_u8; 4096];
let mut byte_count = 0;
loop {
let num_bytes = las_reader.read(&mut buffer).await?;
if num_bytes == 0 {
break;
}
byte_count += num_bytes;
print!("{} bytes read\r", byte_count);
las_bytes.extend_from_slice(&buffer[0..num_bytes]);
} }
byte_count += num_bytes; println!();
print!("{} bytes read\r", byte_count); let mut las_reader = las::Reader::new(std::io::Cursor::new(las_bytes))?;
las_bytes.extend_from_slice(&buffer[0..num_bytes]); for wrapped_point in las_reader.points().take(10) {
let point = wrapped_point.unwrap();
println!("Point coordinates: ({}, {}, {})", point.x, point.y, point.z);
}
Ok(())
} }
println!(); Command::GridPoints {
let mut las_reader = las::Reader::new(std::io::Cursor::new(las_bytes))?; laz_filename,
for wrapped_point in las_reader.points().take(10) { grid_cell_size,
let point = wrapped_point.unwrap(); } => {
println!("Point coordinates: ({}, {}, {})", point.x, point.y, point.z); let mut las_reader = las::Reader::from_path(laz_filename).unwrap();
} let bounds = las_reader.header().bounds();
}
if let Some(laz_filename) = args.laz_file {
let mut las_reader = las::Reader::from_path(laz_filename).unwrap();
let bounds = las_reader.header().bounds();
if let Some(grid_cell_size) = args.grid_cell_size {
let num_cells_x = ((bounds.max.x - bounds.min.x) / grid_cell_size).ceil() as usize; let num_cells_x = ((bounds.max.x - bounds.min.x) / grid_cell_size).ceil() as usize;
let num_cells_y = ((bounds.max.y - bounds.min.y) / grid_cell_size).ceil() as usize; let num_cells_y = ((bounds.max.y - bounds.min.y) / grid_cell_size).ceil() as usize;
let mut height_grid = Grid::new(num_cells_y, num_cells_x); let mut height_grid = Grid::new(num_cells_y, num_cells_x);
@ -288,8 +223,7 @@ async fn main() -> Result<(), anyhow::Error> {
ImageBuffer::from_raw(num_cells_x as u32, num_cells_y as u32, grid_u8.data) ImageBuffer::from_raw(num_cells_x as u32, num_cells_y as u32, grid_u8.data)
.unwrap(); .unwrap();
image.save("test.png")?; image.save("test.png")?;
Ok(())
} }
} }
Ok(())
} }

94
src/numeric_types.rs Normal file
View File

@ -0,0 +1,94 @@
use std::ops::{Add, Div, Mul, Sub};
pub trait FromFloatFloor<F> {
fn from_float_floor(f: F) -> Self;
}
pub trait ToFloat<F> {
fn to_float(self) -> F;
}
pub trait Integer:
Copy
+ Default
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
+ FromFloatFloor<f32>
+ FromFloatFloor<f64>
+ ToFloat<f32>
+ ToFloat<f64>
{
const MIN: Self;
const MAX: Self;
}
macro_rules! impl_int {
( $t:ident ) => {
impl FromFloatFloor<f32> for $t {
fn from_float_floor(f: f32) -> Self {
f as $t
}
}
impl FromFloatFloor<f64> for $t {
fn from_float_floor(f: f64) -> Self {
f as $t
}
}
impl ToFloat<f32> for $t {
fn to_float(self) -> f32 {
self as f32
}
}
impl ToFloat<f64> for $t {
fn to_float(self) -> f64 {
self as f64
}
}
impl Integer for $t {
const MIN: $t = $t::MIN;
const MAX: $t = $t::MAX;
}
};
}
impl_int!(u8);
impl_int!(i8);
impl_int!(u16);
impl_int!(i16);
impl_int!(u32);
impl_int!(i32);
impl_int!(u64);
impl_int!(i64);
pub trait Unsigned {}
impl Unsigned for u8 {}
impl Unsigned for u16 {}
impl Unsigned for u32 {}
impl Unsigned for u64 {}
pub trait Float:
Copy
+ Default
+ Add<Self, Output = Self>
+ Sub<Self, Output = Self>
+ Mul<Self, Output = Self>
+ Div<Self, Output = Self>
{
fn to_int_floor<I: Integer>(self) -> I;
}
macro_rules! impl_float {
( $t:ident ) => {
impl Float for $t {
fn to_int_floor<I: Integer>(self) -> I {
I::from_float_floor(self)
}
}
};
}
impl_float!(f32);
impl_float!(f64);