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 |
|
#6 in 地理空间
每月331 次下载
用于 2 crates
530KB
15K SLoC
Rust D3 Geo
Rust 2021 版本。
这是将 d3-geo 移植到 RUST 的版本。它是移植的 d3-modules 之一
- d3_geo_rs
- d3_delaunay_rs
- d3_geo_voronoi_rs
此库允许开发自定义地图。它提供了一套完整的投影方法以及缩放、旋转和转换最终图像的手段。投影仪处理 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 的版本 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 两种语言的示例代码,用于渲染复杂的多边形。(正射投影和球面投影) |
概述了所有示例中找到的常见步骤
-
对于给定的投影,使用其默认投影构建器,更改比例尺、平移等,然后调用 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();
-
构建一个 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();
-
请参阅不同的示例,但常见的下一步是构建一个 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
基准测试
有两个不同的计算环境
-
浏览器:这是一个高度受限的环境。这里有两个等效的基准测试,一个是用于Rust的 rust_d3_geo_voronoi,另一个是用于JavaScript的 d3-geo-voronoi。Rust以服务工作者的形式运行,没有对DOM的直接控制。尽管这个基准测试运行速度快了两倍,但线程之间通过JsValue传递对象仍然对性能有负面影响。
-
类似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