the relation between rust, wasm and frontend

# 使用rust完成基于wasm的gis工具的简明教程

5 min read
Table of Contents

rust

wasm

get started

  1. 首先我们需要创建一个新的rust项目,由于我们创建的是库项目,所以需要添加--lib参数

    Terminal window
    cargo new gis-wasm --lib
  2. 然后使用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"]}
  3. 接下来我们需要实现具体的代码逻辑,我将会实现一个简单的功能,即计算geojson面要素的bbox

    1. 首先我们需要导入需要用到的依赖

      use serde::{ Deserialize, Serialize };
      use wasm_bindgen::{JsValue, prelude::wasm_bindgen};
      use web_sys::js_sys::{ Array, JsString };
    2. 然后我们根据需要定义几个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]>>>,
      },
      }
    3. 接下来我们实现计算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
    }
My avatar

Thanks for reading my blog post! Feel free to check out my other posts or contact me via the social links in the footer.


More Posts

Comments