// 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