self drawing and drag gesture testing

This commit is contained in:
sleptworld 2023-06-12 08:55:07 +08:00
parent 1c1f2925cf
commit 6d670afdbf
12 changed files with 318 additions and 59 deletions

4
Cargo.lock generated
View File

@ -657,9 +657,9 @@ dependencies = [
[[package]]
name = "glib-build-tools"
version = "0.17.0"
version = "0.17.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f8480c9ba9cc06aa8d5baf446037f8dc237bee127e9b62080c4db7e293d8ea0"
checksum = "7a65d79efe318ef2cbbbb37032b125866fd82c34ea44c816132621bbc552e716"
[[package]]
name = "glib-macros"

View File

@ -23,9 +23,9 @@ ndarray = "0.15.6"
quadtree_rs = "0.1.2"
proj-sys = "0.23.1"
[build-dependencies]
glib-build-tools = "0.17.0"
# plotters = "0.3.4"
[dependencies.geo-macros]
path = "geo-macros"

View File

12
p.py
View File

@ -1,12 +0,0 @@
import numpy as np
a = np.load("/home/ruomu/Desktop/test.npz")
b = a['value']
lon = a['lon']
lat = a['lat']
a = np.load("/home/ruomu/Desktop/test2.npz")['value']
np.savez("/home/ruomu/Desktop/test2.npz", value=np.max(b,axis=0), lon=lon, lat=lat)

View File

@ -25,7 +25,7 @@ pub enum CoordType {
#[derive(Clone)]
pub struct RadarData2d<T, Raw, X = f64, Y = f64>
where
T: Num + Clone,
T: Num + Clone + PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
X: Num,
Y: Num,
@ -53,7 +53,7 @@ trait MultiDimensionData {}
impl<T, Raw> RadarData2d<T, Raw>
where
T: Num + AsPrimitive<f64> + FromPrimitive + Clone + Debug,
T: Num + AsPrimitive<f64> + FromPrimitive + Clone + Debug + PartialOrd + PartialEq,
Raw: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
{
fn value_to_owned(self) -> RadarData2d<T, OwnedRepr<T>> {
@ -198,11 +198,13 @@ fn windowed_sinc(x: f64, y: f64) -> f64 {
sinc * window
}
pub struct LevelData<T: Num + Clone>(pub Quadtree<i64, RadarData2d<T, OwnedRepr<T>>>);
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,
T: Num + Clone + AsPrimitive<f64> + FromPrimitive + Debug + PartialEq + PartialOrd,
{
fn value(
level_data: Vec<RadarData2d<T, OwnedRepr<T>>>,
@ -252,6 +254,7 @@ where
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)
@ -263,6 +266,7 @@ where
impl<T, Raw> MultiDimensionData for RadarData2d<T, Raw>
where
T: Num + Clone,
T: PartialEq + PartialOrd,
Raw: ndarray::Data<Elem = T> + Clone + RawDataClone,
{
}
@ -308,6 +312,7 @@ impl Npz {
impl<T> DataLoader<RadarData2d<T, OwnedRepr<T>>> for Npz
where
T: Num + Clone + Deserialize,
T: PartialEq + PartialOrd,
{
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)?;
@ -324,15 +329,15 @@ where
}
}
impl<T: Num + Clone> RadarData2d<T, OwnedRepr<T>> {
impl<T: Num + Clone + PartialEq + PartialOrd> RadarData2d<T, OwnedRepr<T>> {
pub fn load<P: AsRef<Path>, M: DataLoader<Self>>(p: P, meth: M) -> Self {
meth.load(p).unwrap()
}
}
pub struct Radar2d<T: Num + Clone>(pub RadarData2d<T, OwnedRepr<T>>);
pub struct Radar2d<T: Num + Clone + PartialEq + PartialOrd>(pub RadarData2d<T, OwnedRepr<T>>);
impl<T: Num + Clone> Radar2d<T> {
impl<T: Num + Clone + PartialEq + PartialOrd> Radar2d<T> {
pub fn load(
path: impl AsRef<Path>,
meth: impl DataLoader<RadarData2d<T, OwnedRepr<T>>>,

View File

@ -1,26 +1,24 @@
use geo_types::{MultiPolygon, Polygon};
use gtk::builders::DrawingAreaBuilder;
use gtk::gdk::Display;
use gtk::{
gio, glib, style_context_add_provider_for_display, Application, ApplicationWindow, CssProvider,
GestureDrag,
};
use gtk::{prelude::*, DrawingArea};
use painter::wgs84::*;
use painter::Painter;
// use plotters::prelude::DrawingArea;
use window::Window;
// mod backend;
mod data;
mod painter;
// mod trees;
mod render;
mod tree;
mod window;
use data::{CoordType, Npz, Radar2d};
use painter::wgs84::{LatLonCoord, Mercator, Range};
use painter::Painter;
const APP_ID: &str = "org.gtk_rs.HelloWorld2";
fn main() -> glib::ExitCode {
gio::resources_register_include!("window.gresource").expect("Failed to register resources");
// gio::resources_register_include!("window.gresource").expect("Failed to register resources");
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
@ -36,23 +34,33 @@ fn main() -> glib::ExitCode {
fn build_ui(app: &Application) {
// Create a window and set the title
let window = Window::new(app);
// let window = Window::new(app);
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.build();
let drawing_area = DrawingArea::new();
let path = "/home/ruomu/Desktop/test.npz";
let path = "/Users/ruomu/test2.npz";
let data = Radar2d::<i8>::load(path, Npz).unwrap();
let gesture = GestureDrag::new();
// gesture.connect_drag_end(|s, x, y| {
// (&drawing_area).queue_draw();
// });
// drawing_area.add_controller(gesture);
drawing_area.set_draw_func(move |a, b, c, d| {
let projection = Mercator::new();
let coord = LatLonCoord::new(None, None, ((0, c), (0, d)), projection);
let painter: Painter<'_, f64, LatLonCoord<Mercator>> = Painter::new(b, coord, c as u32, c as u32);
let aa = Range::from((*data.0.dim1.first().unwrap(), *data.0.dim1.last().unwrap()));
let ab = Range::from((*data.0.dim2.first().unwrap(), *data.0.dim2.last().unwrap()));
let coord = LatLonCoord::new(Some(aa), Some(ab), ((0, c), (0, d)), projection);
let painter: Painter<'_, f64, LatLonCoord<Mercator>> =
Painter::new(b, coord, c as u32, c as u32);
painter.draw_radar_2d(&data.0);
});
// window.set_child(Some(&drawing_area));
window.set_child(Some(&drawing_area));
window.set_default_width(1000);
window.set_default_height(800);

View File

@ -1,8 +1,11 @@
use num_traits::Num;
pub mod wgs84;
pub type ScreenCoord = (i32, i32);
pub type ScreenCoord = (f64, f64);
pub trait Coord<T: Num> {
fn map(&self, axis_1: T, axis_2: T) -> ScreenCoord;
fn dim1_range(&self) -> (T, T);
fn dim2_range(&self) -> (T, T);
}

View File

@ -1,10 +1,12 @@
use super::Coord;
use geo_macros::Prj;
use num_traits::{AsPrimitive, FromPrimitive, Num};
use proj::{Proj, ProjError};
use thiserror::Error;
type Lat = f64;
type Lon = f64;
#[derive(Clone, Copy)]
pub struct Range(pub f64, pub f64);
#[derive(Error, Debug)]
@ -16,15 +18,17 @@ pub enum CoordError {
},
}
impl From<(f64, f64)> for Range {
fn from(value: (f64, f64)) -> Self {
impl<T: AsPrimitive<f64> + Num> From<(T, T)> for Range {
fn from(value: (T, T)) -> Self {
let value = (value.0.as_(), value.1.as_());
let (_min, _max) = (value.0.min(value.1), value.0.max(value.1));
Self(_min, _max)
}
}
pub struct LatLonCoord<T: ProjectionS> {
actual: ((i32, i32), (i32, i32)),
// actual: ((f64, f64), (f64, f64)),
actual: (Range, Range),
logical: (Range, Range),
pcs: PCS<T>,
}
@ -53,21 +57,50 @@ impl<T: ProjectionS> LatLonCoord<T> {
lon: Option<Range>,
lat: Option<Range>,
actual: ((i32, i32), (i32, i32)),
// actual: (Range, Range),
project: T,
) -> Self {
let pcs = PCS::new(project, lon, lat);
let _box = pcs.bbox().unwrap();
Self {
actual,
actual: (Range::from(actual.0), Range::from(actual.1)),
pcs: pcs,
logical: _box,
}
}
pub fn lon_range(&self) -> Range {
self.pcs.lon_range
}
pub fn lat_range(&self) -> Range {
self.pcs.lat_range
}
}
impl<T: ProjectionS> Coord<f64> for LatLonCoord<T> {
fn map(&self, axis_1: f64, axis_2: f64) -> super::ScreenCoord {
(0, 0)
let point = self.pcs.map((axis_1, axis_2));
let logical_dim1_span = self.logical.0 .1 - self.logical.0 .0;
let dim1_rate = (point.0 - self.logical.0 .0) / logical_dim1_span;
let logical_dim2_span = self.logical.1 .1 - self.logical.1 .0;
let dim2_rate = (point.1 - self.logical.1 .0) / logical_dim2_span;
(
(dim1_rate * (self.actual.0 .1 - self.actual.0 .0) as f64) + self.actual.0 .0,
(dim2_rate * (self.actual.1 .1 - self.actual.1 .0) as f64) + self.actual.1 .0,
)
}
fn dim1_range(&self) -> (f64, f64) {
let v = self.lon_range();
(v.0, v.1)
}
fn dim2_range(&self) -> (f64, f64) {
let v = self.lat_range();
(v.0, v.1)
}
}
@ -157,8 +190,8 @@ impl Mercator {
pub fn new() -> Self {
Self {
central_lon: 0.0,
min_latitude: 0.0,
max_latitude: 70.0,
min_latitude: -82.0,
max_latitude: 82.0,
false_easting: 0.0,
false_northing: 0.0,
latitude_true_scale: 0.0,
@ -198,7 +231,7 @@ impl ProjectionS for Mercator {
let lat_range = Range {
0: lat_range.0.max(self.min_latitude),
1: lat_range.0.min(self.max_latitude),
1: lat_range.1.min(self.max_latitude),
};
(lon_range, lat_range)
}

View File

@ -1,10 +1,13 @@
use std::marker::PhantomData;
use crate::data::RadarData2d;
use crate::{data::RadarData2d, tree::get};
use super::coords::{Coord, ScreenCoord};
use gtk::{cairo::*, gdk::RGBA};
use num_traits::Num;
use gtk::{
cairo::*,
gdk::{builders::RGBABuilder, RGBA},
};
use num_traits::{FromPrimitive, Num};
pub struct Painter<'a, T: Num, C: Coord<T>> {
width: u32,
height: u32,
@ -33,18 +36,175 @@ impl<'a, T: Num + Clone, C: Coord<T>> Painter<'a, T, C> {
);
}
fn draw_pixel(&self, point: ScreenCoord) {}
fn draw_pixel(&self, point: ScreenCoord, color: &RGBA) {
self.context
.rectangle(f64::from(point.0), f64::from(point.1), 1.0, 1.0);
self.set_color(color);
fn draw_rect(&self, point: ScreenCoord, width: f64, height: f64) {}
self.context.fill();
}
fn draw_line(&self, start: ScreenCoord, end: ScreenCoord) {}
fn draw_rect(&self, point: ScreenCoord, width: f64, height: f64, color: &RGBA, fill: bool) {
self.set_color(color);
self.set_stroke_width(1);
self.context.rectangle(point.0, point.1, width, height);
self.context.fill();
// if fill {
// self.context.fill();
// } else {
// self.context.stroke();
// }
}
fn set_stroke_width(&self, width: u32) {
self.context.set_line_width(f64::from(width));
}
fn draw_line(&self, start: ScreenCoord, end: ScreenCoord) {
self.set_color(&RGBA::BLACK);
self.set_stroke_width(1);
self.context.move_to(start.0, start.1);
self.context.line_to(end.0, end.1);
self.context.stroke();
}
fn draw_path<I: IntoIterator<Item = ScreenCoord>>(&self, path: I) {
let mut path = path.into_iter();
if let Some((x, y)) = path.next() {
self.context.move_to(x, y);
}
for (x, y) in path {
self.context.line_to(x, y);
}
self.context.stroke();
}
}
impl<'a, C: Coord<f64>> Painter<'a, f64, C> {
pub fn draw_radar_2d<T: Num + Clone, D>(&self, data: &RadarData2d<T, D>)
where
pub fn draw_radar_2d<T: Num + Clone + PartialEq + PartialOrd + FromPrimitive + Copy, D>(
&self,
data: &RadarData2d<T, D>,
) where
D: ndarray::Data<Elem = T> + Clone + ndarray::RawDataClone,
{
let levels: Vec<T> = vec![
T::from_i8(0).unwrap(),
T::from_i8(5).unwrap(),
T::from_i8(10).unwrap(),
T::from_i8(15).unwrap(),
T::from_i8(20).unwrap(),
T::from_i8(25).unwrap(),
T::from_i8(30).unwrap(),
T::from_i8(35).unwrap(),
T::from_i8(40).unwrap(),
T::from_i8(45).unwrap(),
T::from_i8(50).unwrap(),
T::from_i8(55).unwrap(),
T::from_i8(60).unwrap(),
T::from_i8(65).unwrap(),
];
let colors = vec![
RGBABuilder::default()
.red(0 as f32 / 255.0)
.green(172 as f32 / 255.0)
.blue(164 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(192 as f32 / 255.0)
.green(192 as f32 / 255.0)
.blue(254 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(122 as f32 / 255.0)
.green(114 as f32 / 255.0)
.blue(238 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(30 as f32 / 255.0)
.green(38 as f32 / 255.0)
.blue(208 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(166 as f32 / 255.0)
.green(252 as f32 / 255.0)
.blue(168 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(0 as f32 / 255.0)
.green(234 as f32 / 255.0)
.blue(0 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(16 as f32 / 255.0)
.green(146 as f32 / 255.0)
.blue(26 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(252 as f32 / 255.0)
.green(244 as f32 / 255.0)
.blue(100 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(200 as f32 / 255.0)
.green(200 as f32 / 255.0)
.blue(2 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(140 as f32 / 255.0)
.green(140 as f32 / 255.0)
.blue(0 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(254 as f32 / 255.0)
.green(172 as f32 / 255.0)
.blue(172 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(254 as f32 / 255.0)
.green(100 as f32 / 255.0)
.blue(92 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(238 as f32 / 255.0)
.green(2 as f32 / 255.0)
.blue(48 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(212 as f32 / 255.0)
.green(142 as f32 / 255.0)
.blue(254 as f32 / 255.0)
.build(),
RGBABuilder::default()
.red(170 as f32 / 255.0)
.green(36 as f32 / 255.0)
.blue(250 as f32 / 255.0)
.build(),
];
let mut polygons: Vec<(shapefile::Polygon, _)> =
shapefile::read_as::<_, shapefile::Polygon, shapefile::dbase::Record>(
"/Users/ruomu/china/省界_region.shp",
)
.unwrap();
// for (polygon, _) in polygons.into_iter() {
// let x_range = polygon.bbox().x_range();
// let y_range = polygon.bbox().y_range();
// let lon_range = self.coord.dim1_range();
// let lat_range = self.coord.dim2_range();
// }
data.data.outer_iter().enumerate().for_each(|(r_num, row)| {
row.iter().enumerate().for_each(|(c_num, v)| {
let point = self.coord.map(data.dim1[c_num], data.dim2[r_num]);
if *v > T::from_i8(-125).unwrap() {
let color = get(&levels, &colors, *v);
// self.draw_pixel(point, &color);
self.draw_rect(point, 1.5, 1.5, &color, true);
}
})
});
}
}

43
src/render/imp.rs Normal file
View File

@ -0,0 +1,43 @@
use glib::{ObjectExt, ParamSpec, Properties, Value};
use gtk::glib;
use gtk::subclass::prelude::*;
use std::cell::Cell;
#[derive(Properties, Default)]
#[properties(wrapper_type = super::Render)]
pub struct Render {
#[property(get, set)]
height: Cell<u32>,
#[property(get, set)]
width: Cell<u32>,
}
#[glib::object_subclass]
impl ObjectSubclass for Render {
const NAME: &'static str = "MyRender";
type Type = super::Render;
type ParentType = gtk::DrawingArea;
}
// Trait shared by all GObjects
impl ObjectImpl for Render {
fn properties() -> &'static [ParamSpec] {
Self::derived_properties()
}
fn set_property(&self, id: usize, value: &Value, pspec: &ParamSpec) {
self.derived_set_property(id, value, pspec)
}
fn property(&self, id: usize, pspec: &ParamSpec) -> Value {
self.derived_property(id, pspec)
}
fn constructed(&self) {
self.parent_constructed();
}
}
// Trait shared by all widgets
impl WidgetImpl for Render {}
impl DrawingAreaImpl for Render {}

18
src/render/mod.rs Normal file
View File

@ -0,0 +1,18 @@
mod imp;
use glib::Object;
use gtk::glib;
glib::wrapper! {
pub struct Render(ObjectSubclass<imp::Render>)
@extends gtk::DrawingArea,gtk::Widget,
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
}
impl Render {
pub fn new(height: u32, width: u32) -> Self {
Object::builder()
.property("height", height)
.property("width", width)
.build()
}
}

View File

@ -1,8 +1,9 @@
use gtk::gdk::RGBA;
use num_traits::Num;
use plotters::style::RGBAColor;
pub fn get<T>(levels: &Vec<T>, colors: &Vec<RGBAColor>, v: T) -> RGBAColor
pub fn get<T>(levels: &Vec<T>, colors: &Vec<RGBA>, v: T) -> RGBA
where
T: Num + PartialOrd,
T: Num + PartialOrd + Copy,
{
let len = levels.len();