mirror of
https://github.com/photoprism/photoprism.git
synced 2026-01-23 02:24:24 +00:00
Places: Use float64 for all coordinates to avoid rounding errors #3953
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
parent
e808de45e3
commit
735a3a2d13
32 changed files with 510 additions and 377 deletions
|
|
@ -2,25 +2,24 @@ package clean
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/geo"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// gpsCeil converts a GPS coordinate to a rounded float32 for use in queries.
|
||||
func gpsCeil(f float64) float32 {
|
||||
return float32((math.Ceil(f*10000) / 10000) + 0.0001)
|
||||
// GPSBoundsDefaultPadding specifies the default padding of the GPS coordinates in meters.
|
||||
const GPSBoundsDefaultPadding = 5.0
|
||||
|
||||
// GPSBounds parses the GPS bounds (Lat N, Lng E, Lat S, Lng W)
|
||||
// and returns the coordinates with default padding.
|
||||
func GPSBounds(bounds string) (latN, lngE, latS, lngW float64, err error) {
|
||||
return GPSBoundsWithPadding(bounds, GPSBoundsDefaultPadding)
|
||||
}
|
||||
|
||||
// gpsFloor converts a GPS coordinate to a rounded float32 for use in queries.
|
||||
func gpsFloor(f float64) float32 {
|
||||
return float32((math.Floor(f*10000) / 10000) - 0.0001)
|
||||
}
|
||||
|
||||
// GPSBounds parses the GPS bounds (Lat N, Lng E, Lat S, Lng W) and returns the coordinates if any.
|
||||
func GPSBounds(bounds string) (latN, lngE, latS, lngW float32, err error) {
|
||||
// GPSBoundsWithPadding parses the GPS bounds (Lat N, Lng E, Lat S, Lng W)
|
||||
// and returns the coordinates with a custom padding in meters.
|
||||
func GPSBoundsWithPadding(bounds string, padding float64) (latN, lngE, latS, lngW float64, err error) {
|
||||
// Bounds string not long enough?
|
||||
if len(bounds) < 7 {
|
||||
return 0, 0, 0, 0, fmt.Errorf("no coordinates found")
|
||||
|
|
@ -29,19 +28,19 @@ func GPSBounds(bounds string) (latN, lngE, latS, lngW float32, err error) {
|
|||
// Trim whitespace and invalid characters.
|
||||
bounds = strings.Trim(bounds, " |\\<>\n\r\t\"'#$%!^*()[]{}")
|
||||
|
||||
// Split string into values.
|
||||
// Split bounding box string into coordinate values.
|
||||
values := strings.SplitN(bounds, ",", 5)
|
||||
found := len(values)
|
||||
|
||||
// Invalid number of values?
|
||||
// Return error if number of coordinates is invalid.
|
||||
if found != 4 {
|
||||
return 0, 0, 0, 0, fmt.Errorf("invalid number of coordinates")
|
||||
}
|
||||
|
||||
// Parse floating point coordinates.
|
||||
// Convert coordinate strings to floating point values.
|
||||
latNorth, lngEast, latSouth, lngWest := txt.Float(values[0]), txt.Float(values[1]), txt.Float(values[2]), txt.Float(values[3])
|
||||
|
||||
// Latitudes (from +90 to -90 degrees).
|
||||
// Latitudes have a valid range of +90 to -90 degrees.
|
||||
if latNorth > 90 {
|
||||
latNorth = 90
|
||||
} else if latNorth < -90 {
|
||||
|
|
@ -54,12 +53,12 @@ func GPSBounds(bounds string) (latN, lngE, latS, lngW float32, err error) {
|
|||
latSouth = -90
|
||||
}
|
||||
|
||||
// latSouth must be smaller.
|
||||
// Make sure latSouth is smaller than latNorth.
|
||||
if latSouth > latNorth {
|
||||
latNorth, latSouth = latSouth, latNorth
|
||||
}
|
||||
|
||||
// Longitudes (from -180 to +180 degrees).
|
||||
// Longitudes have a valid range of -180 to +180 degrees.
|
||||
if lngEast > 180 {
|
||||
lngEast = 180
|
||||
} else if lngEast < -180 {
|
||||
|
|
@ -72,17 +71,20 @@ func GPSBounds(bounds string) (latN, lngE, latS, lngW float32, err error) {
|
|||
lngWest = -180
|
||||
}
|
||||
|
||||
// lngWest must be smaller.
|
||||
// Make sure lngWest is smaller than lngEast.
|
||||
if lngWest > lngEast {
|
||||
lngEast, lngWest = lngWest, lngEast
|
||||
}
|
||||
|
||||
// Return rounded coordinates.
|
||||
return gpsCeil(latNorth), gpsCeil(lngEast), gpsFloor(latSouth), gpsFloor(lngWest), nil
|
||||
// Calculate the latitude and longitude padding in degrees.
|
||||
dLat, dLng := geo.Deg((latNorth+latSouth)/2.0, padding)
|
||||
|
||||
// Return the coordinates of the bounding box with padding applied.
|
||||
return latNorth + dLat, lngEast + dLng, latSouth - dLat, lngWest - dLng, nil
|
||||
}
|
||||
|
||||
// GPSLatRange returns a range based on the specified latitude and distance in km, or an error otherwise.
|
||||
func GPSLatRange(lat float64, km float64) (latN, latS float32, err error) {
|
||||
func GPSLatRange(lat float64, km float64) (latN, latS float64, err error) {
|
||||
// Latitude (from +90 to -90 degrees).
|
||||
if lat == 0 || lat < -90 || lat > 90 {
|
||||
return 0, 0, fmt.Errorf("invalid latitude")
|
||||
|
|
@ -93,8 +95,10 @@ func GPSLatRange(lat float64, km float64) (latN, latS float32, err error) {
|
|||
|
||||
// Approximate longitude range,
|
||||
// see https://en.wikipedia.org/wiki/Decimal_degrees
|
||||
latN = gpsCeil(lat + geo.Deg(r))
|
||||
latS = gpsFloor(lat - geo.Deg(r))
|
||||
dLat, _ := geo.DegKm(lat, r)
|
||||
|
||||
latN = lat + dLat
|
||||
latS = lat - dLat
|
||||
|
||||
if latN > 90 {
|
||||
latN = 90
|
||||
|
|
@ -108,7 +112,7 @@ func GPSLatRange(lat float64, km float64) (latN, latS float32, err error) {
|
|||
}
|
||||
|
||||
// GPSLngRange returns a range based on the specified longitude and distance in km, or an error otherwise.
|
||||
func GPSLngRange(lng float64, km float64) (lngE, lngW float32, err error) {
|
||||
func GPSLngRange(lat, lng float64, km float64) (lngE, lngW float64, err error) {
|
||||
// Longitude (from -180 to +180 degrees).
|
||||
if lng == 0 || lng < -180 || lng > 180 {
|
||||
return 0, 0, fmt.Errorf("invalid longitude")
|
||||
|
|
@ -119,8 +123,10 @@ func GPSLngRange(lng float64, km float64) (lngE, lngW float32, err error) {
|
|||
|
||||
// Approximate longitude range,
|
||||
// see https://en.wikipedia.org/wiki/Decimal_degrees
|
||||
lngE = gpsCeil(lng + geo.Deg(r))
|
||||
lngW = gpsFloor(lng - geo.Deg(r))
|
||||
_, dLng := geo.DegKm(lat, r)
|
||||
|
||||
lngE = lng + dLng
|
||||
lngW = lng - dLng
|
||||
|
||||
if lngE > 180 {
|
||||
lngE = 180
|
||||
|
|
|
|||
|
|
@ -6,77 +6,97 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGPSBoundsWithPadding(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBoundsWithPadding("41.87760543823242,-87.62521362304688,41.89404296875,-87.6215591430664", 1000)
|
||||
assert.InEpsilon(t, 41.903036, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, -87.609479, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 41.868612, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, -87.637294, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGPSBounds(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.87760543823242,-87.62521362304688,41.89404296875,-87.6215591430664")
|
||||
assert.Equal(t, float32(41.8942), latNorth)
|
||||
assert.Equal(t, float32(41.8775), latSouth)
|
||||
assert.Equal(t, float32(-87.6254), lngWest)
|
||||
assert.Equal(t, float32(-87.6214), lngEast)
|
||||
assert.InEpsilon(t, 41.8942, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6214, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 41.8775, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6254, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("China", func(t *testing.T) {
|
||||
// Actual postion: Lat 39.8922, Lng 116.315
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("39.8922004699707,116.31500244140625,39.8922004699707,116.31500244140625")
|
||||
assert.InEpsilon(t, 39.8924, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, 116.3152, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 39.8921, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, 116.3149, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("FlippedLat", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.89404296875,-87.62521362304688,41.87760543823242,-87.6215591430664")
|
||||
assert.Equal(t, float32(41.8942), latNorth)
|
||||
assert.Equal(t, float32(41.8775), latSouth)
|
||||
assert.Equal(t, float32(-87.6254), lngWest)
|
||||
assert.Equal(t, float32(-87.6214), lngEast)
|
||||
assert.InEpsilon(t, 41.8942, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6214, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 41.8775, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6254, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("FlippedLng", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.87760543823242,-87.6215591430664,41.89404296875,-87.62521362304688")
|
||||
assert.Equal(t, float32(41.8942), latNorth)
|
||||
assert.Equal(t, float32(41.8775), latSouth)
|
||||
assert.Equal(t, float32(-87.6254), lngWest)
|
||||
assert.Equal(t, float32(-87.6214), lngEast)
|
||||
assert.InEpsilon(t, 41.8942, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6214, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 41.8775, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, -87.6254, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Empty", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("")
|
||||
assert.Equal(t, float32(0), latNorth)
|
||||
assert.Equal(t, float32(0), lngEast)
|
||||
assert.Equal(t, float32(0), latSouth)
|
||||
assert.Equal(t, float32(0), lngWest)
|
||||
assert.Equal(t, 0.0, latNorth)
|
||||
assert.Equal(t, 0.0, lngEast)
|
||||
assert.Equal(t, 0.0, latSouth)
|
||||
assert.Equal(t, 0.0, lngWest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("One", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.87760543823242")
|
||||
assert.Equal(t, float32(0), latNorth)
|
||||
assert.Equal(t, float32(0), lngEast)
|
||||
assert.Equal(t, float32(0), latSouth)
|
||||
assert.Equal(t, float32(0), lngWest)
|
||||
assert.Equal(t, 0.0, latNorth)
|
||||
assert.Equal(t, 0.0, lngEast)
|
||||
assert.Equal(t, 0.0, latSouth)
|
||||
assert.Equal(t, 0.0, lngWest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("Three", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.87760543823242,-87.62521362304688,41.89404296875")
|
||||
assert.Equal(t, float32(0), latNorth)
|
||||
assert.Equal(t, float32(0), lngEast)
|
||||
assert.Equal(t, float32(0), latSouth)
|
||||
assert.Equal(t, float32(0), lngWest)
|
||||
assert.Equal(t, 0.0, latNorth)
|
||||
assert.Equal(t, 0.0, lngEast)
|
||||
assert.Equal(t, 0.0, latSouth)
|
||||
assert.Equal(t, 0.0, lngWest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("Five", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("41.87760543823242,-87.62521362304688,41.89404296875,-87.6215591430664,41.89404296875")
|
||||
assert.Equal(t, float32(0), latNorth)
|
||||
assert.Equal(t, float32(0), lngEast)
|
||||
assert.Equal(t, float32(0), latSouth)
|
||||
assert.Equal(t, float32(0), lngWest)
|
||||
assert.Equal(t, 0.0, latNorth)
|
||||
assert.Equal(t, 0.0, lngEast)
|
||||
assert.Equal(t, 0.0, latSouth)
|
||||
assert.Equal(t, 0.0, lngWest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
t.Run("Invalid", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("95.87760543823242,-197.62521362304688,98.89404296875,-197.6215591430664")
|
||||
assert.Equal(t, float32(90.0001), latNorth)
|
||||
assert.Equal(t, float32(89.9999), latSouth)
|
||||
assert.Equal(t, float32(-180.0001), lngWest)
|
||||
assert.Equal(t, float32(-179.9999), lngEast)
|
||||
assert.InEpsilon(t, 90.000045, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, -179.974236, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, 89.999955, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, -180.025764, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Invalid2", func(t *testing.T) {
|
||||
latNorth, lngEast, latSouth, lngWest, err := GPSBounds("-95.87760543823242,197.62521362304688,-98.89404296875,197.6215591430664")
|
||||
assert.Equal(t, float32(-89.9999), latNorth)
|
||||
assert.Equal(t, float32(-90.0001), latSouth)
|
||||
assert.Equal(t, float32(179.9999), lngWest)
|
||||
assert.Equal(t, float32(180.0001), lngEast)
|
||||
assert.InEpsilon(t, -89.999955, latNorth, 0.00001)
|
||||
assert.InEpsilon(t, 180.025764, lngEast, 0.00001)
|
||||
assert.InEpsilon(t, -90.000045, latSouth, 0.00001)
|
||||
assert.InEpsilon(t, 179.974236, lngWest, 0.00001)
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
}
|
||||
|
|
@ -84,29 +104,41 @@ func TestGPSBounds(t *testing.T) {
|
|||
func TestGPSLatRange(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
latNorth, latSouth, err := GPSLatRange(41.87760543823242, 2)
|
||||
assert.Equal(t, float32(41.8913), latNorth)
|
||||
assert.Equal(t, float32(41.8639), latSouth)
|
||||
assert.Equal(t, float32(41.891094), float32(latNorth))
|
||||
assert.Equal(t, float32(41.864117), float32(latSouth))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
latNorth, latSouth, err := GPSLatRange(0, 2)
|
||||
assert.Equal(t, float32(0), latNorth)
|
||||
assert.Equal(t, float32(0), latSouth)
|
||||
assert.Equal(t, 0.0, latNorth)
|
||||
assert.Equal(t, 0.0, latSouth)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGPSLngRange(t *testing.T) {
|
||||
t.Run("Valid", func(t *testing.T) {
|
||||
lngEast, lngWest, err := GPSLngRange(-87.62521362304688, 2)
|
||||
assert.Equal(t, float32(-87.6389), lngWest)
|
||||
assert.Equal(t, float32(-87.6116), lngEast)
|
||||
t.Run("Lat0", func(t *testing.T) {
|
||||
lngEast, lngWest, err := GPSLngRange(0.0, -87.62521362304688, 2)
|
||||
assert.Equal(t, float32(-87.6387), float32(lngWest))
|
||||
assert.Equal(t, float32(-87.611725), float32(lngEast))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Lat45", func(t *testing.T) {
|
||||
lngEast, lngWest, err := GPSLngRange(45.0, -87.62521362304688, 2)
|
||||
assert.Equal(t, float32(-87.644295), float32(lngWest))
|
||||
assert.Equal(t, float32(-87.60613), float32(lngEast))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Lat67", func(t *testing.T) {
|
||||
lngEast, lngWest, err := GPSLngRange(67.0, -87.62521362304688, 2)
|
||||
assert.Equal(t, float32(-87.65974), float32(lngWest))
|
||||
assert.Equal(t, float32(-87.59069), float32(lngEast))
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
t.Run("Zero", func(t *testing.T) {
|
||||
lngEast, lngWest, err := GPSLngRange(0, 2)
|
||||
assert.Equal(t, float32(0), lngEast)
|
||||
assert.Equal(t, float32(0), lngWest)
|
||||
lngEast, lngWest, err := GPSLngRange(0, 0, 2)
|
||||
assert.Equal(t, 0.0, lngEast)
|
||||
assert.Equal(t, 0.0, lngWest)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,35 @@ const (
|
|||
DefaultDist float64 = 2
|
||||
)
|
||||
|
||||
// Deg returns the approximate distance in decimal degrees,
|
||||
// see https://en.wikipedia.org/wiki/Decimal_degrees.
|
||||
func Deg(km float64) float64 {
|
||||
return 0.009009009 * km
|
||||
// Deg returns the distance in decimal degrees based on the specified distance in meters and the latitude,
|
||||
// see https://en.wikipedia.org/wiki/Decimal_degrees#Precision.
|
||||
func Deg(lat, meter float64) (dLat, dLng float64) {
|
||||
if meter <= 0.0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// Calculate latitude distance in degrees.
|
||||
dLat = (meter / AverageEarthRadiusMeter) * (180.0 / math.Pi)
|
||||
|
||||
// Do not calculate the exact longitude distance in
|
||||
// degrees if the latitude is zero or out of range.
|
||||
if lat == 0.0 {
|
||||
return dLat, dLat
|
||||
} else if lat < -89.9 {
|
||||
lat = -89.9
|
||||
} else if lat > 89.9 {
|
||||
lat = 89.9
|
||||
}
|
||||
|
||||
// Calculate longitude distance in degrees.
|
||||
dLng = (meter / AverageEarthRadiusMeter) * (180.0 / math.Pi) / math.Cos(lat*math.Pi/180.0)
|
||||
|
||||
return dLat, dLng
|
||||
}
|
||||
|
||||
// DegKm returns the distance in decimal degrees based on the specified distance in kilometers and the latitude.
|
||||
func DegKm(lat, km float64) (dLat, dLng float64) {
|
||||
return Deg(lat, km*1000.0)
|
||||
}
|
||||
|
||||
// DegToRad converts a value from degrees to radians.
|
||||
|
|
@ -40,5 +65,5 @@ func Km(p, q Position) (km float64) {
|
|||
|
||||
c := 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a))
|
||||
|
||||
return c * EarthRadiusKm
|
||||
return c * AverageEarthRadiusKm
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,8 +7,76 @@ import (
|
|||
)
|
||||
|
||||
func TestDeg(t *testing.T) {
|
||||
t.Run("10km", func(t *testing.T) {
|
||||
assert.Equal(t, 0.09009009, Deg(10))
|
||||
t.Run("Lat0", func(t *testing.T) {
|
||||
dLat, dLng := Deg(0.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000100, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat23", func(t *testing.T) {
|
||||
dLat, dLng := Deg(23.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000108, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat45", func(t *testing.T) {
|
||||
dLat, dLng := Deg(45.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000141, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat67", func(t *testing.T) {
|
||||
dLat, dLng := Deg(67.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000255, dLng, 0.01)
|
||||
})
|
||||
t.Run("ZeroMeters", func(t *testing.T) {
|
||||
dLat, dLng := Deg(50.0, 0.0)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.Equal(t, 0.0, dLat)
|
||||
assert.Equal(t, 0.0, dLng)
|
||||
})
|
||||
t.Run("LatOutOfRange", func(t *testing.T) {
|
||||
dLat, dLng := Deg(123.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.057195, dLng, 0.01)
|
||||
dLat, dLng = Deg(-600.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.057195, dLng, 0.01)
|
||||
dLat, dLng = Deg(-600.0, 11.1)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.057195, dLng, 0.01)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDegKm(t *testing.T) {
|
||||
t.Run("Lat0", func(t *testing.T) {
|
||||
dLat, dLng := DegKm(0.0, 0.0111)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000100, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat23", func(t *testing.T) {
|
||||
dLat, dLng := DegKm(23.0, 0.0111)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000108, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat45", func(t *testing.T) {
|
||||
dLat, dLng := DegKm(45.0, 0.0111)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000141, dLng, 0.01)
|
||||
})
|
||||
t.Run("Lat67", func(t *testing.T) {
|
||||
dLat, dLng := DegKm(67.0, 0.0111)
|
||||
t.Logf("dLat: %f, dLng: %f", dLat, dLng)
|
||||
assert.InEpsilon(t, 0.000100, dLat, 0.01)
|
||||
assert.InEpsilon(t, 0.000255, dLng, 0.01)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,5 +25,8 @@ Additional information can be found in our Developer Guide:
|
|||
package geo
|
||||
|
||||
const (
|
||||
EarthRadiusKm = 6371 // Earth radius in km
|
||||
AverageEarthRadiusKm = 6371.0 // Global-average earth radius in km
|
||||
AverageEarthRadiusMeter = AverageEarthRadiusKm * 1000.0 // Global-average earth radius in m
|
||||
WGS84EarthRadiusKm = 6378.137 // WGS84 earth radius in km
|
||||
WGS84EarthRadiusMeter = WGS84EarthRadiusKm * 1000.0 // WGS84 earth radius in m
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue