Skip to content
Snippets Groups Projects
address_utils.go 6.46 KiB
Newer Older
Cornel Rautenbach's avatar
Cornel Rautenbach committed
package address_utils

import (
	"crypto/md5"
	"fmt"
Cornel Rautenbach's avatar
Cornel Rautenbach committed
	"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",
}

	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",
	"KZN": {
		DisplayName: "KwaZulu-Natal",
			"KwaZulu-Natal",
			"KwaZulu Natal",
			"iKwaZulu-Natal",
			"GaZulu-Natala",
			"Hazolo-Natala",
			"KwaZulu-Natali",
			"HaZulu-Natal",
			"KwaZulu-Natala",
		},
	},
	"LP": {
		DisplayName: "Limpopo",
	"MP": {
		DisplayName: "Mpumalanga",
	"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 {
Cornel Rautenbach's avatar
Cornel Rautenbach committed
	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)
	}

Cornel Rautenbach's avatar
Cornel Rautenbach committed
	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.
Cornel Rautenbach's avatar
Cornel Rautenbach committed
func MD5HashOfLowerCaseEnteredAddress(enteredAddress string) string {
	valueToHash := cleanLowerCaseAddress(enteredAddress)
	return fmt.Sprintf("%X", md5.Sum([]byte(valueToHash)))
}

Cornel Rautenbach's avatar
Cornel Rautenbach committed
// cleanLowerCaseAddress makes the entered address lowercase, removes unwanted chars, strip street terms and removes all whitespace
Cornel Rautenbach's avatar
Cornel Rautenbach committed
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) {
	if *newCountry == "South Africa" && zoneToClean != nil {
		zone := string_utils.RemoveAllWhiteSpaces(*zoneToClean)
		for provinceCode, province := range Provinces {
			found := false
				if strings.ToLower(zone) == strings.ToLower(string_utils.RemoveAllWhiteSpaces(name)) ||
					strings.ToLower(zone) == strings.ToLower(provinceCode) {
					zone = provinceCode
					found = true
					break
			if found {
				break
			}

func IsProvince(address string) bool {
	for provinceCode, province := range Provinces {
		if strings.ToLower(address) == strings.ToLower(fmt.Sprintf("%v, South Africa", provinceCode)) {
		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
			}
		}

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
}