百度逆地理编码

百度全球逆地理编码 golang 版本 #

处理过程 golang 版本 #

百度这边请求参数处理时(url.QueryExcape)需要保留部分字符不处理,特殊操作如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
const BaiduMapAk = "yourak"
const BaiduMapSk = "yoursk"
const BaiduMapHost = "https://api.map.baidu.com"

// Coordinate2Region 百度文档有些转换上的问题,粗暴拼接的代码或许就是最佳实践 https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-abroad-base
func Coordinate2Region[T ~string | ~float64 | ~float32](lat, lng T) (BaiduCoordinate, error) {
    // 38.76623,116.43213 lat<纬度>,lng<经度>
    queryParams := fmt.Sprintf("/reverse_geocoding/v3?ak=%v&output=%v&coordtype=%v&extensions_poi=%v&location=%v", BaiduMapAk, "json", "wgs84ll", "0", fmt.Sprintf("%v,%v", lat, lng))
    sn := BaiduSn(queryParams, BaiduMapSk)

    resp, err := http.Get(BaiduMapHost + queryParams + "&sn=" + sn)
    if err != nil {
        return BaiduCoordinate{}, errors.Wrap(err, "failed to get baidu map host")
    }
    defer resp.Body.Close()

    body, err2 := io.ReadAll(resp.Body)
    if err2 != nil {
        return BaiduCoordinate{}, errors.Wrap(err, "response error:")
    }

    coordinage := BaiduCoordinate{}
    if err = json.Unmarshal(body, &coordinage); err != nil {
        logrus.Error("GetCertSnapShot err ", err)
        return BaiduCoordinate{}, errors.Wrap(err, "failed to unmarshal response body")
    }

    return coordinage, nil
}

// BaiduSn https://lbsyun.baidu.com/faq/api?title=lbscloud/api/appendix#sn%E8%AE%A1%E7%AE%97%E7%AE%97%E6%B3%95
func BaiduSn(queryStr, sk string) string {
    logrus.Debugf("query str: %v %v", queryStr, sk)
    queryStr = baiduSnQuote(queryStr, "/:=&?#+!$,;'@()*[]")
    rawStr := queryStr + sk

    hash := md5.Sum([]byte(url.QueryEscape(rawStr)))
    md5String := hex.EncodeToString(hash[0:]) // 将结果转换为十六进制字符串

    return md5String
}

// 粗暴模拟 Python urllib.quote() 的方法
func baiduSnQuote(s, safe string) string {
    //for _, val := range safe {
    //	logrus.Infof("%v %s", string(val), url.QueryEscape(string(val)))
    //}
    query := url.QueryEscape(s)
    query = strings.ReplaceAll(query, `%2F`, `/`)
    query = strings.ReplaceAll(query, `%3A`, `:`)
    query = strings.ReplaceAll(query, `%3D`, `=`)
    query = strings.ReplaceAll(query, `%26`, `&`)
    query = strings.ReplaceAll(query, `%3F`, `?`)
    query = strings.ReplaceAll(query, `%23`, `#`)
    query = strings.ReplaceAll(query, `%2B`, `+`)
    query = strings.ReplaceAll(query, `%21`, `!`)
    query = strings.ReplaceAll(query, `%24`, `$`)
    query = strings.ReplaceAll(query, `%2C`, `,`)
    query = strings.ReplaceAll(query, `%3B`, `;`)
    query = strings.ReplaceAll(query, `%27`, `'`)
    query = strings.ReplaceAll(query, `%40`, `@`)
    query = strings.ReplaceAll(query, `%28`, `(`)
    query = strings.ReplaceAll(query, `%29`, `)`)
    query = strings.ReplaceAll(query, `%2A`, `*`)
    query = strings.ReplaceAll(query, `%5B`, `[`)

    return query
}

响应体定义 #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
type BaiduCoordinate struct {
    Status int `json:"status"`
    Result struct {
        Location struct {
            Lng float64 `json:"lng"`
            Lat float64 `json:"lat"`
        } `json:"location"`
        FormattedAddress string `json:"formatted_address"`
        Edz              struct {
            Name string `json:"name"`
        } `json:"edz"`
        Business         string `json:"business"`
        AddressComponent struct {
            Country         string `json:"country"`
            CountryCode     int    `json:"country_code"`
            CountryCodeIso  string `json:"country_code_iso"`
            CountryCodeIso2 string `json:"country_code_iso2"`
            Province        string `json:"province"`
            City            string `json:"city"`
            CityLevel       int    `json:"city_level"`
            District        string `json:"district"`
            Town            string `json:"town"`
            TownCode        string `json:"town_code"`
            Distance        string `json:"distance"`
            Direction       string `json:"direction"`
            Adcode          string `json:"adcode"`
            Street          string `json:"street"`
            StreetNumber    string `json:"street_number"`
        } `json:"addressComponent"`
        Pois                []interface{} `json:"pois"`
        Roads               []interface{} `json:"roads"`
        PoiRegions          []interface{} `json:"poiRegions"`
        SematicDescription  string        `json:"sematic_description"`
        FormattedAddressPoi string        `json:"formatted_address_poi"`
        CityCode            int           `json:"cityCode"`
    } `json:"result"`
}

python encode 部分 #

python 2.7 url encode 官方参考代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# -*- coding: utf-8 -*- 
  # 第一行必须有,否则报中文字符非ascii码错误
  import urllib
  import hashlib
  
  # 以get请求为例http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak
  queryStr = '/geocoder/v2/?address=百度大厦&output=json&ak=yourak'
  
  # 对queryStr进行转码,safe内的保留字符不转换
  encodedStr = urllib.quote(queryStr, safe="/:=&?#+!$,;'@()*[]")
  
  # 在最后直接追加上yoursk
  rawStr = encodedStr + 'yoursk'
  
  # md5计算出的sn值7de5a22212ffaa9e326444c75a58f9a0
  # 最终合法请求url是http://api.map.baidu.com/geocoder/v2/?address=百度大厦&output=json&ak=yourak&sn=7de5a22212ffaa9e326444c75a58f9a0
  print hashlib.md5(urllib.quote_plus(rawStr)).hexdigest()

https://lbsyun.baidu.com/faq/api?title=webapi/guide/webservice-geocoding-abroad-base