1 /**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2020 Paul Ramsey
7 *
8 * This is free software; you can redistribute and/or modify it under
9 * the terms of the GNU Lesser General Public Licence as published
10 * by the Free Software Foundation.
11 * See the COPYING file for more information.
12 *
13 **********************************************************************/
14
15 #include <geos/index/strtree/SimpleSTRdistance.h>
16 #include <geos/index/strtree/SimpleSTRnode.h>
17 #include <geos/index/strtree/ItemDistance.h>
18 #include <geos/index/strtree/EnvelopeUtil.h>
19 #include <geos/geom/Envelope.h>
20 #include <geos/util/GEOSException.h>
21 #include <geos/util/IllegalArgumentException.h>
22
23 #include <iostream>
24
25 using namespace geos::geom;
26
27 namespace geos {
28 namespace index { // geos.index
29 namespace strtree { // geos.index.strtree
30
31
32
SimpleSTRdistance(SimpleSTRnode * root1,SimpleSTRnode * root2,ItemDistance * p_itemDistance)33 SimpleSTRdistance::SimpleSTRdistance(SimpleSTRnode* root1,
34 SimpleSTRnode* root2, ItemDistance* p_itemDistance)
35 : initPair(createPair(root1, root2, p_itemDistance))
36 , itemDistance(p_itemDistance)
37 {}
38
39
40 SimpleSTRpair*
createPair(SimpleSTRnode * p_node1,SimpleSTRnode * p_node2,ItemDistance * p_itemDistance)41 SimpleSTRdistance::createPair(SimpleSTRnode* p_node1, SimpleSTRnode* p_node2,
42 ItemDistance* p_itemDistance)
43 {
44 pairStore.emplace_back(p_node1, p_node2, p_itemDistance);
45 SimpleSTRpair& pair = pairStore.back();
46 return &pair;
47 }
48
49
50 /*public*/
51 std::pair<const void*, const void*>
nearestNeighbour()52 SimpleSTRdistance::nearestNeighbour()
53 {
54 return nearestNeighbour(initPair);
55 }
56
57
58 /*private*/
59 std::pair<const void*, const void*>
nearestNeighbour(SimpleSTRpair * p_initPair)60 SimpleSTRdistance::nearestNeighbour(SimpleSTRpair* p_initPair)
61 {
62 return nearestNeighbour(p_initPair, std::numeric_limits<double>::infinity());
63 }
64
65
66 /*private*/
67 std::pair<const void*, const void*>
nearestNeighbour(SimpleSTRpair * p_initPair,double maxDistance)68 SimpleSTRdistance::nearestNeighbour(SimpleSTRpair* p_initPair, double maxDistance)
69 {
70 double distanceLowerBound = maxDistance;
71 SimpleSTRpair* minPair = nullptr;
72
73 STRpairQueue priQ;
74 priQ.push(p_initPair);
75
76 while(!priQ.empty() && distanceLowerBound > 0.0) {
77 SimpleSTRpair* pair = priQ.top();
78 double currentDistance = pair->getDistance();
79
80 // std::cout << *pair << std::endl;
81
82 /*
83 * If the distance for the first node in the queue
84 * is >= the current minimum distance, all other nodes
85 * in the queue must also have a greater distance.
86 * So the current minDistance must be the true minimum,
87 * and we are done.
88 */
89 if(minPair && currentDistance >= distanceLowerBound) {
90 break;
91 }
92
93 priQ.pop();
94
95 /*
96 * If the pair members are leaves
97 * then their distance is the exact lower bound.
98 * Update the distanceLowerBound to reflect this
99 * (which must be smaller, due to the test
100 * immediately prior to this).
101 */
102 if(pair->isLeaves()) {
103 distanceLowerBound = currentDistance;
104 minPair = pair;
105 }
106 else {
107 /*
108 * Otherwise, expand one side of the pair,
109 * (the choice of which side to expand is heuristically determined)
110 * and insert the new expanded pairs into the queue
111 */
112 expandToQueue(pair, priQ, distanceLowerBound);
113 }
114 }
115
116 /* Free any remaining node pairs in the queue */
117 while(!priQ.empty()) {
118 priQ.pop();
119 }
120
121 if(!minPair) {
122 throw util::GEOSException("Error computing nearest neighbor");
123 }
124
125 const void* item0 = minPair->getNode(0)->getItem();
126 const void* item1 = minPair->getNode(1)->getItem();
127
128 return std::pair<const void*, const void*>(item0, item1);
129 }
130
131
132 void
expandToQueue(SimpleSTRpair * pair,STRpairQueue & priQ,double minDistance)133 SimpleSTRdistance::expandToQueue(SimpleSTRpair* pair, STRpairQueue& priQ, double minDistance)
134 {
135
136 SimpleSTRnode* node1 = pair->getNode(0);
137 SimpleSTRnode* node2 = pair->getNode(1);
138
139 bool isComp1 = node1->isComposite();
140 bool isComp2 = node2->isComposite();
141
142 /**
143 * HEURISTIC: If both boundable are composite,
144 * choose the one with largest area to expand.
145 * Otherwise, simply expand whichever is composite.
146 */
147 if (isComp1 && isComp2) {
148 if (node1->area() > node2->area()) {
149 expand(node1, node2, false, priQ, minDistance);
150 return;
151 }
152 else {
153 expand(node2, node1, true, priQ, minDistance);
154 return;
155 }
156 }
157 else if (isComp1) {
158 expand(node1, node2, false, priQ, minDistance);
159 return;
160 }
161 else if (isComp2) {
162 expand(node2, node1, true, priQ, minDistance);
163 return;
164 }
165
166 throw util::IllegalArgumentException("neither boundable is composite");
167 }
168
169
170 void
expand(SimpleSTRnode * nodeComposite,SimpleSTRnode * nodeOther,bool isFlipped,STRpairQueue & priQ,double minDistance)171 SimpleSTRdistance::expand(SimpleSTRnode* nodeComposite, SimpleSTRnode* nodeOther,
172 bool isFlipped, STRpairQueue& priQ, double minDistance)
173 {
174 auto children = nodeComposite->getChildNodes();
175 for (auto* child: children) {
176 SimpleSTRpair* sp = nullptr;
177 if (isFlipped) {
178 sp = createPair(nodeOther, child, itemDistance);
179 }
180 else {
181 sp = createPair(child, nodeOther, itemDistance);
182 }
183 // only add to queue if this pair might contain the closest points
184 // MD - it's actually faster to construct the object rather than called distance(child, nodeOther)!
185 if (sp->getDistance() < minDistance) {
186 priQ.push(sp);
187 }
188 }
189 }
190
191 /* public */
192 bool
isWithinDistance(double maxDistance)193 SimpleSTRdistance::isWithinDistance(double maxDistance)
194 {
195 return isWithinDistance(initPair, maxDistance);
196 }
197
198 /* private */
199 bool
isWithinDistance(SimpleSTRpair * p_initPair,double maxDistance)200 SimpleSTRdistance::isWithinDistance(SimpleSTRpair* p_initPair, double maxDistance)
201 {
202 double distanceUpperBound = std::numeric_limits<double>::infinity();
203
204 // initialize search queue
205 STRpairQueue priQ;
206 priQ.push(p_initPair);
207
208 while (! priQ.empty()) {
209 // pop head of queue and expand one side of pair
210 SimpleSTRpair* pair = priQ.top();
211 double pairDistance = pair->getDistance();
212
213 /**
214 * If the distance for the first pair in the queue
215 * is > maxDistance, all other pairs
216 * in the queue must have a greater distance as well.
217 * So can conclude no items are within the distance
218 * and terminate with result = false
219 */
220 if (pairDistance > maxDistance)
221 return false;
222
223 priQ.pop();
224 /**
225 * If the maximum distance between the nodes
226 * is less than the maxDistance,
227 * than all items in the nodes must be
228 * closer than the max distance.
229 * Then can terminate with result = true.
230 *
231 * NOTE: using Envelope MinMaxDistance
232 * would provide a tighter bound,
233 * but not much performance improvement has been observed
234 */
235 if (pair->maximumDistance() <= maxDistance)
236 return true;
237 /**
238 * If the pair items are leaves
239 * then their actual distance is an upper bound.
240 * Update the distanceUpperBound to reflect this
241 */
242 if (pair->isLeaves()) {
243 // assert: currentDistance < minimumDistanceFound
244 distanceUpperBound = pairDistance;
245 /**
246 * If the items are closer than maxDistance
247 * can terminate with result = true.
248 */
249 if (distanceUpperBound <= maxDistance)
250 return true;
251 }
252 else {
253 /**
254 * Otherwise, expand one side of the pair,
255 * and insert the expanded pairs into the queue.
256 * The choice of which side to expand is determined heuristically.
257 */
258 expandToQueue(pair, priQ, distanceUpperBound);
259 }
260 }
261 return false;
262 }
263
264
265 /*********************************************************************************/
266
267 SimpleSTRnode*
getNode(int i) const268 SimpleSTRpair::getNode(int i) const
269 {
270 if (i == 0) {
271 return node1;
272 }
273 return node2;
274 }
275
276
277 /* private */
278 double
distance()279 SimpleSTRpair::distance()
280 {
281 // if items, compute exact distance
282 if (isLeaves()) {
283 return itemDistance->distance(node1, node2);
284 }
285
286 // otherwise compute distance between bounds of nodes
287 const geom::Envelope& e1 = node1->getEnvelope();
288 const geom::Envelope& e2 = node2->getEnvelope();
289 return e1.distance(e2);
290 }
291
292
293 double
getDistance() const294 SimpleSTRpair::getDistance() const
295 {
296 return m_distance;
297 }
298
299 bool
isLeaves() const300 SimpleSTRpair::isLeaves() const
301 {
302 return node1->isLeaf() && node2->isLeaf();
303 }
304
305 double
maximumDistance()306 SimpleSTRpair::maximumDistance()
307 {
308 return EnvelopeUtil::maximumDistance(&(node1->getEnvelope()), &(node2->getEnvelope()));
309 }
310
311 /*public static*/
312 std::ostream&
operator <<(std::ostream & os,SimpleSTRpair & pair)313 operator<<(std::ostream& os, SimpleSTRpair& pair)
314 {
315 const geom::Envelope& e1 = pair.getNode(0)->getEnvelope();
316 const geom::Envelope& e2 = pair.getNode(1)->getEnvelope();
317 double distance = pair.getDistance();
318
319 os << e1 << " " << e2 << " " << distance;
320 return os;
321 }
322
323
324 } // namespace geos.index.strtree
325 } // namespace geos.index
326 } // namespace geos
327
328
329