liujie
2026-01-14 f976a39b294f8c7dd60bcd2c661983c0dcc97fc6
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
package com.estate.expert.admin.component.map;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
 
/**
 * 地理位置翻译组件
 * 
 * <p>该组件提供地址到地理坐标的转换功能,基于高德地图API实现。
 * 主要用于将中文地址转换为精确的经纬度坐标,支持各种基于位置的服务。</p>
 * 
 * <p>主要功能:</p>
 * <ul>
 *   <li>地址解析:将文本地址转换为地理坐标</li>
 *   <li>坐标获取:提供精确的经纬度信息</li>
 *   <li>位置服务:支持地图定位和导航功能</li>
 * </ul>
 * 
 * <p>技术特点:</p>
 * <ul>
 *   <li>基于高德地图Web服务API</li>
 *   <li>支持中文地址解析</li>
 *   <li>提供高精度的地理坐标</li>
 *   <li>包含完整的错误处理机制</li>
 * </ul>
 * 
 * <p>使用场景:</p>
 * <ul>
 *   <li>工程项目位置定位</li>
 *   <li>设备安装位置记录</li>
 *   <li>安全检查地点标记</li>
 *   <li>地图可视化展示</li>
 * </ul>
 * 
 * @author daisy
 * @since 2023/10/29
 */
@Slf4j
@Component
public class GeoTranslateComponent {
 
    /**
     * 高德地图地理编码API地址
     * 
     * <p>高德地图提供的地理编码服务接口,用于将地址转换为地理坐标。</p>
     * 
     * <p>API说明:</p>
     * <ul>
     *   <li>服务类型:RESTful Web API</li>
     *   <li>请求方式:HTTP GET</li>
     *   <li>响应格式:JSON</li>
     *   <li>坐标系统:WGS84</li>
     * </ul>
     * 
     * <p>请求参数:</p>
     * <ul>
     *   <li>address:待解析的地址(必填)</li>
     *   <li>key:API密钥(必填)</li>
     * </ul>
     * 
     * <p>API文档:https://lbs.amap.com/api/webservice/guide/api/georegeo</p>
     */
    private static final String MAP_API_URL = "https://restapi.amap.com/v3/geocode/geo";
 
    /**
     * 高德地图API密钥
     * 
     * <p>用于访问高德地图服务的认证密钥,从配置文件中读取。</p>
     * 
     * <p>配置说明:</p>
     * <ul>
     *   <li>配置文件:application.yml</li>
     *   <li>配置项:map.api.key</li>
     *   <li>默认值:5ad97434d47b7cc74b4145d5509d28d2</li>
     * </ul>
     * 
     * <p>安全注意事项:</p>
     * <ul>
     *   <li>API密钥应妥善保管,避免泄露</li>
     *   <li>建议在生产环境中使用环境变量或加密配置</li>
     *   <li>定期检查和更新API密钥</li>
     * </ul>
     */
    @Value("${map.api.key}")
    private final String API_KEY = "5ad97434d47b7cc74b4145d5509d28d2";
 
    /**
     * HTTP客户端模板
     * 
     * <p>用于发送HTTP请求到高德地图API服务。
     * Spring RestTemplate提供了便捷的HTTP客户端功能。</p>
     */
    RestTemplate restTemplate = new RestTemplate();
 
    /**
     * 地址转坐标方法
     * 
     * <p>该方法将文本地址转换为地理坐标(经纬度),支持中文地址解析。
     * 通过调用高德地图地理编码API实现地址到坐标的转换。</p>
     * 
     * <p>处理流程:</p>
     * <ol>
     *   <li>构建API请求URL,包含地址和API密钥参数</li>
     *   <li>发送HTTP GET请求到高德地图API</li>
     *   <li>解析JSON响应数据</li>
     *   <li>提取第一个地理编码结果的坐标信息</li>
     *   <li>将坐标字符串解析为Double数组</li>
     *   <li>处理异常情况并返回null</li>
     * </ol>
     * 
     * <p>坐标格式:</p>
     * <ul>
     *   <li>返回数组:[经度, 纬度]</li>
     *   <li>经度范围:-180.0 到 180.0</li>
     *   <li>纬度范围:-90.0 到 90.0</li>
     *   <li>精度:通常精确到小数点后6位</li>
     * </ul>
     * 
     * <p>异常处理:</p>
     * <ul>
     *   <li>网络异常:记录错误日志,返回null</li>
     *   <li>API错误:记录错误信息,返回null</li>
     *   <li>解析异常:记录解析错误,返回null</li>
     *   <li>空结果:当API返回空结果时,返回null</li>
     * </ul>
     * 
     * @param address 待解析的地址文本,支持中文地址
     * @return Double[] 坐标数组,格式为[经度, 纬度],解析失败时返回null
     * 
     * @see GeoResponse API响应格式说明
     * 
     * <p>使用示例:</p>
     * <pre>
     * GeoTranslateComponent component = new GeoTranslateComponent();
     * Double[] coords = component.translate("北京市朝阳区阜通东大街6号");
     * if (coords != null) {
     *     double longitude = coords[0]; // 经度
     *     double latitude = coords[1];  // 纬度
     * }
     * </pre>
     */
    public Double[] translate(String address) {
        try {
            // 构建API请求URL
            String url = MAP_API_URL + "?address=" + address + "&key=" + API_KEY;
            log.debug("发送地理编码请求: {}", url);
            
            // 发送HTTP GET请求并获取响应
            var response = restTemplate.getForObject(url, GeoResponse.class);
            
            // 检查响应数据是否有效
            if (response != null && response.getGeocodes() != null && !response.getGeocodes().isEmpty()) {
                // 获取第一个地理编码结果的坐标字符串
                var location = response.getGeocodes().get(0).getLocation();
                log.debug("获取到坐标: {}", location);
                
                // 解析坐标字符串为Double数组
                var locationArray = location.split(",");
                if (locationArray.length >= 2) {
                    double longitude = Double.parseDouble(locationArray[0]);
                    double latitude = Double.parseDouble(locationArray[1]);
                    log.info("地址 '{}' 解析成功: 经度={}, 纬度={}", address, longitude, latitude);
                    return new Double[]{longitude, latitude};
                } else {
                    log.warn("坐标格式无效: {}", location);
                }
            } else {
                log.warn("地址 '{}' 未找到匹配的地理编码结果", address);
            }
 
        } catch (Exception e) {
            // 记录详细的错误信息
            log.error("地理编码API请求失败, 地址: {}, 错误: {}", address, e.getMessage(), e);
        }
        
        // 异常情况下返回null
        return null;
    }
 
    /**
     * 测试方法 - 地址转坐标示例
     * 
     * <p>该方法演示了如何使用地理编码组件进行地址到坐标的转换。
     * 适用于功能测试和开发调试。</p>
     * 
     * <p>测试地址:</p>
     * <ul>
     *   <li>示例地址:北京市朝阳区阜通东大街6号</li>
     *   <li>预期结果:返回该地址的精确经纬度坐标</li>
     * </ul>
     * 
     * <p>使用说明:</p>
     * <ul>
     *   <li>该方法主要用于功能测试和演示</li>
     *   <li>实际使用时需要根据具体需求调整地址参数</li>
     *   <li>建议在生产环境中移除或注释掉此方法</li>
     * </ul>
     */
    public static void main(String[] args) {
        // 创建地理编码组件实例
        GeoTranslateComponent component = new GeoTranslateComponent();
        
        // 测试地址转坐标功能
        var result = component.translate("北京市朝阳区阜通东大街6号");
        
        // 输出结果
        if (result != null) {
            System.out.println("地址解析成功:");
            System.out.println("经度: " + result[0]);
            System.out.println("纬度: " + result[1]);
        } else {
            System.out.println("地址解析失败");
        }
    }
 
}