OpenLayers结合JSTS实现空间运算
1. 引言
空间运算利用几何函数来接收输入的空间数据,对其进行分析,然后生成输出数据,输出数据为针对输入数据执行分析的派生结果。
可从空间运算中获得的派生数据包括:
- 作为输入要素周围缓冲区的面
- 作为对几何集合执行分析的结果的单个要素
- 作为比较结果以确定不与其他要素位于同一物理空间的要素部分的单个要素
- 作为比较结果以查找与其他要素的物理空间相交的要素部分的单个要素
- 由彼此不位于同一物理空间的输入要素部分组成的多部分 (multipart) 要素
- 作为两个几何的并集的要素
参考文档:空间运算—ArcMap | 文档 (arcgis.com)
JTS (Java Topology Suite) Java拓扑套件,是Java的处理地理数据的API。JTS支持一套完整的二元谓词操作。二元谓词方法将两个几何图形作为参数,返回一个布尔值来表示几何图形是否有指定的空间关系。它支持的空间关系有:相等(equals)、分离(disjoint)、相交(intersect)、相接(touches)、交叉(crosses)、包含于(within)、包含(contains)、覆盖/覆盖于(overlaps)
JTS的Github地址为:locationtech/jts: The JTS Topology Suite is a Java library for creating and manipulating vector geometry. (github.com)
JSTS是JTS的JavaScript版,并且对OpenLayers很友好
JSTS的Github地址为:bjornharrtell/jsts: JavaScript Topology Suite (github.com)
OpenLayers与JSTS集成的官方示例:JSTS Integration (openlayers.org)
本文参考OpenLayers与JSTS集成的官方示例,使用示例数据和原生JavaScript,进行求交运算和缓冲区运算,并进行可视化
2. 代码实现
2.1 引入CDN
使用JSTS、OpenLayers、jQuery的在线CDN引入
<!-- openlayers cdn -->
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css">
<!-- JSTS cdn -->
<script src="https://unpkg.com/jsts@2.3.0/dist/jsts.min.js"></script>
<!-- jQuery cdn -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
2.2 页面初始化
构建HTML初始页面,新建一个地图容器,设置一些简单的CSS样式
<!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>
<!-- openlayers cdn -->
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css">
<!-- JSTS cdn -->
<script src="https://unpkg.com/jsts@2.3.0/dist/jsts.min.js"></script>
<!-- jQuery cdn -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
</body>
</html>
2.3 Buffer操作
Buffer操作参考OpenLayers与JSTS集成的官方示例:JSTS Integration (openlayers.org),实现代码如下:
const linesSource = new ol.source.Vector();
$.ajax({
url: 'https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson',
dataType: 'json',
success: function (data) {
// console.log(data)
var features = new ol.format.GeoJSON().readFeatures(data, {
featureProjection: 'EPSG:3857'
});
const parser = new jsts.io.OL3Parser();
parser.inject(
ol.geom.Point,
ol.geom.LineString,
ol.geom.LinearRing,
ol.geom.Polygon,
ol.geom.MultiPoint,
ol.geom.MultiLineString,
ol.geom.MultiPolygon
);
for (let i = 0; i < features.length; i++) {
const feature = features[i];
// convert the OpenLayers geometry to a JSTS geometry
const jstsGeom = parser.read(feature.getGeometry());
// create a buffer of 40 meters around each line
const buffered = jstsGeom.buffer(10);
// convert back from JSTS and replace the geometry on the feature
feature.setGeometry(parser.write(buffered));
}
linesSource.addFeatures(features)
var linesVector = new ol.layer.Vector({
source: linesSource,
});
// map.addLayer(vector);
var map = new ol.Map({
target: 'map',
layers: [
// new ol.layer.Tile({
// source: new ol.source.OSM()
// }),
linesVector,
],
view: new ol.View({
center: ol.proj.fromLonLat([126.979293, 37.528787]),
zoom: 15
})
});
}
})
2.4 相交运算
相交运算(intersection)的主要代码如下,具体见下一小结的全部代码:
for (let index = 0; index < jstsgeoarr.length; index++) {
for (let index2 = index + 1; index2 < jstsgeoarr.length; index2++) {
if (jstsgeoarr[index].intersection(jstsgeoarr[index2]).toText() != 'LINESTRING EMPTY') {
point = jstsgeoarr[index].intersection(jstsgeoarr[index2])
jstsPoints.push(point)
}
}
}
2.5 结果可视化
将相交运算与缓冲区操作结合在一起使用OpenLayers进行可视化,全部代码如下:
<!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>
<!-- openlayers cdn -->
<script src="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/build/ol.js"></script>
<link rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/openlayers/openlayers.github.io@master/en/v6.14.1/css/ol.css">
<!-- JSTS cdn -->
<script src="https://unpkg.com/jsts@2.3.0/dist/jsts.min.js"></script>
<!-- jQuery cdn -->
<script src="https://code.jquery.com/jquery-3.3.1.min.js"
integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<style>
html,
body,
#map {
height: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const pointsSource = new ol.source.Vector();
const linesSource = new ol.source.Vector();
var jstsgeoarr = [];
var jstsPoints = [];
$.ajax({
url: 'https://openlayers.org/en/latest/examples/data/geojson/roads-seoul.geojson',
dataType: 'json',
success: function (data) {
// console.log(data)
var features = new ol.format.GeoJSON().readFeatures(data, {
featureProjection: 'EPSG:3857'
});
const parser = new jsts.io.OL3Parser();
parser.inject(
ol.geom.Point,
ol.geom.LineString,
ol.geom.LinearRing,
ol.geom.Polygon,
ol.geom.MultiPoint,
ol.geom.MultiLineString,
ol.geom.MultiPolygon
);
for (let i = 0; i < features.length; i++) {
const feature = features[i];
// convert the OpenLayers geometry to a JSTS geometry
const jstsGeom = parser.read(feature.getGeometry());
// create a buffer of 40 meters around each line
const buffered = jstsGeom.buffer(10);
// const length = jstsGeom.length();
jstsgeoarr.push(jstsGeom)
// convert back from JSTS and replace the geometry on the feature
feature.setGeometry(parser.write(buffered));
}
for (let index = 0; index < jstsgeoarr.length; index++) {
for (let index2 = index + 1; index2 < jstsgeoarr.length; index2++) {
if (jstsgeoarr[index].intersection(jstsgeoarr[index2]).toText() != 'LINESTRING EMPTY') {
point = jstsgeoarr[index].intersection(jstsgeoarr[index2])
jstsPoints.push(point)
}
}
}
var intersectionfeatures = [];
for (let i = 0; i < jstsPoints.length; i++) {
const jstsPoint = jstsPoints[i];
const buffered = jstsPoint.buffer(40);
intersectionfeature = new ol.format.WKT().readFeatures(buffered.toText());
pointsSource.addFeatures(intersectionfeature)
}
linesSource.addFeatures(features)
var linesVector = new ol.layer.Vector({
source: linesSource,
});
var pointsVector = new ol.layer.Vector({
source: pointsSource,
style: new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#ff0000',
width: 2
})
})
});
// map.addLayer(vector);
var map = new ol.Map({
target: 'map',
layers: [
// new ol.layer.Tile({
// source: new ol.source.OSM()
// }),
linesVector,
pointsVector
],
view: new ol.View({
center: ol.proj.fromLonLat([126.979293, 37.528787]),
zoom: 15
})
});
}
});
</script>
</body>
</html>
最后在浏览器查看:
3. 参考资料
[1]空间运算—ArcMap | 文档 (arcgis.com)
[2]JTS Java空间几何计算、距离、最近点、subLine等 稳健的一比,持续更新中_lakernote的博客-CSDN博客_java jts
[3]JSTS Integration (openlayers.org)
[4]GIS算法:8_JavaScript拓扑套件turf - 知乎 (zhihu.com)
[5]JTS基本概念和使用_runing9的博客-CSDN博客_jts
[6]OpenLayers v6.14.1 API - Class: WKT
[7]OpenLayers结合JSTS实现空间扩展 - 朱凤丽 (zhufengli.com)
[8]JTS Java空间几何计算、距离、最近点、subLine等 稳健的一比,持续更新中_lakernote的博客-CSDN博客_java jts