find-stone-beer / files / coord-round.go
coord-round.go
1ab63e54 2023-08-27 Pavel Safronov | 1 | package main |
| 2 | ||
| 3 | import ( | |
| 4 | "fmt" | |
| 5 | "math" | |
| 6 | "strings" | |
| 7 | ||
| 8 | "github.com/jftuga/geodist" | |
| 9 | ) | |
| 10 | ||
| 11 | type ImperialDistance struct { | |
| 12 | Miles float64 | |
| 13 | } | |
| 14 | ||
| 15 | func (d ImperialDistance) GetFeet() float64 { | |
| 16 | feet := d.Miles * 5280 | |
| 17 | return feet | |
| 18 | } | |
| 19 | func (d ImperialDistance) GetInches() float64 { | |
| 20 | inches := d.GetFeet() * 12 | |
| 21 | return inches | |
| 22 | } | |
| 23 | func (d ImperialDistance) ToString() string { | |
| 24 | miles := Round(d.Miles, 2) | |
| 25 | feet := Round(d.GetFeet(), 2) | |
| 26 | inches := Round(d.GetInches(), 2) | |
| 27 | ||
| 28 | if miles > 1 { | |
| 29 | return fmt.Sprintf("%.2fmi", miles) | |
| 30 | } | |
| 31 | ||
| 32 | if feet > 1 { | |
| 33 | return fmt.Sprintf("%.2fft", feet) | |
| 34 | } | |
| 35 | ||
| 36 | return fmt.Sprintf("%.2fin", inches) | |
| 37 | } | |
| 38 | ||
| 39 | func GetPrecisionDistance(coord geodist.Coord) (ImperialDistance, error) { | |
| 40 | latDelta := getDelta(coord.Lat) | |
| 41 | minLat := coord.Lat - latDelta | |
| 42 | maxLat := coord.Lat + latDelta | |
| 43 | ||
| 44 | lonDelta := getDelta(coord.Lon) | |
| 45 | minLon := coord.Lon - lonDelta | |
| 46 | maxLon := coord.Lon + lonDelta | |
| 47 | ||
| 48 | minCoord := geodist.Coord{Lat: minLat, Lon: minLon} | |
| 49 | maxCoord := geodist.Coord{Lat: maxLat, Lon: maxLon} | |
| 50 | distMiles, _, err := geodist.VincentyDistance(minCoord, maxCoord) | |
| 51 | if err != nil { | |
| 52 | return ImperialDistance{}, err | |
| 53 | } | |
| 54 | id := ImperialDistance{Miles: distMiles} | |
| 55 | return id, nil | |
| 56 | } | |
| 57 | ||
| 58 | func getDelta(f float64) float64 { | |
| 59 | text := fmt.Sprintf("%g", f) | |
| 60 | if !strings.Contains(text, ".") { | |
| 61 | return 1 | |
| 62 | } | |
| 63 | afterDecimal := strings.Split(text, ".")[1] | |
| 64 | afterDecimal = strings.TrimRight(afterDecimal, "0") | |
| 65 | decimals := len(afterDecimal) | |
| 66 | delta := math.Pow(10, -float64(decimals)) | |
| 67 | return delta | |
| 68 | } | |
| 69 | ||
| 70 | func RoundCoord(c geodist.Coord, id ImperialDistance) (geodist.Coord, error) { | |
| 71 | var lastRoundedCoord geodist.Coord | |
| 72 | for decimalPlaces := 1; decimalPlaces < 7; decimalPlaces++ { | |
| 73 | lat := Round(c.Lat, decimalPlaces) | |
| 74 | lon := Round(c.Lon, decimalPlaces) | |
| 75 | roundedCoord := geodist.Coord{Lat: lat, Lon: lon} | |
| 76 | roundedId, err := GetPrecisionDistance(roundedCoord) | |
| 77 | if err != nil { | |
| 78 | return geodist.Coord{}, err | |
| 79 | } | |
| 80 | if roundedId.Miles < id.Miles { | |
| 81 | return lastRoundedCoord, nil | |
| 82 | } | |
| 83 | lastRoundedCoord = roundedCoord | |
| 84 | } | |
| 85 | ||
| 86 | return geodist.Coord{}, fmt.Errorf("could not round coord: %v", c) | |
| 87 | } | |
| 88 | ||
| 89 | func Round(f float64, places int) float64 { | |
| 90 | shift := math.Pow(10, float64(places)) | |
| 91 | return math.Round(f*shift) / shift | |
| 92 | } |