Give a geographic location, download and read LIDAR from GeoNB
This commit is contained in:
parent
054d107d3c
commit
c54de32d30
|
|
@ -0,0 +1,3 @@
|
|||
/target
|
||||
*~
|
||||
\#*#
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "pteropus"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
serde_json = "~1.0"
|
||||
tokio = {version="~1.17", features=["rt-multi-thread", "macros"]}
|
||||
tokio-util = {version="~0.7", features=["io"]}
|
||||
reqwest = {version="~0.11", features=["json", "stream"]}
|
||||
proj = "~0.25"
|
||||
geo-types = "~0.7"
|
||||
clap = {version="~3.1", features=["derive"]}
|
||||
anyhow = "~1.0"
|
||||
futures = "~0.3"
|
||||
bytes = "~1.1"
|
||||
las = {version="~0.7", features=["laz"]}
|
||||
|
|
@ -0,0 +1,111 @@
|
|||
use {futures::stream::TryStreamExt, geo_types::Point, tokio_util::io::StreamReader};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Error {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn new(message: &str) -> Error {
|
||||
let message = message.to_string();
|
||||
Error { message }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "GeoNB Error: \"{}\"", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {}
|
||||
fn convert_err(err: reqwest::Error) -> std::io::Error {
|
||||
todo!()
|
||||
}
|
||||
pub async fn get_lidar_tile_around_point(
|
||||
location: Point<f64>,
|
||||
) -> Result<impl tokio::io::AsyncRead, anyhow::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
let query_response_json = &client
|
||||
.get("https://geonb.snb.ca/arcgis/rest/services/GeoNB_SNB_LidarIndex/MapServer/1/query")
|
||||
.query(&[
|
||||
("f", "json"),
|
||||
("geometryType", "esriGeometryPoint"),
|
||||
("geometry", &format!("{},{}", location.x(), location.y())),
|
||||
("returnIdsOnly", "true"),
|
||||
])
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
.await?;
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&query_response_json).expect("JSON")
|
||||
);
|
||||
|
||||
let object_id = query_response_json
|
||||
.get("objectIds")
|
||||
.and_then(|object_ids| object_ids.get(0))
|
||||
.and_then(&serde_json::Value::as_i64)
|
||||
.ok_or_else(|| Error::new("Could not find \"objectId\" in response."))?;
|
||||
|
||||
let object_response_json = client
|
||||
.get(format!(
|
||||
"https://geonb.snb.ca/arcgis/rest/services/GeoNB_SNB_LidarIndex/MapServer/1/{}",
|
||||
object_id
|
||||
))
|
||||
.query(&[("f", "json")])
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>()
|
||||
.await?;
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&object_response_json).expect("JSON")
|
||||
);
|
||||
|
||||
let laz_file_url = object_response_json
|
||||
.get("feature")
|
||||
.ok_or_else(|| Error::new("Could not find \"feature\" in response."))?
|
||||
.get("attributes")
|
||||
.ok_or_else(|| Error::new("Could not find \"attributes\" in response."))?
|
||||
.get("FILE_URL")
|
||||
.ok_or_else(|| Error::new("Could not find \"FILE_URL\" in response."))?
|
||||
.as_str()
|
||||
.ok_or_else(|| Error::new("Expected \"FILE_URL\" to be a string but it was not."))?;
|
||||
|
||||
println!("LAZ URL: {}", laz_file_url);
|
||||
Ok(StreamReader::new(
|
||||
client
|
||||
.get(laz_file_url)
|
||||
.send()
|
||||
.await?
|
||||
.bytes_stream()
|
||||
.map_err(convert_err),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn test() -> Result<(), reqwest::Error> {
|
||||
let client = reqwest::Client::new();
|
||||
print!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&client
|
||||
.get("https://geonb.snb.ca/arcgis/rest/services/GeoNB_SNB_LidarIndex/MapServer/1/query")
|
||||
.query(&[("f", "json"),("geometryType","esriGeometryPoint"),("geometry","2470000,7443000"),("returnIdsOnly","true")])
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>().await?).expect("JSON")
|
||||
);
|
||||
print!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&client
|
||||
.get("https://geonb.snb.ca/arcgis/rest/services/GeoNB_SNB_LidarIndex/MapServer/1/14601")
|
||||
.query(&[("f", "json")])
|
||||
.send()
|
||||
.await?
|
||||
.json::<serde_json::Value>().await?).expect("JSON")
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
use {clap::Parser, geo_types::Point, las::Read as LasRead, proj::Proj, tokio::io::AsyncReadExt};
|
||||
|
||||
mod geonb;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about=None)]
|
||||
struct Args {
|
||||
// Latitude to fetch LIDAR tile at
|
||||
#[clap(long)]
|
||||
latitude: f64,
|
||||
|
||||
// Longitude to fetch LIDAR tile at
|
||||
#[clap(long)]
|
||||
longitude: f64,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), anyhow::Error> {
|
||||
let args = Args::parse();
|
||||
|
||||
let location = Proj::new_known_crs("+proj=longlat +datum=WGS84", "EPSG:2953", None)
|
||||
.unwrap()
|
||||
.convert(Point::new(args.longitude, args.latitude))
|
||||
.unwrap();
|
||||
println!("{:?}", location);
|
||||
let mut las_reader =
|
||||
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]);
|
||||
}
|
||||
println!();
|
||||
let mut las_reader = las::Reader::new(std::io::Cursor::new(las_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(())
|
||||
}
|
||||
Loading…
Reference in New Issue