Skip to content

Commit

Permalink
Add geographic point validation
Browse files Browse the repository at this point in the history
  • Loading branch information
lggomez committed Sep 23, 2021
1 parent e672069 commit e78a8c3
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 9 deletions.
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,12 @@ func (p Point) Equals(p2 Point) bool
```
Equals returns whether p is equal in latitude and longitude to p2

#### func (Point) IsAntipode
#### func (Point) IsAntipodeOf

```go
func (p Point) IsAntipode(p2 Point) bool
func (p Point) IsAntipodeOf(p2 Point) bool
```
IsAntipode returns whether p is the exact antipode of p2 or not
IsAntipodeOf returns whether p is the exact antipode of p2 or not

#### func (Point) Lat

Expand Down Expand Up @@ -96,7 +96,9 @@ LonRadians returns point p's longitude in radians
func Haversine(p1, p2 geodesy.Point) float64
```
Haversine calculates the ellipsoidal distance in meters between 2 points
using the Haversine formula and the WGS-84 ellipsoid constants
using the Haversine formula and the WGS-84 ellipsoid constants.
If any of the points does not constitute a valid geographic coordinate,
the returned distance will be math.NaN().

#### func VincentyInverse

Expand All @@ -106,6 +108,7 @@ func VincentyInverse(p1, p2 geodesy.Point, accuracy float64, calculateAzimuth bo

VincentyInverse calculates the ellipsoidal distance in meters and azimuth in degrees between 2 points using the inverse Vincenty formulae and the WGS-84 ellipsoid constants. As it is an iterative operation it will converge to the defined accuracy, if accuracy < 0 it will use the default accuracy of 1e-12 (approximately 0.06 mm). If
calculateAzimuth is set to true, it will compute the forward and reverse azimuths (otherwise, these default to math.NaN())
If any of the points does not constitute a valid geographic coordinate, the returned distance will be math.NaN().

The following notations are used in the implementation:

Expand Down
8 changes: 7 additions & 1 deletion distance/haversine.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@ import (
)

// Haversine calculates the ellipsoidal distance in meters between 2 points
// using the Haversine formula and the WGS-84 ellipsoid constants
// using the Haversine formula and the WGS-84 ellipsoid constants.
// If any of the points does not constitute a valid geographic coordinate, the
// returned distance will be math.NaN()
func Haversine(p1, p2 geodesy.Point) float64 {
if !p1.Valid() || !p2.Valid() {
return math.NaN()
}

if p1.Equals(p2) {
return 0
}
Expand Down
9 changes: 7 additions & 2 deletions distance/vincenty.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const (
VincentyInverse calculates the ellipsoidal distance in meters and azimuth in degrees between 2 points using the
inverse Vincenty formulae and the WGS-84 ellipsoid constants. As it is an iterative operation it will converge to
the defined accuracy, if accuracy < 0 it will use the default accuracy of 1e-12 (approximately 0.06 mm). If
calculateAzimuth is set to true, it will compute the forward and reverse azimuths (otherwise, these default to math.NaN())
calculateAzimuth is set to true, it will compute the forward and reverse azimuths (otherwise, these default to math.NaN()).
If any of the points does not constitute a valid geographic coordinate, the returned distance will be math.NaN().
The following notations are used:
a length of semi-major axis of the ellipsoid (radius at equator)
Expand All @@ -38,11 +39,15 @@ The following notations are used:
σm angular separation between the midpoint of the line and the equator;
*/
func VincentyInverse(p1, p2 geodesy.Point, accuracy float64, calculateAzimuth bool) (float64, float64, float64) {
if !p1.Valid() || !p2.Valid() {
return stdMath.NaN(), stdMath.NaN(), stdMath.NaN()
}

if p1.Equals(p2) {
return 0, 0, 0
}

if p1.IsAntipode(p2) {
if p1.IsAntipodeOf(p2) {
// Antipodes are non-convergent
return stdMath.NaN(), stdMath.NaN(), stdMath.NaN()
}
Expand Down
18 changes: 16 additions & 2 deletions point.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ package geodesy

import "math"

const (
latLowerBound = float64(-90)
latUpperBound = float64(90)
lonLowerBound = float64(-180)
lonUpperBound = float64(180)
)

// Point represents a latitude-longitude pair in decimal degrees
type Point [2]float64

Expand Down Expand Up @@ -30,8 +37,8 @@ func (p Point) Antipode() Point {
return Point{-p[0], 180 - math.Abs(p[1])}
}

// IsAntipode returns whether p is the exact antipode of p2 or not
func (p Point) IsAntipode(p2 Point) bool {
// IsAntipodeOf returns whether p is the exact antipode of p2 or not
func (p Point) IsAntipodeOf(p2 Point) bool {
// Shorthand check to avoid Equals() calls between p and p2
return ((p[0] == -p2[0]) && (p[1] == (180 - math.Abs(p2[1])))) ||
(p2[0] == -p[0]) && (p2[1] == (180 - math.Abs(p[1])))
Expand All @@ -41,3 +48,10 @@ func (p Point) IsAntipode(p2 Point) bool {
func (p Point) Equals(p2 Point) bool {
return (p[0] == p2[0]) && (p[1] == p2[1])
}

// Valid returns whether p is valid, that is, contained within the valid range of
// geographic coordinates
func (p Point) Valid() bool {
return ((p[0] >= latLowerBound) && (p[0] <= latUpperBound)) &&
((p[1] >= lonLowerBound) && (p[1] <= lonUpperBound))
}

0 comments on commit e78a8c3

Please sign in to comment.