|
|
@@ -0,0 +1,164 @@
|
|
|
+package com.usky.fire.service.util;
|
|
|
+
|
|
|
+import com.google.gson.*;
|
|
|
+
|
|
|
+import java.io.*;
|
|
|
+import java.net.*;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.nio.file.*;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ *
|
|
|
+ * @author fyc
|
|
|
+ * @email yuchuan.fu@chinausky.com
|
|
|
+ * @date 2025/10/30
|
|
|
+ */
|
|
|
+public class AddressToLonAndLatBaidu {
|
|
|
+ private static final String BATCH_URL = "https://api.map.baidu.com/geocoding/v3";
|
|
|
+ private final String ak;
|
|
|
+ private final Gson gson = new Gson();
|
|
|
+
|
|
|
+ public AddressToLonAndLatBaidu(String ak) {
|
|
|
+ this.ak = ak;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 批量地址转经纬度(百度API单次最多支持10条)
|
|
|
+ * @param batch 地址列表
|
|
|
+ * @return 地址->经纬度(格式:"经度,纬度")映射
|
|
|
+ * @throws IOException 网络或IO异常
|
|
|
+ */
|
|
|
+ public Map<String, String> geo(List<String> batch) throws IOException {
|
|
|
+ Map<String, String> result = new LinkedHashMap<>();
|
|
|
+ // 初始化所有地址为默认空值
|
|
|
+ for (String addr : batch) {
|
|
|
+ result.put(addr, "");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 百度API单次最多10条,拼接地址
|
|
|
+ String joined = String.join("|", batch);
|
|
|
+ String encodedAddress = URLEncoder.encode(joined, StandardCharsets.UTF_8.name());
|
|
|
+
|
|
|
+ // 构建请求URL(可添加&city=上海限定城市)
|
|
|
+ String encodedCity = URLEncoder.encode("上海", StandardCharsets.UTF_8.name());
|
|
|
+ String url = String.format(
|
|
|
+ "%s?ak=%s&address=%s&batch=true&output=json&city=%s",
|
|
|
+ BATCH_URL,
|
|
|
+ ak,
|
|
|
+ encodedAddress,
|
|
|
+ encodedCity
|
|
|
+ );
|
|
|
+
|
|
|
+ HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
|
|
+ conn.setRequestMethod("GET");
|
|
|
+ conn.setConnectTimeout(3000);
|
|
|
+ conn.setReadTimeout(3000);
|
|
|
+
|
|
|
+ // 读取响应内容(注意:需处理HTTP错误状态码,如403、404等)
|
|
|
+ String json;
|
|
|
+ int responseCode = conn.getResponseCode();
|
|
|
+ if (responseCode != HttpURLConnection.HTTP_OK) {
|
|
|
+ System.err.println("HTTP请求失败,状态码:" + responseCode + ",地址批量:" + batch);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ try (BufferedReader br = new BufferedReader(
|
|
|
+ new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
|
|
|
+ json = br.lines().collect(Collectors.joining());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析JSON响应
|
|
|
+ // 解析JSON响应(替换原result数组解析部分)
|
|
|
+ JsonObject root = JsonParser.parseString(json).getAsJsonObject();
|
|
|
+ int status = root.get("status").getAsInt();
|
|
|
+ if (status != 0) {
|
|
|
+ String msg = root.has("msg") ? root.get("msg").getAsString() : "无错误信息";
|
|
|
+ System.err.println("API错误:状态码=" + status + ",信息=" + msg + ",地址批量:" + batch);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键修复:判断result是数组还是对象
|
|
|
+ JsonElement resultElement = root.get("result");
|
|
|
+ JsonArray results;
|
|
|
+
|
|
|
+ if (resultElement.isJsonArray()) {
|
|
|
+ // 多条地址:result是数组
|
|
|
+ results = resultElement.getAsJsonArray();
|
|
|
+ } else if (resultElement.isJsonObject()) {
|
|
|
+ // 单条地址:result是对象,转为只有一个元素的数组
|
|
|
+ results = new JsonArray();
|
|
|
+ results.add(resultElement.getAsJsonObject());
|
|
|
+ } else {
|
|
|
+ // 既不是数组也不是对象(异常情况)
|
|
|
+ System.err.println("API返回result格式异常,地址批量:" + batch);
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 后续遍历results数组的逻辑不变
|
|
|
+ for (int i = 0; i < batch.size(); i++) {
|
|
|
+ String addr = batch.get(i);
|
|
|
+ if (i >= results.size()) {
|
|
|
+ result.put(addr, "");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ JsonElement element = results.get(i);
|
|
|
+ if (element == null || element.isJsonNull()) {
|
|
|
+ result.put(addr, "");
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ JsonObject resultObj = element.getAsJsonObject();
|
|
|
+ if (resultObj.has("location")) {
|
|
|
+ JsonObject location = resultObj.getAsJsonObject("location");
|
|
|
+ double lng = location.get("lng").getAsDouble();
|
|
|
+ double lat = location.get("lat").getAsDouble();
|
|
|
+ result.put(addr, lng + "," + lat);
|
|
|
+ } else {
|
|
|
+ result.put(addr, "");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String[] args) throws Exception {
|
|
|
+ // 替换为你的百度地图AK(需在百度地图开放平台申请)
|
|
|
+ String ak = "PHZ0A1F0JeUDW3oEu7xDHT0qjw92YZt7";
|
|
|
+ Path in = Paths.get("C:\\Users\\f\\Desktop\\input.csv");
|
|
|
+ Path out = Paths.get("C:\\Users\\f\\Desktop\\baidu_result.csv");
|
|
|
+
|
|
|
+ List<String> all = Files.readAllLines(in, StandardCharsets.UTF_8)
|
|
|
+ .stream()
|
|
|
+ .filter(l -> !l.trim().isEmpty())
|
|
|
+ .collect(Collectors.toList());
|
|
|
+
|
|
|
+ AddressToLonAndLatBaidu service = new AddressToLonAndLatBaidu(ak);
|
|
|
+
|
|
|
+ try (BufferedWriter w = Files.newBufferedWriter(out, StandardCharsets.UTF_8)) {
|
|
|
+ w.write("地址,经度,纬度\n");
|
|
|
+ // 百度API单次最多10条,所以步长设为10
|
|
|
+ for (int from = 0; from < all.size(); from += 3) {
|
|
|
+ int to = Math.min(from + 3, all.size());
|
|
|
+ if (from >= to) continue;
|
|
|
+
|
|
|
+ List<String> batch = all.subList(from, to);
|
|
|
+ Map<String, String> map = service.geo(batch);
|
|
|
+
|
|
|
+ for (String addr : batch) {
|
|
|
+ String loc = map.getOrDefault(addr, "");
|
|
|
+ if (loc.isEmpty()) {
|
|
|
+ w.write(addr + ",,\n");
|
|
|
+ } else {
|
|
|
+ String[] lonLat = loc.split(",");
|
|
|
+ w.write(addr + ',' + lonLat[0] + ',' + lonLat[1] + '\n');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 百度API有频率限制,添加延迟
|
|
|
+ Thread.sleep(1000);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("全部完成!结果已写入 " + out.toAbsolutePath());
|
|
|
+ }
|
|
|
+}
|