/* 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.rtrees;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.Comparator;
import java.util.Iterator;
import java.util.Random;
import xxl.core.collections.containers.Container;
import xxl.core.collections.containers.io.BlockFileContainer;
import xxl.core.collections.containers.io.BufferedContainer;
import xxl.core.collections.containers.io.ConverterContainer;
import xxl.core.collections.queues.DynamicHeap;
import xxl.core.cursors.Cursor;
import xxl.core.cursors.filters.Taker;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.indexStructures.ORTree;
import xxl.core.indexStructures.RTree;
import xxl.core.indexStructures.Tree;
import xxl.core.indexStructures.ORTree.IndexEntry;
import xxl.core.indexStructures.Tree.Query;
import xxl.core.indexStructures.Tree.Query.Candidate;
import xxl.core.io.LRUBuffer;
import xxl.core.io.converters.ConvertableConverter;
import xxl.core.io.converters.Converter;
import xxl.core.io.converters.IntegerConverter;
import xxl.core.io.converters.LongConverter;
import xxl.core.spatial.points.DoublePoint;
import xxl.core.spatial.rectangles.DoublePointRectangle;
import xxl.core.spatial.rectangles.Rectangle;
public class SimpleRTreeTest {
/** Dimension of the data.
*/
public static final int dimension = 2;
/** Size of a block in bytes.
*/
public static int blockSize = 1536;
/** Factor which the minimum capacity of nodes is smaller than the maximum capacity.
*/
public static double minMaxFactor = 1.0/3.0;
/** Buffersize (number of node-objects).
*/
public static int bufferSize = 100;
/**
* Factory Function to get a leaf entry.
*/
static Function<Object, DoublePoint> LEAFENTRY_FACTORY = new AbstractFunction<Object, DoublePoint>() {
public DoublePoint invoke() {
return new DoublePoint(dimension);
}
};
/**
* Function creating a descriptor for a given object.
*/
static Function<Object,Object> GET_DESCRIPTOR = new AbstractFunction<Object,Object>() {
@Override
public Object invoke (Object o) {
DoublePoint p = (DoublePoint)o;
return new DoublePointRectangle(p, p);
}
};
/**
* Returns a comparator which evaluates the distance of two candidate objects
* to the specified <tt>queryObject</tt>. This comparator
* is used for nearest neighbor queries and defines an order on candidate-
* descriptors. With the help of a priority queue (Min-heap) and this
* comparator the nearest neighbor query can be performed.
*
* @param queryObject a KPE to which the nearest neighbors should be determined
* @return a comparator defining an order on candidate objects
*/
public static Comparator<Object> getDistanceBasedComparator (DoublePoint queryObject) {
final Rectangle query = new DoublePointRectangle(queryObject, queryObject);
return new Comparator<Object> () {
public int compare (Object candidate1, Object candidate2) {
Rectangle r1 = (Rectangle) (((Candidate) candidate1).descriptor()) ;
Rectangle r2 = (Rectangle) (((Candidate) candidate2).descriptor()) ;
double d1 = query.distance(r1, 2);
double d2 = query.distance(r2, 2);
return (d1<d2) ? -1 : ( (d1==d2) ? 0 : 1 );
}
};
}
/**
* Starts the SimpleRTreeTest.
*
* @param args command line parameter: Path to RTree file
* @throws Exception
*/
public static void main (String args[]) throws Exception {
// check for command line argument
if (args.length!=1)
System.out.println("usage: java SimpleRTreeTest filename");
// test if RTree exists
boolean reopen = (new File(args[0]+".ctr")).canRead();
// size of a data entry = size of DoublePoint (1 double per dimension)
int dataSize = dimension*8;
// size of a decriptor = size of DoublePointRectangle (2 doubles per dimension)
int descriptorSize = dimension*2*8;
RTree rtree = new RTree();
ORTree.IndexEntry rootEntry = null;
Container fileContainer;
if (!reopen) {
System.out.println("Building new RTree");
fileContainer = new BlockFileContainer(args[0], blockSize);
}
else {
System.out.println("Using existing RTree");
fileContainer = new BlockFileContainer(args[0]);
File parameters = new File(args[0]+"_params.dat");
DataInputStream dos = new DataInputStream(new FileInputStream(parameters));
int height = IntegerConverter.DEFAULT_INSTANCE.read(dos);
long rootPageId = LongConverter.DEFAULT_INSTANCE.read(dos);
DoublePointRectangle rootDescriptor = (DoublePointRectangle) ConvertableConverter.DEFAULT_INSTANCE.read(dos, new DoublePointRectangle(dimension));
rootEntry = (ORTree.IndexEntry) ((ORTree.IndexEntry)rtree.createIndexEntry(height)).initialize(rootDescriptor).initialize(rootPageId);
}
// determine Converters and Containers
Converter converter = rtree.nodeConverter(new ConvertableConverter(LEAFENTRY_FACTORY), dimension);
ConverterContainer converterContainer = new ConverterContainer(fileContainer, converter);
// use buffer
BufferedContainer bufferedContainer = new BufferedContainer(converterContainer, new LRUBuffer(bufferSize), true);
// initialize RTree
rtree.initialize(rootEntry, GET_DESCRIPTOR, bufferedContainer, blockSize, dataSize, descriptorSize, minMaxFactor);
/*********************************************************************/
/* INSERT RANDOM DATA */
/*********************************************************************/
if (!reopen) {
Random random = new Random(42);
for (int j=0; j<100000; j++) {
// create random coordinates
double [] point = new double[dimension];
for (int i=0; i<dimension; i++)
point[i] = random.nextDouble();
// insert new point
rtree.insert(new DoublePoint(point));
if (j%10000==0)
System.out.print(j/1000+"%, ");
}
System.out.println("100%");
// flush buffers
bufferedContainer.flush();
}
/*********************************************************************/
/* RANGE QUERY */
/*********************************************************************/
// create query window
double [] leftCorner = new double[dimension];
double [] rightCorner = new double[dimension];
for (int i=0; i<dimension; i++) {
leftCorner[i] = 0.4975;
rightCorner[i] = 0.5025;
}
DoublePointRectangle queryRange = new DoublePointRectangle(leftCorner, rightCorner);
// perform query
Cursor results = rtree.query(queryRange, 0);
// show results
int counter = 0;
System.out.println("Results for range query ("+queryRange+")");
while (results.hasNext()) {
DoublePoint next = (DoublePoint)results.next();
System.out.println("result no. "+(++counter)+": "+next);
}
results.close();
/*********************************************************************/
/* NEAREST NEIGHBOR QUERY */
/*********************************************************************/
// create query point
double [] point = new double[dimension];
for (int i=0; i<dimension; i++)
point[i] = 0.5;
DoublePoint queryObject = new DoublePoint(point);
// lazy Iterator of all nearest neighbors
Iterator neighbors = rtree.query(new DynamicHeap(getDistanceBasedComparator(queryObject)), 0);
// compute only 5 nearest neigbors
results = new Taker(neighbors, 5);
// show results
counter = 0;
System.out.println("\nResults for nearest neighbor query ("+queryObject+")");
while (results.hasNext()) {
DoublePoint next = (DoublePoint)((Candidate)results.next()).entry();
System.out.println("candidate no. "+(++counter)+": "+next+" (distance="+next.distanceTo(queryObject)+")");
}
results.close();
// save parameters of RTree
if (!reopen) {
File parameters = new File(args[0]+"_params.dat");
DataOutputStream dos = new DataOutputStream(new FileOutputStream(parameters));
IntegerConverter.DEFAULT_INSTANCE.write(dos, rtree.height());
LongConverter.DEFAULT_INSTANCE.write(dos, (Long)rtree.rootEntry().id());
ConvertableConverter.DEFAULT_INSTANCE.write(dos, (DoublePointRectangle) rtree.rootDescriptor());
}
// close container
bufferedContainer.flush();
bufferedContainer.close();
}
}