github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/spatial/wkt.go (about) 1 // Copyright 2020-2021 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package spatial 16 17 import ( 18 "fmt" 19 "strconv" 20 "strings" 21 22 "github.com/dolthub/go-mysql-server/sql" 23 "github.com/dolthub/go-mysql-server/sql/expression" 24 "github.com/dolthub/go-mysql-server/sql/types" 25 ) 26 27 // AsWKT is a function that converts a spatial type into WKT format (alias for AsText) 28 type AsWKT struct { 29 expression.UnaryExpression 30 } 31 32 var _ sql.FunctionExpression = (*AsWKT)(nil) 33 var _ sql.CollationCoercible = (*AsWKT)(nil) 34 35 // NewAsWKT creates a new point expression. 36 func NewAsWKT(e sql.Expression) sql.Expression { 37 return &AsWKT{expression.UnaryExpression{Child: e}} 38 } 39 40 // FunctionName implements sql.FunctionExpression 41 func (p *AsWKT) FunctionName() string { 42 return "st_aswkb" 43 } 44 45 // Description implements sql.FunctionExpression 46 func (p *AsWKT) Description() string { 47 return "returns binary representation of given spatial type." 48 } 49 50 // IsNullable implements the sql.Expression interface. 51 func (p *AsWKT) IsNullable() bool { 52 return p.Child.IsNullable() 53 } 54 55 // Type implements the sql.Expression interface. 56 func (p *AsWKT) Type() sql.Type { 57 return types.LongText 58 } 59 60 // CollationCoercibility implements the interface sql.CollationCoercible. 61 func (*AsWKT) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 62 return ctx.GetCollation(), 4 63 } 64 65 func (p *AsWKT) String() string { 66 return fmt.Sprintf("%s(%s)", p.FunctionName(), p.Child.String()) 67 } 68 69 // WithChildren implements the Expression interface. 70 func (p *AsWKT) WithChildren(children ...sql.Expression) (sql.Expression, error) { 71 if len(children) != 1 { 72 return nil, sql.ErrInvalidChildrenNumber.New(p, len(children), 1) 73 } 74 return NewAsWKT(children[0]), nil 75 } 76 77 // TODO: these functions could be refactored to be inside the sql.GeometryValue interface 78 79 // PointToWKT converts a sql.Point to a string 80 func PointToWKT(p types.Point, order bool) string { 81 x := strconv.FormatFloat(p.X, 'g', -1, 64) 82 y := strconv.FormatFloat(p.Y, 'g', -1, 64) 83 if order { 84 x, y = y, x 85 } 86 return fmt.Sprintf("%s %s", x, y) 87 } 88 89 // LineToWKT converts a sql.LineString to a string 90 func LineToWKT(l types.LineString, order bool) string { 91 points := make([]string, len(l.Points)) 92 for i, p := range l.Points { 93 points[i] = PointToWKT(p, order) 94 } 95 return strings.Join(points, ",") 96 } 97 98 // PolygonToWKT converts a sql.Polygon to a string 99 func PolygonToWKT(p types.Polygon, order bool) string { 100 lines := make([]string, len(p.Lines)) 101 for i, l := range p.Lines { 102 lines[i] = "(" + LineToWKT(l, order) + ")" 103 } 104 return strings.Join(lines, ",") 105 } 106 107 // MultiPointToWKT converts a sql.MultiPoint to a string 108 func MultiPointToWKT(p types.MultiPoint, order bool) string { 109 points := make([]string, len(p.Points)) 110 for i, p := range p.Points { 111 points[i] = PointToWKT(p, order) 112 } 113 return strings.Join(points, ",") 114 } 115 116 // MultiLineStringToWKT converts a sql.Polygon to a string 117 func MultiLineStringToWKT(l types.MultiLineString, order bool) string { 118 lines := make([]string, len(l.Lines)) 119 for i, line := range l.Lines { 120 lines[i] = "(" + LineToWKT(line, order) + ")" 121 } 122 return strings.Join(lines, ",") 123 } 124 125 // MultiPolygonToWKT converts a sql.Polygon to a string 126 func MultiPolygonToWKT(p types.MultiPolygon, order bool) string { 127 polys := make([]string, len(p.Polygons)) 128 for i, poly := range p.Polygons { 129 polys[i] = "(" + PolygonToWKT(poly, order) + ")" 130 } 131 return strings.Join(polys, ",") 132 } 133 134 // GeomCollToWKT converts a sql.Polygon to a string 135 func GeomCollToWKT(g types.GeomColl, order bool) string { 136 geoms := make([]string, len(g.Geoms)) 137 for i, geom := range g.Geoms { 138 switch g := geom.(type) { 139 case types.Point: 140 geoms[i] = "POINT(" + PointToWKT(g, order) + ")" 141 case types.LineString: 142 geoms[i] = "LINESTRING(" + LineToWKT(g, order) + ")" 143 case types.Polygon: 144 geoms[i] = "POLYGON(" + PolygonToWKT(g, order) + ")" 145 case types.MultiPoint: 146 geoms[i] = "MULTIPOINT(" + MultiPointToWKT(g, order) + ")" 147 case types.MultiLineString: 148 geoms[i] = "MULTILINESTRING(" + MultiLineStringToWKT(g, order) + ")" 149 case types.MultiPolygon: 150 geoms[i] = "MULTIPOLYGON(" + MultiPolygonToWKT(g, order) + ")" 151 case types.GeomColl: 152 geoms[i] = "GEOMETRYCOLLECTION(" + GeomCollToWKT(g, order) + ")" 153 } 154 } 155 return strings.Join(geoms, ",") 156 } 157 158 // Eval implements the sql.Expression interface. 159 func (p *AsWKT) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 160 // Evaluate child 161 val, err := p.Child.Eval(ctx, row) 162 if err != nil { 163 return nil, err 164 } 165 166 if val == nil { 167 return nil, nil 168 } 169 170 var geomType string 171 var data string 172 switch v := val.(type) { 173 case types.Point: 174 geomType = "POINT" 175 data = PointToWKT(v, v.SRID == types.GeoSpatialSRID) 176 case types.LineString: 177 geomType = "LINESTRING" 178 data = LineToWKT(v, v.SRID == types.GeoSpatialSRID) 179 case types.Polygon: 180 geomType = "POLYGON" 181 data = PolygonToWKT(v, v.SRID == types.GeoSpatialSRID) 182 case types.MultiPoint: 183 geomType = "MULTIPOINT" 184 data = MultiPointToWKT(v, v.SRID == types.GeoSpatialSRID) 185 case types.MultiLineString: 186 geomType = "MULTILINESTRING" 187 data = MultiLineStringToWKT(v, v.SRID == types.GeoSpatialSRID) 188 case types.MultiPolygon: 189 geomType = "MULTIPOLYGON" 190 data = MultiPolygonToWKT(v, v.SRID == types.GeoSpatialSRID) 191 case types.GeomColl: 192 geomType = "GEOMETRYCOLLECTION" 193 data = GeomCollToWKT(v, v.SRID == types.GeoSpatialSRID) 194 default: 195 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 196 } 197 198 return fmt.Sprintf("%s(%s)", geomType, data), nil 199 } 200 201 // GeomFromText is a function that returns a point type from a WKT string 202 type GeomFromText struct { 203 expression.NaryExpression 204 } 205 206 var _ sql.FunctionExpression = (*GeomFromText)(nil) 207 var _ sql.CollationCoercible = (*GeomFromText)(nil) 208 209 // NewGeomFromText creates a new point expression. 210 func NewGeomFromText(args ...sql.Expression) (sql.Expression, error) { 211 if len(args) < 1 || len(args) > 3 { 212 return nil, sql.ErrInvalidArgumentNumber.New("ST_GEOMFROMTEXT", "1, 2, or 3", len(args)) 213 } 214 return &GeomFromText{expression.NaryExpression{ChildExpressions: args}}, nil 215 } 216 217 // FunctionName implements sql.FunctionExpression 218 func (g *GeomFromText) FunctionName() string { 219 return "st_geomfromtext" 220 } 221 222 // Description implements sql.FunctionExpression 223 func (g *GeomFromText) Description() string { 224 return "returns a new point from a WKT string." 225 } 226 227 // Type implements the sql.Expression interface. 228 func (g *GeomFromText) Type() sql.Type { 229 // TODO: return type is determined after Eval, use Geometry for now? 230 return types.GeometryType{} 231 } 232 233 // CollationCoercibility implements the interface sql.CollationCoercible. 234 func (*GeomFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 235 return sql.Collation_binary, 4 236 } 237 238 func (g *GeomFromText) String() string { 239 var args = make([]string, len(g.ChildExpressions)) 240 for i, arg := range g.ChildExpressions { 241 args[i] = arg.String() 242 } 243 return fmt.Sprintf("%s(%s)", g.FunctionName(), strings.Join(args, ",")) 244 } 245 246 // WithChildren implements the Expression interface. 247 func (g *GeomFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 248 return NewGeomFromText(children...) 249 } 250 251 func TrimWKTData(s string) (string, int, error) { 252 // Must start with open parenthesis 253 if s[0] != '(' { 254 return "", 0, sql.ErrInvalidGISData.New() 255 } 256 257 // Read until all parentheses are closed 258 var count, end int 259 for count, end = 1, 1; end < len(s) && count != 0; end++ { 260 switch s[end] { 261 case '(': 262 count++ 263 case ')': 264 count-- 265 } 266 } 267 if count != 0 { 268 return "", 0, sql.ErrInvalidGISData.New() 269 } 270 271 // Remove parentheses, extract data, and trim 272 data := s[1 : end-1] 273 data = strings.TrimSpace(data) 274 275 return data, end, nil 276 } 277 278 // ParseWKTHeader should extract the type and data from the geometry string 279 // `end` is used to detect extra characters after a valid geometry 280 func ParseWKTHeader(s string) (string, string, int, error) { 281 // Read until first open parenthesis 282 start := strings.Index(s, "(") 283 284 // Bad if no parenthesis found 285 if start == -1 { 286 return "", "", 0, sql.ErrInvalidGISData.New() 287 } 288 289 // Get Geometry Type 290 geomType := s[:start] 291 geomType = strings.TrimSpace(geomType) 292 geomType = strings.ToLower(geomType) 293 294 data, end, err := TrimWKTData(s[start:]) 295 if err != nil { 296 return "", "", 0, err 297 } 298 299 return geomType, data, start + end, nil 300 } 301 302 // WKTToPoint expects a string like this "1.2 3.4" 303 func WKTToPoint(s string, srid uint32, order bool) (types.Point, error) { 304 if len(s) == 0 { 305 return types.Point{}, sql.ErrInvalidGISData.New() 306 } 307 308 // Get everything between spaces 309 args := strings.Fields(s) 310 if len(args) != 2 { 311 return types.Point{}, sql.ErrInvalidGISData.New() 312 } 313 314 x, err := strconv.ParseFloat(args[0], 64) 315 if err != nil { 316 return types.Point{}, sql.ErrInvalidGISData.New() 317 } 318 319 y, err := strconv.ParseFloat(args[1], 64) 320 if err != nil { 321 return types.Point{}, sql.ErrInvalidGISData.New() 322 } 323 324 if order { 325 x, y = y, x 326 } 327 328 return types.Point{SRID: srid, X: x, Y: y}, nil 329 } 330 331 // WKTToLine expects a string like "1.2 3.4, 5.6 7.8, ..." 332 func WKTToLine(s string, srid uint32, order bool) (types.LineString, error) { 333 if len(s) == 0 { 334 return types.LineString{}, sql.ErrInvalidGISData.New() 335 } 336 337 pointStrs := strings.Split(s, ",") 338 var points = make([]types.Point, len(pointStrs)) 339 var err error 340 for i, ps := range pointStrs { 341 ps = strings.TrimSpace(ps) 342 if points[i], err = WKTToPoint(ps, srid, order); err != nil { 343 return types.LineString{}, sql.ErrInvalidGISData.New() 344 } 345 } 346 347 // Create LineString object 348 return types.LineString{SRID: srid, Points: points}, nil 349 } 350 351 // WKTToPoly Expects a string like "(1 2, 3 4), (5 6, 7 8), ..." 352 func WKTToPoly(s string, srid uint32, order bool) (types.Polygon, error) { 353 var lines []types.LineString 354 for { 355 // Get first linestring 356 lineStr, end, err := TrimWKTData(s) 357 if err != nil { 358 return types.Polygon{}, err 359 } 360 361 // Parse line 362 line, err := WKTToLine(lineStr, srid, order) 363 if err != nil { 364 return types.Polygon{}, sql.ErrInvalidGISData.New() 365 } 366 if !isLinearRing(line) { 367 return types.Polygon{}, sql.ErrInvalidGISData.New() 368 } 369 lines = append(lines, line) 370 371 // Prepare next string 372 s = s[end:] 373 s = strings.TrimSpace(s) 374 375 // Reached end 376 if len(s) == 0 { 377 break 378 } 379 380 // LineStrings must be comma-separated 381 if s[0] != ',' { 382 return types.Polygon{}, sql.ErrInvalidGISData.New() 383 } 384 385 // Drop leading comma 386 s = s[1:] 387 s = strings.TrimSpace(s) 388 } 389 390 return types.Polygon{SRID: srid, Lines: lines}, nil 391 } 392 393 // WKTToMPoint expects a string like "1.2 3.4, 5.6 7.8, ..." 394 func WKTToMPoint(s string, srid uint32, order bool) (types.MultiPoint, error) { 395 if len(s) == 0 { 396 return types.MultiPoint{}, sql.ErrInvalidGISData.New() 397 } 398 399 pointStrs := strings.Split(s, ",") 400 var points = make([]types.Point, len(pointStrs)) 401 var err error 402 for i, ps := range pointStrs { 403 ps = strings.TrimSpace(ps) 404 if points[i], err = WKTToPoint(ps, srid, order); err != nil { 405 return types.MultiPoint{}, sql.ErrInvalidGISData.New() 406 } 407 } 408 409 return types.MultiPoint{SRID: srid, Points: points}, nil 410 } 411 412 // WKTToMLine Expects a string like "(1 2, 3 4), (5 6, 7 8), ..." 413 func WKTToMLine(s string, srid uint32, order bool) (types.MultiLineString, error) { 414 var lines []types.LineString 415 for { 416 // Get first linestring 417 lineStr, end, err := TrimWKTData(s) 418 if err != nil { 419 return types.MultiLineString{}, err 420 } 421 422 // Parse line 423 line, err := WKTToLine(lineStr, srid, order) 424 if err != nil { 425 return types.MultiLineString{}, sql.ErrInvalidGISData.New() 426 } 427 lines = append(lines, line) 428 429 // Prepare next string 430 s = s[end:] 431 s = strings.TrimSpace(s) 432 433 // Reached end 434 if len(s) == 0 { 435 break 436 } 437 438 // LineStrings must be comma-separated 439 if s[0] != ',' { 440 return types.MultiLineString{}, sql.ErrInvalidGISData.New() 441 } 442 443 // Drop leading comma 444 s = s[1:] 445 s = strings.TrimSpace(s) 446 } 447 448 return types.MultiLineString{SRID: srid, Lines: lines}, nil 449 } 450 451 // WKTToMPoly Expects a string like "((1 2, 3 4), (5 6, 7 8), ...), ..." 452 func WKTToMPoly(s string, srid uint32, order bool) (types.MultiPolygon, error) { 453 var polys []types.Polygon 454 for { 455 // Get first polygon 456 polyStr, end, err := TrimWKTData(s) 457 if err != nil { 458 return types.MultiPolygon{}, err 459 } 460 461 // Parse poly 462 poly, err := WKTToPoly(polyStr, srid, order) 463 if err != nil { 464 return types.MultiPolygon{}, sql.ErrInvalidGISData.New() 465 } 466 polys = append(polys, poly) 467 468 // Prepare next string 469 s = s[end:] 470 s = strings.TrimSpace(s) 471 472 // Reached end 473 if len(s) == 0 { 474 break 475 } 476 477 // Polygons must be comma-separated 478 if s[0] != ',' { 479 return types.MultiPolygon{}, sql.ErrInvalidGISData.New() 480 } 481 482 // Drop leading comma 483 s = s[1:] 484 s = strings.TrimSpace(s) 485 } 486 487 return types.MultiPolygon{SRID: srid, Polygons: polys}, nil 488 } 489 490 // WKTToGeomColl Expects a string like "((1 2, 3 4), (5 6, 7 8), ...), ..." 491 func WKTToGeomColl(s string, srid uint32, order bool) (types.GeomColl, error) { 492 // empty geometry collections 493 if len(s) == 0 { 494 return types.GeomColl{SRID: srid, Geoms: []types.GeometryValue{}}, nil 495 } 496 497 var geoms []types.GeometryValue 498 for { 499 // parse first type 500 geomType, data, end, err := ParseWKTHeader(s) 501 if err != nil { 502 return types.GeomColl{}, sql.ErrInvalidGISData.New() 503 } 504 var geom types.GeometryValue 505 switch geomType { 506 case "point": 507 geom, err = WKTToPoint(data, srid, order) 508 case "linestring": 509 geom, err = WKTToLine(data, srid, order) 510 case "polygon": 511 geom, err = WKTToPoly(data, srid, order) 512 case "multipoint": 513 geom, err = WKTToMPoint(data, srid, order) 514 case "multilinestring": 515 geom, err = WKTToMLine(data, srid, order) 516 case "multipolygon": 517 geom, err = WKTToMPoly(data, srid, order) 518 case "geometrycollection": 519 geom, err = WKTToGeomColl(data, srid, order) 520 default: 521 return types.GeomColl{}, sql.ErrInvalidGISData.New() 522 } 523 geoms = append(geoms, geom) 524 525 // Prepare next string 526 s = s[end:] 527 s = strings.TrimSpace(s) 528 529 // Reached end 530 if len(s) == 0 { 531 break 532 } 533 534 // Geometries must be comma-separated 535 if s[0] != ',' { 536 return types.GeomColl{}, sql.ErrInvalidGISData.New() 537 } 538 539 // Drop leading comma 540 s = s[1:] 541 s = strings.TrimSpace(s) 542 } 543 544 return types.GeomColl{SRID: srid, Geoms: geoms}, nil 545 } 546 547 // WKTToGeom expects a string in WKT format, and converts it to a geometry type 548 func WKTToGeom(ctx *sql.Context, row sql.Row, exprs []sql.Expression, expectedGeomType string) (types.GeometryValue, error) { 549 val, err := exprs[0].Eval(ctx, row) 550 if err != nil { 551 return nil, err 552 } 553 554 if val == nil { 555 return nil, nil 556 } 557 558 s, ok := val.(string) 559 if !ok { 560 return nil, sql.ErrInvalidGISData.New() 561 } 562 563 s = strings.TrimSpace(s) 564 geomType, data, end, err := ParseWKTHeader(s) 565 if err != nil || end != len(s) { // detect extra characters 566 return nil, err 567 } 568 569 if expectedGeomType != "" && geomType != expectedGeomType { 570 return nil, sql.ErrInvalidGISData.New() 571 } 572 573 srid := uint32(0) 574 if len(exprs) >= 2 { 575 s, err := exprs[1].Eval(ctx, row) 576 if err != nil { 577 return nil, err 578 } 579 if s == nil { 580 return nil, nil 581 } 582 s, _, err = types.Int64.Convert(s) 583 if err != nil { 584 return nil, err 585 } 586 if err = types.ValidateSRID(int(s.(int64)), "st_geomfromtext"); err != nil { 587 return nil, err 588 } 589 srid = uint32(s.(int64)) 590 } 591 592 order := srid == types.GeoSpatialSRID 593 if len(exprs) == 3 { 594 o, err := exprs[2].Eval(ctx, row) 595 if err != nil { 596 return nil, err 597 } 598 if o == nil { 599 return nil, nil 600 } 601 order, err = ParseAxisOrder(o.(string)) 602 if err != nil { 603 return nil, err 604 } 605 } 606 607 switch geomType { 608 case "point": 609 return WKTToPoint(data, srid, order) 610 case "linestring": 611 return WKTToLine(data, srid, order) 612 case "polygon": 613 return WKTToPoly(data, srid, order) 614 case "multipoint": 615 return WKTToMPoint(data, srid, order) 616 case "multilinestring": 617 return WKTToMLine(data, srid, order) 618 case "multipolygon": 619 return WKTToMPoly(data, srid, order) 620 case "geometrycollection": 621 return WKTToGeomColl(data, srid, order) 622 default: 623 return nil, sql.ErrInvalidGISData.New() 624 } 625 } 626 627 // Eval implements the sql.Expression interface. 628 func (g *GeomFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 629 geom, err := WKTToGeom(ctx, row, g.ChildExpressions, "") 630 if sql.ErrInvalidGISData.Is(err) { 631 return nil, sql.ErrInvalidGISData.New(g.FunctionName()) 632 } 633 return geom, err 634 } 635 636 // PointFromText is a function that returns a Point type from a WKT string 637 type PointFromText struct { 638 expression.NaryExpression 639 } 640 641 var _ sql.FunctionExpression = (*PointFromText)(nil) 642 var _ sql.CollationCoercible = (*PointFromText)(nil) 643 644 // NewPointFromText creates a new point expression. 645 func NewPointFromText(args ...sql.Expression) (sql.Expression, error) { 646 if len(args) < 1 || len(args) > 3 { 647 return nil, sql.ErrInvalidArgumentNumber.New("ST_POINTFROMTEXT", "1, 2, or 3", len(args)) 648 } 649 return &PointFromText{expression.NaryExpression{ChildExpressions: args}}, nil 650 } 651 652 // FunctionName implements sql.FunctionExpression 653 func (p *PointFromText) FunctionName() string { 654 return "st_pointfromtext" 655 } 656 657 // Description implements sql.FunctionExpression 658 func (p *PointFromText) Description() string { 659 return "returns a new point from a WKT string." 660 } 661 662 // Type implements the sql.Expression interface. 663 func (p *PointFromText) Type() sql.Type { 664 return types.PointType{} 665 } 666 667 // CollationCoercibility implements the interface sql.CollationCoercible. 668 func (*PointFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 669 return sql.Collation_binary, 4 670 } 671 672 func (p *PointFromText) String() string { 673 var args = make([]string, len(p.ChildExpressions)) 674 for i, arg := range p.ChildExpressions { 675 args[i] = arg.String() 676 } 677 return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ",")) 678 } 679 680 // WithChildren implements the Expression interface. 681 func (p *PointFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 682 return NewPointFromText(children...) 683 } 684 685 // Eval implements the sql.Expression interface. 686 func (p *PointFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 687 point, err := WKTToGeom(ctx, row, p.ChildExpressions, "point") 688 if sql.ErrInvalidGISData.Is(err) { 689 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 690 } 691 return point, err 692 } 693 694 // LineFromText is a function that returns a LineString type from a WKT string 695 type LineFromText struct { 696 expression.NaryExpression 697 } 698 699 var _ sql.FunctionExpression = (*LineFromText)(nil) 700 var _ sql.CollationCoercible = (*LineFromText)(nil) 701 702 // NewLineFromText creates a new point expression. 703 func NewLineFromText(args ...sql.Expression) (sql.Expression, error) { 704 if len(args) < 1 || len(args) > 3 { 705 return nil, sql.ErrInvalidArgumentNumber.New("ST_LINEFROMTEXT", "1 or 2", len(args)) 706 } 707 return &LineFromText{expression.NaryExpression{ChildExpressions: args}}, nil 708 } 709 710 // FunctionName implements sql.FunctionExpression 711 func (l *LineFromText) FunctionName() string { 712 return "st_linefromtext" 713 } 714 715 // Description implements sql.FunctionExpression 716 func (l *LineFromText) Description() string { 717 return "returns a new line from a WKT string." 718 } 719 720 // Type implements the sql.Expression interface. 721 func (l *LineFromText) Type() sql.Type { 722 return types.LineStringType{} 723 } 724 725 // CollationCoercibility implements the interface sql.CollationCoercible. 726 func (*LineFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 727 return sql.Collation_binary, 4 728 } 729 730 func (l *LineFromText) String() string { 731 var args = make([]string, len(l.ChildExpressions)) 732 for i, arg := range l.ChildExpressions { 733 args[i] = arg.String() 734 } 735 return fmt.Sprintf("%s(%s)", l.FunctionName(), strings.Join(args, ",")) 736 } 737 738 // WithChildren implements the Expression interface. 739 func (l *LineFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 740 return NewLineFromText(children...) 741 } 742 743 // Eval implements the sql.Expression interface. 744 func (l *LineFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 745 line, err := WKTToGeom(ctx, row, l.ChildExpressions, "linestring") 746 if sql.ErrInvalidGISData.Is(err) { 747 return nil, sql.ErrInvalidGISData.New(l.FunctionName()) 748 } 749 return line, err 750 } 751 752 // PolyFromText is a function that returns a Polygon type from a WKT string 753 type PolyFromText struct { 754 expression.NaryExpression 755 } 756 757 var _ sql.FunctionExpression = (*PolyFromText)(nil) 758 var _ sql.CollationCoercible = (*PolyFromText)(nil) 759 760 // NewPolyFromText creates a new polygon expression. 761 func NewPolyFromText(args ...sql.Expression) (sql.Expression, error) { 762 if len(args) < 1 || len(args) > 3 { 763 return nil, sql.ErrInvalidArgumentNumber.New("ST_POLYFROMTEXT", "1, 2, or 3", len(args)) 764 } 765 return &PolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil 766 } 767 768 // FunctionName implements sql.FunctionExpression 769 func (p *PolyFromText) FunctionName() string { 770 return "st_polyfromtext" 771 } 772 773 // Description implements sql.FunctionExpression 774 func (p *PolyFromText) Description() string { 775 return "returns a new polygon from a WKT string." 776 } 777 778 // Type implements the sql.Expression interface. 779 func (p *PolyFromText) Type() sql.Type { 780 return types.PolygonType{} 781 } 782 783 // CollationCoercibility implements the interface sql.CollationCoercible. 784 func (*PolyFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 785 return sql.Collation_binary, 4 786 } 787 788 func (p *PolyFromText) String() string { 789 var args = make([]string, len(p.ChildExpressions)) 790 for i, arg := range p.ChildExpressions { 791 args[i] = arg.String() 792 } 793 return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ",")) 794 } 795 796 // WithChildren implements the Expression interface. 797 func (p *PolyFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 798 return NewPolyFromText(children...) 799 } 800 801 // Eval implements the sql.Expression interface. 802 func (p *PolyFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 803 poly, err := WKTToGeom(ctx, row, p.ChildExpressions, "polygon") 804 if sql.ErrInvalidGISData.Is(err) { 805 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 806 } 807 return poly, err 808 } 809 810 // MultiPoint is a function that returns a MultiPoint type from a WKT string 811 type MPointFromText struct { 812 expression.NaryExpression 813 } 814 815 var _ sql.FunctionExpression = (*MPointFromText)(nil) 816 var _ sql.CollationCoercible = (*MPointFromText)(nil) 817 818 // NewMPointFromText creates a new MultiPoint expression. 819 func NewMPointFromText(args ...sql.Expression) (sql.Expression, error) { 820 if len(args) < 1 || len(args) > 3 { 821 return nil, sql.ErrInvalidArgumentNumber.New("ST_MULTIPOINTFROMTEXT", "1 or 2", len(args)) 822 } 823 return &MPointFromText{expression.NaryExpression{ChildExpressions: args}}, nil 824 } 825 826 // FunctionName implements sql.FunctionExpression 827 func (p *MPointFromText) FunctionName() string { 828 return "st_mpointfromtext" 829 } 830 831 // Description implements sql.FunctionExpression 832 func (p *MPointFromText) Description() string { 833 return "returns a new multipoint from a WKT string." 834 } 835 836 // Type implements the sql.Expression interface. 837 func (p *MPointFromText) Type() sql.Type { 838 return types.MultiPointType{} 839 } 840 841 // CollationCoercibility implements the interface sql.CollationCoercible. 842 func (*MPointFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 843 return sql.Collation_binary, 4 844 } 845 846 func (p *MPointFromText) String() string { 847 var args = make([]string, len(p.ChildExpressions)) 848 for i, arg := range p.ChildExpressions { 849 args[i] = arg.String() 850 } 851 return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ",")) 852 } 853 854 // WithChildren implements the Expression interface. 855 func (p *MPointFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 856 return NewMPointFromText(children...) 857 } 858 859 // Eval implements the sql.Expression interface. 860 func (p *MPointFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 861 line, err := WKTToGeom(ctx, row, p.ChildExpressions, "multipoint") 862 if sql.ErrInvalidGISData.Is(err) { 863 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 864 } 865 return line, err 866 } 867 868 // MLineFromText is a function that returns a MultiLineString type from a WKT string 869 type MLineFromText struct { 870 expression.NaryExpression 871 } 872 873 var _ sql.FunctionExpression = (*MLineFromText)(nil) 874 var _ sql.CollationCoercible = (*MLineFromText)(nil) 875 876 // NewMLineFromText creates a new multilinestring expression. 877 func NewMLineFromText(args ...sql.Expression) (sql.Expression, error) { 878 if len(args) < 1 || len(args) > 3 { 879 return nil, sql.ErrInvalidArgumentNumber.New("ST_MLINEFROMTEXT", "1 or 2", len(args)) 880 } 881 return &MLineFromText{expression.NaryExpression{ChildExpressions: args}}, nil 882 } 883 884 // FunctionName implements sql.FunctionExpression 885 func (l *MLineFromText) FunctionName() string { 886 return "st_mlinefromtext" 887 } 888 889 // Description implements sql.FunctionExpression 890 func (l *MLineFromText) Description() string { 891 return "returns a new multi line from a WKT string." 892 } 893 894 // Type implements the sql.Expression interface. 895 func (l *MLineFromText) Type() sql.Type { 896 return types.MultiLineStringType{} 897 } 898 899 // CollationCoercibility implements the interface sql.CollationCoercible. 900 func (*MLineFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 901 return sql.Collation_binary, 4 902 } 903 904 func (l *MLineFromText) String() string { 905 var args = make([]string, len(l.ChildExpressions)) 906 for i, arg := range l.ChildExpressions { 907 args[i] = arg.String() 908 } 909 return fmt.Sprintf("%s(%s)", l.FunctionName(), strings.Join(args, ",")) 910 } 911 912 // WithChildren implements the Expression interface. 913 func (l *MLineFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 914 return NewMLineFromText(children...) 915 } 916 917 // Eval implements the sql.Expression interface. 918 func (l *MLineFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 919 mline, err := WKTToGeom(ctx, row, l.ChildExpressions, "multilinestring") 920 if sql.ErrInvalidGISData.Is(err) { 921 return nil, sql.ErrInvalidGISData.New(l.FunctionName()) 922 } 923 return mline, err 924 } 925 926 // MPolyFromText is a function that returns a MultiPolygon type from a WKT string 927 type MPolyFromText struct { 928 expression.NaryExpression 929 } 930 931 var _ sql.FunctionExpression = (*MPolyFromText)(nil) 932 var _ sql.CollationCoercible = (*MPolyFromText)(nil) 933 934 // NewMPolyFromText creates a new multilinestring expression. 935 func NewMPolyFromText(args ...sql.Expression) (sql.Expression, error) { 936 if len(args) < 1 || len(args) > 3 { 937 return nil, sql.ErrInvalidArgumentNumber.New("ST_MPOLYFROMTEXT", "1 or 2", len(args)) 938 } 939 return &MPolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil 940 } 941 942 // FunctionName implements sql.FunctionExpression 943 func (p *MPolyFromText) FunctionName() string { 944 return "st_mpolyfromtext" 945 } 946 947 // Description implements sql.FunctionExpression 948 func (p *MPolyFromText) Description() string { 949 return "returns a new multipolygon from a WKT string." 950 } 951 952 // Type implements the sql.Expression interface. 953 func (p *MPolyFromText) Type() sql.Type { 954 return types.MultiPolygonType{} 955 } 956 957 // CollationCoercibility implements the interface sql.CollationCoercible. 958 func (*MPolyFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 959 return sql.Collation_binary, 4 960 } 961 962 func (p *MPolyFromText) String() string { 963 var args = make([]string, len(p.ChildExpressions)) 964 for i, arg := range p.ChildExpressions { 965 args[i] = arg.String() 966 } 967 return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ",")) 968 } 969 970 // WithChildren implements the Expression interface. 971 func (p *MPolyFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 972 return NewMPolyFromText(children...) 973 } 974 975 // Eval implements the sql.Expression interface. 976 func (p *MPolyFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 977 mpoly, err := WKTToGeom(ctx, row, p.ChildExpressions, "multipolygon") 978 if sql.ErrInvalidGISData.Is(err) { 979 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 980 } 981 return mpoly, err 982 } 983 984 // GeomCollFromText is a function that returns a MultiPolygon type from a WKT string 985 type GeomCollFromText struct { 986 expression.NaryExpression 987 } 988 989 var _ sql.FunctionExpression = (*GeomCollFromText)(nil) 990 var _ sql.CollationCoercible = (*GeomCollFromText)(nil) 991 992 // NewGeomCollFromText creates a new multilinestring expression. 993 func NewGeomCollFromText(args ...sql.Expression) (sql.Expression, error) { 994 if len(args) < 1 || len(args) > 3 { 995 return nil, sql.ErrInvalidArgumentNumber.New("ST_GeomCollFromText", "1 or 2", len(args)) 996 } 997 return &MPolyFromText{expression.NaryExpression{ChildExpressions: args}}, nil 998 } 999 1000 // FunctionName implements sql.FunctionExpression 1001 func (p *GeomCollFromText) FunctionName() string { 1002 return "st_geomcollfromtext" 1003 } 1004 1005 // Description implements sql.FunctionExpression 1006 func (p *GeomCollFromText) Description() string { 1007 return "returns a new geometry collection from a WKT string." 1008 } 1009 1010 // Type implements the sql.Expression interface. 1011 func (p *GeomCollFromText) Type() sql.Type { 1012 return types.GeomCollType{} 1013 } 1014 1015 // CollationCoercibility implements the interface sql.CollationCoercible. 1016 func (*GeomCollFromText) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 1017 return sql.Collation_binary, 4 1018 } 1019 1020 func (p *GeomCollFromText) String() string { 1021 var args = make([]string, len(p.ChildExpressions)) 1022 for i, arg := range p.ChildExpressions { 1023 args[i] = arg.String() 1024 } 1025 return fmt.Sprintf("%s(%s)", p.FunctionName(), strings.Join(args, ",")) 1026 } 1027 1028 // WithChildren implements the Expression interface. 1029 func (p *GeomCollFromText) WithChildren(children ...sql.Expression) (sql.Expression, error) { 1030 return NewGeomFromText(children...) 1031 } 1032 1033 // Eval implements the sql.Expression interface. 1034 func (p *GeomCollFromText) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 1035 geom, err := WKTToGeom(ctx, row, p.ChildExpressions, "geometrycollection") 1036 if sql.ErrInvalidGISData.Is(err) { 1037 return nil, sql.ErrInvalidGISData.New(p.FunctionName()) 1038 } 1039 return geom, err 1040 }