use std::io::Cursor;
use actix_http::StatusCode;
use actix_web::web::Data;
use chrono::Utc;
use image::{ImageBuffer, Rgba};
use crate::{
config::data::AppDataInner,
error::ServiceError,
model::{
dto::{HeatMapQueryParams, RelationSearchParameters, RelationsDto},
entity::plant_layer,
r#enum::heatmap_color::HeatmapColor,
},
service::application_settings,
};
use super::application_settings::HeatmapColors;
pub async fn heatmap(
map_id: i32,
query_params: HeatMapQueryParams,
app_data: &Data<AppDataInner>,
) -> Result<Vec<u8>, ServiceError> {
let mut conn = app_data.pool.get().await?;
let result = plant_layer::heatmap(
map_id,
query_params.plant_layer_id,
query_params.shade_layer_id,
query_params.plant_id,
query_params.date.unwrap_or_else(|| Utc::now().date_naive()),
&mut conn,
)
.await?;
let heatmap_colors = application_settings::get_heatmap_colors(&mut conn).await?;
let buffer = matrix_to_image(&result, heatmap_colors)?;
Ok(buffer)
}
#[allow(
clippy::cast_possible_truncation, clippy::indexing_slicing, clippy::cast_sign_loss )]
fn matrix_to_image(
matrix: &[Vec<(HeatmapColor, f32)>],
colors: HeatmapColors,
) -> Result<Vec<u8>, ServiceError> {
let (width, height) = (matrix[0].len(), matrix.len());
let mut imgbuf = ImageBuffer::new(width as u32, height as u32);
let (red, orange, green, black) = colors;
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
let (color, relevance) = &matrix[y as usize][x as usize];
let alpha = (relevance * 255.0) as u8;
*pixel = match color {
HeatmapColor::Red => Rgba([red.0, red.1, red.2, alpha]),
HeatmapColor::Orange => Rgba([orange.0, orange.1, orange.2, alpha]),
HeatmapColor::Green => Rgba([green.0, green.1, green.2, alpha]),
HeatmapColor::Black => Rgba([black.0, black.1, black.2, alpha]),
};
}
let mut buffer: Vec<u8> = Vec::new();
imgbuf
.write_to(&mut Cursor::new(&mut buffer), image::ImageOutputFormat::Png)
.map_err(|err| ServiceError::new(StatusCode::INTERNAL_SERVER_ERROR, &err.to_string()))?;
Ok(buffer)
}
pub async fn find_relations(
search_query: RelationSearchParameters,
app_data: &Data<AppDataInner>,
) -> Result<RelationsDto, ServiceError> {
let mut conn = app_data.pool.get().await?;
let result = plant_layer::find_relations(search_query, &mut conn).await?;
Ok(result)
}