Pārlūkot izejas kodu

APP城市与城市半径提交

zjy 4 nedēļas atpakaļ
vecāks
revīzija
5991fa18e7

+ 471 - 0
alien-second/src/main/java/shop/alien/second/config/GaoDeMapUtil.java

@@ -0,0 +1,471 @@
+package shop.alien.second.config;
+
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import shop.alien.entity.store.EssentialCityCode;
+import shop.alien.mapper.EssentialCityCodeMapper;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLEncoder;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 高德地图工具类
+ *
+ * @author ssk
+ * @version 1.0.0
+ * @discription 打死你个龟孙
+ * @since 1937-7-7
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class GaoDeMapUtil {
+
+    @Value("${gaode.key}")
+    private String key;
+
+    @Value("${gaode.geoUrl}")
+    private String geoUrl;
+
+    @Value("${gaode.geoListUrl}")
+    private String geoListUrl;
+
+    @Value("${gaode.getDistrict}")
+    private String getDistrict;
+
+    @Value("${gaode.distanceUrl}")
+    private String distanceUrl;
+
+    @Value("${gaode.distanceTypeUrl}")
+    private String distanceTypeUrl;
+
+    @Value("${gaode.nearUrl}")
+    private String nearUrl;
+
+    @Value("${gaode.subwayUrl}")
+    private String subwayUrl;
+
+    @Value("${gaode.addressUrl}")
+    private String addressUrl;
+
+    private final EssentialCityCodeMapper essentialCityCodeMapper;
+
+    /**
+     * 地址转经纬度
+     *
+     * @param address 地址
+     * @return 经纬度
+     */
+    public String getLonAndLatByAddress(String address) {
+        String formattedUrl = String.format(geoUrl, address, key);
+        JSONObject obj = getResponse(formattedUrl);
+        if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+            JSONArray geocodesArray = obj.getJSONArray("geocodes");
+            if (geocodesArray != null && !geocodesArray.isEmpty()) {
+                JSONObject jobJSON = geocodesArray.getJSONObject(0);
+                return jobJSON.getString("location");
+            } else {
+                log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 未找到与地址匹配的经纬度信息");
+                return "ERROR 未找到与地址匹配的经纬度信息";
+            }
+        } else {
+            log.error("AddressLocationUtil.getLonAndLatByAddress ERROR 地址转换经纬度失败");
+            return "ERROR 地址转换经纬度失败";
+        }
+    }
+
+    /**
+     * 地址转经纬度
+     *
+     * @param address 地址
+     * @return 经纬度
+     */
+    public JSONObject getInputPrompt(String address, String city) {
+        //如果按照城市查询
+        String cityCode = "";
+        if (StringUtils.isNotEmpty(city)) {
+            EssentialCityCode essentialCityCode = essentialCityCodeMapper.selectOne(new LambdaQueryWrapper<EssentialCityCode>().eq(EssentialCityCode::getAreaName, city));
+            if (null != essentialCityCode) {
+                cityCode = essentialCityCode.getCityCode().toString();
+            }
+        }
+        String formattedUrl = String.format(geoListUrl, address, key, cityCode);
+        JSONObject obj = getResponse(formattedUrl);
+        return obj;
+    }
+
+    /**
+     * 地址转经纬度
+     * cityCode:城市编码
+     *
+     * @return
+     */
+    public JSONObject getDistrict(String cityCode) {
+        String formattedUrl = String.format(getDistrict, key, cityCode);
+        JSONObject obj = getResponse(formattedUrl);
+        return obj;
+    }
+
+    /**
+     * 计算两个经纬度的距离
+     *
+     * @param longitudeOne 经度1
+     * @param latitudeOne  纬度1
+     * @param longitudeTwo 经度2
+     * @param latitudeTwo  纬度2
+     * @return distance
+     */
+    public Double getDistance(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
+        try {
+            String urlString = String.format(distanceUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("results");
+                if (resultsArray != null && !resultsArray.isEmpty()) {
+                    JSONObject resultJSON = resultsArray.getJSONObject(0);
+                    return Double.parseDouble(resultJSON.getString("distance"));
+                } else {
+                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败,结果为空");
+                    return null;
+                }
+            } else {
+                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 计算两个经纬度的距离
+     *
+     * @param longitudeOne 经度1
+     * @param latitudeOne  纬度1
+     * @param longitudeTwo 经度2
+     * @param latitudeTwo  纬度2
+     * @return distance
+     */
+    public Double getDistanceStraightLine(String longitudeOne, String latitudeOne, String longitudeTwo, String latitudeTwo) {
+        try {
+            String urlString = String.format(distanceTypeUrl, key, longitudeOne, latitudeOne, longitudeTwo, latitudeTwo, 0);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("results");
+                if (resultsArray != null && !resultsArray.isEmpty()) {
+                    JSONObject resultJSON = resultsArray.getJSONObject(0);
+                    return Double.parseDouble(resultJSON.getString("distance"));
+                } else {
+                    log.error("AddressLocationUtil.getDistance ERROR 计算距离失败,结果为空");
+                    return null;
+                }
+            } else {
+                log.error("AddressLocationUtil.getDistance ERROR 计算距离失败");
+                return null;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getDistance ERROR {}", e.getMessage());
+            return null;
+        }
+    }
+
+    /**
+     * 获取附近商家、建筑等信息
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return
+     */
+    public JSONArray getNear(String longitude, String latitude) {
+        try {
+            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("pois");
+                return resultsArray;
+            }
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getNear ERROR {}", e.getMessage());
+        }
+        return new JSONArray();
+    }
+
+    /**
+     * 获取10公里范围内的商场
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return 商场列表
+     */
+    public JSONArray getNearbyShoppingMalls(String longitude, String latitude) {
+        try {
+            // 使用高德地图POI搜索接口,搜索类型为商场(060100)
+            String urlString = String.format(nearUrl, longitude, latitude, key, 10000, 20, 1) + "&types=060100";
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONArray resultsArray = obj.getJSONArray("pois");
+                return resultsArray;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getNearbyShoppingMalls ERROR {}", e.getMessage());
+        }
+        return new JSONArray();
+    }
+
+    public JSONObject getNearbySubway(String longitude, String latitude) {
+        try {
+            // 使用高德地图POI搜索接口,搜索类型为地铁站(010100)
+            String encodedKeywords = URLEncoder.encode("地铁站", "UTF-8");
+            String urlString = String.format(
+                    subwayUrl,
+                    key, latitude, longitude, encodedKeywords, 1000, 150500);
+            JSONObject obj = getResponse(urlString);
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                JSONObject pois = obj.getJSONArray("pois").getJSONObject(0);
+                return pois;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getNearbySubway ERROR {}", e.getMessage());
+        }
+        return new JSONObject();
+    }
+
+    /**
+     * 通过经纬度获取地址信息
+     *
+     * @param longitude
+     * @param latitude
+     * @param radius
+     * @param extensions
+     * @return
+     */
+    public Map<String, String> getAddressByLonAndLat(String longitude, String latitude, String radius, String extensions) {
+        String formattedUrl = String.format(addressUrl, longitude, latitude, key, radius, extensions);
+        JSONObject obj = getResponse(formattedUrl);
+        try {
+            if (null != obj && "1".equals(String.valueOf(obj.get("status")))) {
+                Map<String, String> address = new HashMap<>();
+                JSONObject regeocodeObject = obj.getJSONObject("regeocode");
+                JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
+                address.put("city", addressComponent.getString("city"));
+                address.put("district", addressComponent.getString("district"));
+                address.put("province", addressComponent.getString("province"));
+                JSONObject streetNumber = addressComponent.getJSONObject("streetNumber");
+                address.put("street", streetNumber.getString("street"));
+                address.put("streetNumber", streetNumber.getString("number"));
+                JSONObject pois = regeocodeObject.getJSONArray("pois").getJSONObject(0);
+                address.put("poiName", pois.getString("name"));
+                return address;
+            }
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getAddressByLonAndLat ERROR {}", e.getMessage());
+        }
+        return new HashMap<>();
+    }
+
+    /**
+     * 根据经纬度获取当前城市与城市半径
+     *
+     * @param longitude 经度
+     * @param latitude  纬度
+     * @return Map包含城市名称、城市编码、城市中心点、城市半径(米)
+     */
+    public Map<String, Object> getCityAndRadiusByLonAndLat(String longitude, String latitude) {
+        Map<String, Object> result = new HashMap<>();
+        try {
+            // 1. 通过逆地理编码获取城市信息
+            String formattedUrl = String.format(addressUrl, longitude, latitude, key, "1000", "base");
+            JSONObject geoObj = getResponse(formattedUrl);
+            
+            if (null == geoObj || !"1".equals(String.valueOf(geoObj.get("status")))) {
+                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 逆地理编码失败");
+                return result;
+            }
+            
+            JSONObject regeocodeObject = geoObj.getJSONObject("regeocode");
+            JSONObject addressComponent = regeocodeObject.getJSONObject("addressComponent");
+            
+            String cityName = addressComponent.getString("city");
+            String adcode = addressComponent.getString("adcode");  // 区县级别代码
+            String province = addressComponent.getString("province");
+            
+            // 判断是否是直辖市
+            boolean isMunicipality = StringUtils.isEmpty(cityName) || "[]".equals(cityName);
+            if (isMunicipality) {
+                cityName = province;
+            }
+            
+            // 获取城市级别的行政区划代码
+            String cityCode = getCityLevelCode(adcode, isMunicipality, province);
+            
+            result.put("city", cityName);
+            result.put("province", province);
+            result.put("cityCode", cityCode);
+            
+            // 2. 通过行政区域查询获取城市边界信息
+            // 注意:必须添加 extensions=all 和 subdistrict=0 参数才能获取到polyline边界数据
+            String districtUrl = String.format(getDistrict, key, cityCode) + "&extensions=all&subdistrict=0";
+            JSONObject districtObj = getResponse(districtUrl);
+            
+            if (null != districtObj && "1".equals(String.valueOf(districtObj.get("status")))) {
+                JSONArray districts = districtObj.getJSONArray("districts");
+                if (districts != null && !districts.isEmpty()) {
+                    JSONObject district = districts.getJSONObject(0);
+                    String center = district.getString("center");
+                    result.put("center", center);
+                    
+                    // 3. 计算城市半径:获取边界坐标,计算中心点到边界的最大距离
+                    String polyline = district.getString("polyline");
+                    if (StringUtils.isNotEmpty(polyline) && !"|".equals(polyline)) {
+                        Double radius = calculateCityRadius(center, polyline);
+                        result.put("radius", radius);
+                        // 保留两位小数
+                        result.put("radiusKm", Math.round(radius / 10.0) / 100.0);
+                    } else {
+                        // 如果没有边界数据,返回默认半径
+                        result.put("radius", 50000.0);  // 默认50公里
+                        result.put("radiusKm", 50.00);
+                        log.warn("GaoDeMapUtil.getCityAndRadiusByLonAndLat 城市{}没有边界数据,使用默认半径", cityName);
+                    }
+                } else {
+                    log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 未查询到城市行政区域信息");
+                }
+            } else {
+                log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat 行政区域查询失败");
+            }
+            
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.getCityAndRadiusByLonAndLat ERROR {}", e.getMessage(), e);
+        }
+        return result;
+    }
+
+    /**
+     * 获取城市级别的行政区划代码
+     * 
+     * @param adcode 区县级别的行政区划代码
+     * @param isMunicipality 是否是直辖市
+     * @param province 省份名称
+     * @return 城市级别的行政区划代码
+     */
+    private String getCityLevelCode(String adcode, boolean isMunicipality, String province) {
+        if (StringUtils.isEmpty(adcode) || adcode.length() != 6) {
+            return adcode;
+        }
+        
+        // 直辖市(北京、上海、天津、重庆):使用省级代码(前2位+0000)
+        if (isMunicipality || "北京市".equals(province) || "上海市".equals(province) 
+                || "天津市".equals(province) || "重庆市".equals(province)) {
+            return adcode.substring(0, 2) + "0000";
+        }
+        
+        // 普通地级市:使用市级代码(前4位+00)
+        return adcode.substring(0, 4) + "00";
+    }
+
+    /**
+     * 计算城市半径:计算中心点到边界的最大直线距离
+     *
+     * @param center   中心点坐标 "经度,纬度"
+     * @param polyline 边界坐标串 "经度,纬度;经度,纬度;..." 或 "经度,纬度;经度,纬度|经度,纬度;经度,纬度"
+     * @return 城市半径(米)
+     */
+    private Double calculateCityRadius(String center, String polyline) {
+        try {
+            String[] centerCoords = center.split(",");
+            double centerLon = Double.parseDouble(centerCoords[0]);
+            double centerLat = Double.parseDouble(centerCoords[1]);
+            
+            // 处理多个区域的情况(用|分隔)
+            String[] areas = polyline.split("\\|");
+            double maxDistance = 0.0;
+            
+            for (String area : areas) {
+                String[] points = area.split(";");
+                // 采样边界点,避免计算量过大(每隔10个点采样一次)
+                int step = Math.max(1, points.length / 50);
+                for (int i = 0; i < points.length; i += step) {
+                    String[] pointCoords = points[i].split(",");
+                    if (pointCoords.length >= 2) {
+                        double pointLon = Double.parseDouble(pointCoords[0]);
+                        double pointLat = Double.parseDouble(pointCoords[1]);
+                        
+                        // 使用本地Haversine公式计算直线距离(不调用API,速度快)
+                        double distance = haversineDistance(centerLon, centerLat, pointLon, pointLat);
+                        if (distance > maxDistance) {
+                            maxDistance = distance;
+                        }
+                    }
+                }
+            }
+            
+            return maxDistance;
+        } catch (Exception e) {
+            log.error("GaoDeMapUtil.calculateCityRadius ERROR {}", e.getMessage());
+            return 50000.0; // 默认50公里
+        }
+    }
+
+    /**
+     * 使用Haversine公式计算两点间的直线距离(本地计算,不调用API)
+     *
+     * @param lon1 起点经度
+     * @param lat1 起点纬度
+     * @param lon2 终点经度
+     * @param lat2 终点纬度
+     * @return 距离(米)
+     */
+    private double haversineDistance(double lon1, double lat1, double lon2, double lat2) {
+        final double EARTH_RADIUS = 6371000; // 地球半径(米)
+        
+        // 转换为弧度
+        double lat1Rad = Math.toRadians(lat1);
+        double lat2Rad = Math.toRadians(lat2);
+        double deltaLat = Math.toRadians(lat2 - lat1);
+        double deltaLon = Math.toRadians(lon2 - lon1);
+        
+        // Haversine公式
+        double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
+                   Math.cos(lat1Rad) * Math.cos(lat2Rad) *
+                   Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+        
+        return EARTH_RADIUS * c;
+    }
+
+    /**
+     * 创建请求
+     *
+     * @param serverUrl 请求地址
+     * @return JSONObject
+     */
+    private JSONObject getResponse(String serverUrl) {
+        StringBuilder result = new StringBuilder();
+        try {
+            URL url = new URL(serverUrl);
+            URLConnection conn = url.openConnection();
+            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
+            String line;
+            while ((line = in.readLine()) != null) {
+                result.append(line);
+            }
+            in.close();
+        } catch (Exception e) {
+            log.error("AddressLocationUtil.getResponse ERROR {}", e.getMessage());
+            return null;
+        }
+        return JSONObject.parseObject(result.toString());
+    }
+}

+ 33 - 0
alien-second/src/main/java/shop/alien/second/controller/SecondGaoDeController.java

@@ -0,0 +1,33 @@
+package shop.alien.second.controller;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiSort;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.*;
+import shop.alien.entity.result.R;
+import shop.alien.second.config.GaoDeMapUtil;
+
+import java.util.Map;
+
+@Slf4j
+@Api(tags = {"二手平台-高德"})
+@ApiSort(9)
+@CrossOrigin
+@RestController
+@RequestMapping("/second/gaode")
+@RequiredArgsConstructor
+public class SecondGaoDeController {
+
+    private final GaoDeMapUtil gaodeMapUtil;
+
+    @ApiOperation("获取城市与城市半径")
+    @GetMapping("/queryCity")
+    public R<Map<String, Object>> queryCity(
+            @RequestParam(value = "longitude", required = false) String longitude,
+            @RequestParam(value = "latitude", required = false) String latitude) {
+        return R.data(gaodeMapUtil.getCityAndRadiusByLonAndLat(longitude, latitude), "查询成功");
+    }
+
+}