//Pack.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.*;
import java.util.*;
/**
Modified again on 9/1/2003
This class can now be used Pack rtrees at run time i.e an rtree object can now be packed and later used
without recreating the rtree.
<p>Original comments
A utility class that packs a rtree.
<br>Pack is a special utility class. This class can also be used to maintain
a R-Tree after many insertions and deletions.(Just like defragmentation
in windows).
<p><b> Never create more than one instance of this class.
<br>Never run the methods of this class in any other thread except the main
thread and make sure it is the only thread running.
<br>Positively remember to reinitialise all the rtree objects after you
call <code>packTree</code>.</b>
@author Prachuryya Barua
*/
public class Pack
{
public Pack(){};
/**
Added this new method that takes a list of <code>Element</code>s and builds a
*/
public synchronized int packTree(List elmts, String newFile)
{
try{
if(elmts.size() <= Node.MAX){
RTree rtree = new RTree(newFile);
for(int i=0; i<elmts.size(); i++)
rtree.insert((LeafElement)elmts.get(i));
rtree.flush();
return 0;
}
return packTree((Element[])elmts.toArray(new Element[elmts.size()]), new RTree(newFile), newFile);
}catch(Exception e){
e.printStackTrace();
return 2;
}
}
/**
Sort-Tile-Recursive(STR) packing algo. by Leutenegger.
<p><b>**FLUSH THE RTREE BEFORE CALLING**</b>
<br>Prepocess the file and sort the rectangles
<br>Load into file
<br>Recursively pack above MBRs to nodes at the next level.
<br>If you give the new file name same as the old one then the old would
be overwritten. One word of caution, whichever new file name you give, it
would be overwritten.
@param rtree the rtree object to pack
@param newFile the new rtree file after packing
@return <b>0</b> if successfully created a new file,
<br><b>1</b> if there is no need to pack the file, in this case a new
file is not created and the old file is left untouched,
<br> Greater than zero if all fail.
*/
public synchronized int packTree(RTree rtree,String newFile)
{
try{
if(rtree == null)
throw new IllegalArgumentException("PackTree.packTree: rtree null");
List elmts = rtree.getAllElements();
//RTree.chdNodes.removeAll();
int ret = packTree((Element[])elmts.toArray(new Element[elmts.size()]), rtree, newFile);
return ret;
}catch(Exception e){
e.printStackTrace();
return 2;
}
}
public final int BUFFER_SIZE = 8192;
private int packTree(Element[] elmts, RTree rtree, String newFile)
{
try{
//long t = System.currentTimeMillis();
//rtree.flush();
File tmpPckFile = File.createTempFile("pack",null);
RandomAccessFile rFile = new RandomAccessFile(tmpPckFile.getAbsolutePath(),"rw");
if(newFile.equalsIgnoreCase(rtree.getFileName())){//we need a write lock
rtree.getFileHdr().lockWrite();
}
/*the following is required as we may pack an existing tree.. until we find a way to remove nodes of
a particular rtree*/
RTree.chdNodes.removeAll();
//rtree.getFileHdr().getFile().getFD().sync();
if(elmts.length <= Node.MAX)//change this for the first method
return(1);
System.out.println("Pack.packTree : Size of elmts: "+ elmts.length);
packRec(rFile,tmpPckFile,elmts,elmts.length);
//craete the new file
File fo = new File(newFile);
//delete the new file if it exists !!
if(fo.exists()){
fo.delete();
fo.createNewFile();
}
//overwrite the old rtree file with the temp file
FileInputStream fis=new FileInputStream(tmpPckFile);
FileOutputStream fos=new FileOutputStream(fo);
byte b[]=new byte[BUFFER_SIZE];
int i;
while((i=fis.read(b))!=-1){
fos.write(b, 0, i);
}
fos.close();
fis.close();
rFile.close();
tmpPckFile.deleteOnExit();
//System.out.println("Pack.packTree : packing took " + (System.currentTimeMillis() - t));
return(0);
}
catch(Exception e){
e.printStackTrace();
System.out.println("rtree.RTree.pack: Could not pack rtree, the destination file may be corrupted.");
return(2);
}
finally{//delete the source file header
synchronized(rtree){
//Here we have the old and the new file as same.. so we update the header
try{
rtree.updateHdr();
if(newFile.equalsIgnoreCase(rtree.getFileName())){//we need a write lock
rtree.getFileHdr().unlock();//relaese this lock as this header will be lost for ever
}
}catch(Exception e){
System.out.println("Pack.packTree : The pack tree is made but some other error hs occured. "
+"It is recomended to restart the application");
if(newFile.equalsIgnoreCase(rtree.getFileName()))//we need a write lock
rtree.getFileHdr().unlock();//relaese this lock as this header will be lost for ever
}
}//synchronized
}
}
private void packRec(RandomAccessFile rFile,File tmpPckFile, Element[] elmts,int length)
throws Exception
{
//P the no. of leaf nodes - ceil(objects/max objects per node)
Double temp = new Double(Node.MAX);//temp
temp = new Double(Math.ceil(length/temp.doubleValue()));
//int P = temp.intValue();//leaves
//no. of vertical slices
temp = new Double(Math.ceil(Math.sqrt(temp.doubleValue())));
int S = temp.intValue();
//System.out.println("total slices: "+S);
Slice sls[] = new Slice[S];
//sort all the rectangles on X axis
NonLeafElement.twoWayMerge(elmts,0,length-1,0);
//divide into slices
int start = 0;
int end;
for(int i=0; i<S; i++){
if((start + (S*Node.MAX)) <= length)
end = start+((S*Node.MAX)-1);
else
end = length - 1;
sls[i] = new Slice(start,end);
start = end+1;
}
//sort each slice on Y axis and write to file
for(int i=0; i<S; i++)
NonLeafElement.twoWayMerge(elmts,sls[i].start,sls[i].end,1);
int newLength = writePckFile(rFile,tmpPckFile,elmts,sls);
if(newLength == 1)//last insertion was a root
return;
packRec(rFile,tmpPckFile,elmts,newLength);
}
/**
Method that handles the low level details of nodes, leaves, file headers etc.
It writes the details to the rtree file.
@return the no. of new leaf elements created. The element themselves are in
the 'elmts' array.
*/
private int writePckFile(RandomAccessFile rFile,File tmpPckFile,
Element[] elmts,Slice[] sls)
throws Exception
{
FileHdr hdr = new FileHdr(Node.FREE_LIST_LIMIT,tmpPckFile.getAbsolutePath());
hdr.setBufferPolicy(true);
int length;
Double totNodes;//total nodes for the slice
int netNodes = 0;// !?!!
int l=0;//the position on to which the old elmt. would be replaced
for(int i=0; i<sls.length; i++){//for each slice
//cal. the length of this slice
length = sls[i].end - sls[i].start + 1;
//calculate the no. of new nodes for the slice
totNodes = new Double(length);
totNodes= new Double(Math.ceil(totNodes.doubleValue()/Node.MAX));
netNodes += totNodes.intValue();
//make nodes of the elements in the slice
for(int j=0; j<totNodes.intValue(); j++){//loop for each new node
Node node = new Node(rFile,tmpPckFile.getAbsolutePath(), Node.NOT_DEFINED,
elmts[sls[i].start].getElementType(), hdr);
//add elements to the node
ArrayList list = new ArrayList(Node.MAX);
for(int k=0; (k<Node.MAX) && (sls[i].start <= sls[i].end); k++){
//node.insertElement(elmts[sls[i].start++]);
list.add(elmts[sls[i].start++]);
}
node.insertElement((Element[])list.toArray(new Element[list.size()]), true);
//create the new nonleaf element - always nonleaf
NonLeafElement nlf = new NonLeafElement(node.getNodeMBR(),node.getNodeIndex());
elmts[l++] = (NonLeafElement)nlf.clone();
}
}
return netNodes;
}
/**An inner class for the packing method*/
class Slice
{
int start;
int end;
Slice(int start,int stop)
{
this.start = start;
this.end = stop;
}
}
/**A wrapper class for int*/
class Int
{
int val;
Int(int val)
{
this.val = val;
}
}
}