Adds a new endpoint GET /api/tasks/templates/by-region/?zip= that resolves ZIP codes to IECC climate regions and returns relevant home maintenance task templates. Includes climate region model, region lookup service with tests, seed data for all 8 climate zones with 50+ templates, and OpenAPI spec. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
158 lines
4.1 KiB
Go
158 lines
4.1 KiB
Go
package services
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// StateToClimateRegion maps US state abbreviations to IECC climate zone IDs.
|
|
// Some states span multiple zones — this uses the dominant zone for the most populated areas.
|
|
var StateToClimateRegion = map[string]uint{
|
|
// Zone 1: Hot-Humid
|
|
"HI": 1, "FL": 1, "LA": 1,
|
|
// Zone 2: Hot-Dry / Hot-Humid mix
|
|
"TX": 2, "AZ": 2, "NV": 2, "NM": 2,
|
|
// Zone 3: Mixed-Humid
|
|
"GA": 3, "SC": 3, "AL": 3, "MS": 3, "AR": 3, "NC": 3, "TN": 3, "OK": 3, "CA": 3,
|
|
// Zone 4: Mixed
|
|
"VA": 4, "KY": 4, "MO": 4, "KS": 4, "DE": 4, "MD": 4, "DC": 4, "WV": 4, "OR": 4,
|
|
// Zone 5: Cold
|
|
"NJ": 5, "PA": 5, "CT": 5, "RI": 5, "MA": 5, "OH": 5, "IN": 5, "IL": 5,
|
|
"IA": 5, "NE": 5, "CO": 5, "UT": 5, "WA": 5, "ID": 5, "NY": 5, "MI": 5,
|
|
// Zone 6: Very Cold
|
|
"WI": 6, "MN": 6, "ND": 6, "SD": 6, "MT": 6, "WY": 6, "VT": 6, "NH": 6, "ME": 6,
|
|
// Zone 8: Arctic
|
|
"AK": 8,
|
|
}
|
|
|
|
// GetClimateRegionIDByState returns the climate region ID for a US state abbreviation.
|
|
// Returns 0 if the state is not found.
|
|
func GetClimateRegionIDByState(state string) uint {
|
|
regionID, ok := StateToClimateRegion[strings.ToUpper(strings.TrimSpace(state))]
|
|
if !ok {
|
|
return 0
|
|
}
|
|
return regionID
|
|
}
|
|
|
|
// ZipToState maps a US ZIP code to a state abbreviation using the 3-digit ZIP prefix.
|
|
// Returns empty string if the ZIP is invalid or unrecognized.
|
|
func ZipToState(zip string) string {
|
|
zip = strings.TrimSpace(zip)
|
|
if len(zip) < 3 {
|
|
return ""
|
|
}
|
|
prefix, err := strconv.Atoi(zip[:3])
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
|
|
// ZIP prefix → state mapping (USPS ranges)
|
|
switch {
|
|
case prefix >= 10 && prefix <= 27:
|
|
return "MA"
|
|
case prefix >= 28 && prefix <= 29:
|
|
return "RI"
|
|
case prefix >= 30 && prefix <= 38:
|
|
return "NH"
|
|
case prefix >= 39 && prefix <= 49:
|
|
return "ME"
|
|
case prefix >= 50 && prefix <= 59:
|
|
return "VT"
|
|
case prefix >= 60 && prefix <= 69:
|
|
return "CT"
|
|
case prefix >= 70 && prefix <= 89:
|
|
return "NJ"
|
|
case prefix >= 100 && prefix <= 149:
|
|
return "NY"
|
|
case prefix >= 150 && prefix <= 196:
|
|
return "PA"
|
|
case prefix >= 197 && prefix <= 199:
|
|
return "DE"
|
|
case prefix >= 200 && prefix <= 205:
|
|
return "DC"
|
|
case prefix >= 206 && prefix <= 219:
|
|
return "MD"
|
|
case prefix >= 220 && prefix <= 246:
|
|
return "VA"
|
|
case prefix >= 247 && prefix <= 268:
|
|
return "WV"
|
|
case prefix >= 270 && prefix <= 289:
|
|
return "NC"
|
|
case prefix >= 290 && prefix <= 299:
|
|
return "SC"
|
|
case prefix >= 300 && prefix <= 319:
|
|
return "GA"
|
|
case prefix >= 320 && prefix <= 349:
|
|
return "FL"
|
|
case prefix >= 350 && prefix <= 369:
|
|
return "AL"
|
|
case prefix >= 370 && prefix <= 385:
|
|
return "TN"
|
|
case prefix >= 386 && prefix <= 397:
|
|
return "MS"
|
|
case prefix >= 400 && prefix <= 427:
|
|
return "KY"
|
|
case prefix >= 430 && prefix <= 458:
|
|
return "OH"
|
|
case prefix >= 460 && prefix <= 479:
|
|
return "IN"
|
|
case prefix >= 480 && prefix <= 499:
|
|
return "MI"
|
|
case prefix >= 500 && prefix <= 528:
|
|
return "IA"
|
|
case prefix >= 530 && prefix <= 549:
|
|
return "WI"
|
|
case prefix >= 550 && prefix <= 567:
|
|
return "MN"
|
|
case prefix >= 570 && prefix <= 577:
|
|
return "SD"
|
|
case prefix >= 580 && prefix <= 588:
|
|
return "ND"
|
|
case prefix >= 590 && prefix <= 599:
|
|
return "MT"
|
|
case prefix >= 600 && prefix <= 629:
|
|
return "IL"
|
|
case prefix >= 630 && prefix <= 658:
|
|
return "MO"
|
|
case prefix >= 660 && prefix <= 679:
|
|
return "KS"
|
|
case prefix >= 680 && prefix <= 693:
|
|
return "NE"
|
|
case prefix >= 700 && prefix <= 714:
|
|
return "LA"
|
|
case prefix >= 716 && prefix <= 729:
|
|
return "AR"
|
|
case prefix >= 730 && prefix <= 749:
|
|
return "OK"
|
|
case prefix >= 750 && prefix <= 799:
|
|
return "TX"
|
|
case prefix >= 800 && prefix <= 816:
|
|
return "CO"
|
|
case prefix >= 820 && prefix <= 831:
|
|
return "WY"
|
|
case prefix >= 832 && prefix <= 838:
|
|
return "ID"
|
|
case prefix >= 840 && prefix <= 847:
|
|
return "UT"
|
|
case prefix >= 850 && prefix <= 865:
|
|
return "AZ"
|
|
case prefix >= 870 && prefix <= 884:
|
|
return "NM"
|
|
case prefix >= 889 && prefix <= 898:
|
|
return "NV"
|
|
case prefix >= 900 && prefix <= 966:
|
|
return "CA"
|
|
case prefix >= 967 && prefix <= 968:
|
|
return "HI"
|
|
case prefix >= 970 && prefix <= 979:
|
|
return "OR"
|
|
case prefix >= 980 && prefix <= 994:
|
|
return "WA"
|
|
case prefix >= 995 && prefix <= 999:
|
|
return "AK"
|
|
default:
|
|
return ""
|
|
}
|
|
}
|