41 个版本 (20 个稳定版)

2.0.12 2024年6月9日
2.0.10 2024年3月12日
2.0.8 2023年12月21日
2.0.7 2023年11月16日
0.1.2 2022年12月31日

#6 in 地理空间

Download history 1/week @ 2024-04-28 156/week @ 2024-05-05 8/week @ 2024-05-12 17/week @ 2024-05-19 2/week @ 2024-05-26 8/week @ 2024-06-02 174/week @ 2024-06-09 33/week @ 2024-06-16 4/week @ 2024-06-23 30/week @ 2024-06-30 300/week @ 2024-07-28 20/week @ 2024-08-04 11/week @ 2024-08-11

每月331 次下载
用于 2 crates

MIT 许可证

530KB
15K SLoC

Rust D3 Geo

Rust 2021 版本。

crates.io Documentation Download

这是将 d3-geo 移植到 RUST 的版本。它是移植的 d3-modules 之一

此库允许开发自定义地图。它提供了一套完整的投影方法以及缩放、旋转和转换最终图像的手段。投影仪处理 GeoJSON 对象形式的正多边形、线和点。此外,此库还可以用于计算这些对象的长度、面积和质心

CHANGELOG.md 包含了 v1.x 和 2.0.0 之间破坏性变更的摘要。

何时使用库的 Rust 版本

当开发处理大型数据集的交互式应用程序时,JavaScript 库的局限性变得明显。例如,examples/globe 应用程序在地球上一张高度详细的(1:50M 分辨率)地图上运行。在笔记本电脑上,这超出了 JavaScript 版本的能力。

可用投影
Albers 圆锥等面积 正形投影
AlbersUsa 等距 正射投影
方位等面积 等角圆柱 墨卡托
方位等距 等面积 横墨卡托
保形 等地球 正射投影

示例

这些示例旨在帮助开发人员将现有的 JavaScript 转换为 Rust。它们可以在与此 crate 相关的 GitHub 仓库中找到。

描述
examples/globe/rotating

这是将此 d3-geo 示例移植到 Rust 的版本

www.d3indepth.com/geographic/

JavaScript 版本通过使用低分辨率地图来妥协。这里不需要这种妥协。

此地球被渲染到 HTML CANVAS 元素中。

由于性能原因,此示例最好通过运行 "cargo build" 然后运行 "cargo serve" 来查看,使用 --release 标志编译 rust 代码。

(比例尺 1:50M)

examples/globe/svg

地球以 SVG 图像的形式渲染。

SVG 在需要保留数据的语义意义时非常有用。示例展示了如何加载/解析/显示地球作为单独的 SVG PATH 元素。

还包括生成 SVG 网格的代码示例。

(比例尺 1:50M)

examples/globe/drag_and_zoom

此地球被渲染到 HTML CANVAS 元素中

它故意混合了 TypeScript 方法与 Rust。TypeScript 负责处理鼠标事件和操纵用于计算适当旋转变化的四元数。在 TypeScript 渲染循环中调用 Rust 函数来渲染地球。

此示例目前正在迅速开发中。

(比例尺 1:50M)

examples/projections

所有可用的投影都被渲染到 HTML CANVAS 元素中

作为建立信心的练习,此演示展示了 JavaScript 和 Rust 两种方式渲染的所有投影的并排比较。

(比例尺 1:50M)

examples/globe/albers_usa

显示美国所有县。

AlbersUSA 与其他投影不同。阿拉斯加和夏威夷被渲染为插图。从代码中可以看出,必须使用 Multi-drain 来收集三个投影。

(比例尺 1:10M)

examples/ring

SVG 示例

提供 RUST 和 JavaScript 两种语言的示例代码,用于渲染复杂的多边形。(正射投影和球面投影)


概述了所有示例中找到的常见步骤

  1. 对于给定的投影,使用其默认投影构建器,更改比例尺、平移等,然后调用 build() 来构建投影仪。

    let projector = Stereographic::<f64>::builder()
      .scale_set(100_f64)
      .translate_set(&Coord {
         x: 300_f64,
         y: 300_f64,
      })
      .clip_angle(90_f64)
      .precision_set(&10_f64)
      .build();
    
  2. 构建一个 PathBuilder

    路径是一组节点,每个步骤以某种方式转换几何对象。

    然后对象沿着路径流动。

    以下是关键节点的概述。

    裁剪:使用了两种策略,“对跖线”和“裁剪角度” [见 clip_angle_set() 和 clip_angle_reset()]

    重采样:可以通过声明一个分离距离来减少密集几何形状,在这个距离下,用于描述多边形和线的点被认为是不可区分的 [见 precision_set()]

    边界:可以设置投影空间框,并且只有在此范围内的几何形状将被显示。部分位于框内的多边形将被重构以符合框的边缘。[见 clip_extent_set() clip_extent_clear()]

    端点是特殊的路径节点,它持有计算结果。有多种端点可供选择,例如面积、质心、长度,可以用来计算多边形或线的属性。这些示例仅显示了渲染到 HTML canvas 元素或 SVG 路径元素的端点。

    在渲染到 HTML canvas 时,端点持有 Path2D "渲染上下文"。

     // Construct a PathBuilder linked to Path2d
     // rendering context.
     let path2d = Path2d::new()?;
     let endpoint = PathBuilder::new(path2d);
     let pb = PathBuilder::new(endpoint);
     let path = pb.build();
    

    在生成 SVG 图像时,端点持有可以从中构建 PATH 元素的字符串值。

      let pb = PathBuilder::pathstring();
      let path = pb.build();
    
  3. 请参阅不同的示例,但常见的下一步是构建一个 PathBuilder 对象,然后将一个几何对象流到其中 :-

       // 'countries' is a geometry extracted from
       // a world map json file.
       path.stream(&countries)
    

运行示例


要求

要查看示例应用程序,请创建开发构建,或按以下方式构建静态网站

开始并运行开发构建

git clone https://github.com/martinfrances107/rust_d3_geo.git
cd rust_d3_geo/examples/ring/
npm install
npm run start

最后一个命令 "npm run start" 将自动打开您默认浏览器中的 http://localhost:8080

性能:构建静态网站

通过构建静态网站并直接查看它,可以实现更好的性能。以更长的构建时间为代价,代码的 Rust 部分使用 "--release" 标签构建

  git clone https://github.com/martinfrances107/rust_d3_geo.git
  cd rust_d3_geo/examples/ring
  npm install
  npm run build
  npm run serve

基准测试

有两个不同的计算环境

  1. 浏览器:这是一个高度受限的环境。这里有两个等效的基准测试,一个是用于Rust的 rust_d3_geo_voronoi,另一个是用于JavaScript的 d3-geo-voronoi。Rust以服务工作者的形式运行,没有对DOM的直接控制。尽管这个基准测试运行速度快了两倍,但线程之间通过JsValue传递对象仍然对性能有负面影响。

  2. 类似Node的环境:与crate关联的GitHub仓库有两个“配置目标”和两个“基准测试”,可以用来在此环境中发现瓶颈。这些基准测试是基于Criterion.rs的微基准测试。

火焰图

profile_target是一个二进制文件,输出包含带有经纬线标记的地球SVG图像的HTML页面。

可以通过进入特定的profile_target目录并运行以下命令来创建火焰图:

cd profile_target/albers_usa
sudo CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph

渲染240个国家/多边形的复杂度提供了对内存分配问题的良好视图。

编码标准

  • 尽可能使用Cargo Clippy定义的惯用RUST。

  • 函数/方法不使用布尔值作为参数,而是使用两个状态枚举。

    参见“Reflect”作为示例。

    pub trait ReflectSet {
        /// f64 or f32.
        type T;
       /// Set the projection builder to invert the x-coordinate.
        fn reflect_x_set(&mut self, reflect: Reflect) -> &mut Self;
       /// Set the projection builder to invert the y-coordinate.
        fn reflect_y_set(&mut self, reflect: Reflect) -> &mut Self;
    }
    

    这允许更清晰地表达意图:

    builder.reflect_y_set(Reflect::Flipped);
    
  • "Type-Driven API Design"是构建状态机的首选方法。

    在下面的示例中,当组装流管道时,只有在状态是"Unconnected"时才能调用connect()。输出类型的STATE是"Connected"。

    impl StreamTransformRadians<Unconnected> {
      #[inline]
      /// Connect this node to the next element in the pipeline.
      pub const fn connect<EP, SINK, T>(self, sink: SINK) -> StreamTransformRadians<Connected<SINK>>
      where
        SINK: Clone,
      {
        StreamTransformRadians(Connected { sink })
      }
    }
    

    只有在STATE是"Connected"时才实现"Stream"特质。按照设计,所有代码都被阻止在没有连接到另一个管道阶段的对象上调用line_start()或point()。


Future 2.0升级

"语义版本控制"指南:

  • 当发生破坏性变更时,增加主版本号。
  • 添加新功能时,增加次要版本号,为过时的函数添加@deprecated说明。
  • 为紧密相关的安全修复增加补丁版本号。

未来工作。

  • 自从我移植了这段代码... JavaScript已经添加了一个digits()函数来控制数字以字符串形式输出时的精度。(我需要实现这个升级)

  • rayon是Rust的多线程支持crate。

  • 在移植代码时,我广泛使用了迭代器,而rayon支持将单线程迭代器轻松转换为多线程迭代器。

架构讨论

设计中的一个方面需要审查。它与实现具有节点之间交叉链接的双向链表的最佳方式有关。

clip/rejoin/mod.rs中的裁剪算法需要重构。请参阅The intersection Problem。该区域的测试覆盖率很高,因此算法是有效的,但数据结构广泛使用了包含对其他堆对象引用的向量(堆对象),这并不高效。

完整的讨论可以在这里找到。

库未实现的部分

尚不支持自定义投影。

关于此的示例请参阅标记为"projection.fitExtent(…) custom projection"的测试。

依赖项

~16MB
~280K SLoC