From 501c78f327f307226bdfdda29bc63ce6e0c3fbc3 Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Fri, 8 Oct 2021 19:55:40 +0300 Subject: [PATCH] [#902] util/locode: Fix parsing minutes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert minutes of received coordinates to decimal parts of degree, and do not use decimal part of float as storage for minutes: "5915N 01806E" is 59.25N 18.10E, not 59°15'N 18°06'E. Signed-off-by: Pavel Karpy --- pkg/util/locode/db/point.go | 40 ++++++++++++++++++------- pkg/util/locode/db/point_test.go | 51 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 11 deletions(-) create mode 100644 pkg/util/locode/db/point_test.go diff --git a/pkg/util/locode/db/point.go b/pkg/util/locode/db/point.go index d1371bf19..78590d61a 100644 --- a/pkg/util/locode/db/point.go +++ b/pkg/util/locode/db/point.go @@ -1,8 +1,8 @@ package locodedb import ( + "fmt" "strconv" - "strings" locodecolumn "github.com/nspcc-dev/neofs-node/pkg/util/locode/column" ) @@ -40,12 +40,9 @@ func PointFromCoordinates(crd *locodecolumn.Coordinates) (*Point, error) { cLatDeg := cLat.Degrees() cLatMnt := cLat.Minutes() - lat, err := strconv.ParseFloat(strings.Join([]string{ - string(cLatDeg[:]), - string(cLatMnt[:]), - }, "."), 64) + lat, err := toDecimal(cLatDeg[:], cLatMnt[:]) if err != nil { - return nil, err + return nil, fmt.Errorf("could not parse latitude: %w", err) } if !cLat.Hemisphere().North() { @@ -56,12 +53,9 @@ func PointFromCoordinates(crd *locodecolumn.Coordinates) (*Point, error) { cLngDeg := cLng.Degrees() cLngMnt := cLng.Minutes() - lng, err := strconv.ParseFloat(strings.Join([]string{ - string(cLngDeg[:]), - string(cLngMnt[:]), - }, "."), 64) + lng, err := toDecimal(cLngDeg[:], cLngMnt[:]) if err != nil { - return nil, err + return nil, fmt.Errorf("could not parse longitude: %w", err) } if !cLng.Hemisphere().East() { @@ -73,3 +67,27 @@ func PointFromCoordinates(crd *locodecolumn.Coordinates) (*Point, error) { lng: lng, }, nil } + +func toDecimal(intRaw, minutesRaw []byte) (float64, error) { + integer, err := strconv.ParseFloat(string(intRaw), 64) + if err != nil { + return 0, fmt.Errorf("could not parse integer part: %w", err) + } + + decimal, err := minutesToDegrees(minutesRaw) + if err != nil { + return 0, fmt.Errorf("could not parse decimal part: %w", err) + } + + return integer + decimal, nil +} + +// minutesToDegrees converts minutes to decimal part of a degree +func minutesToDegrees(raw []byte) (float64, error) { + minutes, err := strconv.ParseFloat(string(raw), 64) + if err != nil { + return 0, err + } + + return minutes / 60, nil +} diff --git a/pkg/util/locode/db/point_test.go b/pkg/util/locode/db/point_test.go new file mode 100644 index 000000000..b6f5c627a --- /dev/null +++ b/pkg/util/locode/db/point_test.go @@ -0,0 +1,51 @@ +package locodedb + +import ( + "testing" + + locodecolumn "github.com/nspcc-dev/neofs-node/pkg/util/locode/column" + "github.com/stretchr/testify/require" +) + +func TestPointFromCoordinates(t *testing.T) { + testCases := []struct { + latGot, longGot string + latWant, longWant float64 + }{ + { + latGot: "5915N", + longGot: "01806E", + latWant: 59.25, + longWant: 18.10, + }, + { + latGot: "1000N", + longGot: "02030E", + latWant: 10.00, + longWant: 20.50, + }, + { + latGot: "0145S", + longGot: "03512W", + latWant: -01.75, + longWant: -35.20, + }, + } + + var ( + crd *locodecolumn.Coordinates + point *Point + err error + ) + + for _, test := range testCases { + crd, err = locodecolumn.CoordinatesFromString(test.latGot + " " + test.longGot) + require.NoError(t, err) + + point, err = PointFromCoordinates(crd) + require.NoError(t, err) + + require.Equal(t, test.latWant, point.Latitude()) + require.Equal(t, test.longWant, point.Longitude()) + } +}