github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/geo/geogfn/dwithin_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  	"fmt"
    15  	"testing"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/geo"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func TestDWithin(t *testing.T) {
    22  	// These are cases where the distance is VERY CLOSE to zero.
    23  	closeToZeroCases := map[string]struct{}{
    24  		"LINESTRING to POINT on the line": {},
    25  	}
    26  
    27  	for _, tc := range distanceTestCases {
    28  		t.Run(tc.desc, func(t *testing.T) {
    29  			a, err := geo.ParseGeography(tc.a)
    30  			require.NoError(t, err)
    31  			b, err := geo.ParseGeography(tc.b)
    32  			require.NoError(t, err)
    33  
    34  			for _, subTC := range []struct {
    35  				desc                string
    36  				expected            float64
    37  				useSphereOrSpheroid UseSphereOrSpheroid
    38  			}{
    39  				{"sphere", tc.expectedSphereDistance, UseSphere},
    40  				{"spheroid", tc.expectedSpheroidDistance, UseSpheroid},
    41  			} {
    42  				t.Run(subTC.desc, func(t *testing.T) {
    43  					if subTC.expected == 0 {
    44  						zeroValue := float64(0)
    45  						// Allow a 1cm margin of error for close to zero cases.
    46  						if _, ok := closeToZeroCases[tc.desc]; ok {
    47  							zeroValue = 0.01
    48  						}
    49  						for _, val := range []float64{zeroValue, 1, 10, 10000} {
    50  							t.Run(fmt.Sprintf("dwithin:%f", val), func(t *testing.T) {
    51  								dwithin, err := DWithin(a, b, val, subTC.useSphereOrSpheroid)
    52  								require.NoError(t, err)
    53  								require.True(t, dwithin)
    54  
    55  								dwithin, err = DWithin(b, a, val, subTC.useSphereOrSpheroid)
    56  								require.NoError(t, err)
    57  								require.True(t, dwithin)
    58  							})
    59  						}
    60  					} else {
    61  						for _, val := range []float64{
    62  							subTC.expected + 0.01, // allow 1cm margin of error
    63  							subTC.expected + 0.02,
    64  							subTC.expected + 1,
    65  							subTC.expected * 2,
    66  						} {
    67  							t.Run(fmt.Sprintf("dwithin:%f", val), func(t *testing.T) {
    68  								dwithin, err := DWithin(a, b, val, subTC.useSphereOrSpheroid)
    69  								require.NoError(t, err)
    70  								require.True(t, dwithin)
    71  
    72  								dwithin, err = DWithin(b, a, val, subTC.useSphereOrSpheroid)
    73  								require.NoError(t, err)
    74  								require.True(t, dwithin)
    75  							})
    76  						}
    77  
    78  						for _, val := range []float64{
    79  							subTC.expected - 0.01, // allow 1cm margin of error
    80  							subTC.expected - 0.02,
    81  							subTC.expected - 1,
    82  							subTC.expected / 2,
    83  						} {
    84  							t.Run(fmt.Sprintf("dwithin:%f", val), func(t *testing.T) {
    85  								dwithin, err := DWithin(a, b, val, subTC.useSphereOrSpheroid)
    86  								require.NoError(t, err)
    87  								require.False(t, dwithin)
    88  
    89  								dwithin, err = DWithin(b, a, val, subTC.useSphereOrSpheroid)
    90  								require.NoError(t, err)
    91  								require.False(t, dwithin)
    92  							})
    93  						}
    94  					}
    95  				})
    96  			}
    97  		})
    98  	}
    99  
   100  	t.Run("errors if SRIDs mismatch", func(t *testing.T) {
   101  		_, err := DWithin(mismatchingSRIDGeographyA, mismatchingSRIDGeographyB, 0, UseSpheroid)
   102  		requireMismatchingSRIDError(t, err)
   103  	})
   104  
   105  	t.Run("errors if distance < 0", func(t *testing.T) {
   106  		_, err := DWithin(geo.MustParseGeography("POINT(1.0 2.0)"), geo.MustParseGeography("POINT(3.0 4.0)"), -0.01, UseSpheroid)
   107  		require.Error(t, err)
   108  	})
   109  
   110  	t.Run("empty geographies are never dwithin each other", func(t *testing.T) {
   111  		for _, tc := range []struct {
   112  			a string
   113  			b string
   114  		}{
   115  			{"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION EMPTY"},
   116  			{"GEOMETRYCOLLECTION EMPTY", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"},
   117  			{"POINT(1.0 1.0)", "GEOMETRYCOLLECTION (POINT(1.0 1.0), LINESTRING EMPTY)"}, // This case errors (in a bad way) in PostGIS.
   118  		} {
   119  			for _, useSphereOrSpheroid := range []UseSphereOrSpheroid{
   120  				UseSphere,
   121  				UseSpheroid,
   122  			} {
   123  				t.Run(fmt.Sprintf("DWithin(%s,%s),spheroid=%t", tc.a, tc.b, useSphereOrSpheroid), func(t *testing.T) {
   124  					a, err := geo.ParseGeography(tc.a)
   125  					require.NoError(t, err)
   126  					b, err := geo.ParseGeography(tc.b)
   127  					require.NoError(t, err)
   128  					dwithin, err := DWithin(a, b, 0, useSphereOrSpheroid)
   129  					require.NoError(t, err)
   130  					require.False(t, dwithin)
   131  				})
   132  			}
   133  		}
   134  	})
   135  }