/* 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.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import xxl.core.collections.MapEntry;
import xxl.core.collections.ReversedList;
import xxl.core.collections.containers.Container;
import xxl.core.comparators.ComparableComparator;
import xxl.core.comparators.FeatureComparator;
import xxl.core.cursors.AbstractCursor;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.Cursors;
import xxl.core.cursors.filters.Filter;
import xxl.core.cursors.identities.TeeCursor;
import xxl.core.cursors.mappers.Mapper;
import xxl.core.cursors.sources.EmptyCursor;
import xxl.core.cursors.sources.Enumerator;
import xxl.core.cursors.sources.SingleObjectCursor;
import xxl.core.cursors.unions.Sequentializer;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.io.converters.ConvertableConverter;
import xxl.core.io.converters.Converter;
import xxl.core.predicates.AbstractPredicate;
import xxl.core.spatial.points.DoublePoint;
import xxl.core.spatial.points.Point;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
/**
* Revised R*-Tree
*
* For a detailed discussion see
* Norbert Beckmann, Bernhard Seeger:
* "A Rivised R*-Tree in Comparsion with Related Index Structures"
*
* @see RTree
*/
public class RevisedRTree extends RTree{
/**Defalut value of parameter s*/
public static final double PARAM_S = 0.5d;
/** Parameter s allows to control the variance of the Weighting (Gaussian) function
* (default value s = 0.5 )
*/
protected final double parameterS; // param s
/** scale paramter for weighting function */
protected final double yOne;
/** scale paramter for weighting function */
protected final double yTwo;
/**Dimension of the data */
protected int dimension;
/** Size of the middle point of the node */
protected int pointSize;
/**
* computes maximal margin bound value after split
*/
protected Function<Rectangle, Double> perimMax;
/**
* computes the middle point of the rectangle
*/
protected Function<Rectangle, Point> computeMiddlePoint;
/**
* Creates the new RevisedR*-tree
* with default parameter s = 0.5
* @param dimension
*/
public RevisedRTree(int dimension){
this(dimension, RevisedRTree.PARAM_S);
}
/**
* Creates the new RevisedR*-tree
* @param dimension
* @param parameter S
*/
public RevisedRTree(int dimension, double paramS){
this.dimension = dimension;
this.pointSize = dimension*8; // doublePoint
this.parameterS = paramS;
this.yOne = Math.pow(Math.E, -1d/(parameterS*parameterS));
this.yTwo = 1d/(1d-yOne);
computeMiddlePoint = new AbstractFunction<Rectangle, Point>(){
public Point invoke(Rectangle rectangle){
double[] pointArray = new double[rectangle.dimensions()];
for (int i = 0; i < pointArray.length; i++){
pointArray[i] = (rectangle.getCorner(true).getValue(i) +
rectangle.getCorner(false).getValue(i))/2d ;
}
return new DoublePoint(pointArray);
}
};
perimMax = new AbstractFunction<Rectangle, Double>(){
public Double invoke(Rectangle r){
double min = r.margin();
for (double d: r.deltas()){
min = (d < min) ? d : min;
}
return (2.0*r.margin())- min;
}
};
}
/**
* @since 1.1
* @see ORTree#initialize(Function, Container, int, int, int, double)
*/
public ORTree initialize (Function getDescriptor, Container container, int blockSize, int dataSize, int descriptorSize, double minMaxFactor) {
return (ORTree)super.initialize(getDescriptor, blockSize-pointSize, container, dataSize, descriptorSize, minMaxFactor);
}
/**
* @see ORTree#initialize(IndexEntry, Function, Container, int, int, int, double)
*/
public ORTree initialize (IndexEntry rootEntry, Function getDescriptor, Container container, int blockSize, int dataSize, int descriptorSize, double minMaxFactor) {
return (ORTree)super.initialize(rootEntry, rootEntry==null? null: rootEntry.descriptor(), getDescriptor, blockSize-pointSize, container, dataSize, descriptorSize, minMaxFactor);
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree#createNode(int)
*/
public Tree.Node createNode (int level) {
return new Node().initialize(level, new LinkedList());
}
/** Gets a suitable Converter to serialize the tree's nodes.
*
* @param objectConverter a converter to convert the data objects stored in the tree
* @param dimensions the dimensions of the bounding rectangles
* @return a NodeConverter
*/
public Converter nodeConverter (Converter objectConverter) {
return nodeConverter (objectConverter, this.dimension) ;
}
/** Gets a suitable Converter to serialize the tree's nodes.
*
* @param objectConverter a converter to convert the data objects stored in the tree
* @param dimensions the dimensions of the bounding rectangles
* @return a NodeConverter
*/
public Converter nodeConverter (Converter objectConverter, final int dimensions) {
return nodeConverter(objectConverter, indexEntryConverter(
new ConvertableConverter(
new AbstractFunction() {
public Object invoke () {
return new DoublePointRectangle(dimensions);
}
}
)
));
}
/** Gets a suitable Converter to serialize the tree's nodes.
*
* @param objectConverter a converter to convert the data objects stored in the tree
* @param indexEntryConverter a converter to convert the index entries
* @return a NodeConverter
* @see Converter
* @see ORTree.NodeConverter
*/
public Converter nodeConverter (Converter objectConverter, Converter indexEntryConverter) {
ORTree.NodeConverter converter = (ORTree.NodeConverter)super.nodeConverter(objectConverter, indexEntryConverter);
return new NodeConverter(converter, dimension);
}
/** This method is an implemtation of an efficient querying algorithm.
* The result is a lazy Cursor pointing to all objects whose descriptors
* overlap with the given queryDescriptor.
*( This is original algorithmus of ORTree.query, it is
* only differs in remove method.
* updates Middle point to the center of recomputed MBB)
* @param queryDescriptor describes the query in terms of a descriptor
* @param targetLevel the tree-level to provide the answer-objects
* @return a lazy cursor pointing to all response objects
*/
public Cursor query (final Descriptor queryDescriptor, final int targetLevel){
//
final Iterator [] iterators = new Iterator[height()+1];
Arrays.fill(iterators, EmptyCursor.DEFAULT_INSTANCE);
if (height()>0 && queryDescriptor.overlaps(rootDescriptor()))
iterators[height()] = new SingleObjectCursor(rootEntry());
return new AbstractCursor () {
int queryAllLevel = 0;
Object toRemove = null;
Stack path = new Stack();
public boolean hasNextObject() {
for (int parentLevel = targetLevel;;)
if (iterators[parentLevel].hasNext())
if (parentLevel==targetLevel)
return true;
else {
IndexEntry indexEntry = (IndexEntry)iterators[parentLevel].next();
if (indexEntry.level()>=targetLevel) {
Tree.Node node = indexEntry.get(true);
Iterator queryIterator;
if (parentLevel<=queryAllLevel || queryDescriptor.contains(indexEntry.descriptor())) {
queryIterator = node.entries();
if (parentLevel>queryAllLevel && !iterators[node.level].hasNext())
queryAllLevel = node.level;
}
else
queryIterator = node.query(queryDescriptor);
iterators[parentLevel = node.level] =
iterators[parentLevel].hasNext()?
new Sequentializer(queryIterator, iterators[parentLevel]):
queryIterator;
path.push(new MapEntry(indexEntry, node));
}
}
else
if (parentLevel==height())
return false;
else {
if (parentLevel==queryAllLevel)
queryAllLevel = 0;
if (level(path)==parentLevel)
path.pop();
iterators[parentLevel++] = EmptyCursor.DEFAULT_INSTANCE;
}
}
public Object nextObject() {
return toRemove = iterators[targetLevel].next();
}
public void update (Object object) throws UnsupportedOperationException, IllegalStateException, IllegalArgumentException {
super.update(object);
if (targetLevel > 0)
throw new IllegalStateException();
else
if (targetLevel!=0 || !descriptor(object).equals(descriptor(toRemove)))
throw new IllegalArgumentException();
else {
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
Node node = (Node)node(path);
iterators[0].remove();
node.grow(object, path);
indexEntry.update(node, true);
}
}
public boolean supportsUpdate() {
return true;
}
public void remove () throws UnsupportedOperationException, IllegalStateException {
super.remove();
if (targetLevel<height()) {
IndexEntry indexEntry = (IndexEntry)indexEntry(path);
Node node = (Node)node(path);
iterators[node.level].remove();
for (;;) {
if (indexEntry==rootEntry() && node.level>0 && node.number()==1) {
rootEntry = (IndexEntry)node.entries().next();
rootDescriptor = ((IndexEntry)rootEntry()).descriptor();
indexEntry.remove();
break;
}
if (node.number()==0) {
up(path);
indexEntry.remove();
if (height()==1) {
rootEntry = null;
rootDescriptor = null;
break;
}
else {
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
iterators[node.level].remove();
}
}
else if (indexEntry!=rootEntry() && node.underflows()) {
Iterator entries = node.entries();
indexEntry.descriptor = computeDescriptor(node.entries);
up(path);
indexEntry.remove();
iterators[level(path)].remove();
indexEntry = (IndexEntry)node(path).chooseSubtree(indexEntry.descriptor(), path);
RevisedRTree.this.update(path);
node = (Node)down(path, indexEntry);
while (entries.hasNext())
node.grow(entries.next(), path);
if (node.overflows())
node.redressOverflow(path);
else {
RevisedRTree.this.update(path);
up(path);
}
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
}
else {
RevisedRTree.this.update(path);
while (up(path)!=rootEntry()) {
if (!indexEntry.descriptor().equals
(indexEntry.descriptor = computeDescriptor(node.entries)))
// set middle point to recomputed MBB
node.setMiddlePoint(indexEntry.descriptor());
RevisedRTree.this.update(path);
indexEntry = (IndexEntry)indexEntry(path);
node = (Node)node(path);
}
((IndexEntry)rootEntry).descriptor = rootDescriptor = computeDescriptor(node.entries);
node.setMiddlePoint(indexEntry.descriptor()); // set middle point to recomputed MBB
break;
}
}
}
else {
rootEntry = null;
rootDescriptor = null;
}
if (targetLevel>0) {
IndexEntry indexEntry = (IndexEntry)toRemove;
indexEntry.removeAll();
}
}
public boolean supportsRemove() {
return true;
}
};
}
/** <tt>Node</tt> is the class used to represent leaf- and non-leaf nodes of <tt>RTree</tt>.
* Nodes are stored in containers.
* @see Tree.Node
* @see ORTree.Node
* @see RTree.Node
*/
public class Node extends RTree.Node {
/**Middle Point that keeps track about size changes of the node */
protected Point middlePoint;
/** Initializes the node by inserting a new entry.
* Computes and sets the the middle point of the node
* @param entry the entry wto inserted
* @return SplitInfo contains information about a possible split
*/
public Node initialize(Descriptor oBox) {
this.setMiddlePoint(oBox);
return this;
}
/** Initializes the node by inserting a new entry.
*
* @param entry the entry wto inserted
* @return SplitInfo contains information about a possible split
*/
public Tree.Node.SplitInfo initialize (Object entry) {
Tree.Node.SplitInfo splitInfo = super.initialize(entry);
initialize(descriptor(entry));
return splitInfo;
}
/**
* returns middle point of the node
* @return middlePoint of the node
*/
protected Point middlePoint(){
return this.middlePoint;
}
/**
* Updates der middle Point of the Node
* @param nodeRegion
*/
protected void setMiddlePoint(Descriptor nodeRegion){
middlePoint = computeMiddlePoint.invoke(rectangle(nodeRegion));
}
/**
* Computes overlap of rectangles (recT union entryRec) and recJ
* @param recT
* @param recJ
* @param entryRec
* @return
*/
private double getOverlap(Rectangle recT, Rectangle recJ, Rectangle entryRec){
Rectangle unionRecTEntryRec = Descriptors.union(recT,entryRec);
double overlap = unionRecTEntryRec.overlap(recJ) - recT.overlap(recJ);
return overlap;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.ORTree.Node#chooseSubtree(xxl.core.indexStructures.Descriptor, java.util.Iterator)
*/
protected ORTree.IndexEntry chooseSubtree (Descriptor descriptor, Iterator minima){
//CSOrig
final Rectangle dataRectangle = (Rectangle)descriptor;
TeeCursor validEntries = new TeeCursor(minima);
IndexEntry qulifiedEntry = null;
minima = new Filter(validEntries,
new AbstractPredicate() {
public boolean invoke (Object object) {
return rectangle(object).contains(dataRectangle);
}
}
);
if (minima.hasNext()){
minima = Cursors.minima(minima,
new AbstractFunction() {
public Object invoke (Object object) {
return new Double(rectangle(object).area());
}
}
).iterator();
qulifiedEntry = (IndexEntry)minima.next();
}
//Optimierungs stelle
else {
// sort Entries nach delta Perimeter
List<MapEntry<IndexEntry,Object>> entriesList = new ArrayList<MapEntry<IndexEntry,Object>>();
Iterator it = validEntries.cursor();
while(it.hasNext()){ // copy entries
entriesList.add(new MapEntry(it.next(), null));
}
Collections.sort(entriesList, new Comparator<MapEntry<IndexEntry,Object>>(){ // sort nach delta perimeter
public int compare(MapEntry<IndexEntry,Object> o1, MapEntry<IndexEntry,Object> o2) {
Rectangle rec1 = rectangle(o1.getKey());
Rectangle rec2 = rectangle(o2.getKey());
Rectangle unionRec1Entry = Descriptors.union(rec1, dataRectangle);
Double deltaPerim1 = unionRec1Entry.margin()- rec1.margin();
Rectangle unionRec2Entry = Descriptors.union(rec2, dataRectangle);
Double deltaPerim2 = unionRec2Entry.margin()- rec2.margin();
return deltaPerim1.compareTo(deltaPerim2);
}
} );
// An dieser Stelle berechnung von delta ovlp 1[1, k]
double sum = 0d;
Rectangle recT = rectangle(entriesList.get(0).getKey());
for(int j = 1 ; j < entriesList.size(); j++ ){
double overlap = getOverlap(recT, rectangle( entriesList.get(j).getKey()), dataRectangle);
entriesList.get(j).setValue(new Double(overlap));
sum += overlap;
}
if (sum == 0d){
qulifiedEntry = entriesList.get(0).getKey();
}
else{//bestimme index P
Set<MapEntry<IndexEntry,Object>> cand = new HashSet<MapEntry<IndexEntry,Object>>();
int p = 1; // index
double maxOverlap = 0d;
for (int i = 1; i < entriesList.size(); i++){
double overlap = ((Double)(entriesList.get(i).getValue())).doubleValue();
if (overlap >= maxOverlap){
maxOverlap = overlap;
p = i;
}
}
// checkIndex
checkIndex(entriesList, cand, 0, p , dataRectangle);
if (cand.size() == 1){
qulifiedEntry = cand.iterator().next().getKey();
}
// bestimme minimum
if (qulifiedEntry==null){
minima = Cursors.minima(cand.iterator(),
new AbstractFunction() {
public Object invoke (Object object) {
MapEntry mapEntry = (MapEntry)object;
Double deltaOverlap = (Double)mapEntry.getValue();
return deltaOverlap;
}
}
).iterator();
qulifiedEntry = ((MapEntry<IndexEntry,Object>)minima.next()).getKey();
}
}
}
validEntries.close();
return qulifiedEntry;
}
/**
* Subroutine CheckIndex computes and tries to find in depth-first manner an overlap free
* candidate
* @param entries
* @param cand
* @param t
* @param p
* @return
*/
private boolean checkIndex(List<MapEntry<IndexEntry,Object>> entries,
Set<MapEntry<IndexEntry,Object>> cand, int t, int p , Rectangle dataRectangle){
double deltaOverlap = 0d;
MapEntry<IndexEntry,Object> candEntry = entries.get(t);
Rectangle rec = rectangle(candEntry.getKey());
cand.add(candEntry);
for (int i = 0; i <= p; i++){
if (t!=i){
double overlap = getOverlap(rectangle(rec),
rectangle(entries.get(i).getKey()), dataRectangle);
deltaOverlap += overlap;
if (overlap != 0d && !cand.contains(entries.get(i))){
if (checkIndex(entries, cand, i, p , dataRectangle)){
return true;
}
}
}
}
if (deltaOverlap == 0d){
cand.clear();
cand.add(candEntry);
return true;
}
if (cand.contains(candEntry) ){
cand.remove(candEntry);
candEntry.setValue(new Double(deltaOverlap));
cand.add(candEntry);
}
return false;
}
/* (non-Javadoc)
* @see xxl.core.indexStructures.Tree.Node#split(java.util.Stack)
*/
protected Tree.Node.SplitInfo split (Stack path) {
final Node node = (Node) node(path);
final IndexEntry indexEntry = (IndexEntry)indexEntry(path);
// chooseSubTree aktulisiert den Descriptor von IndexEntry von Uebergelaufenen Knoten
// zur jeder Dim 2(M-2m+2) moegliche Distr
Iterator<List<Distribution>> itListDistr = getAllDistributions(node);
Iterator<Distribution> iteratorWeightedDist = null;
if (node.level()> 0){ //inner Nodes
iteratorWeightedDist = computeCostsDistr(itListDistr, node, indexEntry);
}
else{ //Leaf Nodes, find first suitable dimension
@SuppressWarnings("serial")
Function<Collection<RevisedRTree.Node.Distribution>, Double> getMarginOfDistr =
new AbstractFunction
<Collection<RevisedRTree.Node.Distribution>, Double>(){
public Double invoke (Collection<RevisedRTree.Node.Distribution> list) {
double marginValue = 0.0;
for (Iterator distributions = list.iterator(); distributions.hasNext();)
marginValue += ((Distribution)distributions.next()).marginValue();
return new Double(marginValue);
}
}; // minium of sum of margins
List<Distribution> listDist = Cursors.minima(itListDistr, getMarginOfDistr).getFirst();
Distribution firstDistribution = listDist.get(0);
int dim = firstDistribution.getDim();
// create Function for this dimension
Rectangle mBBN = rectangle(indexEntry);
double pMax = perimMax.invoke(mBBN);
DoublePoint middlePointN = (DoublePoint)computeMiddlePoint.invoke(mBBN);
DoublePoint middlePointOld = (DoublePoint)node.middlePoint();
double[] deltas = mBBN.deltas();
final Function<Distribution, Double> computeCost = createWeightFunktion(node.splitMinNumber(), node.number(), pMax, middlePointN,
middlePointOld, deltas, dim); // map this function
iteratorWeightedDist = new Mapper(new AbstractFunction<Distribution, Distribution>() {
public Distribution invoke(Distribution distr){
distr.setWeight(computeCost.invoke(distr).doubleValue());
return distr;
}
}, listDist.iterator());
}
Distribution minCostDistr = (Distribution)Cursors.minima(iteratorWeightedDist,
new AbstractFunction<Distribution, Double>(){
public Double invoke(Distribution d){
return d.weight();
}
}).getFirst();
// Fill the pages with the entries according to the distribution
node.entries.clear();
node.entries.addAll(minCostDistr.entries(false));
entries.addAll(minCostDistr.entries(true));
// update the descriptor of the old index entry
((IndexEntry)indexEntry(path)).descriptor = minCostDistr.descriptor(false);
//setMiddlePoint
node.setMiddlePoint(minCostDistr.descriptor(false));
this.setMiddlePoint(minCostDistr.descriptor(true));
return new SplitInfo(path).initialize(minCostDistr);
}
/**
* Maps weighting Function for each dimension List
* @param listIterator
* @return Iterator which elements are weighted distributions
*/
protected Iterator<Distribution> computeCostsDistr(final Iterator<List<Distribution>> listIterator, final Node node, final IndexEntry indexEntry){
Rectangle mBBN = rectangle(indexEntry);
final int m = node.splitMinNumber();
final int maxNumber = node.number();
final double pMax = perimMax.invoke(mBBN);
final DoublePoint middlePointN = (DoublePoint)computeMiddlePoint.invoke(mBBN);
final DoublePoint middlePointOld = (DoublePoint)node.middlePoint();
final double[] deltas = mBBN.deltas();
return new Mapper<Distribution, Distribution>( new AbstractFunction<Distribution, Distribution>(){
public Distribution invoke(Distribution distribution){
int dim = distribution.getDim();
Function<Distribution, Double> weightFunction = createWeightFunktion(m, maxNumber, pMax,
middlePointN, middlePointOld, deltas, dim);
distribution.setWeight(weightFunction.invoke(distribution).doubleValue());
return distribution;
}
} , new Sequentializer<Distribution>(new Mapper<List<Distribution>, Iterator<Distribution> >(new AbstractFunction<Collection<Distribution>, Iterator<Distribution> >(){
public Iterator<Distribution> invoke(Collection<Distribution> list){
return list.iterator();
}
}, listIterator )));
}
/**
* Creates weighting function for one dimension
*
* @param m min number of entries in the node
* @param max number max Numebr of entries in the node
* @param pMax max Margin value for Split Distribution
* @param middlePointN old middle point of nodes mbb
* @param middlePointOld new middle point of nodes mbb
* @param deltas array of deltas
* @param dim split axis
* @return weighting function
*/
protected Function<Distribution, Double> createWeightFunktion(final int m, final int maxNumber, final double pMax,
final DoublePoint middlePointN, final DoublePoint middlePointOld, final double[] deltas, final int dim){
final double asym = (2.0*(middlePointN.getValue(dim)-middlePointOld.getValue(dim) ))
/ deltas[dim]; // functin asym 2*(x(MBB(N)) - x(OBox)) / delta dim
final double mu = (1.0-(2.0*m/( maxNumber)))* asym; // mu
final double sigma = parameterS*(1+ Math.abs(mu)); // sigma = s*(1+ |mu|)
return new AbstractFunction<Distribution, Double>(){
public Double invoke (Distribution distr){
double index = (double)distr.secondStart; // hole den index j von distribution
double xi = ((2d*index )/(maxNumber))-1; // berechnung von xi
double wf = yTwo*(Math.pow(Math.E,-(((xi - mu )*(xi - mu ))/sigma*sigma))-yOne); // wf(i)
double wgi = (distr.overlapValue() == 0d) ? (distr.marginValue()- pMax)
: distr.overlapValue(); // wg(i)
double wi = (distr.overlapValue() == 0d) ? wgi * wf : wgi/wf; // composed Function
return new Double(wi);
}
};
}
/**
* Original RTree Algorithmus, Computes all Distributions (2*dim(M-2m+2))
* @param computeDistrCost
* @param node
* @return
*/
protected Iterator<List<Distribution>> getAllDistributions(final Node node){
final int minEntries = node.splitMinNumber();
final int maxEntries = node.splitMaxNumber();
int dimensions = ((Rectangle)rootDescriptor()).dimensions();
return new Mapper(
new AbstractFunction() {
public Object invoke (Object object) {
final int dim = ((Integer)object).intValue();
// list of distributions for this dimension
ArrayList distributionList = new ArrayList(2*(maxEntries-minEntries+1));
Rectangle [][] rectangles = new Rectangle[2][];
// Consider the entrys sorted by left or right borders
for (int i=0; i<2; i++) {
Object [] entryArray = node.entries.toArray();
final boolean right = i==1;
// Sort the entries by left or right border in the actual dimension
Arrays.sort(entryArray, new FeatureComparator(new ComparableComparator(),
new AbstractFunction() {
public Object invoke (Object entry) {
return new Double(rectangle(entry).getCorner(right).getValue(dim));
}
}
));
// Calculation of descriptors for all distributions (linear!)
for (int k = 0; k<2; k++) {
List entryList = Arrays.asList(entryArray);
Iterator entries = (k==0? entryList: new ReversedList(entryList)).iterator();
Rectangle rectangle = new DoublePointRectangle(rectangle(entries.next()));
for (int l = (k==0? minEntries: node.number()-maxEntries); --l>0;)
rectangle.union(rectangle(entries.next()));
(rectangles[k] = new Rectangle[maxEntries-minEntries+1])[0] = rectangle;
for (int j=1; j<=maxEntries-minEntries; rectangles[k][j++] = rectangle)
rectangle = Descriptors.union(rectangle, rectangle(entries.next()));
}
// Creation of the distributions for this dimension
for (int j = minEntries; j<=maxEntries; j++){
Distribution distribution = new Distribution(entryArray, j,
rectangles[0][j-minEntries], rectangles[1][maxEntries-j], dim);
distributionList.add(distribution);
}
}
return distributionList;
}
}
,new Enumerator(dimensions));
}
/** <tt>Distribution</tt> is the class used to represent the distribution of
* entries of a node of the <tt>RTree</tt> into two partitions used for a split.
*/
protected class Distribution extends RTree.Node.Distribution {
/**weight of the distribution*/
protected double weight; // cost
protected Distribution(Object[] entries, int secondStart, Rectangle firstDescriptor, Rectangle secondDescriptor, int dim) {
super(entries, secondStart, firstDescriptor, secondDescriptor, dim);
}
/**
*
* @param cost
*/
protected void setWeight(double cost){
weight = cost;
}
/**
*
* @return
*/
protected double weight(){
return this.weight;
}
}
}
/** The instances of this class are converters to write nodes to the
* external storage (or any other {@link DataOutput}) or read
* them from it (or any other {@link DataInput}).
*
* @see Converter
*/
public class NodeConverter extends Converter {
protected int dimension;
/**
* A converter for index entries.
*/
protected ORTree.NodeConverter orTreeNodeConverter;
/** Creates a new NodeConverter.
*
* @param objectConverter a converter to convert the data objects stored in the tree
* @param indexEntryConverter a converter to convert the index entries
*/
public NodeConverter (ORTree.NodeConverter orTreeNodeConverter, int dimension) {
this.orTreeNodeConverter = orTreeNodeConverter;
this.dimension = dimension;
}
/** Reads a node from the data input.
*
* @param dataInput the data input stream
* @param object is ignored
* @return the read node
*
* @see Converter#read(java.io.DataInput, java.lang.Object)
*/
public Object read (DataInput dataInput, Object object) throws IOException {
Node node = (Node)orTreeNodeConverter.read(dataInput, object);
node.middlePoint = new DoublePoint(dimension);
node.middlePoint.read(dataInput);
return node;
}
/** Writes a node into the data output.
* @param dataOutput the data output stream
* @param object the node to write
* @see xxl.core.io.converters.Converter#write(java.io.DataOutput, java.lang.Object)
*/
public void write (DataOutput dataOutput, Object object) throws IOException {
Node node = (Node)object;
orTreeNodeConverter.write(dataOutput, object);
if (node.middlePoint == null ){
Rectangle nodeMBR = rectangle(computeDescriptor(node.entries));
node.setMiddlePoint(nodeMBR);
}
node.middlePoint().write(dataOutput);
}
}
}