Turf处理等压线

        Turf是一个用于空间分析的JavaScript库。它包括传统的空间操作、用于创建GeoJSON数据的辅助函数以及数据分类和统计工具。Turf可以作为客户端插件添加到您的网站,也可以使用Node.js在服务器端运行Turf。

        Turf是一个模块化的js类库,所有的模块都是在packages下面,每个模块下面都包含一个index.ts的源码文件,这个源码是使用typescript编写.

       

        对于上面这个实例,本章将通过源码的方式进行解析,首先调用的是turf.pointGrid这个函数,

这个模块中的index.ts文件,代码中定义了pointGrid函数,这个函数接受如下几个参数

// 基于box边界,通过细分的方式插入点
function pointGrid<P = GeoJsonProperties>(bbox: BBox,      // 东南西北作为边界cellSide: number,// 细分时的步长options: {        units?: Units; // 单位mask?: Feature<Polygon | MultiPolygon>; // 多边形掩码,插值点在这个范围内才算properties?: P;    // 属性} = {}
): FeatureCollection<Point, P> {// Default parameters// 默认使用千米if (options.mask && !options.units) options.units = "kilometers";// Containers// 结果var results = [];// 东南西北四个边界(经纬度)var west = bbox[0];var south = bbox[1];var east = bbox[2];var north = bbox[3];// 计算两点之间的球面距离// 一个单元占整个距离的百分比var xFraction = cellSide / distance([west, south], [east, south], options);// 一个单元的角度制下的距离var cellWidth = xFraction * (east - west);// 一个单元占整个距离的百分比var yFraction = cellSide / distance([west, south], [west, north], options);// 一个单元的角度制下的距离var cellHeight = yFraction * (north - south);// 宽度(角度)var bboxWidth = east - west;// 高度(角度)var bboxHeight = north - south;// 列数var columns = Math.floor(bboxWidth / cellWidth);// 行数var rows = Math.floor(bboxHeight / cellHeight);// adjust origin of the grid// 多出来的一部分的中间var deltaX = (bboxWidth - columns * cellWidth) / 2;var deltaY = (bboxHeight - rows * cellHeight) / 2;// 当前xvar currentX = west + deltaX;// 计算插值点while (currentX <= east) {// 当前yvar currentY = south + deltaY;while (currentY <= north) {// 封装要素点var cellPt = point([currentX, currentY], options.properties);if (options.mask) {// 点是否在掩码范围被内,只有在这个范围内才添加if (within(cellPt, options.mask)) results.push(cellPt);} else {// 添加到结果中results.push(cellPt);}currentY += cellHeight;}currentX += cellWidth;}// 封装成要素集合return featureCollection(results);
}

 计算distance的类是turf-distance文件夹下的index.ts

// 计算距离
function distance(from: Coord | Point,to: Coord | Point,options: {units?: Units;} = {}
) {// 获取坐标数组var coordinates1 = getCoord(from);// 获取坐标数组var coordinates2 = getCoord(to);// 计算维度var dLat = degreesToRadians(coordinates2[1] - coordinates1[1]);// 计算精度var dLon = degreesToRadians(coordinates2[0] - coordinates1[0]);// 角度转换为弧度var lat1 = degreesToRadians(coordinates1[1]);var lat2 = degreesToRadians(coordinates2[1]);// 计算两点之间的角度var a =Math.pow(Math.sin(dLat / 2), 2) +Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);// 弧长公式Rθreturn radiansToLength(2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)),options.units);
}

  计算等值线的类是turf-isolines文件夹下的index.ts

// 生成等值线
function isolines(pointGrid: FeatureCollection<Point>,// 网格点breaks: number[],options?: {zProperty?: string;commonProperties?: GeoJsonProperties;breaksProperties?: GeoJsonProperties[];}
) {// Optional parametersoptions = options || {};if (!isObject(options)) throw new Error("options is invalid");const zProperty = options.zProperty || "elevation";const commonProperties = options.commonProperties || {};const breaksProperties = options.breaksProperties || [];// Input validationcollectionOf(pointGrid, "Point", "Input must contain Points");if (!breaks) throw new Error("breaks is required");if (!Array.isArray(breaks)) throw new Error("breaks must be an Array");if (!isObject(commonProperties))throw new Error("commonProperties must be an Object");if (!Array.isArray(breaksProperties))throw new Error("breaksProperties must be an Array");// Isoline methods// 等值线方法// 将网格点,按照z值构建一个只包含z值的矩阵const matrix = gridToMatrix(pointGrid, { zProperty: zProperty, flip: true });// 创建等值线const createdIsoLines = createIsoLines(matrix,breaks,zProperty,commonProperties,breaksProperties);// 重新缩放等值线const scaledIsolines = rescaleIsolines(createdIsoLines, matrix, pointGrid);// 封装成要素return featureCollection(scaledIsolines);
}

将所有的点构建一个z值的矩阵

export default function gridToMatrix(grid, options) {// Optional parametersoptions = options || {};if (!isObject(options)) throw new Error("options is invalid");var zProperty = options.zProperty || "elevation";var flip = options.flip;var flags = options.flags;// validationcollectionOf(grid, "Point", "input must contain Points");// 按照经纬度排序var pointsMatrix = sortPointsByLatLng(grid, flip);var matrix = [];// create property matrix from sorted points// looping order matters here// 从排序点创建属性矩阵循环此处的顺序问题for (var r = 0; r < pointsMatrix.length; r++) {var pointRow = pointsMatrix[r];var row = [];for (var c = 0; c < pointRow.length; c++) {var point = pointRow[c];// Check if zProperty existif (point.properties[zProperty]) row.push(point.properties[zProperty]);else row.push(0);// add flagsif (flags === true) point.properties.matrixPosition = [r, c];}matrix.push(row);}return matrix;
}

 

// 创建等值线
function createIsoLines(matrix: number[][],breaks: number[],zProperty: string,commonProperties: GeoJsonProperties,breaksProperties: GeoJsonProperties[]
): Feature<MultiLineString>[] {const results = [];// 遍历中断点for (let i = 1; i < breaks.length; i++) {// 作为阈值const threshold = +breaks[i]; // make sure it's a numberconst properties = { ...commonProperties, ...breaksProperties[i] };properties[zProperty] = threshold;// 应该是按照矩阵中的四个点计算是否可以计算出这个阈值的插值点const isoline = multiLineString(isoContours(matrix, threshold), properties);results.push(isoline);}return results;
}
// 等压线
export default function isoContours(data, threshold, options) {/* process options */options = options ? options : {};var optionKeys = Object.keys(defaultSettings);for (var i = 0; i < optionKeys.length; i++) {var key = optionKeys[i];var val = options[key];val =typeof val !== "undefined" && val !== null ? val : defaultSettings[key];settings[key] = val;}if (settings.verbose)console.log("MarchingSquaresJS-isoContours: computing isocontour for " + threshold);// 计算等压线网格var ret = contourGrid2Paths(computeContourGrid(data, threshold));if (typeof settings.successCallback === "function")settings.successCallback(ret);return ret;
}
/* assume that x1 == 1 &&  x0 == 0 */
// 线性插值
function interpolateX(y, y0, y1) {return (y - y0) / (y1 - y0);
}/* compute the isocontour 4-bit grid */
// 计算等压线4为的网格
function computeContourGrid(data, threshold) {// 行数、列数var rows = data.length - 1;var cols = data[0].length - 1;// var ContourGrid = { rows: rows, cols: cols, cells: [] };// 遍历行数for (var j = 0; j < rows; ++j) {ContourGrid.cells[j] = [];// 遍历列数for (var i = 0; i < cols; ++i) {/* compose the 4-bit corner representation */var cval = 0;//取出相邻的四个点var tl = data[j + 1][i];var tr = data[j + 1][i + 1];var br = data[j][i + 1];var bl = data[j][i];if (isNaN(tl) || isNaN(tr) || isNaN(br) || isNaN(bl)) {continue;}// 检索阈值是否在四个高度内部,还是外部,四个值都高于阈值8421=15,或者四个值都低于阈值在外部0cval |= tl >= threshold ? 8 : 0;cval |= tr >= threshold ? 4 : 0;cval |= br >= threshold ? 2 : 0;cval |= bl >= threshold ? 1 : 0;/* resolve ambiguity for cval == 5 || 10 via averaging */var flipped = false;// 在对角线上if (cval === 5 || cval === 10) {// 计算平均值var average = (tl + tr + br + bl) / 4;// 两个对角线上的中点值偏离比较远,if (cval === 5 && average < threshold) {cval = 10; // 取另外一个对角线来确定flipped = true;} else if (cval === 10 && average < threshold) {cval = 5;flipped = true;}}/* add cell to ContourGrid if it contains edges */// 如果单元格包含边,则将其添加到ContourGrid// 阈值在四个点内部if (cval !== 0 && cval !== 15) {var top, bottom, left, right;top = bottom = left = right = 0.5;/* interpolate edges of cell */if (cval === 1) {  // 一个点在上,其他三个点都在下面// 插值左边left = 1 - interpolateX(threshold, tl, bl);// 插值下边bottom = 1 - interpolateX(threshold, br, bl);} else if (cval === 2) {bottom = interpolateX(threshold, bl, br);right = 1 - interpolateX(threshold, tr, br);} else if (cval === 3) {left = 1 - interpolateX(threshold, tl, bl);right = 1 - interpolateX(threshold, tr, br);} else if (cval === 4) {top = interpolateX(threshold, tl, tr);right = interpolateX(threshold, br, tr);} else if (cval === 5) {top = interpolateX(threshold, tl, tr);right = interpolateX(threshold, br, tr);bottom = 1 - interpolateX(threshold, br, bl);left = 1 - interpolateX(threshold, tl, bl);} else if (cval === 6) {bottom = interpolateX(threshold, bl, br);top = interpolateX(threshold, tl, tr);} else if (cval === 7) {left = 1 - interpolateX(threshold, tl, bl);top = interpolateX(threshold, tl, tr);} else if (cval === 8) {left = interpolateX(threshold, bl, tl);top = 1 - interpolateX(threshold, tr, tl);} else if (cval === 9) {bottom = 1 - interpolateX(threshold, br, bl);top = 1 - interpolateX(threshold, tr, tl);} else if (cval === 10) {top = 1 - interpolateX(threshold, tr, tl);right = 1 - interpolateX(threshold, tr, br);bottom = interpolateX(threshold, bl, br);left = interpolateX(threshold, bl, tl);} else if (cval === 11) {top = 1 - interpolateX(threshold, tr, tl);right = 1 - interpolateX(threshold, tr, br);} else if (cval === 12) {left = interpolateX(threshold, bl, tl);right = interpolateX(threshold, br, tr);} else if (cval === 13) {bottom = 1 - interpolateX(threshold, br, bl);right = interpolateX(threshold, br, tr);} else if (cval === 14) {left = interpolateX(threshold, bl, tl);bottom = interpolateX(threshold, bl, br);} else {console.log("MarchingSquaresJS-isoContours: Illegal cval detected: " + cval);}ContourGrid.cells[j][i] = {cval: cval,   // 代表的是top、right、bottom、left中哪两个值有效果flipped: flipped,top: top,right: right,bottom: bottom,left: left,};}}}return ContourGrid;
}
// 计算等值点组成的路径
function contourGrid2Paths(grid) {var paths = [];var path_idx = 0;var epsilon = 1e-7;grid.cells.forEach(function (g, j) {g.forEach(function (gg, i) {if (typeof gg !== "undefined" && !isSaddle(gg) && !isTrivial(gg)) {var p = tracePath(grid.cells, j, i);var merged = false;/* we may try to merge paths at this point */if (p.info === "mergeable") {/*search backwards through the path array to find an entrythat starts with where the current path ends...*/var x = p.path[p.path.length - 1][0],y = p.path[p.path.length - 1][1];for (var k = path_idx - 1; k >= 0; k--) {if (Math.abs(paths[k][0][0] - x) <= epsilon &&Math.abs(paths[k][0][1] - y) <= epsilon) {for (var l = p.path.length - 2; l >= 0; --l) {paths[k].unshift(p.path[l]);}merged = true;break;}}}if (!merged) paths[path_idx++] = p.path;}});});return paths;
}

总结:

具体的做法是按照给定的采样点,插值(例如反距离权重)成矩阵,将高度值构建成矩阵,将矩阵中的四个相邻的值取出来,按照给定的阈值进行线性插值定位,计算z值对应的经纬度,这就构成一个插值后的xyz值,将这些值按照路径拼起来就是等直线了

参考:

等值线 | Turf.js中文网

GitHub - Turfjs/turf: A modular geospatial engine written in JavaScript

等值线 | Turf.js中文网

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/120681.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端开发 vs. 后端开发:编程之路的选择

文章目录 前端开发&#xff1a;用户界面的创造者1. HTML/CSS/JavaScript&#xff1a;2. 用户体验设计&#xff1a;3. 响应式设计&#xff1a;4. 前端框架&#xff1a; 后端开发&#xff1a;数据和逻辑的构建者1. 服务器端编程&#xff1a;2. 数据库&#xff1a;3. 安全性&#…

冲刺十五届蓝桥杯P0002 日期统计

文章目录 题目分析代码 题目 分析 需要明白一些概念&#xff0c;子序列、连续子序列。 1.子序列&#xff08;subsequence&#xff09;是指原始序列中按照相同顺序选择零个或多个元素而形成的序列。连续子序列&#xff08;subarray&#xff09;是指原始序列中相邻位置的元素构…

使用领域引导图卷积神经网络GCNN增强基于脑电图EEG的神经疾病诊断完整代码

一种基于图卷积神经网络&#xff08;GCNN&#xff09;的新方法&#xff0c;用于改进使用头皮脑电图&#xff08;EEG&#xff09;进行神经系统疾病诊断。尽管脑电图是神经系统疾病诊断中主要使用的检测方法之一&#xff0c;但基于EEG的专家视觉诊断的敏感性仍然只有约50&#xf…

【Redis】Redis做为缓存,MySQL如何与Redis保持数据一致

Redis的作用 一般情况下Redis是用来实现应用和数据库之间的一个读操作的缓存层&#xff0c;主要目的是减少数据库的io&#xff0c;还可以提升数据库io性能 方法一&#xff1a; 先更新MySQL数据库&#xff0c;再删除缓存&#xff0c;再从数据库查询到的最新的数据同步到redis…

面试总结之Spring篇

一、AOP 1、什么是AOP 1.1、概述 AOP&#xff08;Aspect-Oriented Programming&#xff09;&#xff1a;面向切面编程&#xff0c;即把一些业务逻辑中的相同代码抽取出来&#xff0c;让业务逻辑更加简练清爽 如果要CRUD写一堆业务&#xff0c;可如何实现业务代码前后进行打印…

如何使用ArcGIS Pro自动矢量化道路

对于已经制作好的电子地图&#xff0c;我们可以通过像素识别的方式将其中的要素提取出来&#xff0c;比如本教程要讲到的道路数据&#xff0c;这里为大家介绍一下在ArcGIS Pro中如何自动矢量化道路&#xff0c;希望能对你有所帮助。 栅格计算 在工具箱中点击“Spatial Analys…

odoo16 取消“系统各功能状态日报”的邮件

odoo16默认情况下每周都会发送一个“系统各功能状态日报”的邮件&#xff0c;而且是所有人都发&#xff0c; 这个功能在哪配置呢&#xff1f; 今天研究了一下&#xff0c; 线索是“系统各功能状态日报”&#xff0c;先全文检索吧 #. module: digest #: model:digest.digest,na…

构建个人云存储:本地电脑搭建SFTP服务器,开启公网访问,轻松共享与管理个人文件!

本地电脑搭建SFTP服务器&#xff0c;并实现公网访问 文章目录 本地电脑搭建SFTP服务器&#xff0c;并实现公网访问1. 搭建SFTP服务器1.1 下载 freesshd 服务器软件1.3 启动SFTP服务1.4 添加用户1.5 保存所有配置 2. 安装SFTP客户端FileZilla测试2.1 配置一个本地SFTP站点2.2 内…

深度学习(1)---卷积神经网络

文章目录 一、发展历史1.1 CNN简要说明1.2 猫的视觉实验1.3 新认知机1.4 LeNet-51.5 AlexNet 二、卷积层2.1 图像识别特点2.2 卷积运算2.3 卷积核2.4 填充和步长2.5 卷积计算公式2.6 多通道卷积 三、池化层 一、发展历史 1.1 CNN简要说明 1. 卷积神经网络&#xff08;Convolut…

触觉智能 PurPle Pi OH(OpenHarmony)开发板

资料汇总 内容预览 产品介绍 PurPle-Pi OH 规格书​​​​​​ 系统编译 Purple-Pi-OH Linux SDK编译 Purple-Pi-OH OHOS SDK编译 使用手册 Purple-Pi-OH Ubuntu系统使用手册 常见FAQ 常见问题 官网 官网地址 Purple Pi OH介绍 Purple Pi OH作为一款兼容树莓派的开…

【Spring Boot】实战:实现数据缓存框架

🌿欢迎来到@衍生星球的CSDN博文🌿 🍁本文主要学习【Spring Boot】实现数据缓存框架 🍁 🌱我是衍生星球,一个从事集成开发的打工人🌱 ⭐️喜欢的朋友可以关注一下🫰🫰🫰,下次更新不迷路⭐️💠作为一名热衷于分享知识的程序员,我乐于在CSDN上与广大开发者…

conan入门(二十八):解决conan 1.60.0下 arch64-linux-gnu交叉编译openssl/3.1.2报错问题

上一篇博客《conan入门(二十七):因profile [env]字段废弃导致的boost/1.81.0 在aarch64-linux-gnu下交叉编译失败》解决了conan 1.60.0交叉编译boost/1.80.1的问题后&#xff0c;我继续交叉编译openssl/3.1.2时又报错了 conan install openssl/3.1.2 -pr:h aarch64-linux-gnu.…