GeoTools 结合 OpenLayers 实现缓冲区分析

前言

缓冲区分析是地理信息系统(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 实现空间查询