/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.indexStructures;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Stack;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.cursors.sources.Enumerator;
import xxl.core.functions.AbstractFunction;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
/** An <tt>RTree</tt> implementing the linear split-strategy proposed
* in the original R-Tree paper.
*
* For a detailed discussion see Guttman, A.: "R-trees: a dynamic index structure
* for spatial searching", Proc. ACM-SIGMOD Int. Conf. on Management of Data, 47-57, 1984.
*
* @see Tree
* @see ORTree
* @see RTree
*/
public class LinearRTree extends RTree {
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode (int level) {
return new Node().initialize(level, new LinkedList());
}
/** A modification of {@link RTree.Node node} implementing the linear split-algorithm.
*
* @see RTree.Node
*/
public class Node extends RTree.Node {
/**
* Performs an RTree split with complexity O(d*n).
* First, the dimension is chosen by maximizing a normalized
* separation distance that is computed for the rectangles
* that have the greatest distance in a dimension.
* Then, for the split dimension, the two most separated rectangles
* are the seeds for the distribution algorithm.
* Then, each rectangle (one after the other) is added to the partition so
* that the area enlargement of the MBRs of the partitions is minimized.
* Furthermore, the algorithm has to make a distribution so that
* the number of objects in the new nodes is between the minimal and maximal
* number.
*
* @param path the nodes already visited during this insert
* @return a <tt>SplitInfo</tt> containig all information needed about the split
*/
protected Tree.Node.SplitInfo split (final Stack path) {
final Node node = (Node)node(path);
final int dimensions = ((Rectangle)rootDescriptor()).dimensions();
int number = node.number(), minNumber = node.splitMinNumber(), maxNumber = node.splitMaxNumber();
Iterator seeds = new Mapper(
new AbstractFunction() {
Rectangle MBR = rectangle(indexEntry(path));
public Object invoke (Object object) {
final int dimension = ((Integer)object).intValue();
Object e1 = Cursors.minima(node.entries(),
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(rectangle(object).getCorner(true).getValue(dimension));
}
}
).getFirst();
Object e2 = Cursors.maxima(node.entries(),
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(rectangle(object).getCorner(false).getValue(dimension));
}
}
).getLast();
Double normalizedSeparation = new Double(
(rectangle(e2).getCorner(false).getValue(dimension)-rectangle(e1).getCorner(true).getValue(dimension))/
(MBR.getCorner(true).getValue(dimension)-MBR.getCorner(false).getValue(dimension))
);
return new Object [] {normalizedSeparation, e1, e2};
}
}
,new Enumerator(dimensions));
Object [] seed = (Object[])Cursors.maxima(seeds,
new AbstractFunction() {
public Object invoke (Object seed) {
return ((Object[])seed)[0];
}
}
).getFirst();
Rectangle oldNodesMBR = new DoublePointRectangle(rectangle(seed[1])), newNodesMBR = new DoublePointRectangle(rectangle(seed[2]));
this.entries.add(seed[2]); // this is definitely in this partition!
// the following line has to be here, because if entries.remove is called
// during iteration, it must be considered the case when seed[2] is the
// last object in the iteration (not very nice for the if-condition).
node.entries.remove(seed[2]); // object is already in the node's collection
int distrNumber=number-2; // nodes that still have to be distributed
double areaEnlargementDifference, areaDifference;
Rectangle rectangle;
Object entry;
for (Iterator entries = node.entries(); entries.hasNext();) {
entry = entries.next();
if (entry!=seed[1]) {
// now, entry is not one of the seed objects
rectangle = rectangle(entry);
if (
(node.number()>minNumber) && // still enough objects in the node?
(
node.number()==maxNumber ||
distrNumber+number()<=minNumber ||
(
number-number()!=minNumber && (
(areaEnlargementDifference =
(Descriptors.union(newNodesMBR, rectangle).area()-newNodesMBR.area())-
(Descriptors.union(oldNodesMBR, rectangle).area()-oldNodesMBR.area())
)<0 ||
areaEnlargementDifference==0 && (
(areaDifference = newNodesMBR.area()-oldNodesMBR.area())<0 ||
areaDifference==0 && number()<node.number()
)
)
)
)
) {
// putting objects into the new partition
this.entries.add(entry);
// removing it from the old one
entries.remove();
// compute the new descriptor
newNodesMBR.union(rectangle);
}
else
// compute the new descriptor
oldNodesMBR.union(rectangle);
// element was distributed
distrNumber--;
}
}
((IndexEntry)indexEntry(path)).descriptor = oldNodesMBR;
return new SplitInfo(path).initialize(newNodesMBR);
}
}
}