truckstop/zip/zip.go

113 lines
2.1 KiB
Go

package zip
import (
_ "embed"
"gogs.inhome.blapointe.com/local/truckstop/logtr"
"math"
"strconv"
"strings"
)
//go:embed testdata/simplemaps_uszips_basicv1.79/uszips.csv
var csv string
type Zip struct {
Lat float64
Lng float64
City string
State string
}
var zips map[string]Zip
func init() {
if len(zips) > 0 {
return
}
zips = map[string]Zip{}
trim := func(s string) string {
return strings.Trim(s, `"`)
}
for _, line := range strings.Split(csv, "\n")[1:] {
strs := strings.Split(line, ",")
if len(strs) < 5 {
continue
}
zip := trim(strs[0])
lat, _ := strconv.ParseFloat(trim(strs[1]), 32)
lng, _ := strconv.ParseFloat(trim(strs[2]), 32)
city := trim(alphafy(strs[3]))
state := strings.ToUpper(trim(strs[4]))
zips[zip] = Zip{
Lat: lat,
Lng: lng,
City: city,
State: state,
}
}
logtr.Infof("loaded %d zip codes", len(zips))
}
func alphafy(s string) string {
bs := make([]byte, 0, len(s)+1)
for i := range s {
if (s[i] < 'a' || s[i] > 'z') && (s[i] < 'A' || s[i] > 'Z') {
} else {
bs = append(bs, s[i])
}
}
return strings.ToLower(string(bs))
}
func (zip Zip) MilesTo(other Zip) int {
// c**2 = a**2 + b**2
// 69.2 mi per lat
// 60.0 mi per lng
return int(math.Sqrt(
math.Pow(69.2*(math.Abs(zip.Lat)-math.Abs(other.Lat)), 2) +
math.Pow(60.0*(math.Abs(zip.Lng)-math.Abs(other.Lng)), 2),
))
}
func Get(zip string) Zip {
if z, ok := zips[zip]; ok {
return z
}
zipAsI, err := strconv.Atoi(strings.Split(zip, "-")[0])
if err != nil {
return Zip{}
}
for i := 0; i < 5; i++ {
j := i - 2
if z2, ok := zips[strconv.Itoa(zipAsI+j)]; ok {
return z2
}
}
return Zip{}
}
func GetStatesWithin(zip Zip, miles int) []string {
states := map[string]struct{}{}
for _, other := range zips {
if zip.MilesTo(other) <= miles {
states[other.State] = struct{}{}
}
}
result := []string{}
for state := range states {
result = append(result, state)
}
return result
}
func FromCityState(city, state string) Zip {
city = alphafy(city)
state = strings.ToUpper(state)
for _, z := range zips {
if z.City == city && z.State == state {
return z
}
}
return Zip{}
}