三维地理数据在MySQL中的存储和使用

在我司的一个项目中,原本使用 MySQL 自带的空间数据类型来存储地理数据。但由于需求变更,现在需要为每个坐标点添加三维信息,这超出了 MySQL 空间数据类型的能力范围。为了解决这个问题,我重新设计了存储方案:使用 VARCHAR 类型字段存储类似 WKT 格式的三维坐标字符串,同时借助 JTS 库来处理二维空间的查询操作(如点是否在面内、线是否与面相交等),并开发了一系列工具类来处理坐标格式转换和空间关系计算。这样既保留了三维信息的存储,又能继续使用强大的空间查询功能。

前言

我司的一个项目需要将地理数据存储到 MySQL 中,之前使用 MySQL 自带的空间数据类型来存储地理数据(POINT、LINESTRING、POLYGON),其自带的空间函数也可以比较方便地进行空间数据筛选。但需求说变就变(╥﹏╥...),现在又需要为每个点坐标加入三维信息(比如存储了一条线,构成线的每个点都需要有三个维度的坐标)。如果一个地理数据的三维信息是一致的,还可以新加一个字段来存储,但是每一个点的三维信息需要单独存储的话,MySQL 自带的空间数据类型就存储不了了。并且后续的空间范围筛选需要在二维的维度下进行(比如指定某条线以及缓冲区范围,在二维空间中,需要筛选出在缓冲区中的所有点、和缓冲区相交的所有线和面)。所以之前的实现全部要推翻重构,你说为什么一开始不说好呢,功能写了一半了才和我说,好像很好改一样 (╯︵╰)。

更改后的实现逻辑是这样:

  1. 在数据库表中新增一个字段 geometryType,用于标识几何类型点、线、面。
  2. 将地理数据存放在 VARCHAR 类型的字段 geometry 中,用类似 Well-Known Text (WKT) 格式的字符串来存储地理数据,如线的存储:121.5312889779898 31.70429572226042 10,121.52181611362442 31.67929066337639 20,121.5288113599404 31.603770286693038 30
  3. 写一个工具类,用于将字符串格式的空间数据转换成 JTSorg.locationtech.jts.geom)库中的 Geometry 对象(只转换其二维空间)。在进行空间查询时,可以直接调用 JTS 库的实现。
  4. 写一个工具类,用于将字符串格式的空间数据转换为 GeoJSON 格式的字符串,方便返回给前端使用。

引入依赖

<!--jts-->  
<dependency>  
    <groupId>org.locationtech.jts</groupId>  
    <artifactId>jts-core</artifactId>  
    <version>1.20.0</version>  
</dependency>

实体类

SpatialFeature

@Data  
@TableName("spatial_features")  
@ApiModel(value = "三维地理数据对象", description = "三维地理数据表")  
public class SpatialFeature {  
    @ApiModelProperty("主键ID")  
    private Long id;  
  
    @ApiModelProperty("几何类型:点、线、面")  
    private GeometryType geometryType;  
  
    @ApiModelProperty("三维地理数据")  
    private String geometry;  
  
    @ApiModelProperty("创建时间")  
    private LocalDateTime createdAt;  
  
    @ApiModelProperty("阶段ID")  
    private String prId;  
  
    public enum GeometryType {  
        POINT,  
        LINESTRING,  
        POLYGON  
    }  
}

工具类

SpatialUtils,用于将字符串格式的三维空间数据转换成 JTSGeometry 对象,只转换二维空间:

import lombok.experimental.UtilityClass;  
import org.locationtech.jts.geom.Coordinate;  
import org.locationtech.jts.geom.GeometryFactory;  
import org.locationtech.jts.geom.Point;  
import org.locationtech.jts.geom.LineString;  
import org.locationtech.jts.geom.Polygon;  
import org.locationtech.jts.geom.LinearRing;  
  
@UtilityClass  
public class SpatialUtils {  
    private static final GeometryFactory geometryFactory = new GeometryFactory();  
  
    public static boolean validateCoordinates(String coordinates) {  
        String regex = "^(-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?)(,\\s*-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?)*$";  
        return coordinates.matches(regex);  
    }  
  
    public static Point createPoint(String coordinates) {  
        try {  
            String[] parts = coordinates.trim().split("\\s+");  
            double x = Double.parseDouble(parts[0]);  
            double y = Double.parseDouble(parts[1]);  
            // 确保坐标在有效范围内  
            if (Math.abs(x) <= 180 && Math.abs(y) <= 90) {  
                return geometryFactory.createPoint(new Coordinate(x, y));  
            }  
            throw new IllegalArgumentException("Coordinates out of valid range");  
        } catch (Exception e) {  
            throw new IllegalArgumentException("Invalid coordinate format", e);  
        }  
    }  
  
    public static LineString createLineString(String coordinates) {  
        try {  
            String[] points = coordinates.split(",");  
            Coordinate[] coords = new Coordinate[points.length];  
  
            for (int i = 0; i < points.length; i++) {  
                String[] parts = points[i].trim().split("\\s+");  
                double x = Double.parseDouble(parts[0]);  
                double y = Double.parseDouble(parts[1]);  
  
                // 验证坐标范围  
                if (Math.abs(x) > 180 || Math.abs(y) > 90) {  
                    throw new IllegalArgumentException("Coordinates out of valid range: " + x + ", " + y);  
                }  
  
                coords[i] = new Coordinate(x, y);  
            }  
  
            // 线至少需要两个点  
            if (coords.length < 2) {  
                throw new IllegalArgumentException("LineString must have at least 2 points");  
            }  
  
            return geometryFactory.createLineString(coords);  
        } catch (Exception e) {  
            throw new IllegalArgumentException("Invalid LineString coordinates format", e);  
        }  
    }  
  
    public static Polygon createPolygon(String coordinates) {  
        try {  
            String[] points = coordinates.split(",");  
            Coordinate[] coords = new Coordinate[points.length + 1]; // 多一个点用于闭合多边形  
  
            // 构建坐标数组  
            for (int i = 0; i < points.length; i++) {  
                String[] parts = points[i].trim().split("\\s+");  
                double x = Double.parseDouble(parts[0]);  
                double y = Double.parseDouble(parts[1]);  
  
                // 验证坐标范围  
                if (Math.abs(x) > 180 || Math.abs(y) > 90) {  
                    throw new IllegalArgumentException("Coordinates out of valid range: " + x + ", " + y);  
                }  
  
                coords[i] = new Coordinate(x, y);  
            }  
  
            // 多边形至少需要3个点  
            if (points.length < 3) {  
                throw new IllegalArgumentException("Polygon must have at least 3 points");  
            }  
  
            // 闭合多边形:最后一个点必须和第一个点相同  
            coords[coords.length - 1] = coords[0];  
  
            // 创建线性环(外环)  
            LinearRing ring = geometryFactory.createLinearRing(coords);  
  
            // 创建多边形(目前只支持无内环的简单多边形)  
            return geometryFactory.createPolygon(ring);  
        } catch (Exception e) {  
            throw new IllegalArgumentException("Invalid Polygon coordinates format", e);  
        }  
    }  
}

Spatial3DUtils,用于验证三维坐标格式:

import lombok.experimental.UtilityClass;  
import java.util.regex.Pattern;  
  
@UtilityClass  
public class Spatial3DUtils {  
    // 验证三维坐标格式  
    private static final Pattern COORDINATE_PATTERN = Pattern.compile(  
            "^(-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?)(,\\s*-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?\\s+-?\\d+(\\.\\d+)?)*$"  
    );  
  
    public static boolean validateCoordinates(String coordinates) {  
        return COORDINATE_PATTERN.matcher(coordinates).matches();  
    }  
}

GeoUtils,用于将字符串格式的空间数据转换为 GeoJSON 格式的字符串,并包装了 JTS 的空间查询方法:

import com.fasterxml.jackson.databind.ObjectMapper;  
import lombok.experimental.UtilityClass;  
import org.locationtech.jts.geom.Geometry;  
import org.locationtech.jts.geom.LineString;  
import org.locationtech.jts.geom.Point;  
import org.locationtech.jts.geom.Polygon;  
  
import java.io.IOException;  
import java.io.StringWriter;  
import java.util.Map;  
  
  
@UtilityClass  
public class GeoUtils {  
    private static final ObjectMapper objectMapper = new ObjectMapper();  
  
    /**  
     * 将三维坐标字符串转换为 GeoJSON 字符串  
     * @param geometryStr 坐标字符串  
     * @param geometryType 几何类型  
     * @return GeoJSON 格式的字符串  
     */  
    public static String convertToGeoJSONStr(String geometryStr, String geometryType) {  
        // 构建 GeoJSON 结构  
        StringBuilder json = new StringBuilder();  
        json.append("{\"type\":\"").append(geometryType).append("\",\"coordinates\":");  
  
        switch (geometryType) {  
            case "POINT":  
                // 处理点坐标: "x y z" -> [x,y,z]  
                json.append(convertPointCoordinates(geometryStr));  
                break;  
            case "LINESTRING":  
                // 处理线坐标: "x1 y1 z1,x2 y2 z2" -> [[x1,y1,z1],[x2,y2,z2]]  
                json.append(convertLineStringCoordinates(geometryStr));  
                break;  
            case "POLYGON":  
                // 处理面坐标: "x1 y1 z1,x2 y2 z2,x3 y3 z3" -> [[[x1,y1,z1],[x2,y2,z2],[x3,y3,z3],[x1,y1,z1]]]  
                json.append(convertPolygonCoordinates(geometryStr));  
                break;  
            default:  
                throw new IllegalArgumentException("Unsupported geometry type: " + geometryType);  
        }  
  
        json.append("}");  
        return json.toString();  
    }  
  
    /**  
     * 将三维坐标字符串转换为 GeoJSON Map 对象  
     * @param geometryStr 坐标字符串  
     * @param geometryType 几何类型  
     * @return GeoJSON 格式的 Map 对象  
     * @throws IOException  
     */    public static Map<String, Object> convertToGeoJSON(String geometryStr, String geometryType) throws IOException {  
        String geoJsonStr = convertToGeoJSONStr(geometryStr, geometryType);  
        return objectMapper.readValue(geoJsonStr, Map.class);  
    }  
  
    private static String convertPointCoordinates(String coordStr) {  
        String[] coords = coordStr.trim().split("\\s+");  
        return String.format("[%s,%s,%s]", coords[0], coords[1], coords[2]);  
    }  
  
    private static String convertLineStringCoordinates(String coordStr) {  
        StringBuilder result = new StringBuilder("[");  
        String[] points = coordStr.split(",");  
  
        for (int i = 0; i < points.length; i++) {  
            if (i > 0) {  
                result.append(",");  
            }  
            result.append(convertPointCoordinates(points[i].trim()));  
        }  
  
        return result.append("]").toString();  
    }  
  
    private static String convertPolygonCoordinates(String coordStr) {  
        StringBuilder result = new StringBuilder("[[");  
        String[] points = coordStr.split(",");  
  
        // 添加所有点  
        for (int i = 0; i < points.length; i++) {  
            if (i > 0) {  
                result.append(",");  
            }  
            result.append(convertPointCoordinates(points[i].trim()));  
        }  
  
        // 如果第一个点和最后一个点不同,添加第一个点以闭合多边形  
        String firstPoint = points[0].trim();  
        String lastPoint = points[points.length - 1].trim();  
        if (!firstPoint.equals(lastPoint)) {  
            result.append(",").append(convertPointCoordinates(firstPoint));  
        }  
  
        return result.append("]]").toString();  
    }  
  
    /**  
     * 判断点是否在面内  
     * @param point 点  
     * @param polygon 面  
     * @return true 如果点在面内,false 否则  
     */  
    public boolean isPointInPolygon(Point point, Polygon polygon) {  
        if (point == null || polygon == null) {  
            return false;  
        }  
        return polygon.contains(point);  
    }  
  
    /**  
     * 判断点是否在线的缓冲区内  
     * @param point 点  
     * @param line 线  
     * @param bufferDistance 缓冲区距离  
     * @return true如果点在线的缓冲区内,false否则  
     */  
    public boolean isPointInLineBuffer(Point point, LineString line, double bufferDistance) {  
        if (point == null || line == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建线的缓冲区  
        Geometry buffer = line.buffer(bufferDistance);  
        return buffer.contains(point);  
    }  
  
    /**  
     * 判断线是否与另一条线的缓冲区相交  
     * @param line1 第一条线  
     * @param line2 第二条有缓冲区的线  
     * @param bufferDistance 缓冲区距离  
     * @return true如果线与另一条线的缓冲区相交,false否则  
     */  
    public static boolean isLineIntersectLineBuffer(LineString line1, LineString line2, double bufferDistance) {  
        if (line1 == null || line2 == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建第二条线的缓冲区  
        Geometry buffer = line2.buffer(bufferDistance);  
        return buffer.intersects(line1);  
    }  
  
    /**  
     * 判断面是否与线的缓冲区相交  
     */  
    public boolean isPolygonIntersectLineBuffer(Polygon polygon, LineString line, double bufferDistance) {  
        if (line == null || polygon == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建线的缓冲区  
        Geometry buffer = line.buffer(bufferDistance);  
        return buffer.intersects(polygon);  
    }  
  
    /**  
     * 判断线是否与面相交  
     * @param line 线  
     * @param polygon 面  
     * @return true如果线与面相交,false否则  
     */  
    public boolean isLineIntersectPolygon(LineString line, Polygon polygon) {  
        if (line == null || polygon == null) {  
            return false;  
        }  
        return line.intersects(polygon);  
    }  
  
    /**  
     * 判断两个面是否相交  
     * @param polygon1 第一个面  
     * @param polygon2 第二个面  
     * @return true如果两个面相交,false否则  
     */  
    public boolean isPolygonIntersectPolygon(Polygon polygon1, Polygon polygon2) {  
        if (polygon1 == null || polygon2 == null) {  
            return false;  
        }  
        return polygon1.intersects(polygon2);  
    }  
  
    /**  
     * 判断点是否在点的缓冲区内  
     * @param point1 第一个点  
     * @param point2 第二个有缓冲区的点  
     * @param bufferDistance 缓冲区距离  
     */  
    public boolean isPointInPointBuffer(Point point1, Point point2, double bufferDistance) {  
        if (point1 == null || point2 == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建第二个点的缓冲区  
        Geometry buffer = point2.buffer(bufferDistance);  
        return buffer.contains(point1);  
    }  
  
    /**  
     * 判断线是否与点的缓冲区相交  
     * @param line 线  
     * @param point 有缓冲区的点  
     * @param bufferDistance 缓冲区距离  
     */  
    public boolean isLineIntersectPointBuffer(LineString line, Point point, double bufferDistance) {  
        if (line == null || point == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建点的缓冲区  
        Geometry buffer = point.buffer(bufferDistance);  
        return buffer.intersects(line);  
    }  
  
    /**  
     * 判断面是否与点的缓冲区相交  
     * @param polygon 面  
     * @param point 有缓冲区的点  
     * @param bufferDistance 缓冲区距离  
     */  
    public boolean isPolygonIntersectPointBuffer(Polygon polygon, Point point, double bufferDistance) {  
        if (polygon == null || point == null || bufferDistance <= 0) {  
            return false;  
        }  
        // 创建点的缓冲区  
        Geometry buffer = point.buffer(bufferDistance);  
        return buffer.intersects(polygon);  
    }  
  
  
    /**  
     * 判断几何对象是否有效  
     * @param geometry 几何对象  
     * @return true如果几何对象有效,false否则  
     */  
    public boolean isValid(Geometry geometry) {  
        return geometry != null && geometry.isValid();  
    }  
}

具体服务实现

SpatialFeatureServiceImpl

@Slf4j  
@Service  
@Transactional  
public class SpatialFeatureServiceImpl  
        extends ServiceImpl<SpatialFeatureMapper, SpatialFeature>  
        implements SpatialFeatureService {  
  
    @Override  
    public boolean saveGeometryData(SpatialFeature.GeometryType type, SpatialRequest request) {  
        if (!Spatial3DUtils.validateCoordinates(request.getCoordinates())) {  
            throw new IllegalArgumentException(  
                    String.format("Invalid 3D %s coordinates format", type.name().toLowerCase())  
            );  
        }  
  
        try {  
            SpatialFeature feature = new SpatialFeature();  
            feature.setGeometryType(type);  
            feature.setGeometry(request.getCoordinates());  
            feature.setPrId(request.getPrId());  
            feature.setCreatedAt(LocalDateTime.now());  
  
            return save(feature);  
        } catch (Exception e) {  
            log.error("Error saving 3D {}: {}", type.name().toLowerCase(), e.getMessage());  
            throw new RuntimeException("Failed to save 3D " + type.name().toLowerCase(), e);  
        }  
    }  
}

SpatialFeatureController

@Api(tags = "项目对象管理(项目、项目阶段、地理数据)")  
@RestController  
@RequestMapping("/common/project/feature")  
public class SpatialFeatureController {  
  
    @Resource  
    private ProjectStageService stageService;  
  
    @Resource  
    private ProjectNewService projectService;  
  
    @Autowired  
    private SpatialFeatureService spatialFeatureService;  
  
    @ApiOperation("新建项目、项目阶段、地理数据")  
    @RequiresPermissions  
    @PostMapping("add")  
    @Transactional(rollbackFor = Exception.class)  
    @Log(title = "项目管理", businessType = BusinessType.INSERT)  
    public AjaxResult addProjectAndStage(@RequestBody ProjectAndStageRequest request) {  
        ProjectNew project = request.getProject();  
  
        String projectGuid = Optional.ofNullable(project.getProjectGuid())  
                .orElseGet(() -> {  
                    project.setCreateBy(SecurityUtils.getUsername());  
                    project.setCreateTime(new Date());  
                    return projectService.createAndReturnId(project);  
                });  
  
        // 创建项目阶段  
        ProjectStage stage = request.getStage();  
        stage.setCreateTime(String.valueOf(new Date()));  
        stage.setCreateBy(SecurityUtils.getUsername());  
        stage.setProjectGuid(projectGuid);  
        String stageId = stageService.createStageAndReturnId(  
                stage, project.getProjectName(), project.getProjectShortname()  
        );  
  
        // 处理地理数据  
        Optional.ofNullable(request.getGeoData())  
                .filter(data -> request.getGeoType() != null)  
                .ifPresent(data -> {  
                    SpatialRequest spatialRequest = new SpatialRequest();  
                    spatialRequest.setCoordinates(data);  
                    spatialRequest.setPrId(stageId);  
                    spatialFeatureService.saveGeometryData(request.getGeoType(), spatialRequest);  
                });  
  
        return AjaxResult.success();  
    }  
  
  
    // 获取所有项目阶段的地理数据  
    @ApiOperation("获取所有地理数据")  
    @RequiresPermissions  
    @PostMapping("list")  
    public R<List<SpatialFeatureResponse>> listProjectStageGeoData() throws IOException {  
        List<SpatialFeatureResponse> res = new ArrayList<>();  
  
        List<SpatialFeature> list = spatialFeatureService.list();  
        for (SpatialFeature spatialFeature : list) {  
            String geometry = spatialFeature.getGeometry();  
            String prId = spatialFeature.getPrId();  
            Map<String, Object> stringObjectMap = GeoUtils.convertToGeoJSON(geometry, spatialFeature.getGeometryType().name());  
            res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
        }  
  
        return R.ok(res);  
    }  
  
  
    @ApiOperation("根据项目信息筛选地理数据")  
    @RequiresPermissions  
    @PostMapping("listByProject")  
    public R<List<SpatialFeatureResponse>> listByProject(@RequestBody QueryGeoRelationRequest request) throws IOException {  
        List<SpatialFeatureResponse> res = new ArrayList<>();  
  
        List<SpatialFeature> list = spatialFeatureService.list();  
        for (SpatialFeature spatialFeature : list) {  
            String geometry = spatialFeature.getGeometry();  
            String prId = spatialFeature.getPrId();  
            Map<String, Object> stringObjectMap = GeoUtils.convertToGeoJSON(geometry, spatialFeature.getGeometryType().name());  
            res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
        }  
  
        return R.ok(filterByProjectInfo(res, request));  
    }  
  
  
    // 获取在面内的点,与面相交的线和面  
    @ApiOperation("根据面和项目信息筛选地理数据")  
    @RequiresPermissions  
    @PostMapping("queryPolyRelation")  
    public R<List<SpatialFeatureResponse>> getPolyRelation(@RequestBody QueryGeoRelationRequest request) throws IOException {  
  
        List<SpatialFeatureResponse> res = new ArrayList<>();  
  
        // 将 geoFeatureStr 转换为 Geometry 对象  
        Polygon polygon = createPolygon(request.getGeoFeatureStr());  
  
        List<SpatialFeature> spatialFeatures = spatialFeatureService.list();  
        for (SpatialFeature spatialFeature : spatialFeatures) {  
  
            // 获取 Geometry 对象  
            String geometry = spatialFeature.getGeometry();  
            // 获取 prId            String prId = spatialFeature.getPrId();  
  
            Map<String, Object> stringObjectMap = GeoUtils.convertToGeoJSON(geometry, spatialFeature.getGeometryType().name());  
            SpatialFeature.GeometryType geometryType = spatialFeature.getGeometryType();  
  
            switch (geometryType) {  
                case POINT:  
                    Point point = createPoint(geometry);  
                    if (GeoUtils.isPointInPolygon(point, polygon)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case LINESTRING:  
                    LineString lineString = createLineString(geometry);  
                    if (GeoUtils.isLineIntersectPolygon(lineString, polygon)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case POLYGON:  
                    Polygon polygon2 = createPolygon(geometry);  
                    if (GeoUtils.isPolygonIntersectPolygon(polygon2, polygon)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
            }  
        }  
  
        return R.ok(filterByProjectInfo(res, request));  
    }  
  
    // 获取在线的缓冲区内的点、与线的缓冲区相交的线和面  
    @ApiOperation("根据线的缓冲区和项目信息筛选地理数据")  
    @RequiresPermissions  
    @PostMapping("queryLineBufferRelation")  
    public R<List<SpatialFeatureResponse>> getLineBufferRelation(@RequestBody QueryGeoRelationRequest request) throws IOException {  
  
        List<SpatialFeatureResponse> res = new ArrayList<>();  
  
        // 将 geoFeatureStr 转换为 Geometry 对象  
        // 将 "120 30, 121 31, 122 32_50" 分隔成线的字符串和缓冲区距离  
        String[] str_arr = request.getGeoFeatureStr().split("_");  
        LineString line = createLineString(str_arr[0]);  
        double bufferDistance = Double.parseDouble(str_arr[1]);  
  
        List<SpatialFeature> spatialFeatures = spatialFeatureService.list();  
        for (SpatialFeature spatialFeature : spatialFeatures) {  
  
            // 获取 Geometry 对象  
            String geometry = spatialFeature.getGeometry();  
            // 获取 prId            String prId = spatialFeature.getPrId();  
  
            Map<String, Object> stringObjectMap = GeoUtils.convertToGeoJSON(geometry, spatialFeature.getGeometryType().name());  
            SpatialFeature.GeometryType geometryType = spatialFeature.getGeometryType();  
  
            switch (geometryType) {  
                case POINT:  
                    Point point = createPoint(geometry);  
                    if (GeoUtils.isPointInLineBuffer(point, line, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case LINESTRING:  
                    LineString lineString = createLineString(geometry);  
                    if (GeoUtils.isLineIntersectLineBuffer(lineString, line, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case POLYGON:  
                    Polygon polygon = createPolygon(geometry);  
                    if (GeoUtils.isPolygonIntersectLineBuffer(polygon, line, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
            }  
        }  
  
        return R.ok(filterByProjectInfo(res, request));  
    }  
  
    // 获取在点的缓冲区内的点、与点的缓冲区相交的线和面  
    @ApiOperation("根据点的缓冲区和项目信息筛选地理数据")  
    @RequiresPermissions  
    @PostMapping("queryPointBufferRelation")  
    public R<List<SpatialFeatureResponse>> getPointBufferRelation(@RequestBody QueryGeoRelationRequest request) throws IOException {  
  
        List<SpatialFeatureResponse> res = new ArrayList<>();  
  
        // 将 geoFeatureStr 转换为 Geometry 对象  
        // 将 "120 30_50" 分隔成点的字符串和缓冲区距离  
        String[] str_arr = request.getGeoFeatureStr().split("_");  
        Point point = createPoint(str_arr[0]);  
        double bufferDistance = Double.parseDouble(str_arr[1]);  
  
        List<SpatialFeature> spatialFeatures = spatialFeatureService.list();  
        for (SpatialFeature spatialFeature : spatialFeatures) {  
  
            // 获取 Geometry 对象  
            String geometry = spatialFeature.getGeometry();  
            // 获取 prId            String prId = spatialFeature.getPrId();  
  
            Map<String, Object> stringObjectMap = GeoUtils.convertToGeoJSON(geometry, spatialFeature.getGeometryType().name());  
            SpatialFeature.GeometryType geometryType = spatialFeature.getGeometryType();  
  
            switch (geometryType) {  
                case POINT:  
                    Point point2 = createPoint(geometry);  
                    if (GeoUtils.isPointInPointBuffer(point2, point, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case LINESTRING:  
                    LineString lineString = createLineString(geometry);  
                    if (GeoUtils.isLineIntersectPointBuffer(lineString, point, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
                case POLYGON:  
                    Polygon polygon = createPolygon(geometry);  
                    if (GeoUtils.isPolygonIntersectPointBuffer(polygon, point, bufferDistance)) {  
                        res.add(new SpatialFeatureResponse(prId, stringObjectMap));  
                        break;  
                    }  
                    break;  
            }  
        }  
  
        return R.ok(filterByProjectInfo(res, request));  
    }  
}
LICENSED UNDER CC BY-NC-SA 4.0
Comment