KDTree.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. package edu.wlu.cs.levy.CG;
  2. import java.io.Serializable;
  3. import java.util.List;
  4. import java.util.LinkedList;
  5. import java.util.Stack;
  6. /**
  7. * KDTree is a class supporting KD-tree insertion, deletion, equality
  8. * search, range search, and nearest neighbor(s) using double-precision
  9. * floating-point keys. Splitting dimension is chosen naively, by
  10. * depth modulo K. Semantics are as follows:
  11. *
  12. * <UL>
  13. * <LI> Two different keys containing identical numbers should retrieve the
  14. * same value from a given KD-tree. Therefore keys are cloned when a
  15. * node is inserted.
  16. * <BR><BR>
  17. * <LI> As with Hashtables, values inserted into a KD-tree are <I>not</I>
  18. * cloned. Modifying a value between insertion and retrieval will
  19. * therefore modify the value stored in the tree.
  20. *</UL>
  21. *
  22. * Implements the Nearest Neighbor algorithm (Table 6.4) of
  23. *
  24. * <PRE>
  25. * &*064;techreport{AndrewMooreNearestNeighbor,
  26. * author = {Andrew Moore},
  27. * title = {An introductory tutorial on kd-trees},
  28. * institution = {Robotics Institute, Carnegie Mellon University},
  29. * year = {1991},
  30. * number = {Technical Report No. 209, Computer Laboratory,
  31. * University of Cambridge},
  32. * address = {Pittsburgh, PA}
  33. * }
  34. * </PRE>
  35. *
  36. * Copyright (C) Simon D. Levy and Bjoern Heckel 2014
  37. *
  38. * This code is free software: you can redistribute it and/or modify
  39. * it under the terms of the GNU Lesser General Public License as
  40. * published by the Free Software Foundation, either version 3 of the
  41. * License, or (at your option) any later version.
  42. *
  43. * This code is distributed in the hope that it will be useful,
  44. * but WITHOUT ANY WARRANTY without even the implied warranty of
  45. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  46. * GNU General Public License for more details.
  47. *
  48. * You should have received a copy of the GNU Lesser General Public License
  49. * along with this code. If not, see <http:*www.gnu.org/licenses/>.
  50. * You should also have received a copy of the Parrot Parrot AR.Drone
  51. * Development License and Parrot AR.Drone copyright notice and disclaimer
  52. * and If not, see
  53. * <https:*projects.ardrone.org/attachments/277/ParrotLicense.txt>
  54. * and
  55. * <https:*projects.ardrone.org/attachments/278/ParrotCopyrightAndDisclaimer.txt>.
  56. */
  57. public class KDTree<T> implements Serializable{
  58. // number of milliseconds
  59. final long m_timeout;
  60. // K = number of dimensions
  61. final private int m_K;
  62. // root of KD-tree
  63. private KDNode<T> m_root;
  64. // count of nodes
  65. private int m_count;
  66. /**
  67. * Creates a KD-tree with specified number of dimensions.
  68. *
  69. * @param k number of dimensions
  70. */
  71. public KDTree(int k) {
  72. this(k, 0);
  73. }
  74. public KDTree(int k, long timeout) {
  75. this.m_timeout = timeout;
  76. m_K = k;
  77. m_root = null;
  78. }
  79. /**
  80. * Insert a node in a KD-tree. Uses algorithm translated from 352.ins.c of
  81. *
  82. * <PRE>
  83. * &*064;Book{GonnetBaezaYates1991,
  84. * author = {G.H. Gonnet and R. Baeza-Yates},
  85. * title = {Handbook of Algorithms and Data Structures},
  86. * publisher = {Addison-Wesley},
  87. * year = {1991}
  88. * }
  89. * </PRE>
  90. *
  91. * @param key key for KD-tree node
  92. * @param value value at that key
  93. *
  94. * @throws KeySizeException if key.length mismatches K
  95. * @throws KeyDuplicateException if key already in tree
  96. */
  97. public void insert(double [] key, T value)
  98. throws KeySizeException, KeyDuplicateException {
  99. this.edit(key, new Editor.Inserter<T>(value));
  100. }
  101. /**
  102. * Edit a node in a KD-tree
  103. *
  104. * @param key key for KD-tree node
  105. * @param editor object to edit the value at that key
  106. *
  107. * @throws KeySizeException if key.length mismatches K
  108. * @throws KeyDuplicateException if key already in tree
  109. */
  110. public void edit(double [] key, Editor<T> editor)
  111. throws KeySizeException, KeyDuplicateException {
  112. if (key.length != m_K) {
  113. throw new KeySizeException();
  114. }
  115. synchronized (this) {
  116. // the first insert has to be synchronized
  117. if (null == m_root) {
  118. m_root = KDNode.create(new HPoint(key), editor);
  119. m_count = m_root.deleted ? 0 : 1;
  120. return;
  121. }
  122. }
  123. m_count += KDNode.edit(new HPoint(key), editor, m_root, 0, m_K);
  124. }
  125. /**
  126. * Find KD-tree node whose key is identical to key. Uses algorithm
  127. * translated from 352.srch.c of Gonnet & Baeza-Yates.
  128. *
  129. * @param key key for KD-tree node
  130. *
  131. * @return object at key, or null if not found
  132. *
  133. * @throws KeySizeException if key.length mismatches K
  134. */
  135. public T search(double [] key) throws KeySizeException {
  136. if (key.length != m_K) {
  137. throw new KeySizeException();
  138. }
  139. KDNode<T> kd = KDNode.srch(new HPoint(key), m_root, m_K);
  140. return (kd == null ? null : kd.v);
  141. }
  142. public void delete(double [] key)
  143. throws KeySizeException, KeyMissingException {
  144. delete(key, false);
  145. }
  146. /**
  147. * Delete a node from a KD-tree. Instead of actually deleting node and
  148. * rebuilding tree, marks node as deleted. Hence, it is up to the caller
  149. * to rebuild the tree as needed for efficiency.
  150. *
  151. * @param key key for KD-tree node
  152. * @param optional if false and node not found, throw an exception
  153. *
  154. * @throws KeySizeException if key.length mismatches K
  155. * @throws KeyMissingException if no node in tree has key
  156. */
  157. public void delete(double [] key, boolean optional)
  158. throws KeySizeException, KeyMissingException {
  159. if (key.length != m_K) {
  160. throw new KeySizeException();
  161. }
  162. KDNode<T> t = KDNode.srch(new HPoint(key), m_root, m_K);
  163. if (t == null) {
  164. if (optional == false) {
  165. throw new KeyMissingException();
  166. }
  167. }
  168. else {
  169. if (KDNode.del(t)) {
  170. m_count--;
  171. }
  172. }
  173. }
  174. /**
  175. * Find KD-tree node whose key is nearest neighbor to
  176. * key.
  177. *
  178. * @param key key for KD-tree node
  179. *
  180. * @return object at node nearest to key, or null on failure
  181. *
  182. * @throws KeySizeException if key.length mismatches K
  183. */
  184. public T nearest(double [] key) throws KeySizeException {
  185. List<T> nbrs = nearest(key, 1, null);
  186. return nbrs.get(0);
  187. }
  188. /**
  189. * Find KD-tree nodes whose keys are <i>n</i> nearest neighbors to
  190. * key.
  191. *
  192. * @param key key for KD-tree node
  193. * @param n number of nodes to return
  194. *
  195. * @return objects at nodes nearest to key, or null on failure
  196. *
  197. * @throws KeySizeException if key.length mismatches K
  198. */
  199. public List<T> nearest(double [] key, int n)
  200. throws KeySizeException, IllegalArgumentException {
  201. return nearest(key, n, null);
  202. }
  203. /**
  204. * Find KD-tree nodes whose keys are within a given Euclidean distance of
  205. * a given key.
  206. *
  207. * @param key key for KD-tree node
  208. * @param d Euclidean distance
  209. *
  210. * @return objects at nodes with distance of key, or null on failure
  211. *
  212. * @throws KeySizeException if key.length mismatches K
  213. */
  214. public List<T> nearestEuclidean(double [] key, double dist)
  215. throws KeySizeException {
  216. return nearestDistance(key, dist, new EuclideanDistance());
  217. }
  218. /**
  219. * Find KD-tree nodes whose keys are within a given Hamming distance of
  220. * a given key.
  221. *
  222. * @param key key for KD-tree node
  223. * @param d Hamming distance
  224. *
  225. * @return objects at nodes with distance of key, or null on failure
  226. *
  227. * @throws KeySizeException if key.length mismatches K
  228. */
  229. public List<T> nearestHamming(double [] key, double dist)
  230. throws KeySizeException {
  231. return nearestDistance(key, dist, new HammingDistance());
  232. }
  233. /**
  234. * Find KD-tree nodes whose keys are <I>n</I> nearest neighbors to
  235. * key. Uses algorithm above. Neighbors are returned in ascending
  236. * order of distance to key.
  237. *
  238. * @param key key for KD-tree node
  239. * @param n how many neighbors to find
  240. * @param checker an optional object to filter matches
  241. *
  242. * @return objects at node nearest to key, or null on failure
  243. *
  244. * @throws KeySizeException if key.length mismatches K
  245. * @throws IllegalArgumentException if <I>n</I> is negative or
  246. * exceeds tree size
  247. */
  248. public List<T> nearest(double [] key, int n, Checker<T> checker)
  249. throws KeySizeException, IllegalArgumentException {
  250. if (n <= 0) {
  251. return new LinkedList<T>();
  252. }
  253. NearestNeighborList<KDNode<T>> nnl = getnbrs(key, n, checker);
  254. n = nnl.getSize();
  255. Stack<T> nbrs = new Stack<T>();
  256. for (int i=0; i<n; ++i) {
  257. KDNode<T> kd = nnl.removeHighest();
  258. nbrs.push(kd.v);
  259. }
  260. return nbrs;
  261. }
  262. /**
  263. * Range search in a KD-tree. Uses algorithm translated from
  264. * 352.range.c of Gonnet & Baeza-Yates.
  265. *
  266. * @param lowk lower-bounds for key
  267. * @param uppk upper-bounds for key
  268. *
  269. * @return array of Objects whose keys fall in range [lowk,uppk]
  270. *
  271. * @throws KeySizeException on mismatch among lowk.length, uppk.length, or K
  272. */
  273. public List<T> range(double [] lowk, double [] uppk)
  274. throws KeySizeException {
  275. if (lowk.length != uppk.length) {
  276. throw new KeySizeException();
  277. }
  278. else if (lowk.length != m_K) {
  279. throw new KeySizeException();
  280. }
  281. else {
  282. List<KDNode<T>> found = new LinkedList<KDNode<T>>();
  283. KDNode.rsearch(new HPoint(lowk), new HPoint(uppk),
  284. m_root, 0, m_K, found);
  285. List<T> o = new LinkedList<T>();
  286. for (KDNode<T> node : found) {
  287. o.add(node.v);
  288. }
  289. return o;
  290. }
  291. }
  292. public int size() { /* added by MSL */
  293. return m_count;
  294. }
  295. public String toString() {
  296. return m_root.toString(0);
  297. }
  298. private NearestNeighborList<KDNode<T>> getnbrs(double [] key)
  299. throws KeySizeException {
  300. return getnbrs(key, m_count, null);
  301. }
  302. private NearestNeighborList<KDNode<T>> getnbrs(double [] key, int n,
  303. Checker<T> checker) throws KeySizeException {
  304. if (key.length != m_K) {
  305. throw new KeySizeException();
  306. }
  307. NearestNeighborList<KDNode<T>> nnl = new NearestNeighborList<KDNode<T>>(n);
  308. // initial call is with infinite hyper-rectangle and max distance
  309. HRect hr = HRect.infiniteHRect(key.length);
  310. double max_dist_sqd = Double.MAX_VALUE;
  311. HPoint keyp = new HPoint(key);
  312. if (m_count > 0) {
  313. long timeout = (this.m_timeout > 0) ?
  314. (System.currentTimeMillis() + this.m_timeout) :
  315. 0;
  316. KDNode.nnbr(m_root, keyp, hr, max_dist_sqd, 0, m_K, nnl, checker, timeout);
  317. }
  318. return nnl;
  319. }
  320. private List<T> nearestDistance(double [] key, double dist,
  321. DistanceMetric metric) throws KeySizeException {
  322. NearestNeighborList<KDNode<T>> nnl = getnbrs(key);
  323. int n = nnl.getSize();
  324. Stack<T> nbrs = new Stack<T>();
  325. for (int i=0; i<n; ++i) {
  326. KDNode<T> kd = nnl.removeHighest();
  327. HPoint p = kd.k;
  328. if (metric.distance(kd.k.coord, key) < dist) {
  329. nbrs.push(kd.v);
  330. }
  331. }
  332. return nbrs;
  333. }
  334. }