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