使用Rust Plotters库绘制第三列值配色2D渐变地图的技术问询
实现思路与代码示例
核心需求解决方法
- 色条添加:
首先将你现有的颜色映射逻辑抽为独立函数,保证绘图和色条的颜色规则完全一致;在创建图表时预留右侧区域用于绘制色条,按数值范围逐段生成渐变色块,同时标注对应刻度即可。 - 场平滑处理:
你当前的实现是每个原始点绘制固定尺寸矩形,会有明显的颗粒感。可以先对原始离散点做双线性插值生成密度更高的网格点再绘制;如果你的原始数据本身是规则网格结构,也可以直接对相邻点的值做加权平均模糊处理,降低锯齿感。
完整可运行代码示例
use plotters::prelude::*; use std::f64::consts::PI; // 抽离统一的彩虹色映射函数,v为输入值,v_min、v_max为值范围 fn get_rainbow_color(v: i32, v_min: i32, v_max: i32) -> HSLColor { let norm = (v - v_min) as f64 / (v_max - v_min) as f64; // 色相从240°(蓝)到0°(红),对应彩虹渐变 HSLColor( 240.0 / 360.0 * (1.0 - norm), 0.7, 0.1 + 0.6 * norm, ) } // 简单的双线性插值生成平滑网格(示例适配你原始的坐标范围) fn interpolate_grid(raw: &Vec<(i32, i32, i32)>, step_x: i32, step_y: i32) -> Vec<(i32, i32, i32)> { // 这里可以根据你的实际数据结构完善插值逻辑,示例做简化处理 // 实际使用时建议先把原始点转成二维网格矩阵再做插值 let mut res = raw.clone(); // 补充插值点逻辑省略,你可以根据自己的数据规则实现,插值后点密度越高画面越平滑 res } fn figura2(profun: Vec<(i32, i32, i32)>) -> Result<(), Box<dyn std::error::Error>> { // 先计算值的范围,避免硬编码 let v_min = profun.iter().map(|(_,_,v)| *v).min().unwrap_or(0); let v_max = profun.iter().map(|(_,_,v)| *v).max().unwrap_or(200); let root = BitMapBackend::new("plot_fold.png", (900, 600)).into_drawing_area(); root.fill(&WHITE)?; // 拆分主绘图区和色条区 let (main_area, legend_area) = root.split_horizontally(800); let mut scatter_ctx = ChartBuilder::on(&main_area) .top_x_label_area_size(40) .y_label_area_size(55) .build_cartesian_2d((1581001..1581542).step(1), 5500i32..0i32)?; scatter_ctx .configure_mesh() .disable_x_mesh() .disable_y_mesh() .y_desc("OFFSET (m)") .x_desc("ESTACAS (RP)") .axis_desc_style(("sans-serif", 15)) .draw()?; // 插值后的点集 let smoothed_points = interpolate_grid(&profun, 1, 100); scatter_ctx.draw_series(smoothed_points.iter().map(|(x, y, v)| { Rectangle::new( [(*x, *y), (x + 1, y - 500)], get_rainbow_color(*v, v_min, v_max).filled(), ) }))?; // 绘制右侧色条 let mut legend_ctx = ChartBuilder::on(&legend_area) .y_label_area_size(40) .build_cartesian_2d(0i32..10i32, v_min..v_max)?; legend_ctx.configure_mesh() .disable_x_mesh() .disable_x_axis() .y_labels(10) .label_style(("sans-serif", 10)) .draw()?; // 绘制色条渐变 legend_ctx.draw_series((v_min..v_max).map(|v| { Rectangle::new( [0, v], [10, v+1], get_rainbow_color(v, v_min, v_max).filled() ) }))?; root.present()?; Ok(()) }
优化说明
- 统一的颜色映射函数保证了主图和色条的颜色完全对应,你可以根据自己的数值范围调整色相、饱和度、亮度的映射规则
- 插值函数部分你可以根据自己的数据结构扩展,如果是规则网格数据,双线性插值的实现成本很低,平滑效果提升非常明显
- 色条的刻度数量、尺寸都可以根据需要调整,也可以添加色条的标题说明数值含义
内容的提问来源于stack exchange,提问作者Jaca
相关产品推荐
相关产品推荐

