treeadvisor/server/src/area.rs

161 lines
5.3 KiB
Rust

use std::collections::HashSet;
use std::io::Cursor;
use gotham::{
helpers::http::response::create_response,
hyper::{Body, Response},
state::{FromState, State},
};
use geo::{Rect, LineString};
use serde::Deserialize;
use mime::{APPLICATION_JSON, IMAGE_PNG};
use http::StatusCode;
use cairo::{ImageSurface, Context};
use crate::{AppState, AreaExtractor, PointExtractor};
pub fn get_details(state: State) -> (State, Response<Body>) {
let pe = PointExtractor::borrow_from(&state);
let app_state = AppState::borrow_from(&state);
let mut seen_srcs = HashSet::new();
let result = app_state.with_db(|db| {
db.query("SELECT src, attrs FROM areas WHERE coords @> $1::point ORDER BY coords <-> $1::point ASC", &[
&pe.to_point()
]).unwrap()
}).into_iter().filter(|row| {
let src: &str = row.get(0);
if seen_srcs.contains(src) {
false
} else {
seen_srcs.insert(src.to_owned());
true
}
}).map(|row| {
let attrs: serde_json::Value = row.get(1);
attrs
}).collect();
let body = serde_json::to_string(&serde_json::Value::Array(result)).unwrap();
let res = create_response(&state, StatusCode::OK, APPLICATION_JSON, body);
(state, res)
}
pub fn get_heatmap(state: State) -> (State, Response<Body>) {
let pe = AreaExtractor::borrow_from(&state);
let app_state = AppState::borrow_from(&state);
let result = app_state.with_db(|db| {
db.query("SELECT path(coords), src, attrs FROM areas WHERE box(coords) ?# $1::box", &[
&pe.to_rect()
]).unwrap()
});
const TILE_MAX: i32 = 256;
let (w, h) = if pe.w() > pe.h() {
(TILE_MAX, (TILE_MAX as f64 * pe.h() / pe.w()) as i32)
} else {
(((TILE_MAX as f64 * pe.w() / pe.h()) as i32), TILE_MAX)
};
println!("{}x{}", w, h);
let s = ImageSurface::create(cairo::Format::ARgb32, w, h).unwrap();
let ctx = Context::new(&s).unwrap();
ctx.set_antialias(cairo::Antialias::Fast);
for row in result {
// let src: String = row.get(1);
let attrs: serde_json::Value = row.get(2);
// println!("src: {:?} attrs: {:?}", src, attrs);
let coords: LineString<f64> = row.get(0);
let points = coords.into_points();
// println!("{} ps", points.len());
ctx.move_to((points[0].x() - pe.x1) * (w as f64) / pe.w(), (pe.y2 - points[0].y()) * (h as f64) / pe.h());
for point in &points[1..] {
ctx.line_to((point.x() - pe.x1) * (w as f64) / pe.w(), (pe.y2 - point.y()) * (h as f64) / pe.h());
}
ctx.close_path();
let (r, g, b, a) = area_color(&attrs);
ctx.set_source_rgba(r, g, b, a);
ctx.fill().unwrap();
}
let mut buffer = Cursor::new(vec![]);
s.write_to_png(&mut buffer).unwrap();
let res = create_response(&state, StatusCode::OK, IMAGE_PNG, buffer.into_inner());
(state, res)
}
// 0.0: green, 0.5: yellow, 1.0: red
fn hue(mut x: f64) -> (f64, f64, f64) {
x = x.max(0.).min(1.);
if x < 0.5 {
(2.0 * x, 1.0, 0.0)
} else {
(1.0, 2.0 - 2.0 * x, 0.0)
}
}
fn area_color(attrs: &serde_json::Value) -> (f64, f64, f64, f64) {
if let serde_json::Value::Object(attrs) = attrs {
// bodenquali
if let Some(serde_json::Value::Number(q)) = attrs.get("bodenqualitaet") {
if let Some(q) = q.as_f64() {
let (r, g, b) = hue(1.0 - q / 6.0);
return (r, g, b, 0.2);
}
}
if let Some(serde_json::Value::Number(q)) = attrs.get("wasserspeicher") {
if let Some(mut q) = q.as_f64() {
if q > 10. {
q /= 10.;
}
let (r, g, b) = hue((5. - q) / 4.);
return (r, g, b, 0.3);
}
}
// wasserhaushalt
if let Some(serde_json::Value::String(typ)) = attrs.get("gebietstyp") {
match typ.as_str() {
"Gewässer" => return (0., 1., 0., 0.2),
"versickerungsdominiert" => return (0., 1., 0., 0.1),
"verdunstungs- und versickerungsbestimmt" => return (0.5, 1., 0., 0.05),
"verdunstungsdominiert" => return (1.0, 1.0, 0., 0.1),
"verdunstungs- und abflussbestimmt" => return (1.0, 0.5, 0., 0.05),
"abflussdominiert" => return (1., 0., 0., 0.1),
"ausgewogen" => return (0., 0., 0., 0.),
_ => println!("typ: {}", typ),
}
}
// boden_fkt
let mut x = 0.0;
let mut a = 0.0;
if let Some(serde_json::Value::Number(q)) = attrs.get("schutt") {
if let Some(q) = q.as_f64() {
if q > 1. {
println!("schutt: {}", q);
}
x += q / 1.5;
a += 0.1;
}
}
if let Some(serde_json::Value::Number(q)) = attrs.get("versiegelung") {
if let Some(q) = q.as_f64() {
if q > 5. {
println!("versiegelung: {}", q);
}
x += q / 5.0;
a += 0.1;
}
}
let (r, g, b) = hue(x);
return (r, g, b, a);
}
println!("not painted: {:?}", attrs);
(0., 0., 0., 0.)
}