Files
baseapi/platformapi/autonavi/autonavi.go
2018-12-04 16:44:24 +08:00

238 lines
6.7 KiB
Go

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"
)
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
)
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(response *http.Response) (errLevel string, err error) {
jsonResult1, err := utils.HTTPResponse2Json(response)
if err != nil {
return platformapi.ErrLevelGeneralFail, platformapi.ErrResponseDataFormatWrong
}
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
}
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
}
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))
}
}
return districtCode
}
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"].([]interface{})[0].(map[string]interface{})["districts"]), nil
}
return nil, err
}
func (a *API) GetDistrictsFromInterface(districts interface{}) (districtList []*District) {
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) {
switch levelName {
case "country":
level = 0
case "province":
level = 1
case "city":
level = 2
case "district":
level = 3
case "street":
level = 4
}
return level
}