-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(server): implement geo field's position (#1176)
* wip: position value * refactor to value * refactor 2 * wip: unit tests * fix: mapToFloat64 * add Test_propertyPosition_ToValue * add propertyPosition to registry * add more unit tests * fix: float32 to float64 conversion * add decimal numbers to unit tests * fix: bad import in list_test
- Loading branch information
1 parent
3dffe8b
commit 2ae9729
Showing
4 changed files
with
383 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package value | ||
|
||
import ( | ||
"encoding/json" | ||
"slices" | ||
"strconv" | ||
|
||
"github.com/samber/lo" | ||
) | ||
|
||
const TypePoint Type = "point" | ||
// const TypeLineString Type = "lineString" | ||
// const TypePolygon Type = "polygon" | ||
|
||
type propertyPosition struct{} | ||
|
||
type Position = []float64 | ||
|
||
func (p *propertyPosition) ToValue(i any) (any, bool) { | ||
if i == nil { | ||
return nil, true | ||
} | ||
|
||
switch v := i.(type) { | ||
case []float64: | ||
return v, true | ||
case []float32: | ||
return mapFloat32ToFloat64(v) | ||
case []int: | ||
return mapIntegersToFloat64(v), true | ||
case []int8: | ||
return mapIntegersToFloat64(v), true | ||
case []int16: | ||
return mapIntegersToFloat64(v), true | ||
case []int32: | ||
return mapIntegersToFloat64(v), true | ||
case []int64: | ||
return mapIntegersToFloat64(v), true | ||
case []uint: | ||
return mapIntegersToFloat64(v), true | ||
case []uint8: | ||
return mapIntegersToFloat64(v), true | ||
case []uint16: | ||
return mapIntegersToFloat64(v), true | ||
case []uint32: | ||
return mapIntegersToFloat64(v), true | ||
case []uint64: | ||
return mapIntegersToFloat64(v), true | ||
case []uintptr: | ||
return mapIntegersToFloat64(v), true | ||
case []json.Number: | ||
return mapJSONNumbersToFloat64(v) | ||
case []string: | ||
return mapStringsToFloat64(v) | ||
default: | ||
return nil, false | ||
} | ||
} | ||
|
||
func mapIntegersToFloat64[T any](v []T) []float64 { | ||
return lo.Map(v, func(n T, _ int) float64 { | ||
return intToFloat64(n) | ||
}) | ||
} | ||
|
||
func intToFloat64(v any) float64 { | ||
switch val := v.(type) { | ||
case int: | ||
return float64(val) | ||
case int8: | ||
return float64(val) | ||
case int16: | ||
return float64(val) | ||
case int32: | ||
return float64(val) | ||
case int64: | ||
return float64(val) | ||
case uint: | ||
return float64(val) | ||
case uint8: | ||
return float64(val) | ||
case uint16: | ||
return float64(val) | ||
case uint32: | ||
return float64(val) | ||
case uint64: | ||
return float64(val) | ||
case uintptr: | ||
return float64(val) | ||
default: | ||
return 0 | ||
} | ||
} | ||
|
||
func mapStringsToFloat64(v []string) ([]float64, bool) { | ||
var err error | ||
s := lo.Map(v, func(s string, _ int) float64 { | ||
vv, err2 := strconv.ParseFloat(s, 64) | ||
if err2 != nil { | ||
err = err2 | ||
return 0 | ||
} | ||
return vv | ||
}) | ||
if err != nil { | ||
return nil, false | ||
} | ||
return s, true | ||
} | ||
|
||
func mapJSONNumbersToFloat64(v []json.Number) ([]float64, bool) { | ||
var err error | ||
s := lo.Map(v, func(n json.Number, _ int) float64 { | ||
vv, err2 := n.Float64() | ||
if err2 != nil { | ||
err = err2 | ||
return 0 | ||
} | ||
return vv | ||
}) | ||
if err != nil { | ||
return nil, false | ||
} | ||
return s, true | ||
} | ||
|
||
func mapFloat32ToFloat64(v []float32) ([]float64, bool) { | ||
var err error | ||
s := lo.Map(v, func(n float32, _ int) float64 { | ||
ss := strconv.FormatFloat(float64(n), 'f', -1, 32) | ||
vv, err2 := strconv.ParseFloat(ss, 64) | ||
if err2 != nil { | ||
err = err2 | ||
return 0 | ||
} | ||
return vv | ||
}) | ||
if err != nil { | ||
return nil, false | ||
} | ||
return s, true | ||
} | ||
|
||
func (*propertyPosition) ToInterface(v any) (any, bool) { | ||
return v, true | ||
} | ||
|
||
func (*propertyPosition) Validate(i any) bool { | ||
v, ok := i.(Position) | ||
if !ok { | ||
return false | ||
} | ||
return len(v) >= 2 | ||
} | ||
|
||
func (*propertyPosition) Equal(v, w any) bool { | ||
vv := v.(Position) | ||
ww := w.(Position) | ||
if len(vv) != len(ww) { | ||
return false | ||
} | ||
return slices.Equal(vv, ww) | ||
} | ||
|
||
func (*propertyPosition) IsEmpty(i any) bool { | ||
if i == nil { | ||
return true | ||
} | ||
v, ok := i.(Position) | ||
if !ok { | ||
return true | ||
} | ||
return len(v) == 0 | ||
} | ||
|
||
func (v *Value) ValuePosition() (vv Position, ok bool) { | ||
if v == nil { | ||
return | ||
} | ||
vv, ok = v.v.(Position) | ||
if !ok { | ||
return nil, false | ||
} | ||
if len(vv) > 3 { | ||
return vv[:3], true // TODO: need to think about his case | ||
} | ||
return | ||
} | ||
|
||
func (m *Multiple) ValuesPosition() (vv []Position, ok bool) { | ||
if m == nil { | ||
return | ||
} | ||
vv = lo.FilterMap(m.v, func(v *Value, _ int) (Position, bool) { | ||
return v.ValuePosition() | ||
}) | ||
if len(vv) != len(m.v) { | ||
return nil, false | ||
} | ||
return vv, true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package value | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_propertyPosition_ToValue(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
arg any | ||
want1 any | ||
want2 bool | ||
}{ | ||
{ | ||
name: "nil", | ||
arg: nil, | ||
want1: nil, | ||
want2: true, | ||
}, | ||
{ | ||
name: "string", | ||
arg: []string{"1.12345", "2.12345"}, | ||
want1: []float64{1.12345, 2.12345}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "json.Number", | ||
arg: []json.Number{"1.12345", "2.12345"}, | ||
want1: []float64{1.12345, 2.12345}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "float64", | ||
arg: []float64{1.12345, 2.12345}, | ||
want1: []float64{1.12345, 2.12345}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "float32", | ||
arg: []float32{1.1234567, 2.12345}, | ||
want1: []float64{1.1234567, 2.12345}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "int", | ||
arg: []int{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "int8", | ||
arg: []int8{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "int16", | ||
arg: []int16{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "int32", | ||
arg: []int32{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "int64", | ||
arg: []int64{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uint", | ||
arg: []uint{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uint8", | ||
arg: []uint8{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uint16", | ||
arg: []uint16{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uint32", | ||
arg: []uint32{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uint64", | ||
arg: []uint64{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
{ | ||
name: "uintptr", | ||
arg: []uintptr{1, 2}, | ||
want1: []float64{1.0, 2.0}, | ||
want2: true, | ||
}, | ||
} | ||
|
||
for _, tt := range tests { | ||
tt := tt | ||
t.Run(tt.name, func(t *testing.T) { | ||
t.Parallel() | ||
p := &propertyPosition{} | ||
got1, got2 := p.ToValue(tt.arg) | ||
assert.Equal(t, tt.want1, got1) | ||
assert.Equal(t, tt.want2, got2) | ||
}) | ||
} | ||
} | ||
|
||
func Test_propertyPosition_ToInterface(t *testing.T) { | ||
v := []float64{1.1, 2.1, 3.1} | ||
tt, ok := (&propertyPosition{}).ToInterface(v) | ||
assert.Equal(t, v, tt) | ||
assert.Equal(t, true, ok) | ||
} | ||
|
||
func Test_propertyPosition_IsEmpty(t *testing.T) { | ||
assert.True(t, (&propertyPosition{}).IsEmpty([]float64{})) | ||
assert.False(t, (&propertyPosition{}).IsEmpty([]float64{1.1, 2.1, 3.1})) | ||
} | ||
|
||
func Test_propertyPosition_Validate(t *testing.T) { | ||
assert.True(t, (&propertyPosition{}).Validate([]float64{1.1, 2.1, 3.1})) | ||
assert.False(t, (&propertyPosition{}).Validate([]float64{1.1})) | ||
assert.False(t, (&propertyPosition{}).Validate([]int{1, 2, 3})) | ||
assert.False(t, (&propertyPosition{}).Validate([]string{"1", "2", "3"})) | ||
assert.False(t, (&propertyPosition{}).Validate(1)) | ||
} | ||
|
||
func Test_propertyPosition_Equal(t *testing.T) { | ||
ps := &propertyPosition{} | ||
assert.True(t, ps.Equal(Position{1.1, 2.1, 3.1}, Position{1.1, 2.1, 3.1})) | ||
ps1 := &propertyPosition{} | ||
assert.False(t, ps1.Equal(Position{1.1, 2.1, 3.1}, Position{1.1, 2.1})) | ||
} | ||
|
||
func TestValue_ValuePosition(t *testing.T) { | ||
var v *Value | ||
got, ok := v.ValuePosition() | ||
assert.Equal(t, []float64(nil), got) | ||
assert.Equal(t, false, ok) | ||
|
||
v = &Value{ | ||
v: []float64{1.1, 2.1, 3.1}, | ||
} | ||
got, ok = v.ValuePosition() | ||
assert.Equal(t, []float64{1.1, 2.1, 3.1}, got) | ||
assert.Equal(t, true, ok) | ||
} | ||
|
||
func TestMultiple_ValuesPosition(t *testing.T) { | ||
var m *Multiple | ||
got, ok := m.ValuesPosition() | ||
var expected []Position | ||
assert.Equal(t, expected, got) | ||
assert.False(t, ok) | ||
|
||
m = NewMultiple(TypePoint, []any{Position{1.1, 2.1, 3.1}, Position{1.1, 2.1, 3.1}, Position{1.1, 2.1, 3.1}}) | ||
expected = []Position{{1.1, 2.1, 3.1}, {1.1, 2.1, 3.1}, {1.1, 2.1, 3.1}} | ||
got, ok = m.ValuesPosition() | ||
assert.Equal(t, expected, got) | ||
assert.True(t, ok) | ||
} |
Oops, something went wrong.