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  }