chore: snapshot production code before Apple Design UI refinement
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package dailygeo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Address struct{ Province, City, Region, Adcode string }
|
||||
type Resolver interface {
|
||||
Resolve(context.Context, float64, float64) (Address, error)
|
||||
}
|
||||
type AMap struct {
|
||||
Key, BaseURL string
|
||||
Client *http.Client
|
||||
}
|
||||
|
||||
func (a *AMap) Resolve(ctx context.Context, lng, lat float64) (Address, error) {
|
||||
if a.Key == "" {
|
||||
return Address{}, fmt.Errorf("AMAP_API_KEY is not configured")
|
||||
}
|
||||
base := a.BaseURL
|
||||
if base == "" {
|
||||
base = "https://restapi.amap.com"
|
||||
}
|
||||
u, e := url.Parse(base + "/v3/geocode/regeo")
|
||||
if e != nil {
|
||||
return Address{}, fmt.Errorf("invalid geocoder URL")
|
||||
}
|
||||
x, y := WGS84ToGCJ02(lng, lat)
|
||||
q := u.Query()
|
||||
q.Set("key", a.Key)
|
||||
q.Set("location", fmt.Sprintf("%.6f,%.6f", x, y))
|
||||
q.Set("extensions", "base")
|
||||
q.Set("radius", "1000")
|
||||
u.RawQuery = q.Encode()
|
||||
req, e := http.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
|
||||
if e != nil {
|
||||
return Address{}, fmt.Errorf("invalid geocoder request")
|
||||
}
|
||||
client := a.Client
|
||||
if client == nil {
|
||||
client = &http.Client{Timeout: 10 * time.Second}
|
||||
}
|
||||
resp, e := client.Do(req)
|
||||
if e != nil {
|
||||
return Address{}, fmt.Errorf("geocoder request failed")
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
return Address{}, fmt.Errorf("geocoder HTTP %d", resp.StatusCode)
|
||||
}
|
||||
var body struct {
|
||||
Status string
|
||||
Infocode string
|
||||
Regeocode struct {
|
||||
AddressComponent struct{ Province, City, District, Adcode json.RawMessage } `json:"addressComponent"`
|
||||
}
|
||||
}
|
||||
if e = json.NewDecoder(io.LimitReader(resp.Body, 1<<20)).Decode(&body); e != nil {
|
||||
return Address{}, fmt.Errorf("invalid geocoder response")
|
||||
}
|
||||
if body.Status != "1" {
|
||||
return Address{}, fmt.Errorf("geocoder rejected: %s", body.Infocode)
|
||||
}
|
||||
p, c := textValue(body.Regeocode.AddressComponent.Province), textValue(body.Regeocode.AddressComponent.City)
|
||||
if c == "" {
|
||||
switch p {
|
||||
case "北京市", "上海市", "天津市", "重庆市", "香港特别行政区", "澳门特别行政区":
|
||||
c = p
|
||||
}
|
||||
}
|
||||
// Province-administered county-level cities (for example 潜江市) are
|
||||
// returned in district, with city=[], by AMap.
|
||||
if c == "" && p != "" {
|
||||
district := textValue(body.Regeocode.AddressComponent.District)
|
||||
if strings.HasSuffix(district, "市") {
|
||||
c = district
|
||||
}
|
||||
}
|
||||
if p == "" || c == "" {
|
||||
return Address{}, fmt.Errorf("geocoder returned incomplete province/city")
|
||||
}
|
||||
return Address{p, c, Region(p), textValue(body.Regeocode.AddressComponent.Adcode)}, nil
|
||||
}
|
||||
func textValue(raw json.RawMessage) string {
|
||||
var s string
|
||||
if json.Unmarshal(raw, &s) == nil {
|
||||
return s
|
||||
}
|
||||
var ss []string
|
||||
if json.Unmarshal(raw, &ss) == nil {
|
||||
return strings.Join(ss, "")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
func Region(province string) string {
|
||||
groups := []struct {
|
||||
name string
|
||||
provinces []string
|
||||
}{
|
||||
{"华东", []string{"上海", "江苏", "浙江", "安徽", "福建", "江西", "山东", "台湾"}},
|
||||
{"华北", []string{"北京", "天津", "河北", "山西", "内蒙古"}},
|
||||
{"华中", []string{"河南", "湖北", "湖南"}},
|
||||
{"华南", []string{"广东", "广西", "海南", "香港", "澳门"}},
|
||||
{"东北", []string{"辽宁", "吉林", "黑龙江"}},
|
||||
{"西南", []string{"重庆", "四川", "贵州", "云南", "西藏"}},
|
||||
{"西北", []string{"陕西", "甘肃", "青海", "宁夏", "新疆"}},
|
||||
}
|
||||
for _, g := range groups {
|
||||
for _, p := range g.provinces {
|
||||
if strings.HasPrefix(province, p) {
|
||||
return g.name
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user