github.com/dolthub/go-mysql-server@v0.18.0/sql/expression/function/spatial/geojson.go (about) 1 package spatial 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "math" 8 "strconv" 9 "strings" 10 11 "github.com/dolthub/go-mysql-server/sql" 12 "github.com/dolthub/go-mysql-server/sql/expression" 13 "github.com/dolthub/go-mysql-server/sql/types" 14 ) 15 16 // AsGeoJSON is a function that returns a point type from a WKT string 17 type AsGeoJSON struct { 18 expression.NaryExpression 19 } 20 21 var _ sql.FunctionExpression = (*AsGeoJSON)(nil) 22 var _ sql.CollationCoercible = (*AsGeoJSON)(nil) 23 24 // NewAsGeoJSON creates a new point expression. 25 func NewAsGeoJSON(args ...sql.Expression) (sql.Expression, error) { 26 if len(args) < 1 || len(args) > 3 { 27 return nil, sql.ErrInvalidArgumentNumber.New("ST_ASGEOJSON", "1, 2, or 3", len(args)) 28 } 29 return &AsGeoJSON{expression.NaryExpression{ChildExpressions: args}}, nil 30 } 31 32 // FunctionName implements sql.FunctionExpression 33 func (g *AsGeoJSON) FunctionName() string { 34 return "st_asgeojson" 35 } 36 37 // Description implements sql.FunctionExpression 38 func (g *AsGeoJSON) Description() string { 39 return "returns a GeoJSON object from the geometry." 40 } 41 42 // Type implements the sql.Expression interface. 43 func (g *AsGeoJSON) Type() sql.Type { 44 return types.JSON 45 } 46 47 // CollationCoercibility implements the interface sql.CollationCoercible. 48 func (f *AsGeoJSON) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 49 return ctx.GetCollation(), 2 50 } 51 52 func (g *AsGeoJSON) String() string { 53 var args = make([]string, len(g.ChildExpressions)) 54 for i, arg := range g.ChildExpressions { 55 args[i] = arg.String() 56 } 57 return fmt.Sprintf("%s(%s)", g.FunctionName(), strings.Join(args, ",")) 58 } 59 60 // WithChildren implements the Expression interface. 61 func (g *AsGeoJSON) WithChildren(children ...sql.Expression) (sql.Expression, error) { 62 return NewAsGeoJSON(children...) 63 } 64 65 func PointToSlice(p types.Point) [2]float64 { 66 return [2]float64{p.X, p.Y} 67 } 68 69 func LineToSlice(l types.LineString) [][2]float64 { 70 arr := make([][2]float64, len(l.Points)) 71 for i, p := range l.Points { 72 arr[i] = PointToSlice(p) 73 } 74 return arr 75 } 76 77 func PolyToSlice(p types.Polygon) [][][2]float64 { 78 arr := make([][][2]float64, len(p.Lines)) 79 for i, l := range p.Lines { 80 arr[i] = LineToSlice(l) 81 } 82 return arr 83 } 84 85 func MPointToSlice(p types.MultiPoint) [][2]float64 { 86 arr := make([][2]float64, len(p.Points)) 87 for i, point := range p.Points { 88 arr[i] = PointToSlice(point) 89 } 90 return arr 91 } 92 93 func MLineToSlice(p types.MultiLineString) [][][2]float64 { 94 arr := make([][][2]float64, len(p.Lines)) 95 for i, l := range p.Lines { 96 arr[i] = LineToSlice(l) 97 } 98 return arr 99 } 100 101 func MPolyToSlice(p types.MultiPolygon) [][][][2]float64 { 102 arr := make([][][][2]float64, len(p.Polygons)) 103 for i, p := range p.Polygons { 104 arr[i] = PolyToSlice(p) 105 } 106 return arr 107 } 108 109 func GeomCollToSlice(g types.GeomColl) interface{} { 110 arr := make([]interface{}, len(g.Geoms)) 111 for i, geom := range g.Geoms { 112 obj := make(map[string]interface{}) 113 switch v := geom.(type) { 114 case types.Point: 115 obj["type"] = "Point" 116 obj["coordinates"] = PointToSlice(v) 117 case types.LineString: 118 obj["type"] = "LineString" 119 obj["coordinates"] = LineToSlice(v) 120 case types.Polygon: 121 obj["type"] = "Polygon" 122 obj["coordinates"] = PolyToSlice(v) 123 case types.MultiPoint: 124 obj["type"] = "MultiPoint" 125 obj["coordinates"] = MPointToSlice(v) 126 case types.MultiLineString: 127 obj["type"] = "MultiLineString" 128 obj["coordinates"] = MLineToSlice(v) 129 case types.MultiPolygon: 130 obj["type"] = "MultiPolygon" 131 obj["coordinates"] = MPolyToSlice(v) 132 case types.GeomColl: 133 obj["type"] = "GeometryCollection" 134 obj["geometries"] = GeomCollToSlice(v) 135 } 136 arr[i] = obj 137 } 138 139 return arr 140 } 141 142 func FindBBox(v interface{}) [4]float64 { 143 var res [4]float64 144 switch v := v.(type) { 145 case types.Point: 146 res = [4]float64{v.X, v.Y, v.X, v.Y} 147 case types.LineString: 148 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 149 for _, p := range v.Points { 150 tmp := FindBBox(p) 151 res[0] = math.Min(res[0], tmp[0]) 152 res[1] = math.Min(res[1], tmp[1]) 153 res[2] = math.Max(res[2], tmp[2]) 154 res[3] = math.Max(res[3], tmp[3]) 155 } 156 case types.Polygon: 157 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 158 for _, l := range v.Lines { 159 tmp := FindBBox(l) 160 res[0] = math.Min(res[0], tmp[0]) 161 res[1] = math.Min(res[1], tmp[1]) 162 res[2] = math.Max(res[2], tmp[2]) 163 res[3] = math.Max(res[3], tmp[3]) 164 } 165 case types.MultiPoint: 166 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 167 for _, p := range v.Points { 168 tmp := FindBBox(p) 169 res[0] = math.Min(res[0], tmp[0]) 170 res[1] = math.Min(res[1], tmp[1]) 171 res[2] = math.Max(res[2], tmp[2]) 172 res[3] = math.Max(res[3], tmp[3]) 173 } 174 case types.MultiLineString: 175 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 176 for _, l := range v.Lines { 177 tmp := FindBBox(l) 178 res[0] = math.Min(res[0], tmp[0]) 179 res[1] = math.Min(res[1], tmp[1]) 180 res[2] = math.Max(res[2], tmp[2]) 181 res[3] = math.Max(res[3], tmp[3]) 182 } 183 case types.MultiPolygon: 184 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 185 for _, p := range v.Polygons { 186 tmp := FindBBox(p) 187 res[0] = math.Min(res[0], tmp[0]) 188 res[1] = math.Min(res[1], tmp[1]) 189 res[2] = math.Max(res[2], tmp[2]) 190 res[3] = math.Max(res[3], tmp[3]) 191 } 192 case types.GeomColl: 193 res = [4]float64{math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64} 194 for _, geom := range v.Geoms { 195 tmp := FindBBox(geom) 196 res[0] = math.Min(res[0], tmp[0]) 197 res[1] = math.Min(res[1], tmp[1]) 198 res[2] = math.Max(res[2], tmp[2]) 199 res[3] = math.Max(res[3], tmp[3]) 200 } 201 } 202 203 return res 204 } 205 206 func RoundFloatSlices(v interface{}, p float64) interface{} { 207 switch v := v.(type) { 208 case [2]float64: 209 return [2]float64{math.Round(v[0]*p) / p, math.Round(v[1]*p) / p} 210 case [][2]float64: 211 res := make([][2]float64, len(v)) 212 for i, c := range v { 213 res[i] = RoundFloatSlices(c, p).([2]float64) 214 } 215 return res 216 case [][][2]float64: 217 res := make([][][2]float64, len(v)) 218 for i, c := range v { 219 res[i] = RoundFloatSlices(c, p).([][2]float64) 220 } 221 return res 222 case [][][][2]float64: 223 res := make([][][][2]float64, len(v)) 224 for i, c := range v { 225 res[i] = RoundFloatSlices(c, p).([][][2]float64) 226 } 227 return res 228 } 229 return nil 230 } 231 232 // getIntArg is a helper method that evaluates the given sql.Expression to an int type, errors on float32 and float64, 233 // and returns nil 234 func getIntArg(ctx *sql.Context, row sql.Row, expr sql.Expression) (interface{}, error) { 235 x, err := expr.Eval(ctx, row) 236 if err != nil { 237 return nil, err 238 } 239 if x == nil { 240 return nil, nil 241 } 242 switch x.(type) { 243 case float32, float64: 244 return nil, errors.New("received a float when it should be an int") 245 } 246 x, _, err = types.Int64.Convert(x) 247 if err != nil { 248 return nil, err 249 } 250 return int(x.(int64)), nil 251 } 252 253 // Eval implements the sql.Expression interface. 254 func (g *AsGeoJSON) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 255 // convert spatial type to map, then place inside sql.JSONDocument 256 val, err := g.ChildExpressions[0].Eval(ctx, row) 257 if err != nil { 258 return nil, err 259 } 260 261 if val == nil { 262 return nil, nil 263 } 264 265 obj := make(map[string]interface{}) 266 switch v := val.(type) { 267 case types.Point: 268 obj["type"] = "Point" 269 obj["coordinates"] = PointToSlice(v) 270 case types.LineString: 271 obj["type"] = "LineString" 272 obj["coordinates"] = LineToSlice(v) 273 case types.Polygon: 274 obj["type"] = "Polygon" 275 obj["coordinates"] = PolyToSlice(v) 276 case types.MultiPoint: 277 obj["type"] = "MultiPoint" 278 obj["coordinates"] = MPointToSlice(v) 279 case types.MultiLineString: 280 obj["type"] = "MultiLineString" 281 obj["coordinates"] = MLineToSlice(v) 282 case types.MultiPolygon: 283 obj["type"] = "MultiPolygon" 284 obj["coordinates"] = MPolyToSlice(v) 285 case types.GeomColl: 286 obj["type"] = "GeometryCollection" 287 obj["geometries"] = GeomCollToSlice(v) 288 default: 289 return nil, sql.ErrInvalidArgumentType.New(g.FunctionName()) 290 } 291 292 if len(g.ChildExpressions) == 1 { 293 return types.JSONDocument{Val: obj}, nil 294 } 295 296 // Evaluate precision 297 p, err := getIntArg(ctx, row, g.ChildExpressions[1]) 298 if err != nil { 299 return nil, errors.New("incorrect precision value") 300 } 301 if p == nil { 302 return nil, nil 303 } 304 pp := p.(int) 305 if pp < 0 { 306 return nil, errors.New("incorrect precision value") 307 } 308 if pp > 17 { 309 pp = 17 310 } 311 312 // Round floats 313 prec := math.Pow10(pp) 314 if _, ok := obj["coordinates"]; ok { 315 obj["coordinates"] = RoundFloatSlices(obj["coordinates"], prec) 316 } 317 318 if len(g.ChildExpressions) == 2 { 319 return types.JSONDocument{Val: obj}, nil 320 } 321 322 // Evaluate flag argument 323 f, err := getIntArg(ctx, row, g.ChildExpressions[2]) 324 if err != nil { 325 return nil, errors.New("incorrect flag value") 326 } 327 if f == nil { 328 return nil, nil 329 } 330 flag := f.(int) 331 if flag < 0 || flag > 7 { 332 return nil, sql.ErrInvalidArgumentDetails.New(g.FunctionName(), flag) 333 } 334 // TODO: the flags do very different things for when the SRID is GeoSpatial 335 switch flag { 336 // Flags 1,3,5 have bounding box 337 case 1, 3, 5: 338 // Don't find bounding box for empty geometries 339 if g, ok := val.(types.GeomColl); ok { 340 if len(g.Geoms) == 0 { 341 break 342 } 343 } 344 res := FindBBox(val) 345 for i, r := range res { 346 res[i] = math.Round(r*prec) / prec 347 if math.IsInf(res[i], 1) { 348 res[i] = math.MaxFloat64 349 } else if math.IsInf(res[i], -1) { 350 res[i] = -math.MaxFloat64 351 } 352 } 353 obj["bbox"] = res 354 // Flag 2 and 4 add CRS URN (EPSG: <srid>); only shows up if SRID != 0 355 case 2, 4: 356 // CRS obj only shows up if srid != 0 357 srid := val.(types.GeometryValue).GetSRID() 358 if srid != 0 { 359 // Create CRS URN Object 360 crs := make(map[string]interface{}) 361 crs["type"] = "name" 362 363 // Create properties 364 props := make(map[string]interface{}) 365 // Flag 2 is short format CRS URN, while 4 is long format 366 sridStr := strconv.Itoa(int(srid)) 367 if flag == 2 { 368 props["name"] = "EPSG:" + sridStr 369 } else { 370 props["name"] = "urn:ogc:def:crs:EPSG::" + sridStr 371 } 372 // Add properties to crs 373 crs["properties"] = props 374 375 // Add CRS to main object 376 obj["crs"] = crs 377 } 378 } 379 380 return types.JSONDocument{Val: obj}, nil 381 } 382 383 // GeomFromGeoJSON is a function returns a geometry based on a string 384 type GeomFromGeoJSON struct { 385 expression.NaryExpression 386 } 387 388 var _ sql.FunctionExpression = (*GeomFromGeoJSON)(nil) 389 var _ sql.CollationCoercible = (*GeomFromGeoJSON)(nil) 390 391 // NewGeomFromGeoJSON creates a new point expression. 392 func NewGeomFromGeoJSON(args ...sql.Expression) (sql.Expression, error) { 393 if len(args) < 1 || len(args) > 3 { 394 return nil, sql.ErrInvalidArgumentNumber.New("ST_GEOMFROMGEOJSON", "1, 2, or 3", len(args)) 395 } 396 return &GeomFromGeoJSON{expression.NaryExpression{ChildExpressions: args}}, nil 397 } 398 399 // FunctionName implements sql.FunctionExpression 400 func (g *GeomFromGeoJSON) FunctionName() string { 401 return "st_geomfromgeojson" 402 } 403 404 // Description implements sql.FunctionExpression 405 func (g *GeomFromGeoJSON) Description() string { 406 return "returns a GeoJSON object from the geometry." 407 } 408 409 // Type implements the sql.Expression interface. 410 func (g *GeomFromGeoJSON) Type() sql.Type { 411 return types.GeometryType{} 412 } 413 414 // CollationCoercibility implements the interface sql.CollationCoercible. 415 func (*GeomFromGeoJSON) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 416 return sql.Collation_binary, 4 417 } 418 419 func (g *GeomFromGeoJSON) String() string { 420 var args = make([]string, len(g.ChildExpressions)) 421 for i, arg := range g.ChildExpressions { 422 args[i] = arg.String() 423 } 424 return fmt.Sprintf("ST_GEOMFROMGEOJSON(%s)", strings.Join(args, ",")) 425 } 426 427 // WithChildren implements the Expression interface. 428 func (g *GeomFromGeoJSON) WithChildren(children ...sql.Expression) (sql.Expression, error) { 429 return NewGeomFromGeoJSON(children...) 430 } 431 432 func SliceToPoint(coords interface{}) (interface{}, error) { 433 c, ok := coords.([]interface{}) 434 if !ok { 435 return nil, errors.New("member 'coordinates' must be of type 'array'") 436 } 437 if len(c) < 2 { 438 return nil, errors.New("unsupported number of coordinate dimensions") 439 } 440 x, ok := c[0].(float64) 441 if !ok { 442 return nil, errors.New("coordinate must be of type number") 443 } 444 y, ok := c[1].(float64) 445 if !ok { 446 return nil, errors.New("coordinate must be of type number") 447 } 448 return types.Point{SRID: types.GeoSpatialSRID, X: x, Y: y}, nil 449 } 450 451 func SliceToLine(coords interface{}) (interface{}, error) { 452 cs, ok := coords.([]interface{}) 453 if !ok { 454 return nil, errors.New("member 'coordinates' must be of type 'array'") 455 } 456 if len(cs) < 2 { 457 return nil, errors.New("invalid GeoJSON data provided") 458 } 459 points := make([]types.Point, len(cs)) 460 for i, c := range cs { 461 p, err := SliceToPoint(c) 462 if err != nil { 463 return nil, err 464 } 465 points[i] = p.(types.Point) 466 } 467 return types.LineString{SRID: types.GeoSpatialSRID, Points: points}, nil 468 } 469 470 func SliceToPoly(coords interface{}) (interface{}, error) { 471 // coords must be a slice of slices of at least 2 slices of 2 float64 472 cs, ok := coords.([]interface{}) 473 if !ok { 474 return nil, errors.New("member 'coordinates' must be of type 'array'") 475 } 476 if len(cs) == 0 { 477 return nil, errors.New("not enough lines") 478 } 479 lines := make([]types.LineString, len(cs)) 480 for i, c := range cs { 481 l, err := SliceToLine(c) 482 if err != nil { 483 return nil, err 484 } 485 if !isLinearRing(l.(types.LineString)) { 486 return nil, errors.New("invalid GeoJSON data provided") 487 } 488 lines[i] = l.(types.LineString) 489 } 490 return types.Polygon{SRID: types.GeoSpatialSRID, Lines: lines}, nil 491 } 492 493 func SliceToMPoint(coords interface{}) (interface{}, error) { 494 cs, ok := coords.([]interface{}) 495 if !ok { 496 return nil, errors.New("member 'coordinates' must be of type 'array'") 497 } 498 if len(cs) < 2 { 499 return nil, errors.New("invalid GeoJSON data provided") 500 } 501 points := make([]types.Point, len(cs)) 502 for i, c := range cs { 503 p, err := SliceToPoint(c) 504 if err != nil { 505 return nil, err 506 } 507 points[i] = p.(types.Point) 508 } 509 return types.MultiPoint{SRID: types.GeoSpatialSRID, Points: points}, nil 510 } 511 512 func SliceToMLine(coords interface{}) (interface{}, error) { 513 // coords must be a slice of slices of at least 2 slices of 2 float64 514 cs, ok := coords.([]interface{}) 515 if !ok { 516 return nil, errors.New("member 'coordinates' must be of type 'array'") 517 } 518 if len(cs) == 0 { 519 return nil, errors.New("not enough lines") 520 } 521 lines := make([]types.LineString, len(cs)) 522 for i, c := range cs { 523 l, err := SliceToLine(c) 524 if err != nil { 525 return nil, err 526 } 527 lines[i] = l.(types.LineString) 528 } 529 return types.MultiLineString{SRID: types.GeoSpatialSRID, Lines: lines}, nil 530 } 531 532 func SliceToMPoly(coords interface{}) (interface{}, error) { 533 // coords must be a slice of slices of slices at least 4 slices of 2 float64 534 cs, ok := coords.([]interface{}) 535 if !ok { 536 return nil, errors.New("member 'coordinates' must be of type 'array'") 537 } 538 if len(cs) == 0 { 539 return nil, errors.New("not enough polygons") 540 } 541 polys := make([]types.Polygon, len(cs)) 542 for i, c := range cs { 543 p, err := SliceToPoly(c) 544 if err != nil { 545 return nil, err 546 } 547 polys[i] = p.(types.Polygon) 548 } 549 return types.MultiPolygon{SRID: types.GeoSpatialSRID, Polygons: polys}, nil 550 } 551 552 func SliceToGeomColl(geometries interface{}) (interface{}, error) { 553 // geomObjs should be a slice of geojsons 554 geomObjs, ok := geometries.([]interface{}) 555 if !ok { 556 return nil, errors.New("member 'geometries' must be of type 'array'") 557 } 558 559 geoms := make([]types.GeometryValue, len(geomObjs)) 560 for i, o := range geomObjs { 561 obj, ok := o.(map[string]interface{}) 562 if !ok { 563 return nil, errors.New("member 'geometries' must be of type 'object'") 564 } 565 res, _, err := ParseGeoJsonData(obj) 566 if err != nil { 567 return nil, err 568 } 569 geoms[i] = res.(types.GeometryValue) 570 } 571 return types.GeomColl{SRID: types.GeoSpatialSRID, Geoms: geoms}, nil 572 } 573 574 func ParseGeoJsonData(obj map[string]interface{}) (interface{}, string, error) { 575 geomType, ok := obj["type"] 576 if !ok { 577 return nil, "", errors.New("missing required member 'type'") 578 } 579 580 gt, ok := geomType.(string) 581 if !ok { 582 return nil, "", errors.New("member 'type' must be of type 'string'") 583 } 584 585 var res interface{} 586 var err error 587 switch gt { 588 case "Point": 589 coords, ok := obj["coordinates"] 590 if !ok { 591 return nil, "", errors.New("missing required member 'coordinates'") 592 } 593 res, err = SliceToPoint(coords) 594 case "LineString": 595 coords, ok := obj["coordinates"] 596 if !ok { 597 return nil, "", errors.New("missing required member 'coordinates'") 598 } 599 res, err = SliceToLine(coords) 600 case "Polygon": 601 coords, ok := obj["coordinates"] 602 if !ok { 603 return nil, "", errors.New("missing required member 'coordinates'") 604 } 605 res, err = SliceToPoly(coords) 606 case "MultiPoint": 607 coords, ok := obj["coordinates"] 608 if !ok { 609 return nil, "", errors.New("missing required member 'coordinates'") 610 } 611 res, err = SliceToMPoint(coords) 612 case "MultiLineString": 613 coords, ok := obj["coordinates"] 614 if !ok { 615 return nil, "", errors.New("missing required member 'coordinates'") 616 } 617 res, err = SliceToMLine(coords) 618 case "MultiPolygon": 619 coords, ok := obj["coordinates"] 620 if !ok { 621 return nil, "", errors.New("missing required member 'coordinates'") 622 } 623 res, err = SliceToMPoly(coords) 624 case "GeometryCollection": 625 geoms, ok := obj["geometries"] 626 if !ok { 627 return nil, "", errors.New("missing required member 'geometries'") 628 } 629 res, err = SliceToGeomColl(geoms) 630 case "Feature": 631 geom, ok := obj["geometry"] 632 if !ok { 633 return nil, "", errors.New("missing required member 'geometry'") 634 } 635 geomObj, ok := geom.(map[string]interface{}) 636 if !ok { 637 return nil, "", errors.New("member 'geometry' must be of type 'object'") 638 } 639 // TODO: figure out what properties is used for 640 props, ok := obj["properties"] 641 if !ok { 642 return nil, "", errors.New("missing required member 'properties'") 643 } 644 _, ok = props.(map[string]interface{}) 645 if !ok { 646 return nil, "", errors.New("member 'properties' must be of type 'object'") 647 } 648 res, gt, err = ParseGeoJsonData(geomObj) 649 case "FeatureCollection": 650 feats, ok := obj["features"] 651 if !ok { 652 return nil, "", errors.New("missing required member 'features'") 653 } 654 res, err = SliceToGeomColl(feats) 655 default: 656 return nil, "", errors.New("member 'type' is wrong") 657 } 658 return res, gt, err 659 } 660 661 // Eval implements the sql.Expression interface. 662 func (g *GeomFromGeoJSON) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { 663 val, err := g.ChildExpressions[0].Eval(ctx, row) 664 if err != nil { 665 return nil, err 666 } 667 if val == nil { 668 return nil, nil 669 } 670 val, _, err = types.LongBlob.Convert(val) 671 if err != nil { 672 return nil, err 673 } 674 675 switch s := val.(type) { 676 case string: 677 val = []byte(s) 678 case []byte: 679 val = s 680 } 681 682 var obj map[string]interface{} 683 err = json.Unmarshal(val.([]byte), &obj) 684 if err != nil { 685 return nil, err 686 } 687 688 // Create type accordingly 689 res, geomType, err := ParseGeoJsonData(obj) 690 if err != nil { 691 return nil, err 692 } 693 if len(g.ChildExpressions) == 1 { 694 return res, nil 695 } 696 697 // Evaluate flag argument 698 f, err := getIntArg(ctx, row, g.ChildExpressions[1]) 699 if err != nil { 700 return nil, errors.New("incorrect flag value") 701 } 702 if f == nil { 703 return nil, nil 704 } 705 flag := f.(int) 706 if flag < 1 || flag > 4 { 707 return nil, sql.ErrInvalidArgumentDetails.New(g.FunctionName(), flag) 708 } 709 // reject higher dimensions; otherwise, higher dimensions are already stripped off 710 if flag == 1 { 711 switch geomType { 712 case "Point": 713 if len(obj["coordinates"].([]interface{})) > 2 { 714 return nil, errors.New("unsupported number of coordinate dimensions") 715 } 716 case "LineString", "MultiPoint": 717 for _, a := range obj["coordinates"].([]interface{}) { 718 if len(a.([]interface{})) > 2 { 719 return nil, errors.New("unsupported number of coordinate dimensions") 720 } 721 } 722 case "Polygon", "MultiLineString": 723 for _, a := range obj["coordinates"].([]interface{}) { 724 for _, b := range a.([]interface{}) { 725 if len(b.([]interface{})) > 2 { 726 return nil, errors.New("unsupported number of coordinate dimensions") 727 } 728 } 729 } 730 case "MultiPolygon": 731 for _, a := range obj["coordinates"].([]interface{}) { 732 for _, b := range a.([]interface{}) { 733 for _, c := range b.([]interface{}) { 734 if len(c.([]interface{})) > 2 { 735 return nil, errors.New("unsupported number of coordinate dimensions") 736 } 737 } 738 } 739 } 740 } 741 } 742 if len(g.ChildExpressions) == 2 { 743 return res, nil 744 } 745 746 // Evaluate SRID 747 s, err := getIntArg(ctx, row, g.ChildExpressions[2]) 748 if err != nil { 749 return nil, errors.New("incorrect srid value") 750 } 751 if err = types.ValidateSRID(s.(int), g.FunctionName()); err != nil { 752 return nil, err 753 } 754 srid := uint32(s.(int)) 755 res = res.(types.GeometryValue).SetSRID(srid) 756 return res, nil 757 }