fuzzyworld

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.

1ab63e540d082cb3f234e6a9726c6c59a7350df8 · Pavel Safronov · 2023-08-27 11:04:18 -0700 · parents 8da39f4a289d

Added code to round a coordinate value in such a way, that the accuracy of the coordinate is greater than a given value.
This allows rounding a coordinate so that it (for example) is accurate to within .25 miles, instead of whatever accuracy was there originally.
 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
+}