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 }