package address_utils import ( "crypto/md5" "fmt" "math" "regexp" "strings" "gitlab.bob.co.za/bob-public-utils/bobgroup-go-utils/string_utils" ) var southAfricaVariations = []string{ "ZA", "South Africa", "Suid-Afrika", "Suid Afrika", "Iningizimu Afrika", "Mzantsi Afrika", "Afrika Boroa", "Africa Kusini", } type Province struct { Names []string DisplayName string } var Provinces = map[string]Province{ "EC": { DisplayName: "Eastern Cape", Names: []string{ "Eastern Cape", "Eastern-Cape", "Oos-Kaap", "iPumalanga-Kapa", "Kapa Bohlabela", "Kapa Botjhabela", "Kapa-Vuxa", "Kapa Botlhaba", "Kapa Vhubvaḓuvha", "Mpuma-Koloni", "Mpumalanga-Kapa", "Mpumalanga-Koloni", }, }, "FS": { DisplayName: "Free State", Names: []string{ "Free State", "Freestate", "Vrystaat", "iFreyistata", "Freistata", "Foreisetata", "Fureisitata", "Freyistata", "Fuleyisitata", "Freyisitata", }, }, "GP": { DisplayName: "Gauteng", Names: []string{ "GT", "Gauteng", "iGauteng", "Kgauteng", "Rhawuti", }, }, "KZN": { DisplayName: "KwaZulu-Natal", Names: []string{ "NT", "NL", "ZN", "KwaZulu-Natal", "KwaZulu Natal", "iKwaZulu-Natal", "GaZulu-Natala", "Hazolo-Natala", "KwaZulu-Natali", "HaZulu-Natal", "KwaZulu-Natala", }, }, "LP": { DisplayName: "Limpopo", Names: []string{ "NP", "Limpopo", "Vhembe", }, }, "MP": { DisplayName: "Mpumalanga", Names: []string{ "Mpumalanga", "iMpumalanga", }, }, "NC": { DisplayName: "Northern Cape", Names: []string{ "Northern Cape", "Northern-Cape", "Noord-Kaap", "Noord Kaap", "iTlhagwini-Kapa", "Kapa Leboya", "Kapa-N'walungu", "Kapa Bokone", "Kapa Leboa", "Kapa Devhula", "Mntla-Koloni", "Nyakatho-Kapa", "Nyakatho-Koloni", }, }, "NW": { DisplayName: "North West", Names: []string{ "North West", "North-West", "Noordwes", "Noord-wes", "iTlhagwini-Tjhingalanga", "Lebowa Bodikela", "Leboya Bophirima", "Leboya le Bophirima", "N'walungu-Vupeladyambu", "Bokone Bophirima", "Mntla-Ntshona", "Nyakatho-Ntshonalanga", }, }, "WC": { DisplayName: "Western Cape", Names: []string{ "Western Cape", "Western-Cape", "Wes-Kaap", "Wes Kaap", "iTjhingalanga-Kapa", "Kapa Bodikela", "Kapa Bophirimela", "Kapa-Vupeladyambu", "Kapa Bophirima", "Kapa Vhukovhela", "Ntshona-Koloni", "Ntshonalanga-Kapa", "Ntshonalanga-Koloni", }, }, } // MD5HashOfAddress m(E,L,L) - calculates and returns the MD5 hash of the entered address, lat and lng concatenated together. If lat and lng is blank, it is only the hash of the entered address func MD5HashOfAddress(enteredAddress string, lat *float64, lng *float64, addressType *string) string { valueToHash := enteredAddress if lat != nil && lng != nil { valueToHash += fmt.Sprintf(",%v,%v", *lat, *lng) } if addressType != nil && len(*addressType) > 0 && *addressType != "unknown" { valueToHash += fmt.Sprintf(",%s", *addressType) } return fmt.Sprintf("%X", md5.Sum([]byte(valueToHash))) } // MD5HashOfLowerCaseEnteredAddress m(e) - calculates and returns the MD5 hash of a string after preparing the string properly. func MD5HashOfLowerCaseEnteredAddress(enteredAddress string) string { valueToHash := cleanLowerCaseAddress(enteredAddress) return fmt.Sprintf("%X", md5.Sum([]byte(valueToHash))) } // cleanLowerCaseAddress makes the entered address lowercase, removes unwanted chars, strip street terms and removes all whitespace func cleanLowerCaseAddress(enteredAddress string) string { // Lowercase. enteredAddress = strings.ToLower(enteredAddress) // Remove unwanted characters. enteredAddress = stripUnwantedCharacters(enteredAddress) // Remove ave, str, etc. enteredAddress = stripStreetTerms(enteredAddress) // Remove all whitespace. enteredAddress = string_utils.RemoveAllWhiteSpaces(enteredAddress) return enteredAddress } func stripStreetTerms(s string) string { terms := []string{ "ave", "avenue", "str", "street", "st", "straat", "lane", "ln", "laan", "road", "rd", "boulevard", "blvd", "drive", "drv", "way", "weg", } re := regexp.MustCompile(`\s+(` + strings.Join(terms, "|") + `)[\s]*`) s = re.ReplaceAllString(s, "") return s } func stripUnwantedCharacters(s string) string { re := regexp.MustCompile(`[^\s\w,-]`) s = re.ReplaceAllString(s, "") return s } func CleanZone(countryToClean, zoneToClean *string) (newCountry, newZone *string) { newCountry = countryToClean for _, southAfricaVariation := range southAfricaVariations { if countryToClean == nil || len(*countryToClean) == 0 || strings.ToLower(*countryToClean) == strings.ToLower(southAfricaVariation) { defaultCountry := "South Africa" newCountry = &defaultCountry break } } if *newCountry == "South Africa" && zoneToClean != nil { zone := string_utils.RemoveAllWhiteSpaces(*zoneToClean) for provinceCode, province := range Provinces { found := false for _, name := range province.Names { if strings.ToLower(zone) == strings.ToLower(string_utils.RemoveAllWhiteSpaces(name)) || strings.ToLower(zone) == strings.ToLower(provinceCode) { zone = provinceCode found = true break } } if found { break } } newZone = &zone } return } func IsProvince(address string) bool { for provinceCode, province := range Provinces { if strings.ToLower(address) == strings.ToLower(fmt.Sprintf("%v, South Africa", provinceCode)) { return true } if strings.ToLower(address) == strings.ToLower(fmt.Sprintf("%v, South Africa", province.DisplayName)) { return true } for _, provinceName := range province.Names { if strings.ToLower(address) == strings.ToLower(fmt.Sprintf("%v, South Africa", provinceName)) { return true } } } return false } func GetZoneDisplayName(zone string) string { if province, ok := Provinces[zone]; ok { return province.DisplayName } return zone } func GetDistanceInKmBetweenCoordinates(lat1 float64, lng1 float64, lat2 float64, lng2 float64) float64 { radlat1 := float64(math.Pi * lat1 / 180) radlat2 := float64(math.Pi * lat2 / 180) theta := float64(lng1 - lng2) radtheta := float64(math.Pi * theta / 180) dist := math.Sin(radlat1)*math.Sin(radlat2) + math.Cos(radlat1)*math.Cos(radlat2)*math.Cos(radtheta) if dist > 1 { dist = 1 } dist = math.Acos(dist) dist = dist * 180 / math.Pi dist = dist * 60 * 1.1515 // Return in kilometers return dist * 1.609344 }