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(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) { if jsonResult1 == nil { return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil") } 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 } func (a *API) GetCoordinateFromAddress(address string, cityInfo string) (lng, lat float64, districtCode int) { params := map[string]interface{}{ "address": address, } if cityInfo != "" { params["city"] = cityInfo } result, err := a.AccessAPI("geocode/geo", params) if err == nil { if geocodes, ok := result["geocodes"].([]interface{}); ok && len(geocodes) > 0 { geocode := geocodes[0].(map[string]interface{}) locationList := strings.Split(utils.Interface2String(geocode["location"]), ",") if len(locationList) > 1 { return utils.Str2Float64WithDefault(locationList[0], 0), utils.Str2Float64WithDefault(locationList[1], 0), int(utils.Str2Int64(utils.Interface2String(geocode["adcode"]))) } } } return lng, lat, 0 } // 这里的District指的是实际的District,有些市是没有区的,比如东莞,这种情况下返回的区码是一个假的区域,即市的编码加上9000000 func (a *API) GetCoordinateDistrictCode(lng, lat float64) (districtCode int) { result, err := a.GetCoordinateAreaInfo(lng, lat) 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 } func (a *API) GetCoordinateAreaInfo(lng, lat float64) (areaInfo map[string]interface{}, err error) { params := map[string]interface{}{ "location": fmt.Sprintf("%.6f,%.6f", lng, lat), } result, err := a.AccessAPI("geocode/regeo", params) return result, err } func (a *API) GetCoordinateTownInfo(lng, lat float64) (townName, townCode string) { result, err := a.GetCoordinateAreaInfo(lng, lat) // baseapi.SugarLogger.Debug(utils.Format4Output(result, false)) if err == nil { addressComponent := result["regeocode"].(map[string]interface{})["addressComponent"].(map[string]interface{}) townName = utils.Interface2String(addressComponent["township"]) townCode = utils.Interface2String(addressComponent["towncode"]) } return townName, townCode } // 这里的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] }