This commit is contained in:
Tsuki 2024-08-24 12:04:09 +08:00
parent 77d8c1f0eb
commit 58947b9aa1
14 changed files with 123 additions and 1804 deletions

View File

@ -8,11 +8,6 @@ use super::{
}; };
use crate::components::sidebar::{SideBarInputMsg, SideBarModel}; use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
use crate::data_utils::tools; use crate::data_utils::tools;
use crate::pipeline::element::Buffer;
use crate::pipeline::element::Element;
use crate::pipeline::element_imp::{Context, ElementInput, GridImpConfig};
use crate::pipeline::runner::Runner;
use crate::pipeline::{DataTarget, Key};
use crate::pipeline::{KVBuffer, OffscreenRenderer}; use crate::pipeline::{KVBuffer, OffscreenRenderer};
use crate::predefined::color_mapper::{BoundaryNorm, ColorMapper, ColorMapperComb, Discrete}; use crate::predefined::color_mapper::{BoundaryNorm, ColorMapper, ColorMapperComb, Discrete};
use crate::predefined::widgets::ColorBar; use crate::predefined::widgets::ColorBar;
@ -82,10 +77,10 @@ pub enum AppMsg {
CloseRequest, CloseRequest,
Close, Close,
OpenDialog, OpenDialog,
LayerManager(LayerMsg), // LayerManager(LayerMsg),
Layer, // Layer,
NewElement(Rc<RefCell<Element>>), // NewElement(Rc<RefCell<Element>>),
DeleteElement(ElementKey), // DeleteElement(ElementKey),
} }
type RcDispatcher = Rc<Dispatcher>; type RcDispatcher = Rc<Dispatcher>;
type ArcDispatcher = Arc<Dispatcher>; type ArcDispatcher = Arc<Dispatcher>;
@ -96,6 +91,8 @@ pub struct AppModel {
#[do_not_track] #[do_not_track]
cms: CMS, cms: CMS,
waiting_for: Option<DateTime<Utc>>, waiting_for: Option<DateTime<Utc>>,
// Components
#[do_not_track] #[do_not_track]
open_dialog: Controller<OpenDialog>, open_dialog: Controller<OpenDialog>,
#[do_not_track] #[do_not_track]
@ -104,15 +101,19 @@ pub struct AppModel {
render: Controller<MonitorModel>, render: Controller<MonitorModel>,
#[do_not_track] #[do_not_track]
sidebar: Controller<SideBarModel>, sidebar: Controller<SideBarModel>,
// Data
selected_layer: Vec<usize>, selected_layer: Vec<usize>,
#[do_not_track] #[do_not_track]
layers: Rc<RefCell<Vec<Layer>>>, layers: Rc<RefCell<Vec<Layer>>>,
#[do_not_track] // #[do_not_track]
buffer: Arc<Buffer<Key>>, // buffer: Arc<Buffer<Key>>,
// File Pool
#[do_not_track] #[do_not_track]
file_pool: Arc<KVBuffer<PathBuf, Arc<PluginResult>>>, file_pool: Arc<KVBuffer<PathBuf, Arc<PluginResult>>>,
#[do_not_track] // #[do_not_track]
elements: Vec<Rc<RefCell<Element>>>, // elements: Vec<Rc<RefCell<Element>>>,
#[do_not_track] #[do_not_track]
setting: Controller<SettingModel>, setting: Controller<SettingModel>,
} }
@ -239,87 +240,91 @@ impl Component for AppModel {
}, },
); );
let buffer = Arc::new(Buffer::new(100)); // let buffer = Arc::new(Buffer::new(100));
let file_pool = Arc::new(KVBuffer::new(20)); let file_pool = Arc::new(KVBuffer::new(20));
let sidebar = // SideBar Component
SideBarModel::builder() // let sidebar =
.launch(layers.clone()) // SideBarModel::builder()
.forward(sender.input_sender(), |msg| { // .launch(layers.clone())
AppMsg::LayerManager(match msg { // .forward(sender.input_sender(), |msg| {
SideBarOutputMsg::SelectLayer(idx) => LayerMsg::Select(idx), // AppMsg::LayerManager(match msg {
SideBarOutputMsg::NewLayer(layer) => LayerMsg::Add(layer), // SideBarOutputMsg::SelectLayer(idx) => LayerMsg::Select(idx),
SideBarOutputMsg::SwitchToTimeSeries(idx) => LayerMsg::SwitchToTime(idx), // SideBarOutputMsg::NewLayer(layer) => LayerMsg::Add(layer),
}) // SideBarOutputMsg::SwitchToTimeSeries(idx) => LayerMsg::SwitchToTime(idx),
}); // })
let render = // });
MonitorModel::builder()
.launch(layers.clone())
.forward(sender.input_sender(), |a| match a {
MonitorOutputMsg::LayerRenderFinished => AppMsg::Close,
_ => AppMsg::Close,
});
let setting = SettingModel::builder() // // Monitor Component
.launch(()) // let render =
.forward(sender.input_sender(), |a| AppMsg::Close); // MonitorModel::builder()
// .launch(layers.clone())
// .forward(sender.input_sender(), |a| match a {
// MonitorOutputMsg::LayerRenderFinished => AppMsg::Close,
// _ => AppMsg::Close,
// });
let mut dispatcher = Arc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1))); // let setting = SettingModel::builder()
let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0)); // .launch(())
let dialog_buffer = buffer.clone(); // .forward(sender.input_sender(), |a| AppMsg::Close);
let dialog_cms = cms.clone();
let dialog = { // // Background Dispatcher
let dialog_dispatcher = dispatcher.clone(); // let mut dispatcher = Arc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1)));
let dialog_sidebar_sender = sidebar.sender().clone(); // let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
let dialog_render_sender = render.sender().clone(); // let dialog_buffer = buffer.clone();
let dialog_file_pool = file_pool.clone(); // let dialog_cms = cms.clone();
OpenDialog::builder()
.transient_for_native(&root)
.launch(OpenDialogSettings::default())
.forward(sender.input_sender(), move |response| match response {
OpenDialogResponse::Accept(path) => {
*FILE_PATH_ROOT.lock().unwrap() = path.clone();
let data = Self::open_file_only(path.clone());
let meta: MetaInfo = (&data.meta).clone().into();
let (lat_start, lat_end) = meta.lat_range.unwrap();
let (lon_start, lon_end) = meta.lon_range.unwrap();
let (imp, cfg) = tools(&data);
dialog_file_pool.insert(path.clone(), Arc::new(data));
let element = Rc::new(RefCell::new(Element::new( // // File Dialog
"CR", // let dialog = {
dialog_cms.clone(), // let dialog_dispatcher = dispatcher.clone();
dialog_dispatcher.clone(), // let dialog_sidebar_sender = sidebar.sender().clone();
true, // let dialog_render_sender = render.sender().clone();
cfg, // let dialog_file_pool = file_pool.clone();
path.clone(), // OpenDialog::builder()
dialog_buffer.clone(), // .transient_for_native(&root)
dialog_file_pool.clone(), // .launch(OpenDialogSettings::default())
imp, // .forward(sender.input_sender(), move |response| match response {
))); // OpenDialogResponse::Accept(path) => {
// let data = Self::open_file_only(path.clone());
// let meta: MetaInfo = (&data.meta).clone().into();
// let (lat_start, lat_end) = meta.lat_range.unwrap();
// let (lon_start, lon_end) = meta.lon_range.unwrap();
// let (imp, cfg) = tools(&data);
// dialog_file_pool.insert(path.clone(), Arc::new(data));
dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map())); // let element = Rc::new(RefCell::new(Element::new(
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange( // "CR",
lon_start, lon_end, lat_start, lat_end, // dialog_cms.clone(),
)); // dialog_dispatcher.clone(),
// true,
// cfg,
// path.clone(),
// dialog_buffer.clone(),
// dialog_file_pool.clone(),
// imp,
// )));
AppMsg::NewElement(element) // dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map()));
} // dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
_ => AppMsg::Close, // lon_start, lon_end, lat_start, lat_end,
}) // ));
};
// AppMsg::NewElement(element)
// }
// _ => AppMsg::Close,
// })
// };
let model = AppModel { let model = AppModel {
cms, // cms,
dispatcher, // dispatcher,
waiting_for: None, waiting_for: None,
buffer, // buffer,
file_pool, file_pool,
open_dialog: dialog, // open_dialog: dialog,
selected_layer: vec![], selected_layer: vec![],
sidebar, // sidebar,
elements: vec![], // elements: vec![],
control, control,
render, render,
layers, layers,

View File

@ -1,73 +0,0 @@
use crate::coords::Mapper;
use epoxy::XOR;
use geo_types::LineString;
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct CMS {
mapper: Mapper,
window_size: (f32, f32),
bounds: (f64, f64, f64, f64),
}
unsafe impl Send for CMS {}
unsafe impl Sync for CMS {}
impl CMS {
pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self {
let bounds = mapper.get_bounds();
Self {
mapper,
window_size,
bounds,
}
}
pub fn set_lat_range(&mut self, lat_range: Range<f64>) {
self.mapper.set_lat_range(lat_range);
self.bounds = self.mapper.get_bounds()
}
pub fn set_lon_range(&mut self, lon_range: Range<f64>) {
self.mapper.set_lon_range(lon_range);
self.bounds = self.mapper.get_bounds();
}
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
self.mapper.map(loc).ok().map(|(x, y)| {
// println!("x: {}, y: {}", x, y);
let (w, h) = self.window_size;
let (w, h) = (w as f64, h as f64);
let (x, y) = (x - self.bounds.0, y - self.bounds.2);
// TODO: check if the following line is correct : 1.0 - y / (self.bounds.3 - self.bounds.2)
let (x, y) = (
x / (self.bounds.1 - self.bounds.0),
y / (self.bounds.3 - self.bounds.2),
);
let (x, y) = (x * w, y * h);
(x as f32, y as f32)
})
}
pub fn fore_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
let (x, y) = loc;
let (x, y) = (x as f64, y as f64);
let (x, y) = (
self.bounds.0 + x * (self.bounds.1 - self.bounds.0),
self.bounds.2 + y * (self.bounds.3 - self.bounds.2),
);
self.mapper.inverse_map((x, y)).ok()
}
pub fn ring_map(&self, line: &LineString) -> Option<LineString<f32>> {
Some(
line.points()
.into_iter()
.map(|p| self.map((p.x(), p.y())).unwrap())
.collect::<Vec<_>>()
.into(),
)
}
}

View File

@ -1,252 +0,0 @@
use crate::widgets::render::WindowCoord;
use std::borrow::ToOwned;
use super::{proj::ProjectionS, Range};
use geo_types::{coord, Coord as GCoord, LineString};
use proj::{Proj, ProjError};
pub struct Mapper {
proj: Proj,
pub range: (Range, Range),
bounds: (f64, f64, f64, f64),
}
unsafe impl Send for Mapper {}
unsafe impl Sync for Mapper {}
impl std::fmt::Debug for Mapper {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mapper")
.field("proj", &self.proj)
.field("range", &self.range)
.field("bounds", &self.bounds)
.finish()
}
}
impl Clone for Mapper {
fn clone(&self) -> Self {
let c = self.proj.proj_info();
let new_proj = Proj::new(c.definition.unwrap().as_str()).unwrap();
Self {
proj: new_proj,
range: self.range,
bounds: self.bounds,
}
}
}
impl From<Proj> for Mapper {
fn from(proj: Proj) -> Self {
let default_range: (Range, Range) = ((-180.0..180.0).into(), (-81.0..81.0).into());
let bounds = Self::bound(&proj, default_range.clone()).unwrap();
Self {
proj,
range: (default_range.0.into(), default_range.1.into()),
bounds,
}
}
}
impl<C: ProjectionS> From<C> for Mapper {
fn from(proj: C) -> Self {
let p = Proj::new(proj.build().as_str()).unwrap();
p.into()
}
}
impl Mapper {
pub fn new(
proj: Proj,
lon_range: std::ops::Range<f64>,
lat_range: std::ops::Range<f64>,
) -> Self {
let bounds =
Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
let range = (lon_range.into(), lat_range.into());
Self {
proj,
range,
bounds,
}
}
pub fn inverse_map(&self, coord: (f64, f64)) -> Result<(f64, f64), ProjError> {
let (x, y) = coord;
// let c = (x as f64) * (self.bounds.1 - self.bounds.0) + self.bounds.0;
// let d = ((1.0 - y) as f64) * (self.bounds.3 - self.bounds.2) + self.bounds.2;
let (lon, lat) = self.proj.project((x, y), true)?;
Ok((lon.to_degrees(), lat.to_degrees()))
}
pub fn point_in_bound(&self, point: (f64, f64)) -> bool {
let (x, y) = point;
let (lon_range, lat_range) = self.range;
return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0);
}
pub fn get_bounds(&self) -> (f64, f64, f64, f64) {
self.bounds
}
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
let mut point = point;
// if !self.point_in_bound(point) {
// point = (
// point.0.clamp(self.range.0 .0, self.range.0 .1),
// point.1.clamp(self.range.1 .0, self.range.1 .1),
// );
// }
let (p1, p2) = self
.proj
.convert((point.0.to_radians(), point.1.to_radians()))?;
// let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
// let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
// Ok((x, (1.0 - y)))
//
Ok((p1, p2))
}
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
self.range.0 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
self
}
pub fn set_lat_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
self.range.1 = range.into();
self.bounds = Self::bound(&self.proj, self.range.clone()).unwrap();
self
}
fn bound(proj: &Proj, range: (Range, Range)) -> Result<(f64, f64, f64, f64), ProjError> {
let left_bottom = proj.convert((range.0 .0.to_radians(), range.1 .0.to_radians()))?;
let right_top = proj.convert((range.0 .1.to_radians(), range.1 .1.to_radians()))?;
Ok((left_bottom.0, right_top.0, left_bottom.1, right_top.1))
}
pub fn ring_map(&self, ring: &LineString) -> Result<LineString, ProjError> {
let mut result = Vec::new();
let projed = self.map_line(ring)?;
for (l, p) in ring.lines().zip(projed.lines()) {
let start_projected: (f64, f64) = p.start.into();
let end_projected: (f64, f64) = p.end.into();
let cartesian_start = self.cartesian(l.start.x, l.start.y);
let cartesian_end = self.cartesian(l.end.x, l.end.y);
let delta2 = 0.5;
let depth = 16;
let mut res: Vec<GCoord> = Vec::new();
res.push(p.start);
self.resample_line_to(
start_projected,
end_projected,
l.start.x,
l.end.x,
cartesian_start,
cartesian_end,
&|x| self.map((x.x, x.y)),
delta2,
depth,
&mut res,
)?;
res.push(p.end);
result.extend(res);
}
Ok(LineString::new(result))
}
pub fn map_line(&self, line: &LineString) -> Result<LineString, ProjError> {
let result: Result<LineString, ProjError> =
line.points().map(|p| self.map((p.x(), p.y()))).collect();
Ok(result?)
}
#[inline]
fn cartesian(&self, lambda: f64, phi: f64) -> (f64, f64, f64) {
let cos_phi = phi.cosh();
return (cos_phi * lambda.cosh(), cos_phi * lambda.sinh(), phi.sinh());
}
fn resample_line_to<F>(
&self,
start_projected: (f64, f64),
end_projected: (f64, f64),
lambda0: f64,
lambda1: f64,
cartesian_start: (f64, f64, f64),
cartesian_end: (f64, f64, f64),
project: &F,
delta2: f64,
depth: u8,
result: &mut Vec<GCoord>,
) -> Result<(), ProjError>
where
F: Fn(GCoord) -> Result<(f64, f64), ProjError>,
{
let cos_min_distance: f64 = 30f64.cosh();
let (x0, y0) = start_projected;
let (x1, y1) = end_projected;
let (a0, b0, c0) = cartesian_start;
let (a1, b1, c1) = cartesian_end;
let dx = x1 - x0;
let dy = y1 - y0;
let d2 = dx * dx + dy * dy;
if d2 > 4.0 * delta2 && depth > 0 {
let a = a0 + a1;
let b = b0 + b1;
let c = c0 + c1;
let m = (a * a + b * b + c * c).sqrt();
let depth = depth - 1;
let phi2 = (c / m).asin();
let lambda2 =
if (c / m).abs() - 1.0 < f64::EPSILON || (lambda0 - lambda1).abs() < f64::EPSILON {
(lambda0 + lambda1) / 2.0
} else {
b.atan2(a)
};
let (x2, y2) = project(coord! {x:lambda2,y:phi2})?;
let dx2 = x2 - x0;
let dy2 = y2 - y0;
let dz = dy * dx2 - dx * dy2;
if dz * dz / d2 > delta2
|| ((dx * dx2 + dy * dy2) / d2 - 0.5).abs() > 0.3
|| a0 * a1 + b0 * b1 + c0 * c1 < cos_min_distance
{
self.resample_line_to(
start_projected,
(x2, y2),
lambda0,
lambda2,
cartesian_start,
(a / m, b / m, c),
project,
delta2,
depth - 1,
result,
)?;
result.push(coord! {x:x2,y:y2});
self.resample_line_to(
(x2, y2),
end_projected,
lambda2,
lambda1,
(a / m, b / m, c),
cartesian_end,
project,
delta2,
depth - 1,
result,
)?;
}
}
Ok(())
}
}

View File

@ -1,161 +0,0 @@
use num_traits::AsPrimitive;
use num_traits::Num;
pub mod cms;
pub mod mapper;
pub mod proj;
pub mod wgs84;
pub use mapper::Mapper;
// pub use wgs84::LatLonCoord;
pub type ScreenCoord = (f64, f64);
type Lat = f64;
type Lon = f64;
pub trait Coord<T: Num> {
fn map(&self, axis_1: T, axis_2: T) -> ScreenCoord;
fn dim1_range(&self) -> (T, T);
fn dim2_range(&self) -> (T, T);
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Range(pub f64, pub f64);
impl Range {
pub fn key_points(&self, max_points: usize) -> Vec<f64> {
if max_points == 0 {
return vec![];
}
let range = (self.0.min(self.1) as f64, self.1.max(self.0) as f64);
assert!(!(range.0.is_nan() || range.1.is_nan()));
if (range.0 - range.1).abs() < std::f64::EPSILON {
return vec![range.0 as f64];
}
let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
// The value granularity controls how we round the values.
// To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
// value granularity.
// By default, we make the granularity as the 1/10 of the scale.
let mut value_granularity = scale / 10.0;
fn rem_euclid(a: f64, b: f64) -> f64 {
let ret = if b > 0.0 {
a - (a / b).floor() * b
} else {
a - (a / b).ceil() * b
};
if (ret - b).abs() < std::f64::EPSILON {
0.0
} else {
ret
}
}
// At this point we need to make sure that the loop invariant:
// The scale must yield number of points than requested
if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
scale *= 10.0;
value_granularity *= 10.0;
}
'outer: loop {
let old_scale = scale;
for nxt in [2.0, 5.0, 10.0].iter() {
let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
if new_left < range.0 {
new_left += old_scale / nxt;
}
let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
if npoints.round() as usize > max_points {
break 'outer;
}
scale = old_scale / nxt;
}
scale = old_scale / 10.0;
value_granularity /= 10.0;
}
let mut ret = vec![];
// In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
// floating point error.
// In this case, we may loop forever. To avoid this, we need to use two variables to store
// the current left value. So we need keep a left_base and a left_relative.
let left = {
let mut value = range.0 - rem_euclid(range.0, scale);
if value < range.0 {
value += scale;
}
value
};
let left_base = (left / value_granularity).floor() * value_granularity;
let mut left_relative = left - left_base;
let right = range.1 - rem_euclid(range.1, scale);
while (right - left_relative - left_base) >= -std::f64::EPSILON {
let new_left_relative = (left_relative / value_granularity).round() * value_granularity;
if new_left_relative < 0.0 {
left_relative += value_granularity;
}
ret.push((left_relative + left_base) as f64);
left_relative += scale;
}
return ret;
}
}
impl<T: AsPrimitive<f64> + Num> From<(T, T)> for Range {
fn from(value: (T, T)) -> Self {
let value = (value.0.as_(), value.1.as_());
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
Self(_min, _max)
}
}
impl<T: Num + AsPrimitive<f64>> From<std::ops::Range<T>> for Range {
fn from(value: std::ops::Range<T>) -> Self {
let value = (value.start.as_(), value.end.as_());
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
Self(_min, _max)
}
}
// impl<T, Raw> RadarData2d<T, Raw>
// where
// T: Num + Clone + PartialEq + PartialOrd,
// Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
// {
// pub fn mapped(&self, coord: impl Borrow<Mapper>) -> Result<AfterMapping2d<T>, ProjError> {
// let mapper: &Mapper = coord.borrow();
// self.map_by_fn(|x| mapper.map(x))
// }
// pub fn map_by_fn<F>(&self, f: F) -> Result<AfterMapping2d<T>, ProjError>
// where
// F: Fn((f64, f64)) -> Result<(f64, f64), ProjError>,
// {
// let mesh_dim1_len = self.dim1.len();
// let mesh_dim2_len = self.dim2.len();
// let mut d1 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
// let mut d2 = Array2::<f64>::zeros((mesh_dim2_len, mesh_dim1_len));
// for (i, v) in self.dim1.iter().enumerate() {
// for (j, u) in self.dim2.iter().enumerate() {
// let (x, y) = f((*v, *u))?;
// d1[[j, i]] = x;
// d2[[j, i]] = y;
// }
// }
// Ok(AfterMapping2d {
// dim1: d1,
// dim2: d2,
// data: self.data.view(),
// coord_type: self.coord_type.clone(),
// })
// }
// }

View File

@ -1,73 +0,0 @@
use crate::coords::Range;
use super::ProjectionS;
use geo_macros::Prj;
#[derive(Prj)]
/// A struct representing the Mercator projection.
pub struct Mercator {
/// The central longitude of the projection.
pub central_lon: f64,
/// The false easting of the projection.
pub false_easting: f64,
/// The false northing of the projection.
pub false_northing: f64,
/// The latitude of true scale of the projection.
pub latitude_true_scale: f64,
}
fn proj_string<'a>(vs: Vec<(&'a str, &'a str)>) -> String {
vs.into_iter()
.map(|(option, value)| format!("+{}={}", option, value))
.collect::<Vec<String>>()
.join(" ")
}
impl Mercator {
/// Creates a new instance of the `Mercator` projection with default values.
pub fn new(
central_lon: f64,
false_easting: f64,
false_northing: f64,
latitude_true_scale: f64,
) -> Self {
Self {
central_lon,
false_easting,
false_northing,
latitude_true_scale,
}
}
}
impl Default for Mercator {
fn default() -> Self {
Self {
central_lon: 0.0,
false_easting: 0.0,
false_northing: 0.0,
latitude_true_scale: 0.0,
}
}
}
impl ProjectionS for Mercator {
fn build(&self) -> String {
let _central_lon = format!("{:.1}", &self.central_lon);
let _false_easting = format!("{:.1}", &self.false_easting);
let _false_northing = format!("{:.1}", &self.false_northing);
let _ts = format!("{:.1}", &self.latitude_true_scale);
let input = vec![
("proj", "merc"),
("lon_0", _central_lon.as_str()),
("x_0", _false_easting.as_str()),
("y_0", _false_northing.as_str()),
("lat_ts", _ts.as_str()),
("units", "m"),
("ellps", "GRS80"),
];
let _proj_string = proj_string(input);
_proj_string
}
}

View File

@ -1,67 +0,0 @@
use super::{Lat, Lon, Range};
use proj::Proj;
// use proj5;
use thiserror::Error;
mod mercator;
pub use mercator::Mercator;
#[derive(Debug, Clone, Copy)]
pub enum Projs {
PlateCarree,
LambertConformal,
LambertCylindrical,
Mercator,
}
/// A trait for defining a projection.
pub trait ProjectionS {
/// Returns a proj-string of the projection.
fn build(&self) -> String;
}
#[derive(Debug, Error)]
pub(super) enum ProjError {
#[error("proj error")]
ProjError(#[from] proj::ProjError),
}
// pub(super) struct PCS<T: ProjectionS> {
// pub lon_range: Range,
// pub lat_range: Range,
// pub proj_param: T,
// // pub proj_target: proj5::CoordinateBuf,
// pub transformer: Proj,
// }
// impl<T: ProjectionS> PCS<T> {
// pub(super) fn new(proj_param: T, lon_range: Option<Range>, lat_range: Option<Range>) -> Self {
// let (lon_range, lat_range) = proj_param.logic_range(lon_range, lat_range);
// Self {
// lon_range,
// lat_range,
// transformer: Proj::new(proj_param.build().as_str()).unwrap(),
// proj_param: proj_param,
// }
// }
// pub fn bbox(&self) -> Result<(Range, Range), ProjError> {
// let _proj_transformer = &self.transformer;
// let lb = (self.lon_range.0.to_radians(), self.lat_range.0.to_radians());
// let rt = (self.lon_range.1.to_radians(), self.lat_range.1.to_radians());
// let bl = _proj_transformer.convert(lb)?;
// let rt = _proj_transformer.convert(rt)?;
// Ok((Range::from((bl.0, rt.0)), Range::from((bl.1, rt.1))))
// }
// pub fn map(&self, lon_lat: (Lat, Lon)) -> (f64, f64) {
// let _proj_transformer = &self.transformer;
// let _lon_lat = _proj_transformer
// .convert((lon_lat.0.to_radians(), lon_lat.1.to_radians()))
// .unwrap();
// _lon_lat
// }
// }

View File

@ -1,88 +0,0 @@
// use super::proj::{ProjectionS, PCS};
use super::Coord;
use super::{Lat, Lon, Range};
use proj::ProjError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CoordError {
#[error("")]
ProjError {
#[from]
source: ProjError,
},
}
// pub struct LatLonCoord<T: ProjectionS> {
// actual: (Range, Range),
// logical: (Range, Range),
// pcs: PCS<T>,
// }
// impl<T: ProjectionS> LatLonCoord<T> {
// /// Creates a new `LatLonCoord` instance.
// ///
// /// # Arguments
// ///
// /// * `lon` - An optional longitude range.
// /// * `lat` - An optional latitude range.
// /// * `actual` - A tuple containing the actual ranges.
// /// * `project` - A projection.
// ///
// /// # Returns
// ///
// /// A new `LatLonCoord` instance.
// pub fn new(
// lon: Option<Range>,
// lat: Option<Range>,
// actual: ((i32, i32), (i32, i32)),
// // actual: (Range, Range),
// project: T,
// ) -> Self {
// let pcs = PCS::new(project, lon, lat);
// let _box = pcs.bbox().unwrap();
// Self {
// actual: (Range::from(actual.0), Range::from(actual.1)),
// pcs: pcs,
// logical: _box,
// }
// }
// pub fn set_actual(&mut self, actual: ((i32, i32), (i32, i32))) {
// self.actual = (Range::from(actual.0), Range::from(actual.1));
// }
// pub fn lon_range(&self) -> Range {
// self.pcs.lon_range
// }
// pub fn lat_range(&self) -> Range {
// self.pcs.lat_range
// }
// }
// impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> {
// fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord {
// let point = self.pcs.map((axis_1, axis_2));
// let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0;
// let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span;
// let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0;
// let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span;
// (
// (dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0,
// (dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0,
// )
// }
// fn dim1_range(&self) -> (f64, f64) {
// let v = self.lon_range();
// (v.0, v.1)
// }
// fn dim2_range(&self) -> (f64, f64) {
// let v = self.lat_range();
// (v.0, v.1)
// }
// }

View File

@ -1,142 +0,0 @@
use crate::pipeline::element_imp::ElementImpl::MultiLayerGrid;
use crate::pipeline::element_imp::*;
use crate::utils::*;
use crate::CONFIG;
use abi_stable::traits::IntoOwned;
use num_traits::FromPrimitive;
use radarg_plugin_interface::{CoordType, DataShape, PluginResult, PluginResultType};
use std::any::Any;
use std::sync::Arc;
macro_rules! dispatch {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
Arc::new($v) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dispatch_polar {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $center:ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
$v.center = $center;
Arc::new($v) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dispatch_3d {
($block:ident,$conf:ident, $wrap:tt , $fill_value: ident, $(
{
$t:ty | $branch: pat => $v:ident
}
),+ $(,)?) => {
match $block.data_type {
$(
$branch => {
let mut $v = $wrap::default();
$v.color_map = $conf.drawers.$v.color_mapper.clone();
$v.fill_value = <$t>::from_f64($fill_value).unwrap();
let mut cfg = MultiLayerGridImpConfig{
two_d_config: $v,
layer: 0,
};
Arc::new(cfg) as Arc<dyn Any + Send + Sync>
}
)+
_ => { panic!("") }
}
};
}
macro_rules! dis {
($mac:ident ,$block:ident, $config:ident, $wrap:tt, $fill_value: ident, $($other:ident,)?) => {
$mac!(
$block, $config, $wrap, $fill_value, $($other,)?
{ i8|PluginResultType::R => reflectivity },
{ i8|PluginResultType::DBZ => reflectivity },
{ f32|PluginResultType::ZDR => differential_reflectivity },
{ f32|PluginResultType::KDP => specific_differential_phase },
{ f32|PluginResultType::PHIDP => differential_phase },
{ i8 |PluginResultType::V => velocity },
{ i8 |PluginResultType::SW => spectrum_width },
{ f32|PluginResultType::CC => correlation_coefficient },
{ i8 |PluginResultType::HCA => hydrometeor_classification },
{ f32|PluginResultType::VIL => vertically_integrated_liquid },
{ f32|PluginResultType::OHP => one_hour_precipitation },
{ f32|PluginResultType::THP => three_hour_precipitation },
{ f32|PluginResultType::ET => echo_tops },
{ f32|PluginResultType::EB => echo_bases }
)
};
}
type Cfg = Arc<dyn Any + Send + Sync>;
pub fn tools(data: &PluginResult) -> (ElementImpl, Cfg) {
let blocks_num = data.blocks.len();
if blocks_num == 0 {
panic!("No blocks found");
}
if blocks_num > 1 {
panic!("Too many blocks found");
}
let block = data.blocks.first().unwrap();
let fill_value = block.fill_value;
let config = CONFIG.read().unwrap();
let imp = match block.coord_type {
CoordType::Polar(loc, range) => {
let center = (loc.x, loc.y);
let cfg = dis!(dispatch_polar, block, config, PolarElementConfig, fill_value, center,);
(PolarElementImp().into(), cfg)
}
CoordType::Cartesian => match block.shape {
DataShape::Cube => {
let cfg = dis!(dispatch_3d, block, config, GridImpConfig, fill_value,);
(MultiLayerGridImp::new().into(), cfg)
}
DataShape::Matrix => (
GridImp::new().into(),
dis!(dispatch, block, config, GridImpConfig, fill_value,),
),
_ => panic!("Invalid shape"),
},
_ => {
panic!("Invalid type")
}
};
imp
}

View File

@ -0,0 +1 @@
pub struct GridData {}

View File

@ -0,0 +1,35 @@
pub mod data;
use data::GridData;
use quick_cache::sync::Cache;
use std::path::PathBuf;
use std::sync::Arc;
pub type Value<T> = Arc<T>;
pub enum Data {
GridData(GridData),
}
pub struct DataPool {
pool: Cache<PathBuf, Value<Data>>,
}
impl DataPool {
pub fn new() -> Self {
Self {
pool: Cache::new(10),
}
}
pub fn get_or_load(&self, path: impl Into<PathBuf>) -> Result<Value<Data>, ()> {
self.pool.get_or_insert_async(&path.into(), async {
let path = path.into();
let data = match path.extension().and_then(|ext| ext.to_str()) {
Some("grid") => Data::GridData(GridData::load(&path).await.unwrap()),
_ => panic!("Unsupported file type"),
};
Ok(Arc::new(data))
})
}
}

View File

@ -1,8 +1,6 @@
#![allow(unused)] #![allow(unused)]
#![allow(dead_code)] #![allow(dead_code)]
#[macro_use] #[macro_use]
mod utils;
#[macro_use]
extern crate lazy_static; extern crate lazy_static;
use config::{Config, Settings}; use config::{Config, Settings};
use gtk::{gio, prelude::SettingsExt}; use gtk::{gio, prelude::SettingsExt};
@ -14,8 +12,8 @@ use tokio::runtime::Runtime;
mod actions; mod actions;
mod components; mod components;
mod config; mod config;
mod coords;
mod data; mod data;
mod datapool;
mod errors; mod errors;
mod pipeline; mod pipeline;
mod plugin_system; mod plugin_system;
@ -29,9 +27,6 @@ use tracing_subscriber;
use gi::{App as GI, Helper, GL}; use gi::{App as GI, Helper, GL};
mod data_utils;
mod map_tile;
mod map_tile_utils;
mod predefined; mod predefined;
mod widgets; mod widgets;

View File

@ -1,249 +0,0 @@
use crate::components::messages::MonitorInputMsg;
use crate::components::{MonitorCommand, MonitorModel};
use crate::coords::Range;
use crate::map_tile_utils::lat_lon_to_zoom;
use crate::pipeline::{Target, TargetType};
use dirs::cache_dir;
use femtovg::ImageSource;
use futures::future::BoxFuture;
use gtk::ffi::gtk_about_dialog_add_credit_section;
use once_cell::sync::Lazy;
use quick_cache::sync::Cache;
use relm4::{ComponentSender, Sender};
use reqwest::{Client, Error, Url};
use slippy_map_tiles::{merc_location_to_tile_coords, BBox, Tile};
use smallvec::SmallVec;
use sorted_vec::SortedSet;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::Mutex;
use tokio::task;
use tracing::{debug, info};
static TILE_CACHE_PATH: Lazy<std::sync::Mutex<PathBuf>> = Lazy::new(|| {
let new_path = cache_dir()
.unwrap_or(PathBuf::from("./"))
.join("radar-g/tiles");
if !new_path.exists() {
info!("Create cache dir: {:?}", new_path);
std::fs::create_dir_all(&new_path).unwrap();
}
std::sync::Mutex::new(new_path)
});
type TileCache = Cache<Tile, Arc<std::sync::Mutex<Target>>>;
pub struct MapTile {
server: String,
api_key: String,
style: String,
client: Client,
onloading: Arc<std::sync::Mutex<HashSet<Tile>>>,
cache: Arc<std::sync::Mutex<TileCache>>,
zoom_level: Cell<u8>,
}
impl Default for MapTile {
fn default() -> Self {
Self {
server: "https://tiles.stadiamaps.com/tiles/".to_string(),
api_key: "06f1aeed-5d91-48e3-9ce5-1e99063f7f73".to_string(),
// style: "stamen_toner".to_string(),
style: "alidade_smooth_dark".to_string(),
client: Client::new(),
onloading: Arc::new(std::sync::Mutex::new(HashSet::new())),
cache: Arc::new(std::sync::Mutex::new(Cache::new(32))),
zoom_level: Cell::new(4),
}
}
}
impl MapTile {
pub fn new_tiles(
&self,
zoom: u8,
lat_range: (f32, f32),
lon_range: (f32, f32),
) -> impl Iterator<Item = Tile> + Sized {
let bbox = BBox::new(lat_range.1, lon_range.0, lat_range.0, lon_range.1).unwrap();
let tiles = bbox.tiles_for_zoom(zoom);
tiles
}
fn get_tile_task(&self, tile: &Tile) -> (bool, BoxFuture<'static, Result<Vec<u8>, Error>>) {
let _cache_path = TILE_CACHE_PATH.lock().unwrap().clone().join(format!(
"{}-{}-{}.png",
tile.zoom(),
tile.x(),
tile.y()
));
let exists_cache = _cache_path.exists();
if exists_cache {
let client = self.client.clone();
let key = self.api_key.clone();
(
false,
Box::pin(async move {
info!("Read from cache: {:?}", _cache_path);
let result = tokio::fs::read(_cache_path).await.unwrap();
Ok(result)
}),
)
} else {
let base_url = Url::parse(&self.server).unwrap();
let mut request_url = base_url
.join(&format!("{}/", self.style))
.unwrap()
.join(&format!("{}/{}/{}@2x.png", tile.zoom(), tile.x(), tile.y()))
.unwrap();
let client = self.client.clone();
let key = self.api_key.clone();
(
true,
Box::pin(async move {
let result = client
.get(request_url)
.query(&[("api_key", &key)])
.send()
.await?;
let bytes = result.bytes().await?;
Ok(bytes.to_vec())
}),
)
}
}
pub async fn get_tile(&self, tile: &Tile) -> Result<Vec<u8>, Error> {
let base_url = Url::parse(&self.server).unwrap();
let mut request_url = base_url
.join(&self.style)
.unwrap()
.join(&format!("{}/{}/{}@2x.png", tile.zoom(), tile.x(), tile.y()))
.unwrap();
let result = self
.client
.get(request_url)
.query(&[("api_key", &self.api_key)])
.send()
.await?;
let bytes = result.bytes().await?;
Ok(bytes.to_vec())
}
pub fn set_zoom(&self, zoom: u8) {
self.zoom_level.set(zoom);
}
fn insert_to_cache(cache: Arc<std::sync::Mutex<TileCache>>, result: Vec<u8>, tile: Tile) {
let origin = tile.nw_corner();
let rb = tile.se_corner();
let bounds = (
(origin.lon() as f64..rb.lon() as f64).into(),
(origin.lat() as f64..rb.lat() as f64).into(),
);
let result = Target::new(TargetType::Mem(result), 256.0, 256.0, bounds, None);
let cache = cache.lock().unwrap();
cache.insert(tile, Arc::new(std::sync::Mutex::new(result)));
}
async fn save_to_cache_path(result: &Vec<u8>, tile: Tile) -> Result<(), std::io::Error> {
let _cache_path = TILE_CACHE_PATH.lock().unwrap().clone();
let path = _cache_path.join(format!("{}-{}-{}.png", tile.zoom(), tile.x(), tile.y()));
tokio::fs::write(path, result).await
}
pub fn load_tiles(
&self,
range: ((f32, f32), (f32, f32)),
sender: ComponentSender<MonitorModel>,
) -> BoxFuture<'static, ()> {
let zoom = self.zoom_level.get();
let new_tiles = self.new_tiles(zoom, range.0, range.1);
let cache = (*self.cache).lock().unwrap();
let mut bindings = self.onloading.lock().unwrap();
let new_tiles = new_tiles
.filter(|x| cache.peek(x).is_none() && !bindings.contains(x))
.collect::<Vec<_>>();
if new_tiles.len() > 0 {
bindings.extend(new_tiles.iter().cloned());
info!("Load new tiles");
let tasks = new_tiles
.into_iter()
.map(move |tile| {
let (need_save, _task) = self.get_tile_task(&tile);
let sender = sender.clone();
let cache = self.cache.clone();
let onloading = self.onloading.clone();
task::spawn(async move {
let result = _task.await;
if let Ok(result) = result {
if let Err(e) = Self::save_to_cache_path(&result, tile).await {
info!("Error saving to cache: {}", e);
}
if need_save {
Self::save_to_cache_path(&result, tile);
}
Self::insert_to_cache(cache, result, tile);
onloading.lock().unwrap().remove(&tile);
sender.command_sender().emit(MonitorCommand::LoadedTile);
}
})
})
.collect::<Vec<_>>();
return Box::pin(async move {
for task in tasks {
task.await.unwrap();
}
});
} else {
info!("No new tiles need to download");
return Box::pin(async move {
sender.command_sender().emit(MonitorCommand::LoadedTile);
});
}
}
pub fn current_tiles(
&self,
range: ((f32, f32), (f32, f32)),
) -> Vec<Arc<std::sync::Mutex<Target>>> {
let zoom = self.zoom_level.get();
let current = self.new_tiles(zoom, range.0, range.1);
let mut total_len = 0;
let mut results = Vec::new();
let mut cached = Vec::new();
let cache = self.cache.lock().unwrap();
for tile in current {
total_len += 1;
if let Some(target) = cache.get(&tile) {
results.push(target);
} else {
let center = tile.center_point();
let mut start_zoom = zoom - 1;
while start_zoom > 0 {
let (tile, (x, y)) = merc_location_to_tile_coords(
center.lon() as f64,
center.lat() as f64,
start_zoom,
);
let tile = Tile::new(start_zoom, tile.0, tile.1).unwrap();
if cached.contains(&tile) {
break;
} else {
if let Some(target) = cache.get(&tile) {
results.insert(0, target);
cached.push(tile);
break;
}
}
start_zoom -= 1;
}
}
}
results
}
}

View File

@ -1,8 +0,0 @@
pub fn lat_lon_to_zoom(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h: f32) -> u8 {
let lat_diff = lat_range.1 - lat_range.0;
let lon_diff = lon_range.1 - lon_range.0;
let z1 = ((360.0 * w) as f64 / lon_diff / 256.0).log2();
let z2 = ((180.0 * h) as f64 / lat_diff / 256.0).log2();
let z = z1.min(z2);
z as u8
}

View File

@ -1,604 +0,0 @@
use chrono::{DateTime, Utc};
use euclid::Size2D;
use femtovg::Canvas;
use femtovg::Color;
use gtk::glib::{HasParamSpec, ParamSpecInt64, ParamSpecInt64Builder};
use ndarray::{Array2, ArrayView1};
use std::{borrow::BorrowMut, num::NonZeroU32};
use surfman::{
Connection, Context, ContextAttributeFlags, ContextAttributes, ContextDescriptor, Device,
GLVersion, NativeConnection, NativeContext, SurfaceAccess, SurfaceType,
};
use crate::predefined::color_mapper::{BoundaryNorm, BoundaryNormDiscrete, Discrete};
use crate::RUNTIME;
use std::sync::Arc;
use std::time::Duration;
use tokio::{
sync::{mpsc, oneshot, Mutex},
time,
};
pub fn meshgrid<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>)
where
T: Clone,
{
let shape = (y.len(), x.len());
println!("shape: {:?}", shape);
let xx = Array2::from_shape_fn(shape, |(_, j)| x[j].clone());
let yy = Array2::from_shape_fn(shape, |(i, _)| y[i].clone());
(xx, yy)
}
macro_rules! default_cvmapers {
($({
$key: ident, $value: expr, $colors: expr, $_ty:ty
}),+ $(,)?) => {
$(
pub fn $key() -> BoundaryNormDiscrete<$_ty> {
let discrete = Discrete::new($colors);
let boundarynorm = BoundaryNorm::new($value, true, None);
BoundaryNormDiscrete::new(discrete, boundarynorm)
}
)+
};
}
default_cvmapers! {
{
create_reflect_default_cvmapper,
vec![-5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75],
vec![
Color::rgb(23, 174, 165),
Color::rgb(198, 195, 253),
Color::rgb(124, 114, 236),
Color::rgb(1, 160, 246),
Color::rgb(0, 236, 236),
Color::rgb(0, 216, 0),
Color::rgb(1, 144, 0),
Color::rgb(255, 255, 0),
Color::rgb(231, 192, 0),
Color::rgb(255, 144, 0),
Color::rgb(255, 0, 0),
Color::rgb(214, 0, 0),
Color::rgb(192, 0, 0),
Color::rgb(255, 0, 240),
Color::rgb(150, 0, 180),
Color::rgb(139, 0, 255),
],
i8
},
{
create_vel_default_cvmapper,
vec![
-35, -27, -20, -15, -10, -5, -1, 0, 1, 5, 10, 15, 20, 27,
35,
],
vec![
Color::rgb(0, 224, 255),
Color::rgb(0, 128, 255),
Color::rgb(50, 0, 150),
Color::rgb(0, 251, 144),
Color::rgb(0, 187, 144),
Color::rgb(0, 143, 0),
Color::rgb(205, 192, 159),
Color::rgb(255, 255, 255),
Color::rgb(248, 135, 0),
Color::rgb(255, 207, 0),
Color::rgb(255, 255, 0),
Color::rgb(174, 0, 0),
Color::rgb(208, 112, 0),
Color::rgb(255, 0, 0),
],
i8
},
{
create_phidp_default_cvmapper,
vec![
0.0, 22.0, 46.0, 68.0, 90.0, 112.0, 136.0, 158.0, 180.0, 202.0, 224.0, 248.0, 270.0,
292.0, 314.0, 359.0,
],
vec![
Color::rgb(0, 60, 255),
Color::rgb(0, 239, 239),
Color::rgb(0, 186, 191),
Color::rgb(0, 131, 125),
Color::rgb(0, 137, 56),
Color::rgb(0, 183, 41),
Color::rgb(0, 218, 13),
Color::rgb(0, 255, 0),
Color::rgb(255, 255, 59),
Color::rgb(255, 240, 0),
Color::rgb(255, 198, 0),
Color::rgb(255, 165, 0),
Color::rgb(255, 114, 0),
Color::rgb(255, 31, 0),
Color::rgb(193, 0, 0),
],
f32
},
{
create_zdr_default_cvmapper,
vec![
-5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0,
],
vec![
Color::rgb(70, 70, 70),
Color::rgb(80, 80, 80),
Color::rgb(90, 90, 90),
Color::rgb(100, 100, 100),
Color::rgb(110, 110, 110),
Color::rgb(120, 120, 120),
Color::rgb(130, 130, 130),
Color::rgb(140, 140, 140),
Color::rgb(150, 150, 150),
Color::rgb(175, 175, 175),
Color::rgb(200, 200, 200),
Color::rgb(220, 240, 220),
Color::rgb(0, 192, 39),
],
f32
},
{
create_cc_default_cvmapper,
vec![
0.0, 0.1, 0.3, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.92, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99,
],
vec![
Color::rgb(0, 60, 255),
Color::rgb(0, 239, 239),
Color::rgb(0, 186, 191),
Color::rgb(0, 131, 125),
Color::rgb(0, 137, 56),
Color::rgb(0, 183, 41),
Color::rgb(0, 218, 13),
Color::rgb(0, 255, 0),
Color::rgb(255, 255, 59),
Color::rgb(255, 240, 0),
Color::rgb(255, 198, 0),
Color::rgb(255, 165, 0),
Color::rgb(255, 114, 0),
Color::rgb(255, 31, 0),
Color::rgb(193, 0, 0),
],
f32
},
{
create_vil_default_cvmapper,
vec![
1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35., 40., 45., 50., 55., 60., 65., 70.,
],
vec![
Color::rgb(156, 156, 156),
Color::rgb(118, 118, 118),
Color::rgb(250, 170, 170),
Color::rgb(238, 140, 140),
Color::rgb(201, 112, 112),
Color::rgb(0, 251, 144),
Color::rgb(0, 187, 0),
Color::rgb(255, 255, 112),
Color::rgb(208, 208, 96),
Color::rgb(255, 96, 96),
Color::rgb(218, 0, 0),
Color::rgb(174, 0, 0),
Color::rgb(0, 0, 255),
Color::rgb(255, 255, 255),
],
f32
},
{
create_hgt_default_cvmapper,
vec![
0.0, 2.0, 3.0, 5.0, 6.0, 8.0, 9.0, 11., 12., 14., 15., 17., 18., 20., 21.,
],
vec![
Color::rgb(0, 0, 0),
Color::rgb(118, 118, 118),
Color::rgb(0, 224, 255),
Color::rgb(0, 176, 255),
Color::rgb(0, 144, 204),
Color::rgb(50, 0, 150),
Color::rgb(0, 251, 144),
Color::rgb(0, 187, 0),
Color::rgb(0, 239, 0),
Color::rgb(254, 191, 0),
Color::rgb(255, 255, 0),
Color::rgb(174, 0, 0),
Color::rgb(255, 0, 0),
Color::rgb(255, 255, 255),
],
f32
},
{
create_et_default_cvmapper,
vec![
0.1, 0.2, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0, 6.0, 8.0, 9.0, 11.0, 12.0, 14.0, 15.0, 17.0,
18.0, 20.0, 21.0,
],
vec![
Color::rgb(204, 253, 255),
Color::rgb(153, 248, 255),
Color::rgb(101, 239, 255),
Color::rgb(50, 227, 255),
Color::rgb(134, 255, 134),
Color::rgb(80, 255, 80),
Color::rgb(0, 241, 1),
Color::rgb(0, 187, 0),
Color::rgb(255, 255, 84),
Color::rgb(255, 240, 0),
Color::rgb(255, 191, 0),
Color::rgb(255, 168, 0),
Color::rgb(255, 89, 89),
Color::rgb(255, 64, 64),
Color::rgb(255, 13, 13),
Color::rgb(237, 0, 0),
Color::rgb(205, 0, 0),
Color::rgb(139, 0, 0),
],
f32
},
{
create_cpc_default_cvmapper,
vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
vec![
Color::rgb(201, 196, 191),
Color::rgb(112, 188, 73),
Color::rgb(245, 163, 110),
Color::rgb(208, 119, 52),
Color::rgb(234, 37, 47),
Color::rgb(199, 53, 47),
Color::rgb(145, 71, 152),
Color::rgb(178, 177, 65),
Color::rgb(103, 199, 208),
Color::rgb(55, 90, 165),
Color::rgb(187, 165, 204),
],
i8
},
{
create_kdp_default_cvmapper,
vec![
-0.8, -0.4, -0.2, -0.1, 0.1, 0.15, 0.22, 0.33, 0.5, 0.75, 1.1, 1.7, 2.4, 3.1, 7.0, 20.0,
],
vec![
Color::rgb(0, 255, 255),
Color::rgb(0, 239, 239),
Color::rgb(0, 168, 172),
Color::rgb(180, 180, 180),
Color::rgb(180, 180, 180),
Color::rgb(0, 192, 39),
Color::rgb(0, 232, 10),
Color::rgb(36, 255, 36),
Color::rgb(255, 255, 30),
Color::rgb(255, 230, 0),
Color::rgb(255, 188, 0),
Color::rgb(255, 152, 0),
Color::rgb(255, 94, 0),
Color::rgb(242, 15, 0),
Color::rgb(187, 0, 58),
Color::rgb(253, 6, 253),
],
f32
}
}
// pub fn create_dbz_boundarynorm() -> BoundaryNorm<i8> {
//
// // Discrete::new()
//
// // BoundaryNorm::new()
//
// BoundaryNormDiscrete::new(
//
// )
// BoundaryNorm::new(
// vec![
// -5, 0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75,
// ],
// vec![
// Color::rgb(23, 174, 165),
// Color::rgb(198, 195, 253),
// Color::rgb(124, 114, 236),
// Color::rgb(1, 160, 246),
// Color::rgb(0, 236, 236),
// Color::rgb(0, 216, 0),
// Color::rgb(1, 144, 0),
// Color::rgb(255, 255, 0),
// Color::rgb(231, 192, 0),
// Color::rgb(255, 144, 0),
// Color::rgb(255, 0, 0),
// Color::rgb(214, 0, 0),
// Color::rgb(192, 0, 0),
// Color::rgb(255, 0, 240),
// Color::rgb(150, 0, 180),
// Color::rgb(139, 0, 255),
// ],
// true,
// -125,
// )
// }
// pub fn create_vel_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// -35.0, -27.0, -20.0, -15.0, -10.0, -5.0, -1.0, 0.0, 1.0, 5.0, 10.0, 15.0, 20.0, 27.0,
// 35.0,
// ],
// vec![
// Color::rgb(0, 224, 255),
// Color::rgb(0, 128, 255),
// Color::rgb(50, 0, 150),
// Color::rgb(0, 251, 144),
// Color::rgb(0, 187, 144),
// Color::rgb(0, 143, 0),
// Color::rgb(205, 192, 159),
// Color::rgb(255, 255, 255),
// Color::rgb(248, 135, 0),
// Color::rgb(255, 207, 0),
// Color::rgb(255, 255, 0),
// Color::rgb(174, 0, 0),
// Color::rgb(208, 112, 0),
// Color::rgb(255, 0, 0),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_phidp_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// 0.0, 22.0, 46.0, 68.0, 90.0, 112.0, 136.0, 158.0, 180.0, 202.0, 224.0, 248.0, 270.0,
// 292.0, 314.0, 359.0,
// ],
// vec![
// Color::rgb(0, 60, 255),
// Color::rgb(0, 239, 239),
// Color::rgb(0, 186, 191),
// Color::rgb(0, 131, 125),
// Color::rgb(0, 137, 56),
// Color::rgb(0, 183, 41),
// Color::rgb(0, 218, 13),
// Color::rgb(0, 255, 0),
// Color::rgb(255, 255, 59),
// Color::rgb(255, 240, 0),
// Color::rgb(255, 198, 0),
// Color::rgb(255, 165, 0),
// Color::rgb(255, 114, 0),
// Color::rgb(255, 31, 0),
// Color::rgb(193, 0, 0),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_zdr_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// -5.0, -4.5, -4.0, -3.5, -3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.0, 0.5, 1.0,
// ],
// vec![
// Color::rgb(70, 70, 70),
// Color::rgb(80, 80, 80),
// Color::rgb(90, 90, 90),
// Color::rgb(100, 100, 100),
// Color::rgb(110, 110, 110),
// Color::rgb(120, 120, 120),
// Color::rgb(130, 130, 130),
// Color::rgb(140, 140, 140),
// Color::rgb(150, 150, 150),
// Color::rgb(175, 175, 175),
// Color::rgb(200, 200, 200),
// Color::rgb(220, 240, 220),
// Color::rgb(0, 192, 39),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_cc_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// 0.0, 0.1, 0.3, 0.5, 0.6, 0.7, 0.8, 0.85, 0.9, 0.92, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99,
// ],
// vec![
// Color::rgb(0, 60, 255),
// Color::rgb(0, 239, 239),
// Color::rgb(0, 186, 191),
// Color::rgb(0, 131, 125),
// Color::rgb(0, 137, 56),
// Color::rgb(0, 183, 41),
// Color::rgb(0, 218, 13),
// Color::rgb(0, 255, 0),
// Color::rgb(255, 255, 59),
// Color::rgb(255, 240, 0),
// Color::rgb(255, 198, 0),
// Color::rgb(255, 165, 0),
// Color::rgb(255, 114, 0),
// Color::rgb(255, 31, 0),
// Color::rgb(193, 0, 0),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_vil_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// 1.0, 5.0, 10.0, 15.0, 20.0, 25.0, 30.0, 35., 40., 45., 50., 55., 60., 65., 70.,
// ],
// vec![
// Color::rgb(156, 156, 156),
// Color::rgb(118, 118, 118),
// Color::rgb(250, 170, 170),
// Color::rgb(238, 140, 140),
// Color::rgb(201, 112, 112),
// Color::rgb(0, 251, 144),
// Color::rgb(0, 187, 0),
// Color::rgb(255, 255, 112),
// Color::rgb(208, 208, 96),
// Color::rgb(255, 96, 96),
// Color::rgb(218, 0, 0),
// Color::rgb(174, 0, 0),
// Color::rgb(0, 0, 255),
// Color::rgb(255, 255, 255),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_hgt_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// 0.0, 2.0, 3.0, 5.0, 6.0, 8.0, 9.0, 11., 12., 14., 15., 17., 18., 20., 21.,
// ],
// vec![
// Color::rgb(0, 0, 0),
// Color::rgb(118, 118, 118),
// Color::rgb(0, 224, 255),
// Color::rgb(0, 176, 255),
// Color::rgb(0, 144, 204),
// Color::rgb(50, 0, 150),
// Color::rgb(0, 251, 144),
// Color::rgb(0, 187, 0),
// Color::rgb(0, 239, 0),
// Color::rgb(254, 191, 0),
// Color::rgb(255, 255, 0),
// Color::rgb(174, 0, 0),
// Color::rgb(255, 0, 0),
// Color::rgb(255, 255, 255),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_et_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// 0.1, 0.2, 0.5, 1.0, 1.5, 2.0, 3.0, 5.0, 6.0, 8.0, 9.0, 11.0, 12.0, 14.0, 15.0, 17.0,
// 18.0, 20.0, 21.0,
// ],
// vec![
// Color::rgb(204, 253, 255),
// Color::rgb(153, 248, 255),
// Color::rgb(101, 239, 255),
// Color::rgb(50, 227, 255),
// Color::rgb(134, 255, 134),
// Color::rgb(80, 255, 80),
// Color::rgb(0, 241, 1),
// Color::rgb(0, 187, 0),
// Color::rgb(255, 255, 84),
// Color::rgb(255, 240, 0),
// Color::rgb(255, 191, 0),
// Color::rgb(255, 168, 0),
// Color::rgb(255, 89, 89),
// Color::rgb(255, 64, 64),
// Color::rgb(255, 13, 13),
// Color::rgb(237, 0, 0),
// Color::rgb(205, 0, 0),
// Color::rgb(139, 0, 0),
// ],
// true,
// -125.0,
// )
// }
//
// pub fn create_cpc_boundarynorm() -> BoundaryNorm<i8> {
// BoundaryNorm::new(
// vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11],
// vec![
// Color::rgb(201, 196, 191),
// Color::rgb(112, 188, 73),
// Color::rgb(245, 163, 110),
// Color::rgb(208, 119, 52),
// Color::rgb(234, 37, 47),
// Color::rgb(199, 53, 47),
// Color::rgb(145, 71, 152),
// Color::rgb(178, 177, 65),
// Color::rgb(103, 199, 208),
// Color::rgb(55, 90, 165),
// Color::rgb(187, 165, 204),
// ],
// true,
// -125,
// )
// }
//
// pub fn create_kdp_boundarynorm() -> BoundaryNorm<f32> {
// BoundaryNorm::new(
// vec![
// -0.8, -0.4, -0.2, -0.1, 0.1, 0.15, 0.22, 0.33, 0.5, 0.75, 1.1, 1.7, 2.4, 3.1, 7.0, 20.0,
// ],
// vec![
// Color::rgb(0, 255, 255),
// Color::rgb(0, 239, 239),
// Color::rgb(0, 168, 172),
// Color::rgb(180, 180, 180),
// Color::rgb(180, 180, 180),
// Color::rgb(0, 192, 39),
// Color::rgb(0, 232, 10),
// Color::rgb(36, 255, 36),
// Color::rgb(255, 255, 30),
// Color::rgb(255, 230, 0),
// Color::rgb(255, 188, 0),
// Color::rgb(255, 152, 0),
// Color::rgb(255, 94, 0),
// Color::rgb(242, 15, 0),
// Color::rgb(187, 0, 58),
// Color::rgb(253, 6, 253),
// ],
// true,
// -125.0,
// )
// }
pub fn estimate_zoom_level(
lat_min: f64,
lon_min: f64,
lat_max: f64,
lon_max: f64,
screen_width: f64,
screen_height: f64,
) -> u8 {
let r: f64 = 6371.0;
let avg_lat = (lat_min + lat_max) / 2.0;
// 将经纬度范围转换为在该纬度下的公里数
let delta_lon = (lon_max - lon_min)
* (r * std::f64::consts::PI / 180.0)
* (avg_lat * std::f64::consts::PI / 180.0).cos().abs();
let delta_lat = (lat_max - lat_min) * 111.32;
// 估算每个像素代表的实际距离(公里/像素)
let km_per_pixel_x = delta_lon / screen_width;
let km_per_pixel_y = delta_lat / screen_height;
// 选择较小的比例尺
let km_per_pixel = km_per_pixel_x.min(km_per_pixel_y);
// 根据比例尺估算Zoom Level
// 这里的比例尺和Zoom Level的对应关系可能需要根据实际地图服务进行调整
let zoom_level_estimation = 14.0 - km_per_pixel.log10();
zoom_level_estimation.round() as u8
}
pub fn parse_hex_color(hex: &str) -> Result<(u8, u8, u8), &'static str> {
if hex.starts_with('#') && hex.len() == 7 {
let r = u8::from_str_radix(&hex[1..3], 16);
let g = u8::from_str_radix(&hex[3..5], 16);
let b = u8::from_str_radix(&hex[5..7], 16);
match (r, g, b) {
(Ok(r), Ok(g), Ok(b)) => Ok((r, g, b)),
_ => Err("Invalid hexadecimal value"),
}
} else {
Err("Invalid color format")
}
}