OpenLayers结合Turf实现空间运算
1. 引言
空间运算利用几何函数来接收输入的空间数据,对其进行分析,然后生成输出数据,输出数据为针对输入数据执行分析的派生结果。
可从空间运算中获得的派生数据包括:
- 作为输入要素周围缓冲区的面
- 作为对几何集合执行分析的结果的单个要素
- 作为比较结果以确定不与其他要素位于同一物理空间的要素部分的单个要素
- 作为比较结果以查找与其他要素的物理空间相交的要素部分的单个要素
- 由彼此不位于同一物理空间的输入要素部分组成的多部分 (multipart) 要素
- 作为两个几何的并集的要素
参考文档:空间运算—ArcMap | 文档 (arcgis.com)
Turf.js是MapBox公司研发的基于浏览器端的空间分析库,它使用JavaScript进行编写,通过npm进行包管理。值得一提的是,良好的模块化设计使其不仅能够作用于浏览器端、还可通过Node.js在服务端使用。Turf 原生支持 GeoJSON 矢量数据。GeoJSON 的优点是结构简单,并且得到了所有网页地图API的支持;但 GeoJSON 不支持空间索引,这个缺点可能会限制 Turf 处理大型文件的能力效率。其适用于轻量级(数据轻量而非功能轻量)的WebGIS应用
参考文献:Turf.js—让你在浏览器上实现地理分析 - 掘金 (juejin.cn)
turf.js官网:Turf.js | Advanced Geospatial Analysis (turfjs.org)
中文站点:Turf.js中文网 (fenxianglu.cn)
Turf.js Github地址:Turfjs/turf: A modular geospatial engine written in JavaScript (github.com)
空间运算可谓是空间分析的基础,Turf.js提供了大量的空间分析功能,包含了空间运算功能,本文参考OpenLayers与Turf集成的官方示例,使用示例数据和原生JavaScript,进行求交运算与缓冲区运算,并进行可视化
OpenLayers与Turf集成示例:turf.js (openlayers.org)
2.初始化地图
引入OpenLayers和Turf.js的CDN:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
注意:
- OpenLayers与Turf集成示例使用的Turf版本是2.0.0,版本偏老,与目前版本(6.5.0)部分API不兼容,不建议使用
构建网页基本内容,添加地图容器:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
</html>
添加地图并加载示例矢量数据:
<script>
fetch('https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson')
.then(response => response.json())
.then(function (json){
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(json, {
featureProjection: 'EPSG:3857'
})
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([126.980366, 37.52654]),
zoom: 15
})
});
})
</script>
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
fetch('https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson')
.then(response => response.json())
.then(function (json){
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(json, {
featureProjection: 'EPSG:3857'
})
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
}),
vectorLayer
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([126.980366, 37.52654]),
zoom: 15
})
});
})
</script>
</body>
</html>
初始化的地图:

3. 缓冲区计算
参考官方文档:Turf.js | Advanced geospatial analysis (turfjs.org)
步骤实质就是:
- 将Openlayers的Features对象转换为Turf支持的GeoJSON对象
- 调用Turf.buff()函数得到运算结果
- OpenLayers读取运算后的GeoJSON数据加载到图层中
缓存区计算的核心代码如下:
const features = vectorSource.getFeatures();
const turfLines = (new ol.format.GeoJSON()).writeFeaturesObject(features, {
featureProjection: 'EPSG:3857'
});
var buffered = turf.buffer(turfLines, 25, { units: 'meters' });
const bufferedLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(buffered, {
featureProjection: 'EPSG:3857'
})
})
})
map.addLayer(bufferedLayer);
注意:
- 从OpenLayers的Feature对象转换为Turf支持的GeoJSON对象时务必指定坐标系
这一步的完成代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<!-- <script src="https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v2.0.0/turf.min.js"></script> -->
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([126.980366, 37.52654]),
zoom: 15
})
});
fetch('https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson')
.then(response => response.json())
.then(function (json) {
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(json, {
featureProjection: 'EPSG:3857'
})
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2
})
})
});
map.addLayer(vectorLayer);
const features = vectorSource.getFeatures();
const turfLines = (new ol.format.GeoJSON()).writeFeaturesObject(features, {
featureProjection: 'EPSG:3857'
});
var buffered = turf.buffer(turfLines, 25, { units: 'meters' });
const bufferedLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(buffered, {
featureProjection: 'EPSG:3857'
})
})
})
map.addLayer(bufferedLayer);
})
</script>
</body>
</html>
结果图如下:

4. 相交计算
参考官方文档:Turf.js | Advanced geospatial analysis (turfjs.org)
步骤与缓冲区计算类似:
- 将Openlayers的Features对象转换为Turf支持的GeoJSON对象
- 调用Turf.lineIntersect()函数得到运算结果
- OpenLayers读取运算后的GeoJSON数据加载到图层中
本文所使用的数据是LineString,所以此处的相交计算的是线段相交
相交运算的核心代码如下:
const features = vectorSource.getFeatures();
const turfLines = (new ol.format.GeoJSON()).writeFeaturesObject(features, {
featureProjection: 'EPSG:3857'
});
const intersectionSource = new ol.source.Vector()
for (let i = 0; i < turfLines.features.length; i++) {
for (let j = i + 1; j < turfLines.features.length; j++) {
const intersections = turf.lineIntersect(turfLines.features[i], turfLines.features[j]);
if (intersections) {
if (intersections.features.length > 0) {
intersectionSource.addFeatures(new ol.format.GeoJSON().readFeatures(intersections, {
featureProjection: 'EPSG:3857'
}))
}
}
}
}
const intersectionLayer = new ol.layer.Vector({
source: intersectionSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
}),
fill: new ol.style.Fill({
color: '#ff0000'
}),
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: '#ff0000'
})
})
})
})
map.addLayer(intersectionLayer);
注意:
- 计算结果为点集合(Point),最好设置一定的显示样式,不然难以看见
这一步的完成代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<!-- <script src="https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v2.0.0/turf.min.js"></script> -->
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([126.980366, 37.52654]),
zoom: 15
})
});
fetch('https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson')
.then(response => response.json())
.then(function (json) {
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(json, {
featureProjection: 'EPSG:3857'
})
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2
})
})
});
map.addLayer(vectorLayer);
const features = vectorSource.getFeatures();
const turfLines = (new ol.format.GeoJSON()).writeFeaturesObject(features, {
featureProjection: 'EPSG:3857'
});
const intersectionSource = new ol.source.Vector()
for (let i = 0; i < turfLines.features.length; i++) {
for (let j = i + 1; j < turfLines.features.length; j++) {
const intersections = turf.lineIntersect(turfLines.features[i], turfLines.features[j]);
if (intersections) {
// console.log(intersections)
if (intersections.features.length > 0) {
intersectionSource.addFeatures(new ol.format.GeoJSON().readFeatures(intersections, {
featureProjection: 'EPSG:3857'
}))
// console.log(intersectionSource.getFeatures())
}
}
}
}
// console.log(intersectionSource)
const intersectionLayer = new ol.layer.Vector({
source: intersectionSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
}),
fill: new ol.style.Fill({
color: '#ff0000'
}),
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: '#ff0000'
})
})
})
})
map.addLayer(intersectionLayer);
})
</script>
</body>
</html>
结果图如下:

5. 整合
将两个运算结果结合在一起,完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css" type="text/css">
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<!-- <script src="https://api.tiles.mapbox.com/mapbox.js/plugins/turf/v2.0.0/turf.min.js"></script> -->
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
var map = new ol.Map({
layers: [
new ol.layer.Tile({
source: new ol.source.OSM()
})
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([126.980366, 37.52654]),
zoom: 15
})
});
fetch('https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson')
.then(response => response.json())
.then(function (json) {
var vectorSource = new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(json, {
featureProjection: 'EPSG:3857'
})
});
var vectorLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#0000ff',
width: 2
})
})
});
map.addLayer(vectorLayer);
const features = vectorSource.getFeatures();
const turfLines = (new ol.format.GeoJSON()).writeFeaturesObject(features, {
featureProjection: 'EPSG:3857'
});
// console.log(turfLines)
// const buffered = turf.buffer(turfLines, 25);
var buffered = turf.buffer(turfLines, 25, { units: 'meters' });
const bufferedLayer = new ol.layer.Vector({
source: new ol.source.Vector({
features: (new ol.format.GeoJSON()).readFeatures(buffered, {
featureProjection: 'EPSG:3857'
})
})
})
map.addLayer(bufferedLayer);
const intersectionSource = new ol.source.Vector()
for (let i = 0; i < turfLines.features.length; i++) {
for (let j = i + 1; j < turfLines.features.length; j++) {
const intersections = turf.lineIntersect(turfLines.features[i], turfLines.features[j]);
if (intersections) {
// console.log(intersections)
if (intersections.features.length > 0) {
intersectionSource.addFeatures(new ol.format.GeoJSON().readFeatures(intersections, {
featureProjection: 'EPSG:3857'
}))
// console.log(intersectionSource.getFeatures())
}
}
}
}
// console.log(intersectionSource)
const intersectionLayer = new ol.layer.Vector({
source: intersectionSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
}),
fill: new ol.style.Fill({
color: '#ff0000'
}),
image: new ol.style.Circle({
radius: 5,
fill: new ol.style.Fill({
color: '#ff0000'
})
})
})
})
map.addLayer(intersectionLayer);
})
</script>
</body>
</html>
结果图如下:

6. 参考资料
[1]空间运算—ArcMap | 文档 (arcgis.com)
[2]Turf.js—让你在浏览器上实现地理分析 - 掘金 (juejin.cn)
[3]Turf.js | Advanced Geospatial Analysis (turfjs.org)
[5]Turfjs/turf: A modular geospatial engine written in JavaScript (github.com)
[6]使用 Fetch - Web API 接口参考 | MDN (mozilla.org)
[8]openlayers+turf.js实现缓冲区的绘制_gis_SSS的博客-CSDN博客_openlayers缓冲区分析
[9]OpenLayers v6.14.1 API - Class: EsriJSON
OpenLayers结合Turf实现空间运算的更多相关文章
- (转)R空间数据处理与可视化
前言 很多朋友说在R里没法使用高德地图,这里给出一个基于leaflet包的解决方法. library(leaflet) # 添加高德地图 m <- leaflet() %>% addTil ...
- MySQL中的GIS几何函数和空间分析函数
MySQL空间扩展不仅提供了空间数据的存储能力,而且还具备一些空间运算能力,这些功能通过MySQL内建的几何函数实现.最简单的几何函数昨天已经有所涉及,也就是转换WTK的GEOMFROMTEXT和AS ...
- OpenGIS 介绍
转自:http://www.blogjava.net/sinoly/archive/2007/09/25/148002.html 值此FOSS4G大会即将召开之日,最近我会在Blog上依次介绍一些Op ...
- GIS应用及OpenGIS介绍
转自:http://blog.csdn.net/cdl2008sky/article/details/7266680 GIS的三大应用第一类是政府应用,“电子政务” 是当今政府加强信息化建设的新方向, ...
- OpenGIS 介绍(转)
值此FOSS4G大会即将召开之日,最近我会在Blog上依次介绍一些OpenGIS标准.架构及用于实现的软件.一方面给初涉此行的朋友一个快速入门的概览,另一方面也是对我接触OpenGIS近一年来的总结. ...
- Gamma校正与线性空间
基础知识部分 为了方便理解,首先会对(Luminance)的相关概念做一个简单介绍.如果已经了解就跳到后面吧. 我们用Radiant energy(辐射能量)来描述光照的能量,单位是焦耳(J),因为光 ...
- 转自一个CG大神的文章
<如何学好游戏3D引擎编程>此篇文章献给那些为了游戏编程不怕困难的热血青年,它的神秘要我永远不间断的去挑战自我,超越自我,这样才能攀登到游戏技术的最高峰 ——阿哲VS自 ...
- 转载:[转]如何学好3D游戏引擎编程
[转]如何学好3D游戏引擎编程 Albert 本帖被 gamengines 从 游戏引擎(Game Engine) 此文为转载,但是值得一看. 此篇文章献给那些为了游戏编程不怕困难的热血青年,它的 ...
- PRML读书会第七章 Sparse Kernel Machines(支持向量机, support vector machine ,KKT条件,RVM)
主讲人 网神 (新浪微博: @豆角茄子麻酱凉面) 网神(66707180) 18:59:22 大家好,今天一起交流下PRML第7章.第六章核函数里提到,有一类机器学习算法,不是对参数做点估计或求其分 ...
- ArcGIS 帮助(10.2、10.2.1 和 10.2.2)收集
帮助首页 [Oracle基础] 快速浏览:Oracle 地理数据库 什么是 Oracle Spatial? 设置到 Oracle 的连接 存储在 Oracle 地理数据库中的系统表 结合企业级地理数据 ...
随机推荐
- Spring框架之IOC入门
1.开发环境 IDEA版本: 2022.1.4 Maven版本:3.8.6 Spring版本:6.0.2 2.案例分析 2.1 自定义Man类 2.2 自定义Company类 2.3 自定义Compa ...
- 基于EasyCode定制Mybatisplus全自动单表实现:新增/批量新增/修改/批量删除/分页查询/ID查询
基于EasyCode定制Mybatisplus全自动单表实现CRUD接口 分页查询 ID查询 新增 批量新增 修改 批量删除 注意使用了MybatisPlus的自动填充功能,和insertBatchS ...
- 持续发烧,聊聊Dart语言的并发处理,能挑战Go不?
前言 貌似关于Dart的文章没流量啊,就算在小编关怀上了首页,看得人还是很少的. 算了,今天持续发烧,再来写写如何使用 Dart 语言的并发操作.说起并发操作,玩 Go 的同学该笑了,这就是我们的看家 ...
- 金融科技 DevOps 的最佳实践
随着软件技术的发展,越来越多的企业已经开始意识到 DevOps 文化的重要价值.DevOps 能够消除改变公司业务开展方式,并以更快的速度实现交付,同时创建迭代反馈循环以实现持续改进.而对于金融科技( ...
- Azure DevOps 的架构窥探
工作的缘故,接触 TFS (Team Foundation Server)挺多的,现在改名为 Azure DevOps,分为 可私有化部署版本 Azure DevOps Server,简称ADS,以及 ...
- day04-Vue01
Vue01 1.Vue是什么? Vue(读音/vju:/,类似于view)是一个前端框架,依据构建用户界面 Vue的核心库只关注视图层,不仅易于上手,还便于与第三方库或者项目整合 支持和其他类库结合使 ...
- 系列化和反序列化的概述-对象的序列化_Object Output Stream类
系列化和反序列化的概述 Java提供了一种对象序列化的机制.用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型和对象中存储的属性等信息.字节序列写出到文件之后,相当于文件中持久保存了 ...
- DNS 是如何影响你冲浪速度的?
本文详细介绍了 DNS 相关知识,包括 DNS 工作原理.如何提升域名解析速度.以及 DNS 记录与报文等内容. 1. 域名与域名服务器 在日常上网过程中,出于好记的原因,人们更喜欢在浏览器中输入网站 ...
- 【大型软件开发】浅谈大型Qt软件开发(三)QtActive Server如何通过COM口传递自定义结构体?如何通过一个COM口来获得所有COM接口?
前言 最近我们项目部的核心产品正在进行重构,然后又是年底了,除了开发工作之外项目并不紧急,加上加班时间混不够了....所以就忙里偷闲把整个项目的开发思路聊一下,以供参考. 鉴于接下来的一年我要进行这个 ...
- Unity之Android端权限申请
Unity之Android端权限申请 Unity之Android端权限申请 前言 开篇废话 Unity版本 正题 前期准备 挂载脚本 打包发布 安装App 查看结果 结尾 唠家常 今日无推荐 Unit ...