/* 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.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.filters.Filter;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.cursors.sources.Enumerator;
import xxl.core.cursors.unions.Sequentializer;
import xxl.core.functions.AbstractFunction;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
/** An <tt>RTree</tt> implementing the quadratic 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 QuadraticRTree 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 quadratic split-algorithm.
*
* @see RTree.Node
*/
public class Node extends RTree.Node {
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo split (final Stack path) {
final Node node = (Node)node(path);
int number = node.number(), minNumber = node.splitMinNumber(), maxNumber = node.splitMaxNumber();
Iterator seeds = new Sequentializer(
new Mapper(
new AbstractFunction() {
int index = 0;
public Object invoke (final Object entry1) {
return new Mapper(
new AbstractFunction() {
public Object invoke (Object entry2) {
return new Object[] {entry1, entry2};
}
}
,((List)node.entries).listIterator(++index));
}
}
,node.entries())
);
final Object [] seed = (Object[])Cursors.maxima(seeds,
new AbstractFunction() {
public Object invoke (Object seed) {
Rectangle rectangle0 = rectangle(((Object[])seed)[0]);
Rectangle rectangle1 = rectangle(((Object[])seed)[1]);
return new Double(Descriptors.union(rectangle0, rectangle1).area()
-rectangle0.area()-rectangle1.area());
}
}
).getFirst();
final Rectangle [] nodesMBRs = new Rectangle[] {new DoublePointRectangle(rectangle(seed[0])), new DoublePointRectangle(rectangle(seed[1]))};
final Collection [] nodesEntries = new Collection[] {node.entries, this.entries};
final List remainingEntries = Cursors.toList(
new Filter(node.entries(),
new AbstractPredicate() {
public boolean invoke (Object entry) {
return entry!=seed[0] && entry!=seed[1];
}
}
),
new ArrayList(number-2)
);
node.entries.clear();
for (int i=0; i<2; nodesEntries[i].add(seed[i++]));
while (!remainingEntries.isEmpty() && node.number()!=maxNumber && number-number()!=minNumber) {
int entryIndex = ((Integer)Cursors.maxima(new Enumerator(remainingEntries.size()),
new AbstractFunction() {
public Object invoke (Object object) {
Rectangle rectangle = rectangle(remainingEntries.get(((Integer)object).intValue()));
return new Double(Math.abs(
(Descriptors.union(nodesMBRs[1], rectangle).area()-nodesMBRs[1].area())-
(Descriptors.union(nodesMBRs[0], rectangle).area()-nodesMBRs[0].area())
));
}
}
).getFirst()).intValue();
Object entry = remainingEntries.set(entryIndex, remainingEntries.get(remainingEntries.size()-1));
Rectangle rectangle = rectangle(entry);
double areaEnlargementDifference, areaDifference;
int index = (areaEnlargementDifference =
(Descriptors.union(nodesMBRs[1], rectangle).area()-nodesMBRs[1].area())-
(Descriptors.union(nodesMBRs[0], rectangle).area()-nodesMBRs[0].area())
)<0 ||
areaEnlargementDifference==0 && (
(areaDifference = nodesMBRs[1].area()-nodesMBRs[0].area())<0 ||
areaDifference==0 && number()<node.number()
)? 1: 0;
nodesEntries[index].add(entry);
nodesMBRs[index].union(rectangle);
remainingEntries.remove(remainingEntries.size()-1);
}
if (!remainingEntries.isEmpty()) {
int index = node.number()==maxNumber? 1: 0;
for (Iterator entries = remainingEntries.iterator(); entries.hasNext();) {
Object entry = entries.next();
nodesEntries[index].add(entry);
nodesMBRs[index].union(rectangle(entry));
}
}
((IndexEntry)indexEntry(path)).descriptor = nodesMBRs[0];
return new SplitInfo(path).initialize(nodesMBRs[1]);
}
}
}