首页 > 编程语言 >Javascript(turfjs)等值线图绘制

Javascript(turfjs)等值线图绘制

时间:2024-08-09 22:38:19浏览次数:15  
标签:turfjs 等值线图 Javascript isobands value var turf intersection fill

Javascript(turfjs)等值线图绘制

头像 舍瓦温   2020-04-07 阅读 5 分钟 14  

使用气象、环境类空间数据绘制等值线通常是由 NCL、Python 来做,在一些场景中:

  1. 你只是想在 WEB 端做一些简单的绘制
  2. 你的后端只有 Node.js 环境
  3. 你纯粹是个前端工程师

你也许需要使用纯 Javascript 来做这件事。本文尝试根据空间中的一组散点来绘制等值线图(或色斑图)。

1. 准备工作

  1. turfjs, 空间分析(geospatial analysis)工具包,支持在浏览器和 Node.js 环境中运行,空间数据的输入输出使用 GeoJSON 编码
  2. 一组散点数据,这是业务数据了,可以是一些观测点的温度或PM2.5。
  3. 一个边界,用于裁剪出一幅针对某一区域干净的色斑图,格式为 GeoJSON。推荐使用 geojson.io 来查看或编辑这类数据,这里给出一个城市的 boundaries 做示例。
  4. Mapbox, 一个 WebGIS 引擎,用来渲染输入、输出以及以一些中间数据。
  5. Codepen ,一个交互式的在线前端 IDE,这不是等值线绘制必备的工具,只是为演示这个过程提供一个载体。

2. 基本流程

workflowGenerated by carbon

基本流程就是这样一串操作 —— 散点(Points)经过插值、等值线绘制、裁剪和渲染展示。按照 turfjs 的哲学,各个函数间传递GeoJSON 数据。

3. load_data_to_geojson()

image.png

一般业务数据往往是一个数组,大概是这样

var data = [  
    {"Lat":36.18,"Lon":103.75,"value":1.7},
    {"Lat":36.17,"Lon":103.29,"value":1},
    {"Lat":37.98,"Lon":102.75,"value":4},
    {"Lat":36.59,"Lon":104.91,"value":3},
    {"Lat":36.22,"Lon":107.78,"value":0.1}
]

你需要把 data 转为一组 feature。 array.map() 大法好~

var features = data.map(i => {return {
      type: "Feature",
      properties: {
        value: i.value
      },
      geometry: {
        type: "Point",
        coordinates: [i.Lon, i.Lat]
      }
    }
  }
)
var points = turf.featureCollection(features);

你没有业务数据? 那就随机来一些

var points = turf.randomPoint(30, { bbox: turf.bbox(boundaries) });

//再生成些随机数做属性
turf.featureEach(points, function (currentFeature, featureIndex) {
  currentFeature.properties = { value: (Math.random() * 100).toFixed(2) };
});

4. turf.interpolate()

image.png

turf.interpolate() 提供了基于 IDW(反距离权重)算法的将数据插值为格点的方法。插值的精度是由第二个参数与 interpolate_options.units 共同决定的,单位支持 degrees, radians, miles, or kilometers,IDW 要为每个格点计算所有散点的权重,计算规模是 (散点数 * 格点数),所以要在精度与性能间做好平衡。 我们将之前的散点(points)代入

var interpolate_options = {
  gridType: "points",
  property: "value",
  units: "degrees",
  weight: 10
};
var grid = turf.interpolate(points, 0.05, interpolate_options);
// 适当降低插值结果的精度便于显示
grid.features.map((i) => (i.properties.value = i.properties.value.toFixed(2)));

5. turf.isobands()

image.png

这一步基于插值获得的格点绘制等值区域,并为区域配置颜色。turf.isobands() 根据 zProperty 分段,形成一些 MultiPolygon

var isobands_options = {
  zProperty: "value",
  commonProperties: {
    "fill-opacity": 0.8
  },
  breaksProperties: [
    {fill: "#e3e3ff"},
    {fill: "#c6c6ff"},
    {fill: "#a9aaff"},
    {fill: "#8e8eff"},
    {fill: "#7171ff"},
    {fill: "#5554ff"},
    {fill: "#3939ff"},
    {fill: "#1b1cff"}
  ]
};
var isobands = turf.isobands(
  grid,
  [1, 10, 20, 30, 50, 70, 100],
  isobands_options
);

到这步,你就有了覆盖整个格点的色斑图。

6. turf.intersect()

image.png

这一步,我们利用准备的边界来裁剪整个色斑图。这里要用到 turf.intersect(),根据文档,这里输入的参数要 Feature<Polygon> ,而我们拿到的是 MultiPolygon,需要先 flatten() 处理一下。

boundaries = turf.flatten(boundaries);
isobands = turf.flatten(isobands);

之后对每个 Polygon 做一次 intersect() 操作。

var features = [];

isobands.features.forEach(function (layer1) {
  boundaries.features.forEach(function (layer2) {
    let intersection = null;
    try {
      intersection = turf.intersect(layer1, layer2);
    } catch (e) {
      layer1 = turf.buffer(layer1, 0);
      intersection = turf.intersect(layer1, layer2);
    }
    if (intersection != null) {
      intersection.properties = layer1.properties;
      intersection.id = Math.random() * 100000;
      features.push(intersection);
    }
  });
});

var intersection = turf.featureCollection(features);

6.1 异常处理

image.png

色斑图绘制之后,可能会生成一些非法 Polygon ,例如 在 hole 里存在一些形状(听不懂?去查一下 GeoJSON 的规范),我遇到的一个意外情况大概是这样,这种 Polygon 在做 intersect() 操作的时候会报错,所以在代码中做了个容错操作。解决的方法通常就是做一次 turf.buffer() 操作,这样可以把一些小的碎片 Polygon 清理掉。

6.2 性能

image.png

这个操作的计算量很大,在使用精细边界时,运行耗时甚至超过插值过程,所以如果仅仅是为了渲染一个边界范围内的色斑图,那建议利用 turf.mask() 做一个遮罩,在 WebGIS 引擎里叠加到色斑图层之上,可以达到预期的效果。

7. map.addLayer()

最后一步工作就是形成的色斑 GeoJSON 叠加到地图上了,这里使用 MapBox 来实现,利用其 expressions 功能,可以很便捷的实现一些样式渲染和交互效果。

  map.addSource("intersection", {
    type: "geojson",
    data: intersection
  });  map.addSource("intersection", {
    type: "geojson",
    data: intersection
  });
  map.addLayer({
    id: "intersection",
    type: "fill",
    source: "intersection",
    layout: {},
    paint: {
      "fill-color": ["get", "fill"],
      "fill-opacity": [
        "case",
        ["boolean", ["feature-state", "hover"], false],
        0.8,
        0.5
      ],
      "fill-outline-color": [
        "case",
        ["boolean", ["feature-state", "hover"], false],
        "#000",
        "#fff"
      ]
    }
  });

8. 结论

综上,通过一串操作,我们使用 turfjs 实现了散点数据的等值线绘制和渲染,最终的效果请访问 Codepen 。对比在 Python 或 NCL, Javascript 虽然完成了任务,但略显业余,比如,在 Javascript 生态中,达到业务应用水平的插值的算法实现非常少。性能方面,本文给出的方案在浏览器中表现只能应对非常有限的数据规模,仍可以通过空间数据索引、控制插值计算规模等方法进行优化。<iframe data-id="1" frameborder="0" height="240" src="https://codepen.io/shevawen/embed/abOPdJy" width="320"></iframe>

标签:turfjs,等值线图,Javascript,isobands,value,var,turf,intersection,fill
From: https://www.cnblogs.com/sexintercourse/p/18351624

相关文章

  • JavaScript -- 总结 10 (小白)
    MouseEvent属性<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document<......
  • JavaScript toLocaleString() 方法
    定义和用法:toLocaleString()方法可根据本地时间把Date对象转换为字符串,并返回结果。语法:dateObject.toLocaleString()返回值dateObject的字符串表示,以本地时间区表示,并根据本地规则格式化。问题//Javascript中newDate().toLocaleString()在不同浏览器中的结果不一致的解决......
  • ArcGIS API for JavaScript 3.x 到 4.x 的升级手册
    众所周知,3.x版本主要是构建二维地图,且基本不会再添加新功能;而4.x版本主要是构建于三维地图,与3.x相比并不是简单的升级,基本上就是重写了。所以当我们需要把API从3.x升级到4.x时,应用程序基本上是需要重写的,这里将对API升级过程中涉及到的相关变动进行记录与描述。以下......
  • [Javascript] Proper Tail Calls
    Docs:https://webkit.org/blog/6240/ecmascript-6-proper-tail-calls-in-webkit//*ThisisarecursivefunctionwithoutPTC*/functionfatorial(n){if(n===0){return1}else{returnn*fatorial(n-1)}}console.log(fator......
  • JavaScript中的异步编程:Promise、async 和 await
    JavaScript中的异步编程:Promise、async和await在JavaScript中,Promise、async和await是处理异步操作的关键技术。这些技术允许开发者以更清晰、更可维护的方式编写异步代码,特别是在面对复杂的异步逻辑时。下面我会详细解释每个概念并展示它们是如何协同工作的。Prom......
  • JavaScript 数组方法
    JavaScript数组的力量隐藏在数组方法中。把数组转换为字符串JavaScript方法toString()把数组转换为数组值(逗号分隔)的字符串。join()方法也可将所有数组元素结合为一个字符串。它的行为类似toString(),但是您还可以规定分隔符<pid="demo"></p><script>varfruits......
  • JavaScript 中布尔值的创建
    <!DOCTYPEhtml><htmllang="en"><body><script> //CreateaBooleanobjectusingthenewkeywordandtheBoolean()constructor. varmyBoolean1=newBoolean(false);//Usingnewkeyword. console.log(typeofmyBoolean1);......
  • JavaScript (二十六)——JavaScript 代码规范
    目录JavaScript代码规范变量名空格与运算符代码缩进语句规则对象规则每行代码字符小于80命名规则HTML载入外部JavaScript文件文件扩展名所有的JavaScript项目适用同一种规范。JavaScript代码规范代码规范通常包括以下几个方面:变量和函数的命名规则......
  • Javascript——NaN有什么用法
    简介在JavaScript中,NaN(NotaNumber)是一个特殊的值,用来表示非数字的结果,例如一个不合法的数学运算的结果。根据IEEE754浮点数标准,NaN不等于任何值,包括它自己。这意味着NaN是唯一一个与自身不相等的值,所以表达式NaN===NaN返回false。判断NaN如果你需要检测一......
  • JavaScript 中的“this”几种不同含义
    在JavaScript中,“ this ”指的是当前正在执行代码的对象。其值由函数的调用方式动态确定,这意味着它可以根据上下文而变化。1.全球背景在全局上下文中(即任何函数或对象之外),“ this”指的是全局对象。在浏览器中,“ this”是窗口对象,而在Node.js中,它是全局对象。2.对象......