用可视化案例讲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 的方…

微信小程序canvs画布修改元素线条粗细、颜色、填充状态

目录 一、获取画布信息 二、修改元素线条粗细 三、修改元素颜色

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:…

P8655 [蓝桥杯 2017 国 B] 发现环

好水啊&#xff0c;那我来水一篇题解。 题目就是要你在树上找环。那么我们想&#xff0c;怎么知道出现了环&#xff1f; 首先明确一点&#xff0c;在一棵树上&#xff0c;任意两点有且只有一条路径使它们相连。所以当我们发现原本就相连的点又要再连一次就知道出现了环。 是…

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; …

Spring Cache简明教程

Spring Cache简明教程 引言什么是Spring Cache&#xff1f;核心特性应用场景如何工作 如何使用1. 添加依赖2. 启用缓存3. 配置缓存4. 使用缓存注解 使用缓存的注意事项缓存的数据序列化缓存键的生成缓存内容的一致性缓存的并发问题缓存穿透缓存雪崩缓存的存储容量方法的可见性和…

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余场。与此同时&…

洛谷p1157组合的输出

组合的输出 题目描述 排列与组合是常用的数学方法&#xff0c;其中组合就是从 n n n 个元素中抽出 r r r 个元素&#xff08;不分顺序且 r ≤ n r \le n r≤n&#xff09;&#xff0c;我们可以简单地将 n n n 个元素理解为自然数 1 , 2 , … , n 1,2,\dots,n 1,2,…,n&a…

NRZ Modulation

NRZ Modulation 正文简介NRZ 的优势 正文 简介 全称不归零 non-return to zero(NRZ) 调制。代表了使用两个信号等级的一种逻辑信号。一个等级为 1&#xff0c;一个等级为 0。负电压对应逻辑电路中的 0&#xff0c;正电压对应逻辑电路中的 1。对于 NRZ 传输&#xff0c;波特率…

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

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

如何解决云计算中的性能和延迟问题?

随着云计算的普及&#xff0c;越来越多的企业开始将业务迁移到云端。然而&#xff0c;随着业务的发展和数据量的增长&#xff0c;云计算中的性能和延迟问题也逐渐凸显出来。这些问题不仅会影响用户体验&#xff0c;还会对企业的业务发展造成负面影响。因此&#xff0c;如何解决…