Savarese Software Research
Sava Algorithms 0.1.1 Java Unit Test Coverage
[all classes][com.savarese.algorithms.spatial]

COVERAGE SUMMARY FOR SOURCE FILE [KDTree.java]

nameclass, %method, %block, %line, %
KDTree.java100% (11/11)68%  (54/80)66%  (1106/1684)64%  (236.5/369)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class KDTree$MapEntrySet100% (1/1)33%  (2/6)10%  (13/125)8%   (2/26)
contains (Object): boolean 0%   (0/1)0%   (0/20)0%   (0/5)
remove (Object): boolean 0%   (0/1)0%   (0/20)0%   (0/4)
removeAll (Collection): boolean 0%   (0/1)0%   (0/30)0%   (0/6)
retainAll (Collection): boolean 0%   (0/1)0%   (0/42)0%   (0/9)
KDTree$MapEntrySet (KDTree): void 100% (1/1)100% (7/7)100% (1/1)
iterator (): Iterator 100% (1/1)100% (6/6)100% (1/1)
     
class KDTree$ValueCollection100% (1/1)50%  (3/6)16%  (23/145)10%  (3/29)
remove (Object): boolean 0%   (0/1)0%   (0/20)0%   (0/5)
removeAll (Collection): boolean 0%   (0/1)0%   (0/45)0%   (0/9)
retainAll (Collection): boolean 0%   (0/1)0%   (0/57)0%   (0/12)
KDTree$ValueCollection (KDTree): void 100% (1/1)100% (7/7)100% (1/1)
contains (Object): boolean 100% (1/1)100% (5/5)100% (1/1)
iterator (): Iterator 100% (1/1)100% (11/11)100% (1/1)
     
class KDTree$KeySet100% (1/1)33%  (2/6)16%  (18/113)10%  (2/20)
contains (Object): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
remove (Object): boolean 0%   (0/1)0%   (0/16)0%   (0/3)
removeAll (Collection): boolean 0%   (0/1)0%   (0/26)0%   (0/4)
retainAll (Collection): boolean 0%   (0/1)0%   (0/48)0%   (0/10)
KDTree$KeySet (KDTree): void 100% (1/1)100% (7/7)100% (1/1)
iterator (): Iterator 100% (1/1)100% (11/11)100% (1/1)
     
class KDTree$SetView100% (1/1)50%  (1/2)21%  (7/34)9%   (1/11)
equals (Object): boolean 0%   (0/1)0%   (0/27)0%   (0/10)
KDTree$SetView (KDTree): void 100% (1/1)100% (7/7)100% (1/1)
     
class KDTree$CollectionView100% (1/1)30%  (3/10)22%  (26/118)19%  (6/31)
add (Object): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
addAll (Collection): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
clear (): void 0%   (0/1)0%   (0/4)0%   (0/2)
hashCode (): int 0%   (0/1)0%   (0/4)0%   (0/1)
isEmpty (): boolean 0%   (0/1)0%   (0/4)0%   (0/1)
toArray (): Object [] 0%   (0/1)0%   (0/23)0%   (0/7)
toArray (Object []): Object [] 0%   (0/1)0%   (0/47)0%   (0/11)
containsAll (Collection): boolean 100% (1/1)89%  (16/18)80%  (4/5)
KDTree$CollectionView (KDTree): void 100% (1/1)100% (6/6)100% (1/1)
size (): int 100% (1/1)100% (4/4)100% (1/1)
     
class KDTree$KDNode100% (1/1)67%  (4/6)40%  (43/107)49%  (8.9/18)
equals (Object): boolean 0%   (0/1)0%   (0/38)0%   (0/4)
setValue (Object): Object 0%   (0/1)0%   (0/24)0%   (0/5)
hashCode (): int 100% (1/1)89%  (16/18)89%  (0.9/1)
KDTree$KDNode (KDTree, int, Point, Object): void 100% (1/1)100% (21/21)100% (6/6)
getKey (): Point 100% (1/1)100% (3/3)100% (1/1)
getValue (): Object 100% (1/1)100% (3/3)100% (1/1)
     
class KDTree$ValueIterator100% (1/1)75%  (3/4)79%  (22/28)73%  (5.8/8)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/2)
next (): Object 100% (1/1)82%  (9/11)91%  (1.8/2)
KDTree$ValueIterator (KDTree, KDTree$MapEntryIterator): void 100% (1/1)100% (9/9)100% (3/3)
hasNext (): boolean 100% (1/1)100% (4/4)100% (1/1)
     
class KDTree$KeyIterator100% (1/1)75%  (3/4)79%  (23/29)73%  (5.8/8)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/2)
next (): Point 100% (1/1)83%  (10/12)92%  (1.8/2)
KDTree$KeyIterator (KDTree, KDTree$MapEntryIterator): void 100% (1/1)100% (9/9)100% (3/3)
hasNext (): boolean 100% (1/1)100% (4/4)100% (1/1)
     
class KDTree100% (1/1)93%  (25/27)94%  (782/831)92%  (171/185)
equals (Object): boolean 0%   (0/1)0%   (0/19)0%   (0/6)
hashCode (): int 0%   (0/1)0%   (0/3)0%   (0/1)
KDTree (int): void 100% (1/1)62%  (10/16)88%  (4.4/5)
get (Object): Object 100% (1/1)83%  (10/12)92%  (1.8/2)
put (Point, Object): Object 100% (1/1)87%  (96/110)76%  (13/17)
<static initializer> 100% (1/1)88%  (7/8)88%  (0.9/1)
containsKey (Object): boolean 100% (1/1)89%  (8/9)89%  (0.9/1)
isInRange (Point, Point, Point): boolean 100% (1/1)96%  (48/50)92%  (11/12)
optimize (): void 100% (1/1)97%  (30/31)83%  (5/6)
KDTree (): void 100% (1/1)100% (4/4)100% (2/2)
clear (): void 100% (1/1)100% (10/10)100% (3/3)
containsValue (Object): boolean 100% (1/1)100% (10/10)100% (1/1)
entrySet (): Set 100% (1/1)100% (5/5)100% (1/1)
fillArray (KDTree$KDNode [], int, KDTree$KDNode): int 100% (1/1)100% (24/24)100% (5/5)
findValue (KDTree$KDNode, Object): KDTree$KDNode 100% (1/1)100% (31/31)100% (5/5)
getMinimumNode (KDTree$KDNode, KDTree$KDNode, int, KDTree$KDNode []): KDTree$... 100% (1/1)100% (132/132)100% (32/32)
getNode (Point): KDTree$KDNode 100% (1/1)100% (5/5)100% (1/1)
getNode (Point, KDTree$KDNode []): KDTree$KDNode 100% (1/1)100% (64/64)100% (20/20)
isEmpty (): boolean 100% (1/1)100% (7/7)100% (1/1)
iterator (Point, Point): Iterator 100% (1/1)100% (7/7)100% (1/1)
keySet (): Set 100% (1/1)100% (5/5)100% (1/1)
optimize (KDTree$KDNode [], int, int, KDTree$NodeComparator): KDTree$KDNode 100% (1/1)100% (105/105)100% (24/24)
putAll (Map): void 100% (1/1)100% (21/21)100% (3/3)
recursiveRemoveNode (KDTree$KDNode): KDTree$KDNode 100% (1/1)100% (67/67)100% (16/16)
remove (Object): Object 100% (1/1)100% (68/68)100% (16/16)
size (): int 100% (1/1)100% (3/3)100% (1/1)
values (): Collection 100% (1/1)100% (5/5)100% (1/1)
     
class KDTree$MapEntryIterator100% (1/1)80%  (4/5)96%  (121/126)93%  (25/27)
remove (): void 0%   (0/1)0%   (0/4)0%   (0/1)
next (): Map$Entry 100% (1/1)99%  (77/78)93%  (13/14)
KDTree$MapEntryIterator (KDTree): void 100% (1/1)100% (6/6)100% (2/2)
KDTree$MapEntryIterator (KDTree, Point, Point): void 100% (1/1)100% (31/31)100% (9/9)
hasNext (): boolean 100% (1/1)100% (7/7)100% (1/1)
     
class KDTree$NodeComparator100% (1/1)100% (4/4)100% (28/28)100% (6/6)
KDTree$NodeComparator (KDTree): void 100% (1/1)100% (9/9)100% (2/2)
compare (KDTree$KDNode, KDTree$KDNode): int 100% (1/1)100% (12/12)100% (1/1)
getDiscriminator (): int 100% (1/1)100% (3/3)100% (1/1)
setDiscriminator (int): void 100% (1/1)100% (4/4)100% (2/2)

1/*
2 * $Id: KDTree.java 5870 2005-10-28 02:08:08Z dfs $
3 *
4 * Copyright 2001-2005 Daniel F. Savarese
5 * Copyright 2005 Savarese Software Research
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *     https://www.savarese.com/software/ApacheLicense-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
19 
20package com.savarese.algorithms.spatial;
21 
22import java.lang.reflect.Array;
23import java.util.*;
24 
25// All the view classes are inefficient for anything other than iteration.
26/**
27 * <p>A k-d tree divides a k-dimensional space relative to the points it
28 * contains by storing them in a binary tree, discriminating by a
29 * different dimension at each level of the tree.  It allows efficient
30 * point data retrieval (<em>O(lg(n))</em>) and range searching.</p>
31 *
32 * <p>KDTree conforms to the java.util.Map interface except that
33 * Iterator.remove is not supported by the returned views.</p>
34 *
35 * @author <a href="https://www.savarese.com/">Daniel F. Savarese</a>
36 */
37public class KDTree<Coord extends Comparable<? super Coord>,
38                        P extends Point<Coord>, V>
39  implements RangeSearchTree<Coord, P, V>
40{
41  final class KDNode implements Map.Entry<P,V>{
42    int _discriminator;
43    P _point;
44    V _value;
45    KDNode _low, _high;
46 
47    KDNode(int discriminator, P point, V value) {
48      _point = point;
49      _value = value;
50      _low  = _high = null;
51      _discriminator = discriminator;
52    }
53 
54    public boolean equals(Object o) {
55      KDNode node = (KDNode)o;
56 
57      if(node == this)
58        return true;
59 
60      return
61        ((getKey() == null ?
62          node.getKey() == null : getKey().equals(node.getKey()))  &&
63         (getValue() == null ?
64          node.getValue() == null : getValue().equals(node.getValue())));
65    }
66 
67    public P getKey() {
68      return _point;
69    }
70 
71    public V getValue() {
72      return _value;
73    }
74 
75    // Only call if the node is in the tree.
76    public V setValue(V value) {
77      V old = _value;
78      _hashCode-=hashCode();
79      _value = value;
80      _hashCode+=hashCode();
81      return old;
82    }
83 
84    public int hashCode() {
85      return
86        ((getKey() == null ? 0 : getKey().hashCode()) ^
87         (getValue() == null ? 0 : getValue().hashCode()));
88    }
89  }
90 
91  final class MapEntryIterator implements Iterator<Map.Entry<P,V>> {
92    LinkedList<KDNode> _stack;
93    KDNode _next;
94    P _lower, _upper;
95 
96    MapEntryIterator(P lower, P upper) {
97      _stack = new LinkedList<KDNode>();
98      _lower = lower;
99      _upper = upper;
100      _next  = null;
101 
102      if(_root != null)
103        _stack.addLast(_root);
104      next();
105    }
106 
107    MapEntryIterator() {
108      this(null, null);
109    }
110 
111    public boolean hasNext() {
112      return (_next != null);
113    }
114 
115    public Map.Entry<P,V> next() {
116      KDNode old = _next;
117 
118      while(!_stack.isEmpty()) {
119        KDNode node = _stack.removeLast();
120        int discriminator = node._discriminator;
121 
122        if((_upper == null || 
123           node._point.getCoord(discriminator).compareTo(
124           _upper.getCoord(discriminator)) <= 0) && node._high != null)
125          _stack.addLast(node._high);
126 
127        if((_lower == null ||
128           node._point.getCoord(discriminator).compareTo(
129           _lower.getCoord(discriminator)) > 0) && node._low != null)
130          _stack.addLast(node._low);
131 
132        if(isInRange(node._point, _lower, _upper)) {
133          _next = node;
134          return old;
135        }
136      }
137 
138      _next = null;
139 
140      return old;
141    }
142 
143    // This violates the contract for entrySet, but we can't support
144    // in a reasonable fashion the removal of mappings through the iterator
145    // because of changes to the tree structure.
146    public void remove()
147      throws UnsupportedOperationException
148    {
149      throw new UnsupportedOperationException();
150    }
151  }
152 
153  final class KeyIterator implements Iterator<P> {
154    MapEntryIterator iterator;
155 
156    KeyIterator(MapEntryIterator it) {
157      iterator = it;
158    }
159 
160    public boolean hasNext() {
161      return iterator.hasNext();
162    }
163 
164    public P next() {
165      Map.Entry<P,V> next = iterator.next();
166      return (next == null ? null : next.getKey());
167    }
168 
169    public void remove()
170      throws UnsupportedOperationException
171    {
172      iterator.remove();
173    }
174  }
175 
176  final class ValueIterator implements Iterator<V> {
177    MapEntryIterator iterator;
178 
179    ValueIterator(MapEntryIterator it) {
180      iterator = it;
181    }
182 
183    public boolean hasNext() {
184      return iterator.hasNext();
185    }
186 
187    public V next() {
188      Map.Entry<P,V> next = iterator.next();
189      return (next == null ? null : next.getValue());
190    }
191 
192    public void remove()
193      throws UnsupportedOperationException
194    {
195      iterator.remove();
196    }
197  }
198 
199  abstract class CollectionView<E> implements Collection<E> {
200 
201    public boolean add(E o)
202      throws UnsupportedOperationException
203    {
204      throw new UnsupportedOperationException();
205    }
206 
207    public boolean addAll(Collection<? extends E> c)
208      throws UnsupportedOperationException
209    {
210      throw new UnsupportedOperationException();
211    }
212 
213    public void clear() {
214      KDTree.this.clear();
215    }
216 
217    public boolean containsAll(Collection<?> c) {
218      for(Object o : c) {
219        if(!contains(o))
220          return false;
221      }
222      return true;
223    }
224 
225    public int hashCode() {
226      return KDTree.this.hashCode();
227    }
228 
229    public boolean isEmpty() {
230      return KDTree.this.isEmpty();
231    }
232 
233    public int size() {
234      return KDTree.this.size();
235    }
236 
237    public Object[] toArray() {
238      Object[] obja = new Object[size()];
239      int i=0;
240 
241      for(E e : this) {
242        obja[i] = e;
243        ++i;
244      }
245 
246      return obja;
247    }
248 
249    public <T> T[] toArray(T[] a) {
250      Object[] array = a;
251 
252      if(array.length < size())
253        array = a =
254          (T[])Array.newInstance(a.getClass().getComponentType(), size());
255 
256      if(array.length > size())
257        array[size()] = null;
258 
259      int i = 0;
260      for(E e : this) {
261        array[i] = e;
262        ++i;
263      }
264 
265      return a;
266    }
267  }
268 
269  abstract class SetView<E> extends CollectionView<E> implements Set<E> {
270    public boolean equals(Object o) {
271      if(!(o instanceof Set))
272        return false;
273 
274      if(o == this)
275        return true;
276 
277      Set<?> set = (Set<?>)o;
278 
279      if(set.size() != size())
280        return false;
281 
282      try {
283        return containsAll(set);
284      } catch(ClassCastException cce) {
285        return false;
286      }
287    }
288  }
289 
290  final class MapEntrySet extends SetView<Map.Entry<P,V>>
291  {
292    public boolean contains(Object o)
293      throws ClassCastException, NullPointerException
294    {
295      Map.Entry<P,V> e = (Map.Entry<P,V>)o;
296      KDNode node = getNode(e.getKey());
297 
298      if(node == null)
299        return false;
300 
301      return e.getValue().equals(node.getValue());
302    }
303 
304    public Iterator<Map.Entry<P,V>> iterator() {
305      return new MapEntryIterator();
306    }
307 
308    public boolean remove(Object o)
309      throws ClassCastException
310    {
311      int size = size();
312      Map.Entry<P,V> e = (Map.Entry<P,V>)o;
313 
314      KDTree.this.remove(e.getKey());
315 
316      return (size != size());
317    }
318 
319    public boolean removeAll(Collection<?> c)
320      throws ClassCastException
321    {
322      int size = size();
323 
324      for(Object o : c) {
325        Map.Entry<P,V> e = (Map.Entry<P,V>)o;
326        KDTree.this.remove(e.getKey());
327      }
328 
329      return (size != size());
330    }
331 
332    public boolean retainAll(Collection<?> c)
333      throws ClassCastException
334    {
335      for(Object o : c) {
336        if(contains(o)) {
337          Collection<Map.Entry<P,V>> col = (Collection<Map.Entry<P,V>>)c;
338          clear();
339          for(Map.Entry<P,V> e : col)
340            put(e.getKey(), e.getValue());
341          return true;
342        }
343      }
344      return false;
345    }
346  }
347 
348  final class KeySet extends SetView<P> {
349 
350    public boolean contains(Object o)
351      throws ClassCastException, NullPointerException
352    {
353      return KDTree.this.containsKey(o);
354    }
355 
356    public Iterator<P> iterator() {
357      return new KeyIterator(new MapEntryIterator());
358    }
359 
360    public boolean remove(Object o)
361      throws ClassCastException
362    {
363      int size = size();
364      KDTree.this.remove(o);
365      return (size != size());
366    }
367 
368    public boolean removeAll(Collection<?> c)
369      throws ClassCastException
370    {
371      int size = size();
372 
373      for(Object o : c)
374        KDTree.this.remove(o);
375 
376      return (size != size());
377    }
378 
379    public boolean retainAll(Collection<?> c)
380      throws ClassCastException
381    {
382      HashMap<P,V> map = new HashMap<P,V>();
383      int size = size();
384 
385      for(Object o : c) {
386        V val = get(o);
387 
388        if(val != null || contains(o))
389          map.put((P)o, val);
390      }
391 
392      clear();
393      putAll(map);
394 
395      return (size != size());
396    }
397  }
398 
399  final class ValueCollection extends CollectionView<V> {
400 
401    public boolean contains(Object o)
402      throws ClassCastException, NullPointerException
403    {
404      return KDTree.this.containsValue(o);
405    }
406 
407    public Iterator<V> iterator() {
408      return new ValueIterator(new MapEntryIterator());
409    }
410 
411    public boolean remove(Object o)
412      throws ClassCastException
413    {
414      KDNode node = findValue(_root, o);
415 
416      if(node != null) {
417        KDTree.this.remove(node.getKey());
418        return true;
419      }
420 
421      return false;
422    }
423 
424    public boolean removeAll(Collection<?> c)
425      throws ClassCastException
426    {
427      int size = size();
428 
429      for(Object o : c) {
430        KDNode node = findValue(_root, o);
431 
432        while(node != null) {
433          KDTree.this.remove(o);
434          node = findValue(_root, o);
435        }
436      }
437 
438      return (size != size());
439    }
440 
441    public boolean retainAll(Collection<?> c)
442      throws ClassCastException
443    {
444      HashMap<P,V> map = new HashMap<P,V>();
445      int size = size();
446 
447      for(Object o : c) {
448        KDNode node = findValue(_root, o);
449 
450        while(node != null) {
451          map.put(node.getKey(), node.getValue());
452          node = findValue(_root, o);
453        }
454      }
455 
456      clear();
457      putAll(map);
458 
459      return (size != size());
460    }
461  }
462 
463  int _size, _hashCode, _dimensions;
464  KDNode _root;
465 
466  KDNode getNode(P point, KDNode[] parent) {
467    int discriminator;
468    KDNode node = _root, current, last = null;
469    Coord c1, c2;
470 
471    while(node != null) {
472      discriminator = node._discriminator;
473      c1 = point.getCoord(discriminator);
474      c2 = node._point.getCoord(discriminator);
475      current = node;
476 
477      if(c1.compareTo(c2) > 0)
478        node = node._high;
479      else if(c1.compareTo(c2) < 0)
480        node = node._low;
481      else if(node._point.equals(point)) {
482        if(parent != null)
483          parent[0] = last;
484        return node;
485      } else
486        node = node._high;
487 
488      last = current;
489    }
490 
491    if(parent != null)
492      parent[0] = last;
493 
494    return null;
495  }
496 
497  KDNode getNode(P point) {
498    return getNode(point, null);
499  }
500 
501  KDNode getMinimumNode(KDNode node, KDNode p, int discriminator,
502                        KDNode[] parent)
503  {
504    KDNode result;
505 
506    if(discriminator == node._discriminator) {
507      if(node._low != null)
508        return getMinimumNode(node._low, node, discriminator, parent);
509      else
510        result = node;
511    } else {
512      KDNode nlow = null, nhigh = null;
513      KDNode[] plow = new KDTree.KDNode[1], phigh = new KDTree.KDNode[1];
514 
515      if(node._low != null)
516        nlow = getMinimumNode(node._low, node, discriminator, plow);
517 
518      if(node._high != null)
519        nhigh = getMinimumNode(node._high, node, discriminator, phigh);
520 
521      if(nlow != null && nhigh != null) {
522        if(nlow._point.getCoord(discriminator).compareTo(nhigh._point.getCoord(discriminator)) < 0) {
523          result    = nlow;
524          parent[0] = plow[0];
525        } else {
526          result    = nhigh;
527          parent[0] = phigh[0];
528        }
529      } else if(nlow != null) {
530        result    = nlow;
531        parent[0] = plow[0];
532      } else if(nhigh != null) {
533        result    = nhigh;
534        parent[0] = phigh[0];
535      } else
536        result = node;
537    }
538 
539    if(result == node)
540      parent[0] = p;
541    else if(node._point.getCoord(discriminator).compareTo(result._point.getCoord(discriminator)) < 0) {
542      result    = node;
543      parent[0] = p;
544    }
545 
546    return result;
547  }
548 
549  KDNode recursiveRemoveNode(KDNode node) {
550    int discriminator;
551 
552    if(node._low == null && node._high == null)
553      return null;
554    else
555      discriminator = node._discriminator;
556 
557    if(node._high == null) {
558      node._high = node._low;
559      node._low = null;
560    }
561 
562    KDNode[] parent = new KDTree.KDNode[1];
563    KDNode newRoot =
564      getMinimumNode(node._high, node, discriminator, parent);
565    KDNode child = recursiveRemoveNode(newRoot);
566 
567    if(parent[0]._low == newRoot)
568      parent[0]._low = child;
569    else
570      parent[0]._high = child;
571 
572    newRoot._low  = node._low;
573    newRoot._high = node._high;
574    newRoot._discriminator = node._discriminator;
575 
576    return newRoot;
577  }
578 
579  KDNode findValue(KDNode node, Object value) {
580    if(node == null || (value == null ? node.getValue() == null :
581                        value.equals(node.getValue())))
582      return node;
583 
584    KDNode result;
585 
586    if((result = findValue(node._low, value)) == null)
587      result = findValue(node._high, value);
588 
589    return result;
590  }
591 
592  boolean isInRange(P point, P lower, P upper) {
593    Coord coordinate1, coordinate2 = null, coordinate3 = null;
594 
595    if(lower != null || upper != null) {
596      int dimensions;
597      dimensions = point.getDimensions();
598 
599      for(int i = 0; i < dimensions; ++i) {
600        coordinate1 = point.getCoord(i);
601        if(lower != null)
602          coordinate2 = lower.getCoord(i);
603        if(upper != null)
604          coordinate3 = upper.getCoord(i);
605        if((coordinate2 != null && coordinate1.compareTo(coordinate2) < 0) ||
606           (coordinate3 != null && coordinate1.compareTo(coordinate3) > 0))
607          return false;
608      }
609    }
610 
611    return true;
612  }
613 
614  /**
615   * Creates a two-dimensional KDTree.
616   */
617  public KDTree() {
618    this(2);
619  }
620 
621  /**
622   * Creates a KDTree of the specified number of dimensions.
623   *
624   * @param dimensions The number of dimensions.  Must be greater than 0.
625   */
626  public KDTree(int dimensions) {
627    assert(dimensions > 0);
628    _dimensions = dimensions;
629    clear();
630  }
631 
632  // Begin Map interface methods
633 
634  /**
635   * Removes all elements from the container, leaving it empty.
636   */
637  public void clear() {
638    _root = null;
639    _size = _hashCode = 0;
640  }
641 
642  /**
643   * Returns true if the container contains a mapping for the specified key.
644   *
645   * @param key The point key to search for.
646   * @return true if the container contains a mapping for the specified key.
647   * @exception ClassCastException if the key is not an instance of P.
648   */
649  public boolean containsKey(Object key)
650    throws ClassCastException
651  {
652    return (getNode((P)key) != null);
653  }
654 
655  /**
656   * Returns true if the container contains a mapping with the specified value.
657   * Note: this is very inefficient for KDTrees because it requires searching
658   * the entire tree.
659   *
660   * @param value The value to search for.
661   * @return true If the container contains a mapping with the specified value.
662   */
663  public boolean containsValue(Object value) {
664    return (findValue(_root, value) != null);
665  }
666 
667  /**
668   * Returns a Set view of the point to value mappings in the KDTree.
669   * Modifications to the resulting set will be reflected in the KDTree
670   * and vice versa, except that {@code Iterator.remove} is not supported.
671   *
672   * @return A Set view of the point to value mappings in the KDTree.
673   */
674  public Set<Map.Entry<P,V>> entrySet() {
675    return new MapEntrySet();
676  }
677 
678  /**
679   * Returns true if the object contains the same mappings, false if not.
680   *
681   * @param o The object to test for equality.
682   * @return true if the object contains the same mappings, false if not.
683   */
684  public boolean equals(Object o)
685    throws ClassCastException
686  {
687    if(!(o instanceof Map))
688      return false;
689 
690    if(o == this)
691      return true;
692 
693    Map map = (Map)o;
694 
695    return (entrySet().equals(map.entrySet()));
696  }
697 
698  /**
699   * Retrieves the value at the given location.
700   *
701   * @param point The location from which to retrieve the value.
702   * @return The value at the given location, or null if no value is present.
703   * @exception ClassCastException If the given point is not of the
704   * expected type.
705   */
706  public V get(Object point)
707    throws ClassCastException
708  {
709    KDNode node = getNode((P)point);
710 
711    return (node == null ? null : node.getValue());
712  }
713 
714  /**
715   * Returns the hash code value for this map.
716   *
717   * @return The sum of the hash codes of all of the map entries.
718   */
719  public int hashCode() {
720    return _hashCode;
721  }
722 
723  /**
724   * Returns true if the container has no elements, false if it
725   * contains one or more elements.
726   *
727   * @return true if the container has no elements, false if it
728   * contains one or more elements.
729   */
730  public boolean isEmpty() {
731    return (_root == null);
732  }
733 
734  /**
735   * Returns a Set view of the point keys for the mappings in the
736   * KDTree.  Changes to the Set are reflected in the KDTree and vice
737   * versa, except that {@code Iterator.remove} is not supported.
738   *
739   * @return A Set view of the point keys for the mappings in the KDTree.
740   */
741  public Set<P> keySet() {
742    return new KeySet();
743  }
744 
745  /**
746   * Inserts a point value pair into the tree, preserving the
747   * spatial ordering.
748   *
749   * @param point The point serving as a key.
750   * @param value The value to insert at the point.
751   * @return The old value if an existing value is replaced by the
752   * inserted value.
753   */
754  public V put(P point, V value) {
755    KDNode[] parent = new KDTree.KDNode[1];
756    KDNode node = getNode(point, parent);
757    V old = null;
758 
759    if(node != null) {
760      old = node.getValue();
761      _hashCode-=node.hashCode();
762      node._value = value;
763    } else {
764      if(parent[0] == null)
765        node = _root = new KDNode(0, point, value); 
766      else {
767        int discriminator = parent[0]._discriminator;
768 
769        if(point.getCoord(discriminator).compareTo(
770                          parent[0]._point.getCoord(discriminator)) >= 0)
771          node = parent[0]._high =
772            new KDNode((discriminator + 1) % _dimensions, point, value);
773        else
774          node = parent[0]._low =
775            new KDNode((discriminator + 1) % _dimensions, point, value);
776      }
777 
778      ++_size;
779    }
780 
781    _hashCode+=node.hashCode();
782 
783    return old;
784  }
785 
786  /**
787   * Copies all of the point-value mappings from the given Map into the KDTree.
788   *
789   * @param map The Map from which to copy the mappings.
790   */
791  public void putAll(Map<? extends P, ? extends V> map) {
792    for(Map.Entry<? extends P, ? extends V> pair : map.entrySet())
793      put(pair.getKey(), pair.getValue());
794  }
795 
796  /**
797   * Removes the point-value mapping corresponding to the given point key.
798   *
799   * @param key The point key of the mapping to remove.
800   * @return The value part of the mapping, if a mapping existed and
801   * was removed.  Null if not.
802   * @exception ClassCastException If the key is not an instance of P.
803   */
804  public V remove(Object key)
805    throws ClassCastException
806  {
807    KDNode[] parent = new KDTree.KDNode[1];
808    KDNode node = getNode((P)key, parent);
809    V old = null;
810 
811    if(node != null) {
812      KDNode child = node;
813 
814      node = recursiveRemoveNode(child);
815 
816      if(parent[0] == null)
817        _root = node;
818      else if(child == parent[0]._low)
819        parent[0]._low = node;
820      else if(child == parent[0]._high)
821        parent[0]._high = node;
822 
823      --_size;
824      _hashCode-=child.hashCode();
825      old = child.getValue();
826    }
827 
828    return old;
829  }
830 
831  /**
832   * Returns the number of point-value mappings in the KDTree.
833   *
834   * @return The number of point-value mappings in the KDTree.
835   */
836  public int size() {
837    return _size;
838  }
839 
840  /**
841   * Returns a Collection view of the values contained in the KDTree.
842   * Changes to the Collection are reflected in the KDTree and vice versa.
843   * Note: the resulting Collection is very inefficient.
844   *
845   * @return A Collection view of the values contained in the KDTree.
846   */
847  public Collection<V> values() {
848    return new ValueCollection();
849  }
850 
851  // End Map interface methods
852 
853  public Iterator<Map.Entry<P,V>> iterator(P lower, P upper) {
854    return new MapEntryIterator(lower, upper);
855  }
856 
857  int fillArray(KDNode[] a, int index, KDNode node) {
858    if(node == null)
859      return index;
860    a[index] = node;
861    index = fillArray(a, index + 1, node._low);
862    return fillArray(a, index, node._high);
863  }
864 
865  final class NodeComparator implements Comparator<KDNode> {
866    int _discriminator = 0;
867 
868    void setDiscriminator(int val) {
869      _discriminator = val;
870    }
871 
872    int getDiscriminator() {
873      return _discriminator;
874    }
875 
876    public int compare(KDNode n1, KDNode n2) {
877      return
878        n1._point.getCoord(_discriminator).compareTo(n2._point.getCoord(_discriminator));
879    }
880  }
881 
882  KDNode optimize(KDNode[] nodes, int begin, int end, NodeComparator comp) {
883    KDNode midpoint= null;
884    int size = end - begin;
885 
886    if(size > 1) {
887      int nth = begin + (size >> 1);
888      int nthprev = nth - 1;
889      int d = comp.getDiscriminator();
890 
891      Arrays.sort(nodes, begin, end, comp);
892 
893      while(nth > begin &&
894            nodes[nth]._point.getCoord(d).compareTo(
895                                    nodes[nthprev]._point.getCoord(d)) == 0)
896        {
897          --nth;
898          --nthprev;
899        }
900 
901      midpoint = nodes[nth];
902      midpoint._discriminator = d;
903 
904      if(++d >= _dimensions)
905        d = 0;
906 
907      comp.setDiscriminator(d);
908 
909      midpoint._low = optimize(nodes, begin, nth, comp);
910 
911      comp.setDiscriminator(d);
912 
913      midpoint._high = optimize(nodes, nth + 1, end, comp);
914    } else if(size == 1) {
915      midpoint = nodes[begin];
916      midpoint._discriminator = comp.getDiscriminator();
917      midpoint._low = midpoint._high = null;
918    }
919 
920    return midpoint;
921  }
922 
923  /**
924   * Optimizes the performance of future search operations by balancing the
925   * KDTree.  The balancing operation is relatively expensive, but can
926   * significantly improve the performance of searches.  Usually, you
927   * don't have to optimize a tree which contains random key values
928   * inserted in a random order.
929   */
930  public void optimize() {
931    if(isEmpty())
932      return;
933 
934    KDNode[] nodes =
935      (KDNode[])Array.newInstance(KDNode.class, size());
936    fillArray(nodes, 0, _root);
937 
938    _root = optimize(nodes, 0, nodes.length, new NodeComparator());
939  }
940}

[all classes][com.savarese.algorithms.spatial]
Savarese Software Research