Springboot 微信小程序定位后将坐标转换为百度地图坐标,在百度地图做逆地址解析

问题解析以及解决思路

业务:微信小程序定位后,将坐标转换为百度地图坐标,在百度地图做逆地址解析

问题:微信小程序的定位是拿的腾讯地图的经纬度,但是我们app端这边使用的百度地图,如果直接使用腾讯地图的经纬度再使用腾讯地图的逆地址解析需要腾讯和百度商业授权,为了减少授权成本

解决方案:将腾讯地图的经纬度转换为百度地图的经纬度后,再进行逆地址解析

  • 腾讯地图转百度地图此处有两种解决方案

坐标转换

全球逆地理编码

解决方案实现

开通百度地图相关接口权限

Springboot对接接口

  1. 新增接口相关的配置
yaml 复制代码
baidu:
  service: # 服务端应用
    appId: 1160xxxxx
    apiKey: C8NCMTxxxxxxxxxxxxxxxxxxx
  1. 百度接口统一返回对象封装
java 复制代码
import lombok.Data;

@Data
public class BaiduApiResponse<T> {
    private int status;
    private T result;
}
  1. 坐标转换返回对象封装
java 复制代码
import lombok.Data;

import java.util.List;

@Data
public class GeoConvResponseDto {
    private double x;
    private double y;

}
  1. 全球逆地理编码返回对象封装
java 复制代码
import lombok.Data;

import java.util.List;

@Data
public class ReverseGeocodingResponseDto {
    private Location location;
    private String formatted_address;
    private Edz edz;
    private String business;
    private AddressComponent addressComponent;
    private List<Object> pois;
    private List<Object> roads;
    private List<Object> poiRegions;
    private String sematic_description;
    private String formatted_address_poi;
    private int cityCode;

    @Data
    public static class Location {
        private double lng;
        private double lat;
    }

    @Data
    public static class Edz {
        private String name;
    }

    @Data
    public static class AddressComponent {
        private String country;
        private int country_code;
        private String country_code_iso;
        private String country_code_iso2;
        private String province;
        private String city;
        private int city_level;
        private String district;
        private String town;
        private String town_code;
        private String adcode;
        private String street;
        private String street_number;
    }
}
  1. 点位对象封装
java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class CoordinateDto {
    private double longitude;
    private double latitude;

    @Override
    public String toString() {
        return longitude + "," + latitude;
    }
}
  1. BaiduMapService(方法实现)
  • requestGeoConv参数解释
    • coords: 需转换的源坐标,多组坐标以";"我这里使用的coordinate对象重新了toString方法来构造接口参数
    • ak: 开发者密钥, 申请AK
    • model:
      • 转换方式可选值(默认值 1):
        1:amap/tencent to bd09ll
        2:gps to bd09ll
        3:bd09ll to bd09mc
        4:bd09mc to bd09ll
        5:bd09ll to amap/tencent
        6:bd09mc to amap/tencent
  • requestReverseGeocoding 参数解释
    • location: 根据经纬度坐标获取地址。(请注意这里是反过来的38.76623,116.43213lat<纬度>,lng<经度>)
    • ak: 开发者密钥, 申请AK
    • coordtype: 传入的坐标类型,目前支持的坐标类型包括:bd09ll(百度经纬度坐标)、bd09mc(百度米制坐标)、gcj02ll(国测局经纬度坐标,仅限中国)、wgs84ll( GPS经纬度)坐标系说明
      • 因为requestGeoConv方法已经给我们将经纬度转换为bd09ll标准了,所以我写死为bd09ll
    • extensions_poi: extensions_poi=0,不召回pois数据。
      extensions_poi=1,返回pois数据(默认显示周边1000米内的poi),并返回sematic_description语义化数据。
java 复制代码
package com.applets.manager.core.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.applets.manager.core.exception.BusinessException;
import com.applets.manager.core.model.dto.BaiduApiResponse;
import com.applets.manager.core.model.dto.CoordinateDto;
import com.applets.manager.core.model.dto.GeoConvResponseDto;
import com.applets.manager.core.model.dto.ReverseGeocodingResponseDto;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class BaiduMapService {
    private static final String GEO_CONV_URL = "https://api.map.baidu.com/geoconv/v2/?";
    private static final String REVERSE_GEOCODING_URL = "https://api.map.baidu.com/reverse_geocoding/v3?";

    @Value("${baidu.service.appId}")
    private String appId;

    @Value("${baidu.service.apiKey}")
    private String apiKey;
    /**
     * 发送请求到百度地图 API,执行坐标转换
     *
     * @param coordinate  点位对象,即经纬度对象
     * @param model  转换模型
     * @return API 响应结果
     * @throws Exception
     */
    public BaiduApiResponse<List<GeoConvResponseDto>> requestGeoConv(CoordinateDto coordinate, String model) throws Exception {
        Map<String, String> params = new LinkedHashMap<>();
        params.put("coords", coordinate.toString());
        params.put("model", model);
        params.put("ak", apiKey);
        String res = sendGetRequest(GEO_CONV_URL, params);
        BaiduApiResponse<List<GeoConvResponseDto>> response = null;
        if (StringUtils.isNotEmpty(res)){
            response = JSON.parseObject(res, new TypeReference<BaiduApiResponse<List<GeoConvResponseDto>>>() {});
            if (0 != response.getStatus()){
                log.info("坐标转换结果:{}", response);
                throw new BusinessException("百度接口请求错误:"+response.getResult());
            }
        }

        return response;
    }

    /**
     * 发送请求到百度地图 API,执行逆地理编码
     *
     * @param coordinate  点位对象,即经纬度对象
     * @return API 响应结果
     * @throws Exception
     */
    public BaiduApiResponse<ReverseGeocodingResponseDto> requestReverseGeocoding(CoordinateDto coordinate) throws Exception {
        Map<String, String> params = new LinkedHashMap<>();
        params.put("ak", apiKey);
        params.put("output", "json");
        params.put("coordtype", "bd09ll");
        params.put("extensions_poi", "0");
        params.put("location", coordinate.getLatitude()+","+coordinate.getLongitude());

        String res = sendGetRequest(REVERSE_GEOCODING_URL, params);

        BaiduApiResponse<ReverseGeocodingResponseDto> response = null;
        if (StringUtils.isNotEmpty(res)){
            response = JSON.parseObject(res, new TypeReference<BaiduApiResponse<ReverseGeocodingResponseDto>>() {});
            if (0 != response.getStatus()){
                log.info("坐标转换结果:{}", response);
                throw new BusinessException("百度接口请求错误:"+response.getResult());
            }
        }
        return response;
    }

    /**
     * 通用的 GET 请求方法
     *
     * @param strUrl API URL
     * @param param 请求参数
     * @return 响应字符串
     * @throws Exception
     */
    private String sendGetRequest(String strUrl, Map<String, String> param) throws Exception {
        if (strUrl == null || strUrl.isEmpty() || param == null || param.isEmpty()) {
            throw new IllegalArgumentException("URL 和参数不能为空");
        }

        StringBuilder queryString = new StringBuilder(strUrl);
        for (Map.Entry<String, String> entry : param.entrySet()) {
            queryString.append(entry.getKey()).append("=")
                    .append(UriUtils.encode(entry.getValue(), "UTF-8")).append("&");
        }

        // 删除最后一个 '&' 字符
        queryString.deleteCharAt(queryString.length() - 1);

        URL url = new URL(queryString.toString());
        HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
        httpConnection.connect();

        try (InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream());
             BufferedReader reader = new BufferedReader(isr)) {

            StringBuilder response = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                response.append(line);
            }
            log.info("BaiduMapService.sendGetRequest.response:{}",response.toString());

            return response.toString();
        }
    }

}

测试

java 复制代码
import com.alibaba.fastjson.JSON;
import com.applets.manager.api.StartApplication;
import com.applets.manager.core.model.dto.BaiduApiResponse;
import com.applets.manager.core.model.dto.CoordinateDto;
import com.applets.manager.core.model.dto.GeoConvResponseDto;
import com.applets.manager.core.model.dto.ReverseGeocodingResponseDto;
import com.applets.manager.core.service.BaiduMapService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.geotools.referencing.GeodeticCalculator;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
import org.springframework.web.util.UriUtils;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author zr 2024/4/17
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = StartApplication.class)
@Slf4j
public class BaiduMapTest {

    @Autowired
    private BaiduMapService baiduMapService;

    @Test
    public void requestGeoConv() {
        try {
            BaiduApiResponse<List<GeoConvResponseDto>> geoConvResponseDtoBaiduApiResponse = baiduMapService.requestGeoConv(new CoordinateDto(103.979967,30.990777), "1");
            log.info("requestGeoConv: " + JSON.toJSONString(geoConvResponseDtoBaiduApiResponse));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Test
    public void requestReverseGeocoding() {
        try {
            BaiduApiResponse<ReverseGeocodingResponseDto> response = baiduMapService.requestReverseGeocoding(new CoordinateDto(103.98657912268618,30.996470768223653));
            log.info("requestReverseGeocoding: " + JSON.toJSONString(response));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

坐标转换测试

初始点位是我随便再腾讯地图上找的点位

注意:这里百度地图返回的地址是一个list

全球逆地理编码测试

使用第一步转换的结果进行逆地址解析

返回json实例

json 复制代码
{"result":{"addressComponent":{"adcode":"510182","city":"成都市","city_level":2,"country":"中国","country_code":0,"country_code_iso":"CHN","country_code_iso2":"CN","district":"彭州市","province":"四川省","street":"什彭路","street_number":"","town":"天彭街道","town_code":"510182001"},"business":"","cityCode":75,"edz":{"name":""},"formatted_address":"四川省成都市彭州市什彭路","formatted_address_poi":"","location":{"lat":30.99647061454463,"lng":103.98657912268614},"poiRegions":[],"pois":[],"roads":[],"sematic_description":""},"status":0}
相关推荐
rADu REME7 分钟前
SpringBoot + vue 管理系统
vue.js·spring boot·后端
你好潘先生31 分钟前
Next.js + Spring Boot 实现 AI 多模型并行对话系统(架构设计与关键实现)
spring boot·向量检索·next.js·pgvector·ai对话·多模型对比·sse流式输出
苍煜32 分钟前
SpringBoot单体应用到分布式下的数据库锁、事务、Redis事务、分布式锁、分布式事务协调
数据库·spring boot·分布式
Dylan的码园35 分钟前
springBoot与Web后端基础
前端·spring boot·后端
skiy1 小时前
SpringBoot项目中读取resource目录下的文件(六种方法)
spring boot·python·pycharm
salipopl1 小时前
Spring Boot 整合 Druid 并开启监控
java·spring boot·后端
geNE GENT1 小时前
Spring Boot 实战篇(四):实现用户登录与注册功能
java·spring boot·后端
HackTorjan11 小时前
深度神经网络的反向传播与梯度优化原理
人工智能·spring boot·神经网络·机器学习·dnn
直奔標竿21 小时前
Java开发者AI转型第二十五课!Spring AI 个人知识库实战(四)——RAG来源追溯落地,拒绝AI幻觉
java·开发语言·人工智能·spring boot·后端·spring
敖正炀1 天前
WebFlux 深度:Reactor 线程模型、背压与错误处理
spring boot