用可视化案例讲Rust编程5.用泛型和特性实现自适配绘制和颜色设置

news2024/12/23 18:47:52

上一节我们讲了用泛型实现返回结果,这一节我们来讲讲在函数签名里面使用泛型来对输入参数进行自适配。

先看UML设计图:

图片

好吧,看起来有点复杂,我们一个个来解释。

首先定义的是一个生成绘图元素需要的参数结构,并且定义个特性,就叫做构造绘图元素build_trace。

pub struct traceParam<G>{
    pub geometrys:Vec<G>,
    pub colors: Vec<inputColor>,
    pub size: usize,
}

pub trait BuildTrace{
    fn build_trace(&self) -> Vec<Box<ScatterMapbox<f64,f64>>>;
}

绘图元素的参数,定义了三个参数:

  • 第一个参数是输入一个几何要素集合,因为我们最起码有点线面三种几何要素,所以定义的是一个泛型变量G

  • 第二参数是一个颜色集合,这里也可以用泛型,不过我们不需要那么多种颜色定义,这里给了一个枚举型的输入颜色,枚举定义如下:

#[derive(Debug,Clone)]
pub enum inputColor {
    NamedColor(NamedColor),
    Rgba(Rgba),
}

这里支持NameColor和Rgba两种颜色定义即可。

  • 第三个参数是一个size,用来控制点的大小、线的宽度和面要素的边框,当然你也可以设定更多的参数,我这里仅用来说明,就不搞那么麻烦了:

图片

接下去就可以写具体的实现了。

点要素的实现:

impl BuildTrace for traceParam<Point>{
    fn build_trace(&self) -> Vec<Box<ScatterMapbox<f64,f64>>> {
        let mut traces:Vec<Box<ScatterMapbox<f64,f64>>> = Vec::new();
        for (pnt,color) in zip(&self.geometrys,&self.colors) {
            let mut trace: Box<ScatterMapbox<f64, f64>> = ScatterMapbox::new(vec![pnt.y()], vec![pnt.x()]);
            trace = match color {
                inputColor::NamedColor(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Point)
                },
                inputColor::Rgba(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Point)
                },
                _ => panic!(""),
            };
            traces.push(trace);
        }
        traces
    }
}

线要素的实现:

impl BuildTrace for traceParam<LineString>{
    fn build_trace(&self) -> Vec<Box<ScatterMapbox<f64,f64>>> {
        let mut traces:Vec<Box<ScatterMapbox<f64,f64>>> = Vec::new();
        for (line,color) in zip(&self.geometrys,&self.colors) {
            let mut lat:Vec<f64>= Vec::new();
            let mut lon:Vec<f64>= Vec::new();
            for coord in line.coords(){
                lat.push(coord.y);
                lon.push(coord.x);
            }
            let mut trace: Box<ScatterMapbox<f64, f64>> = ScatterMapbox::new(lat, lon);
            trace = match color {
                inputColor::NamedColor(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Line)
                },
                inputColor::Rgba(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Line)
                },
                _ => panic!(""),
            };
            traces.push(trace);
        }
        traces
    }
}

面数据的实现:

impl BuildTrace for traceParam<Polygon>{
    fn build_trace(&self) -> Vec<Box<ScatterMapbox<f64,f64>>> {
        let mut traces:Vec<Box<ScatterMapbox<f64,f64>>> = Vec::new();
        for (poly,color) in zip(&self.geometrys,&self.colors) {
            let mut lat:Vec<f64>= Vec::new();
            let mut lon:Vec<f64>= Vec::new();
            for coord in poly.exterior(){
                lat.push(coord.y);
                lon.push(coord.x);
            }
            let mut trace: Box<ScatterMapbox<f64, f64>> = ScatterMapbox::new(lat, lon);
            trace = match color {
                inputColor::NamedColor(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Polygon)
                },
                inputColor::Rgba(color) => {
                    MyTrace{color:*color,size:self.size,trace:trace}.set_trace(geo_type::Polygon)
                },
                _ => panic!(""),
            };
            traces.push(trace);
            for ipoly in poly.interiors(){
                let mut ilat:Vec<f64>= Vec::new();
                let mut ilon:Vec<f64>= Vec::new();
                for coord in poly.exterior(){
                    ilat.push(coord.y);
                    ilon.push(coord.x);
                }
                trace = ScatterMapbox::new(ilat, ilon);      
                trace = MyTrace{color:NamedColor::White,size:self.size,trace:trace}.set_trace(geo_type::Polygon);
                traces.push(trace);
            }
        }
        traces
    }
}

里面三个方法都用了一个处理trace的方法,实现如下:

struct MyTrace<T>{
    color:T,
    size:usize,
    trace:Box<ScatterMapbox<f64,f64>>
}

enum geo_type{
    Point,
    Line,
    Polygon,
}
trait SetTrace<T> {
    fn set_trace(&self,geo_type:geo_type)->Box<ScatterMapbox<f64,f64>>;
}

impl SetTrace<NamedColor> for MyTrace<NamedColor>{
    fn set_trace(&self,geo_type:geo_type)->Box<ScatterMapbox<f64,f64>> {
        match geo_type{
            geo_type::Point =>{
                let t = *self.trace.to_owned()
                .marker(Marker::new().color(self.color)).show_legend(false);
                Box::new(t)
            },

            geo_type::Line =>{
                let t = *self.trace.to_owned()
                .line(Line::new().width(self.size as f64).color(self.color)).show_legend(false);
                Box::new(t)
            },

            geo_type::Polygon=> {
                let t = *self.trace.to_owned()
                .fill(plotly::scatter_mapbox::Fill::ToSelf).fill_color(self.color).show_legend(false);
                Box::new(t)
            },
            _ => panic!("")
        }
    }
}

impl SetTrace<Rgba> for MyTrace<Rgba>{
    fn set_trace(&self,geo_type:geo_type)->Box<ScatterMapbox<f64,f64>> {
        match geo_type{
            geo_type::Point =>{
                let t = *self.trace.to_owned()
                .marker(Marker::new().color(self.color)).show_legend(false);
                Box::new(t)
            },

            geo_type::Line =>{
                let t = *self.trace.to_owned()
                .line(Line::new().width(self.size as f64).color(self.color)).show_legend(false);
                Box::new(t)
            },

            geo_type::Polygon=> {
                let t = *self.trace.to_owned()
                .fill(plotly::scatter_mapbox::Fill::ToSelf).fill_color(self.color).show_legend(false);
                Box::new(t)
            },
            _ => panic!("")
        }
    }
}

这两个方法,几乎99%是想同的,只是输入的颜色类型不一样,这样就是静态语言的麻烦之处了,只要函数签名不一致,就相当于两个方法,看到这里,大家可能想问,上一节讲过的泛型,在这里能用么?答案当然可以,不过就算用泛型,最终编译出来的代码也会因为编译器的处理,而实现函数单态化,即编译器会针对具体情况,编译出多个静态函数出来。所以这里如果继续抽象,也不是不行,但是算做过度设计了。

之后,就可以写一个绘制函数,然后进行调用了:

pub fn plot_draw_trace(traces:Vec<Box<ScatterMapbox<f64,f64>>>,outimg: Option<&str>){
    let mut plot = Plot::new();
    for t in traces{
        plot.add_trace(t);
    }
    let layout = _get_layout(1024, 800, Center::new(39.9, 116.3),MapboxStyle::Dark);
    plot.set_layout(layout);
    match outimg {
        Some(out) => plot.write_image(out, ImageFormat::PNG, 1200, 900, 1.0),
        None => plot.show(),
    }
}

//这个是一个内部函数,用来初始化构造制图参数的。
fn  _get_layout(width:usize, height:usize,cnt:Center,ms:MapboxStyle) -> Layout{
    Layout::new()
        .drag_mode(DragMode::Zoom)
        .margin(Margin::new().top(10).left(10).bottom(10).right(10))
        .width(width)
        .height(height)
        .mapbox(
            Mapbox::new()
                .style(ms)
                .access_token("pk.eyJ1IjoiYWxsZW5sdTIwMDgiLCJhIjoiY2xxZjNsaGtmMDd0ZTJqcWM1MzRmemx1NCJ9.TbiPQB6j1w9ilBP4pFHRRw")
                .center(cnt)
                .zoom(10),
        )
}

最后我们写一个测试调用方法,来绘制一下百度地图:

// 因为在Html里面绘制比较慢,所以我这里就仅画三个图层
#[test]
fn draw_bd_style(){
    let shp1 = "./data/shp/北京行政区划.shp";
    let poly1:Vec<Polygon> = readShapefile::shp::read_shp(shp1);
    let colors:Vec<inputColor> = (0..poly1.len())
    .map(|x|inputColor::Rgba(Rgba::new(240,243,250,1.0))).collect();
    let mut t1 = traceParam{geometrys:poly1,colors:colors,size:0}.build_trace();
    
    let shp2 = "./data/shp/面状水系.shp";
    let poly2:Vec<Polygon> = readShapefile::shp::read_shp(&shp2);
    let colors:Vec<inputColor> = (0..poly2.len())
    .map(|x|inputColor::Rgba(Rgba::new(108,213,250,1.0))).collect();
    let mut t2 = traceParam{geometrys:poly2,colors:colors,size:0}.build_trace();

    let shp3 = "./data/shp/高速.shp";
    let line1:Vec<LineString> = readShapefile::shp::read_shp(&shp3);
    let colors:Vec<inputColor> = (0..line1.len())
    .map(|x|inputColor::Rgba(Rgba::new(255,182,118,1.0))).collect();
    let mut t3 = traceParam{geometrys:line1,colors:colors,size:1}.build_trace();
    
    t1.append(&mut t2);
    t1.append(&mut t3);
    plot_draw_trace(t1,None);
}

绘制效果如下:

图片

注意:plotly.rs的JS引用的是Mapbox,所以网络访问上可能会有一些障碍,有可能需要科学上网……

打完收工。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1574553.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode-1483. 树节点的第 K 个祖先【树 深度优先搜索 广度优先搜索 设计 二分查找 动态规划】

LeetCode-1483. 树节点的第 K 个祖先【树 深度优先搜索 广度优先搜索 设计 二分查找 动态规划】 题目描述&#xff1a;解题思路一&#xff1a;暴力解法会超时&#xff01;【一级一级往上跳&#xff0c;效率太低】解题思路二&#xff1a;倍增&#xff0c;利用二进制运算&#xf…

Python可视化之pandas

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.解决坐标轴刻度负号乱码2.解决中文乱码问题3.折线图Series.plot()&DataFrame.plot()4.条形图5.箱线图6.区域面积图&#xff08;堆积折线图&#xff09;7.散点…

UNITY实战进阶-BatchRendererGroup+Jobs+Burst+RVO2+GPUAnimation 实现万人团战(一)

研究思路&#xff1a;GPUAnimation把动画放入GPU中处理&#xff0c;BatchRendererGroup进行动态批量渲染处理&#xff0c;JobsBurst进行多线程处理逻辑&#xff08;移动、攻击等&#xff09;&#xff0c;RVO2采用Jobs的寻路导航。 准备工作&#xff1a; Editor > Project S…

注意!今明两天广东等地仍有较强降雨

中央气象台监测显示 进入4月以来 我国江南、华南北部强降雨 接连而至 湖南、江西、浙江中南部 福建大部、广东中北部等地降雨量 较常年同期偏多1倍以上 上述地区部分国家观测站 日雨量突破4月历史极值 截至4月7日早晨 广东广州、惠州、清远 韶关、河源等地部分地区 …

填字母游戏【蓝桥杯】/博弈+dfs

填字母游戏 博弈dfs #include<iostream> #include<map> using namespace std; //要用map存储已经处理过的字符串不然会超时 map<string,int> m; //dfs返回的就是结果 int dfs(string s) {//剪枝if(m.find(s)!m.end()) return m[s];//找到LOL代表输了if(s.fi…

浅谈Redis和一些指令

浅浅谈一谈Redis的客户端 Redis客户端 Redis也是一个客户端/服务端结构的程序。 MySQL也是一个客户端/服务端结构的程序。 Redis的客户端也有多种形态 1.自带命令行客户端 redis-cli 2.图形化界面的客户端&#xff08;桌面程序&#xff0c;web程序&#xff09; 像这样的图形…

随机森林、AdaBoost 和 XGBoost 三者之间的主要区别

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 集成学习是一种强大的机器学习范式&#xff0c;它通过构建并结合多个学习器来提高预测性能。其中&#xff0c;随机森林、AdaBoost 和 XGBoost 是集成学习领域中著名且广泛应用的方法。尽管这些方法共享…

C++ | Leetcode C++题解之第12题整数转罗马数字

题目&#xff1a; 题解&#xff1a; const string thousands[] {"", "M", "MM", "MMM"}; const string hundreds[] {"", "C", "CC", "CCC", "CD", "D", "DC&qu…

绕过断言的LFI-Assertion101

总结 getwebshell : 发现疑似LFI的地方 → 测试..过滤 → 尝试断言绕过 → 远程加载反弹shell → getwebshell 提 权 思 路 : suid文件发现 → aria2c远程下载ssh私钥覆盖/root/.ssh → ssh公钥登录提权 准备工作 启动VPN 获取攻击机IP → 192.168.45.218 启动靶机 获取目标…

邮件服务器:Postfix

文章目录 邮件服务器的功能与工作原理电子邮件的问题Mail server与DNS 之间的关系邮件传输所需要的组件(MTA、MUA、MDA)以及相关协议用户收信时服务器端所提供的相关协议&#xff1a;MRA电子邮件的数据内容 使用Postfix与Dovecot部署邮件系统部署基础的电子邮件系统配置Postfix…

山海鲸智慧农业可视化:开启农业现代化高效管理新时代

随着科技的不断进步&#xff0c;农业现代化已成为当今社会发展的重要趋势。在这一背景下&#xff0c;山海鲸智慧农业可视化解决方案应运而生&#xff0c;为农业生产带来了革命性的变革。它通过创新的可视化技术&#xff0c;将农业生产过程中的各个环节进行高效整合&#xff0c;…

OSPF协议详解

静态缺点 1、中大型复杂网络----配置量大 2、不能实时收敛 动态-----可以实时收敛 IGP----内部网关路由协议 RIP OSPF EIGRP ISIS EGP----外部网关路由协议 BGP IGP &#xff08;选路佳 占用资源 收敛快&#xff09;----一个协议好需满足这三个 距离矢量 DV RIP…

Pandas分箱/离散化cut与qcut的区别

cut与qcut区别 1、pd.cut()2、pd.qcut()3、cut与qcut区别 Pandas提供了智能剪贴功能&#xff1a;pd.cut()与pd.qcut()&#xff0c;它们通常用于更方便直观地处理关系型或标签型数据&#xff0c;将数据进行分箱/离散化 1、pd.cut() 我们可以通过两种方式使用cut()函数&#xff…

C++的并发世界(七)——互斥锁

0.死锁的由来 假设有两个线程T1和T2&#xff0c;它们需要对两个互斥量mtx1和mtx2进行访问。而且需要按照以下顺序获取互斥量的所有权&#xff1a; -T1先获取mte1的所有权,再获取mt2的所有权。 -T2先获取 mtx2的所有权。再铁取 mtx1的所有权。 如果两个线程同时执行&#xff0c…

Android Studio学习4——gradle文件

视频讲解 https://developer.android.google.cn/studio/releases/gradle-plugin.htmIl#updating-gradle

二分查找与搜索树高频问题-算法通关村

二分查找与搜索树高频问题-算法通关村 1 基于二分查找的拓展问题 1.1 山脉数组的封顶索引 LeetCode852&#xff1a;这个题的要求有点啰嗦&#xff0c;核心意思就是在数组中的某位位置i开始&#xff0c;从0到 i 是递增的&#xff0c;从i1到数组最后是递减的&#xff0c;让你找到…

JavaEE 初阶篇-深入了解线程池(线程池创建、线程池如何处理任务)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 线程池概述 1.1 线程池的优点 1.2 不使用线程池的问题 1.3 线程池的工作原理图 1.4 如何创建线程池&#xff1f; 2.0 通过 ThreadPoolExecutor 类自定义创建线程…

数据结构与算法笔记:递归函数设计技巧

ACM金牌带你零基础直达C语言精通-课程资料 本笔记属于船说系列课程之一&#xff0c;课程链接&#xff1a; 哔哩哔哩_bilibilihttps://www.bilibili.com/cheese/play/ep66799?csourceprivate_space_class_null&spm_id_from333.999.0.0 你也可以选择购买『船说系列课程-年度…

Tensorflow2.0笔记 - 自定义Layer和Model实现CIFAR10数据集的训练

本笔记记录使用自定义Layer和Model来做CIFAR10数据集的训练。 CIFAR10数据集下载&#xff1a; https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz 自定义的Layer和Model实现较为简单&#xff0c;参数量较少&#xff0c;并且没有卷积层和dropout等&#xff0c;最终准确率…

穿越代码之海:探寻结构体深层逻辑,展望未来应用新天地

欢迎来到白刘的领域 Miracle_86.-CSDN博客 系列专栏 C语言知识 先赞后看&#xff0c;已成习惯 创作不易&#xff0c;多多支持&#xff01; 结构体作为一种数据结构&#xff0c;其定义和特点决定了它在各种应用中的广泛适用性。随着科技的进步和新兴行业的不断涌现&#xf…