Savarese Software Research Corporation
libssrckdtree-j 1.0.2 Java Unit Test Coverage
[all classes][com.savarese.spatial]

COVERAGE SUMMARY FOR SOURCE FILE [KDTree.java]

nameclass, %method, %block, %line, %
KDTree.java100% (11/11)68%  (54/80)66%  (1106/1684)65%  (230.9/357)

COVERAGE BREAKDOWN BY CLASS AND METHOD

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

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