前言
❝
缓冲区分析是地理信息系统(GIS)空间分析的核心功能之一。它通过围绕点、线或面等地理实体,自动生成指定距离(或宽度)的等距区域(缓冲区)。该功能为量化空间邻近度、评估影响范围、识别潜在冲突或关联区域提供了基础而强大的工具,是理解空间关系、支持空间决策不可或缺的重要手段。
本篇教程在之前一系列文章的基础上讲解如何将使用GeoTools
工具结合OpenLayers
实现空间数据的空间缓冲区分析功能。
- GeoTools 开发环境搭建[1]
- 将 Shp 导入 PostGIS 空间数据的五种方式(全)[2]
- GeoTools 结合 OpenLayers 实现空间查询[3]
如果你还没有看过,建议从那里开始。
1. 开发环境
本文使用如下开发环境,以供参考。
时间:2025年
GeoTools:v34-SNAPSHOT
IDE:IDEA2025.1.2
JDK:v17
OpenLayers:v9.2.4
Layui:v2.9.14
2. 搭建后端服务
在项目接口层创建SpatialAnalyseController
空间分析控制器。声明缓冲区分析需要的参数一个Geometry类型的GeoJSON标准对象和一个缓冲距离对象。
package com.example.geotoolsboot.controller;
import com.example.geotoolsboot.service.ISpatialAnalyseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
* @name: SpatialAnalyseController
* @description: 空间分析控制器
* @author: gis_road
* @date: 2025-08-05
*/
@CrossOrigin(origins = "*") // 允许跨域
@RestController
public class SpatialAnalyseController {
@Autowired
private ISpatialAnalyseService spatialAnalyseService;
@GetMapping("/bufferAnalyse")
public Map<String,Object> bufferAnalyse(@RequestParam() String geoJSON, Float bufferDistance){
return spatialAnalyseService.bufferAnalyse(geoJSON,bufferDistance);
}
}
在服务层创建ISpatialAnalyseService
接口并定义缓冲分析方法。
package com.example.geotoolsboot.service;
import java.util.Map;
/**
* @name: ISpatialAnalyseService
* @description: 空间分析服务层
* @author: gis_road
* @date: 2025-08-05
*/
public interface ISpatialAnalyseService {
Map<String,Object> bufferAnalyse(String geoJSON,Float bufferDistance);
}
在服务层中实现ISpatialAnalyseService
接口。首先将前端传递过来的Geomery GeoJSON字符串对象转换为GeoTools中的Geometry对象,之后便可以调用buffer
方法创建缓冲区对象,最后将缓冲分析结果再转换为GeoJSON对象并返回给前端。
package com.example.geotoolsboot.service.impl;
import com.example.geotoolsboot.service.ISpatialAnalyseService;
import org.geotools.geojson.geom.GeometryJSON;
import org.locationtech.jts.geom.Geometry;
import org.springframework.stereotype.Service;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @name: SpatialAnalyseServiceImpl
* @description: 空间分析实现层
* @author: gis_road
* @date: 2025-08-05
*/
@Service
public class SpatialAnalyseServiceImpl implements ISpatialAnalyseService {
@Override
public Map<String,Object> bufferAnalyse(String geoJSON,Float bufferDistance) {
Map<String,Object> resultMap = new HashMap<>();
// 读取GeoJSON几何对象
GeometryJSON geometryJSON = new GeometryJSON(15);
StringReader reader = new StringReader(geoJSON);
try{
Geometry geometry = geometryJSON.read(reader);
// 根据距离创建缓冲对象
Geometry geoBuffer = geometry.buffer(bufferDistance);
// 将缓冲结果转换为Geometry GeoJSON对象
StringWriter writer = new StringWriter();
geometryJSON.write(geoBuffer,writer);
String geoBufferJSON = writer.toString();
resultMap.put("data",geoBufferJSON);
}catch (Exception e){
e.printStackTrace();
}
return resultMap;
}
}
3. OpenLayers 加载缓冲对象
本文前端使用OpenLayers
结合Layui
框架实现。为了方便控制缓冲区生成距离,本文使用坐标系为投影坐标系,具体EPSG为4522。
缓冲区分析面板CSS结构。
.query-wrap {
position: absolute;
padding: 10px;
top: 80px;
left: 90px;
background: #ffffff;
width: 250px;
border-radius: 2.5px;
}
HTML结构。
<div class="query-wrap">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<label class="layui-form-label">绘制对象</label>
<div class="layui-input-block">
<select name="condition" lay-filter="draw-select-filter">
<option value="None">请选择绘制类型</option>
<option value="Point">点</option>
<option value="LineString">线</option>
<option value="Polygon">面</option>
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">缓冲距离</label>
<div class="layui-input-block">
<input type="text" name="bufferDistance" lay-verify="required" placeholder="缓冲距离(m)"
autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<button lay-submit lay-filter="clearAll" class="layui-btn layui-btn-primary">清除</button>
<button class="layui-btn" lay-submit lay-filter="spatialAnalyse">确认</button>
</div>
</form>
</div>
前端实现代码如下,逻辑也很简单。
let drawInteraction = null // 绘制控件
let geoJSON = null // 绘制的Geometry GeoJSON对象
let bufferDistance = 0 // 缓冲距离
layui.use(['form'], function () {
const form = layui.form;
const layer = layui.layer;
// 绘制事件
form.on('select(draw-select-filter)', function (data) {
removeInteraction()
const value = data.value; // 获得被选中的值
drawShape(value)
});
// 清除事件
form.on("submit(clearAll)", function (data) {
// 清除绘制事件
removeInteraction()
// 清除图形
removeAllLayer(map)
return false; // 阻止默认 form 跳转
})
// 提交事件
form.on('submit(spatialAnalyse)', function (data) {
if (!geoJSON) {
layer.msg("请绘制缓冲区域")
return false
}
const bufferDistance = +data.field.bufferDistance
if (!bufferDistance) {
layer.msg("请输入缓冲距离")
return false
}
const queryParam = encodeURIComponent(geoJSON)
// 后端服务地址
const JSON_URL = `http://127.0.0.1:8080/bufferAnalyse?geoJSON=${queryParam}&bufferDistance=${bufferDistance}`
fetch(JSON_URL).then(response => response.json()
.then(result => {
removeLayerByName("bufferLayer", map)
const bufferJSON = JSON.parse(result.data)
const feature = new ol.Feature({
type: "Feature",
geometry: new ol.format.GeoJSON().readGeometry(bufferJSON)
})
const vectorSource = new ol.source.Vector({
features: [feature],
format: new ol.format.GeoJSON()
})
// 缓冲区图层
const bufferLayer = new ol.layer.Vector({
source: vectorSource,
style: new ol.style.Style({
fill: new ol.style.Fill({
color: "#e77b7e8f"
}),
stroke: new ol.style.Stroke({
color: "#da4736c2",
width: 2.5,
}),
})
})
bufferLayer.set("layerName", "bufferLayer")
map.addLayer(bufferLayer)
map.getView().fit(feature.getGeometry().getExtent())
})
)
return false; // 阻止默认 form 跳转
});
});
创建drawShape
函数,用于绘制点、线和面等几何类型,removeInteraction
方法用于移除绘制控件。需要监听绘制完成事件,当绘制结束后,读取绘制要素并获取Geometry
对象。
/**
* 根据几何类型绘制几何对象
*/
function drawShape(type) {
const drawSource = new ol.source.Vector({ wrapX: false })
const drawLayer = new ol.layer.Vector({
source: drawSource,
style
})
drawLayer.setZIndex(999)
map.addLayer(drawLayer)
geoJSON = null
if (type === "None") {
removeInteraction()
// 清除图形
drawSource.clear()
return
}
let geometryFunction = null
drawInteraction = new ol.interaction.Draw({
source: drawSource,
type,
geometryFunction,
style,
// freehand: true // 是否开启自由绘制模式
})
map.addInteraction(drawInteraction)
drawInteraction.on('drawend', evt => {
const feature = evt.feature
const featObj = new ol.format.GeoJSON().writeFeature(feature)
const geomtObj = new ol.format.GeoJSON().writeGeometry(feature.getGeometry())
// 存储绘制对象
geoJSON = geomtObj
})
}
// 移除绘制控件
function removeInteraction() {
if (drawInteraction) {
map.removeInteraction(drawInteraction)
}
}
参考资料
1\]GeoTools 开发环境搭建 \[2\]将 Shp 导入 PostGIS 空间数据的五种方式(全) \[3\]GeoTools 结合 OpenLayers 实现空间查询