liusuyi
2026-04-22 04a270ff2b5919127524913ffc45c59c6c6d9201
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package com.ard.data.region.utils;
 
import com.ard.data.region.domain.Coordinate;
import org.springframework.util.CollectionUtils;
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
/**
 * 区域坐标工具类
 *
 * @author ruoyi
 */
public class RegionCoordinateUtils {
    /**
     * 根据坐标数量自动判断区域类型
     *
     * @param coordinates 坐标集合
     * @return POINT/LINE/POLYGON
     */
    public static String getRegionTypeByCoordinates(List<Coordinate> coordinates) {
        if (CollectionUtils.isEmpty(coordinates)) {
            throw new RuntimeException("坐标集合不能为空");
        }
 
        int size = coordinates.size();
        if (size == 1) {
            return "POINT";
        } else if (size == 2) {
            return "LINE";
        } else {
            return "POLYGON";
        }
    }
 
    /**
     * 将坐标集合转换为 WKT 格式(自动判断类型)
     */
    public static String toWkt(List<Coordinate> coordinates) {
        if (CollectionUtils.isEmpty(coordinates)) {
            throw new RuntimeException("坐标集合不能为空");
        }
 
        int size = coordinates.size();
 
        if (size == 1) {
            // 点
            Coordinate coord = coordinates.get(0);
            return String.format("POINT(%s)", coord.toLatLngString());
        } else if (size == 2) {
            // 线
            List<String> points = coordinates.stream()
                    .map(Coordinate::toLatLngString)
                    .collect(Collectors.toList());
            return String.format("LINESTRING(%s, %s)", points.get(0), points.get(1));
        } else {
            // 面(多边形)
            List<String> points = coordinates.stream()
                    .map(Coordinate::toLatLngString)
                    .collect(Collectors.toList());
            String allPoints = String.join(", ", points);
            String firstPoint = points.get(0);
            return String.format("POLYGON((%s, %s))", allPoints, firstPoint);
        }
    }
 
    /**
     * 将 WKT 字符串转换为坐标集合
     *
     * @param wkt 如:POLYGON((45.718839 126.670721, 45.708756 126.670151, ...))
     * @return 坐标集合
     */
    public static List<Coordinate> toCoordinates(String wkt) {
        List<Coordinate> coordinates = new ArrayList<>();
        if (wkt == null || wkt.isEmpty()) {
            return coordinates;
        }
        // 提取括号内的坐标字符串
        String coordStr = wkt;
        if (wkt.startsWith("POLYGON")) {
            coordStr = wkt.substring(wkt.indexOf("((") + 2, wkt.lastIndexOf("))"));
        } else if (wkt.startsWith("LINESTRING")) {
            coordStr = wkt.substring(wkt.indexOf("(") + 1, wkt.lastIndexOf(")"));
        } else if (wkt.startsWith("POINT")) {
            coordStr = wkt.substring(wkt.indexOf("(") + 1, wkt.lastIndexOf(")"));
        }
 
        // 分割坐标点
        String[] points = coordStr.split(",");
        for (String point : points) {
            String[] lngLat = point.trim().split(" ");
            if (lngLat.length >= 2) {
                Double lat = Double.parseDouble(lngLat[0]);
                Double lng = Double.parseDouble(lngLat[1]);
                coordinates.add(new Coordinate(lng, lat));
            }
        }
        return coordinates;
    }
 
    /**
     * 计算中心点
     */
    public static Coordinate calculateCenter(List<Coordinate> coordinates) {
        if (CollectionUtils.isEmpty(coordinates)) {
            return null;
        }
        double sumLng = 0;
        double sumLat = 0;
        for (Coordinate coord : coordinates) {
            sumLng += coord.getLng();
            sumLat += coord.getLat();
        }
        return new Coordinate(sumLng / coordinates.size(), sumLat / coordinates.size());
    }
 
    /**
     * 计算面积(仅面类型有效)
     */
    public static double calculateArea(List<Coordinate> coordinates) {
        if (coordinates == null || coordinates.size() < 3) {
            return 0;
        }
 
        double area = 0;
        int n = coordinates.size();
        for (int i = 0; i < n; i++) {
            Coordinate current = coordinates.get(i);
            Coordinate next = coordinates.get((i + 1) % n);
            area += current.getLng() * next.getLat() - next.getLng() * current.getLat();
        }
        area = Math.abs(area) / 2;
 
        double lat = coordinates.get(0).getLat();
        double meterPerDegree = 111000 * Math.cos(Math.toRadians(lat));
        return area * meterPerDegree * 111000;
    }
 
    /**
     * 验证坐标集合
     */
    public static void validateCoordinates(List<Coordinate> coordinates) {
        if (CollectionUtils.isEmpty(coordinates)) {
            throw new RuntimeException("坐标集合不能为空");
        }
 
        for (Coordinate coord : coordinates) {
            if (!coord.isValid()) {
                throw new RuntimeException("无效坐标");
            }
        }
    }
}