Add js endpoint
Signed-off-by: Frank Villaro-Dixon <frank@villaro-dixon.eu>
This commit is contained in:
parent
cd4ccee05c
commit
8b85c31411
2 changed files with 65 additions and 25 deletions
18
src/dem.rs
18
src/dem.rs
|
@ -61,7 +61,11 @@ impl Clone for DatasetRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn elevation_from_coordinates(dr: &DatasetRepository, lat: f64, lon: f64) -> Option<f64> {
|
pub async fn elevation_from_coordinates(
|
||||||
|
dr: &DatasetRepository,
|
||||||
|
lat: f64,
|
||||||
|
lon: f64,
|
||||||
|
) -> Result<Option<f64>, GdalError> {
|
||||||
let span = debug_span!("req", lat=%lat, lon=%lon);
|
let span = debug_span!("req", lat=%lat, lon=%lon);
|
||||||
let _guard = span.enter();
|
let _guard = span.enter();
|
||||||
|
|
||||||
|
@ -70,17 +74,15 @@ pub async fn elevation_from_coordinates(dr: &DatasetRepository, lat: f64, lon: f
|
||||||
|
|
||||||
let ds = &match dr.get(filename).await {
|
let ds = &match dr.get(filename).await {
|
||||||
Some(x) => x,
|
Some(x) => x,
|
||||||
None => return None,
|
None => return Ok(None),
|
||||||
}
|
}
|
||||||
.ds;
|
.ds;
|
||||||
|
|
||||||
let (px, py) = geo_to_pixel(ds, lat, lon).unwrap();
|
let (px, py) = geo_to_pixel(ds, lat, lon)?;
|
||||||
|
|
||||||
let raster_band = ds.rasterband(1).unwrap();
|
let raster_band = ds.rasterband(1)?;
|
||||||
let raster_value = raster_band
|
let raster_value = raster_band.read_as::<f64>((px, py), (1, 1), (1, 1), None)?;
|
||||||
.read_as::<f64>((px, py), (1, 1), (1, 1), None)
|
Ok(Some(raster_value.data[0]))
|
||||||
.unwrap();
|
|
||||||
Some(raster_value.data[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_filename_from_latlon(lat: f64, lon: f64) -> String {
|
fn get_filename_from_latlon(lat: f64, lon: f64) -> String {
|
||||||
|
|
70
src/main.rs
70
src/main.rs
|
@ -1,21 +1,26 @@
|
||||||
mod dem;
|
mod dem;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
extract::{Extension, Path, Query, State}, http::{response, StatusCode}, response::{IntoResponse, Response}, routing::get, Json, Router
|
extract::{Extension, Path, Query, State},
|
||||||
|
http::{response, StatusCode},
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
routing::get,
|
||||||
|
Json, Router,
|
||||||
};
|
};
|
||||||
use axum_macros::debug_handler;
|
use axum_macros::debug_handler;
|
||||||
use std::{borrow::Borrow, fmt};
|
use std::{borrow::Borrow, fmt};
|
||||||
use std::{env, str::FromStr};
|
use std::{env, str::FromStr};
|
||||||
|
use tokio::sync::mpsc::error;
|
||||||
|
|
||||||
|
use serde::ser::{SerializeSeq, Serializer};
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
use serde_qs::axum::{QsQuery, QsQueryConfig, QsQueryRejection};
|
||||||
use tower_http::trace::TraceLayer;
|
use tower_http::trace::TraceLayer;
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
services::ServeDir,
|
services::ServeDir,
|
||||||
trace::{self, DefaultMakeSpan},
|
trace::{self, DefaultMakeSpan},
|
||||||
};
|
};
|
||||||
use tracing::{info, Level, Span};
|
use tracing::{error, info, Level, Span};
|
||||||
use serde_qs::axum::{QsQuery, QsQueryConfig, QsQueryRejection};
|
|
||||||
|
|
||||||
|
|
||||||
use dem::DatasetRepository;
|
use dem::DatasetRepository;
|
||||||
|
|
||||||
|
@ -36,9 +41,29 @@ struct JsParams{
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct JsResult {
|
struct JsResult {
|
||||||
|
#[serde(serialize_with = "serialize_vec_round")]
|
||||||
elevations: Vec<Option<f64>>,
|
elevations: Vec<Option<f64>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_vec_round<S>(v: &Vec<Option<f64>>, s: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
let mut sv = s.serialize_seq(Some(v.len()))?;
|
||||||
|
for e in v {
|
||||||
|
match e {
|
||||||
|
None => sv.serialize_element(&e)?,
|
||||||
|
Some(x) => {
|
||||||
|
// Round the f64 to 1 decimal place. This is ugly as shit.
|
||||||
|
let fmt = format!("{:.1}", x);
|
||||||
|
let xx = fmt.parse::<f64>().unwrap();
|
||||||
|
sv.serialize_element(&xx)?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
sv.end()
|
||||||
|
}
|
||||||
|
|
||||||
fn deserialize_array<'de, D>(deserializer: D) -> Result<Vec<(f64, f64)>, D::Error>
|
fn deserialize_array<'de, D>(deserializer: D) -> Result<Vec<(f64, f64)>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
|
@ -47,7 +72,9 @@ where
|
||||||
let result: Result<Vec<(f64, f64)>, serde_json::Error> = serde_json::from_str(&s);
|
let result: Result<Vec<(f64, f64)>, serde_json::Error> = serde_json::from_str(&s);
|
||||||
match result {
|
match result {
|
||||||
Ok(x) => Ok(x),
|
Ok(x) => Ok(x),
|
||||||
Err(e) => Err(serde::de::Error::custom("Invalid array: ".to_string() + &e.to_string())),
|
Err(e) => Err(serde::de::Error::custom(
|
||||||
|
"Invalid array: ".to_string() + &e.to_string(),
|
||||||
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,9 +110,14 @@ async fn main() {
|
||||||
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
.make_span_with(trace::DefaultMakeSpan::new().level(Level::INFO))
|
||||||
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
|
.on_response(trace::DefaultOnResponse::new().level(Level::INFO)),
|
||||||
)
|
)
|
||||||
.layer(Extension(QsQueryConfig::new(5, false)
|
.layer(Extension(QsQueryConfig::new(5, false).error_handler(
|
||||||
.error_handler(|err| {
|
|err| {
|
||||||
QsQueryRejection::new(format!("Get fucked: {}", err), StatusCode::UNPROCESSABLE_ENTITY)})));
|
QsQueryRejection::new(
|
||||||
|
format!("Get fucked: {}", err),
|
||||||
|
StatusCode::UNPROCESSABLE_ENTITY,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
|
||||||
let host = format!("[::]:{}", config.port);
|
let host = format!("[::]:{}", config.port);
|
||||||
info!("Will start server on {host}");
|
info!("Will start server on {host}");
|
||||||
|
@ -106,10 +138,13 @@ async fn get_elevation(
|
||||||
let ele;
|
let ele;
|
||||||
match dem::elevation_from_coordinates(&dsr, lat, lon).await {
|
match dem::elevation_from_coordinates(&dsr, lat, lon).await {
|
||||||
//None => return (StatusCode::NOT_IMPLEMENTED, "".to_string()),
|
//None => return (StatusCode::NOT_IMPLEMENTED, "".to_string()),
|
||||||
None => {
|
Ok(x) => match x {
|
||||||
return "".to_string().into_response();
|
|
||||||
}
|
|
||||||
Some(el) => ele = el,
|
Some(el) => ele = el,
|
||||||
|
None => ele = -1.0,
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
return e.to_string().into_response();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -125,18 +160,21 @@ async fn get_elevation(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn get_elevation_js(
|
async fn get_elevation_js(
|
||||||
State(dsr): State<DatasetRepository>,
|
State(dsr): State<DatasetRepository>,
|
||||||
Query(params): Query<JsParams>,
|
Query(params): Query<JsParams>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let mut response = JsResult {
|
let mut response = JsResult { elevations: vec![] };
|
||||||
elevations: vec![],
|
|
||||||
};
|
|
||||||
for pt in params.pts {
|
for pt in params.pts {
|
||||||
let ele = dem::elevation_from_coordinates(&dsr, pt.0, pt.1).await;
|
let ele = dem::elevation_from_coordinates(&dsr, pt.0, pt.1).await;
|
||||||
response.elevations.push(ele);
|
match ele {
|
||||||
|
Ok(x) => response.elevations.push(x),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Error: {e}");
|
||||||
|
response.elevations.push(None);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Json(response).into_response()
|
Json(response).into_response()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue