summaryrefslogtreecommitdiff
path: root/vendor/github.com/golang/geo/s2/cell_index.go
diff options
context:
space:
mode:
authorLibravatar Tobi Smethurst <31960611+tsmethurst@users.noreply.github.com>2021-08-12 21:03:24 +0200
committerLibravatar GitHub <noreply@github.com>2021-08-12 21:03:24 +0200
commit98263a7de64269898a2f81207e38943b5c8e8653 (patch)
tree743c90f109a6c5d27832d1dcef2388d939f0f77a /vendor/github.com/golang/geo/s2/cell_index.go
parentText duplication fix (#137) (diff)
downloadgotosocial-98263a7de64269898a2f81207e38943b5c8e8653.tar.xz
Grand test fixup (#138)
* start fixing up tests * fix up tests + automate with drone * fiddle with linting * messing about with drone.yml * some more fiddling * hmmm * add cache * add vendor directory * verbose * ci updates * update some little things * update sig
Diffstat (limited to 'vendor/github.com/golang/geo/s2/cell_index.go')
-rw-r--r--vendor/github.com/golang/geo/s2/cell_index.go498
1 files changed, 498 insertions, 0 deletions
diff --git a/vendor/github.com/golang/geo/s2/cell_index.go b/vendor/github.com/golang/geo/s2/cell_index.go
new file mode 100644
index 000000000..ef16d0895
--- /dev/null
+++ b/vendor/github.com/golang/geo/s2/cell_index.go
@@ -0,0 +1,498 @@
+// Copyright 2020 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 (
+ "sort"
+)
+
+const (
+ // A special label indicating that the ContentsIterator done is true.
+ cellIndexDoneContents = -1
+)
+
+// cellIndexNode represents a node in the CellIndex. Cells are organized in a
+// tree such that the ancestors of a given node contain that node.
+type cellIndexNode struct {
+ cellID CellID
+ label int32
+ parent int32
+}
+
+// newCellIndexNode returns a node with the appropriate default values.
+func newCellIndexNode() cellIndexNode {
+ return cellIndexNode{
+ cellID: 0,
+ label: cellIndexDoneContents,
+ parent: -1,
+ }
+}
+
+// A rangeNode represents a range of leaf CellIDs. The range starts at
+// startID (a leaf cell) and ends at the startID field of the next
+// rangeNode. contents points to the node of the CellIndex cellTree
+// representing the cells that overlap this range.
+type rangeNode struct {
+ startID CellID // First leaf cell contained by this range.
+ contents int32 // Contents of this node (an index within the cell tree).
+}
+
+// CellIndexIterator is an iterator that visits the entire set of indexed
+// (CellID, label) pairs in an unspecified order.
+type CellIndexIterator struct {
+ // TODO(roberts): Implement
+}
+
+// NewCellIndexIterator creates an iterator for the given CellIndex.
+func NewCellIndexIterator(index *CellIndex) *CellIndexIterator {
+ return &CellIndexIterator{}
+}
+
+// CellIndexRangeIterator is an iterator that seeks and iterates over a set of
+// non-overlapping leaf cell ranges that cover the entire sphere. The indexed
+// (CellID, label) pairs that intersect the current leaf cell range can be
+// visited using CellIndexContentsIterator (see below).
+type CellIndexRangeIterator struct {
+ rangeNodes []rangeNode
+ pos int
+ nonEmpty bool
+}
+
+// NewCellIndexRangeIterator creates an iterator for the given CellIndex.
+// The iterator is initially *unpositioned*; you must call a positioning method
+// such as Begin() or Seek() before accessing its contents.
+func NewCellIndexRangeIterator(index *CellIndex) *CellIndexRangeIterator {
+ return &CellIndexRangeIterator{
+ rangeNodes: index.rangeNodes,
+ }
+}
+
+// NewCellIndexNonEmptyRangeIterator creates an iterator for the given CellIndex.
+// The iterator is initially *unpositioned*; you must call a positioning method such as
+// Begin() or Seek() before accessing its contents.
+func NewCellIndexNonEmptyRangeIterator(index *CellIndex) *CellIndexRangeIterator {
+ return &CellIndexRangeIterator{
+ rangeNodes: index.rangeNodes,
+ nonEmpty: true,
+ }
+}
+
+// StartID reports the CellID of the start of the current range of leaf CellIDs.
+//
+// If done is true, this returns the last possible CellID. This property means
+// that most loops do not need to test done explicitly.
+func (c *CellIndexRangeIterator) StartID() CellID {
+ return c.rangeNodes[c.pos].startID
+}
+
+// LimitID reports the non-inclusive end of the current range of leaf CellIDs.
+//
+// This assumes the iterator is not done.
+func (c *CellIndexRangeIterator) LimitID() CellID {
+ return c.rangeNodes[c.pos+1].startID
+}
+
+// IsEmpty reports if no (CellID, label) pairs intersect this range.
+// Also returns true if done() is true.
+func (c *CellIndexRangeIterator) IsEmpty() bool {
+ return c.rangeNodes[c.pos].contents == cellIndexDoneContents
+}
+
+// Begin positions the iterator at the first range of leaf cells (if any).
+func (c *CellIndexRangeIterator) Begin() {
+ c.pos = 0
+ for c.nonEmpty && c.IsEmpty() && !c.Done() {
+ c.pos++
+ }
+}
+
+// Prev positions the iterator at the previous entry and reports whether it was not
+// already positioned at the beginning.
+func (c *CellIndexRangeIterator) Prev() bool {
+ if c.nonEmpty {
+ return c.nonEmptyPrev()
+ }
+ return c.prev()
+}
+
+// prev is used to position the iterator at the previous entry without checking
+// if nonEmpty is true to prevent unwanted recursion.
+func (c *CellIndexRangeIterator) prev() bool {
+ if c.pos == 0 {
+ return false
+ }
+
+ c.pos--
+ return true
+}
+
+// Prev positions the iterator at the previous entry, and reports whether it was
+// already positioned at the beginning.
+func (c *CellIndexRangeIterator) nonEmptyPrev() bool {
+ for c.prev() {
+ if !c.IsEmpty() {
+ return true
+ }
+ }
+
+ // Return the iterator to its original position.
+ if c.IsEmpty() && !c.Done() {
+ c.Next()
+ }
+ return false
+}
+
+// Next advances the iterator to the next range of leaf cells.
+//
+// This assumes the iterator is not done.
+func (c *CellIndexRangeIterator) Next() {
+ c.pos++
+ for c.nonEmpty && c.IsEmpty() && !c.Done() {
+ c.pos++
+ }
+}
+
+// Advance reports if advancing would leave it positioned on a valid range. If
+// the value would not be valid, the positioning is not changed.
+func (c *CellIndexRangeIterator) Advance(n int) bool {
+ // Note that the last element of rangeNodes is a sentinel value.
+ if n >= len(c.rangeNodes)-1-c.pos {
+ return false
+ }
+ c.pos += n
+ return true
+}
+
+// Finish positions the iterator so that done is true.
+func (c *CellIndexRangeIterator) Finish() {
+ // Note that the last element of rangeNodes is a sentinel value.
+ c.pos = len(c.rangeNodes) - 1
+}
+
+// Done reports if the iterator is positioned beyond the last valid range.
+func (c *CellIndexRangeIterator) Done() bool {
+ return c.pos >= len(c.rangeNodes)-1
+}
+
+// Seek positions the iterator at the first range with startID >= target.
+// Such an entry always exists as long as "target" is a valid leaf cell.
+//
+// Note that it is valid to access startID even when done is true.
+func (c *CellIndexRangeIterator) Seek(target CellID) {
+ c.pos = sort.Search(len(c.rangeNodes), func(i int) bool {
+ return c.rangeNodes[i].startID > target
+ }) - 1
+
+ // Ensure we don't go beyond the beginning.
+ if c.pos < 0 {
+ c.pos = 0
+ }
+
+ // Nonempty needs to find the next non-empty entry.
+ for c.nonEmpty && c.IsEmpty() && !c.Done() {
+ // c.Next()
+ c.pos++
+ }
+}
+
+// CellIndexContentsIterator is an iterator that visits the (CellID, label) pairs
+// that cover a set of leaf cell ranges (see CellIndexRangeIterator). Note that
+// when multiple leaf cell ranges are visited, this iterator only guarantees that
+// each result will be reported at least once, i.e. duplicate values may be
+// suppressed. If you want duplicate values to be reported again, be sure to call
+// Clear first.
+//
+// In particular, the implementation guarantees that when multiple leaf
+// cell ranges are visited in monotonically increasing order, then each
+// (CellID, label) pair is reported exactly once.
+type CellIndexContentsIterator struct {
+ // The maximum index within the cellTree slice visited during the
+ // previous call to StartUnion. This is used to eliminate duplicate
+ // values when StartUnion is called multiple times.
+ nodeCutoff int32
+
+ // The maximum index within the cellTree visited during the
+ // current call to StartUnion. This is used to update nodeCutoff.
+ nextNodeCutoff int32
+
+ // The value of startID from the previous call to StartUnion.
+ // This is used to check whether these values are monotonically
+ // increasing.
+ prevStartID CellID
+
+ // The cell tree from CellIndex
+ cellTree []cellIndexNode
+
+ // A copy of the current node in the cell tree.
+ node cellIndexNode
+}
+
+// NewCellIndexContentsIterator returns a new contents iterator.
+//
+// Note that the iterator needs to be positioned using StartUnion before
+// it can be safely used.
+func NewCellIndexContentsIterator(index *CellIndex) *CellIndexContentsIterator {
+ it := &CellIndexContentsIterator{
+ cellTree: index.cellTree,
+ prevStartID: 0,
+ nodeCutoff: -1,
+ nextNodeCutoff: -1,
+ node: cellIndexNode{label: cellIndexDoneContents},
+ }
+ return it
+}
+
+// Clear clears all state with respect to which range(s) have been visited.
+func (c *CellIndexContentsIterator) Clear() {
+ c.prevStartID = 0
+ c.nodeCutoff = -1
+ c.nextNodeCutoff = -1
+ c.node.label = cellIndexDoneContents
+}
+
+// CellID returns the current CellID.
+func (c *CellIndexContentsIterator) CellID() CellID {
+ return c.node.cellID
+}
+
+// Label returns the current Label.
+func (c *CellIndexContentsIterator) Label() int32 {
+ return c.node.label
+}
+
+// Next advances the iterator to the next (CellID, label) pair covered by the
+// current leaf cell range.
+//
+// This requires the iterator to not be done.
+func (c *CellIndexContentsIterator) Next() {
+ if c.node.parent <= c.nodeCutoff {
+ // We have already processed this node and its ancestors.
+ c.nodeCutoff = c.nextNodeCutoff
+ c.node.label = cellIndexDoneContents
+ } else {
+ c.node = c.cellTree[c.node.parent]
+ }
+}
+
+// Done reports if all (CellID, label) pairs have been visited.
+func (c *CellIndexContentsIterator) Done() bool {
+ return c.node.label == cellIndexDoneContents
+}
+
+// StartUnion positions the ContentsIterator at the first (cell_id, label) pair
+// that covers the given leaf cell range. Note that when multiple leaf cell
+// ranges are visited using the same ContentsIterator, duplicate values
+// may be suppressed. If you don't want this behavior, call Reset() first.
+func (c *CellIndexContentsIterator) StartUnion(r *CellIndexRangeIterator) {
+ if r.StartID() < c.prevStartID {
+ c.nodeCutoff = -1 // Can't automatically eliminate duplicates.
+ }
+ c.prevStartID = r.StartID()
+
+ contents := r.rangeNodes[r.pos].contents
+ if contents <= c.nodeCutoff {
+ c.node.label = cellIndexDoneContents
+ } else {
+ c.node = c.cellTree[contents]
+ }
+
+ // When visiting ancestors, we can stop as soon as the node index is smaller
+ // than any previously visited node index. Because indexes are assigned
+ // using a preorder traversal, such nodes are guaranteed to have already
+ // been reported.
+ c.nextNodeCutoff = contents
+}
+
+// CellIndex stores a collection of (CellID, label) pairs.
+//
+// The CellIDs may be overlapping or contain duplicate values. For example, a
+// CellIndex could store a collection of CellUnions, where each CellUnion
+// gets its own non-negative int32 label.
+//
+// Similar to ShapeIndex and PointIndex which map each stored element to an
+// identifier, CellIndex stores a label that is typically used to map the
+// results of queries back to client's specific data.
+//
+// The zero value for a CellIndex is sufficient when constructing a CellIndex.
+//
+// To build a CellIndex where each Cell has a distinct label, call Add for each
+// (CellID, label) pair, and then Build the index. For example:
+//
+// // contents is a mapping of an identifier in my system (restaurantID,
+// // vehicleID, etc) to a CellID
+// var contents = map[int32]CellID{...}
+//
+// for key, val := range contents {
+// index.Add(val, key)
+// }
+//
+// index.Build()
+//
+// There is also a helper method that adds all elements of CellUnion with the
+// same label:
+//
+// index.AddCellUnion(cellUnion, label)
+//
+// Note that the index is not dynamic; the contents of the index cannot be
+// changed once it has been built. Adding more after calling Build results in
+// undefined behavior of the index.
+//
+// There are several options for retrieving data from the index. The simplest
+// is to use a built-in method such as IntersectingLabels (which returns
+// the labels of all cells that intersect a given target CellUnion):
+//
+// labels := index.IntersectingLabels(targetUnion);
+//
+// Alternatively, you can use a ClosestCellQuery which computes the cell(s)
+// that are closest to a given target geometry.
+//
+// For example, here is how to find all cells that are closer than
+// distanceLimit to a given target point:
+//
+// query := NewClosestCellQuery(cellIndex, opts)
+// target := NewMinDistanceToPointTarget(targetPoint);
+// for result := range query.FindCells(target) {
+// // result.Distance() is the distance to the target.
+// // result.CellID() is the indexed CellID.
+// // result.Label() is the label associated with the CellID.
+// DoSomething(targetPoint, result);
+// }
+//
+// Internally, the index consists of a set of non-overlapping leaf cell ranges
+// that subdivide the sphere and such that each range intersects a particular
+// set of (cellID, label) pairs.
+//
+// Most clients should use either the methods such as VisitIntersectingCells
+// and IntersectingLabels, or a helper such as ClosestCellQuery.
+type CellIndex struct {
+ // A tree of (cellID, label) pairs such that if X is an ancestor of Y, then
+ // X.cellID contains Y.cellID. The contents of a given range of leaf
+ // cells can be represented by pointing to a node of this tree.
+ cellTree []cellIndexNode
+
+ // The last element of rangeNodes is a sentinel value, which is necessary
+ // in order to represent the range covered by the previous element.
+ rangeNodes []rangeNode
+}
+
+// Add adds the given CellID and Label to the index.
+func (c *CellIndex) Add(id CellID, label int32) {
+ if label < 0 {
+ panic("labels must be non-negative")
+ }
+ c.cellTree = append(c.cellTree, cellIndexNode{cellID: id, label: label, parent: -1})
+}
+
+// AddCellUnion adds all of the elements of the given CellUnion to the index with the same label.
+func (c *CellIndex) AddCellUnion(cu CellUnion, label int32) {
+ if label < 0 {
+ panic("labels must be non-negative")
+ }
+ for _, cell := range cu {
+ c.Add(cell, label)
+ }
+}
+
+// Build builds the index for use. This method should only be called once.
+func (c *CellIndex) Build() {
+ // To build the cell tree and leaf cell ranges, we maintain a stack of
+ // (CellID, label) pairs that contain the current leaf cell. This struct
+ // represents an instruction to push or pop a (cellID, label) pair.
+ //
+ // If label >= 0, the (cellID, label) pair is pushed on the stack.
+ // If CellID == SentinelCellID, a pair is popped from the stack.
+ // Otherwise the stack is unchanged but a rangeNode is still emitted.
+
+ // delta represents an entry in a stack of (CellID, label) pairs used in the
+ // construction of the CellIndex structure.
+ type delta struct {
+ startID CellID
+ cellID CellID
+ label int32
+ }
+
+ deltas := make([]delta, 0, 2*len(c.cellTree)+2)
+
+ // Create two deltas for each (cellID, label) pair: one to add the pair to
+ // the stack (at the start of its leaf cell range), and one to remove it from
+ // the stack (at the end of its leaf cell range).
+ for _, node := range c.cellTree {
+ deltas = append(deltas, delta{
+ startID: node.cellID.RangeMin(),
+ cellID: node.cellID,
+ label: node.label,
+ })
+ deltas = append(deltas, delta{
+ startID: node.cellID.RangeMax().Next(),
+ cellID: SentinelCellID,
+ label: -1,
+ })
+ }
+
+ // We also create two special deltas to ensure that a RangeNode is emitted at
+ // the beginning and end of the CellID range.
+ deltas = append(deltas, delta{
+ startID: CellIDFromFace(0).ChildBeginAtLevel(maxLevel),
+ cellID: CellID(0),
+ label: -1,
+ })
+ deltas = append(deltas, delta{
+ startID: CellIDFromFace(5).ChildEndAtLevel(maxLevel),
+ cellID: CellID(0),
+ label: -1,
+ })
+
+ sort.Slice(deltas, func(i, j int) bool {
+ // deltas are sorted first by startID, then in reverse order by cellID,
+ // and then by label. This is necessary to ensure that (1) larger cells
+ // are pushed on the stack before smaller cells, and (2) cells are popped
+ // off the stack before any new cells are added.
+
+ if si, sj := deltas[i].startID, deltas[j].startID; si != sj {
+ return si < sj
+ }
+ if si, sj := deltas[i].cellID, deltas[j].cellID; si != sj {
+ return si > sj
+ }
+ return deltas[i].label < deltas[j].label
+ })
+
+ // Now walk through the deltas to build the leaf cell ranges and cell tree
+ // (which is essentially a permanent form of the "stack" described above).
+ c.cellTree = nil
+ c.rangeNodes = nil
+ contents := int32(-1)
+ for i := 0; i < len(deltas); {
+ startID := deltas[i].startID
+ // Process all the deltas associated with the current startID.
+ for ; i < len(deltas) && deltas[i].startID == startID; i++ {
+ if deltas[i].label >= 0 {
+ c.cellTree = append(c.cellTree, cellIndexNode{
+ cellID: deltas[i].cellID,
+ label: deltas[i].label,
+ parent: contents})
+ contents = int32(len(c.cellTree) - 1)
+ } else if deltas[i].cellID == SentinelCellID {
+ contents = c.cellTree[contents].parent
+ }
+ }
+ c.rangeNodes = append(c.rangeNodes, rangeNode{startID, contents})
+ }
+}
+
+// TODO(roberts): Differences from C++
+// IntersectingLabels
+// VisitIntersectingCells
+// CellIndexIterator