github.com/dolthub/go-mysql-server@v0.18.0/sql/types/polygon.go (about)

     1  // Copyright 2022 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 types
    16  
    17  import (
    18  	"math"
    19  	"reflect"
    20  
    21  	"github.com/dolthub/vitess/go/sqltypes"
    22  	"github.com/dolthub/vitess/go/vt/proto/query"
    23  	"gopkg.in/src-d/go-errors.v1"
    24  
    25  	"github.com/dolthub/go-mysql-server/sql"
    26  )
    27  
    28  // PolygonType represents the POLYGON type.
    29  // https://dev.mysql.com/doc/refman/8.0/en/gis-class-polygon.html
    30  // The type of the returned value is Polygon.
    31  type PolygonType struct {
    32  	SRID        uint32
    33  	DefinedSRID bool
    34  }
    35  
    36  // Polygon is the value type returned from PolygonType. Implements GeometryValue.
    37  type Polygon struct {
    38  	SRID  uint32
    39  	Lines []LineString
    40  }
    41  
    42  var _ sql.Type = PolygonType{}
    43  var _ sql.SpatialColumnType = PolygonType{}
    44  var _ sql.CollationCoercible = PolygonType{}
    45  var _ GeometryValue = Polygon{}
    46  
    47  var (
    48  	ErrNotPolygon = errors.NewKind("value of type %T is not a polygon")
    49  
    50  	polygonValueType = reflect.TypeOf(Polygon{})
    51  )
    52  
    53  // Compare implements Type interface.
    54  func (t PolygonType) Compare(a interface{}, b interface{}) (int, error) {
    55  	return GeometryType{}.Compare(a, b)
    56  }
    57  
    58  // Convert implements Type interface.
    59  func (t PolygonType) Convert(v interface{}) (interface{}, sql.ConvertInRange, error) {
    60  	switch buf := v.(type) {
    61  	case nil:
    62  		return nil, sql.InRange, nil
    63  	case []byte:
    64  		poly, _, err := GeometryType{}.Convert(buf)
    65  		if sql.ErrInvalidGISData.Is(err) {
    66  			return nil, sql.OutOfRange, sql.ErrInvalidGISData.New("PolygonType.Convert")
    67  		} else if err != nil {
    68  			return poly, sql.OutOfRange, err
    69  		}
    70  		return poly, sql.InRange, nil
    71  	case string:
    72  		return t.Convert([]byte(buf))
    73  	case Polygon:
    74  		if err := t.MatchSRID(buf); err != nil {
    75  			return nil, sql.OutOfRange, err
    76  		}
    77  		return buf, sql.InRange, nil
    78  	default:
    79  		return nil, sql.OutOfRange, sql.ErrSpatialTypeConversion.New()
    80  	}
    81  }
    82  
    83  // Equals implements the Type interface.
    84  func (t PolygonType) Equals(otherType sql.Type) bool {
    85  	_, ok := otherType.(PolygonType)
    86  	return ok
    87  }
    88  
    89  // MaxTextResponseByteLength implements the Type interface
    90  func (t PolygonType) MaxTextResponseByteLength(_ *sql.Context) uint32 {
    91  	return GeometryMaxByteLength
    92  }
    93  
    94  // Promote implements the Type interface.
    95  func (t PolygonType) Promote() sql.Type {
    96  	return t
    97  }
    98  
    99  // SQL implements Type interface.
   100  func (t PolygonType) SQL(ctx *sql.Context, dest []byte, v interface{}) (sqltypes.Value, error) {
   101  	if v == nil {
   102  		return sqltypes.NULL, nil
   103  	}
   104  
   105  	v, _, err := t.Convert(v)
   106  	if err != nil {
   107  		return sqltypes.Value{}, nil
   108  	}
   109  
   110  	buf := v.(Polygon).Serialize()
   111  
   112  	return sqltypes.MakeTrusted(sqltypes.Geometry, buf), nil
   113  }
   114  
   115  // String implements Type interface.
   116  func (t PolygonType) String() string {
   117  	return "polygon"
   118  }
   119  
   120  // Type implements Type interface.
   121  func (t PolygonType) Type() query.Type {
   122  	return sqltypes.Geometry
   123  }
   124  
   125  // ValueType implements Type interface.
   126  func (t PolygonType) ValueType() reflect.Type {
   127  	return polygonValueType
   128  }
   129  
   130  // Zero implements Type interface.
   131  func (t PolygonType) Zero() interface{} {
   132  	return Polygon{Lines: []LineString{{Points: []Point{{}, {}, {}, {}}}}}
   133  }
   134  
   135  // CollationCoercibility implements sql.CollationCoercible interface.
   136  func (PolygonType) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) {
   137  	return sql.Collation_binary, 5
   138  }
   139  
   140  // GetSpatialTypeSRID implements SpatialColumnType interface.
   141  func (t PolygonType) GetSpatialTypeSRID() (uint32, bool) {
   142  	return t.SRID, t.DefinedSRID
   143  }
   144  
   145  // SetSRID implements SpatialColumnType interface.
   146  func (t PolygonType) SetSRID(v uint32) sql.Type {
   147  	t.SRID = v
   148  	t.DefinedSRID = true
   149  	return t
   150  }
   151  
   152  // MatchSRID implements SpatialColumnType interface
   153  func (t PolygonType) MatchSRID(v interface{}) error {
   154  	val, ok := v.(Polygon)
   155  	if !ok {
   156  		return ErrNotPolygon.New(v)
   157  	}
   158  	if !t.DefinedSRID {
   159  		return nil
   160  	} else if t.SRID == val.SRID {
   161  		return nil
   162  	}
   163  	return sql.ErrNotMatchingSRID.New(val.SRID, t.SRID)
   164  }
   165  
   166  // implementsGeometryValue implements GeometryValue interface.
   167  func (p Polygon) implementsGeometryValue() {}
   168  
   169  // GetSRID implements GeometryValue interface.
   170  func (p Polygon) GetSRID() uint32 {
   171  	return p.SRID
   172  }
   173  
   174  // SetSRID implements GeometryValue interface.
   175  func (p Polygon) SetSRID(srid uint32) GeometryValue {
   176  	lines := make([]LineString, len(p.Lines))
   177  	for i, l := range p.Lines {
   178  		lines[i] = l.SetSRID(srid).(LineString)
   179  	}
   180  	return Polygon{
   181  		SRID:  srid,
   182  		Lines: lines,
   183  	}
   184  }
   185  
   186  // Serialize implements GeometryValue interface.
   187  func (p Polygon) Serialize() (buf []byte) {
   188  	var numPoints int
   189  	for _, l := range p.Lines {
   190  		numPoints += len(l.Points)
   191  	}
   192  	buf = AllocateGeoTypeBuffer(numPoints, len(p.Lines)+1, 0)
   193  	WriteEWKBHeader(buf, p.SRID, WKBPolyID)
   194  	p.WriteData(buf[EWKBHeaderSize:])
   195  	return
   196  }
   197  
   198  // WriteData implements GeometryValue interface.
   199  func (p Polygon) WriteData(buf []byte) int {
   200  	WriteCount(buf, uint32(len(p.Lines)))
   201  	buf = buf[CountSize:]
   202  	count := CountSize
   203  	for _, l := range p.Lines {
   204  		c := l.WriteData(buf)
   205  		buf = buf[c:]
   206  		count += c
   207  	}
   208  	return count
   209  }
   210  
   211  // Swap implements GeometryValue interface.
   212  // TODO: possible in place?
   213  func (p Polygon) Swap() GeometryValue {
   214  	lines := make([]LineString, len(p.Lines))
   215  	for i, l := range p.Lines {
   216  		lines[i] = l.Swap().(LineString)
   217  	}
   218  	return Polygon{
   219  		SRID:  p.SRID,
   220  		Lines: lines,
   221  	}
   222  }
   223  
   224  // BBox implements GeometryValue interface.
   225  func (p Polygon) BBox() (float64, float64, float64, float64) {
   226  	minX, minY, maxX, maxY := math.MaxFloat64, math.MaxFloat64, -math.MaxFloat64, -math.MaxFloat64
   227  	for _, l := range p.Lines {
   228  		lMinX, lMinY, lMaxX, lMaxY := l.BBox()
   229  		minX = math.Min(minX, lMinX)
   230  		minY = math.Min(minY, lMinY)
   231  		maxX = math.Max(maxX, lMaxX)
   232  		maxY = math.Max(maxY, lMaxY)
   233  	}
   234  	return minX, minY, maxX, maxY
   235  }