/*
* Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner,
* Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain,
* Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter,
* Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann,
* Samuel Zweifel
*
* This file is part of Jukefox.
*
* Jukefox is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or any later version. Jukefox is
* distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Jukefox. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.ethz.dcg.jukefox.commons.utils.kdtree;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map.Entry;
import java.util.Vector;
import ch.ethz.dcg.jukefox.commons.utils.Log;
import edu.wlu.cs.levy.CG.Checker;
import edu.wlu.cs.levy.CG.KDTree;
import edu.wlu.cs.levy.CG.KeyDuplicateException;
import edu.wlu.cs.levy.CG.KeySizeException;
public class AdvancedKdTree<I> implements Serializable {
private static final long serialVersionUID = -6342366349021652876L;
private static final String TAG = AdvancedKdTree.class.getSimpleName();
protected KDTree<KdTreeEntry<I>> kdTree;
protected int size;
protected int distinctCoordsCnt;
protected float initialMargin = 0.5f;
protected float margin;
protected int dim;
protected Hashtable<I, KdTreeEntry<I>> table;
protected int deletionCount = 0;
// as soon as deletionCount / size > rebuildThreshold, the tree is going
// to be reconstructed.
protected float rebuildThreshold = 0.3f;
protected int rebuildCount = 0;
public AdvancedKdTree(int dim) {
this.dim = dim;
margin = initialMargin;
initDataStructure();
}
public void insert(float[] key, I id) throws KeySizeException {
KdTreePoint<I> point = new KdTreePoint<I>(key, id);
insert(key, point);
}
public void insert(float[] key, KdTreePoint<I> point) throws KeySizeException {
if (key == point.getPosition()) {
key = key.clone();
}
KdTreeEntry<I> entry = new KdTreeEntry<I>(key, point);
try {
kdTree.insert(key, entry);
distinctCoordsCnt++;
} catch (KeyDuplicateException e) {
entry = kdTree.search(key);
entry.addPoint(point);
}
table.put(point.getID(), entry);
size++;
}
public LinkedList<KdTreePoint<I>> getRange(float[] lowerBounds, float[] upperBounds) throws KeySizeException {
Vector<KdTreeEntry<I>> entries = getExtendedRange(lowerBounds, upperBounds);
LinkedList<KdTreePoint<I>> points = new LinkedList<KdTreePoint<I>>();
for (int i = 0; i < entries.size(); i++) {
ListIterator<KdTreePoint<I>> it = entries.elementAt(i).getPoints().listIterator();
while (it.hasNext()) {
KdTreePoint<I> point = it.next();
if (rangeContainsPoint(lowerBounds, upperBounds, point)) {
points.add(point);
}
}
}
return points;
}
public LinkedList<KdTreePoint<I>> getAll() {
Iterator<Entry<I, KdTreeEntry<I>>> it = table.entrySet().iterator();
LinkedList<KdTreePoint<I>> points = new LinkedList<KdTreePoint<I>>();
while (it.hasNext()) {
Entry<I, KdTreeEntry<I>> entry = it.next();
ListIterator<KdTreePoint<I>> pointIt = entry.getValue().getPoints().listIterator();
while (pointIt.hasNext()) {
KdTreePoint<I> point = pointIt.next();
if (point.getID().equals(entry.getKey())) {
points.add(point);
}
}
}
return points;
}
public KdTreePoint<I> getPoint(I id) {
KdTreeEntry<I> entry = table.get(id);
if (entry == null) {
return null;
}
ListIterator<KdTreePoint<I>> it = entry.getPoints().listIterator();
KdTreePoint<I> point = it.next();
while (!point.getID().equals(id) && it.hasNext()) {
point = it.next();
}
return point;
}
/**
* Moves the point to a
*
* @return new key, null if ID does not exist.
*/
public float[] movePoint(I id, float[] newPosition) throws KeySizeException {
KdTreePoint<I> point = getPoint(id);
if (point == null) {
return null;
}
point.setPosition(newPosition);
KdTreeEntry<I> entry = table.get(id);
if (isOutsideMargin(point, entry)) {
if (entry.getPoints().size() > 1) {
entry.removePoint(id);
} else {
try {
kdTree.delete(entry.getKey());
deletionCount++;
if ((float) deletionCount / (float) size > rebuildThreshold) {
rebuild();
return table.get(id).getKey();
}
} catch (KeySizeException e) {
throw e;
} catch (Exception e) {
Log.w(TAG, "KeyMissingException in " + "AdvancedKDTree.movePoint(). " + "Should never happen...");
// System.exit(0);
}
}
table.remove(id);
insert(newPosition, point);
return newPosition;
}
return entry.getKey();
}
public Vector<KdTreeEntry<I>> getNearest(float[] p, int n) throws KeySizeException {
if (n == 0) {
return new Vector<KdTreeEntry<I>>(0);
}
// Object[] objects = kdTree.nearest(p, Math.min(distinctCoordsCnt, n));
List<KdTreeEntry<I>> objects = kdTree.nearest(p, Math.min(distinctCoordsCnt, n));
// Vector<I> ret = new Vector<I>();
Vector<KdTreeEntry<I>> entries = new Vector<KdTreeEntry<I>>(objects.size());
for (int i = 0; i < objects.size(); i++) {
KdTreeEntry<I> entry = objects.get(i);
entries.add(entry);
}
return entries;
}
public Vector<KdTreeEntry<I>> getNearest(float[] p, int n, Checker<KdTreeEntry<I>> c) throws KeySizeException {
if (n == 0) {
return new Vector<KdTreeEntry<I>>(0);
}
// Object[] objects = kdTree.nearest(p, Math.min(distinctCoordsCnt, n));
List<KdTreeEntry<I>> objects = kdTree.nearest(p, Math.min(distinctCoordsCnt, n), c);
// Vector<I> ret = new Vector<I>();
Vector<KdTreeEntry<I>> entries = new Vector<KdTreeEntry<I>>(objects.size());
for (int i = 0; i < objects.size(); i++) {
KdTreeEntry<I> entry = objects.get(i);
entries.add(entry);
}
return entries;
}
public Vector<KdTreePoint<I>> getNearestPoints(float[] p, int minN) throws KeySizeException {
minN = Math.min(minN, size);
Vector<KdTreePoint<I>> ret = new Vector<KdTreePoint<I>>();
Vector<KdTreeEntry<I>> entries = getNearest(p, minN);
int cnt = 0;
int i = 0;
while (cnt < minN) {
KdTreeEntry<I> entry = entries.get(i++);
for (KdTreePoint<I> point : entry.getPoints()) {
ret.add(point);
cnt++;
}
}
return ret;
}
public Vector<I> getNearestPoints(float[] p, int minN, Checker<KdTreeEntry<I>> c) throws KeySizeException {
Vector<I> ret = new Vector<I>();
Vector<KdTreeEntry<I>> entries = getNearest(p, minN, c);
int cnt = 0;
int i = 0;
while (cnt < minN) {
KdTreeEntry<I> entry = entries.get(i++);
for (KdTreePoint<I> point : entry.getPoints()) {
ret.add(point.getID());
cnt++;
}
}
return ret;
}
/**
* @see KDTree#nearestEuclidean(float[], float)
* @return The element nodes
*/
public Vector<KdTreePoint<I>> nearestEuclidean(float[] key, float dist) throws KeySizeException {
List<KdTreeEntry<I>> entries = kdTree.nearestEuclidean(key, dist);
return getIdsFromKdTreeEntryList(entries);
}
/**
* @see KDTree#nearestHamming(float[], float)
* @return The element nodes
*/
public Vector<KdTreePoint<I>> nearestHamming(float[] key, float dist) throws KeySizeException {
List<KdTreeEntry<I>> entries = kdTree.nearestHamming(key, dist);
return getIdsFromKdTreeEntryList(entries);
}
/**
* Reads the element points from the given {@link List<KdTreeEntry<I>>} into a {@link Vector<KdTreePoint<I>>}.
*
* @param entries
* The entries
* @return The entry points
*/
private Vector<KdTreePoint<I>> getIdsFromKdTreeEntryList(List<KdTreeEntry<I>> entries) {
Vector<KdTreePoint<I>> ret = new Vector<KdTreePoint<I>>();
for (KdTreeEntry<I> entry : entries) {
ret.addAll(entry.getPoints());
}
return ret;
}
public boolean contains(I item) {
return table.containsKey(item);
}
public int getSize() {
return size;
}
public int getDeletionCount() {
return deletionCount;
}
public int getRebuildCount() {
return rebuildCount;
}
public void setMargin(float margin) {
this.margin = margin;
}
public double getMaring() {
return margin;
}
protected void initDataStructure() {
kdTree = new KDTree<KdTreeEntry<I>>(dim);
table = new Hashtable<I, KdTreeEntry<I>>();
}
protected void rebuild() {
LinkedList<KdTreePoint<I>> points = getAll();
initDataStructure();
ListIterator<KdTreePoint<I>> it = points.listIterator();
while (it.hasNext()) {
KdTreePoint<I> point = it.next();
try {
insert(point.getPosition().clone(), point);
} catch (KeySizeException e) {
Log.w(TAG, "Should never happen. " + "AdvancedKDTree.rebuild");
}
}
deletionCount = 0;
rebuildCount++;
}
protected Vector<KdTreeEntry<I>> getExtendedRange(float[] lowerBounds, float[] upperBounds) throws KeySizeException {
float[] lower = new float[lowerBounds.length];
float[] upper = new float[upperBounds.length];
for (int i = 0; i < lowerBounds.length; i++) {
lower[i] = lowerBounds[i] - margin;
upper[i] = upperBounds[i] + margin;
}
// Object[] objects = kdTree.range(lower, upper);
List<KdTreeEntry<I>> objects = kdTree.range(lower, upper);
Vector<KdTreeEntry<I>> entries = new Vector<KdTreeEntry<I>>(objects.size());
for (int i = 0; i < objects.size(); i++) {
entries.add(objects.get(i));
}
return entries;
}
protected boolean rangeContainsPoint(float[] lowerBounds, float[] upperBounds, KdTreePoint<I> point) {
float[] pos = point.getPosition();
for (int i = 0; i < lowerBounds.length; i++) {
if (pos[i] < lowerBounds[i] || pos[i] > upperBounds[i]) {
return false;
}
}
return true;
}
protected boolean isOutsideMargin(KdTreePoint<I> point, KdTreeEntry<I> entry) {
float[] pos = point.getPosition();
float[] key = entry.getKey();
for (int i = 0; i < pos.length; i++) {
if (pos[i] > key[i] + margin || pos[i] < key[i] - margin) {
return true;
}
}
return false;
}
}