Skip to content

Commit bc9a478

Browse files
nes1983dsymonds
authored andcommitted
s2: Add RegionUnion.
Signed-off-by: David Symonds <dsymonds@golang.org>
1 parent 014fac4 commit bc9a478

File tree

3 files changed

+178
-1
lines changed

3 files changed

+178
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ Approximately ~40% complete.
129129
* PointCompression
130130
* Region
131131
* RegionCoverer
132+
* RegionUnion
132133
* s2edge_clipping
133134
* s2edge_crosser
134135
* s2edge_crossings
@@ -193,7 +194,6 @@ started.
193194
* PolygonMeasures
194195
* RegionIntersection
195196
* RegionTermIndexer
196-
* RegionUnion
197197
* ShapeIndexRegion - Allows ShapeIndexes to be used as Regions for things like
198198

199199
### Encode/Decode

s2/regionunion.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright 2020 Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package s2
16+
17+
// A RegionUnion represents a union of possibly overlapping regions.
18+
// It is convenient for computing a covering of a set of regions.
19+
type RegionUnion []Region
20+
21+
// CapBound returns a bounding cap for this RegionUnion.
22+
func (ru RegionUnion) CapBound() Cap { return ru.RectBound().CapBound() }
23+
24+
// RectBound returns a bounding latitude-longitude rectangle for this RegionUnion.
25+
func (ru RegionUnion) RectBound() Rect {
26+
ret := EmptyRect()
27+
for _, reg := range ru {
28+
ret = ret.Union(reg.RectBound())
29+
}
30+
return ret
31+
}
32+
33+
// ContainsCell reports whether the given Cell is contained by this RegionUnion.
34+
func (ru RegionUnion) ContainsCell(c Cell) bool {
35+
for _, reg := range ru {
36+
if reg.ContainsCell(c) {
37+
return true
38+
}
39+
}
40+
return false
41+
}
42+
43+
// IntersectsCell reports whether this RegionUnion intersects the given cell.
44+
func (ru RegionUnion) IntersectsCell(c Cell) bool {
45+
for _, reg := range ru {
46+
if reg.IntersectsCell(c) {
47+
return true
48+
}
49+
}
50+
return false
51+
}
52+
53+
// ContainsPoint reports whether this RegionUnion contains the Point.
54+
func (ru RegionUnion) ContainsPoint(p Point) bool {
55+
for _, reg := range ru {
56+
if reg.ContainsPoint(p) {
57+
return true
58+
}
59+
}
60+
return false
61+
}
62+
63+
// CellUnionBound computes a covering of the RegionUnion.
64+
func (ru RegionUnion) CellUnionBound() []CellID {
65+
return ru.CapBound().CellUnionBound()
66+
}

s2/regionunion_test.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright 2020 Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package s2
16+
17+
import (
18+
"testing"
19+
)
20+
21+
func TestEmptyRegionUnionHasEmptyCap(t *testing.T) {
22+
var empty RegionUnion
23+
24+
got := empty.CapBound()
25+
26+
want := EmptyCap()
27+
if !got.ApproxEqual(want) {
28+
t.Errorf("empty region union cap = %v, want %v", got, want)
29+
}
30+
}
31+
32+
func TestEmptyRegionUnionHasEmptyBound(t *testing.T) {
33+
var empty RegionUnion
34+
35+
got := empty.RectBound()
36+
37+
want := EmptyRect()
38+
if !got.ApproxEqual(want) {
39+
t.Errorf("empty region union cap = %v, want %v", got, want)
40+
}
41+
}
42+
43+
func TestRegionUnionOfTwoPointsHasCorrectBound(t *testing.T) {
44+
got := twoPointsRegionUnion.RectBound()
45+
46+
want := makeRect("-35:-40,35:40")
47+
if !got.ApproxEqual(want) {
48+
t.Errorf("%v.RectBound() = %v, want %v", twoPointsRegionUnion, got, want)
49+
}
50+
}
51+
52+
var twoPointsRegionUnion = RegionUnion{
53+
PointFromLatLng(LatLngFromDegrees(35, 40)),
54+
PointFromLatLng(LatLngFromDegrees(-35, -40)),
55+
}
56+
57+
func TestRegionUnionOfTwoPointsIntersectsFace0(t *testing.T) {
58+
got := twoPointsRegionUnion.IntersectsCell(face0Cell)
59+
60+
if !got {
61+
t.Errorf("%v.IntersectsCell(%v) = %v, want true", twoPointsRegionUnion, face0Cell, got)
62+
}
63+
}
64+
65+
func TestRegionUnionOfTwoPointsDoesNotContainFace0(t *testing.T) {
66+
got := twoPointsRegionUnion.ContainsCell(face0Cell)
67+
68+
if got {
69+
t.Errorf("%v.ContainsCell(%v) = %v, want false", twoPointsRegionUnion, face0Cell, got)
70+
}
71+
}
72+
73+
var face0Cell = CellFromCellID(CellIDFromFace(0))
74+
75+
func TestRegionUnionOfTwoContainsPoint(t *testing.T) {
76+
testCases := []struct {
77+
ll LatLng
78+
want bool
79+
}{
80+
{LatLngFromDegrees(35, 40), true},
81+
{LatLngFromDegrees(-35, -40), true},
82+
{LatLngFromDegrees(0, 0), false},
83+
}
84+
85+
for _, tc := range testCases {
86+
got := twoPointsRegionUnion.ContainsPoint(PointFromLatLng(tc.ll))
87+
88+
if got != tc.want {
89+
t.Errorf("%v.ContainsPoint(%v) = %t, want %t", twoPointsRegionUnion, tc.ll, got, tc.want)
90+
}
91+
}
92+
}
93+
94+
func TestTwoPointsRegionCovering(t *testing.T) {
95+
cov := NewRegionCoverer()
96+
cov.MaxCells = 1
97+
98+
got := cov.Covering(twoPointsRegionUnion)
99+
100+
const wantLen = 1
101+
if l := len(got); l != wantLen {
102+
t.Fatalf("covering = %v, len %v, want %v", got, l, wantLen)
103+
}
104+
want := CellIDFromFace(0)
105+
if g := got[0]; g != want {
106+
t.Errorf("covering[0] = %v, want %v", g, want)
107+
}
108+
}
109+
110+
// Make sure RegionUnion implements Region.
111+
var _ Region = RegionUnion{}

0 commit comments

Comments
 (0)