Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 75 additions & 68 deletions encoding/wkt/internal/token/token_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,58 +76,59 @@ func TestParsePointe(t *testing.T) {
exp *geom.Point
err error
}
fn := func(t *testing.T, tc tcase) {
tt := NewT(strings.NewReader(tc.input))
pt, err := tt.ParsePoint()
if msg, expstr, gotstr, ok := assertError(tc.err, err); !ok {
if msg != "" {
t.Errorf("%v, expected %v got %v", msg, expstr, gotstr)
fn := func(tc tcase) (string, func(t *testing.T)) {
return tc.input, func(t *testing.T) {
tt := NewT(strings.NewReader(tc.input))
pt, err := tt.ParsePoint()
if msg, expstr, gotstr, ok := assertError(tc.err, err); !ok {
if msg != "" {
t.Errorf("%v, expected %v got %v", msg, expstr, gotstr)
}
return
}
if !reflect.DeepEqual(tc.exp, pt) {
t.Errorf("point values, expected %v got %v", tc.exp, pt)
}
return
}
if !reflect.DeepEqual(tc.exp, pt) {
t.Errorf("point values, expected %v got %v", tc.exp, pt)
}
}
tests := map[string]tcase{
"POINT EMPTY": {
tests := [...]tcase{
{
input: "POINT EMPTY",
},
"POINT EMPTY ": {
{
input: "POINT EMPTY ",
},
"POINT ( 1 2 )": {
{
input: "POINT ( 1 2 )",
exp: &geom.Point{1, 2},
},
" POINT ( 1 2 ) ": {
{
input: " POINT ( 1 2 ) ",
exp: &geom.Point{1, 2},
},
" POINT ZM ( 1 2 3 4 ) ": {
{
input: " POINT ZM ( 1 2 3 4 ) ",
exp: &geom.Point{1, 2},
},
"POINT 1 2": {
{
input: "POINT 1 2",
err: fmt.Errorf("expected to find “(” or “EMPTY”"),
},
"POINT ( 1 2": {
{
input: "POINT ( 1 2",
err: fmt.Errorf("expected to find “)”"),
},
"POINT ( 1 )": {
{
input: "POINT ( 1 )",
err: fmt.Errorf("expected to have at least 2 coordinates in a POINT"),
},
"POINT ( 1 2 3 4 5 )": {
{
input: "POINT ( 1 2 3 4 5 )",
err: fmt.Errorf("expected to have no more then 4 coordinates in a POINT"),
},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) { fn(t, tc) })
for _, tc := range tests {
t.Run(fn(tc))
}
}

Expand All @@ -138,35 +139,38 @@ func TestParseMultiPointe(t *testing.T) {
err error
}

fn := func(t *testing.T, tc tcase) {
t.Parallel()
tt := NewT(strings.NewReader(tc.input))
mpt, err := tt.ParseMultiPoint()
if msg, expstr, gotstr, ok := assertError(tc.err, err); !ok {
if msg != "" {
t.Errorf("%v, expected %v got %v", msg, expstr, gotstr)
fn := func(tc tcase) (string, func(t *testing.T)) {
return tc.input, func(t *testing.T) {
t.Parallel()
tt := NewT(strings.NewReader(tc.input))
mpt, err := tt.ParseMultiPoint()
if msg, expstr, gotstr, ok := assertError(tc.err, err); !ok {
if msg != "" {
t.Errorf("%v, expected %v got %v", msg, expstr, gotstr)
}
return
}
if !reflect.DeepEqual(tc.exp, mpt) {
t.Errorf("did not get correct multipoint values, expected %v got %v", tc.exp, mpt)
}
return
}
if !reflect.DeepEqual(tc.exp, mpt) {
t.Errorf("did not get correct multipoint values, expected %v got %v", tc.exp, mpt)
}

}
tests := map[string]tcase{
"empty": {input: "MultiPoint EMPTY"},
"without pren": {
tests := [...]tcase{
{
input: "MultiPoint EMPTY",
},
{
input: "MULTIPOINT ( 10 10, 12 12 )",
exp: geom.MultiPoint{{10, 10}, {12, 12}},
},
"with pren": {
{
input: "MULTIPOINT ( (10 10), (12 12) )",
exp: geom.MultiPoint{{10, 10}, {12, 12}},
},
}
for name, test := range tests {
test := test // make copy
t.Run(name, func(t *testing.T) { fn(t, test) })
for _, test := range tests {
t.Run(fn(test))
}
}

Expand All @@ -176,34 +180,37 @@ func TestParseFloat64(t *testing.T) {
exp float64
err error
}
fn := func(t *testing.T, tc tcase) {
tt := NewT(strings.NewReader(tc.input))
f, err := tt.ParseFloat64()
if tc.err != err {
t.Errorf("error, expected %v got %v", tc.err, err)
}
if tc.err != nil {
return
}
if tc.exp != f {
t.Errorf("prase float64 expected %v got %v", tc.exp, f)
fn := func(tc tcase) (string, func(t *testing.T)) {
return tc.input, func(t *testing.T) {
tt := NewT(strings.NewReader(tc.input))
f, err := tt.ParseFloat64()
if tc.err != err {
t.Errorf("error, expected %v got %v", tc.err, err)
}
if tc.err != nil {
return
}
if tc.exp != f {
t.Errorf("parse for '%v' float64 expected %v got %v", tc.input, tc.exp, f)
}
}
}
tests := map[string]tcase{
"-12": {input: "-12", exp: -12.0},
"0": {input: "0", exp: 0.0},
"+1000.00": {input: "+1000.00", exp: 1000.0},
"-12000.00": {input: "-12000.00", exp: -12000.0},
"10.005e5": {input: "10.005e5", exp: 10.005e5},
"10.005e+5": {input: "10.005e+5", exp: 10.005e5},
"10.005e+05": {input: "10.005e+05", exp: 10.005e5},
"1.0005e+6": {input: "1.0005e+6", exp: 10.005e5},
"1.0005e+06": {input: "1.0005e+06", exp: 10.005e5},
"1.0005e-06": {input: "1.0005e-06", exp: 1.0005e-06},
"1.0005e-06a": {input: "1.0005e-06a", exp: 1.0005e-06},
}
for name, tc := range tests {
tc := tc
t.Run(name, func(t *testing.T) { fn(t, tc) })
tests := []tcase{
{input: "-12", exp: -12.0},
{input: "-.12", exp: -0.12},
{input: "-0.12", exp: -0.12},
{input: "0", exp: 0.0},
{input: "+1000.00", exp: 1000.0},
{input: "-12000.00", exp: -12000.0},
{input: "10.005e5", exp: 10.005e5},
{input: "10.005e+5", exp: 10.005e5},
{input: "10.005e+05", exp: 10.005e5},
{input: "1.0005e+6", exp: 10.005e5},
{input: "1.0005e+06", exp: 10.005e5},
{input: "1.0005e-06", exp: 1.0005e-06},
{input: "1.0005e-06a", exp: 1.0005e-06},
}
for _, tc := range tests {
t.Run(fn(tc))
}
}
92 changes: 90 additions & 2 deletions encoding/wkt/wkt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package wkt
import (
"fmt"
"reflect"
"strconv"
"strings"

"github.com/go-spatial/geom"
"github.com/go-spatial/geom/cmp"
)

func isNil(a interface{}) bool {
Expand Down Expand Up @@ -98,6 +100,19 @@ func isCollectionerEmpty(col geom.Collectioner) bool {
return true
}

func formatFloat(f float64) string {
s := strconv.FormatFloat(f, 'f', 3, 64)
if s[len(s)-3:] == "000" {
// remove the .
return s[:len(s)-4]
}
return s
}

func formatPoint(pt [2]float64) string {
return formatFloat(pt[0]) + " " + formatFloat(pt[1])
}

/*
This purpose of this file is to house the wkt functions. These functions are
use to take a tagola.Geometry and convert it to a wkt string. It will, also,
Expand All @@ -109,8 +124,10 @@ func _encode(geo geom.Geometry) string {
switch g := geo.(type) {

case geom.Pointer:
xy := g.XY()
return fmt.Sprintf("%v %v", xy[0], xy[1])
return formatPoint(g.XY())

case [2]float64:
return formatPoint(g)

case geom.MultiPointer:
var points []string
Expand Down Expand Up @@ -142,6 +159,10 @@ func _encode(geo geom.Geometry) string {
if len(l) == 0 {
continue
}
if !cmp.PointEqual(l[0], l[len(l)-1]) {
// Dup the first point to close the polygon.
l = append(l, l[0])
}
rings = append(rings, _encode(geom.LineString(l)))
}
return "(" + strings.Join(rings, ",") + ")"
Expand All @@ -165,45 +186,58 @@ func _encode(geo geom.Geometry) string {
func Encode(geo geom.Geometry) (string, error) {
switch g := geo.(type) {
default:

return "", geom.ErrUnknownGeometry{geo}

case geom.Pointer:

// POINT( 10 10)
if isNil(g) {
return "POINT EMPTY", nil
}
return "POINT (" + _encode(geo) + ")", nil

case [2]float64:

return "POINT (" + _encode(geo) + ")", nil

case geom.MultiPointer:

if isNil(g) || len(g.Points()) == 0 {
return "MULTIPOINT EMPTY", nil
}
return "MULTIPOINT " + _encode(geo), nil

case geom.LineStringer:

if isNil(g) || len(g.Verticies()) == 0 {
return "LINESTRING EMPTY", nil
}
return "LINESTRING " + _encode(geo), nil

case geom.MultiLineStringer:

if isMultiLineStringerEmpty(g) {
return "MULTILINESTRING EMPTY", nil
}
return "MULTILINESTRING " + _encode(geo), nil

case geom.Polygoner:

if isPolygonerEmpty(g) {
return "POLYGON EMPTY", nil
}
return "POLYGON " + _encode(geo), nil

case geom.MultiPolygoner:

if isMultiPolygonerEmpty(g) {
return "MULTIPOLYGON EMPTY", nil
}
return "MULTIPOLYGON " + _encode(geo), nil

case geom.Collectioner:

if isCollectionerEmpty(g) {
return "GEOMETRYCOLLECTION EMPTY", nil
}
Expand All @@ -216,7 +250,61 @@ func Encode(geo geom.Geometry) (string, error) {
geometries = append(geometries, s)
}
return "GEOMETRYCOLLECTION (" + strings.Join(geometries, ",") + ")", nil

case geom.Line:

return Encode(geom.LineString(g[:]))

case [2][2]float64:

return Encode(geom.LineString(g[:]))

case [][2]float64:

return Encode(geom.LineString(g))

case []geom.Line:

ml := make(geom.MultiLineString, len(g))
for i := range g {
ml[i] = g[i][:]
}
return Encode(ml)

case []geom.Point:
mp := make(geom.MultiPoint, len(g))
for i := range g {
mp[i] = [2]float64(g[i])
}
return Encode(mp)

case geom.Triangle:
// treat a triangle as polygon
return Encode(geom.Polygon{g[:]})

case []geom.Triangle:
mp := make(geom.MultiPolygon, len(g))
for i := range g {
mp[i] = geom.Polygon{g[i][:]}
}
return Encode(mp)
case geom.Extent:
// treat an extent as a ploygon
return Encode(g.AsPolygon())
case *geom.Extent:
// treat an extent as a ploygon
if g != nil {
return Encode(g.AsPolygon())
}
return Encode(geom.Polygon{})
}
}
func MustEncode(geo geom.Geometry) (str string) {
var err error
if str, err = Encode(geo); err != nil {
panic(fmt.Sprintf("unable to encode %T as wkt", geo))
}
return str
}

func Decode(text string) (geo geom.Geometry, err error) {
Expand Down
Loading