OsmAnd/DataExtractionOSM/src/rtree/RTree.java

1206 lines
46 KiB
Java
Raw Normal View History

//RTree.java
//
//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 2.1 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.
package rtree;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import rtree.join.IntersectPred;
import rtree.join.PairElmt;
import rtree.join.SweepLine;
/******************************************************************************************************
* <p><b>1:</b>Call <code>flush</code> after single or multiple inserts. In affect before the application
* shuts down, the flush method flushes all the rtrees.
* <p><b>2:</b>Do not run more than one instance of this application. The package
* cannot handle more than one instance of the application as all the locking
* is done in the code itself(Monitors) and not on the hard-disk file.
* <p><b>3:</b>The file name (of the rtree) is case insensitive.
* <p><b>4:</b>The package is thread proof, i.e you can run as many threads as
* you want, not worrying about concurrent updations. The package will handle the
* situation of concurrent reads and writes. Again, it can take care of threads
* of only one process(Application) and not for more than one process of the program.
* <p><b>5:</b>For immediate effects of tree writes, give the thread that needs
* to write to the file a high priority. This will further speed up tree writes.
* <p><b>6:</b>Give messages like "Updating, please wait..." to the user when
* calling any of the methods that modifies the tree as the thread may have to
* wait on a lock variable.
* <p><b>7:</b>The package maintains a queue of all threads that need to wait on some lock.
* The sequence of excecution of the threads is dependent on that queue. All the actions are fired in
* the order in which they were received. <br><b>In short the tree is perfectly concurrent.</b>
* <p><b>8:</b>For developers: Always obtain a lock from the <code>lockRead</code> or
* <code>lockWrite</code> method before going into a <code>public</code> method of the
* <code>RTree</code> class. Unlock by calling the <code>unlock</code> method.
* See any existing method to understand the mechanism.
* <p><b>9:</b>To adjust the cache buffer size, see the <code>Node</code> class documentation.
* @author Prachuryya Barua
******************************************************************************************************/
public class RTree //the tree that would be made
{
/**<b>Caution:</b> The file name (of the rtree) is case insensitive in spite
of the fact that this package was developed on a Linux(RH7.0) platform.
*/
protected String fileName;
static Map fileList;//the no. of files open
// static for the other way
protected FileHdr fileHdr;
public static CachedNodes chdNodes;
/**Inner class for the fileList vector - A List of files*/
class Header
{
FileHdr flHdr;
String fileName;
Header(FileHdr flH,String name)
{
fileName = name;
flHdr = flH;
}
}
public static void clearCache(){
chdNodes = new CachedNodes();
fileList = new HashMap();
CachedNodes.clearFileNamesMap();
}
public RTree(String fileName)
throws RTreeException
{
try{
this.fileName = fileName;
if(fileList == null)
fileList = new HashMap();
synchronized(fileList){//this may give problem
if(fileList.get(fileName) != null){
fileHdr = ((Header)fileList.get(fileName)).flHdr;
return;
}
//a new file
fileList.put(fileName, new Header(new FileHdr(Node.FREE_LIST_LIMIT, fileName),fileName));
fileHdr = ((Header)fileList.get(fileName)).flHdr;
//the cache of nodes - one cache for all the tree files.
if(chdNodes == null)
chdNodes = new CachedNodes();
}
}
catch(Exception e){
throw new RTreeException("RTree.RTree: " +e.getMessage());
}
}
/**
This method is used to ask the fileHdr to update itself. This method is package parivate used by
<code>Pack</code> class only.
*/
void updateHdr()
throws RTreeException, IOException, FileNotFoundException, NodeWriteException
{
Header tmp = (Header)fileList.get(fileName);
if(tmp != null){
//chdNodes.removeAll();//XXX check this out
fileHdr.update(fileName);
}
}
public Node getReadNode(long index) throws RTreeException
{
try{
return chdNodes.getReadNode(fileHdr.getFile(), fileName, index, fileHdr);
}catch(Exception e){
throw new RTreeException ("RTree.getSortedNode : " + e.getMessage());
}
}
public String getFileName()
{
return fileName;
}
/**
Another package private method for getting the file header
*/
public FileHdr getFileHdr()
{
return fileHdr;
}
public void flush()
throws RTreeException
{
fileHdr.lockWrite();
try{
fileHdr.flush();
chdNodes.flush();
}catch(Exception e){
throw new RTreeException(e.getMessage());
}finally{
fileHdr.unlock();
}
}
/**
* Adjust Tree from <b>Guttman the Great</b>.
* @param Node[] The nodes that was has the new element and also the element
* that resulted from the splt(if any).i.e N-node[0], NN-nodes[1]
* Note:- This method is functionally very much coupled with Node.spliNode()
* @param node The two new nodes caused by split.
* @param slotIndex The index of the slot of this tree if any, else give NOT_DEFINED.
* @return The new root (if it was created). If no new root was created then returns null.
*/
protected Node adjustTree(Node[] nodes, long slotIndex)
throws RTreeException
{
try{
//if(nodes[0].getParent() == Node.NOT_DEFINED){//original
if(nodes[0].getParent() == slotIndex){
if(nodes[1] != null){//if root is split
Node newRoot;
if(fileHdr.isWriteThr())
newRoot = new Node(fileHdr.getFile(),fileName, slotIndex, Node.NONLEAF_NODE,
((Header)fileList.get(fileName)).flHdr);
else
newRoot = chdNodes.getNode(fileHdr.getFile(),fileName, slotIndex, Node.NONLEAF_NODE,
((Header)fileList.get(fileName)).flHdr, nodes[0]);
NonLeafElement branchA = new NonLeafElement(nodes[0].getNodeMBR(),nodes[0].getNodeIndex());
NonLeafElement branchB = new NonLeafElement(nodes[1].getNodeMBR(),nodes[1].getNodeIndex());
newRoot.insertElement(branchB);
newRoot.insertElement(branchA);
return newRoot;
}
return null;
}else{
//where the new element is inserted
Node[] insertedNode = new Node[2];
/*
set the parent element's MBR equal to the nodeA's MBR.
*/
//the parent of this node
Node parentN;
if(fileHdr.isWriteThr())
parentN = new Node(fileHdr.getFile(),fileName,nodes[0].getParent(),fileHdr);
else
parentN = chdNodes.getNode(fileHdr.getFile(),fileName,nodes[0].getParent(),fileHdr);
//get the parent element of nodes[0]
//Integer intValue = new Integer(nodes[0].getNodeIndex());
int parentElmtIndex = parentN.getElementIndex(nodes[0].getNodeIndex()/*intValue*/);
//adjust the parent element's MBR
parentN.modifyElement(parentElmtIndex,nodes[0].getNodeMBR());
insertedNode[0] = parentN;
insertedNode[1] = null;
//if it is an split node add its entry in the parent
if(nodes[1] != null){
NonLeafElement elmtNN = new NonLeafElement(nodes[1].getNodeMBR(),nodes[1].getNodeIndex());
try{//if another insert is possible
parentN.insertElement(elmtNN);
insertedNode[0] = parentN;
insertedNode[1] = null;
}catch(NodeFullException e){
insertedNode = parentN.splitNode(elmtNN, Node.NOT_DEFINED);
}
}
return adjustTree(insertedNode, slotIndex);
}
}
catch(Exception e){
e.printStackTrace();
throw new RTreeException("RTree.adjustTree: "+e.getMessage());
}
}
/**
Pass a <code>LeafElement</code>object.
*/
public void insert(Element elmt)//Leaf
throws RTreeInsertException
{
fileHdr.lockWrite();
Node node = null;
try{
//the node into which to insert
node = chooseLeaf(elmt);
//System.out.println("Returned:"+node.getNodeIndex());
Node[] newNodes = new Node[2];
try{
node.insertElement(elmt);//if another insert is possible
newNodes[0] = node;
newNodes[1] = null;
}
//if another insert is not possible
catch(NodeFullException e){
newNodes = node.splitNode(elmt, Node.NOT_DEFINED);
}
adjustTree(newNodes, Node.NOT_DEFINED);
}
catch(Exception e){
//e.printStackTrace();
throw new RTreeInsertException("RTree.insert: " + e.getMessage() + " for " +
Thread.currentThread());
}
finally{
fileHdr.unlock();
}
}
/**
See <b>Guttman the Great</b>.
*/
private Node chooseLeaf(Element elmt)
throws IllegalValueException, RTreeException
{
try{
//get the root node
long root = fileHdr.getRootIndex();
Node node = null;
if(fileHdr.isWriteThr())
node = new Node(fileHdr.getFile(), fileName,root,fileHdr);
else
node = chdNodes.getNode(fileHdr.getFile(), fileName,root,fileHdr);
switch (node.getElementType()){
case Node.LEAF_NODE :
break;
case Node.NONLEAF_NODE :
//repeat till you reach a leaf node
while(true){
//get the best fitting rect from the node
Element nextElmt = node.getLeastEnlargement(elmt);
if(nextElmt.getElementType() == Node.LEAF_NODE)
break;
if(fileHdr.isWriteThr())
node = new Node(fileHdr.getFile(), fileName, nextElmt.getPtr(), fileHdr);
else
node = chdNodes.getNode(fileHdr.getFile(), fileName, nextElmt.getPtr(), fileHdr);
}
break;
default :
throw new IllegalValueException("RTree.chooseLeaf: Node corrupt, Illegal element type in "
+"node");
}
return node;
}
catch( IllegalValueException e){
throw new IllegalValueException("RTree.chooseLeaf: "+e.getMessage());
}
catch(Exception e){
e.printStackTrace();
throw new RTreeException("RTree.chooseLeaf: " + e.getMessage());
}
}
/**
given the leaf element, this method deletes the element from the tree
compairing its Rect and pointer with the similar entries in the tree.
@throws ElementNotException when the given element is not found.
*/
public void delete(LeafElement elmt)
throws RTreeException,ElementNotFoundException
{
fileHdr.lockWrite();
long root;
if(elmt == null)
throw new RTreeException("RTree.delete: Rect is null");
try{
if(fileHdr.isWriteThr())
chdNodes.removeAll();
root = fileHdr.getRootIndex();//FileHdr.getRootIndex(fileName);
//find the leaf that contains the element
Node delNode;
if(fileHdr.isWriteThr())
delNode = findLeaf(new Node(fileHdr.getFile(),fileName,root,fileHdr),elmt);
else
delNode = findLeaf(chdNodes.getNode(fileHdr.getFile(),fileName,root,fileHdr),elmt);
//if found...
if(delNode != null){
Element[] elmts = delNode.getAllElements();//all the elements
int totElmts = delNode.getTotalElements();//total elements
//index of the desired element
int childIndex = Node.NOT_DEFINED;
//find the index of the element that has to be deleted
for(int i=0;i<totElmts;i++){
if(elmts[i].getRect().encloses(elmt.getRect()) && (elmts[i].getPtr() == elmt.getPtr())){
childIndex = i;
break;
}
}
//strange, but the element is not found....impossible!
if(childIndex == Node.NOT_DEFINED)
throw new ElementNotFoundException("RTree.delete: Element not in tree");
delNode.deleteElement(childIndex, false);
Stack stack = new Stack();
condenseTree(delNode,stack);
//find the root
Node rootNode;
if(fileHdr.isWriteThr())
rootNode = new Node(fileHdr.getFile(),fileName,fileHdr.getRootIndex(),
fileHdr);
else
rootNode = chdNodes.getNode(fileHdr.getFile(),fileName,fileHdr.getRootIndex(),
fileHdr);
//if root has only one element then make the node pointed by the
//element as the new root
if((rootNode.getTotalElements() == 1) && (rootNode.getElementType() == Node.NONLEAF_NODE)){
long childPtr= rootNode.getElement(0).getPtr();
Node child;
if(fileHdr.isWriteThr())
child = new Node(fileHdr.getFile(),fileName, childPtr,fileHdr);
else
child = chdNodes.getNode(fileHdr.getFile(),fileName, childPtr,fileHdr);
//set as the new root
child.setParent(Node.NOT_DEFINED);
//delete the original parent
rootNode.deleteNode();
}
}
else
throw new ElementNotFoundException("RTree.delete: Element not in tree");
}
catch(Exception e){
if(e instanceof ElementNotFoundException)
throw (ElementNotFoundException)e;
throw new RTreeException("RTree.delete: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
private void condenseTree(Node node,Stack stack)
throws Exception
{
//is the parent node
if(node.getParent() == Node.NOT_DEFINED){
while(!stack.empty()){
Node nd = (Node)stack.pop();
List v = trvsRPost(nd,true);
for(int i=0;i<v.size();i++)
insert((LeafElement)v.get(i));
}
return;
}
//get the parent
Node parentN;
if(fileHdr.isWriteThr())
parentN = new Node(fileHdr.getFile(),fileName,node.getParent(),fileHdr);
else
parentN = chdNodes.getNode(fileHdr.getFile(),fileName,node.getParent(),fileHdr);
//get the parent element of 'node'.
//Integer intValue = new Integer(node.getNodeIndex());
int parentElmtIdx = parentN.getElementIndex(node.getNodeIndex()/*intValue*/);
//delete parent element if node has less than 'm' elements
if(node.getTotalElements() < Node.MIN){
parentN.deleteElement(parentElmtIdx, false);
stack.push(node);
}
else{//if ok
parentN.modifyElement(parentElmtIdx,node.getNodeMBR());
}
condenseTree(parentN,stack);
}
/**Basically it returns the node that <b>may</b> contain the required element.
*/
private Node findLeaf(Node node,LeafElement elmt)
throws IllegalValueException,FileNotFoundException,IOException, NodeReadException, NodeWriteException
{
if((node == null) || (elmt == null))
throw new IllegalValueException("RTree.findLeaf: Node is null");
Node nd = null;
Element[] allElmt = node.getAllElements();
int totElements = node.getTotalElements();
//Integer elmtPtr = (Integer)elmt.getPtr();
for(int i=0; i<totElements; i++){//for every element
//select elements that overlap
if(!allElmt[i].getRect().disjoint(elmt.getRect())){//intersect
//Integer nxtIndex = (Integer)allElmt[i].getPtr();
if(allElmt[i].getElementType() == Node.NONLEAF_NODE){//non leaf
if(fileHdr.isWriteThr())
nd = findLeaf(new Node(fileHdr.getFile(),fileName, allElmt[i].getPtr(),fileHdr),elmt);
else
nd = findLeaf(chdNodes.getNode(fileHdr.getFile(),fileName, allElmt[i].getPtr(),fileHdr),elmt);
if(nd != null)
return nd;
}
else{
//if any of the leaf element matches then check for exact
//match with passed element, also check for the pointer
//value so that elements are excactly match.
if(allElmt[i].getRect().equals(elmt.getRect()) && (allElmt[i].getPtr() == elmt.getPtr()))
return node;
}
}
}
return null;
}
//-----------------------------------------------------------------------
/**
* Find all index records whose MBR overlap a search MBR 'rect'. - <b>Guttman the Great</b>.
* An vector is returned. The tree is traversed recusively in post order.
* @return If the file is empty or search rect. is out of scope then the method returns a zero size vector.
*/
public List overlaps(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
//System.out.println("RTree.overlaps : in for thread " + Thread.currentThread().toString());
long root;
if(rect == null)
throw new RTreeException("RTree.overlaps: Rect is null");
root = fileHdr.getRootIndex();
try{
Node node = chdNodes.getReadNode(fileHdr.getFile(),fileName,root, fileHdr);
List ret = getRPostOvrlap(node, rect);
return ret;
//return(getRPostOvrlap(chdNodes.getNode(fileHdr.getFile(),fileName,root, fileHdr), rect));
}
catch(Exception e){
throw new RTreeException("RTree.overlaps: "+e.getMessage());
}
finally{
//System.out.println("RTree.overlaps : out for thread " + Thread.currentThread().toString());
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all overlaping elements in the leaves.
*/
private List getRPostOvrlap(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostOvrlap: Node is null");
List list = new ArrayList();
//System.out.println("Nodes visited:"+node.getNodeIndex());
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
for(int i=0; i<totElements; i++){//for every element; we can use sweepline algorithm here
if(elmts[i].getRect().overlaps(rect)){ //select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostOvrlap(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),rect));
}else{//if leaf element
list.add(elmts[i]);
}
}
}
return list;
}
public List overlapsSweep(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.overlaps: Rect is null");
root = fileHdr.getRootIndex();
try{
return getRPostOvrlapSweep(chdNodes.getReadNode(fileHdr.getFile(),fileName,root, fileHdr), rect);
}
catch(Exception e){
e.printStackTrace();
throw new RTreeException("RTree.overlaps: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
private List getRPostOvrlapSweep(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostOvrlap: Node is null");
List list = new ArrayList();
//System.out.println("Nodes visited:"+node.getNodeIndex());
Element[] elmts = node.getAllElements();
SweepLine spLine = new SweepLine(new IntersectPred());
//System.out.println("RTree.getRPostOvrlapSweep : calling sweep");
List pairs = spLine.intersects(rect, elmts);
//System.out.println("RTree.getRPostOvrlapSweep : called..and now for every pair");
for(int i=0; i<pairs.size(); i++){//for every element; we can use sweepline algorithm here
Element intElmt = ((PairElmt)pairs.get(i)).getRtElmt();
if(intElmt.getElementType() == Node.NONLEAF_NODE){//non leaf
list.addAll(getRPostOvrlapSweep(chdNodes.getReadNode(fileHdr.getFile(),fileName,intElmt.getPtr(),
fileHdr),rect));
//System.out.println("RTree.getRPostOvrlapSweep : total objects NONLEAF " + list.size());
}
else{//if leaf element
list.add(intElmt);
//System.out.println("RTree.getRPostOvrlapSweep : total objects LEAF " + list.size());
}
}
//System.out.println("RTree.getRPostOvrlapSweep : checked pairs as well");
return list;
}
//-----------------------------------------------------------------------
/**
Find all index records whose MBR intsersect a search MBR 'rect'.
The diff. betn. this method and 'overlap' method is that this method returns
all rectangle that is in any way in touch with the test rectangle 'rect'.
In method 'overlap' only the rects that have common area with 'rect'
are returned but unlike this method it does not return rects that have
common side with 'rect'.
An vector is returned. The tree is traversed recusively in post order.
@return If the file is empty or search rect. is out of scope then the method returns a zero size vector.
*/
public List nonDisjoint(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.nonDisjoint: Rect is null");
root = fileHdr.getRootIndex();
try{
return(getRPostIntsect(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr), rect));
}
catch(Exception e){
throw new RTreeException("RTree.nonDisjoint: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all intersecting elements in the leaves.
*/
private List getRPostIntsect(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostIntsect: Node is null");
List list = new ArrayList();
//System.out.println("Nodes visited:"+node.getNodeIndex());
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
for(int i=0; i<totElements; i++){//for every element
if(!elmts[i].getRect().disjoint(rect)){//select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostIntsect(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),rect));
}
else{//if leaf element
list.add(elmts[i]);
}
}
}
return list;
}
//-----------------------------------------------------------------------
/**Find all index records whose MBR are enclosed by the MBR 'rect'.
An Vector is returned. The tree is traversed recusively in post order.
@return If the file is empty or search rect. is out of scope then the
method returns a zero size vector.
*/
public List containedBy(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.containedBy: Rect is null");
root = fileHdr.getRootIndex();
try{
return(getRPostContBy(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr), rect));
}
catch(Exception e){
throw new RTreeException("RTree.containedBy: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all the enclosed(inside 'rect') elements in the leaves.
*/
private List getRPostContBy(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostContBy: Node is null");
List list = new ArrayList();
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
//System.out.println("Nodes visited:"+node.getNodeIndex());
for(int i=0; i<totElements; i++){//for every element //encloses
if(rect.overlaps(elmts[i].getRect())){//select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostContBy(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),rect));
}
else{//if leaf element
if(elmts[i].getRect().containedBy(rect))
list.add(elmts[i]);
}
}
}
return list;
}
//-----------------------------------------------------------------------
/**Find all index records whose MBR are geometrically equal to MBR 'rect'
An Vector is returned. The tree is traversed recusively in post order.
@return If the file is empty or search rect. is out of scope then the
method returns a zero size vector.
*/
public List equal(Rect rect)
throws RTreeException,
FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.equal: Rect is null");
root = fileHdr.getRootIndex();
try{
return(getRPostEqual(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr), rect));
}
catch(Exception e){
throw new RTreeException("RTree.equal: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all the enclosed(inside 'rect') elements in the leaves.
*/
private List getRPostEqual(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostEqual: Node is null");
List list = new ArrayList();
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
//System.out.println("Nodes visited:"+node.getNodeIndex());
for(int i=0; i<totElements; i++){//for every element //encloses
if(rect.overlaps(elmts[i].getRect())){//select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostEqual(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),rect));
}
else{//if leaf element
if(elmts[i].getRect().equals(rect))
list.add(elmts[i]);
}
}
}
return list;
}
//-----------------------------------------------------------------------
/**Find all index records whose MBR meet on the sides of MBR 'rect'.
An Vector is returned. The tree is traversed recusively in post order.
@return If the file is empty or search rect. is out of scope then the
method returns a zero size vector.
*/
public List meet(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.meet: Rect is null");
root = fileHdr.getRootIndex();
try{
return(getRPostMeet(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr), rect));
}
catch(Exception e){
throw new RTreeException("RTree.meet: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all the enclosed(inside 'rect') elements in the leaves.
*/
private List getRPostMeet(Node node,Rect rect)
throws IllegalValueException, FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.meet: Node is null");
List list = new ArrayList();
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
//System.out.println("Nodes visited:"+node.getNodeIndex());
for(int i=0; i<totElements; i++){//for every element //encloses
if(!rect.disjoint(elmts[i].getRect())){//select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostMeet(chdNodes.getReadNode(fileHdr.getFile(),fileName, elmts[i].getPtr() ,
fileHdr),rect));
}
else{//if leaf element
if(elmts[i].getRect().meet(rect))
list.add(elmts[i]);
}
}
}
return list;
}
//----------------------------------------------------------------------
/**Find all index records whose MBR enclose/contain the MBR 'rect'.
An Vector is returned. The tree is traversed recusively in post order.
@return If the file is empty or search rect. is out of scope then the
method returns a zero size vector.
*/
public List contains(Rect rect)
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
if(rect == null)
throw new RTreeException("RTree.contains: Rect is null");
root = fileHdr.getRootIndex();
try{
return(getRPostContains(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr),rect));
}
catch(Exception e){
throw new RTreeException("RTree.contains: " +e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Given any node it traverses a tree in a recursive post order manner
fetching all the enclosed(inside 'rect') elements in the leaves.
*/
private List getRPostContains(Node node, Rect rect)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null) || (rect == null))
throw new IllegalValueException("RTree.getRPostContains: Node is null");
List list = new ArrayList();
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
//System.out.println("Nodes visited:"+node.getNodeIndex());
for(int i=0; i<totElements; i++){//for every element //encloses
if(rect.overlaps(elmts[i].getRect())){//select elements that overlap
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(getRPostContains(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),rect));
}
else{//if leaf element
if(elmts[i].getRect().contains(rect))
list.add(elmts[i]);
}
}
}
return list;
}
//-----------------------------------------------------------------------
/**
Returns all the elements traversing the tree recursively in <b>postorder</b>
*/
public List getAllElements()
throws RTreeException, FileNotFoundException
{
//fileHdr.enter(Node.READ);
fileHdr.lockRead();
//System.out.println("RTree.getAllElements : in for thread " + Thread.currentThread().toString());
long root;
root = fileHdr.getRootIndex();
try{
return(trvsRPost(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr),false));
}
catch(Exception e){
//e.printStackTrace();
throw new RTreeException("RTree.getAllElements: " +e.getMessage());
}
finally{
//fileHdr.leave();
//System.out.println("RTree.getAllElements : out for thread " + Thread.currentThread().toString());
fileHdr.unlock();
}
}
/**
Traverses the tree recursively in post order.
The second parameter is for the delete method. As it goes through the nodes
returning the leafelements it also deletes the nodes.
This feature is not required for any other methods hence set <code>del</code> as
<code>false</code> for all other cases.
*/
private List trvsRPost(Node node,boolean del)
throws IllegalValueException,FileNotFoundException,NodeReadException,IOException, NodeWriteException
{
if((node == null))
throw new IllegalValueException("RTree.getRPostOvrlap: Node is null");
List list = new ArrayList();
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
//System.out.println("Nodes visited:"+node.getNodeIndex())
for(int i=0; i<totElements; i++){//for every element
//non leaf
if(elmts[i].getElementType()==Node.NONLEAF_NODE){
//Integer ndIndex = (Integer)elmts[i].getPtr();
list.addAll(trvsRPost(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(), fileHdr),
del));
}
else{//if leaf element
list.add(elmts[i]);
}
}
if(del)
node.deleteNode();
return list;
}
//-------------------------------------------------------------------
/**Prints the tree in recursively <b>preorder</b> manner.
*/
public void printTree()
throws RTreeException,FileNotFoundException
{
fileHdr.lockRead();
long root;
root = fileHdr.getRootIndex();
try{
trvsRPrePrint(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr));
}
catch(Exception e){
throw new RTreeException("RTree.printTree: "+e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
This method will return MBR of the whole tree.
*/
public Rect getTreeMBR()
{
fileHdr.lockRead();
long root;
try{
root = fileHdr.getRootIndex();
Node node = chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr);
return node.getNodeMBR();
}
catch(Exception e){
return null;
}
finally{
fileHdr.unlock();
}
}
public void deleteAllElements()
throws RTreeException
{
fileHdr.lockWrite();
try{
chdNodes.removeAll();
fileHdr.resetHeader();
}catch(Exception e){
throw new RTreeException("RTree.deleteAllElements : " + e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Traverses the tree recursively in post order.
*/
private void trvsRPrePrint(Node node)
throws IllegalValueException,
FileNotFoundException,NodeReadException,
IOException,NodeWriteException
{
if((node == null))
throw new IllegalValueException("RTree.trvsRPrePrint: Node is null");
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
System.out.println(node.toString());
for(int i=0; i<totElements; i++){//for every element
if(elmts[i].getElementType() == Node.NONLEAF_NODE){//non leaf
//Integer ndIndex = (Integer)elmts[i].getPtr();
trvsRPrePrint(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),fileHdr));
}
}
return;
}
//-----------------------------------------------------------------------
/**
<b>Read Me Well.</b><br>
The Nearest Neighbour(NN) search from Roussopoulos and Cheung.
<br>The returned leaf elements are sorted in ascending order of their
differences from the query point. <br><b>If the no. of leaf elements found
are less then <code>n</code>, then the rest of the values in the returend array
are null.<br></b>Give the limit(distance) within which you want to search.
The limit is in the same unit as the coordinates of the MBRs. <p>There may
be some objects in the tree which come within the region but are so big
that their size makes them extend much beyond the given region.
Such objects would also be considered.
If you do not want any limit then give <code>Long.MAX_VALUE</code> in <code>range</code>.
You can also search for presence of any object at the given point by giving
<code>range</code> as <code>0</code>.
Also required are the no. of objects that need to be fetched(N-Nearest objects).
<br><b>The value of <code>range</code> is actually square of the distance you want.
Therefor if you want to search in an area of 10cm, give the <code>range</code> as 100.</b>
@param pt the query point.
@param range the region within which you want to search.
@param n the number of objects required.
@return the leaf elements found near the point.The length of the returned
array would be equal to <code>n</code>.
*/
public ABL[] nearestSearch(Point pt,long range,int n)
throws RTreeException, IllegalValueException
{
fileHdr.lockRead();
if((pt == null) || (range < 0) || (n <= 0))
throw new IllegalValueException("RTree.nearestSearch: Illegal arguments");
try{
long root = fileHdr.getRootIndex();
ABL[] elmts = new ABL[n];
Nearest nrstDist = new Nearest();
nrstDist.value = range;
return INNSearch(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr),pt, elmts,nrstDist);
}
catch( IllegalValueException e){
throw new IllegalValueException(e.getMessage());
}
catch(Exception e){
throw new RTreeException("RTree.nearestSearch: " +e.getMessage());
}
finally{
fileHdr.unlock();
}
}
/**
Improved Nearest Neighbour Search - Cheung, theory Roussopoulos
*/
private ABL[] INNSearch(Node node, Point pt,ABL[] nrstElements,Nearest nrstDist)
throws IllegalValueException,FileNotFoundException,IOException,NodeReadException,
RTreeException,NodeWriteException
{
if((node == null))
throw new IllegalValueException("RTree.INNSearch: Node is null");
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
if(totElements == 0)//no elements
return null;
//System.out.println("In Node:"+node.getNodeIndex());
//if leaf
if(node.getElementType() == Node.LEAF_NODE){
//at leaf level compute distance to actual objects
for(int i=0; i<totElements; i++){
long lfDist = Rect.minDist(pt,elmts[i].
getRect());
if(lfDist <= nrstDist.value){//assign the new nearest value
//Integer index = (Integer)elmts[i].getPtr();
ABL newElmt = new ABL(new LeafElement(elmts[i].getRect(),elmts[i].getPtr()), lfDist);
insertArray(nrstElements,newElmt,nrstDist);
}
}
return nrstElements;
}
//if non-leaf
else{
//the Active Branch List
ABL[] abl = new ABL[totElements];
//calculate MINDIST and assign to the ABL array
for(int i=0; i<abl.length; i++){
//Integer ndIndex = (Integer)elmts[i].getPtr();
abl[i] = new ABL(new NonLeafElement(elmts[i].getRect(),elmts[i].getPtr()),
Rect.minDist(pt,elmts[i].getRect()));
}
//sort the ABL
abl[0].mergeSort(abl);
//upward prun the branch
for(int i=0; i<abl.length; i++){
if(abl[i].minDist <= nrstDist.value){
//Integer ndIndex = (Integer)abl[i].element.getPtr();
nrstElements = INNSearch(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr),pt,nrstElements,nrstDist);
}
}
return nrstElements;
}//else
}//INNSearch
/**
A utility method for the search algorithm that inserts an element into the
the correct position and adjusts the array accordingly.
Assumes that the new 'element' is lesser than the last element of 'arr'.
*/
protected void insertArray(ABL[] arr,ABL elmt,Nearest nrstDist)
throws RTreeException
{
try{
ABL temp=null,temp1=null;
//int nNearest = 0;
int i=arr.length-1;
//if the first or the only element to enter
if((arr[0] == null) || (arr.length == 1))
arr[0] = (ABL)elmt.clone();
else{
while((i < arr.length) && (i >= 0)){
if(arr[--i] != null){
if((arr[i].minDist <= elmt.minDist) ||
((i == 0)&&(elmt.minDist<arr[i].minDist))){//special case
if((i==0)&&(elmt.minDist<arr[i].minDist))//special case
i--;
temp = elmt;//the element to insert
//shift all elements one place right
while(++i < (arr.length)){
if(arr[i] != null){//if next is not null
temp1 = arr[i];//backup
arr[i] = (ABL)temp.clone();//replace
temp = temp1;//for the next loop
}
else{
arr[i] = (ABL)temp.clone();
break;
}
}
break;
}
}
}
}
//if last element is null(arr is not full) then that means that more
//elements can be accepted in the given region. Thus do not change
//the min. distance. Else the last element will be the min. - every
//element that comes after must have a value less than that.
if(arr[arr.length-1] != null)
nrstDist.value = arr[arr.length-1].minDist;
}
catch(Exception e){
throw new RTreeException("RTree.insertArray: "+e.getMessage());
}
}
//-------------------------------------------------------------------------
/**Another version of the <code>nearestSearch</code> method(not overloaded). This
method finds all the objects within the given range.<b> To understand
this method please refer to the other <code>nearestSearch</code> method.</b>
<br>When the no. of objects required is less then a few thousands, this
method would be many <b>times</b> slower than the above method.
If possible use the other method.
@return vector of ABL objects within the given range.
*/
public List nearestSearch(Point pt,long range)
throws RTreeException, IllegalValueException
{
fileHdr.lockRead();
if((pt == null) || (range < 0))
throw new IllegalValueException("RTree.nearestSearch: "
+"Point null or int less than one");
try{
long root = fileHdr.getRootIndex();
List elmts = new ArrayList();
elmts = INNSearch(chdNodes.getReadNode(fileHdr.getFile(),fileName,root,fileHdr),pt,elmts,range);
return elmts;
}
catch( IllegalValueException e){
throw new IllegalValueException(e.getMessage());
}
catch(Exception e){
throw new RTreeException("RTree.nearestSearch: " + e.getMessage());
}
finally{
fileHdr.unlock();
}
}
private List INNSearch(Node node, Point pt, List nrstElements,long nrstDist)
throws IllegalValueException, FileNotFoundException,IOException,NodeReadException, Exception
{
if((node == null))
throw new IllegalValueException("RTree.INNSearch: Node is null");
Element[] elmts = node.getAllElements();
int totElements = node.getTotalElements();
if(totElements == 0)//no elements
return null;
//System.out.println("In Node:"+node.getNodeIndex());
//if leaf
if(node.getElementType() == Node.LEAF_NODE){
//at leaf level compute distance to actual objects
for(int i=0; i<totElements; i++){
long lfDist = Rect.minDist(pt,elmts[i].
getRect());
if(lfDist <= nrstDist){//assign the new nearest value
//Integer index = (Integer)elmts[i].getPtr();
ABL newElmt = new ABL(new LeafElement(elmts[i].getRect(),elmts[i].getPtr()),lfDist);
//insert into vector
int j=0;
//the following line is doubtful
for(j = 0;
(j < nrstElements.size()) &&
(((ABL)(nrstElements.get(j))).minDist<=newElmt.minDist);
j++);
nrstElements.add(j, newElmt);
}
}
return nrstElements;
}
//if non-leaf
else{
//the Active Branch List
ABL[] abl = new ABL[totElements];
//calculate MINDIST and assign to the ABL array
for(int i=0; i<abl.length; i++){
//Integer ndIndex = (Integer)elmts[i].getPtr();
abl[i] = new ABL(new NonLeafElement(elmts[i].getRect(),elmts[i].getPtr()),
Rect.minDist(pt,elmts[i].getRect()));
}
//sort the ABL
abl[0].mergeSort(abl);
//upward prun the branch
for(int i=0; i<abl.length; i++){
if(abl[i].minDist <= nrstDist){
//Integer ndIndex = (Integer)abl[i].element.getPtr();
nrstElements = INNSearch(chdNodes.getReadNode(fileHdr.getFile(),fileName,elmts[i].getPtr(),
fileHdr), pt,nrstElements,nrstDist);
}
}
return nrstElements;
}//else
}//NNSearch
/**
this class is a wrapper class for a <code>long</code> value. There is no way to overwrite
the value contained in the <code>java.lang.Long</code> wrapper class.
*/
protected class Nearest//a wrapper class for long
{
public long value;
}
//--------------------------------get height------------------------------
/**Upto 5 lakh objectes we can have a maximum height of 3
TODO : Calculate other levels as well. One may need to know whether the tree is packed or unpacked for
this.
`*/
public synchronized int getHeight()
{
int totNodes = fileHdr.getTotalNodes();
if(totNodes <= 1)
return 1;
else if(totNodes <= 170)
return 2;
else //if((totNodes > (84*84)) && (totNodes < (169*169+1)))
return 3;
// else
//return 4;
}
}
/* TODO:
Immediate Improvements
1)Take the common code from each query methods and put them in a single method.
2)Make a way of retuning a Integer objects other than LeafElement Objects.
*/