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
487 changes: 487 additions & 0 deletions encoding/wkt/wkt_decode.go

Large diffs are not rendered by default.

183 changes: 183 additions & 0 deletions encoding/wkt/wkt_decode_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package wkt

import (
"strings"
"testing"
"errors"

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

func TestDecode(t *testing.T) {
type tcase struct {
in string
out geom.Geometry
err error
}

fn := func(tc tcase) func(t *testing.T) {
return func(t *testing.T) {
decoder := NewDecoder(strings.NewReader(tc.in))
out, err := decoder.Decode()
if (err == nil) != (tc.err == nil) {
t.Errorf("incorrect error %v, expected %v", err, tc.err)
return
} else if err != nil {
if err.Error() != tc.err.Error() {
t.Errorf("incorrect error %v, expected %v", err, tc.err)
}
return
}
if !cmp.GeometryEqual(out, tc.out) {
t.Errorf("incorrect geometry %v, expected %v", out, tc.out)
}
}
}

tcases := map[string]tcase{
"point 0": {
in: "POINT(0 0)",
out: geom.Point{0, 0},
err: nil,
},
"point 1": {
in: "POINT(0 99.99)",
out: geom.Point{0, 99.99},
err: nil,
},
"point 2": {
in: "POINT(99.99 0)",
out: geom.Point{99.99, 0},
err: nil,
},
"point 3": {
in: "POINT(99.99 42.0)",
out: geom.Point{99.99, 42.0},
err: nil,
},
"point 4": {
in: "POINT()",
err: errors.New("syntax error (1:8): POINT cannot be empty"),
},
"point 5": {
in: "POINT ( 1 1 )",
out: geom.Point{1, 1},
err: nil,
},
"point 6": {
in: "POINT(0 0, 1 1)",
err: errors.New("syntax error (1:16): too many points in POINT, 2"),
},
"point 7": {
in: "point \t(\n0 \t\n 0 \f \r ) ",
out: geom.Point{0, 0},
},
"point 8": {
in: "POINT(1.3E100 2.3E-35)",
out: geom.Point{1.3e100, 2.3e-35},
},
"multipoint 0": {
in: "MULTIPOINT()",
out: geom.MultiPoint{},
},
"multipoint 1": {
in: "MULTIPOINT(0 0, 2 3)",
out: geom.MultiPoint{{0, 0}, {2, 3}},
},
"multipoint 2": {
in: "MULTIPOINT(0 0, 1 1, 2 3)",
out: geom.MultiPoint{{0, 0}, {1, 1}, {2, 3}},
},
"linestring 0": {
in: "LINESTRING(0 0,1 1)",
out: geom.LineString{{0, 0}, {1, 1}},
err: nil,
},
"linestring 1": {
in: "LINESTRING ( 0\t0\t,\t1\n1 \n \t )",
out: geom.LineString{{0, 0}, {1, 1}},
err: nil,
},
"linestring 2": {
in: "LINESTRING(0 0)",
err: errors.New("syntax error (1:16): not enough points in LINESTRING, 1"),
},
"linestring 3": {
in: "LINESTRING()",
err: errors.New("syntax error (1:13): not enough points in LINESTRING, 0"),
},
"linestring 4": {
in: "LINESTRING(0 0, 1 1, 2 3)",
out: geom.LineString{{0, 0}, {1, 1}, {2, 3}},
},
"multilinestring 0": {
in: "MULTILINESTRING((0 0, 1 1))",
out: geom.MultiLineString{{{0, 0}, {1, 1}}},
},
"multilinestring 1": {
in: "MULTILINESTRING((0 0, 1 1), (2 2, 1 3))",
out: geom.MultiLineString{{{0, 0}, {1, 1}}, {{2, 2}, {1, 3}}},
},
"multilinestring 2": {
in: "MULTILINESTRING((0 0, 1 1), (1 3))",
err: errors.New("syntax error (1:35): not enough points in MULTILINESTRING[1], 1"),
},
"multilinestring 3": {
in: "MULTILINESTRING()",
err: errors.New("syntax error (1:18): not enough lines in MULTILINESTRING, 0"),
},
"polygon 0": {
in: "POLYGON((0 0, 1 1, 1 0, 0 0))",
out: geom.Polygon{{{0, 0}, {1, 1}, {1, 0}}},
},
"polygon 1": {
in: "POLYGON((0 0, 0 1, 1 1, 1 0))",
err: errors.New("syntax error (1:30): first and last point of POLYGON[0] not equal"),
},
"polygon 2": {
in: "POLYGON()",
err: errors.New("syntax error (1:10): not enough lines in POLYGON, 0"),
},
"polygon 3": {
in: "POLYGON((0 0, 1 1, 0 1))",
err: errors.New("syntax error (1:25): not enough points in POLYGON[0], 3"),
},
"polygon 4": {
in: "POLYGON ((35 10, 45 45, 15 40, 10 20, 35 10),(20 30, 35 35, 30 20, 20 30))",
out: geom.Polygon{{{35, 10}, {45, 45}, {15, 40}, {10, 20}}, {{20, 30}, {35, 35}, {30, 20}}},
},
"multipolygon 0": {
in: "multipolygon(((0 0, 1 1, 1 0, 0 0)))",
out: geom.MultiPolygon{{{{0, 0}, {1, 1}, {1, 0}}}},
},
"multipolygon 1": {
in: "multipolygon ( ( ( 0 0, 1 1, 1 0, 0 0 ) ) )",
out: geom.MultiPolygon{{{{0, 0}, {1, 1}, {1, 0}}}},
},
"multipolygon 2": {
in: "MULTIPOLYGON()",
err: errors.New("syntax error (1:15): not enough polys in MULTIPOLYGON, 0"),
},
"collection 0": {
in: "geometrycollection(point(0 0))",
out: geom.Collection{geom.Point{0, 0}},
},
"collection 1": {
in: "geometrycollection ( point ( 0 0 ) )",
out: geom.Collection{geom.Point{0, 0}},
},
"collection 2": {
in: "geometrycollection(multipolygon(((0 0, 1 1, 1 0, 0 0))))",
out: geom.Collection{geom.MultiPolygon{{{{0, 0}, {1, 1}, {1, 0}}}}},
},
"collection 3": {
in: "geometrycollection(MULTIPOLYGON())",
err: errors.New("syntax error (1:34): not enough polys in MULTIPOLYGON, 0"),
},
}

for k, v := range tcases {
t.Run(k, fn(v))
}
}
5 changes: 5 additions & 0 deletions testing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# testing

This package contains functions and variables for using pre made geometries.
Be carefule, some of the geometries are very large, in particular the tile
geometries.
44 changes: 44 additions & 0 deletions testing/dynamic_geoms.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package testing

import (
"math"

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

// BoxPolygon returns a polygon that is a box with side lengths of
// dim, a clockwise winding order, and points at (0, 0) and (dim, dim).
func BoxPolygon(dim float64) geom.Polygon {
return geom.Polygon{{{0, 0}, {dim, 0}, {dim, dim}, {0, dim}}}
}

func SelfIntBoxLineString(dim float64) geom.LineString {
return geom.LineString{{0, 0}, {dim, dim}, {dim, 0}, {0, dim}}
}

// SinLineString returns a line string that is a sin wave with
// the given amplitude and domain (x values) of the set [start, end].
// points is the number of points in the line and must be >= 2, or the
// function panics.
func SinLineString(amp, start, end float64, points int) geom.LineString {
if points < 2 {
panic("cannot have a line with less than 2 points")
}

res := (end - start) / (float64(points) - 1)
ret := make([][2]float64, points)
x := 0.0

// the last point should be end
var i int
for i = 0; i < points - 1; i++ {
ret[i] = [2]float64{x, math.Sin(x)}
x += res
}

ret[i] = [2]float64{end, math.Sin(end)}

return ret
}


46 changes: 46 additions & 0 deletions testing/dynamic_geoms_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package testing

import (
"testing"

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

func TestBoxPolygon(t *testing.T) {
type tcase struct {
dim float64
res geom.Polygon
}

fn := func(tc tcase) func(*testing.T) {
return func(t *testing.T) {
got := BoxPolygon(tc.dim)

wo := windingorder.OfPoints(got[0]...)
if !wo.IsClockwise() {
t.Error("winding order of box not clockwise")
}

if !cmp.GeometryEqual(got, tc.res) {
t.Error("geometries not equal")
}
}
}

tcases := map[string]tcase {
"dim 1" : {
dim: 1.0,
res: geom.Polygon{{{0, 0}, {1.0, 0}, {1.0, 1.0}, {0, 1.0}}},
},
"dim -1" : {
dim: -1.0,
res: geom.Polygon{{{0, 0}, {-1.0, 0}, {-1.0, -1.0}, {0, -1.0}}},
},
}

for k, v := range tcases {
t.Run(k, fn(v))
}
}
109 changes: 109 additions & 0 deletions testing/gen.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/bin/bash
set -e

PARSER_FILE='script.go'
PARSER="go run $PARSER_FILE"


TABLE_LIST=(
"ne_10m_admin_0_countries"
"ne_10m_parks_and_protected_lands_line"
"ne_10m_parks_and_protected_lands_area"
"ne_110m_coastline"
"ne_50m_lakes"
"ne_50m_antarctic_ice_shelves_lines"
"ne_50m_antarctic_ice_shelves_polys"
)

GEOMS_PER_TABLE=10
OUT_FILE='natural_earth_gen.go'
DB_CMD='psql -d natural_earth'

# from https://gist.github.com/ear7h/80d233451e90a31ec63aeea435f296b2
cat << EOF > $PARSER_FILE
package main

import (
"fmt"
"os"
"strings"
"io/ioutil"
)

func geomType(typ string) string {
if strings.HasPrefix(typ, "MULTI") {
return "Multi" + geomType(typ[len("MULTI"):])
}

switch typ {
case "POINT":
return "Point"

case "LINESTRING":
return "LineString"

case "POLYGON":
return "Polygon"

case "GEOMETRYCOLLECTION":
return "Collection"
}
panic("not found " + typ)
}

func main() {
byt, _ := ioutil.ReadAll(os.Stdin)
str := string(byt)
n := strings.Index(str, "|")
name := strings.Replace(str[:n], " ", "", -1)

str = str[n+1:]
n = strings.Index(str, "(")
typ := strings.TrimSpace(str[:n])
typ = geomType(typ)

str = str[n:]

str = strings.Replace(str, ",", "},{", -1)
str = strings.Replace(str, " ", ", ", -1)
str = strings.Replace(str, "(", "{", -1)
str = strings.Replace(str, ")", "}", -1)
str = strings.TrimSpace(str)
fmt.Printf("var %s = geom.%s{%s}\n", name, typ, str)
}
EOF


echo 'package testing' > $OUT_FILE
echo '// this file was auto generated using gen.go' >> $OUT_FILE

for table in "${TABLE_LIST[@]}"; do

if [[ $GEOMS_PER_TABLE -le 0 ]]; then
echo "not writing any geoms"
continue
else
echo "writing geoms from table $table"
fi

for i in $(seq 0 $(($GEOMS_PER_TABLE - 1))); do
$DB_CMD -t \
-c "select '_$table$i', ST_AsText(ST_Transform(wkb_geometry, 3857)) from $table limit 1 offset $i;" | \
$PARSER >> $OUT_FILE
done
done

rm $PARSER_FILE

function varList() {

cat $OUT_FILE natural_earth_picked.go | \
sed -n 's/var \([_a-zA-Z0-9]*\) .*/\1/p' | \
paste -s -d ',' -

}

echo 'var NaturalEarth = []geom.Geometry{' `varList` '}' >> $OUT_FILE

goimports -w $OUT_FILE

76 changes: 76 additions & 0 deletions testing/natural_earth_gen.go

Large diffs are not rendered by default.

Loading