落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
149 lines
4.0 KiB
Go
149 lines
4.0 KiB
Go
package natal
|
||
|
||
import (
|
||
"strings"
|
||
"unicode/utf8"
|
||
)
|
||
|
||
// City is a built-in birth place with WGS84 coordinates.
|
||
type City struct {
|
||
Name string
|
||
Lat float64
|
||
Lng float64
|
||
}
|
||
|
||
// Cities lists common CN cities for MVP geocode (no network).
|
||
var Cities = []City{
|
||
{"北京", 39.9042, 116.4074},
|
||
{"上海", 31.2304, 121.4737},
|
||
{"广州", 23.1291, 113.2644},
|
||
{"深圳", 22.5431, 114.0579},
|
||
{"杭州", 30.2741, 120.1551},
|
||
{"成都", 30.5728, 104.0668},
|
||
{"重庆", 29.5630, 106.5516},
|
||
{"武汉", 30.5928, 114.3055},
|
||
{"西安", 34.3416, 108.9398},
|
||
{"南京", 32.0603, 118.7969},
|
||
{"天津", 39.3434, 117.3616},
|
||
{"苏州", 31.2989, 120.5853},
|
||
{"长沙", 28.2282, 112.9388},
|
||
{"郑州", 34.7466, 113.6254},
|
||
{"青岛", 36.0671, 120.3826},
|
||
{"大连", 38.9140, 121.6147},
|
||
{"厦门", 24.4798, 118.0894},
|
||
{"福州", 26.0745, 119.2965},
|
||
{"昆明", 25.0389, 102.7183},
|
||
{"贵阳", 26.6470, 106.6302},
|
||
{"南宁", 22.8170, 108.3665},
|
||
{"海口", 20.0440, 110.1999},
|
||
{"哈尔滨", 45.8038, 126.5349},
|
||
{"长春", 43.8171, 125.3235},
|
||
{"沈阳", 41.8057, 123.4315},
|
||
{"石家庄", 38.0428, 114.5149},
|
||
{"太原", 37.8706, 112.5489},
|
||
{"济南", 36.6512, 117.1201},
|
||
{"合肥", 31.8206, 117.2272},
|
||
{"南昌", 28.6820, 115.8579},
|
||
{"兰州", 36.0611, 103.8343},
|
||
{"乌鲁木齐", 43.8256, 87.6168},
|
||
{"拉萨", 29.6520, 91.1721},
|
||
{"呼和浩特", 40.8414, 111.7519},
|
||
{"银川", 38.4872, 106.2309},
|
||
{"西宁", 36.6171, 101.7782},
|
||
{"香港", 22.3193, 114.1694},
|
||
{"澳门", 22.1987, 113.5439},
|
||
{"台北", 25.0330, 121.5654},
|
||
{"宁波", 29.8683, 121.5440},
|
||
{"无锡", 31.4912, 120.3119},
|
||
{"佛山", 23.0215, 113.1214},
|
||
{"东莞", 23.0207, 113.7518},
|
||
{"温州", 27.9943, 120.6994},
|
||
{"泉州", 24.8741, 118.6759},
|
||
{"珠海", 22.2710, 113.5767},
|
||
}
|
||
|
||
// DefaultCoords is used when place is missing (东八区中部近似).
|
||
const DefaultLat = 30.0
|
||
const DefaultLng = 114.0
|
||
|
||
// ResolvePlace returns lat/lng and whether a named city matched.
|
||
// Accepts plain city ("杭州") or 省市区 ("浙江省 杭州市 西湖区" / "北京市 市辖区 朝阳区").
|
||
func ResolvePlace(place string) (lat, lng float64, matched string, ok bool) {
|
||
place = strings.TrimSpace(place)
|
||
if place == "" {
|
||
return DefaultLat, DefaultLng, "", false
|
||
}
|
||
// exact city table hit
|
||
for _, c := range Cities {
|
||
if c.Name == place {
|
||
return c.Lat, c.Lng, c.Name, true
|
||
}
|
||
}
|
||
tokens := splitPlace(place)
|
||
// prefer matching city-level tokens (usually 2nd), then others
|
||
order := append([]string{}, tokens...)
|
||
if len(tokens) >= 2 {
|
||
order = append([]string{tokens[1]}, tokens[0])
|
||
if len(tokens) >= 3 {
|
||
order = append(order, tokens[2:]...)
|
||
}
|
||
}
|
||
for _, tok := range order {
|
||
if tok == "" || tok == "市辖区" || tok == "县" {
|
||
continue
|
||
}
|
||
key := normalizeAdmin(tok)
|
||
for _, c := range Cities {
|
||
if c.Name == key || strings.Contains(tok, c.Name) || strings.Contains(c.Name, key) {
|
||
return c.Lat, c.Lng, c.Name, true
|
||
}
|
||
}
|
||
}
|
||
// province-only fallback: use first token city if 直辖市
|
||
if len(tokens) > 0 {
|
||
key := normalizeAdmin(tokens[0])
|
||
for _, c := range Cities {
|
||
if c.Name == key {
|
||
return c.Lat, c.Lng, c.Name, true
|
||
}
|
||
}
|
||
}
|
||
return DefaultLat, DefaultLng, "", false
|
||
}
|
||
|
||
func splitPlace(s string) []string {
|
||
s = strings.ReplaceAll(s, "/", " ")
|
||
s = strings.ReplaceAll(s, "/", " ")
|
||
s = strings.ReplaceAll(s, ",", " ")
|
||
s = strings.ReplaceAll(s, ",", " ")
|
||
parts := strings.Fields(s)
|
||
out := make([]string, 0, len(parts))
|
||
for _, p := range parts {
|
||
p = strings.TrimSpace(p)
|
||
if p != "" {
|
||
out = append(out, p)
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
func normalizeAdmin(s string) string {
|
||
s = strings.TrimSpace(s)
|
||
suffixes := []string{"特别行政区", "维吾尔自治区", "壮族自治区", "回族自治区", "自治区", "省", "市", "地区", "盟"}
|
||
for _, suf := range suffixes {
|
||
if strings.HasSuffix(s, suf) && utf8.RuneCountInString(s) > utf8.RuneCountInString(suf) {
|
||
return strings.TrimSuffix(s, suf)
|
||
}
|
||
}
|
||
return s
|
||
}
|
||
|
||
// CityNames returns names for API/UI pickers.
|
||
func CityNames() []string {
|
||
out := make([]string, len(Cities))
|
||
for i, c := range Cities {
|
||
out[i] = c.Name
|
||
}
|
||
return out
|
||
}
|