summaryrefslogtreecommitdiff
path: root/vendor/github.com/golang/geo/s2/cellunion.go
diff options
context:
space:
mode:
authorLibravatar kim <89579420+NyaaaWhatsUpDoc@users.noreply.github.com>2024-08-02 11:46:41 +0000
committerLibravatar GitHub <noreply@github.com>2024-08-02 12:46:41 +0100
commit94e87610c4ce9bbb1c614a61bab29c1422fed11b (patch)
tree2e06b8ce64212140e796f6077ba841b6cc678501 /vendor/github.com/golang/geo/s2/cellunion.go
parent[feature] Allow import of following and blocks via CSV (#3150) (diff)
downloadgotosocial-94e87610c4ce9bbb1c614a61bab29c1422fed11b.tar.xz
[chore] add back exif-terminator and use only for jpeg,png,webp (#3161)
* add back exif-terminator and use only for jpeg,png,webp * fix arguments passed to terminateExif() * pull in latest exif-terminator * fix test * update processed img --------- Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Diffstat (limited to 'vendor/github.com/golang/geo/s2/cellunion.go')
-rw-r--r--vendor/github.com/golang/geo/s2/cellunion.go590
1 files changed, 590 insertions, 0 deletions
diff --git a/vendor/github.com/golang/geo/s2/cellunion.go b/vendor/github.com/golang/geo/s2/cellunion.go
new file mode 100644
index 000000000..0654de973
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cellunion.go
@@ -0,0 +1,590 @@
+// Copyright 2014 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package s2
+
+import (
+ "fmt"
+ "io"
+ "sort"
+
+ "github.com/golang/geo/s1"
+)
+
+// A CellUnion is a collection of CellIDs.
+//
+// It is normalized if it is sorted, and does not contain redundancy.
+// Specifically, it may not contain the same CellID twice, nor a CellID that
+// is contained by another, nor the four sibling CellIDs that are children of
+// a single higher level CellID.
+//
+// CellUnions are not required to be normalized, but certain operations will
+// return different results if they are not (e.g. Contains).
+type CellUnion []CellID
+
+// CellUnionFromRange creates a CellUnion that covers the half-open range
+// of leaf cells [begin, end). If begin == end the resulting union is empty.
+// This requires that begin and end are both leaves, and begin <= end.
+// To create a closed-ended range, pass in end.Next().
+func CellUnionFromRange(begin, end CellID) CellUnion {
+ // We repeatedly add the largest cell we can.
+ var cu CellUnion
+ for id := begin.MaxTile(end); id != end; id = id.Next().MaxTile(end) {
+ cu = append(cu, id)
+ }
+ // The output is normalized because the cells are added in order by the iteration.
+ return cu
+}
+
+// CellUnionFromUnion creates a CellUnion from the union of the given CellUnions.
+func CellUnionFromUnion(cellUnions ...CellUnion) CellUnion {
+ var cu CellUnion
+ for _, cellUnion := range cellUnions {
+ cu = append(cu, cellUnion...)
+ }
+ cu.Normalize()
+ return cu
+}
+
+// CellUnionFromIntersection creates a CellUnion from the intersection of the given CellUnions.
+func CellUnionFromIntersection(x, y CellUnion) CellUnion {
+ var cu CellUnion
+
+ // This is a fairly efficient calculation that uses binary search to skip
+ // over sections of both input vectors. It takes constant time if all the
+ // cells of x come before or after all the cells of y in CellID order.
+ var i, j int
+ for i < len(x) && j < len(y) {
+ iMin := x[i].RangeMin()
+ jMin := y[j].RangeMin()
+ if iMin > jMin {
+ // Either j.Contains(i) or the two cells are disjoint.
+ if x[i] <= y[j].RangeMax() {
+ cu = append(cu, x[i])
+ i++
+ } else {
+ // Advance j to the first cell possibly contained by x[i].
+ j = y.lowerBound(j+1, len(y), iMin)
+ // The previous cell y[j-1] may now contain x[i].
+ if x[i] <= y[j-1].RangeMax() {
+ j--
+ }
+ }
+ } else if jMin > iMin {
+ // Identical to the code above with i and j reversed.
+ if y[j] <= x[i].RangeMax() {
+ cu = append(cu, y[j])
+ j++
+ } else {
+ i = x.lowerBound(i+1, len(x), jMin)
+ if y[j] <= x[i-1].RangeMax() {
+ i--
+ }
+ }
+ } else {
+ // i and j have the same RangeMin(), so one contains the other.
+ if x[i] < y[j] {
+ cu = append(cu, x[i])
+ i++
+ } else {
+ cu = append(cu, y[j])
+ j++
+ }
+ }
+ }
+
+ // The output is generated in sorted order.
+ cu.Normalize()
+ return cu
+}
+
+// CellUnionFromIntersectionWithCellID creates a CellUnion from the intersection
+// of a CellUnion with the given CellID. This can be useful for splitting a
+// CellUnion into chunks.
+func CellUnionFromIntersectionWithCellID(x CellUnion, id CellID) CellUnion {
+ var cu CellUnion
+ if x.ContainsCellID(id) {
+ cu = append(cu, id)
+ cu.Normalize()
+ return cu
+ }
+
+ idmax := id.RangeMax()
+ for i := x.lowerBound(0, len(x), id.RangeMin()); i < len(x) && x[i] <= idmax; i++ {
+ cu = append(cu, x[i])
+ }
+
+ cu.Normalize()
+ return cu
+}
+
+// CellUnionFromDifference creates a CellUnion from the difference (x - y)
+// of the given CellUnions.
+func CellUnionFromDifference(x, y CellUnion) CellUnion {
+ // TODO(roberts): This is approximately O(N*log(N)), but could probably
+ // use similar techniques as CellUnionFromIntersectionWithCellID to be more efficient.
+
+ var cu CellUnion
+ for _, xid := range x {
+ cu.cellUnionDifferenceInternal(xid, &y)
+ }
+
+ // The output is generated in sorted order, and there should not be any
+ // cells that can be merged (provided that both inputs were normalized).
+ return cu
+}
+
+// The C++ constructor methods FromNormalized and FromVerbatim are not necessary
+// since they don't call Normalize, and just set the CellIDs directly on the object,
+// so straight casting is sufficient in Go to replicate this behavior.
+
+// IsValid reports whether the cell union is valid, meaning that the CellIDs are
+// valid, non-overlapping, and sorted in increasing order.
+func (cu *CellUnion) IsValid() bool {
+ for i, cid := range *cu {
+ if !cid.IsValid() {
+ return false
+ }
+ if i == 0 {
+ continue
+ }
+ if (*cu)[i-1].RangeMax() >= cid.RangeMin() {
+ return false
+ }
+ }
+ return true
+}
+
+// IsNormalized reports whether the cell union is normalized, meaning that it is
+// satisfies IsValid and that no four cells have a common parent.
+// Certain operations such as Contains will return a different
+// result if the cell union is not normalized.
+func (cu *CellUnion) IsNormalized() bool {
+ for i, cid := range *cu {
+ if !cid.IsValid() {
+ return false
+ }
+ if i == 0 {
+ continue
+ }
+ if (*cu)[i-1].RangeMax() >= cid.RangeMin() {
+ return false
+ }
+ if i < 3 {
+ continue
+ }
+ if areSiblings((*cu)[i-3], (*cu)[i-2], (*cu)[i-1], cid) {
+ return false
+ }
+ }
+ return true
+}
+
+// Normalize normalizes the CellUnion.
+func (cu *CellUnion) Normalize() {
+ sortCellIDs(*cu)
+
+ output := make([]CellID, 0, len(*cu)) // the list of accepted cells
+ // Loop invariant: output is a sorted list of cells with no redundancy.
+ for _, ci := range *cu {
+ // The first two passes here either ignore this new candidate,
+ // or remove previously accepted cells that are covered by this candidate.
+
+ // Ignore this cell if it is contained by the previous one.
+ // We only need to check the last accepted cell. The ordering of the
+ // cells implies containment (but not the converse), and output has no redundancy,
+ // so if this candidate is not contained by the last accepted cell
+ // then it cannot be contained by any previously accepted cell.
+ if len(output) > 0 && output[len(output)-1].Contains(ci) {
+ continue
+ }
+
+ // Discard any previously accepted cells contained by this one.
+ // This could be any contiguous trailing subsequence, but it can't be
+ // a discontiguous subsequence because of the containment property of
+ // sorted S2 cells mentioned above.
+ j := len(output) - 1 // last index to keep
+ for j >= 0 {
+ if !ci.Contains(output[j]) {
+ break
+ }
+ j--
+ }
+ output = output[:j+1]
+
+ // See if the last three cells plus this one can be collapsed.
+ // We loop because collapsing three accepted cells and adding a higher level cell
+ // could cascade into previously accepted cells.
+ for len(output) >= 3 && areSiblings(output[len(output)-3], output[len(output)-2], output[len(output)-1], ci) {
+ // Replace four children by their parent cell.
+ output = output[:len(output)-3]
+ ci = ci.immediateParent() // checked !ci.isFace above
+ }
+ output = append(output, ci)
+ }
+ *cu = output
+}
+
+// IntersectsCellID reports whether this CellUnion intersects the given cell ID.
+func (cu *CellUnion) IntersectsCellID(id CellID) bool {
+ // Find index of array item that occurs directly after our probe cell:
+ i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
+
+ if i != len(*cu) && (*cu)[i].RangeMin() <= id.RangeMax() {
+ return true
+ }
+ return i != 0 && (*cu)[i-1].RangeMax() >= id.RangeMin()
+}
+
+// ContainsCellID reports whether the CellUnion contains the given cell ID.
+// Containment is defined with respect to regions, e.g. a cell contains its 4 children.
+//
+// CAVEAT: If you have constructed a non-normalized CellUnion, note that groups
+// of 4 child cells are *not* considered to contain their parent cell. To get
+// this behavior you must use one of the call Normalize() explicitly.
+func (cu *CellUnion) ContainsCellID(id CellID) bool {
+ // Find index of array item that occurs directly after our probe cell:
+ i := sort.Search(len(*cu), func(i int) bool { return id < (*cu)[i] })
+
+ if i != len(*cu) && (*cu)[i].RangeMin() <= id {
+ return true
+ }
+ return i != 0 && (*cu)[i-1].RangeMax() >= id
+}
+
+// Denormalize replaces this CellUnion with an expanded version of the
+// CellUnion where any cell whose level is less than minLevel or where
+// (level - minLevel) is not a multiple of levelMod is replaced by its
+// children, until either both of these conditions are satisfied or the
+// maximum level is reached.
+func (cu *CellUnion) Denormalize(minLevel, levelMod int) {
+ var denorm CellUnion
+ for _, id := range *cu {
+ level := id.Level()
+ newLevel := level
+ if newLevel < minLevel {
+ newLevel = minLevel
+ }
+ if levelMod > 1 {
+ newLevel += (maxLevel - (newLevel - minLevel)) % levelMod
+ if newLevel > maxLevel {
+ newLevel = maxLevel
+ }
+ }
+ if newLevel == level {
+ denorm = append(denorm, id)
+ } else {
+ end := id.ChildEndAtLevel(newLevel)
+ for ci := id.ChildBeginAtLevel(newLevel); ci != end; ci = ci.Next() {
+ denorm = append(denorm, ci)
+ }
+ }
+ }
+ *cu = denorm
+}
+
+// RectBound returns a Rect that bounds this entity.
+func (cu *CellUnion) RectBound() Rect {
+ bound := EmptyRect()
+ for _, c := range *cu {
+ bound = bound.Union(CellFromCellID(c).RectBound())
+ }
+ return bound
+}
+
+// CapBound returns a Cap that bounds this entity.
+func (cu *CellUnion) CapBound() Cap {
+ if len(*cu) == 0 {
+ return EmptyCap()
+ }
+
+ // Compute the approximate centroid of the region. This won't produce the
+ // bounding cap of minimal area, but it should be close enough.
+ var centroid Point
+
+ for _, ci := range *cu {
+ area := AvgAreaMetric.Value(ci.Level())
+ centroid = Point{centroid.Add(ci.Point().Mul(area))}
+ }
+
+ if zero := (Point{}); centroid == zero {
+ centroid = PointFromCoords(1, 0, 0)
+ } else {
+ centroid = Point{centroid.Normalize()}
+ }
+
+ // Use the centroid as the cap axis, and expand the cap angle so that it
+ // contains the bounding caps of all the individual cells. Note that it is
+ // *not* sufficient to just bound all the cell vertices because the bounding
+ // cap may be concave (i.e. cover more than one hemisphere).
+ c := CapFromPoint(centroid)
+ for _, ci := range *cu {
+ c = c.AddCap(CellFromCellID(ci).CapBound())
+ }
+
+ return c
+}
+
+// ContainsCell reports whether this cell union contains the given cell.
+func (cu *CellUnion) ContainsCell(c Cell) bool {
+ return cu.ContainsCellID(c.id)
+}
+
+// IntersectsCell reports whether this cell union intersects the given cell.
+func (cu *CellUnion) IntersectsCell(c Cell) bool {
+ return cu.IntersectsCellID(c.id)
+}
+
+// ContainsPoint reports whether this cell union contains the given point.
+func (cu *CellUnion) ContainsPoint(p Point) bool {
+ return cu.ContainsCell(CellFromPoint(p))
+}
+
+// CellUnionBound computes a covering of the CellUnion.
+func (cu *CellUnion) CellUnionBound() []CellID {
+ return cu.CapBound().CellUnionBound()
+}
+
+// LeafCellsCovered reports the number of leaf cells covered by this cell union.
+// This will be no more than 6*2^60 for the whole sphere.
+func (cu *CellUnion) LeafCellsCovered() int64 {
+ var numLeaves int64
+ for _, c := range *cu {
+ numLeaves += 1 << uint64((maxLevel-int64(c.Level()))<<1)
+ }
+ return numLeaves
+}
+
+// Returns true if the given four cells have a common parent.
+// This requires that the four CellIDs are distinct.
+func areSiblings(a, b, c, d CellID) bool {
+ // A necessary (but not sufficient) condition is that the XOR of the
+ // four cell IDs must be zero. This is also very fast to test.
+ if (a ^ b ^ c) != d {
+ return false
+ }
+
+ // Now we do a slightly more expensive but exact test. First, compute a
+ // mask that blocks out the two bits that encode the child position of
+ // "id" with respect to its parent, then check that the other three
+ // children all agree with "mask".
+ mask := d.lsb() << 1
+ mask = ^(mask + (mask << 1))
+ idMasked := (uint64(d) & mask)
+ return ((uint64(a)&mask) == idMasked &&
+ (uint64(b)&mask) == idMasked &&
+ (uint64(c)&mask) == idMasked &&
+ !d.isFace())
+}
+
+// Contains reports whether this CellUnion contains all of the CellIDs of the given CellUnion.
+func (cu *CellUnion) Contains(o CellUnion) bool {
+ // TODO(roberts): Investigate alternatives such as divide-and-conquer
+ // or alternating-skip-search that may be significantly faster in both
+ // the average and worst case. This applies to Intersects as well.
+ for _, id := range o {
+ if !cu.ContainsCellID(id) {
+ return false
+ }
+ }
+
+ return true
+}
+
+// Intersects reports whether this CellUnion intersects any of the CellIDs of the given CellUnion.
+func (cu *CellUnion) Intersects(o CellUnion) bool {
+ for _, c := range *cu {
+ if o.IntersectsCellID(c) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// lowerBound returns the index in this CellUnion to the first element whose value
+// is not considered to go before the given cell id. (i.e., either it is equivalent
+// or comes after the given id.) If there is no match, then end is returned.
+func (cu *CellUnion) lowerBound(begin, end int, id CellID) int {
+ for i := begin; i < end; i++ {
+ if (*cu)[i] >= id {
+ return i
+ }
+ }
+
+ return end
+}
+
+// cellUnionDifferenceInternal adds the difference between the CellID and the union to
+// the result CellUnion. If they intersect but the difference is non-empty, it divides
+// and conquers.
+func (cu *CellUnion) cellUnionDifferenceInternal(id CellID, other *CellUnion) {
+ if !other.IntersectsCellID(id) {
+ (*cu) = append((*cu), id)
+ return
+ }
+
+ if !other.ContainsCellID(id) {
+ for _, child := range id.Children() {
+ cu.cellUnionDifferenceInternal(child, other)
+ }
+ }
+}
+
+// ExpandAtLevel expands this CellUnion by adding a rim of cells at expandLevel
+// around the unions boundary.
+//
+// For each cell c in the union, we add all cells at level
+// expandLevel that abut c. There are typically eight of those
+// (four edge-abutting and four sharing a vertex). However, if c is
+// finer than expandLevel, we add all cells abutting
+// c.Parent(expandLevel) as well as c.Parent(expandLevel) itself,
+// as an expandLevel cell rarely abuts a smaller cell.
+//
+// Note that the size of the output is exponential in
+// expandLevel. For example, if expandLevel == 20 and the input
+// has a cell at level 10, there will be on the order of 4000
+// adjacent cells in the output. For most applications the
+// ExpandByRadius method below is easier to use.
+func (cu *CellUnion) ExpandAtLevel(level int) {
+ var output CellUnion
+ levelLsb := lsbForLevel(level)
+ for i := len(*cu) - 1; i >= 0; i-- {
+ id := (*cu)[i]
+ if id.lsb() < levelLsb {
+ id = id.Parent(level)
+ // Optimization: skip over any cells contained by this one. This is
+ // especially important when very small regions are being expanded.
+ for i > 0 && id.Contains((*cu)[i-1]) {
+ i--
+ }
+ }
+ output = append(output, id)
+ output = append(output, id.AllNeighbors(level)...)
+ }
+ sortCellIDs(output)
+
+ *cu = output
+ cu.Normalize()
+}
+
+// ExpandByRadius expands this CellUnion such that it contains all points whose
+// distance to the CellUnion is at most minRadius, but do not use cells that
+// are more than maxLevelDiff levels higher than the largest cell in the input.
+// The second parameter controls the tradeoff between accuracy and output size
+// when a large region is being expanded by a small amount (e.g. expanding Canada
+// by 1km). For example, if maxLevelDiff == 4 the region will always be expanded
+// by approximately 1/16 the width of its largest cell. Note that in the worst case,
+// the number of cells in the output can be up to 4 * (1 + 2 ** maxLevelDiff) times
+// larger than the number of cells in the input.
+func (cu *CellUnion) ExpandByRadius(minRadius s1.Angle, maxLevelDiff int) {
+ minLevel := maxLevel
+ for _, cid := range *cu {
+ minLevel = minInt(minLevel, cid.Level())
+ }
+
+ // Find the maximum level such that all cells are at least "minRadius" wide.
+ radiusLevel := MinWidthMetric.MaxLevel(minRadius.Radians())
+ if radiusLevel == 0 && minRadius.Radians() > MinWidthMetric.Value(0) {
+ // The requested expansion is greater than the width of a face cell.
+ // The easiest way to handle this is to expand twice.
+ cu.ExpandAtLevel(0)
+ }
+ cu.ExpandAtLevel(minInt(minLevel+maxLevelDiff, radiusLevel))
+}
+
+// Equal reports whether the two CellUnions are equal.
+func (cu CellUnion) Equal(o CellUnion) bool {
+ if len(cu) != len(o) {
+ return false
+ }
+ for i := 0; i < len(cu); i++ {
+ if cu[i] != o[i] {
+ return false
+ }
+ }
+ return true
+}
+
+// AverageArea returns the average area of this CellUnion.
+// This is accurate to within a factor of 1.7.
+func (cu *CellUnion) AverageArea() float64 {
+ return AvgAreaMetric.Value(maxLevel) * float64(cu.LeafCellsCovered())
+}
+
+// ApproxArea returns the approximate area of this CellUnion. This method is accurate
+// to within 3% percent for all cell sizes and accurate to within 0.1% for cells
+// at level 5 or higher within the union.
+func (cu *CellUnion) ApproxArea() float64 {
+ var area float64
+ for _, id := range *cu {
+ area += CellFromCellID(id).ApproxArea()
+ }
+ return area
+}
+
+// ExactArea returns the area of this CellUnion as accurately as possible.
+func (cu *CellUnion) ExactArea() float64 {
+ var area float64
+ for _, id := range *cu {
+ area += CellFromCellID(id).ExactArea()
+ }
+ return area
+}
+
+// Encode encodes the CellUnion.
+func (cu *CellUnion) Encode(w io.Writer) error {
+ e := &encoder{w: w}
+ cu.encode(e)
+ return e.err
+}
+
+func (cu *CellUnion) encode(e *encoder) {
+ e.writeInt8(encodingVersion)
+ e.writeInt64(int64(len(*cu)))
+ for _, ci := range *cu {
+ ci.encode(e)
+ }
+}
+
+// Decode decodes the CellUnion.
+func (cu *CellUnion) Decode(r io.Reader) error {
+ d := &decoder{r: asByteReader(r)}
+ cu.decode(d)
+ return d.err
+}
+
+func (cu *CellUnion) decode(d *decoder) {
+ version := d.readInt8()
+ if d.err != nil {
+ return
+ }
+ if version != encodingVersion {
+ d.err = fmt.Errorf("only version %d is supported", encodingVersion)
+ return
+ }
+ n := d.readInt64()
+ if d.err != nil {
+ return
+ }
+ const maxCells = 1000000
+ if n > maxCells {
+ d.err = fmt.Errorf("too many cells (%d; max is %d)", n, maxCells)
+ return
+ }
+ *cu = make([]CellID, n)
+ for i := range *cu {
+ (*cu)[i].decode(d)
+ }
+}