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