| | |
| | | package com.ruoyi.web.controller.system; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.util.*; |
| | | import java.text.SimpleDateFormat; |
| | | import java.text.ParseException; |
| | | import java.io.IOException; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.ruoyi.system.domain.*; |
| | | import com.ruoyi.system.service.*; |
| | | import com.ruoyi.common.config.TencentMapConfig; |
| | | import com.ruoyi.common.config.BaiduMapConfig; |
| | | import com.ruoyi.common.config.TiandituMapConfig; |
| | | import org.dom4j.DocumentHelper; |
| | | import org.dom4j.Element; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | |
| | | import com.ruoyi.common.enums.BusinessType; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.common.core.page.TableDataInfo; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | |
| | | |
| | | import javax.annotation.Resource; |
| | | |
| | | |
| | | /** |
| | | * 车辆GPS坐标Controller |
| | |
| | | |
| | | @Autowired |
| | | private ICmsGpsCollectService cmsGpsCollectService; |
| | | |
| | | @Autowired |
| | | private TencentMapConfig tencentMapConfig; |
| | | |
| | | @Autowired |
| | | private BaiduMapConfig baiduMapConfig; |
| | | |
| | | @Autowired |
| | | private TiandituMapConfig tiandituMapConfig; |
| | | |
| | | |
| | | @Resource(name = "tiandituMapService") |
| | | private IMapService tiandituMapService; |
| | | |
| | | @Resource(name = "baiduMapService") |
| | | private IMapService baiduMapService; |
| | | |
| | | /** |
| | | * 查询车辆GPS坐标列表 |
| | |
| | | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); |
| | | String beginTime= sdf.format(dispatchOrd.getDispatchOrdStartDate()); |
| | | String endTime= sdf.format(new Date()); |
| | | |
| | | logger.info("查询车辆轨迹:车辆号:{}, 开始时间:{}, 结束时间:{}", vehicleNo, beginTime, endTime); |
| | | return this.getAnonymousTracks(vehicleNo,beginTime,endTime); |
| | | // |
| | | // vehicleGps.setVehicleNo(tbVehicleOrder.getVehicle()); |
| | |
| | | // 通过车牌号获取设备ID |
| | | VehicleInfo vehicleInfo = vehicleInfoService.selectVehicleInfoByPlateNumber(vehicleNo); |
| | | if (vehicleInfo == null) { |
| | | throw new Error("未找到该车辆对应的GPS设备"); |
| | | |
| | | // throw new Error("未找到该车辆对应的GPS设备"); |
| | | return getDataTable(new ArrayList<>()); |
| | | } |
| | | |
| | | // 处理开始时间 |
| | | beginTime = beginTime.replace("T", " ").replace(" ","%20"); |
| | | beginTime = beginTime.replace("T", " "); |
| | | if (beginTime.split(":").length == 2) { // 只有小时和分钟 |
| | | beginTime += ":00"; |
| | | } |
| | | |
| | | // 处理结束时间 |
| | | endTime = endTime.replace("T", " ").replace(" ","%20"); |
| | | // endTime = endTime.replace("T", " ").replace(" ","%20"); |
| | | endTime = endTime.replace("T", " "); |
| | | if (endTime.split(":").length == 2) { // 只有小时和分钟 |
| | | endTime += ":59"; |
| | | } |
| | |
| | | request.setEndtime(endTime); |
| | | request.setTimezone(8); // 中国时区 |
| | | |
| | | logger.info("查询车辆轨迹:车辆号:{}, 设备ID:{}, 开始时间:{}, 结束时间:{}", vehicleNo, vehicleInfo.getDeviceId(), beginTime, endTime); |
| | | // 查询轨迹 |
| | | GpsTrackQueryResponse response = gpsCollectService.queryTracks(request); |
| | | if (response.getStatus() != 0) { |
| | | throw new Error("查询轨迹失败:" + response.getCause()); |
| | | logger.error("查询轨迹失败,状态码:{}, 错误信息:{}", response.getStatus(), response.getCause()); |
| | | throw new Error("查询轨迹失败:" + (response.getCause() != null ? response.getCause() : "未知错误")); |
| | | } |
| | | |
| | | // 转换GPS51轨迹点为统一格式 |
| | |
| | | throw new Error("查询车辆轨迹失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 腾讯地图地址搜索接口代理 |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/address/search") |
| | | public AjaxResult searchAddress(String keyword, String region) { |
| | | try { |
| | | // 构建腾讯地图搜索API URL |
| | | String url = "https://apis.map.qq.com/ws/place/v1/search"; |
| | | String params = "keyword=" + URLEncoder.encode(keyword, StandardCharsets.UTF_8.toString()) + |
| | | "&boundary=region(" + (region != null ? region : "广州") + ")" + |
| | | "&key=" + tencentMapConfig.getKey(); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("地址搜索失败", e); |
| | | return AjaxResult.error("地址搜索失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 腾讯地图逆地址解析接口代理 |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/address/geocoder") |
| | | public AjaxResult reverseGeocoder(Double lat, Double lng) { |
| | | try { |
| | | // 检查参数 |
| | | logger.info("逆地址解析请求参数: lat={}, lng={}", lat, lng); |
| | | |
| | | if (lat == null || lng == null) { |
| | | logger.warn("参数不完整,缺少经纬度坐标: lat={}, lng={}", lat, lng); |
| | | return AjaxResult.error("参数不完整,缺少经纬度坐标"); |
| | | } |
| | | |
| | | // 检查参数有效性 |
| | | if (Double.isNaN(lat) || Double.isNaN(lng) || |
| | | Double.isInfinite(lat) || Double.isInfinite(lng)) { |
| | | logger.warn("参数无效,经纬度坐标包含非法值: lat={}, lng={}", lat, lng); |
| | | return AjaxResult.error("参数无效,经纬度坐标格式错误"); |
| | | } |
| | | |
| | | // 构建腾讯地图逆地址解析API URL |
| | | String url = "https://apis.map.qq.com/ws/geocoder/v1/"; |
| | | String params = "location=" + lat + "," + lng + |
| | | "&key=" + tencentMapConfig.getKey() + |
| | | "&get_poi=1"; |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("逆地址解析失败: lat={}, lng={}", lat, lng, e); |
| | | return AjaxResult.error("逆地址解析失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 腾讯地图路线规划接口代理(计算两点间距离) |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/route/distance") |
| | | public AjaxResult calculateDistance(Double fromLat, Double fromLng, Double toLat, Double toLng) { |
| | | try { |
| | | // 检查参数 |
| | | if (fromLat == null || fromLng == null || toLat == null || toLng == null) { |
| | | return AjaxResult.error("参数不完整,缺少起点或终点坐标"); |
| | | } |
| | | |
| | | // 构建腾讯地图路线规划API URL |
| | | String url = "https://apis.map.qq.com/ws/distance/v1/"; |
| | | String params = "mode=driving" + |
| | | "&from=" + fromLat + "," + fromLng + |
| | | "&to=" + toLat + "," + toLng + |
| | | "&key=" + tencentMapConfig.getKey(); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("计算成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("距离计算失败", e); |
| | | return AjaxResult.error("距离计算失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 百度地图逆地址解析接口代理 |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/baidu/reverseGeocoding") |
| | | public AjaxResult baiduResolveAddress(Double lng, Double lat) { |
| | | try { |
| | | // 检查参数 |
| | | if (lat == null || lng == null) { |
| | | return AjaxResult.error("参数不完整,缺少经纬度坐标"); |
| | | } |
| | | |
| | | String response= baiduMapService.reverseGeocoding(lng, lat); |
| | | // 发送HTTP请求 |
| | | Map<String,String> objResult=new HashMap<>(); |
| | | objResult.put("address",response); |
| | | return AjaxResult.success("查询成功", objResult); |
| | | }catch (Exception e){ |
| | | logger.error("百度逆地址解析失败: lat={}, lng={}", lat, lng, e); |
| | | return AjaxResult.error("百度逆地址解析失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | /** |
| | | * 百度地图地理编码接口代理(地址转坐标) |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/baidu/geocoding") |
| | | public AjaxResult baiduGeocoding(String address, String city) { |
| | | try { |
| | | // 检查参数 |
| | | if (address == null || address.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少地址信息"); |
| | | } |
| | | Map<String,Double> objResult=baiduMapService.geocoding(address, city); |
| | | |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功", objResult); |
| | | } catch (Exception e) { |
| | | logger.error("百度地图地理编码失败", e); |
| | | return AjaxResult.error("地理编码失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 百度地图路线规划接口代理(计算两个坐标之间的驾车距离) |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/baidu/route/driving") |
| | | public AjaxResult baiduRouteDriving(String origin, String destination) { |
| | | try { |
| | | // 检查参数 |
| | | if (origin == null || origin.trim().isEmpty() || |
| | | destination == null || destination.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少起点或终点坐标"); |
| | | } |
| | | |
| | | // 验证坐标格式(纬度,经度) |
| | | String[] originParts = origin.split(","); |
| | | String[] destParts = destination.split(","); |
| | | if (originParts.length != 2 || destParts.length != 2) { |
| | | return AjaxResult.error("坐标格式错误,应为:纬度,经度"); |
| | | } |
| | | |
| | | // 构建百度地图路线规划API URL |
| | | String url = "https://api.map.baidu.com/directionlite/v1/driving"; |
| | | String params = "origin=" + origin + |
| | | "&destination=" + destination + |
| | | "&ak=" + baiduMapConfig.getAk(); |
| | | |
| | | logger.info("百度地图路线规划请求: origin={}, destination={}", origin, destination); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("计算成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("百度地图路线规划失败", e); |
| | | return AjaxResult.error("路线规划失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 百度地图计算两个地址之间的距离(组合接口:地址转坐标 + 路线规划) |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/baidu/distance/byAddress") |
| | | public AjaxResult baiduDistanceByAddress(String fromAddress, String fromCity, |
| | | String toAddress, String toCity) { |
| | | try { |
| | | // 检查参数 |
| | | if (fromAddress == null || fromAddress.trim().isEmpty() || |
| | | toAddress == null || toAddress.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少起点或终点地址"); |
| | | } |
| | | |
| | | logger.info("开始计算地址距离: fromAddress={}, fromCity={}, toAddress={}, toCity={}", |
| | | fromAddress, fromCity, toAddress, toCity); |
| | | |
| | | // 第一步:起点地址转坐标 |
| | | String geocodingUrl1 = "https://api.map.baidu.com/geocoding/v3/"; |
| | | String geocodingParams1 = "address=" + URLEncoder.encode(fromAddress, StandardCharsets.UTF_8.toString()) + |
| | | (fromCity != null && !fromCity.trim().isEmpty() ? |
| | | "&city=" + URLEncoder.encode(fromCity, StandardCharsets.UTF_8.toString()) : "") + |
| | | "&output=json" + |
| | | "&ak=" + baiduMapConfig.getAk(); |
| | | |
| | | String geocodingResponse1 = HttpUtils.sendGet(geocodingUrl1, geocodingParams1); |
| | | logger.info("起点地理编码响应: {}", geocodingResponse1); |
| | | |
| | | // 解析起点坐标 |
| | | com.alibaba.fastjson2.JSONObject geocodingJson1 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse1); |
| | | if (geocodingJson1.getInteger("status") != 0) { |
| | | logger.error("起点地理编码失败: {}", geocodingResponse1); |
| | | return AjaxResult.error("起点地址解析失败"); |
| | | } |
| | | com.alibaba.fastjson2.JSONObject location1 = geocodingJson1.getJSONObject("result").getJSONObject("location"); |
| | | double fromLat = location1.getDouble("lat"); |
| | | double fromLng = location1.getDouble("lng"); |
| | | logger.info("起点坐标: lat={}, lng={}", fromLat, fromLng); |
| | | |
| | | // 第二步:终点地址转坐标 |
| | | String geocodingUrl2 = "https://api.map.baidu.com/geocoding/v3/"; |
| | | String geocodingParams2 = "address=" + URLEncoder.encode(toAddress, StandardCharsets.UTF_8.toString()) + |
| | | (toCity != null && !toCity.trim().isEmpty() ? |
| | | "&city=" + URLEncoder.encode(toCity, StandardCharsets.UTF_8.toString()) : "") + |
| | | "&output=json" + |
| | | "&ak=" + baiduMapConfig.getAk(); |
| | | |
| | | String geocodingResponse2 = HttpUtils.sendGet(geocodingUrl2, geocodingParams2); |
| | | logger.info("终点地理编码响应: {}", geocodingResponse2); |
| | | |
| | | // 解析终点坐标 |
| | | com.alibaba.fastjson2.JSONObject geocodingJson2 = com.alibaba.fastjson2.JSONObject.parseObject(geocodingResponse2); |
| | | if (geocodingJson2.getInteger("status") != 0) { |
| | | logger.error("终点地理编码失败: {}", geocodingResponse2); |
| | | return AjaxResult.error("终点地址解析失败"); |
| | | } |
| | | com.alibaba.fastjson2.JSONObject location2 = geocodingJson2.getJSONObject("result").getJSONObject("location"); |
| | | double toLat = location2.getDouble("lat"); |
| | | double toLng = location2.getDouble("lng"); |
| | | logger.info("终点坐标: lat={}, lng={}", toLat, toLng); |
| | | |
| | | // 第三步:调用路线规划接口计算距离 |
| | | String routeUrl = "https://api.map.baidu.com/directionlite/v1/driving"; |
| | | String origin = fromLat + "," + fromLng; |
| | | String destination = toLat + "," + toLng; |
| | | String routeParams = "origin=" + origin + |
| | | "&destination=" + destination + |
| | | "&ak=" + baiduMapConfig.getAk(); |
| | | |
| | | logger.info("路线规划请求: origin={}, destination={}", origin, destination); |
| | | String routeResponse = HttpUtils.sendGet(routeUrl, routeParams); |
| | | logger.info("路线规划响应: {}", routeResponse); |
| | | |
| | | // 解析距离结果 |
| | | com.alibaba.fastjson2.JSONObject routeJson = com.alibaba.fastjson2.JSONObject.parseObject(routeResponse); |
| | | if (routeJson.getInteger("status") != 0) { |
| | | logger.error("路线规划失败: {}", routeResponse); |
| | | return AjaxResult.error("路线规划失败"); |
| | | } |
| | | |
| | | // 提取距离信息(单位:米) |
| | | com.alibaba.fastjson2.JSONObject result = routeJson.getJSONObject("result"); |
| | | com.alibaba.fastjson2.JSONArray routes = result.getJSONArray("routes"); |
| | | if (routes == null || routes.isEmpty()) { |
| | | logger.error("未找到路线信息"); |
| | | return AjaxResult.error("未找到路线信息"); |
| | | } |
| | | |
| | | com.alibaba.fastjson2.JSONObject route = routes.getJSONObject(0); |
| | | int distance = route.getInteger("distance"); // 距离,单位:米 |
| | | int duration = route.getInteger("duration"); // 时长,单位:秒 |
| | | |
| | | logger.info("计算成功: 距离={}米, 时长={}秒", distance, duration); |
| | | |
| | | // 构建返回结果 |
| | | Map<String, Object> resultMap = new HashMap<>(); |
| | | resultMap.put("distance", distance); // 距离(米) |
| | | resultMap.put("duration", duration); // 时长(秒) |
| | | resultMap.put("distanceKm", String.format("%.1f", distance / 1000.0)); // 距离(公里) |
| | | resultMap.put("durationMin", duration / 60); // 时长(分钟) |
| | | |
| | | // 起点坐标 |
| | | Map<String, Object> fromLocation = new HashMap<>(); |
| | | fromLocation.put("lat", fromLat); |
| | | fromLocation.put("lng", fromLng); |
| | | resultMap.put("fromLocation", fromLocation); |
| | | |
| | | // 终点坐标 |
| | | Map<String, Object> toLocation = new HashMap<>(); |
| | | toLocation.put("lat", toLat); |
| | | toLocation.put("lng", toLng); |
| | | resultMap.put("toLocation", toLocation); |
| | | |
| | | return AjaxResult.success("计算成功", resultMap); |
| | | } catch (Exception e) { |
| | | logger.error("计算地址距离失败", e); |
| | | return AjaxResult.error("计算距离失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 百度地图地址搜索提示API(输入联想) |
| | | * Place Suggestion API - 用于地址输入时的智能提示 |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/baidu/place/suggestion") |
| | | public AjaxResult baiduPlaceSuggestion(String query, String region) { |
| | | try { |
| | | // 检查参数 |
| | | if (query == null || query.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少搜索关键词"); |
| | | } |
| | | |
| | | // 构建百度地图 Place Suggestion API URL |
| | | String url = "https://api.map.baidu.com/place/v2/suggestion"; |
| | | String params = "query=" + URLEncoder.encode(query, StandardCharsets.UTF_8.toString()) + |
| | | (region != null && !region.trim().isEmpty() ? |
| | | "®ion=" + URLEncoder.encode(region, StandardCharsets.UTF_8.toString()) : "") + |
| | | "&output=json" + |
| | | "&ak=" + baiduMapConfig.getAk(); |
| | | |
| | | logger.info("百度地图地址搜索提示请求: query={}, region={}", query, region); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | logger.debug("百度地图地址搜索提示响应: {}", response); |
| | | |
| | | // 解析响应 |
| | | com.alibaba.fastjson2.JSONObject jsonResponse = com.alibaba.fastjson2.JSONObject.parseObject(response); |
| | | if (jsonResponse.getInteger("status") != 0) { |
| | | logger.error("地址搜索提示失败: {}", response); |
| | | return AjaxResult.error("地址搜索失败"); |
| | | } |
| | | |
| | | // 提取提示列表 |
| | | com.alibaba.fastjson2.JSONArray results = jsonResponse.getJSONArray("result"); |
| | | if (results == null || results.isEmpty()) { |
| | | logger.info("未找到匹配的地址"); |
| | | return AjaxResult.success("查询成功", new ArrayList<>()); |
| | | } |
| | | |
| | | // 构建返回结果 |
| | | List<Map<String, Object>> suggestions = new ArrayList<>(); |
| | | for (int i = 0; i < results.size(); i++) { |
| | | com.alibaba.fastjson2.JSONObject item = results.getJSONObject(i); |
| | | |
| | | Map<String, Object> suggestion = new HashMap<>(); |
| | | suggestion.put("name", item.getString("name")); // 名称 |
| | | suggestion.put("address", item.getString("address")); // 地址 |
| | | suggestion.put("province", item.getString("province")); // 省 |
| | | suggestion.put("city", item.getString("city")); // 市 |
| | | suggestion.put("district", item.getString("district")); // 区 |
| | | suggestion.put("uid", item.getString("uid")); // 地点UID |
| | | |
| | | // 经纬度信息 |
| | | com.alibaba.fastjson2.JSONObject location = item.getJSONObject("location"); |
| | | if (location != null) { |
| | | Map<String, Object> locationMap = new HashMap<>(); |
| | | locationMap.put("lat", location.getDouble("lat")); |
| | | locationMap.put("lng", location.getDouble("lng")); |
| | | suggestion.put("location", locationMap); |
| | | } |
| | | |
| | | suggestions.add(suggestion); |
| | | } |
| | | |
| | | logger.info("地址搜索提示成功: 找到{} 条结果", suggestions.size()); |
| | | return AjaxResult.success("查询成功", suggestions); |
| | | } catch (Exception e) { |
| | | logger.error("地址搜索提示失败", e); |
| | | return AjaxResult.error("地址搜索失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | // ==================== 天地图接口 ==================== |
| | | |
| | | /** |
| | | * 天地图地理编码接口代理(地址转坐标) |
| | | * 文档:https://lbs.tianditu.gov.cn/server/geocoding.html |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/geocoding") |
| | | public AjaxResult tiandituGeocoding(String address) { |
| | | try { |
| | | // 检查参数 |
| | | if (address == null || address.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少地址信息"); |
| | | } |
| | | Map<String,Double> resultMap =tiandituMapService.geocoding(address,""); |
| | | |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功", resultMap); |
| | | } catch (Exception e) { |
| | | logger.error("天地图地理编码失败", e); |
| | | return AjaxResult.error("地理编码失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 天地图逆地理编码接口代理(坐标转地址) |
| | | * 文档:https://lbs.tianditu.gov.cn/server/geocoding.html |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/reverseGeocoding") |
| | | public AjaxResult tiandituReverseGeocoding(Double lon, Double lat) { |
| | | try { |
| | | // 检查参数 |
| | | if (lat == null || lon == null) { |
| | | return AjaxResult.error("参数不完整,缺少经纬度坐标"); |
| | | } |
| | | |
| | | // 检查参数有效性 |
| | | if (Double.isNaN(lat) || Double.isNaN(lon) || |
| | | Double.isInfinite(lat) || Double.isInfinite(lon)) { |
| | | return AjaxResult.error("参数无效,经纬度坐标格式错误"); |
| | | } |
| | | String address=this.tiandituMapService.reverseGeocoding(lon, lat); |
| | | |
| | | |
| | | Map<String,String> resultMap = new HashMap<>(); |
| | | resultMap.put("address",address); |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功",resultMap); |
| | | } catch (Exception e) { |
| | | logger.error("天地图逆地理编码失败: lon={}, lat={}", lon, lat, e); |
| | | return AjaxResult.error("逆地理编码失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 天地图地点搜索接口代理(POI搜索) |
| | | * 文档:https://lbs.tianditu.gov.cn/server/search.html |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/place/search") |
| | | public AjaxResult tiandituPlaceSearch(String keyWord, String queryType, String level, |
| | | String mapBound, Integer start, Integer count) { |
| | | try { |
| | | // 检查参数 |
| | | if (keyWord == null || keyWord.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少搜索关键词"); |
| | | } |
| | | |
| | | // 设置默认值 |
| | | if (queryType == null || queryType.trim().isEmpty()) { |
| | | queryType = "1"; // 1-普通搜索,7-周边搜索 |
| | | } |
| | | if (start == null) { |
| | | start = 0; |
| | | } |
| | | if (count == null) { |
| | | count = 10; |
| | | } |
| | | |
| | | // 构建天地图POI搜索API URL |
| | | String url = "http://api.tianditu.gov.cn/search"; |
| | | StringBuilder paramsBuilder = new StringBuilder(); |
| | | paramsBuilder.append("postStr={\"keyWord\":\"").append(keyWord).append("\""); |
| | | paramsBuilder.append(",\"queryType\":\"").append(queryType).append("\""); |
| | | if (level != null && !level.trim().isEmpty()) { |
| | | paramsBuilder.append(",\"level\":\"").append(level).append("\""); |
| | | } |
| | | if (mapBound != null && !mapBound.trim().isEmpty()) { |
| | | paramsBuilder.append(",\"mapBound\":\"").append(mapBound).append("\""); |
| | | } |
| | | paramsBuilder.append(",\"start\":\"").append(start).append("\""); |
| | | paramsBuilder.append(",\"count\":\"").append(count).append("\""); |
| | | paramsBuilder.append("}" ); |
| | | paramsBuilder.append("&type=query"); |
| | | paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk()); |
| | | |
| | | String params = paramsBuilder.toString(); |
| | | |
| | | logger.info("天地图POI搜索请求: keyWord={}, queryType={}", keyWord, queryType); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("查询成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("天地图POI搜索失败", e); |
| | | return AjaxResult.error("POI搜索失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 天地图路线规划接口代理(驾车路径规划) |
| | | * 文档:https://lbs.tianditu.gov.cn/server/drive.html |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/route/driving") |
| | | public AjaxResult tiandituRouteDriving(String orig, String dest, String mid, String style) { |
| | | try { |
| | | // 检查参数 |
| | | if (orig == null || orig.trim().isEmpty() || |
| | | dest == null || dest.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少起点或终点坐标"); |
| | | } |
| | | |
| | | // 验证坐标格式(经度,纬度) |
| | | String[] origParts = orig.split(","); |
| | | String[] destParts = dest.split(","); |
| | | if (origParts.length != 2 || destParts.length != 2) { |
| | | return AjaxResult.error("坐标格式错误,应为:经度,纬度"); |
| | | } |
| | | |
| | | // 设置默认值 |
| | | if (style == null || style.trim().isEmpty()) { |
| | | style = "0"; // 0-推荐,1-避开高速 |
| | | } |
| | | |
| | | // 构建天地图驾车路径规划API URL |
| | | String url = "http://api.tianditu.gov.cn/drive"; |
| | | StringBuilder paramsBuilder = new StringBuilder(); |
| | | paramsBuilder.append("postStr={\"orig\":\"").append(orig).append("\""); |
| | | paramsBuilder.append(",\"dest\":\"").append(dest).append("\""); |
| | | if (mid != null && !mid.trim().isEmpty()) { |
| | | paramsBuilder.append(",\"mid\":\"").append(mid).append("\""); |
| | | } |
| | | paramsBuilder.append(",\"style\":\"").append(style).append("\""); |
| | | paramsBuilder.append("}" ); |
| | | paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk()); |
| | | |
| | | String params = paramsBuilder.toString(); |
| | | |
| | | logger.info("天地图驾车路径规划请求: orig={}, dest={}", orig, dest); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | |
| | | // 返回结果 |
| | | return AjaxResult.success("计算成功", response); |
| | | } catch (Exception e) { |
| | | logger.error("天地图驾车路径规划失败", e); |
| | | return AjaxResult.error("路径规划失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 天地图计算两个地址之间的距离(组合接口:地址转坐标 + 路径规划) |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/distance/byAddress") |
| | | public AjaxResult tiandituDistanceByAddress(String fromAddress, String toAddress) { |
| | | try { |
| | | // 检查参数 |
| | | if (fromAddress == null || fromAddress.trim().isEmpty() || |
| | | toAddress == null || toAddress.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少起点或终点地址"); |
| | | } |
| | | |
| | | Map<String,Double> fromLngLat= tiandituMapService.geocoding(fromAddress, null); |
| | | |
| | | double fromLng = fromLngLat.get("lng"); |
| | | double fromLat = fromLngLat.get("lat"); |
| | | logger.info("起点坐标: lon={}, lat={}", fromLng, fromLat); |
| | | |
| | | Map<String,Double> toLngLat = tiandituMapService.geocoding(toAddress, null); |
| | | double toLon = toLngLat.get("lng"); |
| | | double toLat = toLngLat.get("lat"); |
| | | logger.info("终点坐标: lon={}, lat={}", toLon, toLat); |
| | | |
| | | // 第三步:调用路径规划接口计算距离 |
| | | String routeUrl = "http://api.tianditu.gov.cn/drive"; |
| | | String orig = fromLng + "," + fromLat; |
| | | String dest = toLon + "," + toLat; |
| | | String routeParams = "postStr={\"orig\":\"" + orig + "\",\"dest\":\"" + dest + "\",\"style\":\"0\"}" + |
| | | "&tk=" + tiandituMapConfig.getTk(); |
| | | |
| | | logger.info("路径规划请求: orig={}, dest={}", orig, dest); |
| | | String routeResponse = HttpUtils.sendGet(routeUrl, routeParams); |
| | | logger.info("路径规划响应: {}", routeResponse); |
| | | //<?xml version="1.0" encoding="UTF-8" standalone="no"?> |
| | | //<result dest="113.3472,23.16957" mid="" orig="113.373308,23.136699"> |
| | | // |
| | | // <parameters> |
| | | // |
| | | // <orig>113.373308,23.136699</orig> |
| | | // |
| | | // <dest>113.3472,23.16957</dest> |
| | | // |
| | | // <mid/> |
| | | // |
| | | // <key/> |
| | | // |
| | | // <width>600</width> |
| | | // |
| | | // <height>400</height> |
| | | // |
| | | // <style>0</style> |
| | | // |
| | | // <version/> |
| | | // |
| | | // <sort/> |
| | | // |
| | | // </parameters> |
| | | // |
| | | // <routes count="9" time="0.0"> |
| | | // |
| | | // <item id="0"> |
| | | // |
| | | // <strguide>从棠安路向西出发,沿棠安路走100米并左转,</strguide> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <streetName>棠安路</streetName> |
| | | // |
| | | // <nextStreetName/> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37323,23.13677</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="1"> |
| | | // |
| | | // <strguide>走100米并向广园快速/省道303/华南快速/省道81/环城高速/华观路/岑村/省道3/广深沿江方向进入科韵中路,</strguide> |
| | | // |
| | | // <signage>广园快速/省道303/华南快速/省道81/环城高速/华观路/岑村/省道3/广深沿江</signage> |
| | | // |
| | | // <streetName/> |
| | | // |
| | | // <nextStreetName>科韵中路</nextStreetName> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37185,23.13699</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="2"> |
| | | // |
| | | // <strguide>沿科韵中路走0.6公里并向广园快速(西行)/省道303/华南快速方向进入广园快速路,</strguide> |
| | | // |
| | | // <signage>广园快速(西行)/省道303/华南快速</signage> |
| | | // |
| | | // <streetName>科韵中路</streetName> |
| | | // |
| | | // <nextStreetName>广园快速路</nextStreetName> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37107,23.13648</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="3"> |
| | | // |
| | | // <strguide>沿广园快速路走1.9公里并从华南快速/广州南站/广州北站/环城高速(北环)/广州长隆/帽峰山/华南快速(北行)/国道4/广州环城高速/华南快速(西)/京港澳高速/广州机场高速/广河高速/韶关/省道81/省道8方向匝道进入华南立交桥,</strguide> |
| | | // |
| | | // <signage>华南快速/广州南站/广州北站/环城高速(北环)/广州长隆/帽峰山/华南快速(北行)/国道4/广州环城高速/华南快速(西)/京港澳高速/广州机场高速/广河高速/韶关/省道81/省道8方向匝道</signage> |
| | | // |
| | | // <streetName>广园快速路</streetName> |
| | | // |
| | | // <nextStreetName>华南立交桥</nextStreetName> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37139,23.14206</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="4"> |
| | | // |
| | | // <strguide>沿华南立交桥盘桥向北走0.6公里并直行到省道4/华南快速,</strguide> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <streetName>华南立交桥</streetName> |
| | | // |
| | | // <nextStreetName>省道4/华南快速</nextStreetName> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.35685,23.14669</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="5"> |
| | | // |
| | | // <strguide>沿省道4/华南快速走0.6公里并从天河客运站/省道15/元岗/佛山/深圳/广州环城高速/沈海高速支线/华南快速管理中心/岑村立交/省道81方向匝道进入岑村立交,</strguide> |
| | | // |
| | | // <signage>天河客运站/省道15/元岗/佛山/深圳/广州环城高速/沈海高速支线/华南快速管理中心/岑村立交/省道81方向匝道</signage> |
| | | // |
| | | // <streetName>省道4/华南快速</streetName> |
| | | // |
| | | // <nextStreetName>岑村立交</nextStreetName> |
| | | // |
| | | // <tollStatus>1</tollStatus> |
| | | // |
| | | // <turnlatlon>113.35518,23.15157</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="6"> |
| | | // |
| | | // <strguide>沿岑村立交盘桥向西北走0.5公里并从天河客运站/元岗/广汕路/岑村路/市交警支队出口驶离并进入长兴路,</strguide> |
| | | // |
| | | // <signage>天河客运站/元岗/广汕路/岑村路/市交警支队出口</signage> |
| | | // |
| | | // <streetName>岑村立交</streetName> |
| | | // |
| | | // <nextStreetName>长兴路</nextStreetName> |
| | | // |
| | | // <tollStatus>1</tollStatus> |
| | | // |
| | | // <turnlatlon>113.35731,23.15636</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="7"> |
| | | // |
| | | // <strguide>沿长兴路走2.8公里并右转到长湴中路,</strguide> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <streetName>长兴路</streetName> |
| | | // |
| | | // <nextStreetName>长湴中路</nextStreetName> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.35829,23.16086</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="8"> |
| | | // |
| | | // <strguide>沿长湴中路走100米到达目的地。</strguide> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <streetName>长湴中路</streetName> |
| | | // |
| | | // <nextStreetName/> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.34714,23.17047</turnlatlon> |
| | | // |
| | | // </item> |
| | | // |
| | | // </routes> |
| | | // |
| | | // <simple> |
| | | // |
| | | // <item id="0"> |
| | | // |
| | | // <strguide>从棠安路出发,进入科韵中路。</strguide> |
| | | // |
| | | // <streetNames>棠安路</streetNames> |
| | | // |
| | | // <lastStreetName/> |
| | | // |
| | | // <linkStreetName>科韵中路</linkStreetName> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37323,23.13677</turnlatlon> |
| | | // |
| | | // <streetLatLon>113.37323,23.13677;113.37291,23.13683;113.37272,23.13687;113.37272,23.13687;113.37265,23.13688;113.37265,23.13688;113.37227,23.13696;113.37227,23.13696;113.37209,23.13699;113.37201,23.13696;113.37201,23.13696;113.37185,23.13699;</streetLatLon> |
| | | // |
| | | // <streetDistance>144.0</streetDistance> |
| | | // |
| | | // <segmentNumber>0</segmentNumber> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="1"> |
| | | // |
| | | // <strguide>沿科韵中路行驶,向广园快速(西行)/省道303/华南快速方向,进入省道4/华南快速。</strguide> |
| | | // |
| | | // <streetNames/> |
| | | // |
| | | // <lastStreetName>科韵中路</lastStreetName> |
| | | // |
| | | // <linkStreetName>省道4/华南快速</linkStreetName> |
| | | // |
| | | // <signage>广园快速(西行)/省道303/华南快速</signage> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.37107,23.13648</turnlatlon> |
| | | // |
| | | // <streetLatLon>113.37185,23.13699;113.37174,23.13683;113.3714,23.13649;113.37134,23.13645;113.37126,23.13643;113.37116,23.13644;113.37107,23.13648;113.37107,23.13648;113.37103,23.13657;113.37091,23.13683;113.37091,23.13683;113.37086,23.13693;113.3708,23.13705;113.3708,23.13705;113.37075,23.13717;113.37075,23.13717;113.37069,23.1373;113.37069,23.1373;113.37067,23.13735;113.37067,23.13735;113.3706,23.13755;113.37057,23.13765;113.37055,23.13779;113.37054,23.13794;113.37054,23.13804;113.37054,23.13814;113.37054,23.13831;113.37055,23.13844;113.37055,23.13844;113.37056,23.13855;113.37058,23.13865;113.3706,23.13878;113.37062,23.13884;113.37068,23.13911;113.37073,23.13935;113.37075,23.13944;113.37079,23.1396;113.3708,23.13964;113.37083,23.13975;113.37083,23.13975;113.37084,23.1398;113.37084,23.1398;113.37087,23.13998;113.3709,23.1401;113.37097,23.14039;113.371,23.14051;113.371,23.14051;113.3711,23.1409;113.37114,23.14105;113.37116,23.14115;113.3712,23.14127;113.37121,23.14135;113.37124,23.14146;113.37125,23.14153;113.37126,23.14155;113.37129,23.14167;113.37129,23.14167;113.37134,23.14187;113.37136,23.14194;113.37139,23.14206;</streetLatLon> |
| | | // |
| | | // <streetDistance>748.0</streetDistance> |
| | | // |
| | | // <segmentNumber>1-2</segmentNumber> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="2"> |
| | | // |
| | | // <strguide>沿省道4/华南快速,岑村立交,行驶到广园快速路,在天河客运站/元岗/广汕路/岑村路/市交警支队出口驶离,进入长湴中路。</strguide> |
| | | // |
| | | // <streetNames>省道4/华南快速,岑村立交,</streetNames> |
| | | // |
| | | // <lastStreetName>广园快速路</lastStreetName> |
| | | // |
| | | // <linkStreetName>长湴中路</linkStreetName> |
| | | // |
| | | // <signage>天河客运站/元岗/广汕路/岑村路/市交警支队出口</signage> |
| | | // |
| | | // <tollStatus>2</tollStatus> |
| | | // |
| | | // <turnlatlon>113.35731,23.15636</turnlatlon> |
| | | // |
| | | // <streetLatLon>113.37139,23.14206;113.37145,23.14219;113.37151,23.1423;113.37157,23.14241;113.37164,23.14249;113.37171,23.14256;113.37175,23.1426;113.37179,23.14263;113.37182,23.14265;113.37186,23.14267;113.37192,23.14269;113.37196,23.1427;113.37206,23.14271;113.37216,23.14269;113.37222,23.14267;113.37229,23.14264;113.37234,23.14261;113.37239,23.14257;113.37243,23.14251;113.37247,23.14246;113.3725,23.14237;113.37251,23.14231;113.37251,23.14226;113.37251,23.14222;113.37249,23.14215;113.37248,23.14212;113.37247,23.14208;113.37245,23.14203;113.37242,23.14198;113.37235,23.14191;113.37226,23.14182;113.37215,23.14174;113.37206,23.14169;113.37202,23.14167;113.37183,23.1416;113.37169,23.14157;113.37157,23.14155;113.37145,23.14153;113.37131,23.14151;113.37131,23.14151;113.37125,23.14153;113.37115,23.14156;113.37049,23.14178;113.37036,23.14183;113.37022,23.14188;113.37006,23.14194;113.36995,23.14197;113.36995,23.14197;113.36986,23.14199;113.36979,23.142;113.36956,23.14203;113.36956,23.14203;113.36949,23.14205;113.36949,23.14205;113.3693,23.14213;113.3693,23.14213;113.3692,23.14218;113.3692,23.14218;113.3691,23.14223;113.3691,23.14223;113.36837,23.1425;113.36837,23.1425;113.36735,23.14293;113.36735,23.14293;113.36683,23.14314;113.36683,23.14314;113.36454,23.14409;113.36454,23.14409;113.36432,23.14418;113.36413,23.14426;113.3637,23.14444;113.3637,23.14444;113.36214,23.14505;113.36138,23.14532;113.36138,23.14532;113.36131,23.14535;113.36131,23.14535;113.36113,23.14541;113.36113,23.14541;113.36101,23.14545;113.36089,23.1455;113.36071,23.14556;113.36071,23.14556;113.36062,23.14559;113.36052,23.14562;113.36052,23.14562;113.36024,23.1457;113.36015,23.14573;113.36015,23.14573;113.35987,23.14581;113.35969,23.14587;113.35818,23.1463;113.35818,23.1463;113.35777,23.14642;113.35777,23.14642;113.35766,23.14645;113.35766,23.14645;113.35762,23.14647;113.35762,23.14647;113.35736,23.14655;113.35736,23.14655;113.35685,23.14669;113.35685,23.14669;113.35657,23.14695;113.35642,23.14713;113.35642,23.14713;113.35637,23.14718;113.3562,23.14738;113.35586,23.14787;113.35582,23.14793;113.35572,23.14812;113.35572,23.14812;113.35557,23.14838;113.35554,23.14844;113.35554,23.14844;113.35545,23.14854;113.35533,23.14872;113.35533,23.14872;113.35529,23.14878;113.35529,23.14878;113.355,23.14912;113.35492,23.14923;113.35486,23.1493;113.35483,23.14936;113.3548,23.14942;113.3548,23.14942;113.35473,23.14964;113.35472,23.14973;113.35472,23.14973;113.35471,23.14981;113.35473,23.15003;113.35485,23.15044;113.35509,23.1512;113.35517,23.15145;113.35518,23.15151;113.35518,23.15157;113.35518,23.15157;113.35523,23.15169;113.35523,23.15169;113.35526,23.15178;113.35526,23.15178;113.35531,23.15192;113.35531,23.15192;113.35534,23.15199;113.35541,23.15216;113.35547,23.15233;113.35554,23.1525;113.35568,23.15287;113.35584,23.15323;113.35591,23.15342;113.35598,23.15358;113.35609,23.15379;113.35617,23.15394;113.35629,23.15421;113.35646,23.15457;113.35648,23.15461;113.35652,23.15471;113.35654,23.15475;113.35673,23.15515;113.35677,23.15524;113.35677,23.15524;113.35693,23.15556;113.35697,23.15564;113.35709,23.1559;113.35709,23.1559;113.35731,23.15636;113.35731,23.15636;113.35756,23.15669;113.35775,23.15697;113.3579,23.15725;113.35805,23.15752;113.35805,23.15752;113.35841,23.15824;113.35865,23.15872;113.3587,23.15889;113.3587,23.15889;113.35879,23.15912;113.35883,23.15927;113.35885,23.15938;113.35886,23.15948;113.35884,23.1596;113.35882,23.15978;113.35878,23.15994;113.35868,23.16016;113.35859,23.16034;113.35855,23.16041;113.35849,23.1605;113.35843,23.16062;113.35829,23.16086;</streetLatLon> |
| | | // |
| | | // <streetDistance>3669.0</streetDistance> |
| | | // |
| | | // <segmentNumber>3-6</segmentNumber> |
| | | // |
| | | // </item> |
| | | // |
| | | // <item id="3"> |
| | | // |
| | | // <strguide>沿长湴中路行驶到目的地。</strguide> |
| | | // |
| | | // <streetNames>长湴中路</streetNames> |
| | | // |
| | | // <lastStreetName/> |
| | | // |
| | | // <linkStreetName/> |
| | | // |
| | | // <signage/> |
| | | // |
| | | // <tollStatus>0</tollStatus> |
| | | // |
| | | // <turnlatlon>113.34714,23.17047</turnlatlon> |
| | | // |
| | | // <streetLatLon>113.34714,23.17047;113.34711,23.1703;113.34708,23.16988;113.34708,23.16988;113.34705,23.16957;</streetLatLon> |
| | | // |
| | | // <streetDistance>101.0</streetDistance> |
| | | // |
| | | // <segmentNumber>7-8</segmentNumber> |
| | | // |
| | | // </item> |
| | | // |
| | | // </simple> |
| | | // |
| | | // <distance>7.48</distance> |
| | | // |
| | | // <duration>548.0</duration> |
| | | // |
| | | // <routelatlon>113.373308,23.136699;113.37323,23.13677;113.37272,23.13687;113.37265,23.13688;113.37227,23.13696;113.37209,23.13699;113.37201,23.13696;113.37185,23.13699;113.37185,23.13699;113.3714,23.13649;113.37126,23.13643;113.37116,23.13644;113.37107,23.13648;113.37107,23.13648;113.37091,23.13683;113.37075,23.13717;113.37057,23.13765;113.37054,23.13794;113.37055,23.13844;113.3706,23.13878;113.3708,23.13964;113.37083,23.13975;113.371,23.14051;113.37125,23.14153;113.37129,23.14167;113.37139,23.14206;113.37139,23.14206;113.37164,23.14249;113.37182,23.14265;113.37206,23.14271;113.37222,23.14267;113.37234,23.14261;113.3725,23.14237;113.37249,23.14215;113.37242,23.14198;113.37206,23.14169;113.37183,23.1416;113.37131,23.14151;113.37049,23.14178;113.36995,23.14197;113.36956,23.14203;113.36949,23.14205;113.3693,23.14213;113.3692,23.14218;113.3691,23.14223;113.36837,23.1425;113.36735,23.14293;113.36683,23.14314;113.36454,23.14409;113.3637,23.14444;113.36138,23.14532;113.36113,23.14541;113.36052,23.14562;113.35818,23.1463;113.35766,23.14645;113.35762,23.14647;113.35685,23.14669;113.35685,23.14669;113.35642,23.14713;113.35637,23.14718;113.35572,23.14812;113.35554,23.14844;113.35533,23.14872;113.35529,23.14878;113.35486,23.1493;113.3548,23.14942;113.35473,23.14964;113.35473,23.15003;113.35518,23.15151;113.35518,23.15157;113.35518,23.15157;113.35526,23.15178;113.35554,23.1525;113.35598,23.15358;113.35652,23.15471;113.35731,23.15636;113.35731,23.15636;113.35805,23.15752;113.3587,23.15889;113.35885,23.15938;113.35878,23.15994;113.35855,23.16041;113.35829,23.16086;113.35829,23.16086;113.35774,23.16199;113.35695,23.16368;113.35625,23.1651;113.35567,23.16616;113.35546,23.16638;113.35484,23.1669;113.3547,23.16697;113.35444,23.1671;113.35392,23.16726;113.35328,23.16732;113.35126,23.16726;113.35046,23.16736;113.34949,23.16765;113.34651,23.16884;113.34571,23.16921;113.34393,23.17017;113.34387,23.17021;113.34252,23.17092;113.34213,23.17112;113.34192,23.17139;113.34177,23.17169;113.34261,23.17145;113.34427,23.17102;113.34559,23.17069;113.34714,23.17047;113.34714,23.17047;113.34708,23.16988;113.34708,23.16988;113.34705,23.16957;113.3472,23.16957;</routelatlon> |
| | | // |
| | | // <mapinfo> |
| | | // |
| | | // <center>113.35754,23.15406</center> |
| | | // |
| | | // <scale>10</scale> |
| | | // |
| | | // </mapinfo> |
| | | //</result> |
| | | //解析这个xml 拿到distance和duration |
| | | if(routeResponse!=null) { |
| | | org.dom4j.Document doc = DocumentHelper.parseText(routeResponse); |
| | | |
| | | // 2. 获取根节点 <result> |
| | | Element rootElement = doc.getRootElement(); |
| | | |
| | | // 3. 直接获取根节点下的 <distance> 子节点文本值 |
| | | String distance = rootElement.elementText("distance"); |
| | | |
| | | // 可选:转为Double类型(若需数值计算) |
| | | Double distanceValue = Double.parseDouble(distance); |
| | | Map<String, Double> resultMap = new HashMap<>(); |
| | | resultMap.put("distance", distanceValue*1000); |
| | | |
| | | // 4. 直接获取根节点下的 <duration> 子节点文本值 |
| | | return AjaxResult.success("计算成功", resultMap); |
| | | } |
| | | else{ |
| | | return AjaxResult.error("计算距离失败:无返回结果"); |
| | | } |
| | | } catch (Exception e) { |
| | | logger.error("计算地址距离失败", e); |
| | | return AjaxResult.error("计算距离失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 天地图输入提示接口代理(地址联想) |
| | | * 文档:https://lbs.tianditu.gov.cn/server/suggestion.html |
| | | */ |
| | | @Anonymous() |
| | | @GetMapping("/tianditu/place/suggestion") |
| | | public AjaxResult tiandituPlaceSuggestion(String keyWord, String region, String city, Integer count) { |
| | | try { |
| | | // 检查必需参数 |
| | | if (keyWord == null || keyWord.trim().isEmpty()) { |
| | | return AjaxResult.error("参数不完整,缺少搜索关键词(keyWord)"); |
| | | } |
| | | |
| | | // 设置默认值 |
| | | if (count == null) { |
| | | count = 10; |
| | | } |
| | | |
| | | // 构建天地图普通搜索API URL |
| | | String url = "http://api.tianditu.gov.cn/v2/search"; |
| | | StringBuilder paramsBuilder = new StringBuilder(); |
| | | paramsBuilder.append("postStr={\"keyWord\":\"").append(keyWord).append("\""); |
| | | // 硬编码中国地图范围 |
| | | paramsBuilder.append(",\"mapBound\":\"").append("73.66,3.86,135.05,53.55").append("\""); |
| | | // 默认级别为18 |
| | | paramsBuilder.append(",\"level\":\"").append("18").append("\""); |
| | | // 默认查询类型为普通搜索 |
| | | paramsBuilder.append(",\"queryType\":\"").append("1").append("\""); |
| | | // 默认起始位置为0 |
| | | paramsBuilder.append(",\"start\":\"").append("0").append("\""); |
| | | paramsBuilder.append(",\"count\":\"").append(count).append("\""); |
| | | |
| | | // 添加可选参数 |
| | | if (region != null && !region.trim().isEmpty()) { |
| | | paramsBuilder.append(",\"specify\":\"").append(region).append("\""); |
| | | } |
| | | if (city != null && !city.trim().isEmpty()) { |
| | | paramsBuilder.append(",\"dataTypes\":\"").append(city).append("\""); |
| | | } |
| | | |
| | | paramsBuilder.append("}"); |
| | | paramsBuilder.append("&type=query"); |
| | | paramsBuilder.append("&tk=").append(tiandituMapConfig.getTk()); |
| | | |
| | | String params = paramsBuilder.toString(); |
| | | |
| | | logger.info("天地图普通搜索请求: keyWord={}, count={}", keyWord, count); |
| | | logger.debug("地图请求接口参数:{}", params); |
| | | |
| | | // 发送HTTP请求 |
| | | String response = HttpUtils.sendGet(url, params); |
| | | logger.debug("天地图普通搜索响应: {}", response); |
| | | // {"count":27,"resultType":1,"prompt":[{"type":4,"admins":[{"adminName":"广州市","adminCode":156440100}]}], |
| | | //"pois":[{"address":"广东省广州市天河区棠下街道子午馆天河棠下店","phone":"","poiType":"101","name":"子午馆(天河棠下店)","source":"0","hotPointID":"17163AD907B29F24","lonlat":"113.36952,23.134056"},{"address":"博汇街6号","phone":"","poiType":"101","name":"天河棠下·智汇park","source":"0","hotPointID":"C2635A7054C1EBB7","lonlat":"113.37003,23.14308"},{"address":"科新路143~147号","phone":"020-88524715","poiType":"101","name":"重庆德庄火锅(天河棠下店)","source":"0","hotPointID":"6919E1CFA6F6EF20","lonlat":"113.37627,23.127416"},{"address":"博汇街6号","phone":"020-87085713","poiType":"101","name":"天河棠下·智汇PAPK停车场","source":"0","hotPointID":"4AC84F8E1CB34C4E","lonlat":"113.36925,23.14256"},{"address":"广园东路博汇街6号天河棠下·智汇PAPK地下停车场","phone":"020-87085713","poiType":"101","name":"天河棠下·智汇PAPK特斯拉超级充电站","source":"0","hotPointID":"70FC2D0A2D061E4B","lonlat":"113.3705,23.14359"},{"address":"棠德南路321","phone":"","poiType":"101","name":"糖园天河棠下店","source":"0","hotPointID":"B97F4B20719EEBD1","lonlat":"113.38009,23.13434"},{"address":"中山大道棠下路东闸大街自编98号2号店","phone":"13590139682","poiType":"101","name":"花甲王天河棠下店","source":"0","hotPointID":"174C40E7FBAE1187","lonlat":"113.37556,23.12844"},{"address":"棠下二社涌边路69号光辉大厦","phone":"020-66351119","poiType":"101","name":"索特来文艺酒店天河棠下店","source":"0","hotPointID":"362052A27B9A1AC8","lonlat":"113.37161,23.12794"},{"address":"中山大道西493号","phone":"020-38288788","poiType":"101","name":"中国电信天河棠下福善营业厅","source":"0","hotPointID":"D50C7774CFC6AA09","lonlat":"113.37455,23.12855"},{"address":"广东省广州市天河区棠下街道愿达语言培训中心天河棠下校区","phone":"","poiType":"101","name":"愿达语言培训中心天河棠下校区","source":"0","hotPointID":"3CEE80F76558BDE4","lonlat":"113.373743,23.128796"}],"lineData":[],"status":{"cndesc":"服务正常","infocode":1000},"keyWord":"广州天河棠下"} |
| | | com.alibaba.fastjson.JSONObject jsonResponse = com.alibaba.fastjson.JSONObject.parseObject(response); |
| | | JSONObject objStatus=jsonResponse.getJSONObject("status"); |
| | | if (objStatus == null || objStatus.getInteger("infocode") != 1000) { |
| | | logger.error("天地图普通搜索失败: {}", response); |
| | | return AjaxResult.error("地址搜索失败"); |
| | | } |
| | | |
| | | JSONArray results = jsonResponse.getJSONArray("pois"); |
| | | |
| | | |
| | | // 构建返回结果 |
| | | List<Map<String, Object>> suggestions = new ArrayList<>(); |
| | | for (int i = 0; i < results.size(); i++) { |
| | | JSONObject item = results.getJSONObject(i); |
| | | |
| | | Map<String, Object> suggestion = new HashMap<>(); |
| | | suggestion.put("name", item.getString("name")); // 名称 |
| | | suggestion.put("address", item.getString("address")); // 地址 |
| | | suggestion.put("province", item.getString("prov")); // 省 |
| | | suggestion.put("city", item.getString("city")); // 市 |
| | | suggestion.put("district", item.getString("county")); // 区县 |
| | | suggestion.put("tel", item.getString("tel")); // 电话 |
| | | suggestion.put("postcode", item.getString("postcode")); // 邮编 |
| | | suggestion.put("type", item.getString("type")); // 类型 |
| | | |
| | | // 经纬度信息 |
| | | String location = item.getString("lonlat"); |
| | | if (location != null) { |
| | | Map<String, Object> locationMap = new HashMap<>(); |
| | | String[] locationArray = location.split(","); |
| | | locationMap.put("lng",Double.parseDouble(locationArray[0])); |
| | | locationMap.put("lat",Double.parseDouble(locationArray[1])); |
| | | suggestion.put("location", locationMap); |
| | | } |
| | | |
| | | suggestions.add(suggestion); |
| | | } |
| | | |
| | | logger.info("地址搜索成功: 找到{}条结果", suggestions.size()); |
| | | return AjaxResult.success("查询成功", suggestions); |
| | | } catch (Exception e) { |
| | | logger.error("地址搜索失败", e); |
| | | return AjaxResult.error("地址搜索失败:" + e.getMessage()); |
| | | } |
| | | } |
| | | } |