sync
This commit is contained in:
parent
77d8c1f0eb
commit
58947b9aa1
@ -8,11 +8,6 @@ use super::{
|
||||
};
|
||||
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
|
||||
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::predefined::color_mapper::{BoundaryNorm, ColorMapper, ColorMapperComb, Discrete};
|
||||
use crate::predefined::widgets::ColorBar;
|
||||
@ -82,10 +77,10 @@ pub enum AppMsg {
|
||||
CloseRequest,
|
||||
Close,
|
||||
OpenDialog,
|
||||
LayerManager(LayerMsg),
|
||||
Layer,
|
||||
NewElement(Rc<RefCell<Element>>),
|
||||
DeleteElement(ElementKey),
|
||||
// LayerManager(LayerMsg),
|
||||
// Layer,
|
||||
// NewElement(Rc<RefCell<Element>>),
|
||||
// DeleteElement(ElementKey),
|
||||
}
|
||||
type RcDispatcher = Rc<Dispatcher>;
|
||||
type ArcDispatcher = Arc<Dispatcher>;
|
||||
@ -96,6 +91,8 @@ pub struct AppModel {
|
||||
#[do_not_track]
|
||||
cms: CMS,
|
||||
waiting_for: Option<DateTime<Utc>>,
|
||||
|
||||
// Components
|
||||
#[do_not_track]
|
||||
open_dialog: Controller<OpenDialog>,
|
||||
#[do_not_track]
|
||||
@ -104,15 +101,19 @@ pub struct AppModel {
|
||||
render: Controller<MonitorModel>,
|
||||
#[do_not_track]
|
||||
sidebar: Controller<SideBarModel>,
|
||||
|
||||
// Data
|
||||
selected_layer: Vec<usize>,
|
||||
#[do_not_track]
|
||||
layers: Rc<RefCell<Vec<Layer>>>,
|
||||
#[do_not_track]
|
||||
buffer: Arc<Buffer<Key>>,
|
||||
// #[do_not_track]
|
||||
// buffer: Arc<Buffer<Key>>,
|
||||
|
||||
// File Pool
|
||||
#[do_not_track]
|
||||
file_pool: Arc<KVBuffer<PathBuf, Arc<PluginResult>>>,
|
||||
#[do_not_track]
|
||||
elements: Vec<Rc<RefCell<Element>>>,
|
||||
// #[do_not_track]
|
||||
// elements: Vec<Rc<RefCell<Element>>>,
|
||||
#[do_not_track]
|
||||
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 sidebar =
|
||||
SideBarModel::builder()
|
||||
.launch(layers.clone())
|
||||
.forward(sender.input_sender(), |msg| {
|
||||
AppMsg::LayerManager(match msg {
|
||||
SideBarOutputMsg::SelectLayer(idx) => LayerMsg::Select(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,
|
||||
});
|
||||
// SideBar Component
|
||||
// let sidebar =
|
||||
// SideBarModel::builder()
|
||||
// .launch(layers.clone())
|
||||
// .forward(sender.input_sender(), |msg| {
|
||||
// AppMsg::LayerManager(match msg {
|
||||
// SideBarOutputMsg::SelectLayer(idx) => LayerMsg::Select(idx),
|
||||
// SideBarOutputMsg::NewLayer(layer) => LayerMsg::Add(layer),
|
||||
// SideBarOutputMsg::SwitchToTimeSeries(idx) => LayerMsg::SwitchToTime(idx),
|
||||
// })
|
||||
// });
|
||||
|
||||
let setting = SettingModel::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |a| AppMsg::Close);
|
||||
// // Monitor Component
|
||||
// let render =
|
||||
// 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 cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
|
||||
let dialog_buffer = buffer.clone();
|
||||
let dialog_cms = cms.clone();
|
||||
// let setting = SettingModel::builder()
|
||||
// .launch(())
|
||||
// .forward(sender.input_sender(), |a| AppMsg::Close);
|
||||
|
||||
let dialog = {
|
||||
let dialog_dispatcher = dispatcher.clone();
|
||||
let dialog_sidebar_sender = sidebar.sender().clone();
|
||||
let dialog_render_sender = render.sender().clone();
|
||||
let dialog_file_pool = file_pool.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));
|
||||
// // Background Dispatcher
|
||||
// let mut dispatcher = Arc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1)));
|
||||
// let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
|
||||
// let dialog_buffer = buffer.clone();
|
||||
// let dialog_cms = cms.clone();
|
||||
|
||||
let element = Rc::new(RefCell::new(Element::new(
|
||||
"CR",
|
||||
dialog_cms.clone(),
|
||||
dialog_dispatcher.clone(),
|
||||
true,
|
||||
cfg,
|
||||
path.clone(),
|
||||
dialog_buffer.clone(),
|
||||
dialog_file_pool.clone(),
|
||||
imp,
|
||||
)));
|
||||
// // File Dialog
|
||||
// let dialog = {
|
||||
// let dialog_dispatcher = dispatcher.clone();
|
||||
// let dialog_sidebar_sender = sidebar.sender().clone();
|
||||
// let dialog_render_sender = render.sender().clone();
|
||||
// let dialog_file_pool = file_pool.clone();
|
||||
// OpenDialog::builder()
|
||||
// .transient_for_native(&root)
|
||||
// .launch(OpenDialogSettings::default())
|
||||
// .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()));
|
||||
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
|
||||
lon_start, lon_end, lat_start, lat_end,
|
||||
));
|
||||
// let element = Rc::new(RefCell::new(Element::new(
|
||||
// "CR",
|
||||
// dialog_cms.clone(),
|
||||
// dialog_dispatcher.clone(),
|
||||
// true,
|
||||
// cfg,
|
||||
// path.clone(),
|
||||
// dialog_buffer.clone(),
|
||||
// dialog_file_pool.clone(),
|
||||
// imp,
|
||||
// )));
|
||||
|
||||
AppMsg::NewElement(element)
|
||||
}
|
||||
_ => AppMsg::Close,
|
||||
})
|
||||
};
|
||||
// dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map()));
|
||||
// dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
|
||||
// lon_start, lon_end, lat_start, lat_end,
|
||||
// ));
|
||||
|
||||
// AppMsg::NewElement(element)
|
||||
// }
|
||||
// _ => AppMsg::Close,
|
||||
// })
|
||||
// };
|
||||
|
||||
let model = AppModel {
|
||||
cms,
|
||||
dispatcher,
|
||||
// cms,
|
||||
// dispatcher,
|
||||
waiting_for: None,
|
||||
buffer,
|
||||
// buffer,
|
||||
file_pool,
|
||||
open_dialog: dialog,
|
||||
// open_dialog: dialog,
|
||||
selected_layer: vec![],
|
||||
sidebar,
|
||||
elements: vec![],
|
||||
// sidebar,
|
||||
// elements: vec![],
|
||||
control,
|
||||
render,
|
||||
layers,
|
||||
|
||||
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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(())
|
||||
}
|
||||
}
|
||||
@ -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(),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
// }
|
||||
// }
|
||||
@ -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)
|
||||
// }
|
||||
// }
|
||||
@ -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
|
||||
}
|
||||
1
radar-g/src/datapool/data.rs
Normal file
1
radar-g/src/datapool/data.rs
Normal file
@ -0,0 +1 @@
|
||||
pub struct GridData {}
|
||||
35
radar-g/src/datapool/mod.rs
Normal file
35
radar-g/src/datapool/mod.rs
Normal 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))
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,6 @@
|
||||
#![allow(unused)]
|
||||
#![allow(dead_code)]
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
use config::{Config, Settings};
|
||||
use gtk::{gio, prelude::SettingsExt};
|
||||
@ -14,8 +12,8 @@ use tokio::runtime::Runtime;
|
||||
mod actions;
|
||||
mod components;
|
||||
mod config;
|
||||
mod coords;
|
||||
mod data;
|
||||
mod datapool;
|
||||
mod errors;
|
||||
mod pipeline;
|
||||
mod plugin_system;
|
||||
@ -29,9 +27,6 @@ use tracing_subscriber;
|
||||
|
||||
use gi::{App as GI, Helper, GL};
|
||||
|
||||
mod data_utils;
|
||||
mod map_tile;
|
||||
mod map_tile_utils;
|
||||
mod predefined;
|
||||
mod widgets;
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user