find-stone-beer / commit
Added code to round a coordinate value in such a way, that the accuracy of the coordinate is greater than a given value.
coord-round.go | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++ coord-round_test.go | 42 ++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/coord-round.go b/coord-round.go new file mode 100644 index 0000000..e73e154 --- /dev/null +++ b/coord-round.go @@ -0,0 +1,92 @@ +package main + +import ( + "fmt" + "math" + "strings" + + "github.com/jftuga/geodist" +) + +type ImperialDistance struct { + Miles float64 +} + +func (d ImperialDistance) GetFeet() float64 { + feet := d.Miles * 5280 + return feet +} +func (d ImperialDistance) GetInches() float64 { + inches := d.GetFeet() * 12 + return inches +} +func (d ImperialDistance) ToString() string { + miles := Round(d.Miles, 2) + feet := Round(d.GetFeet(), 2) + inches := Round(d.GetInches(), 2) + + if miles > 1 { + return fmt.Sprintf("%.2fmi", miles) + } + + if feet > 1 { + return fmt.Sprintf("%.2fft", feet) + } + + return fmt.Sprintf("%.2fin", inches) +} + +func GetPrecisionDistance(coord geodist.Coord) (ImperialDistance, error) { + latDelta := getDelta(coord.Lat) + minLat := coord.Lat - latDelta + maxLat := coord.Lat + latDelta + + lonDelta := getDelta(coord.Lon) + minLon := coord.Lon - lonDelta + maxLon := coord.Lon + lonDelta + + minCoord := geodist.Coord{Lat: minLat, Lon: minLon} + maxCoord := geodist.Coord{Lat: maxLat, Lon: maxLon} + distMiles, _, err := geodist.VincentyDistance(minCoord, maxCoord) + if err != nil { + return ImperialDistance{}, err + } + id := ImperialDistance{Miles: distMiles} + return id, nil +} + +func getDelta(f float64) float64 { + text := fmt.Sprintf("%g", f) + if !strings.Contains(text, ".") { + return 1 + } + afterDecimal := strings.Split(text, ".")[1] + afterDecimal = strings.TrimRight(afterDecimal, "0") + decimals := len(afterDecimal) + delta := math.Pow(10, -float64(decimals)) + return delta +} + +func RoundCoord(c geodist.Coord, id ImperialDistance) (geodist.Coord, error) { + var lastRoundedCoord geodist.Coord + for decimalPlaces := 1; decimalPlaces < 7; decimalPlaces++ { + lat := Round(c.Lat, decimalPlaces) + lon := Round(c.Lon, decimalPlaces) + roundedCoord := geodist.Coord{Lat: lat, Lon: lon} + roundedId, err := GetPrecisionDistance(roundedCoord) + if err != nil { + return geodist.Coord{}, err + } + if roundedId.Miles < id.Miles { + return lastRoundedCoord, nil + } + lastRoundedCoord = roundedCoord + } + + return geodist.Coord{}, fmt.Errorf("could not round coord: %v", c) +} + +func Round(f float64, places int) float64 { + shift := math.Pow(10, float64(places)) + return math.Round(f*shift) / shift +} diff --git a/coord-round_test.go b/coord-round_test.go new file mode 100644 index 0000000..697bbb3 --- /dev/null +++ b/coord-round_test.go @@ -0,0 +1,42 @@ +package main + +import ( + "testing" + + "github.com/jftuga/geodist" + "github.com/stretchr/testify/assert" +) + +func TestCoordRound(t *testing.T) { + // 47.6254333,-122.1632314 + + assert.Equal(t, "1.06in", getPrecisionDistance(t, geodist.Coord{Lat: 47.6254333, Lon: -122.1632314}).ToString()) + assert.Equal(t, "10.57in", getPrecisionDistance(t, geodist.Coord{Lat: 47.625433, Lon: -122.163231}).ToString()) + assert.Equal(t, "5.92in", getPrecisionDistance(t, geodist.Coord{Lat: 47.625433111111, Lon: -122.163231}).ToString()) + assert.Equal(t, "880.62ft", getPrecisionDistance(t, geodist.Coord{Lat: 47.625, Lon: -122.163}).ToString()) + assert.Equal(t, "167.38mi", getPrecisionDistance(t, geodist.Coord{Lat: 47, Lon: -122}).ToString()) + + coord := geodist.Coord{Lat: 47.6254333, Lon: -122.1632314} + roundingDistance := ImperialDistance{Miles: 0.25} + roundedCoord, err := RoundCoord(coord, roundingDistance) + assert.Nil(t, err) + precisionDistance := getPrecisionDistance(t, roundedCoord) + assert.Greater(t, precisionDistance.Miles, roundingDistance.Miles) +} + +func getPrecisionDistance(t *testing.T, coord geodist.Coord) ImperialDistance { + latDelta := getDelta(coord.Lat) + minLat := coord.Lat - latDelta + maxLat := coord.Lat + latDelta + + lonDelta := getDelta(coord.Lon) + minLon := coord.Lon - lonDelta + maxLon := coord.Lon + lonDelta + + minCoord := geodist.Coord{Lat: minLat, Lon: minLon} + maxCoord := geodist.Coord{Lat: maxLat, Lon: maxLon} + distMiles, _, err := geodist.VincentyDistance(minCoord, maxCoord) + assert.Nil(t, err) + id := ImperialDistance{Miles: distMiles} + return id +}