From 469deb5c4f1cfbd378ab6eec2089d6e1bdff2ecc Mon Sep 17 00:00:00 2001 From: Jesse Rosenstock Date: Fri, 7 Nov 2025 09:34:12 +0100 Subject: [PATCH] rect.CapBound: Fix for tall and wide rects For tall (>90 degrees) and wide (>180 degrees) lat/lng rects, rect.CapBound returned an incorrect cap that didn't contain the full rect. The function intends to return a polar cap in this case, but due to an apparent typo (2 * Pi instead of Pi), returned a cap centered at the center of the rect instead. This fix has recently been applied to C++ and Java in google3, but has not been exported to those github repos yet. --- s2/rect.go | 2 +- s2/rect_test.go | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/s2/rect.go b/s2/rect.go index 0fb99a10..165f37ea 100644 --- a/s2/rect.go +++ b/s2/rect.go @@ -247,7 +247,7 @@ func (r Rect) CapBound() Cap { // maximum cap size is achieved at one of the rectangle vertices. For // rectangles that are larger than 180 degrees, we punt and always return a // bounding cap centered at one of the two poles. - if math.Remainder(r.Lng.Hi-r.Lng.Lo, 2*math.Pi) >= 0 && r.Lng.Hi-r.Lng.Lo < 2*math.Pi { + if math.Remainder(r.Lng.Hi-r.Lng.Lo, 2*math.Pi) >= 0 && r.Lng.Hi-r.Lng.Lo <= math.Pi { midCap := CapFromPoint(PointFromLatLng(r.Center())).AddPoint(PointFromLatLng(r.Lo())).AddPoint(PointFromLatLng(r.Hi())) if midCap.Height() < poleCap.Height() { return midCap diff --git a/s2/rect_test.go b/s2/rect_test.go index 66c105cf..272b4441 100644 --- a/s2/rect_test.go +++ b/s2/rect_test.go @@ -395,6 +395,16 @@ func TestRectCapBound(t *testing.T) { rectFromDegrees(-30, -150, -10, 50), CapFromCenterAngle(Point{r3.Vector{X: 0, Y: 0, Z: -1}}, s1.Angle(80)*s1.Degree), }, + { + // Longitude span > 180 degrees and latitude span > 90 degrees. This results + // in a polar cap that is larger than the "midpoint cap" (centered at the + // center of the rect and containing the vertices), but is nonetheless the + // correct result. The "midpoint cap" must not be returned since it doesn't + // contain the entire rect due the rect being wider than 180 degrees. + // In this example, (-34, 49) is in the rect but not the midpoint cap. + rectFromDegrees(-60, -150, 70, 50), + CapFromCenterAngle(Point{r3.Vector{X: 0, Y: 0, Z: 1}}, s1.Angle(150)*s1.Degree), + }, } for _, test := range tests { if got := test.r.CapBound(); !test.want.ApproxEqual(got) {