From 8da45b6d019893dadeb0bc6aa7937adc3effc909 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fernando=20Gonz=C3=A1lez=20Aguilera?= Date: Mon, 15 Apr 2019 16:06:01 +0200 Subject: [PATCH] added DistanceInKmToZipCode, DistanceInMilToZipCode, GetZipcodesWithinKmRadius and GetZipcodesWithinMlRadius methods --- README.md | 28 +++++++ zipcodes.go | 61 ++++++++++++++- zipcodes_test.go | 195 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 279 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 8497df4..ab2ef2c 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,31 @@ Returns the line of sight distance between two zipcodes in miles: ```golang location, err := zipcodesDataset.DistanceInMiles("01945", "03058") // 30.98 ``` + +### DistanceInKmToZipCode +Calculates the distance between a zipcode and a give lat/lon in Kilometers: + +```golang +location, err := zipcodesDataset.DistanceInKmToZipCode("01945", 51.4267, 13.9333) // 1.11 +``` + +### DistanceInMilToZipCode +Calculates the distance between a zipcode and a give lat/lon in Miles: + +```golang +location, err := zipcodesDataset.DistanceInMilToZipCode("01945", 51.4267, 13.9333) // 0.69 +``` + +### GetZipcodesWithinKmRadius +Returns a list of zipcodes within the radius of this zipcode in Kilometers: + +```golang +location, err := zipcodesDataset.GetZipcodesWithinKmRadius("01945", 50) // ["03058"] +``` + +### GetZipcodesWithinMlRadius +Returns a list of zipcodes within the radius of this zipcode in Miles: + +```golang +location, err := zipcodesDataset.GetZipcodesWithinMlRadius("01945", 50) // ["03058"] +``` diff --git a/zipcodes.go b/zipcodes.go index 3fefc35..d574867 100644 --- a/zipcodes.go +++ b/zipcodes.go @@ -13,7 +13,7 @@ import ( ) const ( - earthRaidusKm = 6371 + earthRadiusKm = 6371 earthRadiusMi = 3958 ) @@ -53,7 +53,7 @@ func (zc *Zipcodes) Lookup(zipCode string) (*ZipCodeLocation, error) { // DistanceInKm returns the line of sight distance between two zipcodes in Kilometers func (zc *Zipcodes) DistanceInKm(zipCodeA string, zipCodeB string) (float64, error) { - return zc.CalculateDistance(zipCodeA, zipCodeB, earthRaidusKm) + return zc.CalculateDistance(zipCodeA, zipCodeB, earthRadiusKm) } // DistanceInMiles returns the line of sight distance between two zipcodes in Miles @@ -76,6 +76,63 @@ func (zc *Zipcodes) CalculateDistance(zipCodeA string, zipCodeB string, radius f return DistanceBetweenPoints(locationA.Lat, locationA.Lon, locationB.Lat, locationB.Lon, radius), nil } +// DistanceInKmToZipcode calculates the distance between a zipcode and a give lat/lon in Kilometers +func (zc *Zipcodes) DistanceInKmToZipCode(zipCode string, latitude, longitude float64) (float64, error) { + location, errLoc := zc.Lookup(zipCode) + if errLoc != nil { + return 0, errLoc + } + + return DistanceBetweenPoints(location.Lat, location.Lon, latitude, longitude, earthRadiusKm), nil +} + +// DistanceInMilToZipcode calculates the distance between a zipcode and a give lat/lon in Miles +func (zc *Zipcodes) DistanceInMilToZipCode(zipCode string, latitude, longitude float64) (float64, error) { + location, errLoc := zc.Lookup(zipCode) + if errLoc != nil { + return 0, errLoc + } + + return DistanceBetweenPoints(location.Lat, location.Lon, latitude, longitude, earthRadiusMi), nil +} + +// GetZipcodesWithinKmRadius get all zipcodes within the radius of this zipcode +func (zc *Zipcodes) GetZipcodesWithinKmRadius(zipCode string, radius float64) ([]string, error) { + zipcodeList := []string{} + location, errLoc := zc.Lookup(zipCode) + if errLoc != nil { + return zipcodeList, errLoc + } + + return zc.FindZipcodesWithinRadius(location, radius, earthRadiusKm), nil +} + +// GetZipcodesWithinMlRadius get all zipcodes within the radius of this zipcode +func (zc *Zipcodes) GetZipcodesWithinMlRadius(zipCode string, radius float64) ([]string, error) { + zipcodeList := []string{} + location, errLoc := zc.Lookup(zipCode) + if errLoc != nil { + return zipcodeList, errLoc + } + + return zc.FindZipcodesWithinRadius(location, radius, earthRadiusMi), nil +} + +// FindZipcodesWithinRadius finds zipcodes within a given radius +func (zc *Zipcodes) FindZipcodesWithinRadius(location *ZipCodeLocation, maxRadius float64, earthRadius float64) []string { + zipcodeList := []string{} + for _, elm := range zc.DatasetList { + if elm.ZipCode != location.ZipCode { + distance := DistanceBetweenPoints(location.Lat, location.Lon, elm.Lat, elm.Lon, earthRadius) + if distance < maxRadius { + zipcodeList = append(zipcodeList, elm.ZipCode) + } + } + } + + return zipcodeList +} + func hsin(t float64) float64 { return math.Pow(math.Sin(t/2), 2) } diff --git a/zipcodes_test.go b/zipcodes_test.go index f17dd08..9a26e42 100644 --- a/zipcodes_test.go +++ b/zipcodes_test.go @@ -107,7 +107,7 @@ func TestDistanceBetweenPoints(t *testing.T) { } for _, c := range cases { - kms := DistanceBetweenPoints(c.coordsA[0], c.coordsA[1], c.coordsB[0], c.coordsB[1], earthRaidusKm) + kms := DistanceBetweenPoints(c.coordsA[0], c.coordsA[1], c.coordsB[0], c.coordsB[1], earthRadiusKm) if kms != c.ExpectedKM { t.Errorf("Distance does not match. Expected %v, got %v", c.ExpectedKM, kms) } @@ -144,7 +144,7 @@ func TestCalculateDistance(t *testing.T) { } for _, c := range cases { - kms, err := zipcodesDataset.CalculateDistance(c.ZipCodeA, c.ZipCodeB, earthRaidusKm) + kms, err := zipcodesDataset.CalculateDistance(c.ZipCodeA, c.ZipCodeB, earthRadiusKm) if err != nil { t.Errorf("Unexpected error while looking for zipcode %s", err) } @@ -177,7 +177,7 @@ func TestCalculateDistance(t *testing.T) { } for _, c := range fail { - _, err := zcDataset.CalculateDistance(c.ZipCodeA, c.ZipCodeB, earthRaidusKm) + _, err := zcDataset.CalculateDistance(c.ZipCodeA, c.ZipCodeB, earthRadiusKm) if err.Error() != c.ExpectedErr { t.Errorf("Unexpected error. Got %s, want %s", err, c.ExpectedErr) } @@ -261,3 +261,192 @@ func TestDistanceInMiles(t *testing.T) { } } } + +func TestDistanceInKmToZipCode(t *testing.T) { + cases := []struct { + ZipCode string + Latitude float64 + Longitude float64 + ExpectedResponse float64 + }{ + { + "01945", + 51.4267, + 13.9333, + 1.11, + }, + { + "01945", + 51.4067, + 13.9333, + 1.11, + }, + } + + zipcodesDataset, err := New("datasets/valid_dataset.txt") + if err != nil { + t.Errorf("Unexpected error while initializing struct %v", err) + } + + for _, c := range cases { + kms, err := zipcodesDataset.DistanceInKmToZipCode(c.ZipCode, c.Latitude, c.Longitude) + + if err != nil { + t.Errorf("Unexpected error while looking for zipcode %s", err) + } + + if kms != c.ExpectedResponse { + t.Errorf("Expected distance in kilometers to zipcode does not match. Expected %v, got %v", c.ExpectedResponse, kms) + } + } +} + +func TestDistanceInMilToZipCode(t *testing.T) { + cases := []struct { + ZipCode string + Latitude float64 + Longitude float64 + ExpectedResponse float64 + }{ + { + "01945", + 51.4267, + 13.9333, + 0.69, + }, + { + "01945", + 51.4067, + 13.9333, + 0.69, + }, + } + + zipcodesDataset, err := New("datasets/valid_dataset.txt") + if err != nil { + t.Errorf("Unexpected error while initializing struct %v", err) + } + + for _, c := range cases { + miles, err := zipcodesDataset.DistanceInMilToZipCode(c.ZipCode, c.Latitude, c.Longitude) + + if err != nil { + t.Errorf("Unexpected error while looking for zipcode %s", err) + } + + if miles != c.ExpectedResponse { + t.Errorf("Expected distance in miles to zipcode does not match. Expected %v, got %v", c.ExpectedResponse, miles) + } + } +} + +func TestGetZipcodesWithinKmRadius(t *testing.T) { + cases := []struct { + ZipCode string + Radius float64 + ExpectedResponse []string + }{ + { + "01945", + 50.0, + []string{"03058"}, + }, + { + "01945", + 100.0, + []string{"03058"}, + }, + } + zipcodesDataset, err := New("datasets/valid_dataset.txt") + if err != nil { + t.Errorf("Unexpected error while initializing struct %v", err) + } + for _, c := range cases { + zcList, err := zipcodesDataset.GetZipcodesWithinKmRadius(c.ZipCode, c.Radius) + if err != nil { + t.Errorf("Unexpected error while looking for zipcode %s", err) + } + + if reflect.DeepEqual(zcList, c.ExpectedResponse) != true { + t.Errorf("Unxpected zipcode list returned.") + } + } +} + +func TestGetZipcodesWithinMlRadius(t *testing.T) { + cases := []struct { + ZipCode string + Radius float64 + ExpectedResponse []string + }{ + { + "01945", + 50.0, + []string{"03058"}, + }, + { + "01945", + 100.0, + []string{"03058"}, + }, + } + zipcodesDataset, err := New("datasets/valid_dataset.txt") + if err != nil { + t.Errorf("Unexpected error while initializing struct %v", err) + } + for _, c := range cases { + zcList, err := zipcodesDataset.GetZipcodesWithinMlRadius(c.ZipCode, c.Radius) + if err != nil { + t.Errorf("Unexpected error while looking for zipcode %s", err) + } + + if reflect.DeepEqual(zcList, c.ExpectedResponse) != true { + t.Errorf("Unxpected zipcode list returned.") + } + } +} + +func TestFindZipcodesWithinRadius(t *testing.T) { + cases := []struct { + Location *ZipCodeLocation + MaxRadius float64 + EarthRadius float64 + ExpectedList []string + }{ + { + &ZipCodeLocation{ + ZipCode: "01945", + PlaceName: "Guteborn", + AdminName: "Brandenburg", + Lat: 51.4167, + Lon: 13.9333, + }, + 50, + earthRadiusKm, + []string{"03058"}, + }, + { + &ZipCodeLocation{ + ZipCode: "01945", + PlaceName: "Guteborn", + AdminName: "Brandenburg", + Lat: 51.4167, + Lon: 13.9333, + }, + 50, + earthRadiusKm, + []string{"03058"}, + }, + } + zipcodesDataset, err := New("datasets/valid_dataset.txt") + if err != nil { + t.Errorf("Unexpected error while initializing struct %v", err) + } + for _, c := range cases { + list := zipcodesDataset.FindZipcodesWithinRadius(c.Location, c.MaxRadius, c.EarthRadius) + + if reflect.DeepEqual(list, c.ExpectedList) != true { + t.Errorf("FindZipcodesWithinRadius returned an unexpected zipcode list.") + } + } +}