zoukankan      html  css  js  c++  java
  • 使用百度地图API进行地址和经纬度之间的转换

    简介

    我们在项目中可能会遇到将经纬度转换成省市区的需求,这个时候需要各种地图提供的API,这里我们使用百度地图的API。百度地图开放平台,以下所指的经纬度都是百度地图的经纬度。

    代码实现

    添加一些工具的maven依赖,本例我们使用JDK11

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.4</version>
    </dependency>
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.56</version>
    </dependency>
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.10</version>
    </dependency>
    

    将地理位置转换成经纬度

    定义接口请求

    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class GeoCodingRequest {
    
      /**
       * 待解析的地址。最多支持84个字节。
       */
      private String address;
      /**
       * 用户申请注册的key
       */
      private String ak;
      /**
       * 请求签名
       */
      private String sn;
      /**
       * 输出格式为json或者xml
       */
      private String output;
    }
    

    定义接口响应

    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class GeoCodingInfo {
    
      private static final Integer SUCCESS = 0;
      //返回结果状态值, 成功返回0
      private Integer status;
      //响应结果
      private GeoCodingResultInfo result;
    
      public boolean isSuccess() {
        return SUCCESS.equals(status);
      }
    }
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class GeoCodingResultInfo {
    
      /**
       * 经纬度信息
       */
      private GeoCodingLocationInfo location;
      /**
       * 位置的附加信息,是否精确查找。1为精确查找,即准确打点;0为不精确,即模糊打点。
       */
      private Integer precise;
      /**
       * 描述打点绝对精度(即坐标点的误差范围)。 confidence=100,解析误差绝对精度小于20m; confidence≥90,解析误差绝对精度小于50m;
       * confidence≥80,解析误差绝对精度小于100m; confidence≥75,解析误差绝对精度小于200m; confidence≥70,解析误差绝对精度小于300m;
       * confidence≥60,解析误差绝对精度小于500m; confidence≥50,解析误差绝对精度小于1000m; confidence≥40,解析误差绝对精度小于2000m;
       * confidence≥30,解析误差绝对精度小于5000m; confidence≥25,解析误差绝对精度小于8000m; confidence≥20,解析误差绝对精度小于10000m;
       */
      private Integer confidence;
      /**
       * 能精确理解的地址类型,包含:UNKNOWN、国家、省、城市、区县、乡镇、村庄、道路、地产小区、商务大厦、政府机构、交叉路口、商圈、生活服务、休闲娱乐、餐饮、宾馆、购物、金融、教育、医疗
       * 、工业园区 、旅游景点 、汽车服务、火车站、长途汽车站、桥 、停车场/停车区、港口/码头、收费区/收费站、飞机场 、机场 、收费处/收费站 、加油站、绿地、门址
       */
      private String level;
    }
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class GeoCodingLocationInfo {
    
      /**
       * 经度
       */
      private BigDecimal lng;
      /**
       * 维度
       */
      private BigDecimal lat;
    }
    

    接下来编写访问百度地图API的工具类,这里我们使用的请求校验方式为sn校验。

    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.TypeReference;
    import java.net.URI;
    import java.net.URLEncoder;
    import java.net.http.HttpClient;
    import java.net.http.HttpRequest;
    import java.net.http.HttpResponse.BodyHandlers;
    import java.nio.charset.StandardCharsets;
    import java.util.Collection;
    import java.util.LinkedHashMap;
    import java.util.Map;
    import org.apache.commons.codec.digest.DigestUtils;
    
    public class BaiduMapUtility {
    
      private static final String BASE_URL = "http://api.map.baidu.com";
      private static final String GEOCODING_URL = "http://api.map.baidu.com/geocoding/v3/";
      private static final String REVERSE_GEOCODING_URL = "http://api.map.baidu.com/reverse_geocoding/v3/";
      private static final String AK = "xxx"; // 你的ak
      private static final String SK = "xxx"; // 你的sk
    
      /**
       * 地理编码,将地址转换成经纬度
       *
       * @return 响应
       */
      public static GeoCodingInfo geoCoding(String address) throws Exception {
        String url = GEOCODING_URL;
        GeoCodingRequest request = new GeoCodingRequest();
        request.setAddress(address);
        request.setOutput("json");
        request.setAk(AK);
        request.setSn(generateSign(url, request));
        url += getAppendUrl(request, false);
        return doApiRequest(url, GeoCodingInfo.class);
      }
    
      /**
       * 逆地理编码,将经纬度转换成地址
       *
       * @return 响应
       */
      public static ReverseGeoCodingInfo reverseGeoCoding(String location) throws Exception {
        String url = REVERSE_GEOCODING_URL;
        ReverseGeoCodingRequest request = new ReverseGeoCodingRequest();
        request.setLocation(location);
        request.setOutput("json");
        request.setAk(AK);
        request.setSn(generateSign(url, request));
        //必须
        request.setLocation(encodeValue(true, location));
        url += getAppendUrl(request, false);
        return doApiRequest(url, ReverseGeoCodingInfo.class);
      }
    
      private static <T> T doApiRequest(String url, Class<T> clazz) throws Exception {
        //创建请求客户端
        HttpClient client = HttpClient.newHttpClient();
        //创建GET请求
        HttpRequest httpRequest = HttpRequest.newBuilder()
            .GET()
            .uri(URI.create(url))
            .build();
        //同步发送请求
        String resp = client.send(httpRequest, BodyHandlers.ofString()).body();
        System.out.println(resp);
        return JSON.parseObject(resp, clazz);
      }
    
      /**
       * 生成请求签名
       *
       * @param url 请求url
       * @param request 请求对象
       */
      private static String generateSign(String url, Object request) {
        String params = getAppendUrl(request, true);
        String wholeStr =
            url.substring(url.lastIndexOf(BASE_URL) + BASE_URL.length()) + params + SK;
        String encodedStr = encodeValue(true, wholeStr);
        return DigestUtils.md5Hex(encodedStr.getBytes(StandardCharsets.UTF_8));
      }
    
      /**
       * url拼接参数
       *
       * @param obj 参数对象
       * @param encode 参数值是否编码
       * @return 参数字符串
       */
      private static String getAppendUrl(Object obj, boolean encode) {
        Map<String, Object> map = JSON
            .parseObject(JSON.toJSONString(obj), new TypeReference<LinkedHashMap<String, Object>>() {
            });
        if (map != null && !map.isEmpty()) {
          StringBuilder buffer = new StringBuilder();
          for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (buffer.length() == 0) {
              buffer.append("?");
            } else {
              buffer.append("&");
            }
            buffer.append(entry.getKey()).append("=");
            if (entry.getValue() instanceof Collection) {
              Collection collection = (Collection) entry.getValue();
              for (Object o : collection) {
                buffer.append(encodeValue(encode, o)).append(",");
              }
              buffer.deleteCharAt(buffer.length() - 1);
            } else {
              buffer.append(encodeValue(encode, entry.getValue()));
            }
          }
          return buffer.toString();
        }
        return "";
      }
    
      /**
       * url编码
       *
       * @param encode 是否编码
       * @param value 待编码值
       * @return 编码后的值
       */
      private static String encodeValue(boolean encode, Object value) {
        return encode ? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8) : value.toString();
      }
    }
    

    编写客户端

    public class Client {
    
      public static void main(String[] args) throws Exception {
        GeoCodingInfo geoCodingInfo = BaiduMapUtility.geoCoding("上海市闵行区");
        if (geoCodingInfo.isSuccess()) {
          GeoCodingLocationInfo locationInfo = geoCodingInfo.getResult().getLocation();
          System.out.println(locationInfo.getLng());
          System.out.println(locationInfo.getLat());
        }
      }
    
    }
    

    这里我们将上海市闵行区这个地址转换成经纬度,输出如下

    121.38861193361008
    31.118842580087429
    

    将经纬度显示一下

    将经纬度转换成省市区等地址

    定义接口请求

    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class ReverseGeoCodingRequest {
    
      /**
       * 经纬度坐标获取地址,lat<纬度>,lng<经度>
       */
      private String location;
      /**
       * 用户申请注册的key
       */
      private String ak;
      /**
       * 请求签名
       */
      private String sn;
      /**
       * 输出格式为json或者xml
       */
      private String output;
    }
    

    定义接口响应

    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class ReverseGeoCodingInfo {
    
      private static final Integer SUCCESS = 0;
      //返回结果状态值, 成功返回0
      private Integer status;
      //响应结果
      private ReverseGeoCodingResultInfo result;
    
      public boolean isSuccess() {
        return SUCCESS.equals(status);
      }
    }
    
    @AllArgsConstructor
    @NoArgsConstructor
    @Setter
    @Getter
    @ToString
    public class ReverseGeoCodingResultInfo {
    
      /**
       * 经纬度信息
       */
      private GeoCodingLocationInfo location;
      /**
       * 结构化地址信息
       */
      @JSONField(name = "formatted_address")
      private String formattedAddress;
    }
    

    客户端调用

    public class Client2 {
    
      public static void main(String[] args) throws Exception {
        String location = "31.118842580087429,121.38861193361008";
        ReverseGeoCodingInfo reverseGeoCodingInfo = BaiduMapUtility.reverseGeoCoding(location);
        if (reverseGeoCodingInfo.isSuccess()) {
          System.out.println(reverseGeoCodingInfo.getResult().getFormattedAddress());
        }
      }
    
    }
    

    将上一个接口响应的经纬度转换成地址信息,输出如下

    上海市闵行区沪闵路6258号
    

    遇到的问题

    整个请求访问的核心就是创建请求的签名,逆地理编码的接口请求中,经纬度坐标中的逗号需要进行url编码,其他不需要。
    错误的url

    http://api.map.baidu.com/reverse_geocoding/v3/?ak=ak&location=31.118842580087429,121.38861193361008&output=json&sn=sn
    

    正确的url

    http://api.map.baidu.com/reverse_geocoding/v3/?ak=ak&location=31.118842580087429%2C121.38861193361008&output=json&sn=sn
    

    参考

    讲讲百度地图API遇到的坑,石锤百度官方代码的错,解决SN校验失败
    百度地图开放平台Web服务API

  • 相关阅读:
    linux中WDCP的日志彻底删除技巧
    jquery中页面Ajax方法$.load的功能
    PHP与MYSQL中UTF8 中文排序例子
    mysql获取group by的总记录行数方法
    jquery获取radio值
    python中数组与多维数组用法介绍
    php开启与关闭错误提示
    asp.net url址址中中文汉字参数传递乱码解决方法
    js事件之event.preventDefault()与(www.111cn.net)event.stopPropagation()用法区别
    css中position:fixed实现div居中
  • 原文地址:https://www.cnblogs.com/strongmore/p/14157413.html
Copyright © 2011-2022 走看看