# 使用rust完成基于wasm的gis工具的简明教程
5 min read
Table of Contents
rust
wasm
get started
-
首先我们需要创建一个新的rust项目,由于我们创建的是库项目,所以需要添加
--lib参数Terminal window cargo new gis-wasm --lib -
然后使用
vscode打开项目,并进行依赖的安装,安装依赖不需要手动下载,只需要在Cargo.toml文件中添加依赖即可,下面是我们需要添加的依赖和其他配置:[package]name = "gis-wasm"version = "0.1.0"edition = "2024"[lib]crate-type = ["cdylib", "rlib"][package.metadata.wasm-pack.profile.release]wasm-opt = false[dependencies]geo = "0.31.0"geojson = "0.24.2"serde = "1.0.228"serde_json = "1.0.145"wasm-bindgen = "0.2.93"wasm-bindgen-futures = "0.4"web-sys = { version="0.3.70", features=["console"]} -
接下来我们需要实现具体的代码逻辑,我将会实现一个简单的功能,即计算geojson面要素的bbox
-
首先我们需要导入需要用到的依赖
use serde::{ Deserialize, Serialize };use wasm_bindgen::{JsValue, prelude::wasm_bindgen};use web_sys::js_sys::{ Array, JsString }; -
然后我们根据需要定义几个struct和enum
// 这里是为struct加上需要的derive#[derive(Debug, Clone, Serialize, Deserialize)]struct PolygonGeoJson<T = Geometry> {//由于"type"是rust的关键字,所以我们使用r#type来命名,并使用serde的rename属性来指定序列化和反序列化时的名称#[serde(rename = "type")]r#type: String,features: Vec<PolygonFeature<T>>,}#[derive(Debug, Clone, Serialize, Deserialize)]struct PolygonFeature<T> {#[serde(rename = "type")]r#type: String,geometry: T,}#[derive(Debug, Clone, Serialize, Deserialize)]//这里我们使用serde的tag属性来指定枚举的变体类型,即根据哪个字段来区分不同的变体#[serde(tag = "type")]enum Geometry {Polygon {coordinates: Vec<Vec<[f64; 2]>>,},MultiPolygon {coordinates: Vec<Vec<Vec<[f64; 2]>>>,},} -
接下来我们实现计算bbox的逻辑
//这里需要加上wasm_bindgen的宏,以便将函数标记为wasm-pack可以导出的函数#[wasm_bindgen]pub fn get_bbox(geojson_str: JsString) -> Array {let array = Array::new();//将JsString转换为Rust的String类型let geojson_string: String = geojson_str.into();let mut min_x = f64::INFINITY;let mut min_y = f64::INFINITY;let mut max_x = f64::NEG_INFINITY;let mut max_y = f64::NEG_INFINITY;//这里定义一个宏,用于计算最大最小值,以免重复代码macro_rules! get_max_min_xy {($f:expr) => {$f.iter().for_each(|cor| {let x = cor[0];let y = cor[1];min_x = min_x.min(x);min_y = min_y.min(y);max_x = max_x.max(x);max_y = max_y.max(y);});};}let geojson: PolygonGeoJson = serde_json::from_str(&geojson_string).expect("serde parse failed");if geojson.features.is_empty() {for _ in 0..4 {array.push(&JsValue::from_f64(f64::NAN));}} else {//遍历每个feature的geometry,根据不同的geometry类型进行处理for feature in &geojson.features {match &feature.geometry {//最后通过rust的迭代器配合闭包和自定义宏遍历所有坐标点,计算最小和最大值Geometry::MultiPolygon { coordinates } => {let _ = coordinates.iter().for_each(|f| {f.iter().for_each(|c| {get_max_min_xy!(c);});});}Geometry::Polygon { coordinates } => {let _ = coordinates.iter().for_each(|f| {get_max_min_xy!(f);});}}}array.push(&JsValue::from_f64(min_x));array.push(&JsValue::from_f64(min_y));array.push(&JsValue::from_f64(max_x));array.push(&JsValue::from_f64(max_y));}//返回计算得到的bbox坐标数组array} -