用可视化案例讲Rust编程3. 函数分解与参数传递

上一节我们在绘制面要素的时候,发现了函数功能体是三个不同步骤组成的:

  1. 读取文件获得geometry
  2. 把geometry转变为绘图元素trace
  3. 把绘图元素绘制到地图上

像我们上一节那样,把所有的功能都写在一个函数里面,这样的函数灵活性太差,例如我们要读取和绘制若干个点、线、面,那么如果不去修改,那么每读一个shapefile就要重复去写一个方法,那就太繁琐了,我们重构的第一步,就是要把各种功能尽量的切分出来,形成一个个能够独立运行和维护的模块,所以今天我们就把昨天那个代码给切分成多个函数,增加重用,减少冗余。

首先是第一个函数,用于读取shapefile,输入的参数就是shapefile的路径,输出是解析好的集合:(下面是的注释是写个没有学习过Rust的同学的,了解过的同学就可以略过了)

//Rust通过fn定义函数,函数里面的参数有两种模式
//带`&`符号的的是标识传递进去的是一个引用(不懂引用的同学,暂时可以先跳过)
//如果不带&则标识输入的是一个具体的值。
//函数后面的 -> 表示函数的返回值的类型,这里是一个Polygon类型的vec
fn read_polygon(shapefile: &str) ->Vec<Polygon>{let shp = shapefile::read_as::<_,shapefile::Polygon, shapefile::dbase::Record>(shapefile,).expect("Could not open polygon-shapefile");//构建输入参数let mut polygons:Vec<Polygon> = Vec::new();//shapefile读取文件默认得到的是一个MultiPolygon//for (polygon, polygon_record) in shp {let geo_mpolygon: geo_types::MultiPolygon<f64> = polygon.into();for poly in geo_mpolygon.iter(){polygons.push(poly.to_owned());}}//Rust中,如果语句后面没有分号`;`则表示这是一个表达式,会返回一个结果//如果这个表达式出现在函数最后,则等同于return polygons的功能polygons
}

然后就是第二个函数,把解析出来的polygon集合,转换为plotly的绘图元素trace:

//输入两个参数,上面输入的polygons的几何,和一个颜色参数。
//polygon会有
fn build_polygon(polygons: &Vec<Polygon>,color:Rgba)->Vec<Box<ScatterMapbox<f64,f64>>>{let mut trace_vec = Vec::new();for ps in polygons{let mut lon:Vec<f64> = Vec::new();let mut lat:Vec<f64> = Vec::new();for p in ps.exterior(){lon.push(p.x);lat.push(p.y);}let trace = ScatterMapbox::new(lat, lon).mode(Mode::None).fill(plotly::scatter_mapbox::Fill::ToSelf).fill_color(color);trace_vec.push(trace);}trace_vec
}

第三个函数,就是把trace绘制成图了:

//这里直接就绘制出来了,所以没有返回值
fn draw_trace(traces:&Vec<Box<ScatterMapbox<f64,f64>>>){let mut plot = Plot::new();let layout = Layout::new().drag_mode(DragMode::Zoom).margin(Margin::new().top(10).left(10).bottom(10).right(10)).width(1024).height(700).mapbox(Mapbox::new().style(MapboxStyle::WhiteBg).center(Center::new(39.9, 116.3)).zoom(9),);plot.set_layout(layout);for t in traces.iter(){plot.add_trace(t.to_owned());}plot.show();
}

有这三个函数之后,我们调用就很方便了:

#[test]
fn test_draw_2(){let poly1 = "./data/shp/北京行政区划.shp";let t1 = build_polygon(&read_polygon(poly1), Rgba::new(240,243,250,1.0));draw_trace(&t1);
}

效果如下: 

img

如果我们要绘制两个图层,例如再这个图层上,在绘制一个面状水系,就很省事了:

#[test]
fn test_draw_2(){let poly1 = "./data/shp/北京行政区划.shp";let mut t1 = build_polygon(&read_polygon(poly1), Rgba::new(240,243,250,1.0));let poly2 = "./data/shp/面状水系.shp";let mut t2 = build_polygon(&read_polygon(poly2), Rgba::new(108,213,250,1.0));t1.append(&mut t2);draw_trace(&t1);
}

效果如下: 

img

但是现在带来了一个问题,如果我们要输入的是线要素呢?那不是有得重新写一个读取线要素的方法么?当然,这么写是一点问题都没有的,如下所示:

fn read_line(shapefile: &str) ->Vec<LineString>{let shp = shapefile::read_as::<_,shapefile::Polyline, shapefile::dbase::Record>(shapefile,).expect(&format!("Could not open polyline-shapefile, error: {}", shapefile));let mut linestrings:Vec<LineString> = Vec::new();for (pline, pline_record) in shp {let geo_mline: geo_types::MultiLineString<f64> = pline.into();for line in geo_mline.iter(){linestrings.push(line.to_owned());}}linestrings
}
  • 从代码中可以看见,除了读取部分稍有不同,线要素与面要素的处理原则大致是差不多的。如果是动态类型的语言,可以直接写成一个方法也没问题,但是Rust是静态类型,也就是你需要在编译之前,就固定下类型来的,这个问题,我们明天再说。

同样的,读取了线要素,还要把线要素转换成线类型的绘图要素,如下:

fn build_line(lines: &Vec<LineString>,colors:Vec<Rgba>)->Vec<Box<ScatterMapbox<f64,f64>>>{let mut trace_vec = Vec::new();for (line,color) in zip(lines,colors){let mut lon:Vec<f64> = Vec::new();let mut lat:Vec<f64> = Vec::new();for p in line.coords(){lon.push(p.x);lat.push(p.y);}let trace = ScatterMapbox::new(lat, lon).mode(Mode::Lines).marker(Marker::new().color(color));trace_vec.push(trace);}trace_vec
}

这样就得到了一个和面要素转换的绘图要素是一样的,然后我们就可以用同一个方法来解决了,如下:

  #[test]
fn test_draw_2(){let poly1 = "./data/shp/北京行政区划.shp";let mut t1 = build_polygon(&read_polygon(poly1), Rgba::new(240,243,250,1.0));let poly2 = "./data/shp/面状水系.shp";let mut t2 = build_polygon(&read_polygon(poly2), Rgba::new(108,213,250,1.0));let line1 = "./data/shp/高速.shp";let line1 = read_line(line1);let line1_color:Vec<Rgba> = (0..line1.len()).map(|x|Rgba::new(255,182,118,1.0)).collect();let mut t3 = build_line(&line1,line1_color);//把三个trace合并成一个,传递到绘图函数里面即可t1.append(&mut t2);t1.append(&mut t3);draw_trace(&t1);
}

效果如下: 

img

以此推类,如果有点要素,照样处理就行。

看到这里,有同学会问,我们有多少种类型,就一定要写多少个方法,这个可以理解,每个方法都用不同的名称来调用,让调用的人也太难记了,有没有一种方法,就调用一个方法,根据输入的类型,自动去匹配逻辑处理能力行不行?

当然是没有问题,所以下一节,我们来解决这个问题。

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

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

相关文章

代理IP购买:选择按流量还是端口收费的代理?

​ 代理通常按以下两种标准之一收费&#xff1a;GB 或端口。但您应该选择哪一个&#xff1f;它们与其他产品有何不同&#xff1f;主要取决于您的需求&#xff0c;每种类型都有自己的优缺点&#xff0c;适合不同的情况。 在本文中&#xff0c;我们将详细分析每种类型&#xff0…

【VSCode】增加或减少一Tab的间隔

文章目录 示例 1&#xff1a;给 console 打印添加一 Tab 的间隔&#xff08;按下tab键即可&#xff09; // 原有的格式 if(a b){ console.log(true) }else{ console.log(false) }// 改变后的格式 if(a b){console.log(true) }else{console.log(false) }示例 2&#xff1a;给…

专有钉钉开发记录,及问题总结

先放几个专有钉钉开发文档 专有钉钉官网的开发指南 服务端(后端)api文档 前端api文档 前端开发工具下载地址 小程序配置文件下载地址 后端SDK包下载地址 专有钉钉域名是openplatform.dg-work.cn 开发记录 开发专有钉钉时有时会遇到要使用钉钉的api&#xff1b;通过 my 的方…

JavaWeb学习|Cookie

学习材料声明 所有知识点都来自互联网&#xff0c;进行总结和梳理&#xff0c;侵权必删。 引用来源&#xff1a;尚硅谷最新版JavaWeb全套教程,java web零基础入门完整版 在此之前&#xff0c;复习一下如何创建项目 首先创建正常的Java项目&#xff0c;之后选择add framework…

C#学习笔记-反射

前言 反射是.NET中的重要机制&#xff0c;通过反射可以得到*.exe或*.dll等程序集内部的接口、类、方法、字段、属性、特性等信息&#xff0c;还可以动态创建出类型实例并执行其中的方法。 反射的功能很强大&#xff0c;任何复杂抽象的分层架构或者复杂的设计模式均是建立在这些…

任务修复实例(1)

实例1 任务名&#xff1a;增强防御&#xff08;quest_template.id 8490&#xff09; 涉及的两个数据表分别为 smart_script 和 creature_summon_groups smart_script Reactstate 取值参考源码 UnitDefines.h 的 ReactStates 定义&#xff0c;其中&#xff1a;0为被动&#…

低导通电阻、汽车级STD20NF06LAG,STO47N60M6、STO33N60M6、STO36N60M6 600V MDmesh™ M6 功率MOSFET

1、STD20NF06LAG 汽车级N沟道60V、32 mOhm典型值、24 A STripFET II功率MOSFET STripFET VI™功率MOSFET是采用ST专有STripFET™技术和新型栅极结构的增强模式MOSFET。该款受益于STripFET™技术的功率MOSFET采用沟槽技术&#xff0c;可实现高效率和低RDS(on) &#xff0c;满足…

SparkSql---用户自定义函数UDFUDAF

文章目录 1.UDF2.UDAF2.1 UDF函数实现原理2.2需求:计算用户平均年龄2.2.1 使用RDD实现2.2.2 使用UDAF弱类型实现2.2.3 使用UDAF强类型实现 1.UDF 用户可以通过 spark.udf 功能添加自定义函数&#xff0c;实现自定义功能。 如&#xff1a;实现需求在用户name前加上"Name:…

83、评估权值预加载带来的性能提升

上两节介绍的权值预加载技术&#xff0c;在很多业务中是真实存在的。 只不过限于本小册内容以及大部分同学硬件设备的局限&#xff0c;这里无法通过真正的推理框架(比如 tvm, pytorch) GPU 来实现底层细节&#xff0c;这些推理框架中一般会将类似的优化技术封装起来。 作为 …

APPium简介及安装

1 APPium简介 1. 什么是APPium&#xff1f; APPium是一个开源测试自动化框架&#xff0c;适用于原生、混合或移动Web应用程序的自动化测试工具。 APPium使用WebDriver协议驱动iOS、Android等应用程序。 2. APPium的特点 支持多平台&#xff08;Android、iOS等&#xff09; …

uniapp组件库Card 卡片 的使用方法

目录 #平台差异说明 #基本使用 #配置卡片间距 #配置卡片左上角的缩略图 #配置卡片边框 #设置内边距 #API #Props #Slot #Event 卡片组件一般用于多个列表条目&#xff0c;且风格统一的场景。 #平台差异说明 AppH5微信小程序支付宝小程序百度小程序头条小程序QQ小程…

基于STM32的以太网通信协议选择与实现

在基于STM32的以太网通信中&#xff0c;主要涉及到选择合适的通信协议和实现对应的功能代码。常见的通信协议包括TCP/IP、UDP、HTTP等&#xff0c;选择合适的协议取决于具体应用需求。以下将介绍在STM32上进行以太网通信时&#xff0c;常用的通信协议选择以及对应功能代码的实现…

CSDN·COC城市开发者组织2023年度聚会上海站纪实

目录 前言活动宣传活动议程活动总结结束语 前言 2023年是CSDNCOC成立之年&#xff0c;经过一年的不断发展和壮大&#xff0c;COC上海城市开发者社区的队伍不断壮大&#xff0c;本着每个月都有线下活动的原则&#xff0c;先后举办大大小小的线下沙龙活动20余场。与此同时&…

trucksim三车队列仿真 matlab一直闪退问题

Trucksim2019 matlab版本2023a 基本框架应该都清楚 下图是主界面 overlay videos and polots with other runs 模块是 关联另一个车辆模型 核心是下图标红是核心 Number of vehicle codes 选择ALL 不要选

世微AP5151芯片低压差 线性降压 恒流驱动IC 台灯 指示灯 矿灯

概述 AP5151 是一种低压差、线性降压、 固定输出电流的 LED 恒流驱动器。 除 LED 外&#xff0c;AP5151 无需外接其它元 器件即可构成一个恒流输出的 LED 驱动 电路。 AP5151 内置过热保护功能&#xff0c;可有效 保护芯片&#xff0c;避免结温超过 120oC 时因过热 而造成损坏…

【图形学】双三次贝塞尔曲线绘制方法

双三次贝塞尔曲线的定义 双三次贝塞尔曲面是由16个控制点定义的曲面&#xff0c;通常表示为4x4矩阵。 曲面的公式如下&#xff1a; p ( u , v ) ∑ i 0 3 ∑ j 0 3 P i , j B i , 3 ( u ) B j , 3 ( v ) , ( u , v ) ∈ [ 0 , 1 ] [ 0 , 1 ] p(u,v)\sum_{i0}^3\sum_{j0}…

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson10(电话会话Ⅱ)

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 10 Telephone Conversation Ⅱ 电话会话&#xff08;二&#xff09;会话A会话B笔记I would like to do&#xff08;Id like to to do&#xff09;我想…

如何从视频号中提取视频的文案?抖音文案也可提取

最近不少小伙伴刷抖音看看视频一个一个的进行抄&#xff0c;太慢了。​今天分享一个工具搞定怎么提取视频文案的方法&#xff01; 提取文案小助手 提取文案小助手是专注视频创作者的利器&#xff0c;帮助用户一键提取视频号中的文案&#xff0c;获取的方式比较简单&#xff0c…

Sui TVL跻身全链前十名|全面详解Sui原生功能如何促使DeFi蓬勃发展

根据DefiLlama数据显示&#xff0c;1月28日Sui TVL超过4.02亿美元&#xff0c;跻身所有区块链TVL前十名&#xff01; Sui的创新应用原生功能为DeFi应用铺平了道路&#xff0c;为用户提供了良好的体验。每个原生功能&#xff0c;从促进高效的共享流动性到简化用户互动&#xff0…

js中contextmenu、click和mousedown的区别在哪?

contextmenu、click和mousedown的定义 contextmenu&#xff1a;在鼠标右键点击时触发。可以在此事件中执行特定的操作&#xff0c;比如显示自定义的右键菜单或者阻止浏览器默认的上下文菜单弹窗。它是为了满足特定场景下对右键点击的定制化处理需求。 click事件是通用的鼠标点…