github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geo_test.go (about) 1 // Copyright 2020 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package geo 12 13 import ( 14 "encoding/hex" 15 "fmt" 16 "testing" 17 18 "github.com/cockroachdb/cockroach/pkg/geo/geopb" 19 "github.com/cockroachdb/cockroach/pkg/geo/geos" 20 "github.com/cockroachdb/cockroach/pkg/util/leaktest" 21 "github.com/cockroachdb/datadriven" 22 "github.com/golang/geo/s2" 23 "github.com/stretchr/testify/require" 24 "github.com/twpayne/go-geom" 25 ) 26 27 var ( 28 testGeomPoint = geom.NewPointFlat(geom.XY, []float64{1.0, 2.0}) 29 testGeomLineString = geom.NewLineStringFlat(geom.XY, []float64{1.0, 1.0, 2.0, 2.0}) 30 testGeomPolygon = geom.NewPolygonFlat(geom.XY, []float64{1.0, 1.0, 2.0, 2.0, 1.0, 2.0, 1.0, 1.0}, []int{8}) 31 testGeomMultiPoint = geom.NewMultiPointFlat(geom.XY, []float64{1.0, 1.0, 2.0, 2.0}) 32 testGeomMultiLineString = geom.NewMultiLineStringFlat(geom.XY, []float64{1.0, 1.0, 2.0, 2.0, 3.0, 3.0, 4.0, 4.0}, []int{4, 8}) 33 testGeomMultiPolygon = geom.NewMultiPolygon(geom.XY) 34 testGeomGeometryCollection = geom.NewGeometryCollection() 35 ) 36 var ( 37 emptyGeomPoint = geom.NewPointEmpty(geom.XY) 38 emptyGeomLineString = geom.NewLineString(geom.XY) 39 emptyGeomPolygon = geom.NewPolygon(geom.XY) 40 emptyGeomMultiPoint = geom.NewMultiPoint(geom.XY) 41 emptyGeomMultiLineString = geom.NewMultiLineString(geom.XY) 42 emptyGeomMultiPolygon = geom.NewMultiPolygon(geom.XY) 43 emptyGeomGeometryCollection = geom.NewGeometryCollection() 44 emptyGeomObjectsInGeometryCollection = geom.NewGeometryCollection() 45 emptyGeomPointInGeometryCollection = geom.NewGeometryCollection() 46 ) 47 48 func init() { 49 testGeomLineString.SetSRID(4326) 50 // Initialize geomTestMultiPolygon. 51 { 52 for _, polygon := range []*geom.Polygon{ 53 testGeomPolygon, 54 geom.NewPolygonFlat(geom.XY, []float64{3.0, 3.0, 4.0, 4.0, 3.0, 4.0, 3.0, 3.0}, []int{8}), 55 } { 56 err := testGeomMultiPolygon.Push(polygon) 57 if err != nil { 58 panic(err) 59 } 60 } 61 } 62 // Initialize testGeomGeometryCollection. 63 { 64 err := testGeomGeometryCollection.Push(testGeomPoint, testGeomMultiPoint) 65 if err != nil { 66 panic(err) 67 } 68 } 69 // Initialize emptyGeomPointInGeometryCollection. 70 { 71 err := emptyGeomPointInGeometryCollection.Push( 72 geom.NewLineStringFlat(geom.XY, []float64{1.0, 1.0, 2.0, 2.0}), 73 emptyGeomPoint, 74 ) 75 if err != nil { 76 panic(err) 77 } 78 } 79 // Initialize emptyGeomObjectsInGeometryCollection. 80 { 81 err := emptyGeomObjectsInGeometryCollection.Push( 82 emptyGeomPoint, 83 emptyGeomLineString, 84 emptyGeomPolygon, 85 ) 86 if err != nil { 87 panic(err) 88 } 89 } 90 } 91 92 func mustDecodeEWKBFromString(t *testing.T, h string) geopb.EWKB { 93 decoded, err := hex.DecodeString(h) 94 require.NoError(t, err) 95 return geopb.EWKB(decoded) 96 } 97 98 func TestGeospatialTypeFitsColumnMetadata(t *testing.T) { 99 testCases := []struct { 100 t GeospatialType 101 srid geopb.SRID 102 shape geopb.Shape 103 errorContains string 104 }{ 105 {MustParseGeometry("POINT(1.0 1.0)"), 0, geopb.Shape_Geometry, ""}, 106 {MustParseGeometry("POINT(1.0 1.0)"), 0, geopb.Shape_Unset, ""}, 107 {MustParseGeometry("SRID=4326;POINT(1.0 1.0)"), 0, geopb.Shape_Geometry, ""}, 108 {MustParseGeometry("SRID=4326;POINT(1.0 1.0)"), 0, geopb.Shape_Unset, ""}, 109 {MustParseGeometry("SRID=4326;POINT(1.0 1.0)"), 0, geopb.Shape_LineString, "type Point does not match column type LineString"}, 110 {MustParseGeometry("POINT(1.0 1.0)"), 4326, geopb.Shape_Geometry, "SRID 0 does not match column SRID 4326"}, 111 } 112 113 for _, tc := range testCases { 114 t.Run(fmt.Sprintf("%#v_fits_%d_%s", tc.t, tc.srid, tc.shape), func(t *testing.T) { 115 err := GeospatialTypeFitsColumnMetadata(tc.t, tc.srid, tc.shape) 116 if tc.errorContains != "" { 117 require.Error(t, err) 118 require.Contains(t, err.Error(), tc.errorContains) 119 } else { 120 require.NoError(t, err) 121 } 122 }) 123 } 124 } 125 126 func TestSpatialObjectFromGeom(t *testing.T) { 127 testCases := []struct { 128 desc string 129 g geom.T 130 ret geopb.SpatialObject 131 }{ 132 { 133 "point", 134 testGeomPoint, 135 geopb.SpatialObject{ 136 EWKB: mustDecodeEWKBFromString(t, "0101000000000000000000F03F0000000000000040"), 137 SRID: 0, 138 Shape: geopb.Shape_Point, 139 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 2, MaxY: 2}, 140 }, 141 }, 142 { 143 "linestring", 144 testGeomLineString, 145 geopb.SpatialObject{ 146 EWKB: mustDecodeEWKBFromString(t, "0102000020E610000002000000000000000000F03F000000000000F03F00000000000000400000000000000040"), 147 SRID: 4326, 148 Shape: geopb.Shape_LineString, 149 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 2, MinY: 1, MaxY: 2}, 150 }, 151 }, 152 { 153 "polygon", 154 testGeomPolygon, 155 geopb.SpatialObject{ 156 EWKB: mustDecodeEWKBFromString(t, "01030000000100000004000000000000000000F03F000000000000F03F00000000000000400000000000000040000000000000F03F0000000000000040000000000000F03F000000000000F03F"), 157 SRID: 0, 158 Shape: geopb.Shape_Polygon, 159 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 2, MinY: 1, MaxY: 2}, 160 }, 161 }, 162 { 163 "multipoint", 164 testGeomMultiPoint, 165 geopb.SpatialObject{ 166 EWKB: mustDecodeEWKBFromString(t, "0104000000020000000101000000000000000000F03F000000000000F03F010100000000000000000000400000000000000040"), 167 SRID: 0, 168 Shape: geopb.Shape_MultiPoint, 169 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 2, MinY: 1, MaxY: 2}, 170 }, 171 }, 172 { 173 "multilinestring", 174 testGeomMultiLineString, 175 geopb.SpatialObject{ 176 EWKB: mustDecodeEWKBFromString(t, "010500000002000000010200000002000000000000000000F03F000000000000F03F000000000000004000000000000000400102000000020000000000000000000840000000000000084000000000000010400000000000001040"), 177 SRID: 0, 178 Shape: geopb.Shape_MultiLineString, 179 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 4, MinY: 1, MaxY: 4}, 180 }, 181 }, 182 { 183 "multipolygon", 184 testGeomMultiPolygon, 185 geopb.SpatialObject{ 186 EWKB: mustDecodeEWKBFromString(t, "01060000000200000001030000000100000004000000000000000000F03F000000000000F03F00000000000000400000000000000040000000000000F03F0000000000000040000000000000F03F000000000000F03F0103000000010000000400000000000000000008400000000000000840000000000000104000000000000010400000000000000840000000000000104000000000000008400000000000000840"), 187 SRID: 0, 188 Shape: geopb.Shape_MultiPolygon, 189 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 4, MinY: 1, MaxY: 4}, 190 }, 191 }, 192 { 193 "geometrycollection", 194 testGeomGeometryCollection, 195 geopb.SpatialObject{ 196 EWKB: mustDecodeEWKBFromString(t, "0107000000020000000101000000000000000000F03F00000000000000400104000000020000000101000000000000000000F03F000000000000F03F010100000000000000000000400000000000000040"), 197 SRID: 0, 198 Shape: geopb.Shape_GeometryCollection, 199 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 2, MinY: 1, MaxY: 2}, 200 }, 201 }, 202 } 203 for _, tc := range testCases { 204 t.Run(tc.desc, func(t *testing.T) { 205 so, err := spatialObjectFromGeom(tc.g) 206 require.NoError(t, err) 207 require.Equal(t, tc.ret, so) 208 }) 209 } 210 } 211 212 func TestGeographyAsS2(t *testing.T) { 213 testCases := []struct { 214 wkt string 215 expected []s2.Region 216 }{ 217 { 218 "POINT(1.0 5.0)", 219 []s2.Region{s2.PointFromLatLng(s2.LatLngFromDegrees(5.0, 1.0))}, 220 }, 221 { 222 "LINESTRING(1.0 5.0, 6.0 7.0)", 223 []s2.Region{ 224 s2.PolylineFromLatLngs([]s2.LatLng{ 225 s2.LatLngFromDegrees(5.0, 1.0), 226 s2.LatLngFromDegrees(7.0, 6.0), 227 }), 228 }, 229 }, 230 { 231 `POLYGON( 232 (0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), 233 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2) 234 )`, 235 []s2.Region{ 236 s2.PolygonFromLoops([]*s2.Loop{ 237 s2.LoopFromPoints([]s2.Point{ 238 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 239 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 240 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 241 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 242 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 243 }), 244 s2.LoopFromPoints([]s2.Point{ 245 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 246 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 247 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 248 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 249 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 250 }), 251 }), 252 }, 253 }, 254 { 255 `POLYGON( 256 (0.0 0.0, 0.5 0.0, 0.75 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), 257 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2) 258 )`, 259 []s2.Region{ 260 s2.PolygonFromLoops([]*s2.Loop{ 261 s2.LoopFromPoints([]s2.Point{ 262 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 263 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.5)), 264 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.75)), 265 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 266 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 267 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 268 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 269 }), 270 s2.LoopFromPoints([]s2.Point{ 271 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 272 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 273 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 274 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 275 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 276 }), 277 }), 278 }, 279 }, 280 { 281 `POLYGON( 282 (0.0 0.0, 0.0 1.0, 1.0 1.0, 1.0 0.0, 0.0 0.0), 283 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2) 284 )`, 285 []s2.Region{ 286 s2.PolygonFromLoops([]*s2.Loop{ 287 s2.LoopFromPoints([]s2.Point{ 288 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 289 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 290 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 291 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 292 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 293 }), 294 s2.LoopFromPoints([]s2.Point{ 295 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 296 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 297 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 298 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 299 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 300 }), 301 }), 302 }, 303 }, 304 { 305 `POLYGON( 306 (0.0 0.0, 0.0 0.5, 0.0 1.0, 1.0 1.0, 1.0 0.0, 0.0 0.0), 307 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2) 308 )`, 309 []s2.Region{ 310 s2.PolygonFromLoops([]*s2.Loop{ 311 s2.LoopFromPoints([]s2.Point{ 312 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 313 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 314 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 315 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 316 s2.PointFromLatLng(s2.LatLngFromDegrees(0.5, 0.0)), 317 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 318 }), 319 s2.LoopFromPoints([]s2.Point{ 320 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 321 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 322 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 323 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 324 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 325 }), 326 }), 327 }, 328 }, 329 { 330 `GEOMETRYCOLLECTION(POINT(1.0 2.0), POLYGON( 331 (0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), 332 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2) 333 ))`, 334 []s2.Region{ 335 s2.PointFromLatLng(s2.LatLngFromDegrees(2.0, 1.0)), 336 s2.PolygonFromLoops([]*s2.Loop{ 337 s2.LoopFromPoints([]s2.Point{ 338 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 339 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 340 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 341 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 342 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 343 }), 344 s2.LoopFromPoints([]s2.Point{ 345 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 346 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 347 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 348 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 349 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 350 }), 351 }), 352 }, 353 }, 354 { 355 "MULTIPOINT((1.0 5.0), (3.0 4.0))", 356 []s2.Region{ 357 s2.PointFromLatLng(s2.LatLngFromDegrees(5.0, 1.0)), 358 s2.PointFromLatLng(s2.LatLngFromDegrees(4.0, 3.0)), 359 }, 360 }, 361 { 362 "MULTILINESTRING((1.0 5.0, 6.0 7.0), (3.0 4.0, 5.0 6.0))", 363 []s2.Region{ 364 s2.PolylineFromLatLngs([]s2.LatLng{ 365 s2.LatLngFromDegrees(5.0, 1.0), 366 s2.LatLngFromDegrees(7.0, 6.0), 367 }), 368 s2.PolylineFromLatLngs([]s2.LatLng{ 369 s2.LatLngFromDegrees(4.0, 3.0), 370 s2.LatLngFromDegrees(6.0, 5.0), 371 }), 372 }, 373 }, 374 { 375 `MULTIPOLYGON( 376 ((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), 377 (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2)), 378 379 ((3.0 3.0, 4.0 3.0, 4.0 4.0, 3.0 4.0, 3.0 3.0), 380 (3.2 3.2, 3.2 3.4, 3.4 3.4, 3.4 3.2, 3.2 3.2)) 381 )`, 382 []s2.Region{ 383 s2.PolygonFromLoops([]*s2.Loop{ 384 s2.LoopFromPoints([]s2.Point{ 385 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 386 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 1.0)), 387 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 1.0)), 388 s2.PointFromLatLng(s2.LatLngFromDegrees(1.0, 0.0)), 389 s2.PointFromLatLng(s2.LatLngFromDegrees(0.0, 0.0)), 390 }), 391 s2.LoopFromPoints([]s2.Point{ 392 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 393 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.4)), 394 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.4)), 395 s2.PointFromLatLng(s2.LatLngFromDegrees(0.4, 0.2)), 396 s2.PointFromLatLng(s2.LatLngFromDegrees(0.2, 0.2)), 397 }), 398 }), 399 s2.PolygonFromLoops([]*s2.Loop{ 400 s2.LoopFromPoints([]s2.Point{ 401 s2.PointFromLatLng(s2.LatLngFromDegrees(3.0, 3.0)), 402 s2.PointFromLatLng(s2.LatLngFromDegrees(3.0, 4.0)), 403 s2.PointFromLatLng(s2.LatLngFromDegrees(4.0, 4.0)), 404 s2.PointFromLatLng(s2.LatLngFromDegrees(4.0, 3.0)), 405 s2.PointFromLatLng(s2.LatLngFromDegrees(3.0, 3.0)), 406 }), 407 s2.LoopFromPoints([]s2.Point{ 408 s2.PointFromLatLng(s2.LatLngFromDegrees(3.2, 3.2)), 409 s2.PointFromLatLng(s2.LatLngFromDegrees(3.2, 3.4)), 410 s2.PointFromLatLng(s2.LatLngFromDegrees(3.4, 3.4)), 411 s2.PointFromLatLng(s2.LatLngFromDegrees(3.4, 3.2)), 412 s2.PointFromLatLng(s2.LatLngFromDegrees(3.2, 3.2)), 413 }), 414 }), 415 }, 416 }, 417 } 418 419 for _, tc := range testCases { 420 t.Run(tc.wkt, func(t *testing.T) { 421 g, err := ParseGeography(tc.wkt) 422 require.NoError(t, err) 423 424 shapes, err := g.AsS2(EmptyBehaviorError) 425 require.NoError(t, err) 426 427 require.Equal(t, tc.expected, shapes) 428 }) 429 } 430 431 // Test when things are empty. 432 emptyTestCases := []struct { 433 wkt string 434 expectedOmit []s2.Region 435 }{ 436 { 437 "GEOMETRYCOLLECTION ( LINESTRING EMPTY, MULTIPOINT((1.0 5.0), (3.0 4.0)) )", 438 []s2.Region{ 439 s2.PointFromLatLng(s2.LatLngFromDegrees(5.0, 1.0)), 440 s2.PointFromLatLng(s2.LatLngFromDegrees(4.0, 3.0)), 441 }, 442 }, 443 { 444 "GEOMETRYCOLLECTION EMPTY", 445 nil, 446 }, 447 { 448 "MULTILINESTRING (EMPTY, (1.0 2.0, 3.0 4.0))", 449 []s2.Region{ 450 s2.PolylineFromLatLngs([]s2.LatLng{ 451 s2.LatLngFromDegrees(2.0, 1.0), 452 s2.LatLngFromDegrees(4.0, 3.0), 453 }), 454 }, 455 }, 456 { 457 "MULTILINESTRING (EMPTY, EMPTY)", 458 nil, 459 }, 460 } 461 462 for _, tc := range emptyTestCases { 463 t.Run(tc.wkt, func(t *testing.T) { 464 g, err := ParseGeography(tc.wkt) 465 require.NoError(t, err) 466 467 _, err = g.AsS2(EmptyBehaviorError) 468 require.Error(t, err) 469 require.True(t, IsEmptyGeometryError(err)) 470 471 shapes, err := g.AsS2(EmptyBehaviorOmit) 472 require.NoError(t, err) 473 require.Equal(t, tc.expectedOmit, shapes) 474 }) 475 } 476 } 477 478 func TestClipEWKBByRect(t *testing.T) { 479 defer leaktest.AfterTest(t)() 480 481 var g *Geometry 482 var err error 483 datadriven.RunTest(t, "testdata/clip", func(t *testing.T, d *datadriven.TestData) string { 484 switch d.Cmd { 485 case "geometry": 486 g, err = ParseGeometry(d.Input) 487 if err != nil { 488 return err.Error() 489 } 490 return "" 491 case "clip": 492 var xMin, yMin, xMax, yMax int 493 d.ScanArgs(t, "xmin", &xMin) 494 d.ScanArgs(t, "ymin", &yMin) 495 d.ScanArgs(t, "xmax", &xMax) 496 d.ScanArgs(t, "ymax", &yMax) 497 ewkb, err := geos.ClipEWKBByRect( 498 g.EWKB(), float64(xMin), float64(yMin), float64(xMax), float64(yMax)) 499 if err != nil { 500 return err.Error() 501 } 502 // TODO(sumeer): 503 // - add WKB to WKT and print exact output 504 // - expand test with more inputs 505 return fmt.Sprintf( 506 "%d => %d (srid: %d)", 507 len(g.EWKB()), 508 len(ewkb), 509 g.SRID(), 510 ) 511 default: 512 return fmt.Sprintf("unknown command: %s", d.Cmd) 513 } 514 }) 515 }