github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geogfn/covers_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 geogfn 12 13 import ( 14 "testing" 15 16 "github.com/cockroachdb/cockroach/pkg/geo" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestCovers(t *testing.T) { 21 testCases := []struct { 22 desc string 23 a string 24 b string 25 expected bool 26 }{ 27 { 28 "POINT covers the same POINT", 29 "POINT(1.0 1.0)", 30 "POINT(1.0 1.0)", 31 true, 32 }, 33 { 34 "POINT does not cover different POINT", 35 "POINT(1.0 1.0)", 36 "POINT(1.0 1.1)", 37 false, 38 }, 39 { 40 "POINT does not cover different LINESTRING", 41 "POINT(1.0 1.0)", 42 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 43 false, 44 }, 45 { 46 "POINT does not cover different POLYGON", 47 "POINT(1.0 1.0)", 48 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 49 false, 50 }, 51 { 52 "LINESTRING covers POINT at the start", 53 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 54 "POINT(1.0 1.0)", 55 true, 56 }, 57 { 58 "LINESTRING covers POINT at the middle", 59 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 60 "POINT(2.0 2.0)", 61 true, 62 }, 63 { 64 "LINESTRING covers POINT at the end", 65 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 66 "POINT(3.0 3.0)", 67 true, 68 }, 69 { 70 "LINESTRING covers POINT in between", 71 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 72 "POINT(1.5 1.5001714)", 73 true, 74 }, 75 { 76 "LINESTRING does not cover any POINT out of the way", 77 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 78 "POINT(2.0 4.0)", 79 false, 80 }, 81 { 82 "LINESTRING does not cover longer LINESTRING", 83 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 84 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0, 4.0 4.0)", 85 false, 86 }, 87 { 88 "LINESTRING does not cover different LINESTRING", 89 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 90 "LINESTRING(5.0 5.0, 4.0 4.0)", 91 false, 92 }, 93 { 94 "LINESTRING covers itself", 95 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 96 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 97 true, 98 }, 99 { 100 "LINESTRING covers itself in reverse", 101 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0, 4.0 4.0)", 102 "LINESTRING(4.0 4.0, 3.0 3.0, 2.0 2.0, 1.0 1.0)", 103 true, 104 }, 105 { 106 "LINESTRING covers subline of itself", 107 "LINESTRING(0.0 0.0, 1.0 1.0, 2.0 2.0, 3.0 3.0)", 108 "LINESTRING(1.5 1.5001714, 2.0 2.0)", 109 true, 110 }, 111 { 112 "LINESTRING does not cover subline of with a tail in b", 113 "LINESTRING(1.0 1.0, 2.0 2.0)", 114 "LINESTRING(1.5 1.5001714, 2.0 2.0, 2.5 2.5)", 115 false, 116 }, 117 { 118 "LINESTRING covers subline of itself but veers off", 119 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 120 "LINESTRING(1.5 1.5001714, 2.0 2.0, 2.5 2.5)", 121 false, 122 }, 123 { 124 "LINESTRING covers LINESTRING with subline missing a vertex in A", 125 "LINESTRING(0.999 0.999, 1.0 1.0, 1.01 1.01)", 126 "LINESTRING(0.9995 0.9995, 1.005 1.005)", 127 true, 128 }, 129 { 130 "LINESTRING does not cover LINESTRING with subline covering some point in A but B extends past A", 131 "LINESTRING(0.9 0.9, 0.999 0.999, 1.0 1.0)", 132 "LINESTRING(0.999 0.999, 0.9995 0.9995, 1.005 1.005)", 133 false, 134 }, 135 { 136 "LINESTRING covers subline of itself from the beginning", 137 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 138 "LINESTRING(1.0 1.0, 1.5 1.5001714, 2.0 2.0, 2.5 2.5002856, 3.0 3.0)", 139 true, 140 }, 141 { 142 "LINESTRING covers subline of itself from the beginning (swapped a and b)", 143 "LINESTRING(1.0 1.0, 1.5 1.5001714, 2.0 2.0, 2.5 2.5002856, 3.0 3.0)", 144 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 145 true, 146 }, 147 { 148 "LINESTRING covers subline of itself from the beginning (reversed)", 149 "LINESTRING(1.0 1.0, 1.5 1.5001714, 2.0 2.0, 2.5 2.5002856, 3.0 3.0)", 150 "LINESTRING(3.0 3.0, 2.0 2.0, 1.0 1.0)", 151 true, 152 }, 153 { 154 "LINESTRING covers a substring of itself", 155 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 156 "LINESTRING(1.0 1.0, 2.0 2.0)", 157 true, 158 }, 159 { 160 "LINESTRING covers a substring of itself", 161 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0)", 162 "LINESTRING(2.0 2.0, 3.0 3.0)", 163 true, 164 }, 165 { 166 "LINESTRING covers a substring of itself", 167 "LINESTRING(1.0 1.0, 2.0 2.0, 3.0 3.0, 4.0 4.0)", 168 "LINESTRING(2.0 2.0, 3.0 3.0)", 169 true, 170 }, 171 { 172 "LINESTRING does not cover any POLYGON", 173 "POINT(1.0 1.0)", 174 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 175 false, 176 }, 177 { 178 "POLYGON covers POINT inside", 179 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 180 "POINT(0.5 0.5)", 181 true, 182 }, 183 { 184 "POLYGON does not cover POINT outside", 185 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 186 "POINT(0.5 5.5)", 187 false, 188 }, 189 { 190 "POLYGON does not cover POINT in hole", 191 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2))", 192 "POINT(0.3 0.3)", 193 false, 194 }, 195 { 196 "POLYGON covers POINT on vertex", 197 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 198 "POINT(1.0 0.0)", 199 true, 200 }, 201 { 202 "POLYGON covers POINT on edge of vertex", 203 "POLYGON((1.0 1.0, 2.0 2.0, 0.0 2.0, 1.0 1.0))", 204 "POINT(1.5 1.5001714)", 205 true, 206 }, 207 { 208 "POLYGON covers LINESTRING inside the POLYGON", 209 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 210 "LINESTRING(0.1 0.1, 0.4 0.4)", 211 true, 212 }, 213 { 214 "POLYGON does not cover LINESTRING in hole", 215 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2))", 216 "LINESTRING(0.3 0.3, 0.35 0.35)", 217 false, 218 }, 219 { 220 "POLYGON does not cover LINESTRING intersecting with hole", 221 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2))", 222 "LINESTRING(0.3 0.3, 0.55 0.55)", 223 false, 224 }, 225 { 226 "POLYGON does not cover LINESTRING intersecting with hole and outside", 227 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0), (0.2 0.2, 0.2 0.4, 0.4 0.4, 0.4 0.2, 0.2 0.2))", 228 "LINESTRING(0.3 0.3, 4.0 4.0)", 229 false, 230 }, 231 { 232 "POLYGON does not cover LINESTRING outside the POLYGON", 233 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 234 "LINESTRING(-1 -1, -2 -2)", 235 false, 236 }, 237 { 238 "POLYGON does not cover LINESTRING intersecting the POLYGON", 239 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 240 "LINESTRING(-0.5 -0.5, 0.5 0.5)", 241 false, 242 }, 243 { 244 "POLYGON covers itself", 245 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 246 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 247 true, 248 }, 249 { 250 "POLYGON covers a window of itself, intersecting with the edges", 251 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 252 "POLYGON((0.2 0.2, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.2 0.2))", 253 true, 254 }, 255 { 256 "POLYGON covers a nested version of itself", 257 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 258 "POLYGON((0.1 0.1, 0.2 0.1, 0.2 0.2, 0.1 0.2, 0.1 0.1))", 259 true, 260 }, 261 { 262 "POLYGON does not cover POLYGON intersecting", 263 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 264 "POLYGON((-1.0 0.0, 1.0 0.0, 1.0 1.0, -1.0 1.0, -1.0 0.0))", 265 false, 266 }, 267 { 268 "POLYGON does not cover POLYGON totally out of range", 269 "POLYGON((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0))", 270 "POLYGON((3.0 3.0, 4.0 3.0, 4.0 4.0, 3.0 4.0, 3.0 3.0))", 271 false, 272 }, 273 { 274 "MULTIPOINT covers single MULTIPOINT", 275 "MULTIPOINT((1.0 1.0), (2.0 2.0))", 276 "MULTIPOINT((2.0 2.0))", 277 true, 278 }, 279 { 280 "MULTIPOINT not covered by multiple MULTI POINTs", 281 "MULTIPOINT((1.0 1.0))", 282 "MULTIPOINT((1.0 1.0), (2.0 2.0))", 283 false, 284 }, 285 { 286 "MULTILINESTRING covers MULTIPOINTs", 287 "MULTILINESTRING((1.0 1.0, 1.1 1.1), (2.0 2.0, 2.1 2.1))", 288 "MULTIPOINT(2.0 2.0, 2.1 2.1)", 289 true, 290 }, 291 { 292 "MULTILINESTRING does not cover all MULTIPOINTs", 293 "MULTILINESTRING((1.0 1.0, 1.1 1.1), (2.0 2.0, 2.1 2.1))", 294 "MULTIPOINT(2.0 2.0, 1.0 1.0, 3.0 3.0)", 295 false, 296 }, 297 { 298 "MULTILINESTRING covers all MULTILINESTRING", 299 "MULTILINESTRING((1.0 1.0, 2.0 2.0), (2.0 2.0, 2.1 2.1), (3.0 3.0, 3.1 3.1))", 300 "MULTILINESTRING((1.0 1.0, 1.5 1.5001714), (1.5 1.5001714, 2.0 2.0))", 301 true, 302 }, 303 { 304 "MULTILINESTRING does not cover all MULTILINESTRING", 305 "MULTILINESTRING((1.0 1.0, 1.1 1.1), (2.0 2.0, 2.1 2.1), (3.0 3.0, 3.1 3.1))", 306 "MULTILINESTRING((2.0 2.0, 2.1 2.1), (4.0 3.0, 3.1 3.1))", 307 false, 308 }, 309 { 310 "MULTIPOLYGON covers MULTIPOINT", 311 "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)), ((15 5, 40 10, 10 20, 5 10, 15 5)))", 312 "MULTIPOINT((30 20), (30 30))", 313 true, 314 }, 315 { 316 "MULTIPOLYGON does not cover MULTIPOINT", 317 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 318 "MULTIPOINT((30 20), (30 30), (45 66))", 319 false, 320 }, 321 { 322 "MULTIPOLYGON does not cover MULTIPOINT", 323 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 324 "MULTIPOINT((30 20), (30 30), (45 66))", 325 false, 326 }, 327 { 328 "MULTIPOLYGON does not cover MULTILINESTRING intersecting at the edge (known edge case *should* return true)", 329 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 330 "MULTILINESTRING((45 40, 10 40), (45 40, 10 40, 30 20))", 331 false, 332 }, 333 { 334 "MULTIPOLYGON does not cover MULTILINESTRING", 335 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 336 "MULTILINESTRING((45 40, 10 40), (45 40, 10 40, 30 11))", 337 false, 338 }, 339 { 340 "MULTIPOLYGON covers MULTIPOLYGON", 341 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 342 "MULTIPOLYGON (((30 20, 45 40, 10 40, 30 20)))", 343 true, 344 }, 345 { 346 "MULTIPOLYGON does not cover MULTIPOLYGON", 347 "MULTIPOLYGON (((15 5, 40 10, 10 20, 5 10, 15 5)),((30 20, 45 40, 10 40, 30 20)))", 348 "MULTIPOLYGON (((30 20, 45 40, 15 45, 30 20)))", 349 false, 350 }, 351 { 352 "multiple MULTIPOLYGONs required to cover MULTIPOINTS", 353 "MULTIPOLYGON(((0.0 0.0, 1.0 0.0, 1.0 1.0, 0.0 1.0, 0.0 0.0)), ((1.0 0.0, 2.0 0.0, 2.0 1.0, 1.0 1.0, 1.0 0.0)))", 354 "MULTIPOINT((0.5 0.5), (1.5 0.5))'", 355 true, 356 }, 357 { 358 "EMPTY GEOMETRYCOLLECTION does not cover itself", 359 "GEOMETRYCOLLECTION EMPTY", 360 "GEOMETRYCOLLECTION EMPTY", 361 false, 362 }, 363 { 364 "nothing covers an empty GEOMETRYCOLLECTION", 365 "POINT(1.0 1.0)", 366 "GEOMETRYCOLLECTION EMPTY", 367 false, 368 }, 369 { 370 "nothing covers a GEOMETRYCOLLECTION with an EMPTY element", 371 "POINT(1.0 1.0)", 372 "GEOMETRYCOLLECTION EMPTY", 373 false, 374 }, 375 { 376 "empty collection contains point which covers another", 377 "GEOMETRYCOLLECTION(LINESTRING EMPTY, POINT(1.0 2.0))", 378 "POINT(1.0 2.0)", 379 true, 380 }, 381 } 382 383 for _, tc := range testCases { 384 t.Run(tc.desc, func(t *testing.T) { 385 a, err := geo.ParseGeography(tc.a) 386 require.NoError(t, err) 387 b, err := geo.ParseGeography(tc.b) 388 require.NoError(t, err) 389 390 covers, err := Covers(a, b) 391 require.NoError(t, err) 392 require.Equal(t, tc.expected, covers) 393 394 coveredBy, err := CoveredBy(b, a) 395 require.NoError(t, err) 396 require.Equal(t, tc.expected, coveredBy) 397 }) 398 } 399 400 t.Run("errors if SRIDs mismatch", func(t *testing.T) { 401 _, err := Covers(mismatchingSRIDGeographyA, mismatchingSRIDGeographyB) 402 requireMismatchingSRIDError(t, err) 403 _, err = CoveredBy(mismatchingSRIDGeographyA, mismatchingSRIDGeographyB) 404 requireMismatchingSRIDError(t, err) 405 }) 406 }