1 // Copyright 2015 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 
16 // Original Author: ericv@google.com (Eric Veach)
17 // Converted to D:  madric@gmail.com (Vijay Nayar)
18 
19 module s2.s2point_index;
20 
21 import s2.s2cell_id;
22 import s2.s2point;
23 import s2.util.container.btree_map;
24 
25 
26 /**
27  * S2PointIndex maintains an index of points sorted by leaf S2CellId.  Each
28  * point has some associated client-supplied data, such as an integer or
29  * pointer.  This can be used to map results back to client data structures.
30  *
31  * The class supports adding or removing points dynamically, and provides a
32  * seekable iterator interface for navigating the index.
33  *
34  * You can use this class in conjuction with S2ClosestPointQuery to find the
35  * closest index points to a given query point.  For example:
36  *
37  * void Test(const vector<S2Point>& points, const S2Point& target) {
38  *   // The template argument allows auxiliary data to be attached to each
39  *   // point (in this case, the array index).
40  *   S2PointIndex<int> index;
41  *   for (int i = 0; i < points.size(); ++i) {
42  *     index.Add(points[i], i);
43  *   }
44  *   S2ClosestPointQuery<int> query(&index);
45  *   query.FindClosestPoint(target);
46  *   if (query.num_points() > 0) {
47  *     // query.point(0) is the closest point (result 0).
48  *     // query.distance(0) is the distance to the target.
49  *     // query.data(0) is the auxiliary data (the array index set above).
50  *     DoSomething(query.point(0), query.data(0), query.distance(0));
51  *   }
52  * }
53  *
54  * Alternatively, you can access the index directly using the iterator
55  * interface.  For example, here is how to iterate through all the points in a
56  * given S2CellId "target_id":
57  *
58  *   S2PointIndex<int>::Iterator it(&index);
59  *   it.Seek(target_id.range_min());
60  *   for (; !it.done() && it.id() <= target_id.range_max(); it.Next()) {
61  *     DoSomething(it.id(), it.point(), it.data());
62  *   }
63  *
64  * Points can be added or removed from the index at any time by calling Add()
65  * or Remove().  However when the index is modified, you must call Init() on
66  * each iterator before using it again (or simply create a new iterator).
67  *
68  *   index.Add(new_point, 123456);
69  *   it.Init(&index);
70  *   it.Seek(target.range_min());
71  *
72  * TODO(ericv): Make this a subtype of S2Region, so that it can also be used
73  * to efficiently compute coverings of a collection of S2Points.
74  *
75  * REQUIRES: "Data" has default and copy constructors.
76  * REQUIRES: "Data" has operator== and operator<.
77  */
78 class S2PointIndex(DataT) {
79 public:
80   // PointData is essentially std::pair with named fields.  It stores an
81   // S2Point and its associated client data.
82   static class PointData {
83   public:
84 
85     this(in S2Point point, in DataT data) {
86       _point = point;
87       _data = data;
88     }
89 
90     S2Point point() const {
91       return _point;
92     }
93 
94     const(DataT) data() const {
95       return _data;
96     }
97 
98     bool opEquals(in PointData other) const {
99       return _point == other._point && _data == other._data;
100     }
101 
102     // Not required by S2PointIndex but useful for tests.
103     int opCmp(in PointData other) const {
104       if (_point < other._point) return -1;
105       if (_point != other._point) return 1;
106       if (_data < other._data) return -1;
107       if (_data != other._data) return 1;
108       return 0;
109     }
110 
111     override
112     string toString() const {
113       import std.conv;
114       return "PointData[point=" ~ point.toString ~ ", data=" ~ to!string(data) ~ "]";
115     }
116 
117   private:
118     S2Point _point;
119     DataT _data;
120   }
121 
122   // Default constructor.
123   this() {
124     _map = new Map();
125   }
126 
127   // Returns the number of points in the index.
128   int numPoints() const {
129     return cast(int) _map.length;
130   }
131 
132   // Adds the given point to the index.  Invalidates all iterators.
133   void add(in S2Point point, in DataT data) {
134     add(new PointData(point, data));
135   }
136 
137   void add(PointData point_data) {
138     auto id = S2CellId(point_data.point());
139     _map.insert(id, point_data);
140   }
141 
142   // Removes the given point from the index.  Both the "point" and "data"
143   // fields must match the point to be removed.  Returns false if the given
144   // point was not present.  Invalidates all iterators.
145   bool remove(in S2Point point, in DataT data) {
146     return remove(new PointData(point, data));
147   }
148 
149   bool remove(in PointData point_data) {
150     auto id = S2CellId(point_data.point());
151     foreach (Map.Pair entry; _map.equalRange(id)) {
152       if (entry.value == point_data) {
153         _map.remove(entry.key);
154         return true;
155       }
156     }
157     return false;
158   }
159 
160   // Resets the index to its original empty state.  Invalidates all iterators.
161   void clear() {
162     _map.clear();
163   }
164 
165 
166 private:
167   // Defined here because the Iterator class below uses it.
168   alias Map = BTreeMap!(S2CellId, PointData);
169 
170 public:
171   static struct Iterator {
172   public:
173     // Default constructor; must be followed by a call to Init().
174     //this() { }
175 
176     // Convenience constructor that calls Init().
177     this(S2PointIndex index) {
178       initialize(index);
179     }
180 
181     // Initializes an iterator for the given S2PointIndex.  If the index is
182     // non-empty, the iterator is positioned at the first cell.
183     //
184     // This method may be called multiple times, e.g. to make an iterator
185     // valid again after the index is modified.
186     void initialize(S2PointIndex index) {
187       _map = index._map;
188       _iter = _map.begin();
189       _end = _map.end();
190     }
191 
192     // The S2CellId for the current index entry.
193     // REQUIRES: !done()
194     S2CellId id() const
195     in {
196       assert(!done());
197     } do {
198       return _iter.getValue().key;
199     }
200 
201     // The point associated with the current index entry.
202     // REQUIRES: !done()
203     const(S2Point) point() const
204     in {
205       assert(!done());
206     } do {
207       return _iter.getValue().value.point();
208     }
209 
210     // The client-supplied data associated with the current index entry.
211     // REQUIRES: !done()
212     inout(DataT) data() inout
213     in {
214       assert(!done());
215     } do {
216       return _iter.getValue().value.data();
217     }
218 
219 
220     // The (S2Point, data) pair associated with the current index entry.
221     inout(PointData) pointData() inout
222     in {
223       assert(!done());
224     } do {
225       return _iter.getValue().value;
226     }
227 
228     // Returns true if the iterator is positioned past the last index entry.
229     bool done() const {
230       return _iter == _end;
231     }
232 
233     // Positions the iterator at the first index entry (if any).
234     void begin() {
235       _iter = _map.begin();
236     }
237 
238     // Positions the iterator so that done() is true.
239     void finish() {
240       _iter = _end;
241     }
242 
243     // Advances the iterator to the next index entry.
244     // REQUIRES: !done()
245     void next()
246     in {
247       assert(!done());
248     } do {
249       ++_iter;
250     }
251 
252     // If the iterator is already positioned at the beginning, returns false.
253     // Otherwise positions the iterator at the previous entry and returns true.
254     bool prev() {
255       if (_iter == _map.begin()) return false;
256       --_iter;
257       return true;
258     }
259 
260     // Positions the iterator at the first entry with id() >= target, or at the
261     // end of the index if no such entry exists.
262     void seek(S2CellId target) {
263       _iter = _map.equalRange(target).toIterator();
264     }
265 
266   private:
267     Map _map;
268     Map.Iterator _iter, _end;
269   }
270 
271  private:
272   Map _map;
273 }