add more detail
This commit is contained in:
parent
6bcf8ccc84
commit
b70b86e71c
64
Cargo.lock
generated
64
Cargo.lock
generated
@ -285,6 +285,7 @@ dependencies = [
|
||||
"geo",
|
||||
"geo-macros",
|
||||
"geo-types",
|
||||
"geojson 0.24.1",
|
||||
"glib",
|
||||
"glib-build-tools",
|
||||
"glib-macros",
|
||||
@ -307,6 +308,7 @@ dependencies = [
|
||||
"shapefile",
|
||||
"svg",
|
||||
"thiserror",
|
||||
"topojson",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -900,6 +902,32 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geojson"
|
||||
version = "0.23.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c1147be22f66284de4387c43e4abab872e525a528f4d0af4e4e0f231895ea4"
|
||||
dependencies = [
|
||||
"geo-types",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "geojson"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5d728c1df1fbf328d74151efe6cb0586f79ee813346ea981add69bd22c9241b"
|
||||
dependencies = [
|
||||
"geo-types",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
@ -1309,6 +1337,12 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.26"
|
||||
@ -2050,6 +2084,7 @@ dependencies = [
|
||||
"fragile",
|
||||
"futures",
|
||||
"gtk4",
|
||||
"libadwaita",
|
||||
"once_cell",
|
||||
"relm4-macros",
|
||||
"tokio",
|
||||
@ -2141,6 +2176,12 @@ dependencies = [
|
||||
"unicode-script",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "scoped_threadpool"
|
||||
version = "0.1.9"
|
||||
@ -2179,6 +2220,17 @@ dependencies = [
|
||||
"syn 2.0.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.99"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.3"
|
||||
@ -2480,6 +2532,18 @@ dependencies = [
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topojson"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41ed0491c918a501d49a5da86736c9e27666b6f5794fadec0ff8d4842c642b73"
|
||||
dependencies = [
|
||||
"geojson 0.23.0",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.37"
|
||||
|
||||
@ -32,10 +32,12 @@ proj = "0.27.2"
|
||||
image = "0.24.7"
|
||||
anyhow = "1.0.72"
|
||||
proj5 = { version = "0.1.7", features = ["multithreading"] }
|
||||
relm4 = "0.6.1"
|
||||
relm4 = { version = "0.6.1",features=["libadwaita"]}
|
||||
relm4-components = "0.6.1"
|
||||
rstar = "*"
|
||||
geo = "0.26.0"
|
||||
topojson = "0.5.1"
|
||||
geojson = "0.24.1"
|
||||
|
||||
|
||||
|
||||
@ -48,3 +50,4 @@ path = "geo-macros"
|
||||
[dependencies.adw]
|
||||
package = "libadwaita"
|
||||
version = "*"
|
||||
features= ["v1_4"]
|
||||
|
||||
79
back.txt
Normal file
79
back.txt
Normal file
@ -0,0 +1,79 @@
|
||||
|
||||
Layer::geojson_layer_with_path(
|
||||
"/Users/tsuki/Downloads/new_zhejiang.json",
|
||||
true,
|
||||
|_self, c, render, _| {
|
||||
if let Some(json_resources) = _self.get_resources() {
|
||||
if let Resources::GeoJson(geojson) = json_resources.deref() {
|
||||
MapRender::test(&geojson, c, render);
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
Layer::new(true, None, |s, c, render, _| {
|
||||
if let Some(target) = s.render_target() {
|
||||
if let Ok(_) = c.image_size(target.target) {
|
||||
let (x, y) = target.size(render);
|
||||
let (ox, oy) = target.origin(render);
|
||||
let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0);
|
||||
let mut path = Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
c.fill_path(&path, &painter);
|
||||
}
|
||||
} else {
|
||||
let renderer = RadarEchoRenderer::new(BoundaryNorm::new(
|
||||
vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
|
||||
vec![
|
||||
Color::rgb(0, 172, 164),
|
||||
Color::rgb(192, 192, 254),
|
||||
Color::rgb(122, 114, 238),
|
||||
Color::rgb(30, 38, 208),
|
||||
Color::rgb(166, 252, 168),
|
||||
Color::rgb(0, 234, 0),
|
||||
Color::rgb(16, 146, 26),
|
||||
Color::rgb(252, 244, 100),
|
||||
Color::rgb(200, 200, 2),
|
||||
Color::rgb(140, 140, 0),
|
||||
Color::rgb(254, 172, 172),
|
||||
Color::rgb(254, 100, 92),
|
||||
Color::rgb(238, 2, 48),
|
||||
Color::rgb(212, 142, 254),
|
||||
Color::rgb(170, 36, 250),
|
||||
],
|
||||
true,
|
||||
));
|
||||
let loader = Npz;
|
||||
let data: RadarData2d<i8, OwnedRepr<i8>> = loader
|
||||
.load("/Users/tsuki/projects/radar-g/test2.npz")
|
||||
.unwrap();
|
||||
let img = renderer.render(render, c, &data);
|
||||
if let Ok(_) = c.image_size(img.target) {
|
||||
let (x, y) = img.size(render);
|
||||
let (ox, oy) = img.origin(render);
|
||||
println!("{} {} {} {}", x, y, ox, oy);
|
||||
let painter = Paint::image(img.target, ox, oy, x, y, 0.0, 1.0);
|
||||
let mut path = Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
s.set_render_target(img);
|
||||
c.fill_path(&path, &painter);
|
||||
c.flush();
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
############# TEMPLATES ###################
|
||||
// Monitor::new(
|
||||
// Render::new(
|
||||
// Some(Mapper::new(
|
||||
// Proj::new(Mercator::default().build().as_str()).unwrap(),
|
||||
// (119.539..121.135).into(),
|
||||
// (29.13..30.164).into(),
|
||||
// )),
|
||||
// RenderConfig { padding: [50.0;4] }
|
||||
// )
|
||||
// )
|
||||
9
check.py
Normal file
9
check.py
Normal file
@ -0,0 +1,9 @@
|
||||
import numpy as np
|
||||
|
||||
data = np.load("/Users/tsuki/projects/radar-g/test2.npz")
|
||||
|
||||
print(data['lat'][0])
|
||||
print(data['lat'][-1])
|
||||
|
||||
print(data['lon'][0])
|
||||
print(data['lon'][-1])
|
||||
@ -1,9 +1,12 @@
|
||||
use super::{control_panel::ControlPanelModel, render_panel::RenderPanelModel};
|
||||
use gtk::{prelude::{
|
||||
ApplicationExt, ButtonExt, DialogExt, GtkWindowExt, ToggleButtonExt, WidgetExt,
|
||||
}, traits::OrientableExt};
|
||||
use gtk::prelude::GtkApplicationExt;
|
||||
use gtk::{
|
||||
prelude::{ApplicationExt, ButtonExt, DialogExt, GtkWindowExt, ToggleButtonExt, WidgetExt},
|
||||
traits::OrientableExt,
|
||||
};
|
||||
use relm4::*;
|
||||
use adw::prelude::*;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum AppMode {
|
||||
@ -35,14 +38,19 @@ impl SimpleComponent for AppModel {
|
||||
type Output = ();
|
||||
|
||||
view! {
|
||||
main_window = gtk::ApplicationWindow {
|
||||
main_window=adw::ApplicationWindow {
|
||||
set_default_width: 1200,
|
||||
set_default_height: 700,
|
||||
set_default_height: 900,
|
||||
set_focus_on_click:true,
|
||||
set_titlebar: Some(>k::HeaderBar::new()),
|
||||
// set_titlebar: Some(>k::HeaderBar::new()),
|
||||
gtk::Box{
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_valign:gtk::Align::Fill,
|
||||
adw::HeaderBar {
|
||||
#[wrap(Some)]
|
||||
set_title_widget = &adw::WindowTitle {
|
||||
}
|
||||
},
|
||||
model.control.widget(),
|
||||
model.render.widget(),
|
||||
},
|
||||
@ -51,6 +59,21 @@ impl SimpleComponent for AppModel {
|
||||
gtk::Inhibit(true)
|
||||
}
|
||||
}
|
||||
// main_window = gtk::ApplicationWindow {
|
||||
// set_default_width: 1200,
|
||||
// set_default_height: 900,
|
||||
// set_focus_on_click:true,
|
||||
// set_titlebar: Some(>k::HeaderBar::new()),
|
||||
// gtk::Box{
|
||||
// set_orientation: gtk::Orientation::Vertical,
|
||||
// set_valign:gtk::Align::Fill,
|
||||
|
||||
// },
|
||||
// connect_close_request[sender] => move |_| {
|
||||
// sender.input(AppMsg::CloseRequest);
|
||||
// gtk::Inhibit(true)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn init(
|
||||
@ -63,7 +86,7 @@ impl SimpleComponent for AppModel {
|
||||
.forward(sender.input_sender(), |msg| AppMsg::Close);
|
||||
|
||||
let render = RenderPanelModel::builder()
|
||||
.launch(0)
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |a| AppMsg::Close);
|
||||
|
||||
relm4::menu! {
|
||||
|
||||
0
src/components/control_panel/messages.rs
Normal file
0
src/components/control_panel/messages.rs
Normal file
3
src/components/control_panel/mod.rs
Normal file
3
src/components/control_panel/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod control_panel;
|
||||
mod messages;
|
||||
pub use control_panel::*;
|
||||
@ -1,3 +1,6 @@
|
||||
pub mod app;
|
||||
mod control_panel;
|
||||
mod render_panel;
|
||||
|
||||
pub use control_panel::*;
|
||||
pub use render_panel::*;
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
use crate::monitor::Monitor;
|
||||
use crate::coords::proj::Mercator;
|
||||
use crate::render::{
|
||||
BackgroundConfig, BackgroundWidget, ForegroundConfig, ForegroundWidget, Render,
|
||||
};
|
||||
use gtk::prelude::{BoxExt, ButtonExt, GtkWindowExt, OrientableExt, ToggleButtonExt};
|
||||
use relm4::*;
|
||||
|
||||
pub struct RenderPanelModel;
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for RenderPanelModel {
|
||||
type Init = i8;
|
||||
type Output = ();
|
||||
type Input = ();
|
||||
|
||||
view! {
|
||||
#[root]
|
||||
Monitor::new(
|
||||
Render::new(
|
||||
Some(Mercator::new().into())
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: relm4::ComponentSender<Self>,
|
||||
) -> relm4::ComponentParts<Self> {
|
||||
let model = RenderPanelModel {};
|
||||
// Insert the macro code generation here
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||
}
|
||||
12
src/components/render_panel/messages.rs
Normal file
12
src/components/render_panel/messages.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::render::Layer;
|
||||
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum RenderInputMsg {
|
||||
AddLayer(Layer),
|
||||
RemoveLayer(usize),
|
||||
MoveLayer(usize, usize),
|
||||
SetLayerVisibility(usize, bool),
|
||||
EditLayer(usize),
|
||||
}
|
||||
4
src/components/render_panel/mod.rs
Normal file
4
src/components/render_panel/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
mod render_panel;
|
||||
mod messages;
|
||||
mod monitor;
|
||||
pub use render_panel::RenderPanelModel;
|
||||
4
src/components/render_panel/monitor/mod.rs
Normal file
4
src/components/render_panel/monitor/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod monitor;
|
||||
pub mod sidebar;
|
||||
pub mod render;
|
||||
pub use monitor::MonitorModel;
|
||||
46
src/components/render_panel/monitor/monitor.rs
Normal file
46
src/components/render_panel/monitor/monitor.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use super::{render::render::RenderModel, sidebar::sidebar::SideBarModel};
|
||||
use relm4::*;
|
||||
pub struct MonitorModel {
|
||||
render: Controller<RenderModel>,
|
||||
sidebar: Controller<SideBarModel>,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for MonitorModel {
|
||||
type Init = ();
|
||||
type Output = ();
|
||||
type Input = ();
|
||||
|
||||
view! {
|
||||
adw::OverlaySplitView {
|
||||
set_sidebar_position:gtk::PackType::End,
|
||||
#[wrap(Some)]
|
||||
set_sidebar = &adw::NavigationPage::builder().title("sidebar").child(model.sidebar.widget()).build(),
|
||||
#[wrap(Some)]
|
||||
set_content = &adw::NavigationPage::builder().title("content").child(model.render.widget()).build(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: relm4::ComponentSender<Self>,
|
||||
) -> relm4::ComponentParts<Self> {
|
||||
let sidebar: Controller<SideBarModel> = SideBarModel::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |msg| {});
|
||||
let render: Controller<RenderModel> = RenderModel::builder()
|
||||
.launch((None, None))
|
||||
.forward(sender.input_sender(), |msg| {});
|
||||
|
||||
let model = MonitorModel { render, sidebar };
|
||||
|
||||
|
||||
let widgets = view_output!();
|
||||
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||
}
|
||||
2
src/components/render_panel/monitor/render/mod.rs
Normal file
2
src/components/render_panel/monitor/render/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod render;
|
||||
pub use render::*;
|
||||
40
src/components/render_panel/monitor/render/render.rs
Normal file
40
src/components/render_panel/monitor/render/render.rs
Normal file
@ -0,0 +1,40 @@
|
||||
use crate::coords::proj::Mercator;
|
||||
use crate::coords::Mapper;
|
||||
use crate::render::{Render, RenderConfig};
|
||||
use relm4::*;
|
||||
|
||||
pub struct RenderModel {
|
||||
config: RenderConfig,
|
||||
mapper: Mapper,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for RenderModel {
|
||||
type Init = (Option<Mapper>, Option<RenderConfig>);
|
||||
type Output = ();
|
||||
type Input = ();
|
||||
|
||||
view! {
|
||||
Render{
|
||||
set_mapper = model.mapper.clone(),
|
||||
set_cfg = model.config.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: relm4::ComponentSender<Self>,
|
||||
) -> relm4::ComponentParts<Self> {
|
||||
let (mapper, config) = init;
|
||||
let config = config.unwrap_or(RenderConfig::default());
|
||||
let mapper = mapper.unwrap_or(Mercator::default().into());
|
||||
|
||||
let model = RenderModel { config, mapper };
|
||||
let widgets = view_output!();
|
||||
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||
}
|
||||
2
src/components/render_panel/monitor/sidebar/mod.rs
Normal file
2
src/components/render_panel/monitor/sidebar/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod sidebar;
|
||||
pub use sidebar::*;
|
||||
171
src/components/render_panel/monitor/sidebar/sidebar.rs
Normal file
171
src/components/render_panel/monitor/sidebar/sidebar.rs
Normal file
@ -0,0 +1,171 @@
|
||||
use gtk::prelude::*;
|
||||
use relm4::{
|
||||
binding::{Binding, U8Binding},
|
||||
prelude::*,
|
||||
typed_list_view::{RelmListItem, TypedListView},
|
||||
RelmObjectExt,
|
||||
};
|
||||
pub struct SideBarModel {
|
||||
counter: u8,
|
||||
list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Msg {
|
||||
Append,
|
||||
Remove,
|
||||
OnlyShowEven(bool),
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for SideBarModel {
|
||||
type Init = ();
|
||||
type Output = ();
|
||||
type Input = Msg;
|
||||
|
||||
view! {
|
||||
gtk::Box {
|
||||
set_orientation: gtk::Orientation::Vertical,
|
||||
set_spacing: 5,
|
||||
set_margin_all: 5,
|
||||
gtk::Notebook {
|
||||
set_vexpand: true,
|
||||
append_page:(&page_1, Some(&label)),
|
||||
},
|
||||
gtk::Button {
|
||||
set_label: "Append 10 items",
|
||||
connect_clicked => Msg::Append,
|
||||
},
|
||||
|
||||
gtk::Button {
|
||||
set_label: "Remove second item",
|
||||
connect_clicked => Msg::Remove,
|
||||
},
|
||||
gtk::ScrolledWindow {
|
||||
set_vexpand: true,
|
||||
|
||||
#[local_ref]
|
||||
my_view -> gtk::ListView {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: ComponentSender<Self>,
|
||||
) -> ComponentParts<Self> {
|
||||
// Initialize the ListView wrapper
|
||||
let mut list_view_wrapper: TypedListView<MyListItem, gtk::SingleSelection> =
|
||||
TypedListView::with_sorting();
|
||||
|
||||
// Add a filter and disable it
|
||||
list_view_wrapper.add_filter(|item| item.value % 2 == 0);
|
||||
list_view_wrapper.set_filter_status(0, false);
|
||||
|
||||
let model = SideBarModel {
|
||||
counter: 0,
|
||||
list_view_wrapper,
|
||||
};
|
||||
let my_view = &model.list_view_wrapper.view;
|
||||
|
||||
let page_1 = gtk::Box::new(gtk::Orientation::Vertical, 5);
|
||||
let label = gtk::Label::new(Some("Page 1"));
|
||||
|
||||
let widgets = view_output!();
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Self::Input, sender: ComponentSender<Self>) {
|
||||
match message {
|
||||
Msg::Append => {
|
||||
// Add 10 items
|
||||
for _ in 0..10 {
|
||||
self.counter = self.counter.wrapping_add(1);
|
||||
self.list_view_wrapper.append(MyListItem::new(self.counter));
|
||||
}
|
||||
|
||||
// Count up the first item
|
||||
let first_item = self.list_view_wrapper.get(0).unwrap();
|
||||
let first_binding = &mut first_item.borrow_mut().binding;
|
||||
let mut guard = first_binding.guard();
|
||||
*guard += 1;
|
||||
}
|
||||
Msg::Remove => {
|
||||
// Remove the second item
|
||||
self.list_view_wrapper.remove(1);
|
||||
}
|
||||
Msg::OnlyShowEven(show_only_even) => {
|
||||
// Disable or enable the first filter
|
||||
self.list_view_wrapper.set_filter_status(0, show_only_even);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
struct MyListItem {
|
||||
value: u8,
|
||||
binding: U8Binding,
|
||||
}
|
||||
|
||||
impl MyListItem {
|
||||
fn new(value: u8) -> Self {
|
||||
Self {
|
||||
value,
|
||||
binding: U8Binding::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Widgets {
|
||||
label: gtk::Label,
|
||||
label2: gtk::Label,
|
||||
button: gtk::CheckButton,
|
||||
}
|
||||
|
||||
impl Drop for Widgets {
|
||||
fn drop(&mut self) {
|
||||
dbg!(self.label.label());
|
||||
}
|
||||
}
|
||||
|
||||
impl RelmListItem for MyListItem {
|
||||
type Root = gtk::Box;
|
||||
type Widgets = Widgets;
|
||||
|
||||
fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) {
|
||||
relm4::view! {
|
||||
my_box = gtk::Box {
|
||||
#[name = "label"]
|
||||
gtk::Label,
|
||||
|
||||
#[name = "label2"]
|
||||
gtk::Label,
|
||||
|
||||
#[name = "button"]
|
||||
gtk::CheckButton,
|
||||
}
|
||||
}
|
||||
|
||||
let widgets = Widgets {
|
||||
label,
|
||||
label2,
|
||||
button,
|
||||
};
|
||||
|
||||
(my_box, widgets)
|
||||
}
|
||||
|
||||
fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) {
|
||||
let Widgets {
|
||||
label,
|
||||
label2,
|
||||
button,
|
||||
} = widgets;
|
||||
|
||||
label.set_label(&format!("Value: {} ", self.value));
|
||||
label2.add_write_only_binding(&self.binding, "label");
|
||||
button.set_active(self.value % 2 == 0);
|
||||
}
|
||||
}
|
||||
39
src/components/render_panel/render_panel.rs
Normal file
39
src/components/render_panel/render_panel.rs
Normal file
@ -0,0 +1,39 @@
|
||||
use super::messages::RenderInputMsg;
|
||||
use gtk::prelude::*;
|
||||
use super::monitor::MonitorModel;
|
||||
use relm4::*;
|
||||
pub struct RenderPanelModel {
|
||||
monitor: Controller<MonitorModel>,
|
||||
}
|
||||
|
||||
#[relm4::component(pub)]
|
||||
impl SimpleComponent for RenderPanelModel {
|
||||
type Init = ();
|
||||
type Output = ();
|
||||
type Input = ();
|
||||
|
||||
view! {
|
||||
gtk::Box{
|
||||
set_orientation: gtk::Orientation::Horizontal,
|
||||
set_hexpand:true,
|
||||
set_vexpand:true,
|
||||
model.monitor.widget(),
|
||||
}
|
||||
}
|
||||
|
||||
fn init(
|
||||
init: Self::Init,
|
||||
root: &Self::Root,
|
||||
sender: relm4::ComponentSender<Self>,
|
||||
) -> relm4::ComponentParts<Self> {
|
||||
let monitor: Controller<MonitorModel> = MonitorModel::builder()
|
||||
.launch(())
|
||||
.forward(sender.input_sender(), |msg| {});
|
||||
|
||||
let model = RenderPanelModel { monitor };
|
||||
let widgets = view_output!();
|
||||
ComponentParts { model, widgets }
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) {}
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
use crate::render::WindowCoord;
|
||||
use std::borrow::ToOwned;
|
||||
|
||||
use super::{proj::ProjectionS, Range};
|
||||
use geo_types::{coord, Coord as GCoord, LineString};
|
||||
@ -9,12 +10,25 @@ pub struct Mapper {
|
||||
pub range: (Range, Range),
|
||||
bounds: (f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl Clone for Mapper {
|
||||
fn clone(&self) -> Self {
|
||||
let c = self.proj.proj_info();
|
||||
let new_proj = Proj::new(c.definition.unwrap().as_str()).unwrap();
|
||||
Self {
|
||||
proj: new_proj,
|
||||
range: self.range,
|
||||
bounds: self.bounds,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Proj> for Mapper {
|
||||
fn from(proj: Proj) -> Self {
|
||||
let default_range: (Range, Range) = ((-180.0..180.0).into(), (-81.0..81.0).into());
|
||||
let bounds = Self::bound(&proj, default_range.clone()).unwrap();
|
||||
Self {
|
||||
proj: proj,
|
||||
proj,
|
||||
range: (default_range.0.into(), default_range.1.into()),
|
||||
bounds,
|
||||
}
|
||||
@ -33,24 +47,22 @@ impl Mapper {
|
||||
proj: Proj,
|
||||
lon_range: std::ops::Range<f64>,
|
||||
lat_range: std::ops::Range<f64>,
|
||||
scale: f32,
|
||||
translate: (f32, f32),
|
||||
) -> Self {
|
||||
let bounds =
|
||||
Self::bound(&proj, (lon_range.clone().into(), lat_range.clone().into())).unwrap();
|
||||
let range = (lon_range.into(), lat_range.into());
|
||||
Self {
|
||||
proj: proj,
|
||||
proj,
|
||||
range,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fore_map(&self, coord: WindowCoord) -> Result<(f64, f64), ProjError> {
|
||||
pub fn inverse_map(&self, coord: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
||||
let (x, y) = coord;
|
||||
let c = (x as f64) * (self.bounds.1 - self.bounds.0) + self.bounds.0;
|
||||
let d = ((1.0 - y) as f64) * (self.bounds.3 - self.bounds.2) + self.bounds.2;
|
||||
let (lon, lat) = self.proj.project((c, d), true)?;
|
||||
// let c = (x as f64) * (self.bounds.1 - self.bounds.0) + self.bounds.0;
|
||||
// let d = ((1.0 - y) as f64) * (self.bounds.3 - self.bounds.2) + self.bounds.2;
|
||||
let (lon, lat) = self.proj.project((x, y), true)?;
|
||||
Ok((lon.to_degrees(), lat.to_degrees()))
|
||||
}
|
||||
|
||||
@ -60,22 +72,28 @@ impl Mapper {
|
||||
return (x <= lon_range.1 && x >= lon_range.0) && (y <= lat_range.1 && y >= lat_range.0);
|
||||
}
|
||||
|
||||
pub fn get_bounds(&self) -> (f64, f64, f64, f64) {
|
||||
self.bounds
|
||||
}
|
||||
|
||||
pub fn map(&self, point: (f64, f64)) -> Result<(f64, f64), ProjError> {
|
||||
let mut point = point;
|
||||
if !self.point_in_bound(point) {
|
||||
point = (
|
||||
point.0.clamp(self.range.0 .0, self.range.0 .1),
|
||||
point.1.clamp(self.range.1 .0, self.range.1 .1),
|
||||
);
|
||||
}
|
||||
// if !self.point_in_bound(point) {
|
||||
// point = (
|
||||
// point.0.clamp(self.range.0 .0, self.range.0 .1),
|
||||
// point.1.clamp(self.range.1 .0, self.range.1 .1),
|
||||
// );
|
||||
// }
|
||||
let (p1, p2) = self
|
||||
.proj
|
||||
.convert((point.0.to_radians(), point.1.to_radians()))?;
|
||||
|
||||
let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
|
||||
let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
|
||||
// let x = (p1 - self.bounds.0) / (self.bounds.1 - self.bounds.0);
|
||||
// let y = (p2 - self.bounds.2) / (self.bounds.3 - self.bounds.2);
|
||||
|
||||
Ok((x, (1.0 - y)))
|
||||
// Ok((x, (1.0 - y)))
|
||||
//
|
||||
Ok((p1, p2))
|
||||
}
|
||||
|
||||
pub fn set_lon_range(&mut self, range: std::ops::Range<f64>) -> &mut Self {
|
||||
|
||||
@ -17,7 +17,7 @@ pub trait Coord<T: Num> {
|
||||
fn dim2_range(&self) -> (T, T);
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Range(pub f64, pub f64);
|
||||
|
||||
impl Range {
|
||||
|
||||
@ -25,7 +25,23 @@ fn proj_string<'a>(vs: Vec<(&'a str, &'a str)>) -> String {
|
||||
|
||||
impl Mercator {
|
||||
/// Creates a new instance of the `Mercator` projection with default values.
|
||||
pub fn new() -> Self {
|
||||
pub fn new(
|
||||
central_lon: f64,
|
||||
false_easting: f64,
|
||||
false_northing: f64,
|
||||
latitude_true_scale: f64,
|
||||
) -> Self {
|
||||
Self {
|
||||
central_lon,
|
||||
false_easting,
|
||||
false_northing,
|
||||
latitude_true_scale,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Mercator {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
central_lon: 0.0,
|
||||
false_easting: 0.0,
|
||||
@ -54,5 +70,4 @@ impl ProjectionS for Mercator {
|
||||
let _proj_string = proj_string(input);
|
||||
_proj_string
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use crate::errors::DataError;
|
||||
use image::RgbImage;
|
||||
use ndarray::{
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Ix1, Ix2, OwnedRepr, RawDataClone,
|
||||
ViewRepr,
|
||||
s, Array, Array1, Array2, Array3, ArrayBase, Axis, DataMut, Dimension, Ix1, Ix2, OwnedRepr,
|
||||
RawDataClone, ViewRepr,
|
||||
};
|
||||
use npyz::{npz::NpzArchive, Deserialize};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
@ -30,9 +30,29 @@ where
|
||||
pub dim1: ArrayBase<OwnedRepr<X>, I>,
|
||||
pub dim2: ArrayBase<OwnedRepr<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>
|
||||
where
|
||||
T: Num + Clone + PartialEq + PartialOrd + Debug,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
X: Num + Debug,
|
||||
Y: Num + Debug,
|
||||
I: Dimension,
|
||||
{
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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)?)
|
||||
@ -66,6 +86,7 @@ where
|
||||
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(),
|
||||
};
|
||||
}
|
||||
@ -115,6 +136,7 @@ where
|
||||
return Radar2d {
|
||||
dim1,
|
||||
dim2,
|
||||
fill_value: self.fill_value.clone(),
|
||||
data: output,
|
||||
coord_type: self.coord_type.clone(),
|
||||
};
|
||||
@ -208,24 +230,28 @@ where
|
||||
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,
|
||||
},
|
||||
];
|
||||
@ -289,7 +315,7 @@ impl Npz {
|
||||
|
||||
impl<T> DataLoader<T, RadarData2d<T, OwnedRepr<T>>> for Npz
|
||||
where
|
||||
T: Num + Clone + Deserialize,
|
||||
T: Num + Clone + Deserialize + FromPrimitive,
|
||||
T: PartialEq + PartialOrd,
|
||||
{
|
||||
fn load<P: AsRef<Path>>(&self, path: P) -> Result<RadarData2d<T, OwnedRepr<T>>, DataError> {
|
||||
@ -301,6 +327,7 @@ where
|
||||
Ok(RadarData2d {
|
||||
dim1: dim1,
|
||||
dim2: dim2,
|
||||
fill_value: T::from_f64(-125.0).unwrap(),
|
||||
data: value,
|
||||
coord_type: CoordType::LatLon,
|
||||
})
|
||||
@ -473,7 +500,7 @@ pub enum DownSampleMeth {
|
||||
VAR,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CoordType {
|
||||
Polar,
|
||||
LatLon,
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
#![allow(deprecated)]
|
||||
|
||||
use coords::proj::Mercator;
|
||||
use coords::Mapper;
|
||||
use data::{Npz, Radar2d};
|
||||
use femtovg::{Color, Paint};
|
||||
mod utils;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{gio, glib, Application, ApplicationWindow};
|
||||
use relm4::menu;
|
||||
use relm4::RelmApp;
|
||||
use std::ptr;
|
||||
use relm4::menu;
|
||||
mod components;
|
||||
mod coords;
|
||||
mod data;
|
||||
|
||||
@ -19,6 +19,11 @@ impl ObjectSubclass for Monitor {
|
||||
|
||||
impl ObjectImpl for Monitor {}
|
||||
|
||||
impl WidgetImpl for Monitor {}
|
||||
impl WidgetImpl for Monitor {
|
||||
fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
|
||||
println!("new width {}", width);
|
||||
self.parent_size_allocate(width, height, baseline)
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxImpl for Monitor {}
|
||||
|
||||
@ -1,15 +1,11 @@
|
||||
mod imp;
|
||||
use crate::data::RadarData2d;
|
||||
use crate::render::Render;
|
||||
use adw::prelude::{GLAreaExt, GestureDragExt};
|
||||
use glib::clone;
|
||||
use crate::render::{Render, RenderMotion, RenderConfig};
|
||||
use adw::prelude::{ButtonExt, GLAreaExt, GestureDragExt};
|
||||
use glib::subclass::prelude::*;
|
||||
use gtk::glib;
|
||||
use glib::{clone, ObjectExt};
|
||||
use gtk::traits::WidgetExt;
|
||||
use gtk::{glib, AspectFrame};
|
||||
use gtk::{EventControllerScrollFlags, Inhibit};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use proj::ProjError;
|
||||
use std::fmt::Debug;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct Monitor(ObjectSubclass<imp::Monitor>)
|
||||
@ -17,13 +13,18 @@ glib::wrapper! {
|
||||
}
|
||||
|
||||
impl Monitor {
|
||||
pub fn new(render: Render) -> Self {
|
||||
pub fn new(config:RenderConfig) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
||||
|
||||
let render = Render::new(None, config);
|
||||
|
||||
pointer_location_detecture.connect_motion(
|
||||
clone!(@weak this as s, @weak render as r => move |_context, x, y| {
|
||||
r.update_status(|s| {
|
||||
s.pointer_location = (x as f32, y as f32);
|
||||
let dpi = r.scale_factor();
|
||||
let (_,h) = s.window_size;
|
||||
s.pointer_location = (x as f32 * dpi as f32, h as f32 - y as f32 * dpi as f32);
|
||||
});
|
||||
}
|
||||
),
|
||||
@ -31,13 +32,9 @@ impl Monitor {
|
||||
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
||||
scale_detecture.connect_scroll(clone!(
|
||||
@weak this as s, @weak render as r => @default-panic,move |_context, _x, y| {
|
||||
let renderer = s.imp().renderer.borrow();
|
||||
let mut scale = 1.0;
|
||||
r.update_status(|status|{
|
||||
let current_scale = status.scale;
|
||||
let s = (current_scale - y as f32 / 5.0).max(1.0).min(5.0);
|
||||
status.scale = s;
|
||||
scale = s;
|
||||
status.scale = y as f32;
|
||||
status.motion = RenderMotion::Scale;
|
||||
});
|
||||
r.queue_render();
|
||||
Inhibit(false)
|
||||
@ -49,44 +46,47 @@ impl Monitor {
|
||||
@weak render as r => move |this, _, _| {
|
||||
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
|
||||
r.update_status(|s| {
|
||||
s.updated_translate = ( - ox as f32, -oy as f32);
|
||||
s.translate = Some((-ox as f32 * 1.35, oy as f32 * 1.35));
|
||||
s.motion = RenderMotion::Translate;
|
||||
});
|
||||
r.queue_render();
|
||||
}));
|
||||
|
||||
drag_detecture.connect_drag_end(clone!(
|
||||
@weak render as r => move |this,_,_|{
|
||||
let t = r.imp().translate();
|
||||
@weak render as r => move |_,_,_|{
|
||||
r.update_status(|cfg| {
|
||||
cfg.translate = t ;
|
||||
cfg.updated_translate = (0.0,0.0);
|
||||
cfg.translate = None;
|
||||
cfg.motion = RenderMotion::Translate;
|
||||
})
|
||||
}
|
||||
r.queue_render();
|
||||
));
|
||||
|
||||
let split_view = adw::OverlaySplitView::new();
|
||||
let sidebar = adw::NavigationPage::builder().title("sidebar").child(>k::ListView::builder().build()).build();
|
||||
let content = adw::NavigationPage::builder().title("content").child(&render).build();
|
||||
|
||||
split_view.set_sidebar_position(gtk::PackType::End);
|
||||
|
||||
split_view.set_sidebar(Some(&sidebar));
|
||||
split_view.set_content(Some(&content));
|
||||
|
||||
render.add_controller(pointer_location_detecture);
|
||||
render.add_controller(scale_detecture);
|
||||
render.add_controller(drag_detecture);
|
||||
render.set_hexpand(true);
|
||||
render.set_vexpand(true);
|
||||
render.set_parent(&this);
|
||||
// render.set_hexpand(true);
|
||||
// render.set_vexpand(true);
|
||||
|
||||
split_view.set_parent(&this);
|
||||
split_view.set_hexpand(true);
|
||||
split_view.set_vexpand(true);
|
||||
|
||||
// paned.set_parent(&this);
|
||||
// render.set_parent(&this);
|
||||
// aspect_frame.set_hexpand(true);
|
||||
// aspect_frame.set_vexpand(true);
|
||||
// aspect_frame.set_parent(&this);
|
||||
this.imp().renderer.replace(render);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn load_data_2d<T, Raw>(&self, data: RadarData2d<T, Raw>) -> Result<(), ProjError>
|
||||
where
|
||||
T: Num
|
||||
+ Clone
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ AsPrimitive<i8>
|
||||
+ AsPrimitive<f64>
|
||||
+ Debug
|
||||
+ FromPrimitive,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
let renderer = self.imp().renderer.borrow();
|
||||
renderer.load_data_2d(data)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
45
src/render/cms.rs
Normal file
45
src/render/cms.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use geo_types::LineString;
|
||||
|
||||
use crate::coords::Mapper;
|
||||
|
||||
pub struct CMS {
|
||||
mapper: Mapper,
|
||||
window_size: (f32, f32),
|
||||
bounds: (f64, f64, f64, f64),
|
||||
}
|
||||
|
||||
impl CMS {
|
||||
pub fn new(mapper: Mapper, window_size: (f32, f32)) -> Self {
|
||||
let bounds = mapper.get_bounds();
|
||||
Self {
|
||||
mapper,
|
||||
window_size,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||
self.mapper.map(loc).ok().map(|(x, y)| {
|
||||
// println!("x: {}, y: {}", x, y);
|
||||
let (w, h) = self.window_size;
|
||||
let (w, h) = (w as f64, h as f64);
|
||||
let (x, y) = (x - self.bounds.0, y - self.bounds.2);
|
||||
let (x, y) = (
|
||||
x / (self.bounds.1 - self.bounds.0),
|
||||
1.0 - y / (self.bounds.3 - self.bounds.2),
|
||||
);
|
||||
let (x, y) = (x * w, y * h);
|
||||
(x as f32, y as f32)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ring_map(&self, line: &LineString) -> Option<LineString<f32>> {
|
||||
Some(
|
||||
line.points()
|
||||
.into_iter()
|
||||
.map(|p| self.map((p.x(), p.y())).unwrap())
|
||||
.collect::<Vec<_>>()
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
mod imp;
|
||||
use crate::coords::{Mapper, Range};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use geo_types::{line_string, LineString, Point};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use std::cell::Ref;
|
||||
|
||||
use super::imp::{RenderStatus, RenderConfig};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ExteriorWidget(ObjectSubclass<imp::ExteriorWidget>);
|
||||
}
|
||||
|
||||
impl Default for ExteriorWidget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExteriorWidget {
|
||||
pub fn new() -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this
|
||||
}
|
||||
|
||||
pub(super) fn draw(&self, canvas: &mut Canvas<OpenGl>, mapper: Ref<'_, Mapper>, status:Ref<'_, RenderStatus>, _c:Ref<'_, RenderConfig>) {
|
||||
let imp = self.imp();
|
||||
let canvas_width = canvas.width();
|
||||
let canvas_height = canvas.height();
|
||||
let padding = [30.0,30.0,30.0,30.0];
|
||||
|
||||
let w = canvas_width - padding[1] - padding[3];
|
||||
let h = canvas_height - padding[0] - padding[2];
|
||||
|
||||
let paint = Paint::color(Color::white());
|
||||
let mut path = Path::new();
|
||||
path.rect(padding[3], padding[0], w, h);
|
||||
canvas.stroke_path(&path, &paint);
|
||||
}
|
||||
}
|
||||
@ -1,193 +0,0 @@
|
||||
mod imp;
|
||||
use crate::coords::Mapper;
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use femtovg::{Color, FontId, ImageFlags, ImageId, Paint};
|
||||
use geo_types::{LineString, Point};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::glib;
|
||||
use ndarray::{s, Array2, Axis, Zip};
|
||||
use std::cell::Ref;
|
||||
|
||||
pub use self::imp::ForegroundConfig;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ForegroundWidget(ObjectSubclass<imp::ForegroundWidget>);
|
||||
}
|
||||
|
||||
impl Default for ForegroundWidget {
|
||||
fn default() -> Self {
|
||||
Self::new(ForegroundConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl ForegroundWidget {
|
||||
pub fn new(config: ForegroundConfig) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
let imp = this.imp();
|
||||
imp.config.replace(config);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32, mapper: Ref<'_, Mapper>) {
|
||||
let canvas_width = canvas.width();
|
||||
let canvas_height = canvas.height();
|
||||
let config = self.imp().config.borrow();
|
||||
|
||||
let colors = self.imp().color.borrow();
|
||||
let data = self.imp().data.borrow();
|
||||
|
||||
if self.imp().image.borrow().is_none() {
|
||||
println!("rebuild image");
|
||||
let img_id = canvas
|
||||
.create_image_empty(
|
||||
canvas_width as usize,
|
||||
canvas_height as usize,
|
||||
femtovg::PixelFormat::Rgb8,
|
||||
ImageFlags::empty(),
|
||||
)
|
||||
.unwrap();
|
||||
if let Ok(v) = canvas.image_size(img_id) {
|
||||
println!("create image: {:?}", v);
|
||||
canvas.set_render_target(femtovg::RenderTarget::Image(img_id));
|
||||
let colors = self.imp().color.borrow();
|
||||
let data = self.imp().data.borrow();
|
||||
if colors.is_some() && data.is_some() {
|
||||
for (i, c) in colors
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.t()
|
||||
.iter()
|
||||
.zip(data.as_ref().unwrap().iter())
|
||||
{
|
||||
if i.is_none() {
|
||||
continue;
|
||||
}
|
||||
let pts = c.points().collect::<Vec<Point>>();
|
||||
let first_point = pts[0];
|
||||
let mut path = Path::new();
|
||||
path.move_to(
|
||||
first_point.x() as f32 * v.0 as f32 + 0.8,
|
||||
first_point.y() as f32 * v.1 as f32 + 0.8,
|
||||
);
|
||||
|
||||
pts[1..].into_iter().for_each(|p| {
|
||||
path.line_to(p.x() as f32 * v.0 as f32, p.y() as f32 * v.1 as f32)
|
||||
});
|
||||
let c = i.unwrap();
|
||||
canvas.fill_path(&path, &Paint::color(c));
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().image.replace(Some(img_id));
|
||||
}
|
||||
}
|
||||
|
||||
canvas.set_render_target(femtovg::RenderTarget::Screen);
|
||||
|
||||
let mut path = Path::new();
|
||||
path.rect(0.0, 0.0, canvas_width, canvas_height);
|
||||
|
||||
canvas.fill_path(
|
||||
&path,
|
||||
&Paint::image(
|
||||
self.imp().image.borrow().as_ref().unwrap().to_owned(),
|
||||
0.0,
|
||||
0.0,
|
||||
canvas_width,
|
||||
canvas_height,
|
||||
0.0,
|
||||
1.0,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
pub(super) fn set_dims(&self, dims: (Array2<f64>, Array2<f64>)) {
|
||||
self.imp().dim1.replace(Some(dims.0));
|
||||
self.imp().dim2.replace(Some(dims.1));
|
||||
}
|
||||
|
||||
pub(super) fn set_image(&self, image: ImageId) {
|
||||
self.imp().image.replace(Some(image));
|
||||
}
|
||||
|
||||
pub fn set_colors(&self, colors: Array2<Option<Color>>) {
|
||||
self.imp().color.replace(Some(colors));
|
||||
}
|
||||
|
||||
pub fn set_data(&self, data: Array2<LineString>) {
|
||||
self.imp().data.replace(Some(data));
|
||||
}
|
||||
}
|
||||
|
||||
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
||||
// let colors = vec![
|
||||
// Color::rgb(0, 172, 164),
|
||||
// Color::rgb(192, 192, 254),
|
||||
// Color::rgb(122, 114, 238),
|
||||
// Color::rgb(30, 38, 208),
|
||||
// Color::rgb(166, 252, 168),
|
||||
// Color::rgb(0, 234, 0),
|
||||
// Color::rgb(16, 146, 26),
|
||||
// Color::rgb(252, 244, 100),
|
||||
// Color::rgb(200, 200, 2),
|
||||
// Color::rgb(140, 140, 0),
|
||||
// Color::rgb(254, 172, 172),
|
||||
// Color::rgb(254, 100, 92),
|
||||
// Color::rgb(238, 2, 48),
|
||||
// Color::rgb(212, 142, 254),
|
||||
// Color::rgb(170, 36, 250),
|
||||
// ];
|
||||
|
||||
// Zip::from(c).and(d).and(e).for_each(|lon, lat, d| {
|
||||
// if *d < -5 || *d > 70 {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// let left_bottom = mapper.map((*lon, *lat)).unwrap();
|
||||
// let right_top = mapper.map((lon + 0.005, lat + 0.005)).unwrap();
|
||||
|
||||
// // let (lon1, lat1) = (
|
||||
// // left_bottom.0 * canvas_width as f64,
|
||||
// // left_bottom.1 * canvas_height as f64,
|
||||
// // );
|
||||
|
||||
// let color = get(&levels, &colors, *d);
|
||||
// //
|
||||
// // let (lon2, lat2) = (
|
||||
// // right_top.0 * canvas_width as f64,
|
||||
// // right_top.1 * canvas_height as f64,
|
||||
// // );
|
||||
|
||||
// let line_string: LineString = vec![
|
||||
// (left_bottom.0, left_bottom.1),
|
||||
// (right_top.0, left_bottom.1),
|
||||
// (right_top.0, right_top.1),
|
||||
// (left_bottom.0, right_top.1),
|
||||
// (left_bottom.0, left_bottom.1),
|
||||
// ]
|
||||
// .into();
|
||||
|
||||
// // let line_string: LineString = vec![
|
||||
// // (lon1, lat1),
|
||||
// // (lon2, lat1),
|
||||
// // (lon2, lat2),
|
||||
// // (lon1, lat2),
|
||||
// // (lon1, lat1),
|
||||
// // ]
|
||||
// // .into();
|
||||
|
||||
// let res = mapper.ring_map(&line_string).unwrap();
|
||||
|
||||
// let mut path = Path::new();
|
||||
|
||||
// path.move_to(
|
||||
// left_bottom.0 as f32 * canvas_width,
|
||||
// left_bottom.1 as f32 * canvas_height,
|
||||
// );
|
||||
|
||||
// for p in line_string.points() {
|
||||
// // path.move_to(p.x() as f32 * canvas_width, p.y() as f32 * canvas_height);
|
||||
// path.line_to(p.x() as f32 * canvas_width, p.y() as f32 * canvas_height);
|
||||
// }
|
||||
// canvas.fill_path(&path, &Paint::color(color));
|
||||
// });
|
||||
@ -1,13 +1,10 @@
|
||||
use super::background::BackgroundWidget;
|
||||
use super::exterior::ExteriorWidget;
|
||||
use super::interior::InteriorWidget;
|
||||
use super::foreground::ForegroundWidget;
|
||||
use super::layer::foreground::exterior::ExteriorWidget;
|
||||
use super::layer::foreground::interior::InteriorWidget;
|
||||
use super::WindowCoord;
|
||||
use crate::coords::proj::Mercator;
|
||||
use crate::coords::Mapper;
|
||||
use femtovg::{Color, FontId, Paint, Path, Transform2D};
|
||||
use gtk::glib;
|
||||
use gtk::prelude::WidgetExtManual;
|
||||
use gtk::subclass::prelude::*;
|
||||
use gtk::traits::{GLAreaExt, WidgetExt};
|
||||
use ndarray::Array2;
|
||||
@ -16,18 +13,32 @@ use std::num::NonZeroU32;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RenderConfig {
|
||||
pub dim1: Option<Array2<f64>>,
|
||||
pub dim2: Option<Array2<f64>>,
|
||||
pub padding: [f32; 4],
|
||||
// pub pointer_lon_lat: (f64, f64),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderMotion {
|
||||
Translate,
|
||||
Scale,
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for RenderMotion {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct RenderStatus{
|
||||
pub scale: f32,
|
||||
pub struct RenderStatus {
|
||||
pub window_size: (i32, i32),
|
||||
pub(super) scale_rate: Option<f64>,
|
||||
translation: Option<(f64, f64)>,
|
||||
init_translation: (f64, f64),
|
||||
pub pointer_location: WindowCoord,
|
||||
pub translate: WindowCoord,
|
||||
pub updated_translate: WindowCoord,
|
||||
pub motion: RenderMotion,
|
||||
pub scale: f32,
|
||||
pub translate: Option<(f32, f32)>,
|
||||
}
|
||||
|
||||
struct Fonts {
|
||||
@ -52,7 +63,7 @@ impl Default for Render {
|
||||
interior: RefCell::new(InteriorWidget::default()),
|
||||
config: RefCell::new(RenderConfig::default()),
|
||||
status: RefCell::new(RenderStatus::default()),
|
||||
mapper: RefCell::new(Mercator::new().into()),
|
||||
mapper: RefCell::new(Mercator::default().into()),
|
||||
canvas: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
@ -71,10 +82,6 @@ impl ObjectImpl for Render {
|
||||
self.parent_constructed();
|
||||
let area = self.obj();
|
||||
area.set_has_stencil_buffer(true);
|
||||
// area.add_tick_callback(|area, _| {
|
||||
// area.queue_render();
|
||||
// glib::Continue(true)
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +90,7 @@ impl WidgetImpl for Render {}
|
||||
|
||||
impl GLAreaImpl for Render {
|
||||
fn resize(&self, width: i32, height: i32) {
|
||||
self.status.borrow_mut().window_size = (width, height);
|
||||
self.ensure_canvas();
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
let canvas = canvas.as_mut().unwrap();
|
||||
@ -91,16 +99,33 @@ impl GLAreaImpl for Render {
|
||||
height as u32,
|
||||
self.obj().scale_factor() as f32,
|
||||
);
|
||||
|
||||
let translation = self.status.borrow().translation;
|
||||
let scale_rate = self.status.borrow().scale_rate;
|
||||
let mapper = self.mapper.borrow();
|
||||
|
||||
if let None = translation {
|
||||
let mut status = self.status.borrow_mut();
|
||||
status.translation = Some((mapper.get_bounds().0, mapper.get_bounds().2));
|
||||
status.init_translation = (mapper.get_bounds().0, mapper.get_bounds().2);
|
||||
}
|
||||
|
||||
if let None = scale_rate {
|
||||
let scale = (mapper.get_bounds().3 - mapper.get_bounds().2) / canvas.height() as f64;
|
||||
self.status.borrow_mut().scale_rate = Some(scale);
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, context: >k::gdk::GLContext) -> bool {
|
||||
self.ensure_canvas();
|
||||
|
||||
let mut canvas = self.canvas.borrow_mut();
|
||||
let canvas = canvas.as_mut().unwrap();
|
||||
|
||||
let dpi = self.obj().scale_factor();
|
||||
let w = canvas.width();
|
||||
let h = canvas.height();
|
||||
|
||||
let configs = self.config.borrow();
|
||||
|
||||
canvas.clear_rect(
|
||||
@ -111,34 +136,44 @@ impl GLAreaImpl for Render {
|
||||
Color::rgba(0, 0, 0, 255),
|
||||
);
|
||||
|
||||
let mapper = self.mapper.borrow();
|
||||
{
|
||||
let mut status = self.status.borrow_mut();
|
||||
match status.motion {
|
||||
RenderMotion::Translate => {
|
||||
if let Some((x, y)) = status.translate {
|
||||
let (ix, iy) = status.init_translation;
|
||||
status.translation = Some((
|
||||
ix + x as f64 * status.scale_rate.unwrap(),
|
||||
iy + y as f64 * status.scale_rate.unwrap(),
|
||||
));
|
||||
} else {
|
||||
status.init_translation = status.translation.unwrap();
|
||||
}
|
||||
}
|
||||
RenderMotion::Scale => {
|
||||
let scale_rate = status.scale_rate.unwrap();
|
||||
let scale_flag = status.scale as f64;
|
||||
let step = scale_rate * 0.1;
|
||||
|
||||
self.interior.borrow().draw(canvas, mapper, self.status.borrow(), configs);
|
||||
let (tx, ty) = status.translation.unwrap();
|
||||
let (px, py) = status.pointer_location;
|
||||
|
||||
// let (lon_range, lat_range) = mapper.range.clone();
|
||||
// {
|
||||
// let background_widget = self.background.borrow_mut();
|
||||
// background_widget.set_lat_lines(lat_range.key_points(9));
|
||||
// }
|
||||
// {
|
||||
// let background_widget = self.background.borrow_mut();
|
||||
// background_widget.set_lon_lines(lon_range.key_points(20));
|
||||
// }
|
||||
// let translate = self.translate();
|
||||
let scaled = scale_rate + scale_flag * step;
|
||||
status.scale_rate = Some(scaled);
|
||||
let sx = scale_flag * step * px as f64;
|
||||
let sy = scale_flag * step * py as f64;
|
||||
status.translation = Some((tx - sx, ty - sy));
|
||||
status.init_translation = status.translation.unwrap();
|
||||
}
|
||||
RenderMotion::None => {}
|
||||
}
|
||||
}
|
||||
|
||||
// self.exterior.borrow().draw(canvas,self.mapper.borrow());
|
||||
// self.foreground
|
||||
// .borrow()
|
||||
// .draw(canvas, configs.scale, dpi, self.mapper.borrow());
|
||||
self.interior
|
||||
.borrow()
|
||||
.draw(canvas, &self.obj(), self.status.borrow(), configs);
|
||||
|
||||
// self.background.borrow().draw(
|
||||
// canvas,
|
||||
// configs.scale,
|
||||
// dpi,
|
||||
// translate,
|
||||
// self.mapper.borrow(),
|
||||
// (lon_range, lat_range),
|
||||
// );
|
||||
self.exterior.borrow().draw(canvas, &self.obj());
|
||||
canvas.flush();
|
||||
true
|
||||
}
|
||||
@ -178,11 +213,74 @@ impl Render {
|
||||
self.canvas.replace(Some(canvas));
|
||||
}
|
||||
|
||||
pub fn translate(&self) -> WindowCoord {
|
||||
let cfg = self.status.borrow();
|
||||
let (rx, ry) = cfg.translate;
|
||||
let (ux, uy) = cfg.updated_translate;
|
||||
pub(super) fn window_size(&self) -> Option<(i32, i32)> {
|
||||
Some(self.status.borrow().window_size)
|
||||
}
|
||||
|
||||
return (rx + ux, ry + uy);
|
||||
pub(super) fn window_range(&self) -> Option<((f64, f64), (f64, f64))> {
|
||||
let padding = self.padding();
|
||||
let (w, h) = self.window_size().unwrap();
|
||||
let (w, h) = (
|
||||
w as f32 - padding[1] - padding[3],
|
||||
h as f32 - padding[0] - padding[2],
|
||||
);
|
||||
let (w, h) = (w as f64, h as f64);
|
||||
|
||||
let mapper = self.mapper.borrow();
|
||||
|
||||
let status = self.status.borrow();
|
||||
status.translation.and_then(|(tx, ty)| {
|
||||
status.scale_rate.and_then(|scale| {
|
||||
let (x1, y1) = (tx + padding[3] as f64, ty + padding[2] as f64);
|
||||
let (x2, y2) = (tx + w * scale + padding[3] as f64, ty + h * scale + padding[2] as f64);
|
||||
let (x1, y1) = mapper.inverse_map((x1, y1)).unwrap();
|
||||
let (x2, y2) = mapper.inverse_map((x2, y2)).unwrap();
|
||||
Some(((x1, x2), (y1, y2)))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn inverse_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
|
||||
let (x, y) = loc;
|
||||
// let (_, h) = self.window_size().unwrap();
|
||||
let status = self.status.borrow();
|
||||
status.translation.and_then(|(tx, ty)| {
|
||||
status.scale_rate.and_then(|scale| {
|
||||
let (x, y) = (x as f64, y as f64);
|
||||
Some((tx + x * scale, (ty + y * scale)))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
fn padding(&self) -> [f32; 4] {
|
||||
self.config.borrow().padding
|
||||
}
|
||||
|
||||
pub(super) fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||
// let padding = self.padding();
|
||||
// let left_padding = padding[3];
|
||||
// let bottom_padding = padding[2];
|
||||
let (x, y) = loc;
|
||||
let (_, h) = self.window_size().unwrap();
|
||||
let status = self.status.borrow();
|
||||
status.translation.and_then(|(tx, ty)| {
|
||||
status.scale_rate.and_then(|scale| {
|
||||
Some((
|
||||
(x - tx as f64) as f32 / scale as f32,
|
||||
h as f32 - (y - ty as f64) as f32 / scale as f32,
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn set_translation(&self, translation: (f64, f64)) {
|
||||
let mut status = self.status.borrow_mut();
|
||||
status.translation = Some(translation);
|
||||
}
|
||||
|
||||
fn pointer_loc(&self) -> (f32, f32) {
|
||||
let (x, y) = self.status.borrow().pointer_location.clone();
|
||||
let (_, h) = self.window_size().unwrap();
|
||||
(x, h as f32 - y)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
mod imp;
|
||||
use crate::coords::{Mapper, Range};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use geo_types::{line_string, LineString, Point};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use std::cell::Ref;
|
||||
|
||||
use super::imp::{RenderConfig, RenderStatus};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct InteriorWidget(ObjectSubclass<imp::InteriorWidget>);
|
||||
}
|
||||
|
||||
impl Default for InteriorWidget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl InteriorWidget {
|
||||
pub fn new() -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this
|
||||
}
|
||||
|
||||
pub(super) fn draw(
|
||||
&self,
|
||||
canvas: &mut Canvas<OpenGl>,
|
||||
mapper: Ref<'_, Mapper>,
|
||||
status: Ref<'_, RenderStatus>,
|
||||
_c: Ref<'_, RenderConfig>,
|
||||
) {
|
||||
let imp = self.imp();
|
||||
let canvas_width = canvas.width();
|
||||
let canvas_height = canvas.height();
|
||||
|
||||
let padding = _c.padding;
|
||||
|
||||
let w = canvas_width - padding[1] - padding[3];
|
||||
let h = canvas_height - padding[0] - padding[2];
|
||||
let scale = status.scale;
|
||||
let dpi = 2;
|
||||
|
||||
let (tx, ty) = status.translate;
|
||||
let range = mapper.range.clone();
|
||||
|
||||
self.imp().background.borrow().draw(
|
||||
canvas,
|
||||
scale,
|
||||
dpi,
|
||||
w,
|
||||
h,
|
||||
(tx + padding[3], ty + padding[0]),
|
||||
mapper,
|
||||
range,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -80,7 +80,7 @@ impl BackgroundWidget {
|
||||
.map(|p| {
|
||||
(
|
||||
p.x() as f32 * canvas_width * scale - translate.0,
|
||||
(1.0 - p.y() as f32) * canvas_height * scale - translate.1,
|
||||
p.y() as f32 * canvas_height * scale - translate.1,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
@ -136,7 +136,7 @@ impl BackgroundWidget {
|
||||
.map(|p| {
|
||||
(
|
||||
p.x() as f32 * canvas_width * scale - translate.0,
|
||||
(1.0 - p.y()) as f32 * canvas_height * scale - translate.1,
|
||||
p.y() as f32 * canvas_height * scale - translate.1,
|
||||
)
|
||||
.into()
|
||||
})
|
||||
@ -222,7 +222,7 @@ impl BackgroundWidget {
|
||||
let (x, y) = mapper.map(point).unwrap();
|
||||
(
|
||||
x as f32 * canvas_width * scale - translate.0,
|
||||
(1.0 - y as f32) * canvas_height * scale - translate.1,
|
||||
y as f32 * canvas_height * scale - translate.1,
|
||||
)
|
||||
}
|
||||
|
||||
89
src/render/layer/foreground/exterior/mod.rs
Normal file
89
src/render/layer/foreground/exterior/mod.rs
Normal file
@ -0,0 +1,89 @@
|
||||
mod imp;
|
||||
use crate::{coords::Range, render::Render};
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ExteriorWidget(ObjectSubclass<imp::ExteriorWidget>);
|
||||
}
|
||||
|
||||
impl Default for ExteriorWidget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExteriorWidget {
|
||||
pub fn new() -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) {
|
||||
let padding = render.imp().config.borrow().padding;
|
||||
let (w, h) = render.window_size();
|
||||
let (w, h) = (w as f32, h as f32);
|
||||
let origins = [
|
||||
(0.0, 0.0),
|
||||
(w - padding[1], 0.0),
|
||||
(0.0, h - padding[2]),
|
||||
(0.0, 0.0),
|
||||
];
|
||||
let whs = [
|
||||
(w, padding[0]),
|
||||
(padding[1], h),
|
||||
(w, padding[2]),
|
||||
(padding[3], h),
|
||||
];
|
||||
|
||||
let painter = Paint::color(Color::rgb(0, 0, 0));
|
||||
|
||||
for edge in 0..4 {
|
||||
let mut path = Path::new();
|
||||
let (ox, oy) = origins[edge];
|
||||
let (w, h) = whs[edge];
|
||||
path.rect(ox, oy, w, h);
|
||||
canvas.fill_path(&path, &painter);
|
||||
}
|
||||
|
||||
let (lon_range, lat_range) = render.render_range();
|
||||
let (lon_range, lat_range): (Range, Range) = (lon_range.into(), lat_range.into());
|
||||
|
||||
let lon_keypoints = lon_range.key_points(10);
|
||||
let lat_keypoints = lat_range.key_points(5);
|
||||
let mut paint = Paint::color(Color::white()); // 黑色字体
|
||||
paint.set_font_size(25.0);
|
||||
|
||||
for lon in lon_keypoints.iter() {
|
||||
let (x, _) = render.map((*lon, lat_range.0)).unwrap();
|
||||
|
||||
let text = format!("{:.2}", lon);
|
||||
let metrics = canvas
|
||||
.measure_text(x, 0.0, text.as_str(), &paint)
|
||||
.expect("Cannot measure text");
|
||||
|
||||
let text_x = x - metrics.width() / 2.0;
|
||||
let text_y = h - metrics.height() / 2.0;
|
||||
|
||||
canvas
|
||||
.fill_text(text_x, text_y, text.as_str(), &paint)
|
||||
.expect("Cannot draw text");
|
||||
}
|
||||
|
||||
for lat in lat_keypoints.iter() {
|
||||
let (_, y) = render.map((lon_range.0, *lat)).unwrap();
|
||||
|
||||
let text = format!("{:.2}", lat);
|
||||
let metrics = canvas
|
||||
.measure_text(0.0, y, text.as_str(), &paint)
|
||||
.expect("Cannot measure text");
|
||||
|
||||
let text_x = 0.0;
|
||||
let text_y = y - metrics.height() / 2.0;
|
||||
|
||||
canvas
|
||||
.fill_text(text_x, text_y, text.as_str(), &paint)
|
||||
.expect("Cannot draw text");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,12 @@
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::{ImageId, Paint, Color};
|
||||
use femtovg::{Color, ImageId, Paint, RenderTarget};
|
||||
use geo_macros::Prj;
|
||||
use geo_types::LineString;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use ndarray::Array2;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Prj, Default)]
|
||||
pub struct ForegroundConfig {
|
||||
@ -1,14 +1,20 @@
|
||||
use crate::render::layer::background::BackgroundWidget;
|
||||
use crate::render::layer::foreground::ForegroundWidget;
|
||||
use crate::render::{imp, WindowCoord};
|
||||
use femtovg::RenderTarget;
|
||||
use gtk::glib;
|
||||
use gtk::subclass::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use crate::render::background::BackgroundWidget;
|
||||
use crate::render::foreground::ForegroundWidget;
|
||||
|
||||
use super::layers::Layer;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InteriorWidget {
|
||||
pub(super) background: RefCell<BackgroundWidget>,
|
||||
pub(super) foreground: RefCell<ForegroundWidget>,
|
||||
pub(super) trans: RefCell<WindowCoord>,
|
||||
pub(super) update_trans: RefCell<WindowCoord>,
|
||||
pub(super) layers: RefCell<Vec<Layer>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
@ -17,4 +23,4 @@ impl ObjectSubclass for InteriorWidget {
|
||||
type Type = super::InteriorWidget;
|
||||
}
|
||||
|
||||
impl ObjectImpl for InteriorWidget{}
|
||||
impl ObjectImpl for InteriorWidget {}
|
||||
107
src/render/layer/foreground/interior/layers.rs
Normal file
107
src/render/layer/foreground/interior/layers.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use geojson::GeoJson;
|
||||
use std::{
|
||||
any::Any,
|
||||
cell::{Ref, RefCell}, fmt::Debug,
|
||||
};
|
||||
use topojson::TopoJson;
|
||||
|
||||
use crate::{
|
||||
coords::{Coord, Mapper, Range},
|
||||
render::{renders::DataRenderer, Render},
|
||||
};
|
||||
use femtovg::{renderer::OpenGl, Canvas, ImageId};
|
||||
|
||||
pub struct Layer {
|
||||
pub visiable: bool,
|
||||
target: RefCell<Option<Target>>,
|
||||
imp: RefCell<Option<Box<dyn LayerImpl>>>,
|
||||
draw: Box<dyn Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32))>,
|
||||
}
|
||||
|
||||
impl Debug for Layer {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Layer")
|
||||
.field("visiable", &self.visiable)
|
||||
.field("target", &self.target)
|
||||
.field("imp", &self.imp)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait LayerImpl:Debug {
|
||||
fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render) -> Option<Target>;
|
||||
}
|
||||
|
||||
impl Layer {
|
||||
pub fn new<
|
||||
F: 'static + Fn(&Self, &mut Canvas<OpenGl>, &Render, (f32, f32)),
|
||||
IMP: LayerImpl + 'static,
|
||||
>(
|
||||
visiable: bool,
|
||||
draw: F,
|
||||
imp: Option<IMP>,
|
||||
) -> Self {
|
||||
Layer {
|
||||
visiable,
|
||||
target: RefCell::new(None),
|
||||
draw: Box::new(draw),
|
||||
imp: RefCell::new(imp.map(|x| Box::new(x) as Box<dyn LayerImpl>)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, render: &Render, window_size: (f32, f32)) {
|
||||
if self.visiable {
|
||||
(self.draw)(self, canvas, render, window_size);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_render_target(&self, target: Target) {
|
||||
*self.target.borrow_mut() = Some(target);
|
||||
}
|
||||
|
||||
pub fn render_target(&self) -> Option<Target> {
|
||||
self.target.borrow().clone()
|
||||
}
|
||||
|
||||
pub fn get_imp(&self) -> Ref<Option<Box<dyn LayerImpl>>> {
|
||||
let im = self.imp.borrow();
|
||||
im
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Target {
|
||||
pub target: ImageId,
|
||||
width: f32,
|
||||
height: f32,
|
||||
bounds: (Range, Range),
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn new(target: ImageId, width: f32, height: f32, bounds: (Range, Range)) -> Self {
|
||||
Self {
|
||||
target,
|
||||
width,
|
||||
height,
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(&self, render: &Render) -> (f32, f32) {
|
||||
let (x, y) = self.bounds;
|
||||
|
||||
let p1 = (x.0, y.0);
|
||||
let p2 = (x.1, y.1);
|
||||
|
||||
let (x1, y1) = render.map(p1).unwrap();
|
||||
let (x2, y2) = render.map(p2).unwrap();
|
||||
|
||||
((x2 - x1).abs(), (y2 - y1).abs())
|
||||
}
|
||||
|
||||
pub fn origin(&self, render: &Render) -> (f32, f32) {
|
||||
let (x, y) = self.bounds;
|
||||
let p1 = (x.0, y.1);
|
||||
render.map(p1).unwrap()
|
||||
}
|
||||
}
|
||||
56
src/render/layer/foreground/interior/mod.rs
Normal file
56
src/render/layer/foreground/interior/mod.rs
Normal file
@ -0,0 +1,56 @@
|
||||
mod imp;
|
||||
mod layers;
|
||||
use crate::coords::{Mapper, Range};
|
||||
use crate::data::Npz;
|
||||
use crate::data::{DataLoader, RadarData2d};
|
||||
use crate::render::predefined::color_mapper::BoundaryNorm;
|
||||
use crate::render::predefined::gis::MapRender;
|
||||
use crate::render::renders::DataRenderer;
|
||||
use crate::render::Render;
|
||||
use femtovg::{renderer::OpenGl, Canvas, Color, Paint, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
pub use layers::{Layer, LayerImpl, Target};
|
||||
use ndarray::OwnedRepr;
|
||||
use std::cell::Ref;
|
||||
use std::ops::Deref;
|
||||
|
||||
use crate::render::imp::{RenderConfig, RenderStatus};
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct InteriorWidget(ObjectSubclass<imp::InteriorWidget>);
|
||||
}
|
||||
|
||||
impl Default for InteriorWidget {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl InteriorWidget {
|
||||
pub fn new() -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
this.imp().layers.replace(vec![
|
||||
// Layer::grid_render_layer_with_path(
|
||||
// "/users/tsuki/projects/radar-g/test2.npz",
|
||||
// Npz,
|
||||
// BoundaryNorm::default(),
|
||||
// )
|
||||
]);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn draw(
|
||||
&self,
|
||||
canvas: &mut Canvas<OpenGl>,
|
||||
render: &Render,
|
||||
status: Ref<'_, RenderStatus>,
|
||||
_c: Ref<'_, RenderConfig>,
|
||||
) {
|
||||
let mut layers = self.imp().layers.borrow_mut();
|
||||
|
||||
let (x, y) = (canvas.width(), canvas.height());
|
||||
for layer in layers.iter_mut().filter(|x| x.visiable) {
|
||||
layer.draw(canvas, &render, (x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/render/layer/foreground/mod.rs
Normal file
34
src/render/layer/foreground/mod.rs
Normal file
@ -0,0 +1,34 @@
|
||||
pub mod exterior;
|
||||
mod imp;
|
||||
pub mod interior;
|
||||
use crate::coords::Mapper;
|
||||
use femtovg::{renderer::OpenGl, Canvas, Path};
|
||||
use glib::subclass::types::ObjectSubclassIsExt;
|
||||
use gtk::glib;
|
||||
use std::cell::Ref;
|
||||
|
||||
pub use self::imp::ForegroundConfig;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct ForegroundWidget(ObjectSubclass<imp::ForegroundWidget>);
|
||||
}
|
||||
|
||||
impl Default for ForegroundWidget {
|
||||
fn default() -> Self {
|
||||
Self::new(ForegroundConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl ForegroundWidget {
|
||||
pub fn new(config: ForegroundConfig) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
let imp = this.imp();
|
||||
imp.config.replace(config);
|
||||
this
|
||||
}
|
||||
|
||||
pub fn draw(&self, canvas: &mut Canvas<OpenGl>, scale: f32, dpi: i32, mapper: Ref<'_, Mapper>) {
|
||||
let config = self.imp().config.borrow();
|
||||
}
|
||||
|
||||
}
|
||||
2
src/render/layer/mod.rs
Normal file
2
src/render/layer/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod background;
|
||||
pub mod foreground;
|
||||
@ -1,23 +1,22 @@
|
||||
mod background;
|
||||
mod exterior;
|
||||
mod foreground;
|
||||
mod cms;
|
||||
mod imp;
|
||||
mod interior;
|
||||
mod layer;
|
||||
mod predefined;
|
||||
mod renders;
|
||||
use self::cms::CMS;
|
||||
pub use self::imp::{RenderConfig, RenderMotion, RenderStatus};
|
||||
pub use self::layer::background::{BackgroundConfig, BackgroundWidget};
|
||||
pub use self::layer::foreground::{ForegroundConfig, ForegroundWidget};
|
||||
use crate::coords::Mapper;
|
||||
use crate::data::RadarData2d;
|
||||
use crate::pipeline::ProjPipe;
|
||||
use crate::pipeline::{Pipeline, ShadePipe};
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub use self::background::{BackgroundConfig, BackgroundWidget};
|
||||
pub use self::foreground::{ForegroundConfig, ForegroundWidget};
|
||||
use self::imp::{RenderConfig, RenderStatus};
|
||||
use adw::prelude::WidgetExt;
|
||||
use femtovg::Color;
|
||||
use adw::prelude::{GLAreaExt, GestureDragExt};
|
||||
use geo_types::LineString;
|
||||
use glib::clone;
|
||||
pub use glib::subclass::prelude::*;
|
||||
use ndarray::{self, Array2};
|
||||
use num_traits::{AsPrimitive, FromPrimitive, Num};
|
||||
use proj::ProjError;
|
||||
use gtk::traits::WidgetExt;
|
||||
use gtk::{EventControllerScrollFlags, Inhibit};
|
||||
pub use layer::foreground::interior::Layer;
|
||||
use std::cell::Ref;
|
||||
use gtk::prelude::*;
|
||||
|
||||
pub(super) type WindowCoord = (f32, f32);
|
||||
|
||||
@ -28,21 +27,74 @@ glib::wrapper! {
|
||||
|
||||
impl Default for Render {
|
||||
fn default() -> Self {
|
||||
Self::new(None)
|
||||
Self::new(None, RenderConfig { padding: [10.0; 4] })
|
||||
}
|
||||
}
|
||||
|
||||
impl Render {
|
||||
pub fn new(mapper: Option<Mapper>) -> Self {
|
||||
pub fn new(mapper: Option<Mapper>, cfg: RenderConfig) -> Self {
|
||||
let this: Self = glib::Object::new();
|
||||
{
|
||||
let mut configs = this.imp().config.borrow_mut();
|
||||
let mut status = this.imp().status.borrow_mut();
|
||||
status.scale = 1.0;
|
||||
}
|
||||
this.imp().config.replace(cfg);
|
||||
if let Some(mapper) = mapper {
|
||||
this.set_mapper(mapper);
|
||||
}
|
||||
|
||||
|
||||
let pointer_location_detecture = gtk::EventControllerMotion::new();
|
||||
pointer_location_detecture.connect_motion(
|
||||
clone!( @weak this as r => move |_context, x, y| {
|
||||
r.update_status(|s| {
|
||||
let dpi = r.scale_factor();
|
||||
let (_,h) = s.window_size;
|
||||
s.pointer_location = (x as f32 * dpi as f32, h as f32 - y as f32 * dpi as f32);
|
||||
});
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
let scale_detecture = gtk::EventControllerScroll::new(EventControllerScrollFlags::VERTICAL);
|
||||
scale_detecture.connect_scroll(clone!(
|
||||
@weak this as r => @default-panic,move |_context, _x, y| {
|
||||
r.update_status(|status|{
|
||||
status.scale = y as f32;
|
||||
status.motion = RenderMotion::Scale;
|
||||
});
|
||||
r.queue_render();
|
||||
Inhibit(false)
|
||||
}
|
||||
));
|
||||
|
||||
let drag_detecture = gtk::GestureDrag::new();
|
||||
drag_detecture.connect_drag_update(clone!(
|
||||
@weak this as r => move |this, _, _| {
|
||||
let (ox, oy) = this.offset().unwrap_or((0.0,0.0));
|
||||
r.update_status(|s| {
|
||||
s.translate = Some((-ox as f32 * 1.35, oy as f32 * 1.35));
|
||||
s.motion = RenderMotion::Translate;
|
||||
});
|
||||
r.queue_render();
|
||||
}));
|
||||
|
||||
drag_detecture.connect_drag_end(clone!(
|
||||
@weak this as r => move |_,_,_|{
|
||||
r.update_status(|cfg| {
|
||||
cfg.translate = None;
|
||||
cfg.motion = RenderMotion::Translate;
|
||||
})
|
||||
}
|
||||
r.queue_render();
|
||||
));
|
||||
|
||||
this.set_hexpand(true);
|
||||
this.set_vexpand(true);
|
||||
this.add_controller(pointer_location_detecture);
|
||||
this.add_controller(scale_detecture);
|
||||
this.add_controller(drag_detecture);
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
@ -54,6 +106,10 @@ impl Render {
|
||||
f(&mut cfg);
|
||||
}
|
||||
|
||||
pub fn set_cfg(&self, cfg: RenderConfig) {
|
||||
self.imp().config.replace(cfg);
|
||||
}
|
||||
|
||||
pub fn update_status<F>(&self, mut f: F)
|
||||
where
|
||||
F: FnMut(&mut RenderStatus),
|
||||
@ -62,83 +118,75 @@ impl Render {
|
||||
f(&mut status);
|
||||
}
|
||||
|
||||
// pub fn change_pointer(&mut self, x: f32, y: f32) {
|
||||
// if let Some(canvas) = self.imp().canvas.borrow().as_ref() {
|
||||
// let dpi = self.scale_factor();
|
||||
// let cw = canvas.width();
|
||||
// let ch = canvas.height();
|
||||
// let (tx, ty) = self.imp().translate();
|
||||
// let scale = self.imp().config.borrow().scale;
|
||||
// let (lon, lat) = self
|
||||
// .imp()
|
||||
// .mapper
|
||||
// .borrow()
|
||||
// .fore_map((
|
||||
// (x * dpi as f32 + tx) / cw / scale,
|
||||
// (y * dpi as f32 + ty) / ch / scale,
|
||||
// ))
|
||||
// .unwrap();
|
||||
// let mut cfg = self.imp().config.borrow_mut();
|
||||
// cfg.pointer_lon_lat = (lon, lat);
|
||||
// cfg.pointer_location = (x, y);
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn set_translate(&self, trans: (f32, f32)) {
|
||||
// if let Some(c) = self.imp().canvas.borrow().as_ref() {
|
||||
// self.imp().config.borrow_mut().translate = (c.width() * trans.0, c.height() * trans.1);
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn set_mapper(&self, mapper: Mapper) {
|
||||
self.imp().mapper.replace(mapper);
|
||||
}
|
||||
|
||||
pub(super) fn load_data_2d<T, Raw>(
|
||||
&self,
|
||||
mut data: RadarData2d<T, Raw>,
|
||||
) -> Result<(), ProjError>
|
||||
where
|
||||
T: Num
|
||||
+ Clone
|
||||
+ PartialEq
|
||||
+ PartialOrd
|
||||
+ AsPrimitive<i8>
|
||||
+ AsPrimitive<f64>
|
||||
+ Debug
|
||||
+ FromPrimitive,
|
||||
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
|
||||
{
|
||||
assert!(data.dim1.shape().len() == data.dim2.shape().len());
|
||||
pub fn map(&self, loc: (f64, f64)) -> Option<(f32, f32)> {
|
||||
// let (x, y) = loc;
|
||||
// let (x_range, y_range) = self.imp().window_range().unwrap();
|
||||
|
||||
let levels: Vec<T> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65]
|
||||
// if x >= x_range.0 && x <= x_range.1 && y >= y_range.0 && y <= y_range.1 {
|
||||
|
||||
// }
|
||||
let foremapped = self.get_mapper().map(loc).unwrap();
|
||||
return self.imp().map(foremapped);
|
||||
}
|
||||
|
||||
pub fn inverse_map(&self, loc: (f32, f32)) -> Option<(f64, f64)> {
|
||||
let foremapped = self.imp().inverse_map(loc);
|
||||
foremapped.and_then(|foremapped| self.get_mapper().inverse_map(foremapped).ok())
|
||||
}
|
||||
|
||||
pub fn ring_map(&self, ring: &LineString<f64>) -> Option<LineString<f32>> {
|
||||
Some(
|
||||
ring.points()
|
||||
.into_iter()
|
||||
.map(|b| T::from_i8(b).unwrap())
|
||||
.collect();
|
||||
let colors = vec![
|
||||
Color::rgb(0, 172, 164),
|
||||
Color::rgb(192, 192, 254),
|
||||
Color::rgb(122, 114, 238),
|
||||
Color::rgb(30, 38, 208),
|
||||
Color::rgb(166, 252, 168),
|
||||
Color::rgb(0, 234, 0),
|
||||
Color::rgb(16, 146, 26),
|
||||
Color::rgb(252, 244, 100),
|
||||
Color::rgb(200, 200, 2),
|
||||
Color::rgb(140, 140, 0),
|
||||
Color::rgb(254, 172, 172),
|
||||
Color::rgb(254, 100, 92),
|
||||
Color::rgb(238, 2, 48),
|
||||
Color::rgb(212, 142, 254),
|
||||
Color::rgb(170, 36, 250),
|
||||
];
|
||||
.map(|p| self.map((p.x(), p.y())))
|
||||
.filter(|p| p.is_some())
|
||||
.collect::<Option<Vec<_>>>()
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
|
||||
let mapper = self.imp().mapper.borrow();
|
||||
pub fn point_in_bound(&self, loc: (f64, f64)) -> bool {
|
||||
self.get_mapper().point_in_bound(loc)
|
||||
}
|
||||
|
||||
let pjp = ProjPipe::new(&mapper);
|
||||
let rrp = ShadePipe::new(levels, colors.into_iter().map(|v| v.into()).collect());
|
||||
let rainbow = pjp.run(&data).unwrap();
|
||||
let pbow: Array2<Option<Color>> = rrp.run(&data).unwrap();
|
||||
Ok(())
|
||||
fn get_mapper(&self) -> Ref<Mapper> {
|
||||
self.imp().mapper.borrow()
|
||||
}
|
||||
|
||||
pub fn window_size(&self) -> (i32, i32) {
|
||||
self.imp().window_size().unwrap()
|
||||
}
|
||||
|
||||
pub fn create_drawer<F>(&self, range: (f64, f64, f64, f64), window_size: (f32, f32), mut f: F)
|
||||
where
|
||||
F: FnMut(&CMS),
|
||||
{
|
||||
let (lon1, lon2, lat1, lat2) = range;
|
||||
let mut mapper = self.get_mapper().clone();
|
||||
mapper.set_lat_range(lat1..lat2);
|
||||
mapper.set_lon_range(lon1..lon2);
|
||||
|
||||
println!("new_mapper: {:?}", mapper.get_bounds());
|
||||
println!("old_mapper: {:?}", self.get_mapper().get_bounds());
|
||||
let cms = CMS::new(mapper, window_size);
|
||||
f(&cms);
|
||||
}
|
||||
|
||||
pub fn pointer_loc(&self, transed: bool) -> (f64, f64) {
|
||||
let raw = self.imp().status.borrow().pointer_location.clone();
|
||||
if transed {
|
||||
self.inverse_map(raw).unwrap()
|
||||
} else {
|
||||
(raw.0 as f64, raw.1 as f64)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_range(&self) -> ((f64, f64), (f64, f64)) {
|
||||
self.imp().window_range().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
72
src/render/predefined/color_mapper.rs
Normal file
72
src/render/predefined/color_mapper.rs
Normal file
@ -0,0 +1,72 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use femtovg::Color;
|
||||
use num_traits::NumOps;
|
||||
pub trait ColorMapper<T: NumOps + PartialOrd> : Debug {
|
||||
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoundaryNorm<T: NumOps + PartialOrd> {
|
||||
boundaries: Vec<T>,
|
||||
extrand: bool,
|
||||
colors: Vec<femtovg::Color>,
|
||||
}
|
||||
|
||||
impl Default for BoundaryNorm<i8> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
boundaries: vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65],
|
||||
extrand: true,
|
||||
colors: vec![
|
||||
Color::rgb(0, 172, 164),
|
||||
Color::rgb(192, 192, 254),
|
||||
Color::rgb(122, 114, 238),
|
||||
Color::rgb(30, 38, 208),
|
||||
Color::rgb(166, 252, 168),
|
||||
Color::rgb(0, 234, 0),
|
||||
Color::rgb(16, 146, 26),
|
||||
Color::rgb(252, 244, 100),
|
||||
Color::rgb(200, 200, 2),
|
||||
Color::rgb(140, 140, 0),
|
||||
Color::rgb(254, 172, 172),
|
||||
Color::rgb(254, 100, 92),
|
||||
Color::rgb(238, 2, 48),
|
||||
Color::rgb(212, 142, 254),
|
||||
Color::rgb(170, 36, 250),
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumOps + PartialOrd> BoundaryNorm<T> {
|
||||
pub fn new(boundaries: Vec<T>, colors: Vec<femtovg::Color>, extrand: bool) -> Self {
|
||||
// assert_eq!(boundaries.len(), colors.len() + 1);
|
||||
BoundaryNorm {
|
||||
boundaries,
|
||||
extrand,
|
||||
colors,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
||||
let mut index = 0;
|
||||
if value == invalid_value {
|
||||
return None;
|
||||
}
|
||||
for (i, boundary) in self.boundaries.iter().enumerate() {
|
||||
if value < *boundary {
|
||||
break;
|
||||
}
|
||||
index = i;
|
||||
}
|
||||
index = index.saturating_sub(1).min(self.colors.len() - 1);
|
||||
Some(self.colors[index])
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: NumOps + PartialOrd + Debug> ColorMapper<T> for BoundaryNorm<T> {
|
||||
fn map_value_to_color(&self, value: T, invalid_value: T) -> Option<femtovg::Color> {
|
||||
self.map_value_to_color(value, invalid_value)
|
||||
}
|
||||
}
|
||||
63
src/render/predefined/gis.rs
Normal file
63
src/render/predefined/gis.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::coords::Mapper;
|
||||
use crate::render::Render;
|
||||
use femtovg::renderer::OpenGl;
|
||||
use femtovg::{Canvas, Paint};
|
||||
use geo_types::LineString;
|
||||
use geojson::GeoJson;
|
||||
|
||||
pub struct MapRender;
|
||||
|
||||
impl MapRender {
|
||||
pub fn test(geojson: &GeoJson, canvas: &mut Canvas<OpenGl>, render: &Render) {
|
||||
let paint = Paint::color(femtovg::Color::rgb(255, 255, 255));
|
||||
|
||||
if let GeoJson::FeatureCollection(ref feature_collection) = geojson {
|
||||
for feature in &feature_collection.features {
|
||||
feature
|
||||
.geometry
|
||||
.as_ref()
|
||||
.iter()
|
||||
.for_each(|geometry| match geometry.value {
|
||||
geojson::Value::Polygon(ref polygon) => {
|
||||
let mut path = femtovg::Path::new();
|
||||
let polygon = &polygon[0];
|
||||
for (i, point) in polygon.iter().enumerate() {
|
||||
let _point = (point[0], point[1]);
|
||||
if render.point_in_bound(_point) {
|
||||
let (x, y) = render.map(_point).unwrap();
|
||||
if i == 0 {
|
||||
path.move_to(x, y);
|
||||
} else {
|
||||
path.line_to(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
geojson::Value::MultiPolygon(ref multi_polygon) => {
|
||||
let mut path = femtovg::Path::new();
|
||||
for polygon in multi_polygon {
|
||||
let out_ring = &polygon[0];
|
||||
let out_ring_line: LineString = out_ring
|
||||
.iter()
|
||||
.map(|x| (x[0], x[1]))
|
||||
.collect::<Vec<_>>()
|
||||
.into();
|
||||
let out_ring = render.ring_map(&out_ring_line).unwrap();
|
||||
|
||||
for (i, point) in out_ring.points().enumerate() {
|
||||
let (x, y) = (point.x(), point.y());
|
||||
if i == 0 {
|
||||
path.move_to(x, y);
|
||||
} else {
|
||||
path.line_to(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
canvas.stroke_path(&mut path, &paint);
|
||||
}
|
||||
_ => println!("Unknown geometry type: {:?}", geometry.value),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
179
src/render/predefined/grid_field_renderer.rs
Normal file
179
src/render/predefined/grid_field_renderer.rs
Normal file
@ -0,0 +1,179 @@
|
||||
use super::color_mapper::{BoundaryNorm, ColorMapper};
|
||||
use crate::render::layer::foreground::interior::LayerImpl;
|
||||
use femtovg::{ImageFlags, Paint, Path, PixelFormat::Rgba8, RenderTarget};
|
||||
use geo_types::LineString;
|
||||
use ndarray::ArrayView2;
|
||||
use num_traits::{Num, NumOps};
|
||||
use std::{marker::PhantomData, fmt::Debug};
|
||||
|
||||
use super::super::renders::DataRenderer;
|
||||
use crate::{
|
||||
data::Radar2d,
|
||||
render::{layer::foreground::interior::Target, Render},
|
||||
utils::meshgrid,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridFieldRenderer<CMAP, T>
|
||||
where
|
||||
T: NumOps + PartialOrd,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
cmap: CMAP,
|
||||
value_phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: NumOps + PartialOrd + Copy, CMAP: ColorMapper<T>> GridFieldRenderer<CMAP, T> {
|
||||
pub fn new(cmap: CMAP) -> Self {
|
||||
Self {
|
||||
cmap,
|
||||
value_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
fn draw_2d(
|
||||
&self,
|
||||
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||
data: ArrayView2<T>,
|
||||
render: &Render,
|
||||
dims: (ArrayView2<f64>, ArrayView2<f64>),
|
||||
window_size: (f32, f32),
|
||||
fill_value: T,
|
||||
) {
|
||||
let shape = data.shape();
|
||||
let (rows, cols) = (shape[0], shape[1]);
|
||||
let (dim1, dim2) = dims;
|
||||
|
||||
let d1_s = dim1[[0, 0]];
|
||||
let d1_e = dim1[[0, cols - 1]];
|
||||
|
||||
let d2_s = dim2[[0, 0]];
|
||||
let d2_e = dim2[[rows - 1, 0]];
|
||||
|
||||
render.create_drawer((d1_s, d1_e, d2_s, d2_e), window_size, |cms| {
|
||||
for r in 0..rows - 1 {
|
||||
for c in 0..cols - 1 {
|
||||
let lb_lat = dim2[[r, c]];
|
||||
let lb_lon = dim1[[r, c]];
|
||||
|
||||
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);
|
||||
|
||||
if None == mapped_color {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mapped_ring = cms.ring_map(&cell).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.close();
|
||||
canvas.fill_path(&path, &Paint::color(mapped_color.unwrap()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, CMAP> DataRenderer for GridFieldRenderer<CMAP, T>
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
type Data = Radar2d<T>;
|
||||
fn render(
|
||||
&self,
|
||||
render: &Render,
|
||||
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||
data: &Self::Data,
|
||||
) -> Target {
|
||||
let new_img = canvas
|
||||
.create_image_empty(3000, 3000, Rgba8, ImageFlags::empty())
|
||||
.expect("Can't Create Image");
|
||||
|
||||
canvas.save();
|
||||
canvas.reset();
|
||||
|
||||
if let Ok(_) = canvas.image_size(new_img) {
|
||||
canvas.set_render_target(RenderTarget::Image(new_img));
|
||||
let _data = data.data.view();
|
||||
let (_dim1, _dim2) = meshgrid(data.dim1.view(), data.dim2.view());
|
||||
self.draw_2d(
|
||||
canvas,
|
||||
_data,
|
||||
render,
|
||||
(_dim1.view(), _dim2.view()),
|
||||
(3000.0, 3000.0),
|
||||
data.fill_value,
|
||||
);
|
||||
}
|
||||
|
||||
canvas.restore();
|
||||
canvas.set_render_target(RenderTarget::Screen);
|
||||
|
||||
let d1_start = (data.dim1.view()).first().unwrap().clone();
|
||||
let d1_end = (data.dim1.view()).last().unwrap().clone();
|
||||
|
||||
let d2_start = data.dim2.view().first().unwrap().clone();
|
||||
let d2_end = data.dim2.view().last().unwrap().clone();
|
||||
|
||||
Target::new(
|
||||
new_img,
|
||||
3000f32,
|
||||
3000f32,
|
||||
((d1_start, d1_end).into(), (d2_start, d2_end).into()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GridLayerImpl<CMAP, T>
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
renderer: GridFieldRenderer<CMAP, T>,
|
||||
data: Radar2d<T>,
|
||||
}
|
||||
|
||||
pub type DbzGridLayerImpl = GridLayerImpl<BoundaryNorm<i8>, i8>;
|
||||
|
||||
impl<CMAP, T> GridLayerImpl<CMAP, T>
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone,
|
||||
CMAP: ColorMapper<T>,
|
||||
{
|
||||
pub fn new(renderer: GridFieldRenderer<CMAP, T>, data: Radar2d<T>) -> Self {
|
||||
Self { renderer, data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<CMAP, T> LayerImpl for GridLayerImpl<CMAP, T>
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone + Debug,
|
||||
CMAP: ColorMapper<T> + Debug,
|
||||
{
|
||||
fn draw(
|
||||
&self,
|
||||
canvas: &mut femtovg::Canvas<femtovg::renderer::OpenGl>,
|
||||
render: &Render,
|
||||
) -> Option<Target> {
|
||||
Some(self.renderer.render(render, canvas, &self.data))
|
||||
}
|
||||
}
|
||||
67
src/render/predefined/layers.rs
Normal file
67
src/render/predefined/layers.rs
Normal file
@ -0,0 +1,67 @@
|
||||
use femtovg::Paint;
|
||||
use num_traits::{Num, NumOps};
|
||||
use std::path::Path;
|
||||
|
||||
use crate::{
|
||||
data::{DataLoader, Radar2d},
|
||||
render::layer::foreground::interior::Layer,
|
||||
};
|
||||
|
||||
use super::{
|
||||
color_mapper::ColorMapper,
|
||||
grid_field_renderer::{GridFieldRenderer, GridLayerImpl},
|
||||
};
|
||||
|
||||
impl Layer {
|
||||
pub fn grid_render_layer<T, CMAP>(data: Radar2d<T>, color_map: CMAP) -> Self
|
||||
where
|
||||
T: std::fmt::Debug + Num + NumOps + PartialOrd + Copy + Clone + 'static,
|
||||
CMAP: ColorMapper<T> + 'static,
|
||||
{
|
||||
Self::new(
|
||||
true,
|
||||
|s, c, render, _| {
|
||||
if let Some(target) = s.render_target() {
|
||||
if let Ok(_) = c.image_size(target.target) {
|
||||
let (x, y) = target.size(render);
|
||||
let (ox, oy) = target.origin(render);
|
||||
let painter = Paint::image(target.target, ox, oy, x, y, 0.0, 1.0);
|
||||
let mut path = femtovg::Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
c.fill_path(&path, &painter);
|
||||
}
|
||||
} else {
|
||||
if let Some(renderer) = s.get_imp().as_ref() {
|
||||
let img = renderer.draw(c, render).unwrap();
|
||||
if let Ok(_) = c.image_size(img.target) {
|
||||
let (x, y) = img.size(render);
|
||||
let (ox, oy) = img.origin(render);
|
||||
println!("{} {} {} {}", x, y, ox, oy);
|
||||
let painter = Paint::image(img.target, ox, oy, x, y, 0.0, 1.0);
|
||||
let mut path = femtovg::Path::new();
|
||||
path.rect(ox, oy, x, y);
|
||||
s.set_render_target(img);
|
||||
c.fill_path(&path, &painter);
|
||||
c.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Some(GridLayerImpl::new(GridFieldRenderer::new(color_map), data)),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn grid_render_layer_with_path<T, CMAP, LOADER>(
|
||||
path: impl AsRef<Path>,
|
||||
loader: LOADER,
|
||||
color_map: CMAP,
|
||||
) -> Self
|
||||
where
|
||||
T: Num + NumOps + PartialOrd + Copy + Clone + 'static + std::fmt::Debug,
|
||||
CMAP: ColorMapper<T> + 'static,
|
||||
LOADER: DataLoader<T, Radar2d<T>>,
|
||||
{
|
||||
let data = loader.load(path).unwrap();
|
||||
self::Layer::grid_render_layer(data, color_map)
|
||||
}
|
||||
}
|
||||
4
src/render/predefined/mod.rs
Normal file
4
src/render/predefined/mod.rs
Normal file
@ -0,0 +1,4 @@
|
||||
pub mod color_mapper;
|
||||
pub mod grid_field_renderer;
|
||||
pub mod gis;
|
||||
pub mod layers;
|
||||
14
src/render/renders.rs
Normal file
14
src/render/renders.rs
Normal file
@ -0,0 +1,14 @@
|
||||
use femtovg::{renderer::OpenGl, Canvas};
|
||||
use crate::render::layer::foreground::interior::Target;
|
||||
use crate::coords::Mapper;
|
||||
use super::Render;
|
||||
|
||||
pub trait DataRenderer {
|
||||
type Data;
|
||||
fn render(
|
||||
&self,
|
||||
render: &Render,
|
||||
canvas: &mut Canvas<OpenGl>,
|
||||
data: &Self::Data,
|
||||
) -> Target;
|
||||
}
|
||||
12
src/utils.rs
12
src/utils.rs
@ -1,3 +1,15 @@
|
||||
use ndarray::{Array2, ArrayView1};
|
||||
|
||||
pub fn meshgrid<T>(x: ArrayView1<T>, y: ArrayView1<T>) -> (Array2<T>, Array2<T>)
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
let shape = (y.len(), x.len());
|
||||
println!("shape: {:?}", shape);
|
||||
let xx = Array2::from_shape_fn(shape, |(_, j)| x[j].clone());
|
||||
let yy = Array2::from_shape_fn(shape, |(i, _)| y[i].clone());
|
||||
(xx, yy)
|
||||
}
|
||||
// let levels: Vec<i8> = vec![0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65];
|
||||
// let colors = vec![
|
||||
// RGBABuilder::default()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user