-
Notifications
You must be signed in to change notification settings - Fork 137
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add functionality for calculating Aerial Distances #47
Closed
Closed
Changes from all commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
a8d9cc7
Aerial Distance Calculation
i-sanyam f518ce2
Update README for aerial distance calculation
i-sanyam abf0ede
get latlng package
i-sanyam ff32922
add aerial service
i-sanyam 3a49bca
to remove debug comments
i-sanyam f56f1e0
return formatted string from calculateAerialDistance
i-sanyam 9fd5dcb
add todo
i-sanyam 6d0f037
return formatted string from calculateAerialDistance
i-sanyam 70e585a
add custom validation for lat lng
i-sanyam f5354b6
remove google latlng package
i-sanyam da69c5e
lat lng validation
i-sanyam 21883e3
calculateAerialDistance returns float64
i-sanyam cba8b9d
delimiter is -
i-sanyam 32ca54b
delimiter is , in docs
i-sanyam 0cba627
add type Location in pacakge aerial
i-sanyam 784b87b
add tests for aerial package
i-sanyam e7faed4
check out of bounds lat lng in test
i-sanyam 33a4c6c
format aerial package files
i-sanyam de796aa
remove nested if
i-sanyam aa74d20
return all validation errors at once
i-sanyam b23e456
add comma in must compile
i-sanyam b1818fe
remove fmt logs
i-sanyam 34d41ce
update docs
i-sanyam ad56407
use error srtrings instead of []error
i-sanyam 31fadf9
Merge branch 'knadh:master' into master
i-sanyam File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,3 +81,6 @@ enabled = true | |
[epoch] | ||
enabled = true | ||
send_local_time = true | ||
|
||
[aerial] | ||
enabled = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
package aerial | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"math" | ||
"regexp" | ||
"strconv" | ||
) | ||
|
||
type Aerial struct{} | ||
|
||
type Location struct { | ||
Lat float64 | ||
Lng float64 | ||
} | ||
|
||
// New returns a new instance of Aerial. | ||
func New() *Aerial { | ||
return &Aerial{} | ||
} | ||
|
||
var validPointRegex = "(-?\\d+.\\d+)" | ||
var delimiter = "," | ||
var separator = "/" | ||
var latlngpair = validPointRegex + delimiter + validPointRegex | ||
|
||
var reParse = regexp.MustCompile("A" + latlngpair + separator + latlngpair) | ||
|
||
// Query returns the aerial distance in KMs between lat lng pair | ||
func (a *Aerial) Query(q string) ([]string, error) { | ||
regexGroups := reParse.FindStringSubmatch(q) | ||
|
||
if len(regexGroups) != 5 { | ||
return nil, errors.New("invalid lat lng format") | ||
} | ||
|
||
res := regexGroups[1:] | ||
cord := make([]float64, 0, len(res)) | ||
for _, p := range res { | ||
// iterate overy every point to convert into float | ||
f, err := strconv.ParseFloat(p, 64) | ||
if err != nil { | ||
return nil, fmt.Errorf("invalid point %s; Error: %w", p, err) | ||
} | ||
cord = append(cord, f) | ||
} | ||
|
||
l1 := Location{Lat: cord[0], Lng: cord[1]} | ||
l2 := Location{Lat: cord[2], Lng: cord[3]} | ||
|
||
d, e := CalculateAerialDistance(l1, l2) | ||
if e != nil { | ||
return nil, e | ||
} | ||
|
||
result := "aerial distance = " + strconv.FormatFloat(d, 'f', 2, 64) + " KMs" | ||
|
||
r := fmt.Sprintf(`%s 1 TXT "%s"`, q, result) | ||
return []string{r}, nil | ||
} | ||
|
||
// Dump is not implemented in this package. | ||
func (n *Aerial) Dump() ([]byte, error) { | ||
return nil, nil | ||
} | ||
|
||
// calculates aerial distance in KMs | ||
func CalculateAerialDistance(l1, l2 Location) (float64, error) { | ||
|
||
e1 := validateLocation(l1) | ||
e2 := validateLocation(l2) | ||
|
||
if e1 != nil || e2 != nil { | ||
errString := ""; | ||
if (e1 != nil) { | ||
errString += e1.Error(); | ||
} | ||
if (e2 != nil) { | ||
if (e1 != nil) { | ||
errString += "; " | ||
} | ||
errString += e2.Error() | ||
} | ||
|
||
return 0, errors.New(errString) | ||
} | ||
|
||
lat1 := l1.Lat | ||
lng1 := l1.Lng | ||
lat2 := l2.Lat | ||
lng2 := l2.Lng | ||
|
||
radlat1 := float64(math.Pi * lat1 / 180) | ||
radlat2 := float64(math.Pi * lat2 / 180) | ||
|
||
radtheta := float64(math.Pi * float64(lng1-lng2) / 180) | ||
|
||
d := math.Sin(radlat1)*math.Sin(radlat2) + math.Cos(radlat1)*math.Cos(radlat2)*math.Cos(radtheta) | ||
if d > 1 { | ||
d = 1 | ||
} | ||
|
||
d = math.Acos(d) | ||
d = d * 180 / math.Pi | ||
d = d * 60 * 1.1515 * 1.609344 | ||
|
||
return d, nil | ||
knadh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
func isValidPoint(point, maxVal float64) bool { | ||
absoluteVal := math.Abs(point) | ||
return absoluteVal <= maxVal | ||
} | ||
|
||
func validateLocation(l Location) error { | ||
errString := "" | ||
|
||
isLatValid := isValidPoint(l.Lat, 90) | ||
if !isLatValid { | ||
errString += strconv.FormatFloat(l.Lat, 'f', -1, 64) + " lat out of bounds" | ||
} | ||
|
||
isLngValid := isValidPoint(l.Lng, 180) | ||
if !isLngValid { | ||
if (!isLatValid) { | ||
errString += " " | ||
} | ||
errString += strconv.FormatFloat(l.Lng, 'f', -1, 64) + " lng out of bounds" | ||
} | ||
|
||
if (errString != "") { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: to be consistent with the code we can remove parantheses for if statements |
||
return errors.New(errString) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package aerial_test | ||
|
||
import ( | ||
"errors" | ||
"testing" | ||
|
||
"github.com/knadh/dns.toys/internal/services/aerial" | ||
) | ||
|
||
var tests = []struct { | ||
l1 aerial.Location | ||
l2 aerial.Location | ||
d float64 | ||
e error | ||
}{ | ||
{ | ||
aerial.Location{Lat: 30.2458, Lng: 75.8421}, // Sangrur | ||
aerial.Location{Lat: 30.2001, Lng: 75.6755}, // Longowal | ||
16.793459061041027, | ||
nil, | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9352, Lng: 77.6245}, // Kormangala | ||
aerial.Location{Lat: 12.9698, Lng: 77.7500}, // Whitefield | ||
14.132940521067107, | ||
nil, | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 77.5946}, // Bengaluru | ||
aerial.Location{Lat: 28.7041, Lng: 77.1025}, // New Delhi | ||
1750.0305628709923, | ||
nil, | ||
}, | ||
{ | ||
aerial.Location{Lat: -120.9716, Lng: 77.5946}, // Wrong Lat | ||
aerial.Location{Lat: 28.7041, Lng: 77.1025}, // New Delhi | ||
0, | ||
errors.New("-120.9716 lat out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 120.9716, Lng: 77.5946}, // Wrong Lat | ||
aerial.Location{Lat: 28.7041, Lng: 77.1025}, // New Delhi | ||
0, | ||
errors.New("120.9716 lat out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 277.5946}, // Wrong Lng | ||
aerial.Location{Lat: 28.7041, Lng: 77.1025}, // New Delhi | ||
0, | ||
errors.New("277.5946 lng out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: -277.5946}, // Wrong Lng | ||
aerial.Location{Lat: 28.7041, Lng: 77.1025}, // New Delhi | ||
0, | ||
errors.New("-277.5946 lng out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 77.5946}, // Bengaluru | ||
aerial.Location{Lat: 128.7041, Lng: 77.1025}, // Wrong Lat | ||
0, | ||
errors.New("128.7041 lat out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 77.5946}, // Bengaluru | ||
aerial.Location{Lat: -128.7041, Lng: 77.1025}, // Wrong Lat | ||
0, | ||
errors.New("-128.7041 lat out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 77.5946}, // Bengaluru | ||
aerial.Location{Lat: 28.7041, Lng: -187.1025}, // Wrong Lng | ||
0, | ||
errors.New("-187.1025 lng out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: 12.9716, Lng: 77.5946}, // Bengaluru | ||
aerial.Location{Lat: 28.7041, Lng: 187.1025}, // Wrong Lng | ||
0, | ||
errors.New("187.1025 lng out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: -120.9716, Lng: 77.5946}, // Wrong Lat | ||
aerial.Location{Lat: 28.7041, Lng: 187.1025}, // Wrong Lng | ||
0, | ||
errors.New("-120.9716 lat out of bounds; 187.1025 lng out of bounds"), | ||
}, | ||
{ | ||
aerial.Location{Lat: -120.9716, Lng: 277.5946}, // Wrong Lat Lng | ||
aerial.Location{Lat: 28.7041, Lng: 187.1025}, // Wrong Lng | ||
0, | ||
errors.New("-120.9716 lat out of bounds 277.5946 lng out of bounds; 187.1025 lng out of bounds"), | ||
}, | ||
} | ||
|
||
func TestCalculateAerialDistance(t *testing.T) { | ||
for _, input := range tests { | ||
d, e := aerial.CalculateAerialDistance(input.l1, input.l2) | ||
|
||
if e != nil && input.e == nil { | ||
t.Errorf("fail: %v %v -> expected %v -> got error:%s;", input.l1, input.l2, input.d, e.Error()) | ||
} | ||
|
||
if e != nil && input.e != nil && input.e.Error() != e.Error() { | ||
t.Errorf("fail: %v %v ->expected error:%s-> got error:%s;", input.l1, input.l2, input.e.Error(), e.Error()) | ||
} | ||
|
||
if input.d != d { | ||
t.Errorf("fail: want %v %v -> %v got %v", input.l1, input.l2, input.d, d) | ||
} | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add reciever here as well?