From b63def4be9c8b4eca7cd1112f5ba3ee7abbddda6 Mon Sep 17 00:00:00 2001 From: Andy Yang Date: Thu, 4 Feb 2021 17:00:33 -0500 Subject: [PATCH] geo/wkt: add support for parsing polygons with Z and M dimensions This patch extends the capabilities of the WKT parser to include parsing of polygons with Z and M dimensions. Release note: None --- pkg/geo/wkt/lex.go | 8 + pkg/geo/wkt/wkt.y | 188 ++++++++++++++-- pkg/geo/wkt/wkt_generated.go | 416 ++++++++++++++++++++++++++--------- pkg/geo/wkt/wkt_test.go | 102 +++++++++ 4 files changed, 590 insertions(+), 124 deletions(-) diff --git a/pkg/geo/wkt/lex.go b/pkg/geo/wkt/lex.go index a51f501f3756..285fc9c8d293 100644 --- a/pkg/geo/wkt/lex.go +++ b/pkg/geo/wkt/lex.go @@ -98,6 +98,14 @@ func getKeywordToken(tokStr string) int { return LINESTRINGZ case "LINESTRINGZM": return LINESTRINGZM + case "POLYGON": + return POLYGON + case "POLYGONM": + return POLYGONM + case "POLYGONZ": + return POLYGONZ + case "POLYGONZM": + return POLYGONZM default: return eof } diff --git a/pkg/geo/wkt/wkt.y b/pkg/geo/wkt/wkt.y index 52b5cdbfa8bd..6b5ec3d1f5b0 100644 --- a/pkg/geo/wkt/wkt.y +++ b/pkg/geo/wkt/wkt.y @@ -14,6 +14,33 @@ package wkt import "github.com/twpayne/go-geom" +func isValidLineString(wktlex wktLexer, flatCoords []float64, stride int) bool { + if len(flatCoords) < 2 * stride { + wktlex.(*wktLex).Error("syntax error: non-empty linestring with only one point") + return false + } + return true +} + +func isValidPolygonRing(wktlex wktLexer, flatCoords []float64, stride int) bool { + if len(flatCoords) < 4 * stride { + wktlex.(*wktLex).Error("syntax error: polygon ring doesn't have enough points") + return false + } + for i := 0; i < stride; i++ { + if flatCoords[i] != flatCoords[len(flatCoords)-stride+i] { + wktlex.(*wktLex).Error("syntax error: polygon ring not closed") + return false + } + } + return true +} + +type geomPair struct { + flatCoords []float64 + ends []int +} + %} %union { @@ -21,18 +48,23 @@ import "github.com/twpayne/go-geom" geom geom.T coord float64 coordList []float64 + pair geomPair } %token POINT POINTM POINTZ POINTZM %token LINESTRING LINESTRINGM LINESTRINGZ LINESTRINGZM +%token POLYGON POLYGONM POLYGONZ POLYGONZM %token EMPTY -//%token POLYGON MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION +//%token MULTIPOINT MULTILINESTRING MULTIPOLYGON GEOMETRYCOLLECTION %token NUM %type geometry -%type point linestring +%type point linestring polygon %type two_coords three_coords four_coords %type two_coords_list three_coords_list four_coords_list +%type two_coords_line three_coords_line four_coords_line +%type two_coords_ring three_coords_ring four_coords_ring +%type two_coords_ring_list three_coords_ring_list four_coords_ring_list %% @@ -44,8 +76,8 @@ start: geometry: point - // TODO(ayang) have parser check that linestrings are either empty or have 2+ points | linestring +| polygon point: POINT '(' two_coords ')' @@ -90,29 +122,47 @@ point: } linestring: - LINESTRING '(' two_coords_list ')' + LINESTRING two_coords_line { - $$ = geom.NewLineStringFlat(geom.XY, $3) + if !isValidLineString(wktlex, $2, 2) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XY, $2) } -| LINESTRING '(' three_coords_list ')' +| LINESTRING three_coords_line { - $$ = geom.NewLineStringFlat(geom.XYZ, $3) + if !isValidLineString(wktlex, $2, 3) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XYZ, $2) } -| LINESTRING '(' four_coords_list ')' +| LINESTRING four_coords_line { - $$ = geom.NewLineStringFlat(geom.XYZM, $3) + if !isValidLineString(wktlex, $2, 4) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XYZM, $2) } -| LINESTRINGM '(' three_coords_list ')' +| LINESTRINGM three_coords_line { - $$ = geom.NewLineStringFlat(geom.XYM, $3) + if !isValidLineString(wktlex, $2, 3) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XYM, $2) } -| LINESTRINGZ '(' three_coords_list ')' +| LINESTRINGZ three_coords_line { - $$ = geom.NewLineStringFlat(geom.XYZ, $3) + if !isValidLineString(wktlex, $2, 3) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XYZ, $2) } -| LINESTRINGZM '(' four_coords_list ')' +| LINESTRINGZM four_coords_line { - $$ = geom.NewLineStringFlat(geom.XYZM, $3) + if !isValidLineString(wktlex, $2, 4) { + return 1 + } + $$ = geom.NewLineStringFlat(geom.XYZM, $2) } | LINESTRING EMPTY { @@ -131,6 +181,114 @@ linestring: $$ = geom.NewLineString(geom.XYZM) } +polygon: + POLYGON '(' two_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XY, $3.flatCoords, $3.ends) + } +| POLYGON '(' three_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XYZ, $3.flatCoords, $3.ends) + } +| POLYGON '(' four_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XYZM, $3.flatCoords, $3.ends) + } +| POLYGONM '(' three_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XYM, $3.flatCoords, $3.ends) + } +| POLYGONZ '(' three_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XYZ, $3.flatCoords, $3.ends) + } +| POLYGONZM '(' four_coords_ring_list ')' + { + $$ = geom.NewPolygonFlat(geom.XYZM, $3.flatCoords, $3.ends) + } +| POLYGON EMPTY + { + $$ = geom.NewPolygon(geom.XY) + } +| POLYGONM EMPTY + { + $$ = geom.NewPolygon(geom.XYM) + } +| POLYGONZ EMPTY + { + $$ = geom.NewPolygon(geom.XYZ) + } +| POLYGONZM EMPTY + { + $$ = geom.NewPolygon(geom.XYZM) + } + +two_coords_ring_list: + two_coords_ring_list ',' two_coords_ring + { + $$ = geomPair{append($1.flatCoords, $3.flatCoords...), append($1.ends, $1.ends[len($1.ends)-1] + $3.ends[0])} + } +| two_coords_ring + +three_coords_ring_list: + three_coords_ring_list ',' three_coords_ring + { + $$ = geomPair{append($1.flatCoords, $3.flatCoords...), append($1.ends, $1.ends[len($1.ends)-1] + $3.ends[0])} + } +| three_coords_ring + +four_coords_ring_list: + four_coords_ring_list ',' four_coords_ring + { + $$ = geomPair{append($1.flatCoords, $3.flatCoords...), append($1.ends, $1.ends[len($1.ends)-1] + $3.ends[0])} + } +| four_coords_ring + +two_coords_ring: + two_coords_line + { + if !isValidPolygonRing(wktlex, $1, 2) { + return 1 + } + $$ = geomPair{$1, []int{len($1)}} + } + +three_coords_ring: + three_coords_line + { + if !isValidPolygonRing(wktlex, $1, 3) { + return 1 + } + $$ = geomPair{$1, []int{len($1)}} + } + +four_coords_ring: + four_coords_line + { + if !isValidPolygonRing(wktlex, $1, 4) { + return 1 + } + $$ = geomPair{$1, []int{len($1)}} + } + +two_coords_line: + '(' two_coords_list ')' + { + $$ = $2 + } + +three_coords_line: + '(' three_coords_list ')' + { + $$ = $2 + } + +four_coords_line: + '(' four_coords_list ')' + { + $$ = $2 + } + two_coords_list: two_coords ',' two_coords_list { diff --git a/pkg/geo/wkt/wkt_generated.go b/pkg/geo/wkt/wkt_generated.go index 67f93678be58..7185dafdb787 100644 --- a/pkg/geo/wkt/wkt_generated.go +++ b/pkg/geo/wkt/wkt_generated.go @@ -10,13 +10,41 @@ import __yyfmt__ "fmt" import "github.com/twpayne/go-geom" -//line wkt.y:19 +func isValidLineString(wktlex wktLexer, flatCoords []float64, stride int) bool { + if len(flatCoords) < 2*stride { + wktlex.(*wktLex).Error("syntax error: non-empty linestring with only one point") + return false + } + return true +} + +func isValidPolygonRing(wktlex wktLexer, flatCoords []float64, stride int) bool { + if len(flatCoords) < 4*stride { + wktlex.(*wktLex).Error("syntax error: polygon ring doesn't have enough points") + return false + } + for i := 0; i < stride; i++ { + if flatCoords[i] != flatCoords[len(flatCoords)-stride+i] { + wktlex.(*wktLex).Error("syntax error: polygon ring not closed") + return false + } + } + return true +} + +type geomPair struct { + flatCoords []float64 + ends []int +} + +//line wkt.y:46 type wktSymType struct { yys int str string geom geom.T coord float64 coordList []float64 + pair geomPair } const POINT = 57346 @@ -27,8 +55,12 @@ const LINESTRING = 57350 const LINESTRINGM = 57351 const LINESTRINGZ = 57352 const LINESTRINGZM = 57353 -const EMPTY = 57354 -const NUM = 57355 +const POLYGON = 57354 +const POLYGONM = 57355 +const POLYGONZ = 57356 +const POLYGONZM = 57357 +const EMPTY = 57358 +const NUM = 57359 var wktToknames = [...]string{ "$end", @@ -42,6 +74,10 @@ var wktToknames = [...]string{ "LINESTRINGM", "LINESTRINGZ", "LINESTRINGZM", + "POLYGON", + "POLYGONM", + "POLYGONZ", + "POLYGONZM", "EMPTY", "NUM", "'('", @@ -64,69 +100,90 @@ var wktExca = [...]int{ const wktPrivate = 57344 -const wktLast = 73 +const wktLast = 120 var wktAct = [...]int{ - 40, 38, 43, 39, 61, 42, 5, 6, 7, 8, - 9, 10, 11, 12, 60, 59, 31, 73, 64, 30, - 63, 33, 36, 35, 62, 58, 57, 44, 46, 45, - 56, 54, 53, 51, 49, 48, 47, 28, 26, 27, - 25, 24, 22, 23, 21, 20, 18, 19, 17, 16, - 14, 15, 13, 41, 72, 37, 34, 69, 67, 66, - 65, 68, 71, 55, 70, 52, 50, 29, 32, 1, - 4, 3, 2, + 67, 66, 65, 58, 56, 64, 63, 61, 57, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 60, 97, 94, 88, 85, 49, 96, 92, 95, + 92, 87, 54, 93, 94, 91, 92, 89, 90, 86, + 48, 84, 51, 83, 53, 81, 80, 78, 71, 76, + 72, 73, 75, 74, 46, 44, 45, 43, 42, 40, + 41, 39, 37, 35, 38, 33, 32, 29, 33, 30, + 25, 23, 24, 22, 21, 19, 20, 18, 59, 38, + 33, 106, 30, 102, 110, 109, 55, 52, 100, 99, + 98, 101, 104, 105, 107, 108, 103, 47, 82, 79, + 77, 50, 70, 68, 1, 62, 69, 5, 4, 3, + 2, 0, 0, 28, 26, 0, 36, 27, 31, 34, } var wktPact = [...]int{ - 2, -1000, -1000, -1000, -1000, 38, 37, 34, 33, 30, - 29, 26, 25, 55, -1000, 43, -1000, 43, -1000, 42, - -1000, 55, -1000, 43, -1000, 43, -1000, 42, -1000, 21, - 20, 19, 53, 18, 52, 17, 16, 50, 15, 11, - 10, -1, -2, -12, 9, 5, 3, -1000, -1000, -1000, - 47, -1000, 46, -1000, -1000, 45, -1000, -1000, -1000, 44, - 43, 42, -1000, -1000, -1000, 41, -1000, 41, -1000, 4, - -1000, -1000, -1000, -1000, + 5, -1000, -1000, -1000, -1000, -1000, 59, 58, 55, 54, + 51, 50, 47, 46, 43, 42, 39, 38, 84, -1000, + 70, -1000, 70, -1000, 69, -1000, -1000, -1000, -1000, -1000, + 84, -1000, -1000, 70, -1000, -1000, -1000, -1000, 69, 64, + -1000, 62, -1000, 62, -1000, 61, -1000, 34, 33, 30, + 83, 28, 82, 27, 26, 81, 24, 22, 6, 19, + 11, 4, 18, 16, 14, -1000, -1000, -1000, -1000, -1000, + -1000, 10, 8, 3, -1000, -1000, -1000, 73, -1000, 72, + -1000, -1000, 71, -1000, -1000, -1000, 66, 70, 69, -1000, + 63, -1000, 62, -1000, 61, -1000, -1000, -1000, 68, -1000, + 68, -1000, 67, -1000, -1000, -1000, 66, -1000, -1000, -1000, + -1000, } var wktPgo = [...]int{ - 0, 72, 71, 70, 53, 5, 2, 1, 3, 0, - 69, + 0, 110, 109, 108, 107, 78, 21, 7, 4, 8, + 3, 103, 106, 102, 2, 1, 0, 105, 6, 5, + 104, } var wktR1 = [...]int{ - 0, 10, 1, 1, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 7, 7, 8, 8, 9, 9, - 4, 5, 6, + 0, 20, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 17, 17, 18, 18, 19, + 19, 14, 15, 16, 11, 12, 13, 8, 8, 9, + 9, 10, 10, 5, 6, 7, } var wktR2 = [...]int{ - 0, 1, 1, 1, 4, 4, 4, 4, 4, 4, - 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, - 2, 2, 2, 2, 3, 1, 3, 1, 3, 1, - 2, 3, 4, + 0, 1, 1, 1, 1, 4, 4, 4, 4, 4, + 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, + 4, 2, 2, 2, 2, 3, 1, 3, 1, 3, + 1, 1, 1, 1, 3, 3, 3, 3, 1, 3, + 1, 3, 1, 2, 3, 4, } var wktChk = [...]int{ - -1000, -10, -1, -2, -3, 4, 5, 6, 7, 8, - 9, 10, 11, 14, 12, 14, 12, 14, 12, 14, - 12, 14, 12, 14, 12, 14, 12, 14, 12, -4, - -5, -6, 13, -5, 13, -5, -6, 13, -7, -8, - -9, -4, -5, -6, -8, -8, -9, 15, 15, 15, - 13, 15, 13, 15, 15, 13, 15, 15, 15, 16, - 16, 16, 15, 15, 15, 13, 13, 13, -7, 13, - -8, -9, 13, 13, + -1000, -20, -1, -2, -3, -4, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 18, 16, + 18, 16, 18, 16, 18, 16, -11, -12, -13, 16, + 18, -12, 16, 18, -12, 16, -13, 16, 18, 18, + 16, 18, 16, 18, 16, 18, 16, -5, -6, -7, + 17, -6, 17, -6, -7, 17, -8, -9, -10, -5, + -6, -7, -17, -18, -19, -14, -15, -16, -11, -12, + -13, -18, -18, -19, 19, 19, 19, 17, 19, 17, + 19, 19, 17, 19, 19, 19, 20, 20, 20, 19, + 20, 19, 20, 19, 20, 19, 19, 19, 17, 17, + 17, -8, 17, -9, -10, -14, 18, -15, -16, 17, + 17, } var wktDef = [...]int{ - 0, -2, 1, 2, 3, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 10, 0, 11, 0, 12, 0, - 13, 0, 20, 0, 21, 0, 22, 0, 23, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 25, 27, 29, 0, 0, 0, 4, 5, 6, - 30, 7, 0, 8, 9, 0, 14, 15, 16, 0, - 0, 0, 17, 18, 19, 31, 31, 0, 24, 0, - 26, 28, 32, 30, + 0, -2, 1, 2, 3, 4, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, + 0, 12, 0, 13, 0, 14, 15, 16, 17, 21, + 0, 18, 22, 0, 19, 23, 20, 24, 0, 0, + 31, 0, 32, 0, 33, 0, 34, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, + 50, 52, 0, 0, 0, 36, 38, 40, 41, 42, + 43, 0, 0, 0, 5, 6, 7, 53, 8, 0, + 9, 10, 0, 44, 45, 46, 0, 0, 0, 25, + 0, 26, 0, 27, 0, 28, 29, 30, 54, 54, + 0, 47, 0, 49, 51, 35, 0, 37, 39, 55, + 53, } var wktTok1 = [...]int{ @@ -134,12 +191,12 @@ var wktTok1 = [...]int{ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 14, 15, 3, 3, 16, + 18, 19, 3, 3, 20, } var wktTok2 = [...]int{ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, - 12, 13, + 12, 13, 14, 15, 16, 17, } var wktTok3 = [...]int{ @@ -485,163 +542,304 @@ wktdefault: case 1: wktDollar = wktS[wktpt-1 : wktpt+1] -//line wkt.y:41 +//line wkt.y:73 { wktlex.(*wktLex).ret = wktDollar[1].geom } - case 4: + case 5: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:52 +//line wkt.y:84 { wktVAL.geom = geom.NewPointFlat(geom.XY, wktDollar[3].coordList) } - case 5: + case 6: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:56 +//line wkt.y:88 { wktVAL.geom = geom.NewPointFlat(geom.XYZ, wktDollar[3].coordList) } - case 6: + case 7: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:60 +//line wkt.y:92 { wktVAL.geom = geom.NewPointFlat(geom.XYZM, wktDollar[3].coordList) } - case 7: + case 8: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:64 +//line wkt.y:96 { wktVAL.geom = geom.NewPointFlat(geom.XYM, wktDollar[3].coordList) } - case 8: + case 9: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:68 +//line wkt.y:100 { wktVAL.geom = geom.NewPointFlat(geom.XYZ, wktDollar[3].coordList) } - case 9: + case 10: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:72 +//line wkt.y:104 { wktVAL.geom = geom.NewPointFlat(geom.XYZM, wktDollar[3].coordList) } - case 10: + case 11: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:76 +//line wkt.y:108 { wktVAL.geom = geom.NewPointEmpty(geom.XY) } - case 11: + case 12: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:80 +//line wkt.y:112 { wktVAL.geom = geom.NewPointEmpty(geom.XYM) } - case 12: + case 13: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:84 +//line wkt.y:116 { wktVAL.geom = geom.NewPointEmpty(geom.XYZ) } - case 13: + case 14: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:88 +//line wkt.y:120 { wktVAL.geom = geom.NewPointEmpty(geom.XYZM) } - case 14: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:94 - { - wktVAL.geom = geom.NewLineStringFlat(geom.XY, wktDollar[3].coordList) - } case 15: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:98 + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:126 { - wktVAL.geom = geom.NewLineStringFlat(geom.XYZ, wktDollar[3].coordList) + if !isValidLineString(wktlex, wktDollar[2].coordList, 2) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XY, wktDollar[2].coordList) } case 16: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:102 + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:133 { - wktVAL.geom = geom.NewLineStringFlat(geom.XYZM, wktDollar[3].coordList) + if !isValidLineString(wktlex, wktDollar[2].coordList, 3) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XYZ, wktDollar[2].coordList) } case 17: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:106 + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:140 { - wktVAL.geom = geom.NewLineStringFlat(geom.XYM, wktDollar[3].coordList) + if !isValidLineString(wktlex, wktDollar[2].coordList, 4) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XYZM, wktDollar[2].coordList) } case 18: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:110 + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:147 { - wktVAL.geom = geom.NewLineStringFlat(geom.XYZ, wktDollar[3].coordList) + if !isValidLineString(wktlex, wktDollar[2].coordList, 3) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XYM, wktDollar[2].coordList) } case 19: - wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:114 + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:154 { - wktVAL.geom = geom.NewLineStringFlat(geom.XYZM, wktDollar[3].coordList) + if !isValidLineString(wktlex, wktDollar[2].coordList, 3) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XYZ, wktDollar[2].coordList) } case 20: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:118 +//line wkt.y:161 { - wktVAL.geom = geom.NewLineString(geom.XY) + if !isValidLineString(wktlex, wktDollar[2].coordList, 4) { + return 1 + } + wktVAL.geom = geom.NewLineStringFlat(geom.XYZM, wktDollar[2].coordList) } case 21: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:122 +//line wkt.y:168 { - wktVAL.geom = geom.NewLineString(geom.XYM) + wktVAL.geom = geom.NewLineString(geom.XY) } case 22: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:126 +//line wkt.y:172 { - wktVAL.geom = geom.NewLineString(geom.XYZ) + wktVAL.geom = geom.NewLineString(geom.XYM) } case 23: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:130 +//line wkt.y:176 { - wktVAL.geom = geom.NewLineString(geom.XYZM) + wktVAL.geom = geom.NewLineString(geom.XYZ) } case 24: + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:180 + { + wktVAL.geom = geom.NewLineString(geom.XYZM) + } + case 25: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:186 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XY, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 26: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:190 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XYZ, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 27: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:194 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XYZM, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 28: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:198 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XYM, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 29: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:202 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XYZ, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 30: + wktDollar = wktS[wktpt-4 : wktpt+1] +//line wkt.y:206 + { + wktVAL.geom = geom.NewPolygonFlat(geom.XYZM, wktDollar[3].pair.flatCoords, wktDollar[3].pair.ends) + } + case 31: + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:210 + { + wktVAL.geom = geom.NewPolygon(geom.XY) + } + case 32: + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:214 + { + wktVAL.geom = geom.NewPolygon(geom.XYM) + } + case 33: + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:218 + { + wktVAL.geom = geom.NewPolygon(geom.XYZ) + } + case 34: + wktDollar = wktS[wktpt-2 : wktpt+1] +//line wkt.y:222 + { + wktVAL.geom = geom.NewPolygon(geom.XYZM) + } + case 35: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:228 + { + wktVAL.pair = geomPair{append(wktDollar[1].pair.flatCoords, wktDollar[3].pair.flatCoords...), append(wktDollar[1].pair.ends, wktDollar[1].pair.ends[len(wktDollar[1].pair.ends)-1]+wktDollar[3].pair.ends[0])} + } + case 37: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:235 + { + wktVAL.pair = geomPair{append(wktDollar[1].pair.flatCoords, wktDollar[3].pair.flatCoords...), append(wktDollar[1].pair.ends, wktDollar[1].pair.ends[len(wktDollar[1].pair.ends)-1]+wktDollar[3].pair.ends[0])} + } + case 39: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:242 + { + wktVAL.pair = geomPair{append(wktDollar[1].pair.flatCoords, wktDollar[3].pair.flatCoords...), append(wktDollar[1].pair.ends, wktDollar[1].pair.ends[len(wktDollar[1].pair.ends)-1]+wktDollar[3].pair.ends[0])} + } + case 41: + wktDollar = wktS[wktpt-1 : wktpt+1] +//line wkt.y:249 + { + if !isValidPolygonRing(wktlex, wktDollar[1].coordList, 2) { + return 1 + } + wktVAL.pair = geomPair{wktDollar[1].coordList, []int{len(wktDollar[1].coordList)}} + } + case 42: + wktDollar = wktS[wktpt-1 : wktpt+1] +//line wkt.y:258 + { + if !isValidPolygonRing(wktlex, wktDollar[1].coordList, 3) { + return 1 + } + wktVAL.pair = geomPair{wktDollar[1].coordList, []int{len(wktDollar[1].coordList)}} + } + case 43: + wktDollar = wktS[wktpt-1 : wktpt+1] +//line wkt.y:267 + { + if !isValidPolygonRing(wktlex, wktDollar[1].coordList, 4) { + return 1 + } + wktVAL.pair = geomPair{wktDollar[1].coordList, []int{len(wktDollar[1].coordList)}} + } + case 44: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:276 + { + wktVAL.coordList = wktDollar[2].coordList + } + case 45: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:282 + { + wktVAL.coordList = wktDollar[2].coordList + } + case 46: wktDollar = wktS[wktpt-3 : wktpt+1] -//line wkt.y:136 +//line wkt.y:288 + { + wktVAL.coordList = wktDollar[2].coordList + } + case 47: + wktDollar = wktS[wktpt-3 : wktpt+1] +//line wkt.y:294 { wktVAL.coordList = append(wktDollar[1].coordList, wktDollar[3].coordList...) } - case 26: + case 49: wktDollar = wktS[wktpt-3 : wktpt+1] -//line wkt.y:143 +//line wkt.y:301 { wktVAL.coordList = append(wktDollar[1].coordList, wktDollar[3].coordList...) } - case 28: + case 51: wktDollar = wktS[wktpt-3 : wktpt+1] -//line wkt.y:150 +//line wkt.y:308 { wktVAL.coordList = append(wktDollar[1].coordList, wktDollar[3].coordList...) } - case 30: + case 53: wktDollar = wktS[wktpt-2 : wktpt+1] -//line wkt.y:157 +//line wkt.y:315 { wktVAL.coordList = []float64{wktDollar[1].coord, wktDollar[2].coord} } - case 31: + case 54: wktDollar = wktS[wktpt-3 : wktpt+1] -//line wkt.y:163 +//line wkt.y:321 { wktVAL.coordList = []float64{wktDollar[1].coord, wktDollar[2].coord, wktDollar[3].coord} } - case 32: + case 55: wktDollar = wktS[wktpt-4 : wktpt+1] -//line wkt.y:169 +//line wkt.y:327 { wktVAL.coordList = []float64{wktDollar[1].coord, wktDollar[2].coord, wktDollar[3].coord, wktDollar[4].coord} } diff --git a/pkg/geo/wkt/wkt_test.go b/pkg/geo/wkt/wkt_test.go index 5dc1748cbb17..09e96635aee8 100644 --- a/pkg/geo/wkt/wkt_test.go +++ b/pkg/geo/wkt/wkt_test.go @@ -105,6 +105,59 @@ func TestUnmarshal(t *testing.T) { equivInputs: []string{"LINESTRING ZM EMPTY", "LINESTRINGZM EMPTY"}, expected: geom.NewLineString(geom.XYZM), }, + // POLYGON tests + { + desc: "parse 2D polygon", + equivInputs: []string{"POLYGON((0 0, 1 -1, 2 0, 0 0))", "POLYGON ((0 0, 1 -1, 2 0, 0 0))"}, + expected: geom.NewPolygonFlat(geom.XY, []float64{0, 0, 1, -1, 2, 0, 0, 0}, []int{8}), + }, + { + desc: "parse 2D polygon with hole", + equivInputs: []string{"POLYGON((0 0, 0 100, 100 100, 100 0, 0 0),(10 10, 11 11, 12 10, 10 10))"}, + expected: geom.NewPolygonFlat(geom.XY, + []float64{0, 0, 0, 100, 100, 100, 100, 0, 0, 0, 10, 10, 11, 11, 12, 10, 10, 10}, []int{10, 18}), + }, + { + desc: "parse 2D polygon with two holes", + equivInputs: []string{"POLYGON((0 0, 0 100, 100 100, 100 0, 0 0),(10 10, 11 11, 12 10, 10 10), (2 2, 4 4, 5 1, 2 2))"}, + expected: geom.NewPolygonFlat(geom.XY, + []float64{0, 0, 0, 100, 100, 100, 100, 0, 0, 0, 10, 10, 11, 11, 12, 10, 10, 10, 2, 2, 4, 4, 5, 1, 2, 2}, []int{10, 18, 26}), + }, + { + desc: "parse 2D+M polygon", + equivInputs: []string{"POLYGONM((0 0 7, 1 -1 -50, 2 0 0, 0 0 7))", "POLYGON M ((0 0 7, 1 -1 -50, 2 0 0, 0 0 7))"}, + expected: geom.NewPolygonFlat(geom.XYM, []float64{0, 0, 7, 1, -1, -50, 2, 0, 0, 0, 0, 7}, []int{12}), + }, + { + desc: "parse 3D polygon", + equivInputs: []string{"POLYGON((0 0 7, 1 -1 -50, 2 0 0, 0 0 7))", "POLYGON Z ((0 0 7, 1 -1 -50, 2 0 0, 0 0 7))"}, + expected: geom.NewPolygonFlat(geom.XYZ, []float64{0, 0, 7, 1, -1, -50, 2, 0, 0, 0, 0, 7}, []int{12}), + }, + { + desc: "parse 4D polygon", + equivInputs: []string{"POLYGON((0 0 12 7, 1 -1 12 -50, 2 0 12 0, 0 0 12 7))", "POLYGON ZM ((0 0 12 7, 1 -1 12 -50, 2 0 12 0, 0 0 12 7))"}, + expected: geom.NewPolygonFlat(geom.XYZM, []float64{0, 0, 12, 7, 1, -1, 12, -50, 2, 0, 12, 0, 0, 0, 12, 7}, []int{16}), + }, + { + desc: "parse empty 2D polygon", + equivInputs: []string{"POLYGON EMPTY"}, + expected: geom.NewPolygon(geom.XY), + }, + { + desc: "parse empty 2D+M polygon", + equivInputs: []string{"POLYGON M EMPTY", "POLYGONM EMPTY"}, + expected: geom.NewPolygon(geom.XYM), + }, + { + desc: "parse empty 3D polygon", + equivInputs: []string{"POLYGON Z EMPTY", "POLYGONZ EMPTY"}, + expected: geom.NewPolygon(geom.XYZ), + }, + { + desc: "parse empty 4D polygon", + equivInputs: []string{"POLYGON ZM EMPTY", "POLYGONZM EMPTY"}, + expected: geom.NewPolygon(geom.XYZM), + }, } for _, tc := range testCases { @@ -145,6 +198,13 @@ DOT(0 0) input: "POINT(2 2.3.7)", expectedErrStr: `lex error: invalid number at pos 8 POINT(2 2.3.7) + ^`, + }, + { + desc: "invalid keyword when extraneous spaces are present in ZM", + input: "POINT Z M (1 1 1 1)", + expectedErrStr: `lex error: invalid keyword at pos 8 +POINT Z M (1 1 1 1) ^`, }, // ParseError @@ -161,6 +221,13 @@ POINT POINT expectedErrStr: `syntax error: unexpected ',', expecting NUM at pos 7 POINT(0, 0) ^`, + }, + { + desc: "2D linestring with not enough points", + input: "LINESTRING(0 0)", + expectedErrStr: `syntax error: non-empty linestring with only one point at pos 14 +LINESTRING(0 0) + ^`, }, { desc: "linestring with mixed dimensionality", @@ -169,6 +236,41 @@ POINT(0, 0) LINESTRING(0 0, 1 1 1) ^`, }, + { + desc: "2D polygon with not enough points", + input: "POLYGON((0 0, 1 1, 2 0))", + expectedErrStr: `syntax error: polygon ring doesn't have enough points at pos 22 +POLYGON((0 0, 1 1, 2 0)) + ^`, + }, + { + desc: "2D polygon with ring that isn't closed", + input: "POLYGON((0 0, 1 1, 2 0, 1 -1))", + expectedErrStr: `syntax error: polygon ring not closed at pos 28 +POLYGON((0 0, 1 1, 2 0, 1 -1)) + ^`, + }, + { + desc: "2D polygon with empty second ring", + input: "POLYGON((0 0, 1 -1, 2 0, 0 0), ())", + expectedErrStr: `syntax error: unexpected ')', expecting NUM at pos 32 +POLYGON((0 0, 1 -1, 2 0, 0 0), ()) + ^`, + }, + { + desc: "2D polygon with EMPTY as second ring", + input: "POLYGON((0 0, 1 -1, 2 0, 0 0), EMPTY)", + expectedErrStr: `syntax error: unexpected EMPTY, expecting '(' at pos 31 +POLYGON((0 0, 1 -1, 2 0, 0 0), EMPTY) + ^`, + }, + { + desc: "2D polygon with invalid second ring", + input: "POLYGON((0 0, 1 -1, 2 0, 0 0), (0.5 -0.5))", + expectedErrStr: `syntax error: polygon ring doesn't have enough points at pos 40 +POLYGON((0 0, 1 -1, 2 0, 0 0), (0.5 -0.5)) + ^`, + }, } for _, tc := range errorTestCases {