Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cd4c46f9c6 | |||
|
|
09b7e83669 | ||
|
|
59a886f222 | ||
| b0458c34a5 | |||
| 18b7d2d3d8 | |||
| 1e8a90aebe | |||
|
|
5eee473c6b | ||
|
|
2e7e7f0a9f | ||
|
|
ae324eef15 |
113
Cargo.lock
generated
113
Cargo.lock
generated
@ -513,7 +513,6 @@ dependencies = [
|
||||
"iana-time-zone",
|
||||
"js-sys",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
@ -531,7 +530,6 @@ dependencies = [
|
||||
"crossbeam",
|
||||
"dirs",
|
||||
"epoxy",
|
||||
"etws_loader",
|
||||
"euclid",
|
||||
"femtovg",
|
||||
"flate2",
|
||||
@ -552,7 +550,7 @@ dependencies = [
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"libadwaita",
|
||||
"libloading 0.8.0",
|
||||
"libloading 0.8.3",
|
||||
"ndarray",
|
||||
"npyz",
|
||||
"num-traits",
|
||||
@ -988,7 +986,7 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412"
|
||||
dependencies = [
|
||||
"libloading 0.8.0",
|
||||
"libloading 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1075,24 +1073,6 @@ dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "etws_loader"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"abi_stable",
|
||||
"anyhow",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"nom",
|
||||
"nom-derive",
|
||||
"num-traits",
|
||||
"radarg_plugin_interface",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "euclid"
|
||||
version = "0.22.9"
|
||||
@ -1135,19 +1115,20 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "femtovg"
|
||||
version = "0.7.1"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a3a2d0ff0df09856a5c1c89cc83863a1f0f994c55452186621bb57a01f270b3"
|
||||
checksum = "ad3cf7e8f8e3c684b418c2640c931afc8bbc7ebe547bed6bf64170f1f51d57c9"
|
||||
dependencies = [
|
||||
"bitflags 2.4.2",
|
||||
"fnv",
|
||||
"generational-arena",
|
||||
"glow",
|
||||
"image",
|
||||
"imgref",
|
||||
"log",
|
||||
"lru",
|
||||
"rgb",
|
||||
"rustybuzz",
|
||||
"slotmap",
|
||||
"unicode-bidi",
|
||||
"unicode-segmentation",
|
||||
"wasm-bindgen",
|
||||
@ -1763,9 +1744,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.12.2"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "807edf58b70c0b5b2181dd39fe1839dbdb3ba02645630dc5f753e23da307f762"
|
||||
checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"slotmap",
|
||||
@ -2348,12 +2329,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.0"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d580318f95776505201b28cf98eb1fa5e4be3b689633ba6a3e6cd880ff22d8cb"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2408,9 +2389,9 @@ checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.10.1"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "718e8fae447df0c7e1ba7f5189829e63fd536945c8988d61444c19039f16b670"
|
||||
checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc"
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
@ -2626,28 +2607,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom-derive"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ff943d68b88d0b87a6e0d58615e8fa07f9fd5a1319fa0a72efc1f62275c79a7"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"nom-derive-impl",
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom-derive-impl"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd0b9a93a84b0d3ec3e70e02d332dc33ac6dfac9cde63e17fcb77172dededa62"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.76",
|
||||
"quote 1.0.35",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "npyz"
|
||||
version = "0.8.1"
|
||||
@ -3698,25 +3657,19 @@ dependencies = [
|
||||
"base64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4"
|
||||
|
||||
[[package]]
|
||||
name = "rustybuzz"
|
||||
version = "0.7.0"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "162bdf42e261bee271b3957691018634488084ef577dddeb6420a9684cab2a6a"
|
||||
checksum = "88117946aa1bfb53c2ae0643ceac6506337f44887f8c9fbfb43587b1cc52ba49"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"bitflags 2.4.2",
|
||||
"bytemuck",
|
||||
"smallvec",
|
||||
"ttf-parser 0.18.1",
|
||||
"ttf-parser 0.20.0",
|
||||
"unicode-bidi-mirroring",
|
||||
"unicode-ccc",
|
||||
"unicode-general-category",
|
||||
"unicode-properties",
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
@ -3963,9 +3916,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
@ -4570,12 +4523,6 @@ version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633"
|
||||
|
||||
[[package]]
|
||||
name = "ttf-parser"
|
||||
version = "0.20.0"
|
||||
@ -4608,21 +4555,15 @@ checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi-mirroring"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
|
||||
checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ccc"
|
||||
version = "0.1.2"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-general-category"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7"
|
||||
checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
@ -4639,6 +4580,12 @@ dependencies = [
|
||||
"tinyvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-properties"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4259d9d4425d9f0661581b804cb85fe66a4c631cadd8f490d1c13a35d5d9291"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-script"
|
||||
version = "0.5.5"
|
||||
|
||||
12
Cargo.toml
12
Cargo.toml
@ -20,11 +20,11 @@ quadtree_rs = "0.1.2"
|
||||
proj-sys = "0.23.1"
|
||||
glib-macros = "0.19.2"
|
||||
svg = "0.13.1"
|
||||
libloading = "0.8.0"
|
||||
libloading = "0.8.3"
|
||||
glue = "0.8.7"
|
||||
epoxy = "0.1.0"
|
||||
femtovg = "0.7.1"
|
||||
glow = "0.12.2"
|
||||
femtovg = "0.9.0"
|
||||
glow = "0.13.1"
|
||||
proj = "0.27.2"
|
||||
image = "0.24.7"
|
||||
anyhow = "1.0.72"
|
||||
@ -82,10 +82,10 @@ path = "geo-macros"
|
||||
[dependencies.radarg_plugin_interface]
|
||||
path = "radarg_plugin_interface"
|
||||
|
||||
[dependencies.etws_loader]
|
||||
path = "etws_loader"
|
||||
#[dependencies.etws_loader]
|
||||
#path = "etws_loader"
|
||||
|
||||
[dependencies.adw]
|
||||
package = "libadwaita"
|
||||
version = "*"
|
||||
version = "0.6.0"
|
||||
features = ["v1_4"]
|
||||
|
||||
@ -29,6 +29,10 @@ paned>separator {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
font-size: 8px;
|
||||
}
|
||||
|
||||
.lv {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
@ -89,15 +89,24 @@ impl Plugin for ETWSLoader {
|
||||
CoordType::Other
|
||||
};
|
||||
|
||||
let lat = b.info.dimension_values.get(0).unwrap();
|
||||
let lon = b.info.dimension_values.get(1).unwrap();
|
||||
lat_range = [lat[0], lat[lat.len() - 1]];
|
||||
lon_range = [lon[0], lon[lon.len() - 1]];
|
||||
|
||||
|
||||
let shape = match b.info.dimension_size.len() {
|
||||
1 => radarg_plugin_interface::DataShape::Vector,
|
||||
2 => radarg_plugin_interface::DataShape::Matrix,
|
||||
_ => radarg_plugin_interface::DataShape::Scalar,
|
||||
2 => {
|
||||
let lat = b.info.dimension_values.get(0).unwrap();
|
||||
let lon = b.info.dimension_values.get(1).unwrap();
|
||||
lat_range = [lat[0], lat[lat.len() - 1]];
|
||||
lon_range = [lon[0], lon[lon.len() - 1]];
|
||||
radarg_plugin_interface::DataShape::Matrix
|
||||
},
|
||||
_ => {
|
||||
let lat = b.info.dimension_values.get(1).unwrap();
|
||||
let lon = b.info.dimension_values.get(2).unwrap();
|
||||
lat_range = [lat[0], lat[lat.len() - 1]];
|
||||
lon_range = [lon[0], lon[lon.len() - 1]];
|
||||
radarg_plugin_interface::DataShape::Cube
|
||||
},
|
||||
};
|
||||
|
||||
let data_type = match b.info.value_name.as_str() {
|
||||
@ -115,6 +124,7 @@ impl Plugin for ETWSLoader {
|
||||
"HCA" => PluginResultType::HCA,
|
||||
"QPE" => PluginResultType::QPE,
|
||||
"QPF" => PluginResultType::QPF,
|
||||
"FR" => PluginResultType::DBZ,
|
||||
_ => PluginResultType::Unknown,
|
||||
};
|
||||
|
||||
@ -187,7 +197,7 @@ mod tests {
|
||||
|
||||
fn test() {
|
||||
let result =
|
||||
Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/BasicProductsX/20230624/20230624000800/ZJHZAA_20230624000800_VIL.dat.gz")
|
||||
Record::parse_from_path("/Volumes/data2/RadarArray/HangZhou/radarData/OutputProducts/RadarProducts/FuseDataX/20220727/ZJHZAA_20220727200000_FR.dat.gz")
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -15,4 +15,7 @@ icons = [
|
||||
"home-filled",
|
||||
"settings-filled",
|
||||
"save-filled",
|
||||
"timer-filled",
|
||||
"eye-filled",
|
||||
"eye-off-filled",
|
||||
]
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use abi_stable::{
|
||||
std_types::{ RBoxError, RVec},
|
||||
std_types::{RBoxError, RVec},
|
||||
StableAbi,
|
||||
};
|
||||
|
||||
@ -18,7 +18,6 @@ pub enum Error {
|
||||
Many(RVec<Error>),
|
||||
}
|
||||
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
@ -33,15 +32,15 @@ impl Display for Error {
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
Error::Custom(e) => Display::fmt(e, f),
|
||||
Error::Many(list) => {
|
||||
for e in list {
|
||||
writeln!(f, "{}", e)?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
_ => { Ok(())}
|
||||
}
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,9 +50,9 @@ pub enum VecResult {
|
||||
#[derive(StableAbi, Clone, Copy, Debug)]
|
||||
#[sabi(impl_InterfaceType(Sync, Send, Debug))]
|
||||
pub enum DataShape {
|
||||
Scalar,
|
||||
Vector,
|
||||
Matrix,
|
||||
Cube,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
|
||||
38
src/actions.rs
Normal file
38
src/actions.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use gtk::gio::SimpleAction;
|
||||
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt};
|
||||
use relm4::actions::{AccelsPlus, ActionablePlus, RelmAction, RelmActionGroup};
|
||||
use relm4::{ComponentParts, ComponentSender, RelmApp, RelmWidgetExt, SimpleComponent};
|
||||
|
||||
use crate::components::app::{AppModel, AppMsg, LayerMsg};
|
||||
use crate::widgets::{AssoElement, Layer};
|
||||
|
||||
relm4::new_action_group!(pub LayerActionGroup, "layer");
|
||||
relm4::new_stateless_action!(pub AddLayerAction, LayerActionGroup, "add");
|
||||
relm4::new_stateless_action!(pub RemoveLayerAction, LayerActionGroup, "remove");
|
||||
relm4::new_stateless_action!(pub MoveLayerAction, LayerActionGroup, "move");
|
||||
|
||||
pub fn register_layer_actions<W: AsRef<gtk::Widget>>(widget: W, sender: ComponentSender<AppModel>) {
|
||||
let add_action: RelmAction<AddLayerAction> = {
|
||||
let sender = sender.clone();
|
||||
RelmAction::new_stateless(move |_| {
|
||||
sender.input(AppMsg::LayerManager(LayerMsg::Add(Layer::new(
|
||||
true,
|
||||
"Test".to_string(),
|
||||
AssoElement::Test,
|
||||
))))
|
||||
})
|
||||
};
|
||||
|
||||
let remove_action: RelmAction<RemoveLayerAction> = {
|
||||
let sender = sender.clone();
|
||||
RelmAction::new_stateless(move |_| sender.input(AppMsg::LayerManager(LayerMsg::Remove(0))))
|
||||
};
|
||||
|
||||
let mut group: RelmActionGroup<LayerActionGroup> = RelmActionGroup::new();
|
||||
group.add_action(add_action);
|
||||
group.add_action(remove_action);
|
||||
group.register_for_widget(widget)
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
use super::sidebar::SideBarOutputMsg;
|
||||
use super::{
|
||||
control_panel::{ControlPanelInputMsg, ControlPanelModel},
|
||||
messages::{MonitorInputMsg, MonitorOutputMsg},
|
||||
@ -5,12 +6,14 @@ use super::{
|
||||
setting::SettingModel,
|
||||
ControlPanelOutputMsg, TimelineMsg,
|
||||
};
|
||||
use crate::data_utils::plugin_result_impl;
|
||||
use crate::pipeline::element::{
|
||||
Element, InstantElement, InstantElementDrawerType, TimeSeriesElement,
|
||||
};
|
||||
use crate::components::sidebar::{SideBarInputMsg, SideBarModel};
|
||||
use crate::pipeline::{GridElementImpl, OffscreenRenderer};
|
||||
use crate::widgets::AssoElement;
|
||||
use crate::predefined::widgets::ColorBar;
|
||||
use crate::widgets::{AssoElement, DynamicCol};
|
||||
use crate::{
|
||||
actions::register_layer_actions,
|
||||
pipeline::element::{Element, InstantElement, InstantElementDrawerType, TimeSeriesElement},
|
||||
};
|
||||
use crate::{
|
||||
coords::{
|
||||
cms::CMS,
|
||||
@ -24,6 +27,7 @@ use crate::{
|
||||
widgets::render::Layer,
|
||||
CONFIG, PLUGIN_MANAGER,
|
||||
};
|
||||
use crate::{data_utils::plugin_result_impl, pipeline::element::DataTarget};
|
||||
use abi_stable::std_types::RStr;
|
||||
use adw::prelude::*;
|
||||
use chrono::{prelude::*, Duration};
|
||||
@ -59,15 +63,24 @@ pub static FILE_PATH_ROOT: Lazy<Mutex<PathBuf>> = Lazy::new(|| Mutex::new(PathBu
|
||||
|
||||
pub type ElementKey = String;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum LayerMsg {
|
||||
Add(Layer),
|
||||
Remove(usize),
|
||||
SwitchToTime(usize),
|
||||
Select(Vec<usize>),
|
||||
RemoveSelected,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppMsg {
|
||||
CloseRequest,
|
||||
Close,
|
||||
OpenDialog,
|
||||
SwitchToTime(usize),
|
||||
LayerManager(LayerMsg),
|
||||
Layer,
|
||||
NewElement(Element),
|
||||
DeleteElement(ElementKey),
|
||||
NewLayer(Layer),
|
||||
}
|
||||
pub type Buffer = Rc<RefCell<HashMap<String, BTreeMap<DateTime<Utc>, Option<RenderResult>>>>>;
|
||||
type RcDispatcher = Rc<Dispatcher>;
|
||||
@ -85,6 +98,9 @@ pub struct AppModel {
|
||||
#[do_not_track]
|
||||
render: Controller<MonitorModel>,
|
||||
#[do_not_track]
|
||||
sidebar: Controller<SideBarModel>,
|
||||
selected_layer: Vec<usize>,
|
||||
#[do_not_track]
|
||||
layers: Rc<RefCell<Vec<Layer>>>,
|
||||
#[do_not_track]
|
||||
elements: Vec<Arc<Mutex<TimeSeriesElement>>>,
|
||||
@ -158,7 +174,21 @@ impl Component for AppModel {
|
||||
adw::ToastOverlay{
|
||||
set_hexpand: true,
|
||||
set_vexpand: true,
|
||||
model.render.widget(),
|
||||
DynamicCol {
|
||||
set_end_width: 300,
|
||||
set_hexpand: true,
|
||||
set_vexpand: true,
|
||||
#[wrap(Some)]
|
||||
#[name="paned"]
|
||||
set_child_paned = >k::Paned{
|
||||
#[local_ref]
|
||||
#[wrap(Some)]
|
||||
set_start_child=render->gtk::Frame{},
|
||||
#[local_ref]
|
||||
#[wrap(Some)]
|
||||
set_end_child=sidebar->gtk::Box{},
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
home_stack_page = view_stack.add_titled(&home_page, Some("home"), "Home") -> adw::ViewStackPage{
|
||||
@ -192,61 +222,83 @@ impl Component for AppModel {
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let layers = Rc::new(RefCell::new(Vec::with_capacity(20)));
|
||||
let layers = Rc::new(RefCell::new(vec![
|
||||
Layer::new(true, "Layer 1".to_string(), AssoElement::Test),
|
||||
Layer::new(true, "Layer 2".to_string(), AssoElement::Test),
|
||||
]));
|
||||
let control = ControlPanelModel::builder().launch(layers.clone()).forward(
|
||||
sender.input_sender(),
|
||||
|msg| match msg {
|
||||
ControlPanelOutputMsg::OpenFile((key, time)) => AppMsg::Close,
|
||||
},
|
||||
);
|
||||
|
||||
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,
|
||||
MonitorOutputMsg::LayerSwitchToTime(idx) => AppMsg::SwitchToTime(idx),
|
||||
_ => AppMsg::Close,
|
||||
});
|
||||
|
||||
let setting = SettingModel::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |a| AppMsg::Close);
|
||||
|
||||
let mut dispatcher = Rc::new(Dispatcher::new(5, 5, chrono::Duration::minutes(1)));
|
||||
let cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
|
||||
let dialog_dispatcher = dispatcher.clone();
|
||||
let dialog_render_sender = render.sender().clone();
|
||||
let dialog = 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);
|
||||
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 element_impl = plugin_result_impl(&data);
|
||||
let mut renderer = OffscreenRenderer::new(3000, 3000).unwrap();
|
||||
let mut canvas = renderer.create_canvas();
|
||||
let mut dialog_cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
|
||||
let mut data_target = element_impl.render(&data, &mut canvas, &mut dialog_cms);
|
||||
data_target.data = Some(Arc::new(data) as Arc<dyn Any + Send + Sync + 'static>);
|
||||
|
||||
let element = Element::create_instant(
|
||||
InstantElementDrawerType::Prepared((data_target, Arc::new(element_impl))),
|
||||
dialog_dispatcher.clone(),
|
||||
"ET".to_string(),
|
||||
)
|
||||
.get_instance();
|
||||
let layer =
|
||||
Layer::new(true, "New Layer".to_string(), AssoElement::Instant(element));
|
||||
dialog_render_sender.emit(MonitorInputMsg::AddMetaItem(meta.to_map()));
|
||||
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
|
||||
lon_start, lon_end, lat_start, lat_end,
|
||||
));
|
||||
AppMsg::NewLayer(layer)
|
||||
}
|
||||
_ => AppMsg::Close,
|
||||
});
|
||||
let dialog = {
|
||||
let dialog_dispatcher = dispatcher.clone();
|
||||
let dialog_sidebar_sender = sidebar.sender().clone();
|
||||
let dialog_render_sender = render.sender().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);
|
||||
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 element_impl = plugin_result_impl(&data);
|
||||
let mut renderer = OffscreenRenderer::new(3000, 3000).unwrap();
|
||||
let mut canvas = renderer.create_canvas();
|
||||
let mut dialog_cms = CMS::new(Mercator::default().into(), (3000.0, 3000.0));
|
||||
let data_target = element_impl.render(&data, &mut canvas, &mut dialog_cms);
|
||||
let data_target = DataTarget::new(Some(data), data_target);
|
||||
let element = Element::create_instant(
|
||||
InstantElementDrawerType::Prepared((data_target, element_impl)),
|
||||
dialog_dispatcher.clone(),
|
||||
"ET".to_string(),
|
||||
)
|
||||
.get_instance();
|
||||
let layer = Layer::new(
|
||||
true,
|
||||
"New Layer".to_string(),
|
||||
AssoElement::Instant(element),
|
||||
);
|
||||
dialog_sidebar_sender.emit(SideBarInputMsg::AddMetaItems(meta.to_map()));
|
||||
dialog_render_sender.emit(MonitorInputMsg::SetRenderRange(
|
||||
lon_start, lon_end, lat_start, lat_end,
|
||||
));
|
||||
|
||||
AppMsg::LayerManager(LayerMsg::Add(layer))
|
||||
}
|
||||
_ => AppMsg::Close,
|
||||
})
|
||||
};
|
||||
|
||||
let buffer: Buffer = Rc::new(RefCell::new(HashMap::new()));
|
||||
let model = AppModel {
|
||||
@ -255,15 +307,21 @@ impl Component for AppModel {
|
||||
waiting_for: None,
|
||||
elements: Vec::with_capacity(20),
|
||||
open_dialog: dialog,
|
||||
selected_layer: vec![],
|
||||
sidebar,
|
||||
control,
|
||||
render,
|
||||
layers,
|
||||
setting,
|
||||
tracker: 0,
|
||||
};
|
||||
|
||||
let render = model.render.widget();
|
||||
let sidebar = model.sidebar.widget();
|
||||
let widgets = view_output!();
|
||||
let mut group = RelmActionGroup::<FileActionGroup>::new();
|
||||
relm4::main_application().set_accelerators_for_action::<OpenAction>(&["<primary>O"]);
|
||||
register_layer_actions(&widgets.main_window, sender.clone());
|
||||
let action: RelmAction<OpenAction> = {
|
||||
RelmAction::new_stateless(move |_| {
|
||||
sender.input(AppMsg::OpenDialog);
|
||||
@ -271,7 +329,6 @@ impl Component for AppModel {
|
||||
};
|
||||
group.add_action(action);
|
||||
group.register_for_widget(&widgets.main_window);
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
@ -284,31 +341,56 @@ impl Component for AppModel {
|
||||
) {
|
||||
self.reset();
|
||||
match msg {
|
||||
AppMsg::NewLayer(layer) => {
|
||||
(*self.layers).borrow_mut().push(layer);
|
||||
self.render.sender().send(MonitorInputMsg::RefreshLayerList);
|
||||
}
|
||||
AppMsg::LayerManager(msg) => match msg {
|
||||
LayerMsg::Add(layer) => {
|
||||
(*self.layers).borrow_mut().push(layer);
|
||||
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
|
||||
}
|
||||
LayerMsg::SwitchToTime(idx) => {
|
||||
let mut layer = (*self.layers).borrow_mut();
|
||||
let switched_layer = layer.get_mut(idx).unwrap();
|
||||
let asso_element = switched_layer.pop_associated_element();
|
||||
|
||||
if let AssoElement::Instant(e) = asso_element {
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let cms = self.cms.clone();
|
||||
let (mut series, start_time) = e.to_time_series(dispatcher, cms);
|
||||
switched_layer.set_time(start_time);
|
||||
series.register(start_time).unwrap();
|
||||
let element = Arc::new(Mutex::new(series));
|
||||
switched_layer
|
||||
.set_associated_element(AssoElement::TimeSeries(element.clone()));
|
||||
self.elements.push(element);
|
||||
}
|
||||
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
|
||||
}
|
||||
|
||||
LayerMsg::Select(idx) => {
|
||||
self.set_selected_layer(idx);
|
||||
}
|
||||
|
||||
LayerMsg::RemoveSelected => {
|
||||
let mut layer = (*self.layers).borrow_mut();
|
||||
let selected = self.selected_layer.clone();
|
||||
for idx in selected {
|
||||
layer.remove(idx);
|
||||
}
|
||||
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
|
||||
}
|
||||
|
||||
LayerMsg::Remove(idx) => {
|
||||
let mut layers = (*self.layers).borrow_mut();
|
||||
let mut layer = layers.remove(idx);
|
||||
if let AssoElement::TimeSeries(e) = layer.pop_associated_element() {
|
||||
let size = Arc::strong_count(&e);
|
||||
}
|
||||
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
|
||||
}
|
||||
},
|
||||
AppMsg::CloseRequest => {
|
||||
relm4::main_application().quit();
|
||||
}
|
||||
AppMsg::Close => {}
|
||||
AppMsg::SwitchToTime(idx) => {
|
||||
let mut layer = (*self.layers).borrow_mut();
|
||||
let switched_layer = layer.get_mut(idx).unwrap();
|
||||
let asso_element = switched_layer.pop_associated_element();
|
||||
|
||||
if let AssoElement::Instant(e) = asso_element {
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let cms = self.cms.clone();
|
||||
let (mut series, start_time) = e.to_time_series(dispatcher, cms);
|
||||
switched_layer.set_time(start_time);
|
||||
series.register(start_time).unwrap();
|
||||
let element = Arc::new(Mutex::new(series));
|
||||
switched_layer.set_associated_element(AssoElement::TimeSeries(element.clone()));
|
||||
self.elements.push(element);
|
||||
}
|
||||
self.render.sender().send(MonitorInputMsg::RefreshLayerList);
|
||||
}
|
||||
AppMsg::OpenDialog => {
|
||||
self.open_dialog.emit(OpenDialogMsg::Open);
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use super::messages::*;
|
||||
use super::thumbnail::{ImgItem, TypedListView};
|
||||
use crate::data::{CoordType, Radar2d, RadarData2d};
|
||||
use crate::plugin_system::init_plugin;
|
||||
use crate::widgets::render::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::widgets::render::Layer;
|
||||
use crate::widgets::timeline::{Selection, TimeLine};
|
||||
use abi_stable::std_types::RStr;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
pub mod app;
|
||||
mod control_panel;
|
||||
mod monitor;
|
||||
pub mod monitor;
|
||||
mod setting;
|
||||
|
||||
pub mod sidebar;
|
||||
|
||||
pub use control_panel::*;
|
||||
pub use monitor::*;
|
||||
|
||||
@ -1,20 +1,12 @@
|
||||
use std::{collections::HashMap, fmt::Debug};
|
||||
use std::fmt::Debug;
|
||||
|
||||
use crate::{
|
||||
components::app::ElementKey,
|
||||
pipeline::element::ElementID,
|
||||
widgets::{render::Layer, widget::Widget},
|
||||
};
|
||||
use crate::components::monitor::widget::Widget;
|
||||
|
||||
pub enum MonitorInputMsg {
|
||||
RefreshRender,
|
||||
AddWidget(Box<dyn Widget>),
|
||||
RemoveWidget,
|
||||
AddMetaItem(HashMap<String, String>),
|
||||
ClearMetaItems,
|
||||
UpdateMetaItem(HashMap<String, String>),
|
||||
RefreshTiles,
|
||||
RefreshLayerList,
|
||||
SetRenderRange(f64, f64, f64, f64),
|
||||
ChangeZoom(f64),
|
||||
None,
|
||||
@ -29,23 +21,15 @@ impl Debug for MonitorInputMsg {
|
||||
MonitorInputMsg::SetRenderRange(_, _, _, _) => {
|
||||
write!(f, "MonitorInputMsg::SetRenderRange")
|
||||
}
|
||||
MonitorInputMsg::RefreshLayerList => write!(f, "MonitorInputMsg::RefreshLayerList"),
|
||||
MonitorInputMsg::None => write!(f, "MonitorInputMsg::None"),
|
||||
MonitorInputMsg::AddWidget(_) => write!(f, "MonitorInputMsg::AddWidget"),
|
||||
MonitorInputMsg::RemoveWidget => write!(f, "MonitorInputMsg::RemoveWidget"),
|
||||
MonitorInputMsg::AddMetaItem(_) => write!(f, "MonitorInputMsg::RemoveWidget"),
|
||||
MonitorInputMsg::ClearMetaItems => write!(f, "MonitorInputMsg::ClearMetaItems"),
|
||||
MonitorInputMsg::UpdateMetaItem(_) => write!(f, "MonitorInputMsg::UpdateMetaItem"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MonitorOutputMsg {
|
||||
LayerAdded(usize),
|
||||
LayerRemoved(usize),
|
||||
LayerUpdated(usize),
|
||||
LayerSwitchToTime(usize),
|
||||
LayerRenderFinished,
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,6 @@
|
||||
pub mod messages;
|
||||
pub mod monitor;
|
||||
pub mod sidebar;
|
||||
pub mod widget;
|
||||
|
||||
pub use monitor::*;
|
||||
pub use widget::*;
|
||||
|
||||
@ -1,10 +1,9 @@
|
||||
use super::messages::{MonitorInputMsg, MonitorOutputMsg};
|
||||
use crate::components::monitor::widget::{Widget, WidgetType};
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::pipeline::offscreen_renderer::OffscreenRenderer;
|
||||
use crate::widgets::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::widgets::predefined::widgets::ColorBar;
|
||||
use crate::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::widgets::render::RenderConfig;
|
||||
use crate::widgets::widget::{Widget, WidgetType};
|
||||
use crate::widgets::WidgetFrame;
|
||||
use crate::{
|
||||
coords::{proj::Mercator, Mapper},
|
||||
@ -19,7 +18,7 @@ use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tracing::*;
|
||||
|
||||
use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg};
|
||||
// use super::sidebar::{sidebar::SideBarModel, SideBarInputMsg, SideBarOutputMsg};
|
||||
use crate::coords::Range;
|
||||
use crate::map_tile::MapTile;
|
||||
use crate::map_tile_utils::lat_lon_to_zoom;
|
||||
@ -54,8 +53,6 @@ pub struct MonitorModel {
|
||||
widgets: Vec<WidgetFrame>,
|
||||
#[no_eq]
|
||||
layers: Rc<RefCell<Vec<Layer>>>,
|
||||
#[no_eq]
|
||||
sidebar: Controller<SideBarModel>,
|
||||
}
|
||||
|
||||
pub struct MonitorWidgets {
|
||||
@ -71,71 +68,50 @@ impl Component for MonitorModel {
|
||||
|
||||
view! {
|
||||
#[root]
|
||||
adw::BreakpointBin {
|
||||
set_hexpand: true,
|
||||
set_vexpand: true,
|
||||
set_height_request: 500,
|
||||
set_width_request: 700,
|
||||
#[wrap(Some)]
|
||||
#[name="test"]
|
||||
set_child = &DynamicCol{
|
||||
set_end_width: 300,
|
||||
set_hexpand: true,
|
||||
set_vexpand: true,
|
||||
#[wrap(Some)]
|
||||
#[name="paned"]
|
||||
set_child_paned=>k::Paned{
|
||||
gtk::Frame{
|
||||
add_css_class: "rb",
|
||||
set_margin_all: 5,
|
||||
#[name="widget_layer"]
|
||||
gtk::Overlay{
|
||||
#[name = "renderer"]
|
||||
#[wrap(Some)]
|
||||
#[name="render"]
|
||||
set_start_child=>k::Frame{
|
||||
add_css_class: "rb",
|
||||
set_margin_all: 5,
|
||||
#[name="widget_layer"]
|
||||
gtk::Overlay{
|
||||
#[name = "renderer"]
|
||||
#[wrap(Some)]
|
||||
set_child = &Render{
|
||||
#[track = "model.changed(MonitorModel::render_cfg())"]
|
||||
set_cfg: model.render_cfg,
|
||||
#[track = "model.changed(MonitorModel::render_range())"]
|
||||
set_view: model.render_range,
|
||||
set_tiles: model.map_tile_getter.clone(),
|
||||
connect_render_status_notify[sender] => move |r| {
|
||||
sender.output(MonitorOutputMsg::LayerRenderFinished);
|
||||
},
|
||||
connect_range_changing_notify[sender] => move |r| {
|
||||
sender.input(MonitorInputMsg::RefreshTiles);
|
||||
},
|
||||
connect_scale_notify[sender] => move |r| {
|
||||
let scale = r.scale();
|
||||
{
|
||||
let initial = r.scale_rate();
|
||||
let mut rate_start = initial_rate.lock().unwrap();
|
||||
if rate_start.is_none() {
|
||||
*rate_start = Some(scale);
|
||||
}
|
||||
}
|
||||
debouncer.call(scale);
|
||||
},
|
||||
set_interior_layers: model.layers.clone(),
|
||||
},
|
||||
add_overlay=>k::Button{
|
||||
set_label:"Add",
|
||||
set_margin_all:10,
|
||||
set_valign: gtk::Align::Start,
|
||||
set_halign: gtk::Align::End,
|
||||
},
|
||||
#[track = "model.changed(MonitorModel::new_layer())"]
|
||||
#[iterate]
|
||||
add_overlay: &model.widgets
|
||||
set_child = &Render{
|
||||
#[track = "model.changed(MonitorModel::render_cfg())"]
|
||||
set_cfg: model.render_cfg,
|
||||
#[track = "model.changed(MonitorModel::render_range())"]
|
||||
set_view: model.render_range,
|
||||
set_tiles: model.map_tile_getter.clone(),
|
||||
connect_render_status_notify[sender] => move |r| {
|
||||
sender.output(MonitorOutputMsg::LayerRenderFinished);
|
||||
},
|
||||
|
||||
connect_range_changing_notify[sender] => move |r| {
|
||||
sender.input(MonitorInputMsg::RefreshTiles);
|
||||
},
|
||||
connect_scale_notify[sender] => move |r| {
|
||||
let scale = r.scale();
|
||||
{
|
||||
let initial = r.scale_rate();
|
||||
let mut rate_start = initial_rate.lock().unwrap();
|
||||
if rate_start.is_none() {
|
||||
*rate_start = Some(scale);
|
||||
}
|
||||
}
|
||||
debouncer.call(scale);
|
||||
},
|
||||
set_interior_layers: model.layers.clone(),
|
||||
},
|
||||
#[wrap(Some)]
|
||||
set_end_child=model.sidebar.widget(),
|
||||
}
|
||||
add_overlay=>k::Button{
|
||||
set_label:"Add",
|
||||
set_margin_all:10,
|
||||
set_valign: gtk::Align::Start,
|
||||
set_halign: gtk::Align::End,
|
||||
},
|
||||
#[track = "model.changed(MonitorModel::new_layer())"]
|
||||
#[iterate]
|
||||
add_overlay: &model.widgets
|
||||
},
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_with_view(
|
||||
@ -150,25 +126,13 @@ impl Component for MonitorModel {
|
||||
MonitorInputMsg::RefreshRender => {
|
||||
widgets.renderer.queue_render();
|
||||
}
|
||||
MonitorInputMsg::RefreshLayerList => {
|
||||
self.sidebar.sender().send(SideBarInputMsg::RefreshList);
|
||||
sender.input(MonitorInputMsg::RefreshRender);
|
||||
}
|
||||
MonitorInputMsg::AddMetaItem(map) => {
|
||||
self.sidebar.emit(SideBarInputMsg::AddMetaItems(map))
|
||||
}
|
||||
MonitorInputMsg::SetRenderRange(lon_start, lon_end, lat_start, lat_end) => {
|
||||
let current_rate = widgets.renderer.scale_rate();
|
||||
self.set_render_range((lat_start, lat_end, lon_start, lon_end));
|
||||
let new_rate = widgets.renderer.scale_rate();
|
||||
let zoom: f64 = (current_rate / new_rate).log2();
|
||||
let zoom: f64 = (new_rate / current_rate).log2();
|
||||
sender.input(MonitorInputMsg::ChangeZoom(zoom));
|
||||
}
|
||||
MonitorInputMsg::ClearMetaItems => self.sidebar.emit(SideBarInputMsg::ClearMetaItems),
|
||||
MonitorInputMsg::UpdateMetaItem(map) => {
|
||||
self.sidebar.emit(SideBarInputMsg::ClearMetaItems);
|
||||
self.sidebar.emit(SideBarInputMsg::AddMetaItems(map))
|
||||
}
|
||||
MonitorInputMsg::RefreshTiles => {
|
||||
let ((x1, x2), (y1, y2)) = widgets.renderer.render_range();
|
||||
self.load_tile(&sender, ((y1 as f32, y2 as f32), (x1 as f32, x2 as f32)));
|
||||
@ -203,16 +167,16 @@ impl Component for MonitorModel {
|
||||
root: Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
let sidebar_sender = sender.clone();
|
||||
let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
||||
.launch(init.clone())
|
||||
.forward(sender.input_sender(), move |msg| match msg {
|
||||
SideBarOutputMsg::SwitchToTimeSeries(layer) => {
|
||||
sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
|
||||
MonitorInputMsg::None
|
||||
}
|
||||
_ => MonitorInputMsg::None,
|
||||
});
|
||||
// let sidebar_sender = sender.clone();
|
||||
// let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
||||
// .launch(init.clone())
|
||||
// .forward(sender.input_sender(), move |msg| match msg {
|
||||
// SideBarOutputMsg::SwitchToTimeSeries(layer) => {
|
||||
// sidebar_sender.output(MonitorOutputMsg::LayerSwitchToTime(layer));
|
||||
// MonitorInputMsg::None
|
||||
// }
|
||||
// _ => MonitorInputMsg::None,
|
||||
// });
|
||||
|
||||
let render_cfg = RenderConfig {
|
||||
padding: [0.0, 0.0, 0.0, 0.0],
|
||||
@ -231,7 +195,6 @@ impl Component for MonitorModel {
|
||||
layers: init,
|
||||
zoom: 4,
|
||||
map_tile_getter: Rc::new(MapTile::default()),
|
||||
sidebar,
|
||||
tracker: 0,
|
||||
};
|
||||
|
||||
|
||||
@ -11,8 +11,8 @@ pub enum WidgetType {
|
||||
}
|
||||
|
||||
pub trait Widget: 'static + Send + Sync {
|
||||
fn opengl_render(&self, canvas: &mut Canvas<OpenGl>, cms: CMS) {}
|
||||
fn cairo_render(&self, canvas: >k::cairo::Context, w: f32, h: f32) {}
|
||||
fn opengl_render(&self, layers: &Vec<Layer>, canvas: &mut Canvas<OpenGl>, cms: CMS) {}
|
||||
fn cairo_render(&self, layers: &Vec<Layer>, canvas: >k::cairo::Context, w: f32, h: f32) {}
|
||||
fn widget_type(&self) -> WidgetType;
|
||||
|
||||
fn size(&self) -> (f32, f32);
|
||||
@ -2,7 +2,8 @@ use super::dispatcher_list::PathItem;
|
||||
use crate::{
|
||||
config::PATH_FORMATS,
|
||||
data::{self, CoordType, Radar2d},
|
||||
widgets::render::{predefined::color_mapper::BoundaryNorm, Layer},
|
||||
predefined::color_mapper::BoundaryNorm,
|
||||
widgets::render::Layer,
|
||||
CONFIG, PLUGIN_MANAGER,
|
||||
};
|
||||
use adw::prelude::*;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
use crate::actions::*;
|
||||
use gtk::prelude::*;
|
||||
use relm4::{
|
||||
factory::FactoryView,
|
||||
gtk,
|
||||
prelude::{DynamicIndex, FactoryComponent},
|
||||
FactorySender,
|
||||
actions::traits::ActionablePlus, factory::FactoryView, gtk, prelude::*, FactorySender,
|
||||
};
|
||||
|
||||
use super::SideBarInputMsg;
|
||||
@ -40,10 +38,9 @@ impl FactoryComponent for BottomBarModel {
|
||||
|
||||
view! {
|
||||
#[root]
|
||||
gtk::Box{
|
||||
gtk::Button{
|
||||
set_icon_name=self.icon.as_str(),
|
||||
}
|
||||
gtk::Button{
|
||||
set_icon_name=self.icon.as_str(),
|
||||
ActionablePlus::set_stateless_action::<RemoveLayerAction>: &(),
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,7 +9,7 @@ use relm4::{
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(super) struct MyListItem {
|
||||
pub(in crate::components) struct MyListItem {
|
||||
tag: String,
|
||||
info: String,
|
||||
}
|
||||
@ -20,7 +20,7 @@ impl MyListItem {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct TagColumn;
|
||||
pub(in crate::components) struct TagColumn;
|
||||
|
||||
impl LabelColumn for TagColumn {
|
||||
type Item = MyListItem;
|
||||
@ -38,7 +38,7 @@ impl LabelColumn for TagColumn {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct InfoColumn;
|
||||
pub(in crate::components) struct InfoColumn;
|
||||
|
||||
impl RelmColumn for InfoColumn {
|
||||
type Root = gtk::Label;
|
||||
@ -1,3 +1,4 @@
|
||||
use crate::actions::*;
|
||||
use crate::widgets::AssoElement;
|
||||
use abi_stable::type_level::trait_marker::Hash;
|
||||
use chrono::{DateTime, Utc};
|
||||
@ -20,11 +21,7 @@ use relm4::{
|
||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||
|
||||
use crate::components::app::AppMsg;
|
||||
use crate::{
|
||||
chart::Chart,
|
||||
data::Npz,
|
||||
widgets::render::{predefined::color_mapper::BoundaryNorm, Layer},
|
||||
};
|
||||
use crate::{chart::Chart, predefined::color_mapper::BoundaryNorm, widgets::render::Layer};
|
||||
|
||||
use super::{
|
||||
bottom_bar::BottomBarModel,
|
||||
@ -35,7 +32,7 @@ pub struct SideBarModel {
|
||||
layers: Rc<RefCell<Vec<Layer>>>,
|
||||
selected_layer_idx: usize,
|
||||
counter: u8,
|
||||
list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection>,
|
||||
list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection>,
|
||||
bottom_bar_vec: FactoryVecDeque<BottomBarModel>,
|
||||
meta_list_view: TypedColumnView<MyListItem, gtk::NoSelection>,
|
||||
}
|
||||
@ -50,6 +47,7 @@ pub enum SideBarInputMsg {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum SideBarOutputMsg {
|
||||
SelectLayer(Vec<usize>),
|
||||
NewLayer(Layer),
|
||||
SwitchToTimeSeries(usize),
|
||||
}
|
||||
@ -104,7 +102,8 @@ impl SimpleComponent for SideBarModel {
|
||||
.build() -> gtk::ScrolledWindow{
|
||||
#[wrap(Some)]
|
||||
#[local_ref]
|
||||
set_child=my_view -> gtk::ListView{},
|
||||
set_child=my_view -> gtk::ListView{
|
||||
},
|
||||
set_margin_horizontal:5,
|
||||
set_margin_vertical:3
|
||||
},
|
||||
@ -132,8 +131,19 @@ impl SimpleComponent for SideBarModel {
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
// Initialize the ListView wrapper
|
||||
let mut list_view_wrapper: TypedListView<LayerItem, gtk::SingleSelection> =
|
||||
let mut list_view_wrapper: TypedListView<LayerItem, gtk::MultiSelection> =
|
||||
TypedListView::with_sorting();
|
||||
|
||||
list_view_wrapper.selection_model.connect_selection_changed(
|
||||
clone!(@strong sender => move |s,_, _| {
|
||||
let selection = s.selection();
|
||||
|
||||
let (iter, first) = gtk::BitsetIter::init_first(&selection).unwrap();
|
||||
let mut result = vec![first as usize];
|
||||
result.extend(iter.map(|v| v as usize));
|
||||
sender.output(SideBarOutputMsg::SelectLayer(result));
|
||||
}),
|
||||
);
|
||||
// let mut bottom_bar_vec = FactoryVecDeque::new(gtk::Box::default(), sender.input_sender());
|
||||
|
||||
let mut bottom_bar_vec =
|
||||
@ -270,11 +280,13 @@ impl LayerItem {
|
||||
}
|
||||
|
||||
struct Widgets {
|
||||
label: gtk::Label,
|
||||
label: gtk::EditableLabel,
|
||||
screen_shot: gtk::Image,
|
||||
status: gtk::Label,
|
||||
button: gtk::CheckButton,
|
||||
menu: gtk::PopoverMenu,
|
||||
status_icon: gtk::Image,
|
||||
status_label: gtk::Label,
|
||||
visible: gtk::Button,
|
||||
opacity: gtk::Label,
|
||||
menu: gtk::Popover,
|
||||
}
|
||||
|
||||
impl RelmListItem for LayerItem {
|
||||
@ -282,13 +294,22 @@ impl RelmListItem for LayerItem {
|
||||
type Widgets = Widgets;
|
||||
|
||||
fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) {
|
||||
let position = _item.position() as u8;
|
||||
|
||||
relm4::menu! {
|
||||
main_menu: {
|
||||
custom: "MyWidget",
|
||||
"Remove" => RemoveLayerAction,
|
||||
section!{
|
||||
"test" => RemoveLayerAction,
|
||||
"select" => AddLayerAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
relm4::view! {
|
||||
my_box = gtk::Box {
|
||||
set_valign: gtk::Align::Center,
|
||||
gtk::Frame{
|
||||
set_margin_end: 10,
|
||||
#[name = "screen_shot"]
|
||||
@ -296,31 +317,46 @@ impl RelmListItem for LayerItem {
|
||||
set_size_request: (65, 40),
|
||||
}
|
||||
},
|
||||
#[name = "label"]
|
||||
gtk::Label{
|
||||
set_halign: gtk::Align::Start,
|
||||
},
|
||||
#[name = "status"]
|
||||
gtk::Label{
|
||||
set_halign: gtk::Align::Start
|
||||
},
|
||||
gtk::Label{
|
||||
set_hexpand: true,
|
||||
},
|
||||
#[name = "button"]
|
||||
gtk::CheckButton{
|
||||
set_halign: gtk::Align::End,
|
||||
gtk::Grid{
|
||||
set_row_homogeneous: true,
|
||||
attach[0,0,1,1] = >k::Box{
|
||||
#[name="status_icon"]
|
||||
gtk::Image{
|
||||
inline_css: "-gtk-icon-transform: scale(0.8);",
|
||||
},
|
||||
#[name="status_label"]
|
||||
gtk::Label{
|
||||
add_css_class:"tooltip",
|
||||
set_halign: gtk::Align::Start,
|
||||
}
|
||||
},
|
||||
#[name="label"]
|
||||
attach[0,1,1,1] = >k::EditableLabel{
|
||||
set_hexpand: true,
|
||||
},
|
||||
#[name="opacity"]
|
||||
attach[0,2,1,1] = >k::Label{
|
||||
add_css_class:"tooltip",
|
||||
set_halign: gtk::Align::Start
|
||||
},
|
||||
},
|
||||
#[name="visible"]
|
||||
gtk::Button{
|
||||
set_vexpand: false,
|
||||
set_hexpand: false,
|
||||
},
|
||||
#[name = "menu"]
|
||||
gtk::PopoverMenu::from_model(Some(&main_menu)){}
|
||||
gtk::Popover{}
|
||||
}
|
||||
}
|
||||
|
||||
let widgets = Widgets {
|
||||
screen_shot,
|
||||
label,
|
||||
status,
|
||||
button,
|
||||
status_icon,
|
||||
status_label,
|
||||
opacity,
|
||||
visible,
|
||||
menu,
|
||||
};
|
||||
|
||||
@ -330,24 +366,28 @@ impl RelmListItem for LayerItem {
|
||||
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
|
||||
let Widgets {
|
||||
label,
|
||||
button,
|
||||
visible,
|
||||
screen_shot,
|
||||
status,
|
||||
status_label,
|
||||
status_icon,
|
||||
opacity,
|
||||
menu,
|
||||
} = widgets;
|
||||
|
||||
relm4::menu! {
|
||||
main_menu: {
|
||||
}
|
||||
}
|
||||
menu.set_menu_model(Some(&main_menu));
|
||||
|
||||
status.set_label(&match self.status {
|
||||
status_label.set_label(&match self.status {
|
||||
LayerStatus::BindToTime(t) => format!("Bind To Time: {}", t),
|
||||
LayerStatus::Instance => "Instance".to_string(),
|
||||
LayerStatus::BindToOtherLayer(idx) => format!("Bind To Layer: {}", idx),
|
||||
});
|
||||
|
||||
status_icon.set_icon_name(Some(match self.status {
|
||||
LayerStatus::BindToTime(_) => "timer-filled",
|
||||
LayerStatus::Instance => "timer-filled",
|
||||
LayerStatus::BindToOtherLayer(_) => "timer-filled",
|
||||
}));
|
||||
|
||||
opacity.set_markup(&format!("<b>Opacity:</b> 0.5"));
|
||||
|
||||
let gesture_click = gtk::GestureClick::new();
|
||||
gesture_click.set_button(gtk::gdk::BUTTON_SECONDARY);
|
||||
screen_shot.set_paintable(self.img.as_ref());
|
||||
@ -358,8 +398,10 @@ impl RelmListItem for LayerItem {
|
||||
menu.popup();
|
||||
}));
|
||||
|
||||
visible.set_icon_name("eye-filled");
|
||||
|
||||
_root.add_controller(gesture_click);
|
||||
label.set_label(&self.layer_name);
|
||||
button.set_active(self.visiable);
|
||||
label.set_text(&self.layer_name);
|
||||
// button.set_active(self.visiable);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
use std::ops::Range;
|
||||
use geo_types::LineString;
|
||||
use crate::coords::Mapper;
|
||||
use geo_types::LineString;
|
||||
use std::ops::Range;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CMS {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use num_traits::AsPrimitive;
|
||||
use num_traits::Num;
|
||||
pub mod cms;
|
||||
pub mod mapper;
|
||||
pub mod proj;
|
||||
pub mod wgs84;
|
||||
pub mod cms;
|
||||
|
||||
pub use mapper::Mapper;
|
||||
// pub use wgs84::LatLonCoord;
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
button {
|
||||
color: magenta;
|
||||
}
|
||||
564
src/data/mod.rs
564
src/data/mod.rs
@ -2,18 +2,27 @@ pub mod meta;
|
||||
use crate::errors::DataError;
|
||||
use async_trait::async_trait;
|
||||
pub use meta::MetaInfo;
|
||||
use ndarray::iter::Indices;
|
||||
use ndarray::{
|
||||
s, Array1, Array2, Array3, ArrayBase, DataMut, Dimension, Ix1, Ix2, OwnedRepr, RawDataClone,
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Dimension, Ix1, Ix2, Ix3,
|
||||
OwnedRepr, RawDataClone, Slice, ViewRepr,
|
||||
};
|
||||
use npyz::{npz::NpzArchive, Deserialize};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use std::io::{Cursor, Seek};
|
||||
use std::{self, f64::consts::PI, fmt::Debug, io::BufReader, path::Path};
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{self, AsyncReadExt};
|
||||
|
||||
pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>>;
|
||||
pub type Radar2d<T> = RadarData2d<T, OwnedRepr<T>, OwnedRepr<f64>, OwnedRepr<f64>>;
|
||||
pub type Radar3d<T> = RadarData3d<T, OwnedRepr<T>, OwnedRepr<f64>, OwnedRepr<f64>, OwnedRepr<f64>>;
|
||||
|
||||
pub type Radar2dRef<'a, T> = RadarData2d<T, ViewRepr<&'a T>, ViewRepr<&'a f64>, ViewRepr<&'a f64>>;
|
||||
pub type Radar3dRef<'a, T> =
|
||||
RadarData3d<T, ViewRepr<&'a T>, ViewRepr<&'a f64>, ViewRepr<&'a f64>, ViewRepr<&'a f64>>;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CoordType {
|
||||
Polar,
|
||||
LatLon,
|
||||
}
|
||||
pub trait MultiDimensionData<T>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
@ -24,248 +33,84 @@ where
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RadarData2d<T, Raw, X = f64, Y = f64, I = Ix1>
|
||||
pub struct RadarData2d<T, Raw, X, Y, I = Ix1>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
X: Num,
|
||||
Y: Num,
|
||||
Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
|
||||
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
{
|
||||
pub dim1: ArrayBase<OwnedRepr<X>, I>,
|
||||
pub dim2: ArrayBase<OwnedRepr<Y>, I>,
|
||||
pub dim1: ArrayBase<X, I>,
|
||||
pub dim2: ArrayBase<Y, I>,
|
||||
pub data: ArrayBase<Raw, Ix2>,
|
||||
pub fill_value: T,
|
||||
pub coord_type: CoordType,
|
||||
}
|
||||
|
||||
impl<T, Raw, X, Y, I> Debug for RadarData2d<T, Raw, X, Y, I>
|
||||
impl<T> Radar2d<T>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd + Debug,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
X: Num + Debug,
|
||||
Y: Num + Debug,
|
||||
I: Dimension,
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("RadarData2d")
|
||||
.field("dim1", &self.dim1)
|
||||
.field("dim2", &self.dim2)
|
||||
.field("data", &self.data)
|
||||
.field("fill_value", &self.fill_value)
|
||||
.field("coord_type", &self.coord_type)
|
||||
.finish()
|
||||
pub fn as_ref<'a>(&'a self) -> Radar2dRef<'a, T> {
|
||||
RadarData2d {
|
||||
dim1: self.dim1.view(),
|
||||
dim2: self.dim2.view(),
|
||||
data: self.data.view(),
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
|
||||
pub fn load(path: impl AsRef<Path>, meth: impl DataLoader<T, Self>) -> Result<Self, DataError> {
|
||||
Ok(meth.load(path)?)
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct RadarData3d<T, Raw, X, Y, Z, I = Ix1>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
|
||||
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
{
|
||||
pub dim1: ArrayBase<X, I>,
|
||||
pub dim2: ArrayBase<Y, I>,
|
||||
pub dim3: ArrayBase<Z, I>,
|
||||
pub data: ArrayBase<Raw, Ix3>,
|
||||
pub fill_value: T,
|
||||
pub coord_type: CoordType,
|
||||
}
|
||||
|
||||
pub struct RadarData3d<T, X = f64, Y = f64, Z = f64>
|
||||
impl<T, Raw, X, Y, Z> RadarData3d<T, Raw, X, Y, Z>
|
||||
where
|
||||
T: Num,
|
||||
X: Num,
|
||||
Y: Num,
|
||||
Z: Num,
|
||||
T: Num + Clone + PartialEq + PartialOrd,
|
||||
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Raw: ndarray::Data<Elem = T> + ndarray::RawDataClone,
|
||||
{
|
||||
pub dim1: Array1<X>,
|
||||
pub dim2: Array1<Y>,
|
||||
pub dim3: Array1<Z>,
|
||||
pub data: Array3<T>,
|
||||
}
|
||||
|
||||
impl<T, Raw> RadarData2d<T, Raw>
|
||||
where
|
||||
T: Num + AsPrimitive<f64> + FromPrimitive + Clone + Debug + PartialOrd + PartialEq,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
pub fn downsample(&self, output_shape: (usize, usize), meth: DownSampleMeth) -> Radar2d<T> {
|
||||
pub fn index_axis(&self, axis: Axis, slice: usize) -> Radar2dRef<T> {
|
||||
let shape = self.data.shape();
|
||||
assert!(shape[0] > output_shape.0 && shape[1] > output_shape.1);
|
||||
|
||||
if shape[0] == output_shape.0 && shape[1] == output_shape.1 {
|
||||
return Radar2d {
|
||||
dim1: self.dim1.to_owned(),
|
||||
dim2: self.dim2.to_owned(),
|
||||
data: self.data.to_owned(),
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
let scale_x = shape[1] as f64 / output_shape.1 as f64;
|
||||
let scale_y = shape[0] as f64 / output_shape.0 as f64;
|
||||
|
||||
let mut output: Array2<T> = Array2::zeros(output_shape);
|
||||
|
||||
let dim1 = Array1::linspace(
|
||||
*self.dim1.first().unwrap(),
|
||||
*self.dim1.last().unwrap(),
|
||||
output_shape.1,
|
||||
);
|
||||
|
||||
let dim2 = Array1::linspace(
|
||||
*self.dim2.first().unwrap(),
|
||||
*self.dim2.last().unwrap(),
|
||||
output_shape.0,
|
||||
);
|
||||
|
||||
output.iter_mut().enumerate().for_each(|(s, vv)| {
|
||||
let ri = s / output_shape.1;
|
||||
let ci = s % output_shape.1;
|
||||
let src_yf0 = scale_y * ri as f64;
|
||||
let src_yf1 = src_yf0 + scale_y;
|
||||
let src_y0 = src_yf0.floor() as usize;
|
||||
let src_y1 = src_yf1.floor() as usize;
|
||||
let src_xf0 = scale_x * ci as f64;
|
||||
let src_xf1 = src_xf0 + scale_x;
|
||||
let src_x0 = src_xf0.floor() as usize;
|
||||
let src_x1 = src_xf1.floor() as usize;
|
||||
|
||||
match meth {
|
||||
DownSampleMeth::MEAN => {}
|
||||
DownSampleMeth::VAR | DownSampleMeth::STD => self.var(
|
||||
src_yf0, src_yf1, src_xf0, src_xf1, src_y0, src_y1, src_x0, src_x1, vv,
|
||||
),
|
||||
}
|
||||
});
|
||||
|
||||
if let DownSampleMeth::STD = meth {
|
||||
output = output.mapv(|x| T::from_f64(f64::sqrt(x.as_())).unwrap());
|
||||
}
|
||||
|
||||
return Radar2d {
|
||||
println!("shape: {:?}", shape);
|
||||
let dim1 = self.dim1.view();
|
||||
let dim2 = self.dim2.view();
|
||||
let view = self.data.index_axis(axis, slice);
|
||||
RadarData2d {
|
||||
dim1,
|
||||
dim2,
|
||||
data: view,
|
||||
fill_value: self.fill_value.clone(),
|
||||
data: output,
|
||||
coord_type: self.coord_type.clone(),
|
||||
};
|
||||
}
|
||||
|
||||
fn var(
|
||||
&self,
|
||||
src_yf0: f64,
|
||||
src_yf1: f64,
|
||||
src_xf0: f64,
|
||||
src_xf1: f64,
|
||||
src_y0: usize,
|
||||
mut src_y1: usize,
|
||||
src_x0: usize,
|
||||
mut src_x1: usize,
|
||||
row: &mut T,
|
||||
) {
|
||||
let wy0 = 1f64 - (src_yf0 - src_y0 as f64);
|
||||
let mut wy1 = src_yf1 - src_y1 as f64;
|
||||
let wx0 = 1f64 - (src_xf0 - src_x0 as f64);
|
||||
let mut wx1 = src_xf1 - src_x1 as f64;
|
||||
|
||||
if wy1 < f64::EPSILON {
|
||||
wy1 = 1f64;
|
||||
if src_y1 > src_y0 {
|
||||
src_y1 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if wx1 < f64::EPSILON {
|
||||
wx1 = 1f64;
|
||||
if src_x1 > src_x0 {
|
||||
src_x1 -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
let mut v_sum: f64 = 0.0;
|
||||
let mut w_sum = 0f64;
|
||||
let mut wv_sum = 0f64;
|
||||
let mut wvv_sum = 0f64;
|
||||
|
||||
for src_y in src_y0..=src_y1 {
|
||||
let wy = if src_y == src_y0 {
|
||||
wy0
|
||||
} else {
|
||||
if src_y == src_y1 {
|
||||
wy1
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
};
|
||||
for src_x in src_x0..=src_x1 {
|
||||
let wx = if src_x == src_x0 {
|
||||
wx0
|
||||
} else {
|
||||
if src_x == src_x1 {
|
||||
wx1
|
||||
} else {
|
||||
1.0
|
||||
}
|
||||
};
|
||||
|
||||
let v = self.data[[src_y, src_x]].as_();
|
||||
let w = wx * wy;
|
||||
|
||||
v_sum += v;
|
||||
w_sum += w;
|
||||
wv_sum += w * v;
|
||||
wvv_sum += w * v * v;
|
||||
}
|
||||
|
||||
if w_sum < f64::EPSILON {
|
||||
return;
|
||||
}
|
||||
let v = ((wvv_sum * w_sum - wv_sum * wv_sum) / w_sum / w_sum).clamp(-125f64, 124f64);
|
||||
*row = T::from_f64(v).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn split(&self) -> [RadarData2d<T, ndarray::ViewRepr<&T>>; 4] {
|
||||
let middle_dim1 = self.dim1.len() / 2;
|
||||
let middle_dim2 = self.dim2.len() / 2;
|
||||
|
||||
let lt = self.data.slice(s![..middle_dim1, ..middle_dim2]);
|
||||
let rt = self.data.slice(s![middle_dim1.., ..middle_dim2]);
|
||||
let lb = self.data.slice(s![..middle_dim1, middle_dim2..]);
|
||||
let rb = self.data.slice(s![middle_dim1.., middle_dim2..]);
|
||||
|
||||
return [
|
||||
RadarData2d {
|
||||
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
||||
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
||||
data: lt,
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type,
|
||||
},
|
||||
RadarData2d {
|
||||
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
||||
dim2: self.dim2.slice(s![..middle_dim2]).to_owned(),
|
||||
data: rt,
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type,
|
||||
},
|
||||
RadarData2d {
|
||||
dim1: self.dim1.slice(s![..middle_dim1]).to_owned(),
|
||||
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
||||
data: lb,
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type,
|
||||
},
|
||||
RadarData2d {
|
||||
dim1: self.dim1.slice(s![middle_dim1..]).to_owned(),
|
||||
dim2: self.dim2.slice(s![middle_dim2..]).to_owned(),
|
||||
data: rb,
|
||||
fill_value: self.fill_value.clone(),
|
||||
coord_type: self.coord_type,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, Raw> MultiDimensionData<T> for RadarData2d<T, Raw>
|
||||
impl<T, Raw, X, Y, Z> MultiDimensionData<T> for RadarData3d<T, Raw, X, Y, Z>
|
||||
where
|
||||
T: Num + Clone,
|
||||
T: PartialEq + PartialOrd,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + RawDataClone + DataMut,
|
||||
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Z: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Raw: ndarray::Data<Elem = T> + RawDataClone + DataMut,
|
||||
{
|
||||
fn map_by_fn<F>(&mut self, f: F)
|
||||
where
|
||||
@ -275,285 +120,18 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait DataLoader<V, T>
|
||||
impl<T, Raw, X, Y> MultiDimensionData<T> for RadarData2d<T, Raw, X, Y>
|
||||
where
|
||||
V: Num + Clone + PartialEq + PartialOrd,
|
||||
T: MultiDimensionData<V>,
|
||||
{
|
||||
fn load<P: AsRef<Path>>(&self, path: P) -> Result<T, DataError>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait AsyncDataLoader<V, T>
|
||||
where
|
||||
V: Num + Clone + PartialEq + PartialOrd,
|
||||
T: MultiDimensionData<V> + Send + Sync,
|
||||
{
|
||||
async fn load<P: AsRef<Path> + Send>(&self, path: P) -> Result<T, DataError>;
|
||||
}
|
||||
|
||||
pub struct Npz;
|
||||
impl Npz {
|
||||
#[inline]
|
||||
fn load_1d<T: Num + Deserialize, R: std::io::Read + Seek>(
|
||||
&self,
|
||||
data: &mut NpzArchive<R>,
|
||||
name: &str,
|
||||
) -> Result<Array1<T>, DataError> {
|
||||
// let mut data = npyz::npz::NpzArchive::open(path)?;
|
||||
let a = data.by_name(name)?.unwrap();
|
||||
let b: Vec<T> = a.into_vec().unwrap();
|
||||
|
||||
Ok(Array1::from_shape_vec(b.len(), b).unwrap())
|
||||
}
|
||||
#[inline]
|
||||
fn load_2d<T: Num + Deserialize, R: std::io::Read + Seek>(
|
||||
&self,
|
||||
// path: &Path,
|
||||
data: &mut NpzArchive<R>,
|
||||
name: &str,
|
||||
) -> Result<Array2<T>, DataError> {
|
||||
// let mut data = npyz::npz::NpzArchive::open(path)?;
|
||||
let a = data.by_name(name)?.unwrap();
|
||||
|
||||
let shape = a.shape().to_vec();
|
||||
let b: Vec<T> = a.into_vec().unwrap();
|
||||
|
||||
Ok(Array2::from_shape_vec((shape[0] as usize, shape[1] as usize), b).unwrap())
|
||||
}
|
||||
fn load_3d(&self, data: &mut NpzArchive<BufReader<std::fs::File>>) {}
|
||||
}
|
||||
|
||||
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||
where
|
||||
T: Num + Clone + Deserialize + FromPrimitive,
|
||||
T: Num + Clone,
|
||||
T: PartialEq + PartialOrd,
|
||||
X: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Y: ndarray::Data<Elem = f64> + ndarray::RawDataClone,
|
||||
Raw: ndarray::Data<Elem = T> + RawDataClone + DataMut,
|
||||
{
|
||||
fn load<P: AsRef<Path>>(&self, path: P) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
||||
let mut data: NpzArchive<BufReader<std::fs::File>> = npyz::npz::NpzArchive::open(path)?;
|
||||
let dim1 = self.load_1d::<f64, BufReader<std::fs::File>>(&mut data, "lon")?;
|
||||
let dim2 = self.load_1d::<f64, BufReader<std::fs::File>>(&mut data, "lat")?;
|
||||
let value = self.load_2d::<T, BufReader<std::fs::File>>(&mut data, "value")?;
|
||||
|
||||
Ok(RadarData2d {
|
||||
dim1: dim1,
|
||||
dim2: dim2,
|
||||
fill_value: T::from_f64(-125.0).unwrap(),
|
||||
data: value,
|
||||
coord_type: CoordType::LatLon,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsyncDataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||
where
|
||||
T: Num + Clone + Deserialize + FromPrimitive,
|
||||
T: PartialEq + PartialOrd + Send + Sync,
|
||||
{
|
||||
fn load<'life0, 'async_trait, P>(
|
||||
&'life0 self,
|
||||
path: P,
|
||||
) -> core::pin::Pin<
|
||||
Box<
|
||||
dyn core::future::Future<Output = Result<RadarData2d<T, OwnedRepr<T>>, DataError>>
|
||||
+ core::marker::Send
|
||||
+ 'async_trait,
|
||||
>,
|
||||
>
|
||||
fn map_by_fn<F>(&mut self, f: F)
|
||||
where
|
||||
P: 'async_trait + AsRef<Path> + Send,
|
||||
'life0: 'async_trait,
|
||||
Self: 'async_trait,
|
||||
F: FnMut(&mut T),
|
||||
{
|
||||
Box::pin(async {
|
||||
let mut f = File::open(path).await?;
|
||||
let mut buffer = Vec::new();
|
||||
f.read_to_end(&mut buffer).await?;
|
||||
|
||||
let mut data = npyz::npz::NpzArchive::new(Cursor::new(buffer))?;
|
||||
let dim1 = self.load_1d::<f64, Cursor<Vec<u8>>>(&mut data, "lon")?;
|
||||
let dim2 = self.load_1d::<f64, Cursor<Vec<u8>>>(&mut data, "lat")?;
|
||||
let value = self.load_2d::<T, Cursor<Vec<u8>>>(&mut data, "value")?;
|
||||
|
||||
Ok(RadarData2d {
|
||||
dim1: dim1,
|
||||
dim2: dim2,
|
||||
fill_value: T::from_f64(-125.0).unwrap(),
|
||||
data: value,
|
||||
coord_type: CoordType::LatLon,
|
||||
})
|
||||
})
|
||||
self.data.map_inplace(f);
|
||||
}
|
||||
}
|
||||
|
||||
// fn resample(
|
||||
// &self,
|
||||
// width_rate: f64,
|
||||
// height_rate: f64,
|
||||
// filter_len: f64,
|
||||
// ) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
||||
// let width_rate = width_rate.min(1.0);
|
||||
// let height_rate = height_rate.min(1.0);
|
||||
// match self.coord_type {
|
||||
// CoordType::Polar => Err(DataError::FormatError),
|
||||
// CoordType::LatLon => {
|
||||
// let width_filtered: ArrayBase<ndarray::OwnedRepr<T>, Ix2> =
|
||||
// Self::_resample(&self.data, width_rate, filter_len);
|
||||
|
||||
// let result: ArrayBase<OwnedRepr<T>, Ix2> =
|
||||
// Self::_resample(&width_filtered.t(), height_rate, filter_len)
|
||||
// .t()
|
||||
// .to_owned();
|
||||
|
||||
// let new_dim1: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
|
||||
// &Array2::from_shape_vec((1, self.dim1.len()), self.dim1.to_vec()).unwrap(),
|
||||
// width_rate,
|
||||
// filter_len,
|
||||
// )
|
||||
// .slice(s![0, ..])
|
||||
// .to_owned();
|
||||
|
||||
// let new_dim2: ArrayBase<OwnedRepr<f64>, Ix1> = Self::_resample(
|
||||
// &Array2::from_shape_vec((1, self.dim2.len()), self.dim2.to_vec()).unwrap(),
|
||||
// height_rate,
|
||||
// filter_len,
|
||||
// )
|
||||
// .slice(s![0, ..])
|
||||
// .to_owned();
|
||||
|
||||
// Ok(RadarData2d {
|
||||
// dim1: new_dim1,
|
||||
// dim2: new_dim2,
|
||||
// data: result,
|
||||
// coord_type: self.coord_type.to_owned(),
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn _resample<'a, V, R: ndarray::Data<Elem = V>>(
|
||||
// data: &'a ArrayBase<R, Ix2>,
|
||||
// rate: f64,
|
||||
// filter_len: f64,
|
||||
// ) -> Array2<V>
|
||||
// where
|
||||
// V: Num + Clone + AsPrimitive<f64> + FromPrimitive,
|
||||
// {
|
||||
// let ori_width = data.ncols();
|
||||
// let ori_height = data.nrows();
|
||||
|
||||
// let new_width = (ori_width as f64 * rate).ceil() as usize;
|
||||
// let mut result: Array2<V> = Array2::zeros((ori_height, new_width));
|
||||
// (0..ori_height).into_iter().for_each(|height| {
|
||||
// for width in 0..new_width {
|
||||
// let center_x = (width as f64 + 0.5) / new_width as f64 * ori_width as f64;
|
||||
// let filter_start = center_x - filter_len / 2.0;
|
||||
// let start_idx = (filter_start - 0.5).ceil() as usize;
|
||||
|
||||
// let mut value_sum = 0.0;
|
||||
// let mut filter_sum = 0.0;
|
||||
|
||||
// for i in 0..filter_len as usize {
|
||||
// let input_x = start_idx + i;
|
||||
// let weight = windowed_sinc(
|
||||
// (input_x as f64 + 0.5 - center_x) * rate,
|
||||
// (input_x as f64 + 0.5 - filter_start) / filter_len,
|
||||
// );
|
||||
// value_sum += weight * data[[height, input_x.clamp(0, ori_width - 1)]].as_();
|
||||
// filter_sum += weight;
|
||||
// }
|
||||
|
||||
// result[[height, width]] = V::from_f64(value_sum / filter_sum).unwrap();
|
||||
// }
|
||||
// });
|
||||
// result
|
||||
// }
|
||||
|
||||
// pub struct LevelData<T: Num + Clone + PartialEq + PartialOrd>(
|
||||
// pub Quadtree<i64, RadarData2d<T, OwnedRepr<T>>>,
|
||||
// );
|
||||
|
||||
// impl<T> LevelData<T>
|
||||
// where
|
||||
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug + PartialEq + PartialOrd,
|
||||
// {
|
||||
// fn value(
|
||||
// level_data: Vec<RadarData2d<T, OwnedRepr<T>>>,
|
||||
// level_num: usize,
|
||||
// ) -> Vec<RadarData2d<T, OwnedRepr<T>>> {
|
||||
// if level_num == 0 {
|
||||
// return level_data;
|
||||
// }
|
||||
|
||||
// let mut result: Vec<RadarData2d<T, OwnedRepr<T>>> =
|
||||
// Vec::with_capacity(level_data.len() * 4);
|
||||
|
||||
// let results = level_data
|
||||
// .iter()
|
||||
// .flat_map(|v| v.split().into_iter().map(|x| x.value_to_owned()));
|
||||
|
||||
// result.extend(results);
|
||||
|
||||
// return Self::value(result, level_num - 1);
|
||||
// }
|
||||
|
||||
// fn new(data: &RadarData2d<T, OwnedRepr<T>>, level: usize, rate: f64) -> Self {
|
||||
// // let rate = 1.0 / level as f64;
|
||||
// let resampled = data.resample(rate, rate, 2.0).unwrap();
|
||||
// let blocks = Self::value(vec![resampled], level);
|
||||
// let mut tree: Quadtree<i64, RadarData2d<T, OwnedRepr<T>>> =
|
||||
// quadtree_rs::Quadtree::new(level);
|
||||
|
||||
// blocks.into_iter().for_each(|block| {
|
||||
// tree.insert(
|
||||
// AreaBuilder::default()
|
||||
// .anchor(quadtree_rs::point::Point {
|
||||
// x: *block.dim1.first().unwrap() as i64,
|
||||
// y: *block.dim2.first().unwrap() as i64,
|
||||
// })
|
||||
// .dimensions((block.dim1.len() as i64, block.dim2.len() as i64))
|
||||
// .build()
|
||||
// .unwrap(),
|
||||
// block,
|
||||
// );
|
||||
// });
|
||||
|
||||
// Self(tree)
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn levels<T>(data: Radar2d<T>, levels: usize) -> Vec<LevelData<T>>
|
||||
// where
|
||||
// T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug,
|
||||
// T: PartialEq + PartialOrd,
|
||||
// {
|
||||
// let numerator = 1.0 / levels as f64;
|
||||
// (0..levels)
|
||||
// .into_iter()
|
||||
// .map(|level| LevelData::new(&data, level + 1, 1.0 - level as f64 * numerator))
|
||||
// .collect()
|
||||
// }
|
||||
|
||||
#[inline]
|
||||
fn windowed_sinc(x: f64, y: f64) -> f64 {
|
||||
let x = x * PI;
|
||||
let sinc = if x != 0.0 { f64::sin(x) / x } else { 1.0 };
|
||||
let window = if 0f64 <= y && y <= 1.0 {
|
||||
1.0 - (y - 0.5).abs() * 2.0
|
||||
} else {
|
||||
0f64
|
||||
};
|
||||
sinc * window
|
||||
}
|
||||
|
||||
pub enum DownSampleMeth {
|
||||
STD,
|
||||
MEAN,
|
||||
VAR,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CoordType {
|
||||
Polar,
|
||||
LatLon,
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
use radarg_plugin_interface::{CoordType, PluginResult, PluginResultType};
|
||||
use crate::pipeline::element::ElementImpl;
|
||||
use crate::pipeline::GridElementImpl;
|
||||
use crate::utils::*;
|
||||
use radarg_plugin_interface::{CoordType, DataShape, PluginResult, PluginResultType};
|
||||
use std::sync::Arc;
|
||||
macro_rules! data_to_grid {
|
||||
($_type:ident,$(($branch:path ,$boundary_norm: expr)),+) => {
|
||||
match $_type {
|
||||
$(
|
||||
$branch => {
|
||||
let element_impl = GridElementImpl::new($boundary_norm);
|
||||
Box::new(element_impl)
|
||||
Arc::new(element_impl)
|
||||
}
|
||||
),+
|
||||
_ => panic!("Invalid type")
|
||||
@ -16,8 +17,8 @@ macro_rules! data_to_grid {
|
||||
|
||||
};
|
||||
}
|
||||
pub fn plugin_result_impl(a: &PluginResult) -> Box<dyn ElementImpl> {
|
||||
let block= a.blocks.first().unwrap();
|
||||
pub fn plugin_result_impl(a: &PluginResult) -> Arc<dyn ElementImpl> {
|
||||
let block = a.blocks.first().unwrap();
|
||||
match block.coord_type {
|
||||
CoordType::Cartesian => {
|
||||
let _type = block.data_type;
|
||||
@ -41,4 +42,3 @@ pub fn plugin_result_impl(a: &PluginResult) -> Box<dyn ElementImpl> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ pub enum PipelineError {
|
||||
source: ProjError,
|
||||
},
|
||||
#[error("data error")]
|
||||
DataError(String)
|
||||
DataError(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
@ -50,7 +50,6 @@ pub enum PluginError {
|
||||
PluginError,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum PoolError {
|
||||
#[error("")]
|
||||
@ -61,7 +60,6 @@ pub enum PoolError {
|
||||
PoolInitialized(&'static str),
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum RenderError {
|
||||
#[error("")]
|
||||
@ -70,5 +68,4 @@ pub enum RenderError {
|
||||
None,
|
||||
#[error("Canceled")]
|
||||
Canceled,
|
||||
|
||||
}
|
||||
|
||||
14
src/main.rs
14
src/main.rs
@ -12,6 +12,7 @@ use gtk::{
|
||||
use plugin_system::{init_plugin, PluginManager};
|
||||
use std::{ptr, sync::Mutex};
|
||||
use tokio::runtime::Runtime;
|
||||
mod actions;
|
||||
mod chart;
|
||||
mod components;
|
||||
mod config;
|
||||
@ -20,17 +21,21 @@ mod data;
|
||||
mod errors;
|
||||
mod pipeline;
|
||||
mod plugin_system;
|
||||
use crate::components::app::AppMsg;
|
||||
use components::app::AppModel;
|
||||
use once_cell::{sync::Lazy as SafeLazy, unsync::Lazy as UnsafeLazy};
|
||||
use relm4::RelmApp;
|
||||
use surfman::declare_surfman;
|
||||
use tracing::info;
|
||||
use tracing_subscriber;
|
||||
|
||||
mod data_utils;
|
||||
mod map_tile;
|
||||
mod map_tile_utils;
|
||||
mod predefined;
|
||||
mod widgets;
|
||||
|
||||
#[cfg(target_env = "msvc")]
|
||||
declare_surfman!();
|
||||
|
||||
const APP_ID: &str = "org.tsuki.radar_g";
|
||||
@ -57,16 +62,19 @@ fn main() {
|
||||
.unwrap();
|
||||
|
||||
epoxy::load_with(|name| {
|
||||
unsafe { library.get(name.as_bytes()) }
|
||||
unsafe { library.get::<_>(name.as_bytes()) }
|
||||
.map(|symbol| *symbol)
|
||||
.unwrap_or(ptr::null())
|
||||
});
|
||||
}
|
||||
let relm = relm4::RelmApp::new(APP_ID);
|
||||
|
||||
let app = adw::Application::builder().application_id(APP_ID).build();
|
||||
let relm: RelmApp<AppMsg> = relm4::RelmApp::from_app(app.clone());
|
||||
relm4_icons::initialize_icons();
|
||||
initialize_custom_css();
|
||||
info!("Init plugin system");
|
||||
let pluginmanager = PluginManager::new();
|
||||
initialize_custom_css();
|
||||
|
||||
relm.run::<AppModel>(());
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +144,7 @@ impl MapTile {
|
||||
(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, None);
|
||||
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)));
|
||||
}
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
|
||||
pub fn lat_lon_to_zoom(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h:f32 ) -> u8 {
|
||||
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 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,36 +1,32 @@
|
||||
use super::{offscreen_renderer::CanvasWrapper, Dispatcher, Pipeline};
|
||||
use crate::components::app::AppCommand;
|
||||
use crate::components::ControlPanelInputMsg;
|
||||
use crate::components::Widget;
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::coords::Range;
|
||||
use crate::data::MetaInfo;
|
||||
use crate::errors::{PipelineError, RenderError};
|
||||
use crate::predefined::color_mapper::ColorMapper;
|
||||
use crate::widgets::Render;
|
||||
use crate::RUNTIME;
|
||||
use crate::{coords::Range, widgets::widget::Widget};
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
use core_extensions::SelfOps;
|
||||
use femtovg::rgb::alt::GRAY8;
|
||||
use femtovg::{renderer::OpenGl, Canvas, ImageFlags, ImageId, ImageInfo, PixelFormat};
|
||||
use femtovg::{ImageFlags, ImageId};
|
||||
use futures::StreamExt;
|
||||
use num_traits::{AsPrimitive, FromPrimitive, NumOps};
|
||||
use radarg_plugin_interface::PluginResult;
|
||||
use std::any::Any;
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::fmt::Formatter;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use tokio::sync::{
|
||||
oneshot::{channel, Receiver, Sender},
|
||||
oneshot::{channel, Receiver},
|
||||
Notify,
|
||||
};
|
||||
use tracing::Instrument;
|
||||
|
||||
pub type ElementID = usize;
|
||||
static ELEMENT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||
@ -48,7 +44,7 @@ pub enum Element {
|
||||
|
||||
impl Element {
|
||||
pub fn create_time_series(
|
||||
imp: Arc<Box<dyn ElementImpl>>,
|
||||
imp: Arc<dyn ElementImpl>,
|
||||
dispatcher: Rc<Dispatcher>,
|
||||
key: String,
|
||||
cms: CMS,
|
||||
@ -95,7 +91,7 @@ pub struct TimeSeriesElement {
|
||||
pub id: ElementID,
|
||||
pub key: String,
|
||||
cms: CMS,
|
||||
imp: Arc<Box<dyn ElementImpl>>,
|
||||
imp: Arc<dyn ElementImpl>,
|
||||
registers: Arc<Mutex<HashMap<DateTime<Utc>, Vec<Arc<Notify>>>>>,
|
||||
pipeline: Pipeline,
|
||||
pub buffer: Buffer,
|
||||
@ -105,7 +101,7 @@ pub struct TimeSeriesElement {
|
||||
#[derive(Clone)]
|
||||
pub enum InstantElementDrawerType {
|
||||
Draw(DrawFunc),
|
||||
Prepared((Target, Arc<Box<dyn ElementImpl>>)),
|
||||
Prepared((DataTarget, Arc<dyn ElementImpl>)),
|
||||
}
|
||||
|
||||
impl Debug for InstantElementDrawerType {
|
||||
@ -121,6 +117,11 @@ pub struct InstantElement {
|
||||
dispatcher: Rc<Dispatcher>,
|
||||
}
|
||||
|
||||
pub enum ElementImplMap {
|
||||
ColorMap,
|
||||
None,
|
||||
}
|
||||
|
||||
pub trait ElementImpl: Debug + Send + Sync + 'static {
|
||||
fn render(&self, data: &PluginResult, canvas: &mut CanvasWrapper, cms: &mut CMS) -> Target;
|
||||
}
|
||||
@ -141,51 +142,24 @@ impl InstantElement {
|
||||
func(render);
|
||||
}
|
||||
InstantElementDrawerType::Prepared((ref mut target, _)) => {
|
||||
let mut canvas = render.get_canvas();
|
||||
let mut canvas = canvas.as_mut().unwrap();
|
||||
let (ox, oy) = target.origin(render);
|
||||
let (x, y) = target.size(render);
|
||||
|
||||
let result_id = match target.target {
|
||||
TargetType::ImageId(id) => id,
|
||||
TargetType::Mem(ref mem) => {
|
||||
let gl_bind = render.get_context();
|
||||
let gl = gl_bind.as_ref().unwrap();
|
||||
let flags = ImageFlags::empty();
|
||||
let texture = target.mem_to_native_texture(gl, flags);
|
||||
let converted = canvas
|
||||
.create_image_from_native_texture(
|
||||
texture,
|
||||
ImageInfo::new(flags, 3000, 3000, PixelFormat::Rgba8),
|
||||
)
|
||||
.unwrap();
|
||||
target.set_target(TargetType::ImageId(converted));
|
||||
converted
|
||||
}
|
||||
};
|
||||
|
||||
let paint = femtovg::Paint::image(result_id, ox, oy, x, y, 0.0, 1.0);
|
||||
let mut path = femtovg::Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
canvas.fill_path(&path, &paint);
|
||||
render.draw_img(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn imp(&self) -> &InstantElementDrawerType {
|
||||
&self.draw_type
|
||||
}
|
||||
|
||||
pub fn to_time_series(
|
||||
self,
|
||||
dispatcher: Rc<Dispatcher>,
|
||||
cms: CMS,
|
||||
) -> (TimeSeriesElement, DateTime<Utc>) {
|
||||
// let imp = Arc::new(InstantElementImpl::new(self));
|
||||
if let InstantElementDrawerType::Prepared((target, imp)) = self.draw_type {
|
||||
if let InstantElementDrawerType::Prepared((mut target, imp)) = self.draw_type {
|
||||
let mut time_series = TimeSeriesElement::new(imp, dispatcher, cms, self.key);
|
||||
let data = target
|
||||
.data
|
||||
.clone()
|
||||
.unwrap()
|
||||
.downcast::<PluginResult>()
|
||||
.unwrap();
|
||||
let data = target.take_data().unwrap();
|
||||
let time_stamp = data.blocks.first().unwrap().datetime;
|
||||
let meta_info: MetaInfo = data.meta.clone().into();
|
||||
use chrono::prelude::*;
|
||||
@ -202,12 +176,7 @@ impl InstantElement {
|
||||
}
|
||||
|
||||
impl TimeSeriesElement {
|
||||
fn new(
|
||||
imp: Arc<Box<dyn ElementImpl>>,
|
||||
dispatcher: Rc<Dispatcher>,
|
||||
cms: CMS,
|
||||
key: String,
|
||||
) -> Self {
|
||||
fn new(imp: Arc<dyn ElementImpl>, dispatcher: Rc<Dispatcher>, cms: CMS, key: String) -> Self {
|
||||
let id = ELEMENT_ID.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
let mut pipeline = Pipeline::new(20, key.clone());
|
||||
pipeline.set_dispatcher(dispatcher.clone());
|
||||
@ -266,6 +235,10 @@ impl TimeSeriesElement {
|
||||
self.cms = cms;
|
||||
}
|
||||
|
||||
pub fn imp(&self) -> &Arc<dyn ElementImpl> {
|
||||
&self.imp
|
||||
}
|
||||
|
||||
fn register_noti(&self, datetime: DateTime<Utc>, noti: Arc<Notify>) {
|
||||
self.registers
|
||||
.lock()
|
||||
@ -329,12 +302,12 @@ impl TimeSeriesElement {
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RenderResult {
|
||||
target: Target,
|
||||
target: DataTarget,
|
||||
meta_info: MetaInfo,
|
||||
}
|
||||
|
||||
impl RenderResult {
|
||||
pub fn new(target: Target, meta_info: MetaInfo) -> Self {
|
||||
pub fn new(target: DataTarget, meta_info: MetaInfo) -> Self {
|
||||
Self { target, meta_info }
|
||||
}
|
||||
|
||||
@ -354,13 +327,14 @@ pub struct Target {
|
||||
pub width: f32,
|
||||
pub height: f32,
|
||||
pub bounds: (Range, Range),
|
||||
pub data: Option<Arc<dyn Any + Send + Sync + 'static>>,
|
||||
// pub data: Option<Arc<dyn Any + Send + Sync + 'static>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
|
||||
pub enum TargetType {
|
||||
ImageId(ImageId),
|
||||
Mem(Vec<u8>),
|
||||
NativeBuffer(Vec<u8>),
|
||||
}
|
||||
|
||||
impl Target {
|
||||
@ -370,7 +344,7 @@ impl Target {
|
||||
height: f32,
|
||||
bounds: (Range, Range),
|
||||
thumbnail: Option<gtk::gdk::Texture>,
|
||||
data: Option<Arc<dyn Any + Send + Sync + 'static>>,
|
||||
// data: Option<Arc<dyn Any + Send + Sync + 'static>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
target,
|
||||
@ -378,7 +352,7 @@ impl Target {
|
||||
height,
|
||||
bounds,
|
||||
thumbnail,
|
||||
data,
|
||||
// data,
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,12 +368,12 @@ impl Target {
|
||||
((x2 - x1).abs(), (y2 - y1).abs())
|
||||
}
|
||||
|
||||
pub fn mem_to_native_texture(
|
||||
pub fn native_buffer_to_native_texture(
|
||||
&self,
|
||||
gl: &glow::Context,
|
||||
flags: ImageFlags,
|
||||
) -> glow::NativeTexture {
|
||||
if let TargetType::Mem(ref mem) = self.target {
|
||||
if let TargetType::NativeBuffer(ref mem) = self.target {
|
||||
use glow::*;
|
||||
let texture = unsafe {
|
||||
let id = gl.create_texture().unwrap();
|
||||
@ -549,3 +523,41 @@ impl Target {
|
||||
self.target = target;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DataTarget {
|
||||
data: Option<PluginResult>,
|
||||
target: Target,
|
||||
}
|
||||
|
||||
impl DataTarget {
|
||||
pub fn new(data: Option<PluginResult>, target: Target) -> Self {
|
||||
Self { data, target }
|
||||
}
|
||||
|
||||
pub fn take_data(&mut self) -> Option<PluginResult> {
|
||||
self.data.take()
|
||||
}
|
||||
|
||||
pub fn data(&self) -> Option<&PluginResult> {
|
||||
self.data.as_ref()
|
||||
}
|
||||
|
||||
pub fn mut_data(&mut self) -> Option<&mut PluginResult> {
|
||||
self.data.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for DataTarget {
|
||||
type Target = Target;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.target
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for DataTarget {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.target
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
use super::predefined::GridFieldRenderer;
|
||||
use super::renders::DataRenderer;
|
||||
use crate::data::Radar2d;
|
||||
use crate::pipeline::element::{ElementImpl, Target};
|
||||
use crate::components::Widget;
|
||||
use crate::data::{Radar2d, Radar3d};
|
||||
use crate::pipeline::element::{ElementImpl, ElementImplMap, Target};
|
||||
use crate::pipeline::offscreen_renderer::CanvasWrapper;
|
||||
use crate::widgets::predefined::color_mapper::ColorMapper;
|
||||
use crate::widgets::{LayerImpl};
|
||||
use crate::predefined::color_mapper::ColorMapper;
|
||||
use crate::widgets::LayerImpl;
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps};
|
||||
use radarg_plugin_interface::{DataShape, PluginResult};
|
||||
use std::any::Any;
|
||||
use std::fmt::Debug;
|
||||
use radarg_plugin_interface::PluginResult;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridElementImpl<CMAP, T>
|
||||
@ -52,8 +53,22 @@ where
|
||||
cms: &mut crate::coords::cms::CMS,
|
||||
) -> crate::pipeline::element::Target {
|
||||
let first_block = data.blocks.first().unwrap();
|
||||
let data = first_block.clone().into();
|
||||
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0));
|
||||
result
|
||||
match first_block.shape {
|
||||
DataShape::Vector => {
|
||||
panic!("Vector data is not supported")
|
||||
}
|
||||
DataShape::Matrix => {
|
||||
let data:Radar2d<T> = first_block.clone().into();
|
||||
let data = data.as_ref();
|
||||
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0));
|
||||
result
|
||||
}
|
||||
DataShape::Cube => {
|
||||
let data: Radar3d<T> = first_block.clone().into();
|
||||
let data = data.index_axis(ndarray::Axis(0), 0);
|
||||
let result = self.renderer.render(canvas, cms, &data, (3000.0, 3000.0));
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@ mod renders;
|
||||
pub mod utils;
|
||||
|
||||
pub use dispatcher::Dispatcher;
|
||||
pub use element::RenderResult;
|
||||
pub use element::*;
|
||||
pub use element_impl::*;
|
||||
pub use new_pipeline::Pipeline;
|
||||
pub use offscreen_renderer::OffscreenRenderer;
|
||||
pub use element_impl::*;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
dispatcher::Dispatcher,
|
||||
element::RenderResult,
|
||||
element::{DataTarget, RenderResult},
|
||||
offscreen_renderer::{CanvasWrapper, OffscreenRenderer},
|
||||
utils::data_to_element,
|
||||
};
|
||||
@ -16,6 +16,7 @@ use crate::{
|
||||
use chrono::prelude::*;
|
||||
use femtovg::{renderer::OpenGl, Canvas, ImageId};
|
||||
use futures::{future::BoxFuture, Future};
|
||||
use radarg_plugin_interface::PluginResult;
|
||||
use smallvec::SmallVec;
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::{
|
||||
@ -23,7 +24,6 @@ use std::{
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use radarg_plugin_interface::PluginResult;
|
||||
use tokio::{
|
||||
sync::{mpsc, oneshot},
|
||||
task,
|
||||
@ -84,7 +84,7 @@ impl Pipeline {
|
||||
current_time: DateTime<Utc>,
|
||||
check_existed: bool,
|
||||
max_retry_time: usize,
|
||||
task: Arc<dyn Fn(&PluginResult ,&mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
|
||||
task: Arc<dyn Fn(&PluginResult, &mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
|
||||
cms: CMS,
|
||||
) -> Option<Vec<DateTime<Utc>>> {
|
||||
let paths = {
|
||||
@ -123,7 +123,7 @@ impl Pipeline {
|
||||
fn worker(
|
||||
&self,
|
||||
datetime: DateTime<Utc>,
|
||||
task: Arc<dyn Fn(&PluginResult ,&mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
|
||||
task: Arc<dyn Fn(&PluginResult, &mut CanvasWrapper, &mut CMS) -> Target + Send + Sync>,
|
||||
mut cms: CMS,
|
||||
path: impl AsRef<str> + Send + 'static,
|
||||
) -> BoxFuture<'static, RenderR> {
|
||||
@ -135,8 +135,8 @@ impl Pipeline {
|
||||
let handle = task::spawn_blocking(move || {
|
||||
let mut offscreen_renderer = OffscreenRenderer::new(3000, 3000).unwrap();
|
||||
let mut canvas_wrapper = offscreen_renderer.create_canvas();
|
||||
let target = task(&loaded_data,&mut canvas_wrapper, &mut cms);
|
||||
target
|
||||
let target = task(&loaded_data, &mut canvas_wrapper, &mut cms);
|
||||
DataTarget::new(Some(loaded_data), target)
|
||||
});
|
||||
|
||||
let target = handle.await.unwrap();
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
use super::super::renders::DataRenderer;
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::data::{Radar2dRef, RadarData2d};
|
||||
use crate::pipeline::element::{Target, TargetType};
|
||||
use crate::widgets::predefined::color_mapper::ColorMapper;
|
||||
use crate::predefined::color_mapper::ColorMapper;
|
||||
use crate::{data::Radar2d, utils::meshgrid};
|
||||
use femtovg::ImageInfo;
|
||||
use femtovg::{
|
||||
@ -9,11 +10,11 @@ use femtovg::{
|
||||
};
|
||||
use geo_types::LineString;
|
||||
use gl::types::GLvoid;
|
||||
use gtk::ResponseType::No;
|
||||
use image::{imageops::resize, ImageBuffer, Rgba};
|
||||
use ndarray::ArrayView2;
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num, NumOps};
|
||||
use std::{fmt::Debug, io::Cursor, marker::PhantomData};
|
||||
use gtk::ResponseType::No;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -22,7 +23,7 @@ where
|
||||
T: NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
cmap: CMAP,
|
||||
pub cmap: CMAP,
|
||||
value_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
@ -61,15 +62,6 @@ impl<T: NumOps + PartialOrd + Copy + FromPrimitive + AsPrimitive<f64>, CMAP: Col
|
||||
|
||||
let rt_lat = dim2[[r + 1, c + 1]];
|
||||
let rt_lon = dim1[[r + 1, c + 1]];
|
||||
// let cell: LineString = vec![
|
||||
// (lb_lon, lb_lat),
|
||||
// (rt_lon + 0.001, lb_lat),
|
||||
// (rt_lon + 0.001, rt_lat),
|
||||
// (lb_lon, rt_lat + 0.001),
|
||||
// (lb_lon, lb_lat + 0.001),
|
||||
// ]
|
||||
// .into();
|
||||
|
||||
let v = &data[[r, c]];
|
||||
let mapped_color = self.cmap.map_value_to_color(*v, fill_value);
|
||||
|
||||
@ -77,32 +69,23 @@ impl<T: NumOps + PartialOrd + Copy + FromPrimitive + AsPrimitive<f64>, CMAP: Col
|
||||
continue;
|
||||
}
|
||||
|
||||
// let mapped_ring = cms.ring_map(&cell).unwrap();
|
||||
let (ox,oy) = cms.map((lb_lon, lb_lat)).unwrap();
|
||||
let (ox, oy) = cms.map((lb_lon, lb_lat)).unwrap();
|
||||
let (rx, ry) = cms.map((rt_lon, rt_lat)).unwrap();
|
||||
|
||||
let mut path = Path::new();
|
||||
// let mut points = mapped_ring.points();
|
||||
// let first_point = points.next().unwrap();
|
||||
// path.move_to(first_point.x(), first_point.y());
|
||||
//
|
||||
// for point in points {
|
||||
// path.line_to(point.x(), point.y());
|
||||
// }
|
||||
path.rect(ox, oy, (rx - ox) * 1.5, (ry - oy) * 1.5);
|
||||
// path.close();
|
||||
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T>
|
||||
impl<'a, T, CMAP> DataRenderer<'a> for GridFieldRenderer<CMAP, T>
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone + FromPrimitive + AsPrimitive<f64>,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
type Data = Radar2d<T>;
|
||||
type Data = Radar2dRef<'a, T>;
|
||||
|
||||
fn render(
|
||||
&self,
|
||||
@ -156,25 +139,6 @@ where
|
||||
debug_assert_eq!(gl::GetError(), gl::NO_ERROR);
|
||||
}
|
||||
info!("Time to read pixels: {:?}", start.elapsed());
|
||||
|
||||
// let img: ImageBuffer<Rgba<u8>, Vec<u8>> = ImageBuffer::from_raw(w as u32, h as u32, pixels)
|
||||
// .expect("Failed to create ImageBuffer");
|
||||
// let thumbnail = resize(&img, 500, 500, image::imageops::FilterType::Lanczos3);
|
||||
// let mut thumb_buffer = Cursor::new(Vec::new());
|
||||
// img.write_to(&mut thumb_buffer, image::ImageOutputFormat::Png)
|
||||
// .expect("Failed to write PNG buffer");
|
||||
// info!("Time to write PNG: {:?}", start.elapsed());
|
||||
// let thumb_data = thumb_buffer.into_inner();
|
||||
// let thumbnail_tex =
|
||||
// gtk::gdk::Texture::from_bytes(>k::glib::Bytes::from(&thumb_data)).unwrap();
|
||||
|
||||
// 将 ImageBuffer 编码为 PNG
|
||||
// let mut png_buffer = Cursor::new(Vec::new());
|
||||
// img.write_to(&mut png_buffer, image::ImageOutputFormat::Bmp)
|
||||
// .expect("Failed to write PNG buffer");
|
||||
|
||||
// let png_data = png_buffer.into_inner();
|
||||
|
||||
let d1_start = (data.dim1.view()).first().unwrap().clone();
|
||||
let d1_end = (data.dim1.view()).last().unwrap().clone();
|
||||
|
||||
@ -183,13 +147,11 @@ where
|
||||
canvas.set_render_target(RenderTarget::Screen);
|
||||
|
||||
Target::new(
|
||||
TargetType::Mem(pixels),
|
||||
TargetType::NativeBuffer(pixels),
|
||||
w,
|
||||
h,
|
||||
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
|
||||
// Some(thumbnail_tex),
|
||||
None,
|
||||
None
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ use crate::coords::cms::CMS;
|
||||
use crate::pipeline::element::Target;
|
||||
use femtovg::{renderer::OpenGl, Canvas};
|
||||
|
||||
pub trait DataRenderer {
|
||||
pub trait DataRenderer<'a> {
|
||||
type Data;
|
||||
fn render(
|
||||
&self,
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
use crate::{
|
||||
coords::cms::CMS, data::Radar2d, errors::RenderError,
|
||||
widgets::predefined::color_mapper::BoundaryNorm,
|
||||
coords::cms::CMS, data::Radar2d, errors::RenderError, predefined::color_mapper::BoundaryNorm,
|
||||
};
|
||||
use chrono::{prelude::*, Duration};
|
||||
use radarg_plugin_interface::*;
|
||||
@ -25,7 +24,7 @@ macro_rules! match_in_macro {
|
||||
match $block.data_type {
|
||||
$(
|
||||
$branch => {
|
||||
let element = Element::create_time_series(Arc::new(Box::new(GridElementImpl::new($color))), $dispatcher, $name.to_string(), $cms);
|
||||
let element = Element::create_time_series(Arc::new(GridElementImpl::new($color)), $dispatcher, $name.to_string(), $cms);
|
||||
Some(element)
|
||||
},
|
||||
)+
|
||||
@ -36,11 +35,7 @@ macro_rules! match_in_macro {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn data_to_element(
|
||||
block: &Block,
|
||||
dispatcher: Rc<Dispatcher>,
|
||||
cms: CMS,
|
||||
) -> Option<Element> {
|
||||
pub fn data_to_element(block: &Block, dispatcher: Rc<Dispatcher>, cms: CMS) -> Option<Element> {
|
||||
use crate::utils::*;
|
||||
use radarg_plugin_interface::PluginResultType;
|
||||
match block.shape {
|
||||
|
||||
@ -1,8 +1,42 @@
|
||||
use crate::data::{CoordType, Radar2d};
|
||||
use crate::data::{CoordType, Radar2d, Radar3d};
|
||||
use ndarray::{Array1, Array2, Array3, ShapeBuilder};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use radarg_plugin_interface::{Block, DataShape, PluginResult, VecResult};
|
||||
|
||||
macro_rules! match_in_macro_3d {
|
||||
// ($(($branch:path, $t:ty)),+, $block:ident)
|
||||
($block:tt, $(($branch:path, $t:path)),+) => {
|
||||
{
|
||||
let data = $block.data;
|
||||
let dim3: Vec<_> = $block.dimension_values.remove(0).into();
|
||||
let dim2: Vec<_> = $block.dimension_values.remove(0).into();
|
||||
let dim1: Vec<_> = $block.dimension_values.remove(0).into();
|
||||
let fill_value = $block.fill_value;
|
||||
let data_shape: Vec<usize> = $block.size.into();
|
||||
let coord_type = match $block.coord_type{
|
||||
radarg_plugin_interface::CoordType::Cartesian => CoordType::LatLon,
|
||||
radarg_plugin_interface::CoordType::Polar => CoordType::Polar,
|
||||
_ => panic!("Unsupported coord type")
|
||||
};
|
||||
match data {
|
||||
$(
|
||||
$branch(x) => {
|
||||
let shape = [data_shape[0], data_shape[1], data_shape[2]];
|
||||
Radar3d {
|
||||
fill_value: T::from_f64(fill_value).unwrap(),
|
||||
data: Array3::from_shape_vec(shape, x.into_iter().map(|x| $t(x).unwrap()).collect::<Vec<T>>()).unwrap(),
|
||||
dim1: Array1::from_vec(dim1),
|
||||
dim2: Array1::from_vec(dim2),
|
||||
dim3: Array1::from_vec(dim3),
|
||||
coord_type: coord_type,
|
||||
}},
|
||||
)+
|
||||
_ => panic!("Unsupported data type"),
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
macro_rules! match_in_macro {
|
||||
// ($(($branch:path, $t:ty)),+, $block:ident)
|
||||
($block:tt, $(($branch:path, $t:path)),+) => {
|
||||
@ -61,3 +95,31 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Block> for Radar3d<T>
|
||||
where
|
||||
T: Num + Copy + PartialOrd + PartialEq,
|
||||
T: FromPrimitive,
|
||||
{
|
||||
fn from(block: Block) -> Self {
|
||||
let mut block = block;
|
||||
let a = &block.dimension_values;
|
||||
println!("{:?}", a);
|
||||
if let DataShape::Cube = block.shape {
|
||||
let result = match_in_macro_3d!(
|
||||
block,
|
||||
(VecResult::I8, T::from_i8),
|
||||
(VecResult::I32, T::from_i32),
|
||||
(VecResult::I64, T::from_i64),
|
||||
(VecResult::U8, T::from_u8),
|
||||
(VecResult::U32, T::from_u32),
|
||||
(VecResult::U64, T::from_u64),
|
||||
(VecResult::F32, T::from_f32),
|
||||
(VecResult::F64, T::from_f64)
|
||||
);
|
||||
result
|
||||
} else {
|
||||
panic!("Expected matrix data shape");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
use crate::map_tile_utils::*;
|
||||
use slippy_map_tiles::{BBox, merc_location_to_tile_coords, size_bbox_zoom_metatiles, Tile};
|
||||
use slippy_map_tiles::{merc_location_to_tile_coords, size_bbox_zoom_metatiles, BBox, Tile};
|
||||
|
||||
pub fn map_tile_layer(lat_range: (f64, f64), lon_range: (f64, f64), w: f32, h: f32) {
|
||||
let z = lat_lon_to_zoom(lat_range, lon_range, w, h);
|
||||
let bbox = BBox::new(lat_range.0 as f32, lon_range.0 as f32, lat_range.1 as f32, lon_range.1 as f32).unwrap();
|
||||
let bbox = BBox::new(
|
||||
lat_range.0 as f32,
|
||||
lon_range.0 as f32,
|
||||
lat_range.1 as f32,
|
||||
lon_range.1 as f32,
|
||||
)
|
||||
.unwrap();
|
||||
let size = size_bbox_zoom_metatiles(&bbox, z, 4);
|
||||
let t = Tile::new(10, 547, 380).unwrap();
|
||||
println!("size: {:?}", size);
|
||||
println!("tile: {:?}", t);
|
||||
}
|
||||
|
||||
|
||||
mod test {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_map_tile_layer() {
|
||||
map_tile_layer((37.7749, 37.7749), (-122.4194, -122.4194), 1000.0, 1000.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,5 @@
|
||||
pub mod map_tile;
|
||||
pub mod map_tile;
|
||||
// pub mod grid_field_renderer;
|
||||
// pub mod layers;
|
||||
pub mod color_mapper;
|
||||
pub mod widgets;
|
||||
|
||||
100
src/predefined/widgets.rs
Normal file
100
src/predefined/widgets.rs
Normal file
@ -0,0 +1,100 @@
|
||||
use crate::components::widget::{Widget, WidgetType};
|
||||
use crate::predefined::color_mapper::ColorMapper;
|
||||
use crate::widgets::{AssoElement, Layer};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use num_traits::*;
|
||||
use std::any::Any;
|
||||
use topojson::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ColorBar {
|
||||
padding: [f32; 4],
|
||||
width: f32,
|
||||
height: f32,
|
||||
margin: [i32; 4],
|
||||
}
|
||||
|
||||
impl ColorBar {
|
||||
pub fn new(padding: [f32; 4], size: (f32, f32), margin: [i32; 4]) -> Self {
|
||||
Self {
|
||||
padding,
|
||||
width: size.0,
|
||||
height: size.1,
|
||||
margin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Widget for ColorBar {
|
||||
fn cairo_render(&self, layers: &Vec<Layer>, canvas: >k::cairo::Context, w: f32, h: f32) {
|
||||
let bar_width = 10;
|
||||
let bar_height = h - self.padding[0] - self.padding[2];
|
||||
let (x, y) = (self.padding[3], self.padding[0]);
|
||||
|
||||
let length = layers.len();
|
||||
|
||||
if length == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let first = layers.first().unwrap();
|
||||
|
||||
match first.get_associated_element() {
|
||||
AssoElement::Instant(i) => {
|
||||
let imp = i.imp();
|
||||
}
|
||||
AssoElement::TimeSeries(t) => {
|
||||
let c = t.lock().unwrap();
|
||||
let imp = c.imp();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// for ((i, color), label) in self
|
||||
// .color_list
|
||||
// .iter()
|
||||
// .enumerate()
|
||||
// .zip(self.color_mapper.labels())
|
||||
// {
|
||||
// let y = y + i as f32 * b_h;
|
||||
//
|
||||
// canvas.set_source_rgba(
|
||||
// color.r as f64,
|
||||
// color.g as f64,
|
||||
// color.b as f64,
|
||||
// color.a as f64,
|
||||
// );
|
||||
// canvas.rectangle(x as f64, y as f64, bar_width as f64, b_h as f64);
|
||||
// canvas.fill();
|
||||
//
|
||||
// let extents = canvas.text_extents(&label).unwrap();
|
||||
//
|
||||
// canvas.move_to(
|
||||
// (x + bar_width as f32 + 5.0) as f64,
|
||||
// y as f64 + extents.height() / 2.0 as f64,
|
||||
// );
|
||||
// canvas.set_source_rgba(1.0, 1.0, 1.0, 1.0);
|
||||
// canvas.show_text(&label);
|
||||
// }
|
||||
}
|
||||
|
||||
fn location(&self) -> (gtk::Align, gtk::Align) {
|
||||
(gtk::Align::End, gtk::Align::End)
|
||||
}
|
||||
|
||||
fn size(&self) -> (f32, f32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
fn widget_type(&self) -> WidgetType {
|
||||
WidgetType::Cairo
|
||||
}
|
||||
|
||||
fn margin(&self) -> [i32; 4] {
|
||||
self.margin
|
||||
}
|
||||
|
||||
fn padding(&self) -> [i32; 4] {
|
||||
[10, 10, 10, 10]
|
||||
}
|
||||
}
|
||||
43
src/utils.rs
43
src/utils.rs
@ -11,7 +11,7 @@ use surfman::{
|
||||
GLVersion, NativeConnection, NativeContext, SurfaceAccess, SurfaceType,
|
||||
};
|
||||
|
||||
use crate::widgets::render::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::RUNTIME;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@ -31,47 +31,6 @@ where
|
||||
(xx, yy)
|
||||
}
|
||||
|
||||
pub fn creator() {
|
||||
let connection = Connection::new().unwrap();
|
||||
let adapter = connection.create_adapter().unwrap();
|
||||
let mut device = connection.create_device(&adapter).unwrap();
|
||||
let api = device.gl_api();
|
||||
let descriptor = device
|
||||
.create_context_descriptor(&ContextAttributes {
|
||||
version: GLVersion::new(3, 3),
|
||||
flags: ContextAttributeFlags::empty(),
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let mut context = device.create_context(&descriptor, None).unwrap();
|
||||
let mut surface = device
|
||||
.create_surface(
|
||||
&context,
|
||||
SurfaceAccess::GPUOnly,
|
||||
SurfaceType::Generic {
|
||||
size: Size2D::new(500, 500),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let surface_info = device.surface_info(&surface);
|
||||
device
|
||||
.bind_surface_to_context(&mut context, surface)
|
||||
.expect("Failed to bind surface to context");
|
||||
device.make_context_current(&context).unwrap();
|
||||
use femtovg::renderer::OpenGl;
|
||||
static LOAD_FN: fn(&str) -> *const std::ffi::c_void = |s| epoxy::get_proc_addr(s) as *const _;
|
||||
let (mut renderer, fbo) = unsafe {
|
||||
let renderer = OpenGl::new_from_function(LOAD_FN).expect("Cannot create renderer");
|
||||
let fbo =
|
||||
glow::NativeFramebuffer(NonZeroU32::new(surface_info.framebuffer_object).unwrap());
|
||||
(renderer, fbo)
|
||||
};
|
||||
|
||||
renderer.set_screen_target(Some(fbo));
|
||||
device.destroy_context(&mut context).unwrap();
|
||||
}
|
||||
|
||||
pub fn create_dbz_boundarynorm() -> BoundaryNorm<i8> {
|
||||
BoundaryNorm::new(
|
||||
vec![
|
||||
|
||||
@ -0,0 +1 @@
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
use std::ops::Range;
|
||||
use geo_types::LineString;
|
||||
use crate::coords::Mapper;
|
||||
use geo_types::LineString;
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct CMS {
|
||||
mapper: Mapper,
|
||||
|
||||
@ -4,8 +4,7 @@ use gtk::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct ExteriorWidget {
|
||||
}
|
||||
pub struct ExteriorWidget {}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for ExteriorWidget {
|
||||
@ -13,4 +12,4 @@ impl ObjectSubclass for ExteriorWidget {
|
||||
type Type = super::ExteriorWidget;
|
||||
}
|
||||
|
||||
impl ObjectImpl for ExteriorWidget{}
|
||||
impl ObjectImpl for ExteriorWidget {}
|
||||
|
||||
@ -98,7 +98,7 @@ impl ObjectImpl for Render {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
let area = self.obj();
|
||||
// area.set_has_stencil_buffer(true);
|
||||
area.set_has_stencil_buffer(true);
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
@ -114,7 +114,16 @@ impl ObjectImpl for Render {
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for Render {}
|
||||
impl WidgetImpl for Render {
|
||||
fn realize(&self) {
|
||||
self.parent_realize();
|
||||
}
|
||||
fn unrealize(&self) {
|
||||
self.obj().make_current();
|
||||
self.canvas.replace(None);
|
||||
self.parent_unrealize();
|
||||
}
|
||||
}
|
||||
|
||||
impl GLAreaImpl for Render {
|
||||
fn resize(&self, width: i32, height: i32) {
|
||||
@ -330,6 +339,20 @@ impl Render {
|
||||
target.set_target(TargetType::ImageId(converted));
|
||||
converted
|
||||
}
|
||||
TargetType::NativeBuffer(ref mem) => {
|
||||
let gl_bind = self.glow_context.borrow();
|
||||
let gl = gl_bind.as_ref().unwrap();
|
||||
let flags = femtovg::ImageFlags::empty();
|
||||
let texture = target.native_buffer_to_native_texture(gl, flags);
|
||||
let converted = canvas
|
||||
.create_image_from_native_texture(
|
||||
texture,
|
||||
femtovg::ImageInfo::new(flags, 3000, 3000, femtovg::PixelFormat::Rgba8),
|
||||
)
|
||||
.unwrap();
|
||||
target.set_target(TargetType::ImageId(converted));
|
||||
converted
|
||||
}
|
||||
};
|
||||
|
||||
let painter = Paint::image(id, ox, oy, x, y, 0.0, 1.0);
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use super::super::WindowCoord;
|
||||
use super::layers::Layer;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use super::layers::Layer;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InteriorWidget {
|
||||
|
||||
@ -1,19 +1,14 @@
|
||||
use super::super::Render;
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::pipeline::element::{self, Element, ElementID, Target, TargetType};
|
||||
use crate::errors::PipelineError;
|
||||
use crate::pipeline::element::{self, Target};
|
||||
use crate::pipeline::offscreen_renderer::CanvasWrapper;
|
||||
use crate::{coords::Range, widgets::widget::Widget};
|
||||
use chrono::{prelude::*, DateTime};
|
||||
use femtovg::{renderer::OpenGl, Canvas, ImageId};
|
||||
use femtovg::{renderer::OpenGl, Canvas};
|
||||
use std::{
|
||||
cell::{Ref, RefCell},
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
use core_extensions::SelfOps;
|
||||
use crate::errors::PipelineError;
|
||||
|
||||
type PrepareFunc = Arc<
|
||||
Mutex<
|
||||
@ -29,7 +24,7 @@ pub type LayerImplSync = Arc<Mutex<Box<dyn LayerImpl + Send + Sync>>>;
|
||||
pub enum AssoElement {
|
||||
TimeSeries(Arc<Mutex<element::TimeSeriesElement>>),
|
||||
Instant(element::InstantElement),
|
||||
Test
|
||||
Test,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -70,7 +65,7 @@ impl Layer {
|
||||
|
||||
pub fn draw(&mut self, render: &Render, window_size: (f32, f32)) -> Result<(), PipelineError> {
|
||||
if self.visiable {
|
||||
match self.associated_element{
|
||||
match self.associated_element {
|
||||
AssoElement::Instant(ref mut e) => {
|
||||
e.render(render);
|
||||
}
|
||||
@ -79,34 +74,30 @@ impl Layer {
|
||||
let mut buffer = e.buffer.lock().unwrap();
|
||||
let mut result = buffer.get_mut(&self.time.unwrap()).map(|x| x.as_mut());
|
||||
|
||||
if result.is_none(){
|
||||
return Ok(()) ;
|
||||
if result.is_none() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(result) = result.unwrap(){
|
||||
if let Some(result) = result.unwrap() {
|
||||
let target = result.get_mut_target();
|
||||
|
||||
let mut canvas = render.get_canvas();
|
||||
let mut canvas = canvas.as_mut().unwrap();
|
||||
let (ox, oy) = target.origin(render);
|
||||
let (x, y) = target.size(render);
|
||||
// let mut canvas = render.get_canvas();
|
||||
// let mut canvas = canvas.as_mut().unwrap();
|
||||
// let (ox, oy) = target.origin(render);
|
||||
// let (x, y) = target.size(render);
|
||||
|
||||
let result_id = match target.target {
|
||||
TargetType::ImageId(id) => id,
|
||||
TargetType::Mem(ref mem) => {
|
||||
let converted = canvas
|
||||
.load_image_mem(mem, femtovg::ImageFlags::empty())
|
||||
.unwrap();
|
||||
target.set_target(TargetType::ImageId(converted));
|
||||
converted
|
||||
}
|
||||
};
|
||||
|
||||
let paint = femtovg::Paint::image(result_id, ox, oy, x, y, 0.0, self.alpha);
|
||||
let mut path = femtovg::Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
canvas.fill_path(&path, &paint);
|
||||
render.draw_img(target);
|
||||
|
||||
// let result_id = match target.target {
|
||||
// TargetType::ImageId(id) => id,
|
||||
// TargetType::Mem(ref mem) => {
|
||||
// let converted = canvas
|
||||
// .load_image_mem(mem, femtovg::ImageFlags::empty())
|
||||
// .unwrap();
|
||||
// target.set_target(TargetType::ImageId(converted));
|
||||
// converted
|
||||
// }
|
||||
// };
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -140,5 +131,4 @@ impl Layer {
|
||||
pub fn set_associated_element(&mut self, element: AssoElement) {
|
||||
self.associated_element = element;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,13 +1,11 @@
|
||||
use tracing::info;
|
||||
mod cms;
|
||||
mod exterior;
|
||||
mod imp;
|
||||
mod interior;
|
||||
pub mod predefined;
|
||||
pub mod renders;
|
||||
pub mod widget;
|
||||
|
||||
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
|
||||
use crate::components::messages::MonitorInputMsg;
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::coords::{Mapper, Range};
|
||||
use crate::errors::PipelineError;
|
||||
@ -21,9 +19,7 @@ use gtk::prelude::*;
|
||||
use gtk::subclass::prelude::ObjectSubclassIsExt;
|
||||
use gtk::EventControllerScrollFlags;
|
||||
pub use interior::*;
|
||||
use slippy_map_tiles::Tile;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
@ -251,7 +247,6 @@ impl Render {
|
||||
height,
|
||||
bounds,
|
||||
None,
|
||||
None,
|
||||
))
|
||||
}
|
||||
|
||||
@ -263,6 +258,10 @@ impl Render {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_img(&self, img: &mut Target) {
|
||||
self.imp().draw_target(img);
|
||||
}
|
||||
|
||||
pub fn scale_rate(&self) -> f64 {
|
||||
let status = self.imp().status.borrow();
|
||||
status.scale_rate.unwrap()
|
||||
|
||||
@ -13,8 +13,8 @@ use std::{fmt::Debug, io::Cursor, marker::PhantomData};
|
||||
|
||||
use super::super::renders::DataRenderer;
|
||||
use super::super::{LayerImpl, Render};
|
||||
use crate::{data::Radar2d, utils::meshgrid};
|
||||
use crate::coords::cms::CMS;
|
||||
use crate::{data::Radar2d, utils::meshgrid};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridFieldRenderer<CMAP, T>
|
||||
@ -175,7 +175,7 @@ where
|
||||
let d2_end = data.dim2.view().last().unwrap().clone();
|
||||
|
||||
Target::new(
|
||||
TargetType::Mem(png_data),
|
||||
TargetType::NativeBuffer(png_data),
|
||||
w,
|
||||
h,
|
||||
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
|
||||
|
||||
@ -1,5 +1 @@
|
||||
pub mod color_mapper;
|
||||
pub mod gis;
|
||||
// pub mod grid_field_renderer;
|
||||
// pub mod layers;
|
||||
pub mod widgets;
|
||||
|
||||
@ -1,103 +0,0 @@
|
||||
use super::{
|
||||
super::widget::{Widget as WidgetTrait, WidgetType},
|
||||
super::Layer,
|
||||
color_mapper::ColorMapper,
|
||||
};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use num_traits::*;
|
||||
#[derive(Debug)]
|
||||
pub struct ColorBar<V, T>
|
||||
where
|
||||
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
|
||||
T: ColorMapper<V>,
|
||||
{
|
||||
color_mapper: T,
|
||||
padding: [f32; 4],
|
||||
width: f32,
|
||||
height: f32,
|
||||
margin: [i32; 4],
|
||||
color_list: Vec<femtovg::Color>,
|
||||
phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V, T> ColorBar<V, T>
|
||||
where
|
||||
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64>,
|
||||
T: ColorMapper<V>,
|
||||
{
|
||||
pub fn new(color_mapper: T, padding: [f32; 4], size: (f32, f32), margin: [i32; 4]) -> Self {
|
||||
let (l, ll) = color_mapper.min_max();
|
||||
let invalid = color_mapper.invalid();
|
||||
let colors = color_mapper.map_min_to_max();
|
||||
|
||||
Self {
|
||||
color_list: colors,
|
||||
color_mapper,
|
||||
padding,
|
||||
width: size.0,
|
||||
height: size.1,
|
||||
margin,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V, T> WidgetTrait for ColorBar<V, T>
|
||||
where
|
||||
V: num_traits::NumOps + PartialOrd + FromPrimitive + AsPrimitive<f64> + Send + Sync,
|
||||
T: ColorMapper<V> + 'static,
|
||||
{
|
||||
fn cairo_render(&self, canvas: >k::cairo::Context, w: f32, h: f32) {
|
||||
let bar_width = 10;
|
||||
let bar_height = h - self.padding[0] - self.padding[2];
|
||||
let (x, y) = (self.padding[3], self.padding[0]);
|
||||
let b_h = bar_height / self.color_list.len() as f32;
|
||||
|
||||
for ((i, color), label) in self
|
||||
.color_list
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(self.color_mapper.labels())
|
||||
{
|
||||
let y = y + i as f32 * b_h;
|
||||
|
||||
canvas.set_source_rgba(
|
||||
color.r as f64,
|
||||
color.g as f64,
|
||||
color.b as f64,
|
||||
color.a as f64,
|
||||
);
|
||||
canvas.rectangle(x as f64, y as f64, bar_width as f64, b_h as f64);
|
||||
canvas.fill();
|
||||
|
||||
let extents = canvas.text_extents(&label).unwrap();
|
||||
|
||||
canvas.move_to(
|
||||
(x + bar_width as f32 + 5.0) as f64,
|
||||
y as f64 + extents.height() / 2.0 as f64,
|
||||
);
|
||||
canvas.set_source_rgba(1.0, 1.0, 1.0, 1.0);
|
||||
canvas.show_text(&label);
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> (gtk::Align, gtk::Align) {
|
||||
(gtk::Align::End, gtk::Align::End)
|
||||
}
|
||||
|
||||
fn size(&self) -> (f32, f32) {
|
||||
(self.width, self.height)
|
||||
}
|
||||
|
||||
fn widget_type(&self) -> WidgetType {
|
||||
WidgetType::Cairo
|
||||
}
|
||||
|
||||
fn margin(&self) -> [i32; 4] {
|
||||
self.margin
|
||||
}
|
||||
|
||||
fn padding(&self) -> [i32; 4] {
|
||||
[10, 10, 10, 10]
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
use super::Render;
|
||||
use crate::coords::cms::CMS;
|
||||
use femtovg::{renderer::OpenGl, Canvas};
|
||||
use crate::pipeline::element::Target;
|
||||
use femtovg::{renderer::OpenGl, Canvas};
|
||||
|
||||
pub trait DataRenderer {
|
||||
type Data;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::widgets::widget::Widget;
|
||||
use crate::components::monitor::Widget;
|
||||
use chrono::{prelude::*, Duration};
|
||||
use gtk::glib::{self, prelude::*, ParamSpec, Properties, Value};
|
||||
use gtk::prelude::*;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use self::widget::Widget;
|
||||
use crate::components::monitor::Widget;
|
||||
|
||||
pub use super::*;
|
||||
mod imp;
|
||||
@ -66,7 +66,7 @@ impl WidgetFrame {
|
||||
drawing_area.set_draw_func(clone!(@strong widget => move |_, canvas, x, y| {
|
||||
let widget = widget.borrow();
|
||||
let widget = widget.as_ref().unwrap();
|
||||
widget.cairo_render(canvas,x as f32,y as f32);
|
||||
// widget.cairo_render(canvas,x as f32,y as f32);
|
||||
}));
|
||||
drawing_area.queue_draw();
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user