用可视化案例讲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.hqwc.cn/news/438743.html

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

相关文章

防御保护第四次作业

防火墙的智能选路 就近选路 --- 我们希望在访问不同运营商的服务器是&#xff0c;通过对应运营商的链路。这样可以高 通信效率&#xff0c;避免绕路。 策略路由 -- PBR 传统的路由&#xff0c;仅基于数据包中的目标IP地址查找路由表。仅关心其目标&#xff0c;所以&#…

Linux中条件变量的使用及注意事项

条件变量 应用场景&#xff1a;生产者消费者问题(类似于出租车和乘客问题)必要性&#xff1a;为了实现等待某个资源&#xff0c;让线程休眠。提高运行效率函数如下&#xff1a; int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); int p…

扩展学习|一文明晰推荐系统应用开发核心技术发展

文献来源&#xff1a;Lu J, Wu D, Mao M, et al. Recommender system application developments: a survey[J]. Decision support systems, 2015, 74: 12-32. 主题&#xff1a;关于推荐系统应用开发的调查研究 关键词:推荐系统、电子服务个性化、电子商务、电子学习、电子政务 …

【一】esp32芯片开发板环境搭建

1、esp32的源码在github上的地址 不同的芯片支持的源码版本不一样&#xff0c;需要根据自己的实际的esp32开发板的芯片下载不用版本的代码 esp32支持多种开发方式&#xff0c;如arduino&#xff0c;ESP-IDF等。官方推荐使用idf开发&#xff0c;ESP-IDF 是乐鑫官方推出的物联网开…

thinkadmin用户列表点击用户晒单记录弹出窗体显示关联表记录

admin.html <!--{if auth("mark")}--><a class="layui-btn layui-btn-sm layui-btn-primary" data-modal="{:url(user.mark/index)}?user_id={$vo.id}"

HarmonyOS Slider滑动条组件讲解

好 那么 我们继续来说 ArkTS提供的基础组件 今天 我们来说 slider slider 是一个滑块组件 我们还是在组件中 编写一个 Slider 然后鼠标移上去 弹出提示时 点击进入API 这个组件相对是比较复杂的 min 数字类型 默认值 0 控制 最小是多少 max 数值类型 默认值 100 我们滑块 正…

虚拟机设置静态ip

有时候搭环境需要局域网&#xff0c;设置一下虚拟机静态ip&#xff0c;这里做个记录&#xff1a; 这里我用的是ubuntu18.04的虚拟机&#xff0c;安装完成之后&#xff0c;点击进入设置 这里设置一下桥接模式 这个时候输入ifconfig&#xff0c;就是和主机一个网段了&#xff…

【UEFI实战】Redfish的BIOS实现——生成EDK数据

生成Redfish文件 Redfish数据的表示形式&#xff0c;最常用的是JSON。将JSON表示的数据转换成C语言可以操作的结构体&#xff0c;是必不可少的步骤。当然如果手动转换的话&#xff0c;需要浪费大量的时间&#xff0c;因此DMTF组织开发了一个工具&#xff0c;用于将JSON数据快速…

大专生能不能学习鸿蒙开发?

目前安卓有2,000万的开发者。本科及以上学历占比为35%&#xff1b;iOS有2,400万开发者&#xff0c;本科及以上学历占比为40% 绝大多数的前端开发者都是大专及以下学历&#xff0c;在2023年华为开发者大会上余承东透露华为的开发者目前有200万&#xff0c;但鸿蒙开发者统计的数据…

Java 数据库连接

1&#xff0c;JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 JDBC概念 JDBC 就是使用Java语言操作关系型数据库的一套API 全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接 …

实现Crm系统的灵活配置,满足不同行业客户需求

目录 一&#xff1a;数据模型配置 二&#xff1a;流程配置 三&#xff1a;扩展性配置 实现CRM系统的可配置性需要关注以下几个方面&#xff1a; 一&#xff1a;数据模型配置 为了满足企业的个性化需求&#xff0c;CRM系统需要提供灵活的数据模型配置。用户可以根据自己的业…

vue预览pdf文件的几种方法

文章目录 vue预览pdf集中方法方法一&#xff1a;方法二&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a; 方法三&#xff1a;展示效果&#xff1a;需要包依赖&#xff1a;代码&#xff1a;自己调参数&#xff0c;选择符合自己的 vue预览pdf集中方法 我…