77 lines
1.6 KiB
Go
77 lines
1.6 KiB
Go
package geo
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"time"
|
|
)
|
|
|
|
const Meter = 0.00001
|
|
|
|
// Position represents a geo coordinate.
|
|
type Position struct {
|
|
Name string // Optional name
|
|
Time time.Time // Optional time
|
|
Lat float64 // In degree
|
|
Lng float64 // In degree
|
|
Altitude float64 // In meter
|
|
Accuracy int // In meter
|
|
Estimate bool
|
|
}
|
|
|
|
// String returns the position information as string for logging.
|
|
func (p Position) String() string {
|
|
name := p.Name
|
|
|
|
if name == "" {
|
|
name = "position"
|
|
}
|
|
|
|
return fmt.Sprintf("%s @ %f, %f, alt %f m ± %d m", name, p.Lat, p.Lng, p.Altitude, p.Accuracy)
|
|
}
|
|
|
|
// AltitudeInt returns the altitude as integer.
|
|
func (p Position) AltitudeInt() int {
|
|
return int(math.Round(p.Altitude))
|
|
}
|
|
|
|
// Km calculates the distance to another position in km.
|
|
func (p Position) Km(other Position) float64 {
|
|
return math.Abs(Km(p, other))
|
|
}
|
|
|
|
// InRange tests if coordinates are within a certain range of the position.
|
|
func (p *Position) InRange(lat, lng, r float64) bool {
|
|
switch {
|
|
case lat == 0 && lng == 0:
|
|
return false
|
|
case p.Lat == 0 && p.Lng == 0:
|
|
return false
|
|
case lat < p.Lat-r || lat > p.Lat+r:
|
|
return false
|
|
case lng < p.Lng-r || lng > p.Lng+r:
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Randomize adds a random offset to the coordinates.
|
|
func (p *Position) Randomize(diameter float64) {
|
|
if diameter <= 0 {
|
|
// Nothing to do.
|
|
return
|
|
}
|
|
|
|
// Randomize latitude and longitude.
|
|
p.Lat = Randomize(p.Lat, diameter)
|
|
p.Lng = Randomize(p.Lng, diameter)
|
|
|
|
// Estimate change in accuracy.
|
|
meter := int(math.Round(diameter / Meter))
|
|
|
|
// Increase accuracy if needed.
|
|
if p.Accuracy < meter {
|
|
p.Accuracy = meter
|
|
}
|
|
}
|