Files
digital-psychology/apps/api/internal/star/natal/cities.go
T
jackyu66gitandCursor bd22d9dddd feat: P1 合盘/星座/问答与测测完整设计包及模拟器取证工具
落地 synastry/star/ask API 与 H5 页面,补齐 cece-frontend-re complete-design 证据文档,并加入 Android 模拟器截图抓取脚本。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-03 11:37:53 +08:00

149 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}