github.com/dgraph-io/dgraph@v1.2.8/types/s2index_test.go (about)

     1  /*
     2   * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package types
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"fmt"
    23  	"io"
    24  	"os"
    25  	"testing"
    26  
    27  	"github.com/golang/geo/s2"
    28  	"github.com/stretchr/testify/require"
    29  	"github.com/twpayne/go-geom"
    30  	"github.com/twpayne/go-geom/encoding/geojson"
    31  	"github.com/twpayne/go-geom/encoding/wkb"
    32  )
    33  
    34  func loadPolygon(name string) (geom.T, error) {
    35  	f, err := os.Open(name)
    36  	if err != nil {
    37  		return nil, err
    38  	}
    39  	defer f.Close()
    40  	var b bytes.Buffer
    41  	_, err = io.Copy(&b, f)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	var gf geojson.Feature
    47  	if err := gf.UnmarshalJSON(b.Bytes()); err != nil {
    48  		return nil, err
    49  	}
    50  	return gf.Geometry, nil
    51  }
    52  
    53  func TestIndexCellsPoint(t *testing.T) {
    54  	p := geom.NewPoint(geom.XY).MustSetCoords(geom.Coord{-122.082506, 37.4249518})
    55  	parents, cover, err := indexCells(p)
    56  	require.NoError(t, err)
    57  	require.Len(t, parents, MaxCellLevel-MinCellLevel+1)
    58  	c := parents[0]
    59  	if c.Level() != MinCellLevel {
    60  		t.Errorf("Expected cell level %d. Got %d instead.", MinCellLevel, c.Level())
    61  	}
    62  	if c.ToToken() != "808c" {
    63  		t.Errorf("Unexpected cell token %s.", c.ToToken())
    64  	}
    65  	c = parents[len(parents)-1]
    66  	if c.Level() != MaxCellLevel {
    67  		t.Errorf("Expected cell level %d. Got %d instead.", MaxCellLevel, c.Level())
    68  	}
    69  	if c.ToToken() != "808fb9f81" {
    70  		t.Errorf("Unexpected cell token %s.", c.ToToken())
    71  	}
    72  	// check that all cell levels are different
    73  	pc := parents[0]
    74  	for _, c := range parents[1:] {
    75  		if c.Level() <= pc.Level() {
    76  			t.Errorf("Expected cell to have level greater than %d. Got %d", pc.Level(), c.Level())
    77  		}
    78  		pc = c
    79  	}
    80  
    81  	// Check that cover only has one item
    82  	require.Len(t, cover, 1)
    83  	c = cover[0]
    84  	require.Equal(t, c.Level(), MaxCellLevel)
    85  	require.Equal(t, c.ToToken(), "808fb9f81")
    86  }
    87  
    88  func printCells(cu s2.CellUnion) {
    89  	for _, c := range cu {
    90  		cell := s2.CellFromCellID(c)
    91  		area := EarthArea(cell.ExactArea())
    92  		r := cell.RectBound()
    93  		top := r.Vertex(0).Distance(r.Vertex(1))
    94  		side := r.Vertex(1).Distance(r.Vertex(2))
    95  		fmt.Printf("Level: %d, Cell: %s, area: %s, boundary: %s x %s\n", c.Level(), c.ToToken(),
    96  			area, EarthDistance(top), EarthDistance(side))
    97  	}
    98  }
    99  
   100  func TestIndexCellsPolygon(t *testing.T) {
   101  	p, err := loadPolygon("testdata/zip.json")
   102  	require.NoError(t, err)
   103  	parents, cover, err := indexCells(p)
   104  	require.NoError(t, err)
   105  	if len(cover) > MaxCells {
   106  		t.Errorf("Expected less than %d cells. Got %d instead.", MaxCells, len(cover))
   107  	}
   108  	for _, c := range cover {
   109  		if c.Level() > MaxCellLevel || c.Level() < MinCellLevel {
   110  			t.Errorf("Invalid cell level %d.", c.Level())
   111  		}
   112  		require.Contains(t, parents, c)
   113  	}
   114  	require.True(t, len(parents) > len(cover))
   115  }
   116  
   117  func TestIndexCellsPolygonError(t *testing.T) {
   118  	poly := geom.NewPolygon(geom.XY).MustSetCoords([][]geom.Coord{
   119  		{{-122, 37}, {-123, 37}, {-123, 38}, {-122, 38}, {-122, 38}}})
   120  	_, _, err := indexCells(poly)
   121  	require.Error(t, err)
   122  	require.Contains(t, err.Error(), "Last coordinate not same as first")
   123  }
   124  
   125  func TestKeyGeneratorPoint(t *testing.T) {
   126  	p := geom.NewPoint(geom.XY).MustSetCoords(geom.Coord{-122.082506, 37.4249518})
   127  	data, err := wkb.Marshal(p, binary.LittleEndian)
   128  	require.NoError(t, err)
   129  
   130  	src := ValueForType(BinaryID)
   131  	src.Value = data
   132  	gc, err := Convert(src, GeoID)
   133  	require.NoError(t, err)
   134  	g := gc.Value.(geom.T)
   135  
   136  	keys, err := IndexGeoTokens(g)
   137  	require.NoError(t, err)
   138  	require.Len(t, keys, MaxCellLevel-MinCellLevel+1+1) // +1 for the cover
   139  }
   140  
   141  func TestKeyGeneratorPolygon(t *testing.T) {
   142  	p, err := loadPolygon("testdata/zip.json")
   143  	require.NoError(t, err)
   144  	data, err := wkb.Marshal(p, binary.LittleEndian)
   145  	require.NoError(t, err)
   146  
   147  	src := ValueForType(BinaryID)
   148  	src.Value = data
   149  	gc, err := Convert(src, GeoID)
   150  	require.NoError(t, err)
   151  	g := gc.Value.(geom.T)
   152  
   153  	keys, err := IndexGeoTokens(g)
   154  	require.NoError(t, err)
   155  	require.Len(t, keys, 67)
   156  }
   157  
   158  func testCover(file string, max int) {
   159  	fmt.Printf("Testing %s with max %d\n", file, max)
   160  	p, err := loadPolygon(file)
   161  	if err != nil {
   162  		return
   163  	}
   164  	l, _ := loopFromPolygon(p.(*geom.Polygon))
   165  	cu := coverLoop(l, MinCellLevel, MaxCellLevel, max)
   166  	printCells(cu)
   167  	printCoverAccuracy(l, cu)
   168  }
   169  
   170  func printCoverAccuracy(l *s2.Loop, cu s2.CellUnion) {
   171  	a1 := cellUnionArea(cu)
   172  	a2 := l.Area()
   173  	fmt.Printf("Loop area: %v. Cell area %v. Ratio %.3f\n", EarthArea(a2), EarthArea(a1), a1/a2)
   174  }
   175  
   176  func cellUnionArea(cu s2.CellUnion) float64 {
   177  	var area float64
   178  	for _, c := range cu {
   179  		cell := s2.CellFromCellID(c)
   180  		area += cell.ExactArea()
   181  	}
   182  	return area
   183  }
   184  
   185  func BenchmarkToLoopZip(b *testing.B) {
   186  	benchToLoop(b, "testdata/zip.json")
   187  }
   188  
   189  func BenchmarkToLoopAruba(b *testing.B) {
   190  	benchToLoop(b, "testdata/aruba.json")
   191  }
   192  
   193  func BenchmarkCoverZip_10(b *testing.B) {
   194  	benchCover(b, "testdata/zip.json", 10)
   195  }
   196  
   197  func BenchmarkCoverZip_15(b *testing.B) {
   198  	benchCover(b, "testdata/zip.json", 15)
   199  }
   200  
   201  func BenchmarkCoverZip_18(b *testing.B) {
   202  	benchCover(b, "testdata/zip.json", 18)
   203  }
   204  
   205  func BenchmarkCoverZip_30(b *testing.B) {
   206  	benchCover(b, "testdata/zip.json", 30)
   207  }
   208  
   209  func BenchmarkCoverAruba_10(b *testing.B) {
   210  	benchCover(b, "testdata/aruba.json", 10)
   211  }
   212  
   213  func BenchmarkCoverAruba_15(b *testing.B) {
   214  	benchCover(b, "testdata/aruba.json", 15)
   215  }
   216  
   217  func BenchmarkCoverAruba_18(b *testing.B) {
   218  	benchCover(b, "testdata/aruba.json", 18)
   219  }
   220  
   221  func BenchmarkCoverAruba_30(b *testing.B) {
   222  	benchCover(b, "testdata/aruba.json", 30)
   223  }
   224  
   225  func BenchmarkKeyGeneratorPoint(b *testing.B) {
   226  	p := geom.NewPoint(geom.XY).MustSetCoords(geom.Coord{-122.082506, 37.4249518})
   227  	data, err := wkb.Marshal(p, binary.LittleEndian)
   228  	if err != nil {
   229  		b.Error(err)
   230  	}
   231  
   232  	src := ValueForType(BinaryID)
   233  	src.Value = data
   234  	gc, err := Convert(src, GeoID)
   235  	require.NoError(b, err)
   236  	g := gc.Value.(geom.T)
   237  
   238  	b.ResetTimer()
   239  	for n := 0; n < b.N; n++ {
   240  		IndexGeoTokens(g)
   241  	}
   242  }
   243  
   244  func BenchmarkKeyGeneratorPolygon(b *testing.B) {
   245  	p, err := loadPolygon("testdata/zip.json")
   246  	if err != nil {
   247  		b.Error(err)
   248  	}
   249  	data, err := wkb.Marshal(p, binary.LittleEndian)
   250  	if err != nil {
   251  		b.Error(err)
   252  	}
   253  
   254  	src := ValueForType(GeoID)
   255  	src.Value = data
   256  	gc, err := Convert(src, GeoID)
   257  	require.NoError(b, err)
   258  	g := gc.Value.(geom.T)
   259  
   260  	b.ResetTimer()
   261  	for n := 0; n < b.N; n++ {
   262  		IndexGeoTokens(g)
   263  	}
   264  }
   265  
   266  func benchCover(b *testing.B, file string, max int) {
   267  	p, err := loadPolygon(file)
   268  	if err != nil {
   269  		b.Error(err)
   270  	}
   271  	l, _ := loopFromPolygon(p.(*geom.Polygon))
   272  	var cu s2.CellUnion
   273  	b.ResetTimer()
   274  	for n := 0; n < b.N; n++ {
   275  		cu = coverLoop(l, MinCellLevel, MaxCellLevel, max)
   276  	}
   277  	printCoverAccuracy(l, cu)
   278  }
   279  
   280  func benchToLoop(b *testing.B, file string) {
   281  	p, err := loadPolygon(file)
   282  	if err != nil {
   283  		b.Error(err)
   284  	}
   285  	b.ResetTimer()
   286  	for n := 0; n < b.N; n++ {
   287  		_, _ = loopFromPolygon(p.(*geom.Polygon))
   288  	}
   289  }