forked from paul/sinoptik
Add an extra map handler for showing lat/lon
* Split of the positioning and drawing code to the `draw_position` helper method * Move the positioning and drawing code to a Tokia task
This commit is contained in:
parent
a03573d20d
commit
7c24f73937
106
src/main.rs
106
src/main.rs
|
@ -58,50 +58,86 @@ async fn forecast_geo(
|
|||
Json(forecast)
|
||||
}
|
||||
|
||||
/// Handler for showing the current map with the geocoded position for a specific metric.
|
||||
/// Handler for showing the current map with the geocoded position of an address for a specific
|
||||
/// metric.
|
||||
///
|
||||
/// Note: This handler is mosly used for debugging purposes!
|
||||
#[get("/map?<address>&<metric>")]
|
||||
async fn show_map(
|
||||
async fn show_map_address(
|
||||
address: String,
|
||||
metric: Metric,
|
||||
maps_handle: &State<MapsHandle>,
|
||||
) -> Option<Custom<Vec<u8>>> {
|
||||
let position = resolve_address(address).await?;
|
||||
let image_data = draw_position(position, metric, maps_handle).await;
|
||||
|
||||
image_data.map(|id| Custom(ContentType::PNG, id))
|
||||
}
|
||||
|
||||
/// Handler for showing the current map with the geocoded position for a specific metric.
|
||||
///
|
||||
/// Note: This handler is mosly used for debugging purposes!
|
||||
#[get("/map?<lat>&<lon>&<metric>", rank = 2)]
|
||||
async fn show_map_geo(
|
||||
lat: f64,
|
||||
lon: f64,
|
||||
metric: Metric,
|
||||
maps_handle: &State<MapsHandle>,
|
||||
) -> Option<Custom<Vec<u8>>> {
|
||||
let position = Position::new(lat, lon);
|
||||
let image_data = draw_position(position, metric, maps_handle).await;
|
||||
|
||||
image_data.map(|id| Custom(ContentType::PNG, id))
|
||||
}
|
||||
|
||||
/// Draws a crosshair on a map for the given position.
|
||||
///
|
||||
/// The map that is used is determined by the metric.
|
||||
// FIXME: Maybe move this to the `maps` module?
|
||||
async fn draw_position(
|
||||
position: Position,
|
||||
metric: Metric,
|
||||
maps_handle: &MapsHandle,
|
||||
) -> Option<Vec<u8>> {
|
||||
use image::{GenericImage, Rgba};
|
||||
use std::io::Cursor;
|
||||
|
||||
let position = resolve_address(address).await?;
|
||||
let now = Instant::now();
|
||||
let maps = maps_handle.lock().expect("Maps handle lock was poisoned");
|
||||
let (mut image, coords) = match metric {
|
||||
Metric::PAQI => (maps.pollen_at(now)?, maps.pollen_project(position)),
|
||||
Metric::Pollen => (maps.pollen_at(now)?, maps.pollen_project(position)),
|
||||
Metric::UVI => (maps.uvi_at(now)?, maps.uvi_project(position)),
|
||||
_ => return None, // Unsupported metric
|
||||
};
|
||||
drop(maps);
|
||||
let maps_handle = Arc::clone(maps_handle);
|
||||
tokio::task::spawn_blocking(move || {
|
||||
let now = Instant::now();
|
||||
let maps = maps_handle.lock().expect("Maps handle lock was poisoned");
|
||||
let (mut image, coords) = match metric {
|
||||
Metric::PAQI => (maps.pollen_at(now)?, maps.pollen_project(position)),
|
||||
Metric::Pollen => (maps.pollen_at(now)?, maps.pollen_project(position)),
|
||||
Metric::UVI => (maps.uvi_at(now)?, maps.uvi_project(position)),
|
||||
_ => return None, // Unsupported metric
|
||||
};
|
||||
drop(maps);
|
||||
|
||||
if let Some((x, y)) = coords {
|
||||
for py in 0..(image.height() - 1) {
|
||||
image.put_pixel(x, py, Rgba::from([0x00, 0x00, 0x00, 0x70]));
|
||||
if let Some((x, y)) = coords {
|
||||
for py in 0..(image.height() - 1) {
|
||||
image.put_pixel(x, py, Rgba::from([0x00, 0x00, 0x00, 0x70]));
|
||||
}
|
||||
|
||||
for px in 0..(image.width() - 1) {
|
||||
image.put_pixel(px, y, Rgba::from([0x00, 0x00, 0x00, 0x70]));
|
||||
}
|
||||
}
|
||||
|
||||
for px in 0..(image.width() - 1) {
|
||||
image.put_pixel(px, y, Rgba::from([0x00, 0x00, 0x00, 0x70]));
|
||||
}
|
||||
}
|
||||
// Encode the image as PNG image data.
|
||||
let mut image_data = Cursor::new(Vec::new());
|
||||
image
|
||||
.write_to(
|
||||
&mut image_data,
|
||||
image::ImageOutputFormat::from(image::ImageFormat::Png),
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
// Encode the image as PNG image data.
|
||||
// FIXME: This encoding call blocks the worker thread!
|
||||
let mut image_data = Cursor::new(Vec::new());
|
||||
image
|
||||
.write_to(
|
||||
&mut image_data,
|
||||
image::ImageOutputFormat::from(image::ImageFormat::Png),
|
||||
)
|
||||
.ok()?;
|
||||
|
||||
Some(Custom(ContentType::PNG, image_data.into_inner()))
|
||||
Some(image_data.into_inner())
|
||||
})
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
}
|
||||
|
||||
/// Starts the main maps refresh loop and sets up and launches Rocket.
|
||||
|
@ -117,7 +153,15 @@ async fn main() -> Result<()> {
|
|||
|
||||
let rocket = rocket::build()
|
||||
.manage(maps_handle)
|
||||
.mount("/", routes![forecast_address, forecast_geo, show_map])
|
||||
.mount(
|
||||
"/",
|
||||
routes![
|
||||
forecast_address,
|
||||
forecast_geo,
|
||||
show_map_address,
|
||||
show_map_geo
|
||||
],
|
||||
)
|
||||
.ignite()
|
||||
.await?;
|
||||
let shutdown = rocket.shutdown();
|
||||
|
|
Loading…
Reference in New Issue