[TOC]
作为一个 GISer,在日常 WebGIS 开发中,会常用到的 turf.js,这是一个地理空间分析的 JavaScript 库,经常搭配各种 WebGIS JsAPI 使用,如 Leaflet、MapboxGL、OpenLayers 等;在后台 Java 开发中,也有个比较强大的GIS库,GeoTools,里面包含构建一个完整的地理信息系统所需要的全部工具类;数据库端常用是 postgis 扩展,需要在 PostgreSQL 数据库中引入使用。
然而在开发某一些业务系统的时候,有些需求只需要调用某一个 GIS 算法,简单的几行代码即可完成,没有必要去引用一个 GIS 类库。
而且有些算法在这些常用的GIS类库中没有对应接口,就比如在下文记录的这几种常用算法中,求垂足、判断线和面的关系,在 turf.js 就没有对应接口。
下面文章中是我总结的一些常用 GIS 算法,这里统一用JavaScript 语言实现,因为 JS 代码相对比较简洁,方便理解其中算法逻辑,也方便在浏览器下预览效果。
在具体应用时可以根据具体需求,翻译成 Rust、C/C++、Java、C#.NET、Python 等语言来使用。
文中代码大部分为之前遇到需求时在网上搜索得到,然后自己根据具体需要做了优化修改,通过这篇文章做个总结收集,也方便后续使用时查找。
以下方法中传参的点、线、面都是对应 GeoJSON 格式中 coordinates
,方便统一调用。
GeoJSON 标准参考:https://geojson.org/
/**
* @typedef {[number, number]} LonLat
* @typedef {LonLat[]} LineString
* @typedef {LonLat[]} Ring
* @typedef {LonLat[][]} Polygon
* @typedef {[LonLat, LonLat]} LineSegment
*/
以下所有算法函数用到三个简单的输入类型,两个数构成的数组称为 LonLat
,即经纬度点;LonLat
数组,即一系列的经纬度点,称为折线,特殊情况下,两个点构成的叫线段 LineSegment
;多个 LonLat
点除了表示折线外,还可以表示闭合的单一多边形 Ring
,但是在 GeoJSON 中, Polygon
是允许有多个简单多边形构成的,即 LonLat[][]
。
// LonLat 经纬度点
const p1 = [112.5, 41.4]
// LineString 折线
const l1 = [[112.5, 41.4], [112.5, 41.5], [112.5, 41.6]]
// LineSegment 线段
const seg = [[112.5, 41.4], [112.5, 41.5]]
// Ring 单环
const ring = [[112.5, 41.4], [112.9, 41.5], [113.1, 40.6], [112.5, 41.4]]
// Polygon 多边形
const polygon = [[[112.5, 41.4], [112.9, 41.5], [113.1, 40.6], [112.5, 41.4]]]
适用场景:测量
const p1 = [112.5, 41.2]
const p2 = [121.3, 22.4]
const distance = getDistance(p1, p2) // 2249255.8
适用场景:封闭管段定位问题点
const l1 = [[116.01,40.01],[116.52,40.01]]
const offset = 8500
getLinePoint(l1, offset) // return [116.1096913821572, 40.01]
垂足可能在线段上,也可能在线段延长线上。
适用场景:求垂足
const l1 = [[116.01,40.01],[116.52,40.01]]
const p1 = [116.35, 40.08]
getFootPoint(l1, p1) // return [116.35, 40.01]
不同于上面求垂足方法,该方法求出的点肯定在线段上。
如果垂足在线段上,则最近的点就是垂足,如果垂足在线段延长线上,则最近的点就是线段某一个端点。
适用场景:根据求出最近的点计算点到线段的最短距离
const l = [[116.01,40.01],[116.52,40.01]]
const p = [116.72, 40.18]
getShortestPointInLine(l, p) // return [116.52, 40.01]
这里缓冲属于测地线方法,由于这里并没有严格的投影转换体系,所以与标准的测地线缓冲还有些许误差,不过经测试,半径 100km 内,误差基本可以忽略。
具体缓冲类型可看下之前的文章 你真的会用PostGIS中的buffer缓冲吗?
适用场景:根据点和半径画圆
const p = [116.72, 40.18]
bufferPoint(p, 50000, 64) // 返回一个数组,有 64 个 LonLat
该方法采用射线法思路实现。
了解射线法可参考:https://blog.csdn.net/qq_27161673/article/details/52973866
这里已经考虑到环状多边形的情况。
适用场景:判断点是否在面内
const polygon = [
[
[116.1, 39.5],
[116.1, 40.5],
[116.9, 40.5],
[116.9, 39.5],
],
[
[116.3, 39.7],
[116.3, 40.3],
[116.7, 40.3],
[116.7, 39.7],
],
[
[116.4, 39.8],
[116.4, 40.2],
[116.6, 40.2],
[116.6, 39.8],
]
]
pointInPolygon([116.35, 40.08], polygon) // return 0
pointInPolygon([116.35, 40.08], polygon) // return 1
pointInPolygon([116.35, 40.08], polygon) // return 2
适用场景:判断线和线的关系
const l1 = [[116.01,40.01],[116.52,40.01]]
const l2 = [[116.33,40.21],[116.36,39.76]]
intersectLineAndLine(l1, l2) // return 1
适用场景:判断线与面的关系
该方法考虑到环状多边形的情况,且把相切情况分为了内切和外切。
参考链接:https://www.cnblogs.com/xiaozhi_5638/p/4165353.html
const l1 = [[116.01,40.01],[116.52,40.01]]
const l2 = [[116.33,40.21],[116.36,39.76]]
const l3 = [[116.3,39.7],[116.4,40.1]]
const polygon = [
[
[116.1, 39.5],
[116.1, 40.5],
[116.9, 40.5],
[116.9, 39.5],
],
[
[116.3, 39.7],
[116.3, 40.3],
[116.7, 40.3],
[116.7, 39.7],
],
[
[116.4, 39.8],
[116.4, 40.2],
[116.6, 40.2],
[116.6, 39.8],
]
]
intersectLineAndPolygon(l1, polygon) // return 1
intersectLineAndPolygon(l2, polygon) // return 0
intersectLineAndPolygon(l3, polygon) // return 4
适用场景:只有 GeoJSON 面数据,获取线的边界
const polygon = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
112.34344482421875,
22.172144917381736
],
[
114.74395751953125,
22.172144917381736
],
[
113.51348876953124,
23.274150156028483
],
[
112.34344482421875,
22.172144917381736
]
]
]
}
}
]
}
convertPolygonToPolyline(polygon)
// 输出
/*
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "MultiLineString",
"coordinates": [
[
[
112.34344482421875,
22.172144917381736
],
[
114.74395751953125,
22.172144917381736
],
[
113.51348876953124,
23.274150156028483
],
[
112.34344482421875,
22.172144917381736
]
]
]
}
}
]
}
*/
在线示例:GISAlgorithm Demo
代码地址:GISAlgorithm Code
原文地址:http://gisarmory.xyz/blog/index.html?blog=GISAlgorithm
关注《GIS兵器库》, 只给你网上搜不到的GIS知识技能。
本文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名《GIS兵器库》(包含链接: http://gisarmory.xyz/blog/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。