trees: add TileExtractor for heatmap
This commit is contained in:
parent
2a10109d7f
commit
d009f29756
|
@ -3,6 +3,7 @@
|
|||
#[macro_use]
|
||||
extern crate gotham_derive;
|
||||
|
||||
use core::f64::consts::PI;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use gotham::{
|
||||
handler::assets::FileOptions,
|
||||
|
@ -78,6 +79,40 @@ impl PointExtractor {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, StateData, StaticResponseExtender)]
|
||||
pub struct TileExtractor {
|
||||
zoom: i32,
|
||||
x: u32,
|
||||
y: u32,
|
||||
}
|
||||
|
||||
impl TileExtractor {
|
||||
fn lon(&self) -> f64 {
|
||||
360. * self.x as f64 / 2f64.powi(self.zoom) - 180.
|
||||
}
|
||||
|
||||
fn lat(&self) -> f64 {
|
||||
(PI * (1f64 - 2f64 * self.y as f64 / 2f64.powi(self.zoom))).sinh().atan() * 180. / PI
|
||||
}
|
||||
|
||||
pub fn south_west(&self) -> Self {
|
||||
TileExtractor {
|
||||
zoom: self.zoom,
|
||||
x: self.x + 1,
|
||||
y: self.y + 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_point(&self) -> Point<f64> {
|
||||
Point::new(self.lon(), self.lat())
|
||||
}
|
||||
|
||||
pub fn to_rect(&self) -> Rect<f64> {
|
||||
let sw = self.south_west();
|
||||
Rect::new(self.to_point(), sw.to_point())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn main() {
|
||||
const DB_URL: &str = "host=10.233.1.2 dbname=treeadvisor user=treeadvisor password=123";
|
||||
|
@ -94,8 +129,8 @@ fn main() {
|
|||
)
|
||||
);
|
||||
let router = build_router(chain, pipelines, |route| {
|
||||
route.get("/heatmap/:x1/:y1/:x2/:y2")
|
||||
.with_path_extractor::<AreaExtractor>()
|
||||
route.get("/heatmap/:zoom/:x/:y/tile.png")
|
||||
.with_path_extractor::<TileExtractor>()
|
||||
.to(trees::get_heatmap);
|
||||
route.get("/area/:x/:y")
|
||||
.with_path_extractor::<PointExtractor>()
|
||||
|
|
|
@ -5,13 +5,27 @@ use gotham::{
|
|||
hyper::{Body, Response},
|
||||
state::{FromState, State},
|
||||
};
|
||||
use geo::Point;
|
||||
use geo::{Point, Rect};
|
||||
use serde::Serialize;
|
||||
use mime::{APPLICATION_JSON, IMAGE_PNG};
|
||||
use http::StatusCode;
|
||||
use chrono::{Local, NaiveDate};
|
||||
use cairo::{ImageSurface, Context};
|
||||
use crate::{AppState, AreaExtractor, IdExtractor};
|
||||
use crate::{AppState, AreaExtractor, IdExtractor, TileExtractor};
|
||||
|
||||
trait Grow {
|
||||
fn grow(&self, factor: f64) -> Self;
|
||||
}
|
||||
|
||||
impl Grow for Rect<f64> {
|
||||
fn grow(&self, factor: f64) -> Self {
|
||||
let c = self.center();
|
||||
let w = self.width();
|
||||
let h = self.height();
|
||||
Rect::new((c.x - factor * w / 2., c.y - factor * h / 2.),
|
||||
(c.x + factor * w / 2., c.y + factor * h / 2.))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct Tree {
|
||||
|
@ -119,12 +133,12 @@ pub fn get_tree(state: State) -> (State, Response<Body>) {
|
|||
}
|
||||
|
||||
pub fn get_heatmap(state: State) -> (State, Response<Body>) {
|
||||
let pe = AreaExtractor::borrow_from(&state);
|
||||
let rect = TileExtractor::borrow_from(&state).to_rect();
|
||||
let app_state = AppState::borrow_from(&state);
|
||||
let result = {
|
||||
let mut db = app_state.db.lock().unwrap();
|
||||
db.query("SELECT coord, score, planted FROM trees WHERE coord <@ $1::box AND SCORE IS NOT NULL", &[
|
||||
&pe.grow(0.2).to_rect()
|
||||
&rect.grow(1.2)
|
||||
]).unwrap()
|
||||
};
|
||||
|
||||
|
@ -141,9 +155,9 @@ pub fn get_heatmap(state: State) -> (State, Response<Body>) {
|
|||
let age = planted.map(|planted| ((Local::today().naive_local() - planted).num_days()) / 365);
|
||||
|
||||
let (r, g, b) = temperature(1. - score);
|
||||
ctx.set_source_rgba(r, g, b, 0.1);
|
||||
let radius = 1.5 + (0.4 + age.unwrap_or(50) as f64 / 80.).min(1.4) * 0.02 / pe.w();
|
||||
ctx.arc((point.x() - pe.x1) * (w as f64) / pe.w(), (pe.y2 - point.y()) * (h as f64) / pe.h(),
|
||||
ctx.set_source_rgba(r, g, b, 0.5);
|
||||
let radius = 1.5 + (0.4 + age.unwrap_or(50) as f64 / 80.).min(1.4) * 0.02 / rect.width();
|
||||
ctx.arc((point.x() - rect.min().x) * (w as f64) / rect.width(), (rect.max().y - point.y()) * (h as f64) / rect.height(),
|
||||
radius, 0., 2. * core::f64::consts::PI);
|
||||
ctx.fill().unwrap();
|
||||
}
|
||||
|
@ -158,7 +172,7 @@ pub fn get_heatmap(state: State) -> (State, Response<Body>) {
|
|||
fn temperature(mut x: f64) -> (f64, f64, f64) {
|
||||
x = x.max(0.).min(1.);
|
||||
|
||||
const MAX: f64 = 0.85;
|
||||
const MAX: f64 = 0.95;
|
||||
if x < 0.4 {
|
||||
(MAX * x / 0.4, MAX, 0.0)
|
||||
} else if x < 0.8 {
|
||||
|
|
|
@ -19,10 +19,10 @@ var HeatmapLayer = L.TileLayer.extend({
|
|||
return ["", "heatmap", x1, y1, x2, y2].join("/");
|
||||
},
|
||||
});
|
||||
(new HeatmapLayer({
|
||||
L.tileLayer('/heatmap/{z}/{x}/{y}/tile.png', {
|
||||
maxZoom: TREES_MIN_ZOOM - 1,
|
||||
opacity: 0.4,
|
||||
})).addTo(map);
|
||||
opacity: 0.8,
|
||||
}).addTo(map);
|
||||
|
||||
var treeIcon = L.icon({
|
||||
iconUrl: "tree.png",
|
||||
|
|
Loading…
Reference in New Issue