使用高德地图API根据地址名称获取经纬度(Go语言)

天地图、腾讯地图、高德地图等都提供对外开放的API,这里主要针对高德地图,进行地理编码接口的调用。
高德地图web API官方文档链接,根据文档的步骤,构造对应的请求URL
https://restapi.amap.com/v3/geocode/geo?parameters

背景

地理编码表:共五级行政区域,分别为省/直辖市、区/县、街道/镇、社区/乡村

地理编码表中各地理编码和高德地图提供的地理编码不完全相同

方法

按级别分批获取地理编码表中的地址名称,根据pcode构造结构化地址信息,作为参数address的值,默认返回格式为json,判断返回的地理信息匹配级别是否对应,将匹配正确的经纬度信息插入数据库。

匹配级别和五级行政区域名称相差,但是返回数据合理的,可以将该匹配级别加入ignore list

Go语言代码如下:

package main

import (
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "net/http"
    "strconv"
    "strings"

    "gorm.io/driver/mysql"
    "gorm.io/gorm"
)

// Area 地理编码表结构
type Area struct {
    ID            int     `json:"id" gorm:"column:id; PRIMARY_KEY; AUTO_INCREMENT"`
    Code          int64   `json:"code" gorm:"column:code;type:bigint(12)"`
    Name          string  `json:"name" gorm:"column:name; type:varchar(50); default:''"`
    Pcode         int64   `json:"pcode" gorm:"column:pcode;type:bigint(12)"`
    ProvinceCode  int64   `json:"province_code" gorm:"column:province_code;type:bigint(12)"`
    CityCode      int64   `json:"city_code" gorm:"column:city_code;type:bigint(12)"`
    AreaCode      int64   `json:"area_code" gorm:"column:area_code;type:bigint(12)"`
    StreetCode    int64   `json:"street_code" gorm:"column:street_code;type:bigint(12)"`
    CommitteeCode int64   `json:"committee_code" gorm:"column:committee_code;type:bigint(12)"`
    CommitteeType int64   `json:"committee_type" gorm:"column:committee_type;type:bigint(12)"`
    Sort          int     `json:"sort" gorm:"column:sort; type:int(2); default:1"`
    Level         int     `json:"level" gorm:"column:level; type:int(2); default:1"`
    Longitude     float64 `json:"longitude" gorm:"column:longitude; type:decimal(10); default:NULL"`
    Latitude      float64 `json:"latitude" gorm:"column:latitude; type:decimal(10); default:NULL"`
}

// TableName  return table name
func (Area) TableName() string {
    return "tbl_area"
}

// GetAreaMapByIDS 根据地区id获取 map[id]area结构
func GetAreaMapByIDS(db *gorm.DB, IDS []int) (map[int]Area, error) {
    var res []Area
    r := db.Model(&Area{}).Where("id in (?)", IDS).Find(&res)

    if r.Error != nil {
        return nil, r.Error
    }

    areaMap := make(map[int]Area)

    for _, val := range res {
        areaMap[val.ID] = Area{
            ID:        val.ID,
            Name:      val.Name,
            Code:      val.Code,
            Pcode:     val.Pcode,
            Level:     val.Level,
            Longitude: val.Longitude,
            Latitude:  val.Latitude,
        }
    }
    return areaMap, nil
}

// GetAreaNameMapByCodeS 根据地区code获取 mapd[code]name 结构
func GetAreaNameMapByCodeS(db *gorm.DB, codeS []int64) (map[int64]string, error) {
    var res []Area
    r := db.Model(&Area{}).Where("code in (?)", codeS).Find(&res)

    if r.Error != nil {
        return nil, r.Error
    }

    areaMap := make(map[int64]string)

    for _, val := range res {
        areaMap[val.Code] = val.Name
    }
    return areaMap, nil
}

// GetProvinceNameMapByCodeS 根据省code获取 map[id]name 结构
func GetProvinceNameMapByCodeS(db *gorm.DB, codeS []int64) (map[int64]string, error) {
    var res []Area
    r := db.Model(&Area{}).Where("code in (?)", codeS).Find(&res)

    if r.Error != nil {
        return nil, r.Error
    }

    areaMap := make(map[int64]string)

    for _, val := range res {
        areaMap[val.ProvinceCode] = val.Name
    }
    return areaMap, nil
}

// GetCityNameMapByCodeS 根据市code获取 map[id]name 结构
func GetCityNameMapByCodeS(db *gorm.DB, codeS []int64) (map[int64]string, error) {
    var res []Area
    r := db.Model(&Area{}).Where("code in (?)", codeS).Find(&res)

    if r.Error != nil {
        return nil, r.Error
    }

    areaMap := make(map[int64]string)

    for _, val := range res {
        areaMap[val.CityCode] = val.Name
    }
    return areaMap, nil
}

// GetAreaNameMapByCodeS2 根据区县code获取 map[id]name 结构
func GetAreaNameMapByCodeS2(db *gorm.DB, codeS []int64) (map[int64]string, error) {
    var res []Area
    r := db.Model(&Area{}).Where("code in (?)", codeS).Find(&res)

    if r.Error != nil {
        return nil, r.Error
    }

    areaMap := make(map[int64]string)

    for _, val := range res {
        areaMap[val.AreaCode] = val.Name
    }
    return areaMap, nil
}

// RequetForFile request for file
func RequetForFile(url string) (string, error) {
    var bodyStr string
    // Get the data
    resp, err := http.Get(url)
    if err != nil {
        return bodyStr, err
    }
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return bodyStr, err
    }
    bodyStr = string(body)
    return bodyStr, nil
}

func main() {
    var province string
    var areaLevel string
    var areaName string
    var areaNick string
    flag.StringVar(&province, "p", "", "省") //五级需要
    flag.StringVar(&areaLevel, "l", "", "地区级别")
    flag.StringVar(&areaName, "n", "", "地区级别名称")
    flag.StringVar(&areaNick, "k", "", "地区级别名称别名")
    // 解析
    flag.Parse()
    dsn := fmt.Sprintf(
        "%s:%s@tcp(%s)/%s?parseTime=True&loc=Local",
        LocalUser,
        LocalPassword,
        LocalHost,
        LocalDatabase,
    )
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        fmt.Println(err)
    }
    var res []Area
    //省、直辖市
    //sqlStr := fmt.Sprintf("select * from %s where level=%s and longitude is null ", Table, areaLevel)
    //市
    //sqlStr := fmt.Sprintf("select * from %s where level=%s and name not in('市辖区','省直辖县级行政区划','县') ", Table, areaLevel)
    //区、县
    //sqlStr := fmt.Sprintf("select * from %s where level=%s  and name not in('市辖区') and longitude is null", Table, areaLevel)
    //街道、镇
    //sqlStr := fmt.Sprintf("select * from %s where level=%s  and  longitude is null", Table, areaLevel)
    //社区、乡村
    sqlStr := fmt.Sprintf("select * from %s where level=%s  and  longitude is null and province_code = %s", Table, areaLevel, province)
    fmt.Println("--this is sqlstr--")
    fmt.Println(sqlStr)
    db.Raw(sqlStr).Scan(&res)
    pCodeList := make([]int64, 0, len(res))
    // provinceList := make([]int64, 0, len(res))
    cityList := make([]int64, 0, len(res))
    areaList := make([]int64, 0, len(res))
    for _, v := range res {
        cityTemp := ZeroFillByStr(strconv.FormatInt(v.CityCode, 10), 12, false)
        cityTempInt, _ := strconv.ParseInt(cityTemp, 10, 64)
        cityList = append(cityList, cityTempInt)
        areaTemp := ZeroFillByStr(strconv.FormatInt(v.AreaCode, 10), 12, false)
        areaTempInt, _ := strconv.ParseInt(areaTemp, 10, 64)
        areaList = append(areaList, areaTempInt)
        pCodeList = append(pCodeList, v.Pcode)
    }
    pAreaMap, err := GetAreaNameMapByCodeS(db, pCodeList)
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("--this is p map--")
    fmt.Println(pAreaMap)
    provinceCode := ZeroFillByStr(province, 12, false)
    tempInt, _ := strconv.ParseInt(provinceCode, 10, 64)
    proAreaMap, err := GetProvinceNameMapByCodeS(db, []int64{tempInt})
    if err != nil {
        fmt.Println(err)
        return
    }
    cityAreaMap, err := GetCityNameMapByCodeS(db, cityList)
    if err != nil {
        fmt.Println(err)
        return
    }
    areaMap, err := GetAreaNameMapByCodeS2(db, areaList)
    if err != nil {
        fmt.Println(err)
        return
    }
    for _, val := range res {
        fmt.Println(val)
        urlString := fmt.Sprintf(AmapApi, proAreaMap[val.ProvinceCode]+cityAreaMap[val.CityCode]+areaMap[val.AreaCode]+pAreaMap[val.Pcode]+val.Name, Key)
        fmt.Println("---this is request url--")
        fmt.Println(urlString)
        bodyResp, err := RequetForFile(urlString)
        if err != nil {
            fmt.Println(err)
        }
        var dataInfo map[string]interface{}
        var location string
        if err := json.Unmarshal([]byte(bodyResp), &dataInfo); err != nil {
            fmt.Println(err)
        }
        fmt.Println("--this is resp---")
        fmt.Println(dataInfo)
        for idx, value := range dataInfo {
            if idx == "status" {
                if value.(string) != "1" {
                    return
                }
            }
            if idx == "geocodes" {
                mapTemp := value.([]interface{})
                temp := mapTemp[0].(map[string]interface{})
                //  && temp["level"].(string) != LevelFour  && temp["level"].(string) != DevLevel
                if temp["level"].(string) != areaName && temp["level"].(string) != areaNick{
                    return
                }
                location = temp["location"].(string)
            }
        }
        locationList := strings.Split(location, ",")
        fmt.Println("----this is location --")
        updateSql := fmt.Sprintf("UPDATE %s SET longitude = %s, latitude = %s where id = %d ", Table, locationList[0], locationList[1], val.ID)
        fmt.Println("---this is sql str---")
        fmt.Println(updateSql)
        db.Exec(updateSql)
        if db.Error != nil {
            fmt.Println(db.Error)
        }
    }

}

const (
    AmapApi       = "https://restapi.amap.com/v3/geocode/geo?address=%s&key=%s"
    LocalHost     = "127.0.0.1"
    LocalDatabase = "xxx"
    LocalPort     = "3306"
    LocalUser     = "xxx"
    LocalPassword = "xxx"
    Key           = "xxx"
)

// ZeroFillByStr 字符串前置/后置补零
func ZeroFillByStr(str string, resultLen int, reverse bool) string {
    if len(str) > resultLen || resultLen <= 0 {
        return str
    }
    if reverse {
        return fmt.Sprintf("%0*s", resultLen, str) //不足前置补零
    }
    result := str
    for i := 0; i < resultLen-len(str); i++ {
        result += "0"
    }
    return result
}

版权声明:本博客所有文章除特别声明外,均采用 CC BY 4.0许可协议,转载请注明出处
本文链接:https://blog.redamancy.tech/technique/16