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  }