Commit 798505e0 authored by Daniel Eggert's avatar Daniel Eggert
Browse files

added RE tree implementation to new datastructure package

parent 5ca192e5
package de.potsdam.gfz.gms.utils.datastructures.trees.rtree;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicInteger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.aux.Envelope;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.aux.InterruptibleNodeVisitor;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.aux.ItemVisitor;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.aux.NodeVisitor;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.aux.Tree;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.InnerNode;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.Leaf;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.MultiSegmentLine;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.MultiSegmentLine.Segment;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.Node;
import de.potsdam.gfz.gms.utils.datastructures.trees.rtree.model.Vector;
/**
* An abstract RTree with methode for all types of RTrees
*
* @author Jano Espenhahn (jano.espenhahn@gfz-potsdam.de)
* @since 21.10.2016
*
*/
public abstract class AbstractRTree<T> implements Tree<T> {
/** Logger */
protected final Logger LOG = LoggerFactory.getLogger(this.getClass());
/** the number of dimensions of the SimpleRTree */
protected final int dimension;
/** minimum number of entries */
protected final int minEntries;
/** maximum number of entries */
protected final int maxEntries;
/** default minimum number of entries */
protected static final int DEFAULT_MIN_ENTRIES = 2;
/** default maximal number of entries */
protected static final int DEFAULT_MAX_ENTRIES = 10;
private static final double OVERLAP_SAMPLING_RATE = 0.05;
/**
* Number of available cpus
*/
private static final int NUM_CPUS = Runtime.getRuntime().availableProcessors();
/** the root node */
protected InnerNode root = null;
/** number of items */
protected int size = 0;
/** global pool */
private static ForkJoinPool pool = null; // new
// ForkJoinPool();
// //
// new
// ForkJoinPool();
/** count of visited nodes */
private final static AtomicInteger visitedNodeCount = new AtomicInteger(0);
private NodeWorker[] worker;
private class NodeWorker implements Runnable, ItemVisitor<T> {
private final Semaphore trigger = new Semaphore(0);
private final Semaphore finish = new Semaphore(0);
private final List<T> results = new ArrayList<>();
private Node node = null;
private Envelope env = null;
private Thread thread;
public void search(Node n, Envelope e) {
node = n;
env = e;
results.clear();
trigger.release();
}
public void waitFor() {
try {
finish.acquire();
} catch (InterruptedException e) {
}
}
@Override
public void run() {
thread = Thread.currentThread();
while (!thread.isInterrupted()) {
// wait for a search request
try {
trigger.acquire();
} catch (InterruptedException e) {
return;
}
// execute search request
AbstractRTree.this.search(env, node, this);
// signal search done
finish.release();
node = null;
env = null;
}
}
@Override
public void visitItem(T item) {
results.add(item);
}
public List<T> getResults() {
return results;
}
public void shutdown() {
if (thread != null) {
thread.interrupt();
}
}
}
/** default constructor */
public AbstractRTree() {
this(DEFAULT_MIN_ENTRIES, DEFAULT_MAX_ENTRIES, 1);
}
public AbstractRTree(int minEntries, int maxEntries, int dimension) {
this.minEntries = minEntries;
this.maxEntries = maxEntries;
this.dimension = dimension;
}
/**
*
*/
@SuppressWarnings("unchecked")
private void initWorker() {
if (worker == null) {
worker = new AbstractRTree.NodeWorker[maxEntries];
for (int i = 0; i < maxEntries; ++i) {
worker[i] = new NodeWorker();
new Thread(worker[i]).start();
}
}
}
private void shutdownWorker() {
if (worker != null) {
for (NodeWorker w : worker) {
w.shutdown();
}
worker = null;
}
}
public int getMinEntries() {
return minEntries;
}
public int getMaxEntries() {
return maxEntries;
}
@Override
public InnerNode getRoot() {
return root;
}
@Override
public int getSize() {
return size;
}
@Override
public int getTreeHeight() {
return root != null ? root.getLevel() : 0;
}
@Override
public void clear() {
root = null;
size = 0;
shutdownWorker();
if (pool != null) {
pool.shutdownNow();
pool = null;
}
}
@Override
public void build() {
// update the subtree sizes of all nodes
updateSubtreeSizes();
// init parallel worker
initWorker();
// init fork join pool
pool = new ForkJoinPool();
}
protected void updateSubtreeSizes() {
postOrderVisit(new NodeVisitor() {
@Override
public void visitNode(Node node) {
// skip leafs
if (node.getLevel() > 0) {
((InnerNode) node).updateSubtreeSize();
}
}
});
}
public void postOrderVisit(NodeVisitor v) {
if (root != null) {
postOrderVisit(v, root);
}
}
/**
* Traverses the tree in post-order. l-r-N
*
* @param v
* @param n
*/
private void postOrderVisit(NodeVisitor v, Node n) {
if (n.getLevel() > 0) {
// this is an inner node, visit children first
for (Node child : ((InnerNode) n).getChildren()) {
postOrderVisit(v, child);
}
}
// we visited all children, now visit the node it self
v.visitNode(n);
}
@Override
public void search(Envelope env, ItemVisitor<T> visitor) {
if (env.getDimension() == dimension) {
visitedNodeCount.set(0);
search(env, root, visitor);
} else {
throw new IllegalArgumentException("There are different dimensions. The envelopes are not comparable.");
}
}
@SuppressWarnings("unchecked")
private void search(Envelope env, Node node, ItemVisitor<T> visitor) {
// visitedNodeCount.incrementAndGet();
if (node.getEnv().intersects(env)) {
if (node.getLevel() == 0) {
visitor.visitItem(((Leaf<T>) node).getItem());
} else {
for (Node n : ((InnerNode) node).getChildren()) {
search(env, n, visitor);
}
}
}
}
@Override
public List<T> search(Envelope env) {
final List<T> results = new ArrayList<>();
search(env, new ItemVisitor<T>() {
@Override
public void visitItem(T item) {
results.add(item);
}
});
return results;
}
@Override
public List<T> parallelForkJoinSearch(Envelope env) {
// use a thread safe list -> vector
final List<T> results = new CopyOnWriteArrayList<>();
parallelForkJoinSearch(env, new ItemVisitor<T>() {
@Override
public void visitItem(T item) {
results.add(item);
}
});
return results;
}
/**
* returns the relative overlapping volume of a tree level
*
* @param level
* @return
*/
public List<Double> getRelativeOverlappingVolume(int level) {
List<Double> resultList;
if (level > root.getLevel()) {
resultList = new ArrayList<>(1);
resultList.add(0.0);
} else if (level == root.getLevel()) {
resultList = new ArrayList<>(1);
resultList.add(1.0);
} else {
List<Node> nodes = searchNode(level);
resultList = new ArrayList<>(nodes.size());
for (int i = 0; i < nodes.size() - 1; i++) {
for (int j = i + 1; j < nodes.size(); j++) {
resultList.add(nodes.get(i).getEnv().getOverlappingVolume(nodes.get(j).getEnv()) / root.getEnv().getVolume());
}
}
}
return resultList;
}
public List<Node> searchNode(int level) {
final List<Node> results = new ArrayList<>();
searchNode(level, new NodeVisitor() {
@Override
public void visitNode(Node node) {
results.add(node);
}
});
return results;
}
public void searchNode(int level, NodeVisitor visitor) {
searchNode(level, level, root, visitor);
}
public void searchNode(int minLevel, int maxLevel, NodeVisitor visitor) {
searchNode(minLevel, maxLevel, root, visitor);
}
private void searchNode(int minLevel, int maxLevel, Node node, NodeVisitor visitor) {
final int l = node.getLevel();
if (l >= minLevel && l <= maxLevel) {
visitor.visitNode(node);
}
if (l > minLevel) {
for (Node n : ((InnerNode) node).getChildren()) {
searchNode(minLevel, maxLevel, n, visitor);
}
}
}
public void searchNode(Envelope envelope, int level, InterruptibleNodeVisitor visitor) {
if (root != null && root.getEnv().intersects(envelope)) {
searchNode(envelope, level, root, visitor);
}
}
private boolean searchNode(Envelope envelope, int level, Node node, InterruptibleNodeVisitor visitor) {
if (node.getLevel() == level) {
return visitor.visitNode(node);
} else {
for (Node n : ((InnerNode) node).getChildren()) {
if (n.getEnv().intersects(envelope)) {
if (!searchNode(envelope, level, n, visitor)) {
return false;
}
}
}
return true;
}
}
public double getAverageOverlappingVolume(int level) {
List<Double> volumes = getRelativeOverlappingVolume(level);
double addedVolume = 0;
int count = 0;
while (count < volumes.size()) {
addedVolume += volumes.get(count);
count++;
}
return (addedVolume / count);
}
/*
* (non-Javadoc)
*
* @see de.potsdam.gfz.index.playground.trees.Tree#getLevel1AverageDeadSpacePerDim()
*/
@Override
public double getLevel1AverageDeadSpacePerDim() {
if (root == null) {
return 0;
}
final Vector rootExtent = root.getEnv().getExtent();
final double[] sum = new double[] { 0, 0 };
searchNode(1, new NodeVisitor() {
@Override
public void visitNode(Node node) {
Envelope env = node.getEnv();
final Segment s = new Segment();
// determine dimension segments
MultiSegmentLine[] dimSegs = new MultiSegmentLine[dimension];
// init with node extends
for (int dim = 0; dim < dimension; ++dim) {
dimSegs[dim] = new MultiSegmentLine(env.getMin()[dim], env.getMax()[dim]);
}
// remove child extensions segments to determine dead space
for (Node child : ((InnerNode) node).getChildren()) {
Envelope childEnv = child.getEnv();
for (int dim = 0; dim < dimension; ++dim) {
s.start = childEnv.getMin()[dim];
s.end = childEnv.getMax()[dim];
dimSegs[dim].remove(s);
}
}
for (int dim = 0; dim < dimension; ++dim) {
double extent = rootExtent.get(dim);
if (extent != 0.0) {
sum[0] += dimSegs[dim].getLength() / extent / root.getChildren().size();
}
++sum[1];
}
}
});
return sum[0] / sum[1];
}
/*
* (non-Javadoc)
*
* @see de.potsdam.gfz.index.playground.trees.Tree#getLevel1EnvelopeMargin()
*/
@Override
public double getLevel1EnvelopeMargin() {
Vector avgRootEdge = root.getEnv().getExtent();
List<Node> leafs = searchNode(1);
double sumLeafs = 0;
for (int i = 0; i < leafs.size(); ++i) {
for (int j = 0; j < avgRootEdge.getDimension(); ++j) {
double avgRootEdgeDim = avgRootEdge.get(j);
if (avgRootEdgeDim != 0.0) {
sumLeafs += leafs.get(i).getEnv().getExtent().get(j) / avgRootEdgeDim;
}
}
}
return sumLeafs / leafs.size() / dimension;
}
@Override
public double getLevel1Overlap() {
// return getOverlap(1, OVERLAP_SAMPLING_RATE);
double[] overlap = new double[] { 0, 0 };
searchNode(2, root.getLevel() - 1, new NodeVisitor() {
@Override
public void visitNode(Node node) {
overlap[0] += ((InnerNode) node).determineOverlapBySampling(OVERLAP_SAMPLING_RATE);
++overlap[1];
}
});
if (overlap[1] == 0.0) {
return 0;
} else {
return overlap[0] / overlap[1];
}
}
public double getOverlap(int level, double samplingRate) {
if (root == null) {
return 0;
}
// resulting overlap 0..1 (first) and number of overlapping children (second)
final double[] overlap = new double[] { 0, 0 };
// this nodes envelope
final Envelope env = root.getEnv();
final double[] min = env.getMin();
final double[] max = env.getMax();
final int dim = env.getDimension();
// number of samples per dimension
final double numSamplesPerDim = 1.0 / samplingRate + 1;
final double numSamples = Math.pow(numSamplesPerDim, dim);
// increment for each sample step per dimension
final double[] sampleIncrements = new double[dim];
final long[] sampleDimMods = new long[dim];
for (int d = 0; d < dim; ++d) {
sampleIncrements[d] = samplingRate * (max[d] - min[d]);
sampleDimMods[d] = (long) Math.pow(numSamplesPerDim, d);
}
// start sampling at min
final double[] samplePoint = Arrays.copyOf(min, dim);
Envelope sample = new Envelope(samplePoint, samplePoint);
// sample all values
for (long i = 1; i <= numSamples; ++i) {
// check number of children containing the sample point
overlap[1] = 0;
searchNode(sample, level, new InterruptibleNodeVisitor() {
@Override
public boolean visitNode(Node node) {
// the corresponding level-1 node contains the sample point
if (++overlap[1] > 1) {
// more than one child contains the sample point - increase overlap count
++overlap[0];
return false;
}
return true;
}
});