github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/parse_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 "testing" 15 16 "github.com/cockroachdb/cockroach/pkg/geo/geopb" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestParseWKB(t *testing.T) { 21 testCases := []struct { 22 desc string 23 b []byte 24 defaultSRID geopb.SRID 25 expected geopb.SpatialObject 26 expectedError string 27 }{ 28 { 29 "EWKB should make this error", 30 []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 31 4326, 32 geopb.SpatialObject{}, 33 "wkb: unknown type: 536870913", 34 }, 35 { 36 "Normal WKB should take the SRID", 37 []byte("\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 38 4326, 39 geopb.SpatialObject{ 40 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 41 SRID: 4326, 42 Shape: geopb.Shape_Point, 43 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 44 }, 45 "", 46 }, 47 } 48 49 for _, tc := range testCases { 50 t.Run(tc.desc, func(t *testing.T) { 51 ret, err := parseWKB(tc.b, tc.defaultSRID) 52 if tc.expectedError != "" { 53 require.Error(t, err) 54 require.EqualError(t, err, tc.expectedError) 55 } else { 56 require.NoError(t, err) 57 require.Equal(t, tc.expected, ret) 58 } 59 }) 60 } 61 } 62 63 func TestParseEWKB(t *testing.T) { 64 testCases := []struct { 65 desc string 66 b []byte 67 defaultSRID geopb.SRID 68 overwrite defaultSRIDOverwriteSetting 69 expected geopb.SpatialObject 70 }{ 71 { 72 "SRID 4326 is hint; EWKB has 4004", 73 []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 74 4326, 75 DefaultSRIDIsHint, 76 geopb.SpatialObject{ 77 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 78 SRID: 4004, 79 Shape: geopb.Shape_Point, 80 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 81 }, 82 }, 83 { 84 "Overwrite SRID 4004 with 4326", 85 []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 86 4326, 87 DefaultSRIDShouldOverwrite, 88 geopb.SpatialObject{ 89 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 90 SRID: 4326, 91 Shape: geopb.Shape_Point, 92 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 93 }, 94 }, 95 } 96 97 for _, tc := range testCases { 98 t.Run(tc.desc, func(t *testing.T) { 99 ret, err := parseEWKB(tc.b, tc.defaultSRID, tc.overwrite) 100 require.NoError(t, err) 101 require.Equal(t, tc.expected, ret) 102 }) 103 } 104 } 105 106 func TestParseEWKT(t *testing.T) { 107 testCases := []struct { 108 desc string 109 wkt geopb.EWKT 110 defaultSRID geopb.SRID 111 overwrite defaultSRIDOverwriteSetting 112 expected geopb.SpatialObject 113 }{ 114 { 115 "SRID 4326 is hint; EWKT has 4004", 116 "SRID=4004;POINT(1.0 1.0)", 117 4326, 118 DefaultSRIDIsHint, 119 geopb.SpatialObject{ 120 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 121 SRID: 4004, 122 Shape: geopb.Shape_Point, 123 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 124 }, 125 }, 126 { 127 "Overwrite SRID 4004 with 4326", 128 "SRID=4004;POINT(1.0 1.0)", 129 4326, 130 DefaultSRIDShouldOverwrite, 131 geopb.SpatialObject{ 132 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 133 SRID: 4326, 134 Shape: geopb.Shape_Point, 135 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 136 }, 137 }, 138 } 139 140 for _, tc := range testCases { 141 t.Run(tc.desc, func(t *testing.T) { 142 ret, err := parseEWKT(tc.wkt, tc.defaultSRID, tc.overwrite) 143 require.NoError(t, err) 144 require.Equal(t, tc.expected, ret) 145 }) 146 } 147 } 148 149 func TestParseGeometry(t *testing.T) { 150 testCases := []struct { 151 str string 152 expected *Geometry 153 expectedErr string 154 }{ 155 { 156 "0101000000000000000000F03F000000000000F03F", 157 &Geometry{ 158 SpatialObject: geopb.SpatialObject{ 159 EWKB: []byte("\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 160 SRID: 0, 161 Shape: geopb.Shape_Point, 162 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 163 }, 164 }, 165 "", 166 }, 167 { 168 "0101000020E6100000000000000000F03F000000000000F03F", 169 &Geometry{ 170 SpatialObject: geopb.SpatialObject{ 171 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 172 SRID: 4326, 173 Shape: geopb.Shape_Point, 174 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 175 }, 176 }, 177 "", 178 }, 179 { 180 "POINT(1.0 1.0)", 181 &Geometry{ 182 SpatialObject: geopb.SpatialObject{ 183 EWKB: []byte("\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 184 SRID: 0, 185 Shape: geopb.Shape_Point, 186 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 187 }, 188 }, 189 "", 190 }, 191 { 192 "SRID=4004;POINT(1.0 1.0)", 193 &Geometry{ 194 SpatialObject: geopb.SpatialObject{ 195 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 196 SRID: 4004, 197 Shape: geopb.Shape_Point, 198 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 199 }, 200 }, 201 "", 202 }, 203 { 204 "SRid=4004;POINT(1.0 1.0)", 205 &Geometry{ 206 SpatialObject: geopb.SpatialObject{ 207 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 208 SRID: 4004, 209 Shape: geopb.Shape_Point, 210 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 211 }, 212 }, 213 "", 214 }, 215 { 216 "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f", 217 &Geometry{ 218 SpatialObject: geopb.SpatialObject{ 219 EWKB: []byte("\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 220 SRID: 0, 221 Shape: geopb.Shape_Point, 222 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 223 }, 224 }, 225 "", 226 }, 227 { 228 `{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [1.0, 1.0] }, "properties": { "name": "┳━┳ ヽ(ಠل͜ಠ)ノ" } }`, 229 &Geometry{ 230 SpatialObject: geopb.SpatialObject{ 231 EWKB: []byte("\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 232 SRID: 0, 233 Shape: geopb.Shape_Point, 234 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 235 }, 236 }, 237 "", 238 }, 239 { 240 "invalid", 241 nil, 242 "geos error: ParseException: Unknown type: 'INVALID'", 243 }, 244 { 245 "", 246 nil, 247 "geo: parsing empty string to geo type", 248 }, 249 } 250 251 for _, tc := range testCases { 252 t.Run(tc.str, func(t *testing.T) { 253 g, err := ParseGeometry(tc.str) 254 if len(tc.expectedErr) > 0 { 255 require.Equal(t, tc.expectedErr, err.Error()) 256 } else { 257 require.NoError(t, err) 258 require.Equal(t, tc.expected, g) 259 require.Equal(t, tc.expected.SRID(), g.SpatialObject.SRID) 260 } 261 }) 262 } 263 } 264 265 func TestParseGeography(t *testing.T) { 266 testCases := []struct { 267 str string 268 expected *Geography 269 expectedErr string 270 }{ 271 { 272 // Even forcing an SRID to 0 using EWKB will make it 4326. 273 "0101000000000000000000F03F000000000000F03F", 274 &Geography{ 275 SpatialObject: geopb.SpatialObject{ 276 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 277 SRID: 4326, 278 Shape: geopb.Shape_Point, 279 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 280 }, 281 }, 282 "", 283 }, 284 { 285 "0101000020E6100000000000000000F03F000000000000F03F", 286 &Geography{ 287 SpatialObject: geopb.SpatialObject{ 288 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 289 SRID: 4326, 290 Shape: geopb.Shape_Point, 291 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 292 }, 293 }, 294 "", 295 }, 296 { 297 "0101000020A40F0000000000000000F03F000000000000F03F", 298 &Geography{ 299 SpatialObject: geopb.SpatialObject{ 300 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 301 SRID: 4004, 302 Shape: geopb.Shape_Point, 303 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 304 }, 305 }, 306 "", 307 }, 308 { 309 "POINT(1.0 1.0)", 310 &Geography{ 311 SpatialObject: geopb.SpatialObject{ 312 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 313 SRID: 4326, 314 Shape: geopb.Shape_Point, 315 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 316 }, 317 }, 318 "", 319 }, 320 { 321 // Even forcing an SRID to 0 using WKT will make it 4326. 322 "SRID=0;POINT(1.0 1.0)", 323 &Geography{ 324 SpatialObject: geopb.SpatialObject{ 325 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 326 SRID: 4326, 327 Shape: geopb.Shape_Point, 328 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 329 }, 330 }, 331 "", 332 }, 333 { 334 "SRID=4004;POINT(1.0 1.0)", 335 &Geography{ 336 SpatialObject: geopb.SpatialObject{ 337 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 338 SRID: 4004, 339 Shape: geopb.Shape_Point, 340 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 341 }, 342 }, 343 "", 344 }, 345 { 346 "\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f", 347 &Geography{ 348 SpatialObject: geopb.SpatialObject{ 349 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 350 SRID: 4326, 351 Shape: geopb.Shape_Point, 352 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 353 }, 354 }, 355 "", 356 }, 357 { 358 "\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f", 359 &Geography{ 360 SpatialObject: geopb.SpatialObject{ 361 EWKB: []byte("\x01\x01\x00\x00\x20\xA4\x0F\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 362 SRID: 4004, 363 Shape: geopb.Shape_Point, 364 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 365 }, 366 }, 367 "", 368 }, 369 { 370 `{ "type": "Feature", "geometry": { "type": "Point", "coordinates": [1.0, 1.0] }, "properties": { "name": "┳━┳ ヽ(ಠل͜ಠ)ノ" } }`, 371 &Geography{ 372 SpatialObject: geopb.SpatialObject{ 373 EWKB: []byte("\x01\x01\x00\x00\x20\xe6\x10\x00\x00\x00\x00\x00\x00\x00\x00\xf0\x3f\x00\x00\x00\x00\x00\x00\xf0\x3f"), 374 SRID: 4326, 375 Shape: geopb.Shape_Point, 376 BoundingBox: &geopb.BoundingBox{MinX: 1, MaxX: 1, MinY: 1, MaxY: 1}, 377 }, 378 }, 379 "", 380 }, 381 { 382 "invalid", 383 nil, 384 "geos error: ParseException: Unknown type: 'INVALID'", 385 }, 386 { 387 "", 388 nil, 389 "geo: parsing empty string to geo type", 390 }, 391 } 392 393 for _, tc := range testCases { 394 t.Run(tc.str, func(t *testing.T) { 395 g, err := ParseGeography(tc.str) 396 if len(tc.expectedErr) > 0 { 397 require.Equal(t, tc.expectedErr, err.Error()) 398 } else { 399 require.NoError(t, err) 400 require.Equal(t, tc.expected, g) 401 require.Equal(t, tc.expected.SRID(), g.SpatialObject.SRID) 402 } 403 }) 404 } 405 }