GEOS 3.14.0dev
TemplateSTRtreeDistance.h
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2020-2021 Daniel Baston
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#pragma once
16
17#include <geos/constants.h>
18#include <geos/index/strtree/TemplateSTRNode.h>
19#include <geos/index/strtree/TemplateSTRNodePair.h>
20#include <geos/util/IllegalArgumentException.h>
21#include <geos/util.h>
22
23#include <queue>
24#include <memory>
25#include <vector>
26
27namespace geos {
28namespace index {
29namespace strtree {
30
31template<typename ItemType, typename BoundsType, typename ItemDistance>
32class TemplateSTRtreeDistance {
33 using Node = TemplateSTRNode<ItemType, BoundsType>;
34 using NodePair = TemplateSTRNodePair<ItemType, BoundsType, ItemDistance>;
35 using ItemPair = std::pair<ItemType, ItemType>;
36
37 struct PairQueueCompare {
38 bool operator()(const NodePair& a, const NodePair& b) {
39 return a.getDistance() > b.getDistance();
40 }
41 };
42
43 using PairQueue = std::priority_queue<NodePair, std::vector<NodePair>, PairQueueCompare>;
44
45public:
46 explicit TemplateSTRtreeDistance(ItemDistance& id) : m_id(id) {}
47
48 ItemPair nearestNeighbour(const Node& root1, const Node& root2) {
49 NodePair initPair(root1, root2, m_id);
50 return nearestNeighbour(initPair);
51 }
52
53 ItemPair nearestNeighbour(NodePair& initPair) {
54 return nearestNeighbour(initPair, DoubleInfinity);
55 }
56
57 bool isWithinDistance(const Node& root1, const Node& root2, double maxDistance) {
58 NodePair initPair(root1, root2, m_id);
59 return isWithinDistance(initPair, maxDistance);
60 }
61
62private:
63
64 ItemPair nearestNeighbour(NodePair& initPair, double maxDistance) {
65 double distanceLowerBound = maxDistance;
66 std::unique_ptr<NodePair> minPair;
67
68 PairQueue priQ;
69 priQ.push(initPair);
70
71 while (!priQ.empty() && distanceLowerBound > 0) {
72 NodePair pair = priQ.top();
73 priQ.pop();
74 double currentDistance = pair.getDistance();
75
76 /*
77 * If the distance for the first node in the queue
78 * is >= the current minimum distance, all other nodes
79 * in the queue must also have a greater distance.
80 * So the current minDistance must be the true minimum,
81 * and we are done.
82 */
83 if (minPair && currentDistance >= distanceLowerBound) {
84 break;
85 }
86
87 /*
88 * If the pair members are leaves
89 * then their distance is the exact lower bound.
90 * Update the distanceLowerBound to reflect this
91 * (which must be smaller, due to the test
92 * immediately prior to this).
93 */
94 if (pair.isLeaves()) {
95 distanceLowerBound = currentDistance;
96 if (minPair) {
97 *minPair = pair;
98 } else {
99 minPair = detail::make_unique<NodePair>(pair);
100 }
101 } else {
102 /*
103 * Otherwise, expand one side of the pair,
104 * (the choice of which side to expand is heuristically determined)
105 * and insert the new expanded pairs into the queue
106 */
107 expandToQueue(pair, priQ, distanceLowerBound);
108 }
109 }
110
111 if (!minPair) {
112 throw util::GEOSException("Error computing nearest neighbor");
113 }
114
115 return minPair->getItems();
116 }
117
118 void expandToQueue(const NodePair& pair, PairQueue& priQ, double minDistance) {
119 const Node& node1 = pair.getFirst();
120 const Node& node2 = pair.getSecond();
121
122 bool isComp1 = node1.isComposite();
123 bool isComp2 = node2.isComposite();
124
130 if (isComp1 && isComp2) {
131 if (node1.getSize() > node2.getSize()) {
132 expand(node1, node2, false, priQ, minDistance);
133 return;
134 } else {
135 expand(node2, node1, true, priQ, minDistance);
136 return;
137 }
138 } else if (isComp1) {
139 expand(node1, node2, false, priQ, minDistance);
140 return;
141 } else if (isComp2) {
142 expand(node2, node1, true, priQ, minDistance);
143 return;
144 }
145
146 throw util::IllegalArgumentException("neither boundable is composite");
147
148 }
149
150 void expand(const Node &nodeComposite, const Node &nodeOther, bool isFlipped, PairQueue& priQ,
151 double minDistance) {
152 for (const auto *child = nodeComposite.beginChildren();
153 child < nodeComposite.endChildren(); ++child) {
154 NodePair sp = isFlipped ? NodePair(nodeOther, *child, m_id) : NodePair(*child, nodeOther, m_id);
155
156 // only add to queue if this pair might contain the closest points
157 if (minDistance == DoubleInfinity || sp.getDistance() < minDistance) {
158 priQ.push(sp);
159 }
160 }
161 }
162
163 bool isWithinDistance(const NodePair& initPair, double maxDistance) {
164 double distanceUpperBound = DoubleInfinity;
165
166 // initialize search queue
167 PairQueue priQ;
168 priQ.push(initPair);
169
170 while (! priQ.empty()) {
171 // pop head of queue and expand one side of pair
172 NodePair pair = priQ.top();
173 double pairDistance = pair.getDistance();
174
182 if (pairDistance > maxDistance)
183 return false;
184
185 priQ.pop();
186
198 if (pair.maximumDistance() <= maxDistance)
199 return true;
205 if (pair.isLeaves()) {
206 // assert: currentDistance < minimumDistanceFound
207 distanceUpperBound = pairDistance;
212 if (distanceUpperBound <= maxDistance)
213 return true;
214 }
215 else {
221 expandToQueue(pair, priQ, distanceUpperBound);
222 }
223 }
224 return false;
225
226 }
227
228
229 ItemDistance& m_id;
230};
231}
232}
233}
Basic namespace for all GEOS functionalities.
Definition geos.h:39