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  }