Files
baseapi/platformapi/autonavi/autonavi.go

247 lines
7.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package autonavi
import (
"crypto/md5"
"fmt"
"net/http"
"sort"
"strings"
"git.rosy.net.cn/baseapi/platformapi"
"git.rosy.net.cn/baseapi/utils"
)
const (
signKey = "sig"
prodURL = "https://restapi.amap.com/v3"
)
const (
StatusCodeFailed = "0"
StatusCodeSuccess = "1"
)
const (
InfoCode_OK = "10000"
InfoCode_ACCESS_TOO_FREQUENT = "10004"
InfoCode_QPS_HAS_EXCEEDED_THE_LIMIT = "10014"
InfoCode_SERVER_IS_BUSY = "10016"
InfoCode_RESOURCE_UNAVAILABLE = "10017"
InfoCode_CQPS_HAS_EXCEEDED_THE_LIMIT = "10019"
InfoCode_CKQPS_HAS_EXCEEDED_THE_LIMIT = "10020"
InfoCode_CIQPS_HAS_EXCEEDED_THE_LIMIT = "10021"
InfoCode_CIKQPS_HAS_EXCEEDED_THE_LIMIT = "10022"
InfoCode_KQPS_HAS_EXCEEDED_THE_LIMIT = "10023"
)
const (
CoordSysAutonavi = "autonavi"
CoordSysGPS = "gps"
CoordSysMapbar = "mapbar"
CoordSysBaidu = "baidu"
)
const (
FakeDistrictPadding = 9000000
)
var (
exceedLimitCodes = map[string]int{
InfoCode_ACCESS_TOO_FREQUENT: 1,
InfoCode_QPS_HAS_EXCEEDED_THE_LIMIT: 1,
InfoCode_CQPS_HAS_EXCEEDED_THE_LIMIT: 1,
InfoCode_CKQPS_HAS_EXCEEDED_THE_LIMIT: 1,
InfoCode_CIQPS_HAS_EXCEEDED_THE_LIMIT: 1,
InfoCode_CIKQPS_HAS_EXCEEDED_THE_LIMIT: 1,
InfoCode_KQPS_HAS_EXCEEDED_THE_LIMIT: 1,
}
canRetryCodes = map[string]int{
InfoCode_SERVER_IS_BUSY: 1,
}
)
const (
DistrictLevelCountry = 0
DistrictLevelProvince = 1
DistrictLevelCity = 2
DistrictLevelDistrict = 3
DistrictLevelStreet = 4
)
var (
levelStr2IntMap = map[string]int{
"country": DistrictLevelCountry,
"province": DistrictLevelProvince,
"city": DistrictLevelCity,
"district": DistrictLevelDistrict,
"street": DistrictLevelStreet,
}
)
type District struct {
Adcode string `json:"adcode"` // 国家行政编码
Lng float64 `json:"lng"`
Lat float64 `json:"lat"`
CityCode string `json:"citycode"` // 电话区号
Level int `json:"level"`
Name string `json:"name"`
Districts []*District `json:"districts"`
}
type ResponseResult map[string]interface{}
type API struct {
client *http.Client
config *platformapi.APIConfig
key string
}
func New(key string, config ...*platformapi.APIConfig) *API {
curConfig := platformapi.DefAPIConfig
if len(config) > 0 {
curConfig = *config[0]
}
return &API{
key: key,
client: &http.Client{Timeout: curConfig.ClientTimeout},
config: &curConfig,
}
}
func (a *API) signParams(mapData map[string]interface{}) string {
keys := make([]string, 0)
for k := range mapData {
if k != signKey {
keys = append(keys, k)
}
}
sort.Strings(keys)
finalStr := ""
for _, k := range keys {
finalStr += k + "=" + fmt.Sprint(mapData[k])
}
// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
}
func (a *API) AccessAPI(apiStr string, params map[string]interface{}) (retVal ResponseResult, err error) {
params2 := utils.MergeMaps(utils.Params2Map("key", a.key, "output", "json"), params)
params2[signKey] = a.signParams(params2)
err = platformapi.AccessPlatformAPIWithRetry(a.client,
func() *http.Request {
request, _ := http.NewRequest(http.MethodGet, utils.GenerateGetURL(prodURL, apiStr, params2), nil)
return request
},
a.config,
func(jsonResult1 map[string]interface{}) (errLevel string, err error) {
status := jsonResult1["status"].(string)
if status == StatusCodeSuccess {
retVal = jsonResult1
return platformapi.ErrLevelSuccess, nil
}
infoCode := jsonResult1["infocode"].(string)
newErr := utils.NewErrorCode(jsonResult1["info"].(string), infoCode)
if _, ok := exceedLimitCodes[infoCode]; ok {
return platformapi.ErrLevelExceedLimit, newErr
} else if _, ok := canRetryCodes[infoCode]; ok {
return platformapi.ErrLevelRecoverableErr, newErr
} else {
return platformapi.ErrLevelCodeIsNotOK, newErr
}
})
return retVal, err
}
// 为了方便调用者编码,如果失败,也会返回未转换的原始值
func (a *API) CoordinateConvert(lng, lat float64, coordsys string) (retLng, retLat float64, err error) {
if coordsys == "" || coordsys == CoordSysAutonavi {
return lng, lat, nil
}
if lng == 0.0 && lat == 0.0 {
return 0.0, 0.0, nil
}
params := map[string]interface{}{
"locations": fmt.Sprintf("%.6f,%.6f", lng, lat),
"coordsys": coordsys,
}
result, err := a.AccessAPI("assistant/coordinate/convert", params)
if err == nil {
coordinate := result["locations"].(string)
index := strings.Index(coordinate, ",")
return utils.Str2Float64(coordinate[:index]), utils.Str2Float64(coordinate[index+1:]), nil
}
return lng, lat, err
}
// 这里的District指的是实际的District有些市是没有区的比如东莞这种情况下返回的区码是一个假的区域即市的编码加上9000000
func (a *API) GetCoordinateDistrictCode(lng, lat float64) (districtCode int) {
params := map[string]interface{}{
"location": fmt.Sprintf("%.6f,%.6f", lng, lat),
}
result, err := a.AccessAPI("geocode/regeo", params)
// baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
if err == nil {
addressComponent := result["regeocode"].(map[string]interface{})["addressComponent"].(map[string]interface{})
if strAdcode, ok := addressComponent["adcode"].(string); ok {
districtCode = int(utils.Str2Int64WithDefault(strAdcode, 0))
if _, ok := addressComponent["district"].(string); !ok {
districtCode += FakeDistrictPadding
}
}
}
return districtCode
}
// 这里的District指的是地点不是实际上的区具体级别看level
// 这个函数返回的可能不是同级别的地点
func (a *API) GetDistricts(subDistrict int, keywords string) (districtList []*District, err error) {
params := map[string]interface{}{
"subdistrict": subDistrict,
}
if keywords != "" {
params["keywords"] = keywords
}
result, err := a.AccessAPI("config/district", params)
// baseapi.SugarLogger.Debug(utils.Format4Output(result, false))
if err == nil {
return a.getDistrictsFromInterface(result["districts"]), nil
}
return nil, err
}
func (a *API) getDistrictsFromInterface(districts interface{}) (districtList []*District) {
if districts != nil {
districts2 := districts.([]interface{})
districtList = make([]*District, len(districts2))
for k, v := range districts2 {
v2 := v.(map[string]interface{})
districtList[k] = &District{
Adcode: utils.Interface2String(v2["adcode"]),
Name: utils.Interface2String(v2["name"]),
Level: GetDistrictLevel(utils.Interface2String(v2["level"])),
}
coordStrNodes := strings.Split(utils.Interface2String(v2["center"]), ",")
if len(coordStrNodes) == 2 {
if coordStrNodes[0] != "" && coordStrNodes[1] != "" {
districtList[k].Lng = utils.Str2Float64(coordStrNodes[0])
districtList[k].Lat = utils.Str2Float64(coordStrNodes[1])
}
}
if cityCodeStr, ok := v2["citycode"].(string); ok {
districtList[k].CityCode = cityCodeStr
}
districtList[k].Districts = a.getDistrictsFromInterface(v2["districts"])
}
}
return districtList
}
func GetDistrictLevel(levelName string) (level int) {
return levelStr2IntMap[levelName]
}