├── README.md ├── src ├── rstar │ ├── dto │ │ ├── AbstractDTO.java │ │ ├── MbrDTO.java │ │ ├── PointDTO.java │ │ ├── TreeDTO.java │ │ └── NodeDTO.java │ ├── interfaces │ │ ├── IDtoConvertible.java │ │ ├── IRStarNode.java │ │ ├── IDiskQuery.java │ │ └── ISpatialQuery.java │ ├── nodes │ │ ├── RStarNode.java │ │ ├── RStarLeaf.java │ │ ├── RStarInternal.java │ │ └── RStarSplit.java │ ├── spatial │ │ ├── SpatialComparator.java │ │ ├── SpatialPoint.java │ │ └── HyperRectangle.java │ ├── StorageManager.java │ └── RStarTree.java ├── util │ ├── Constants.java │ ├── Utils.java │ └── Trace.java └── Q1.java └── RStar.iml /README.md: -------------------------------------------------------------------------------- 1 | ## *** NOT Maintained 2 | 3 | A Simple implementation of the R* Tree in java. 4 | -------------------------------------------------------------------------------- /src/rstar/dto/AbstractDTO.java: -------------------------------------------------------------------------------- 1 | package rstar.dto; 2 | 3 | import java.io.Serializable; 4 | 5 | public abstract class AbstractDTO implements Serializable { 6 | } 7 | -------------------------------------------------------------------------------- /src/rstar/interfaces/IDtoConvertible.java: -------------------------------------------------------------------------------- 1 | package rstar.interfaces; 2 | 3 | import rstar.dto.AbstractDTO; 4 | 5 | public interface IDtoConvertible { 6 | public T toDTO(); 7 | } -------------------------------------------------------------------------------- /src/rstar/dto/MbrDTO.java: -------------------------------------------------------------------------------- 1 | package rstar.dto; 2 | 3 | public class MbrDTO extends AbstractDTO{ 4 | public float[][] points; 5 | 6 | public MbrDTO(float[][] points) { 7 | this.points = points; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/rstar/dto/PointDTO.java: -------------------------------------------------------------------------------- 1 | package rstar.dto; 2 | 3 | public class PointDTO extends AbstractDTO{ 4 | public float oid; 5 | public float[] coords; 6 | 7 | public PointDTO(float oid, float[] coords) { 8 | this.oid = oid; 9 | this.coords = coords; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/rstar/dto/TreeDTO.java: -------------------------------------------------------------------------------- 1 | package rstar.dto; 2 | 3 | public class TreeDTO extends AbstractDTO { 4 | public int dimension; 5 | public int pagesize; 6 | public long rootPointer; 7 | 8 | public TreeDTO(int dimension, int pagesize, long rootPointer) { 9 | this.dimension = dimension; 10 | this.pagesize = pagesize; 11 | this.rootPointer = rootPointer; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /RStar.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/rstar/interfaces/IRStarNode.java: -------------------------------------------------------------------------------- 1 | package rstar.interfaces; 2 | 3 | import rstar.spatial.HyperRectangle; 4 | 5 | public interface IRStarNode{ 6 | 7 | public boolean isLeaf(); 8 | 9 | public boolean isNotFull(); 10 | 11 | public int insert(T newChild); 12 | 13 | public HyperRectangle getMBR(); 14 | 15 | public void setMbr(HyperRectangle mbr); 16 | 17 | void createId(); 18 | 19 | long getNodeId(); 20 | 21 | void setNodeId(long nodeId); 22 | } 23 | -------------------------------------------------------------------------------- /src/rstar/dto/NodeDTO.java: -------------------------------------------------------------------------------- 1 | package rstar.dto; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class NodeDTO extends AbstractDTO { 6 | public ArrayList children; 7 | public MbrDTO mbr; 8 | public boolean isLeaf; 9 | public long parentId; 10 | 11 | public NodeDTO(long parentId, boolean leaf, MbrDTO mbr, ArrayList children) { 12 | this.parentId = parentId; 13 | isLeaf = leaf; 14 | this.mbr = mbr; 15 | this.children = children; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/rstar/interfaces/IDiskQuery.java: -------------------------------------------------------------------------------- 1 | package rstar.interfaces; 2 | 3 | import rstar.nodes.RStarNode; 4 | import rstar.dto.PointDTO; 5 | import rstar.dto.TreeDTO; 6 | 7 | import java.io.File; 8 | import java.io.FileNotFoundException; 9 | 10 | public interface IDiskQuery { 11 | void saveNode(RStarNode node); 12 | 13 | RStarNode loadNode(long nodeId) throws FileNotFoundException; 14 | 15 | long savePoint(PointDTO pointDTO); 16 | 17 | PointDTO loadPoint(long pointer); 18 | 19 | int saveTree(TreeDTO tree, File saveFile); 20 | 21 | TreeDTO loadTree(File saveFile); 22 | } 23 | -------------------------------------------------------------------------------- /src/util/Constants.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * User: Lokesh 5 | * Date: 4/4/12 6 | * Time: 1:41 AM 7 | */ 8 | public class Constants { 9 | public static final String TREE_DATA_DIRECTORY = "RStar_Data"; 10 | public static final String TREE_FILE = "MyRStarTree.rstar"; 11 | public static final String DATA_FILE = "dataStore.dat"; 12 | public static final String NODE_FILE_PREFIX = "rstar_"; 13 | public static final String NODE_FILE_SUFFIX = ".node"; 14 | 15 | public static int PAGESIZE = 8*1024; 16 | public static int MAX_CHILDREN; 17 | public static int MIN_CHILDREN; 18 | public static int DIMENSION = 2; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/util/Utils.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import rstar.spatial.SpatialPoint; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * User: Lokesh 9 | * Date: 1/4/12 10 | * Time: 10:00 PM 11 | */ 12 | public class Utils { 13 | private static long idSeed = 1; 14 | 15 | public static float getMedian(List list) { 16 | int size = list.size(); 17 | if(size % 2 == 0) 18 | return (list.get(size/2) + list.get(size/2 +1))/(float)2; 19 | else 20 | return list.get((size+1)/2); 21 | } 22 | 23 | public static synchronized long getRandomId() { 24 | return idSeed++; 25 | } 26 | 27 | public static String SpatialPointListToString(List list) { 28 | String output = ""; 29 | for (SpatialPoint point : list) { 30 | output += point.toString() +",\n"; 31 | } 32 | return output; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/rstar/interfaces/ISpatialQuery.java: -------------------------------------------------------------------------------- 1 | package rstar.interfaces; 2 | 3 | import rstar.spatial.SpatialPoint; 4 | 5 | import java.util.List; 6 | 7 | public interface ISpatialQuery { 8 | /** 9 | * inserts a point in the tree 10 | * @param point the point to be inserted 11 | * @return 1 if successfull, -1 otherwise 12 | */ 13 | int insert(SpatialPoint point); 14 | 15 | /** 16 | * returns the oid of the supplied point 17 | * in the tree if present 18 | * @param point the point to be searched 19 | * @return float oid 20 | */ 21 | float pointSearch(SpatialPoint point); 22 | 23 | /** 24 | * returns all points in distance range of 25 | * point center 26 | * @return List of points in the range 27 | */ 28 | List rangeSearch(SpatialPoint center, double range); 29 | 30 | /** 31 | * returns the k nearest neighbours of center 32 | * @return List of k nearest neighbours of center 33 | */ 34 | List knnSearch(SpatialPoint center, int k); 35 | } 36 | -------------------------------------------------------------------------------- /src/rstar/nodes/RStarNode.java: -------------------------------------------------------------------------------- 1 | package rstar.nodes; 2 | 3 | import rstar.interfaces.IDtoConvertible; 4 | import rstar.interfaces.IRStarNode; 5 | import rstar.spatial.HyperRectangle; 6 | import util.Utils; 7 | 8 | import java.util.ArrayList; 9 | 10 | public abstract class RStarNode implements IDtoConvertible, IRStarNode{ 11 | protected long nodeId = -1; 12 | protected static int _dimension; 13 | protected HyperRectangle mbr; 14 | public ArrayList childPointers; //ids of all children = file names are derivable from this. 15 | 16 | private Long parentId; 17 | 18 | public Long getParentId() { 19 | return parentId; 20 | } 21 | 22 | @Override 23 | public void createId() { 24 | if (nodeId == -1) { 25 | nodeId = Utils.getRandomId(); 26 | if(nodeId < 0) 27 | nodeId = -1 * nodeId; 28 | } 29 | } 30 | 31 | public void setParentId(Long parentId) { 32 | this.parentId = parentId; 33 | } 34 | 35 | @Override 36 | public long getNodeId() { 37 | createId(); 38 | return nodeId; 39 | } 40 | 41 | @Override 42 | public void setNodeId(long nodeId1){ 43 | this.nodeId = nodeId1; 44 | } 45 | } -------------------------------------------------------------------------------- /src/util/Trace.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.io.RandomAccessFile; 7 | import java.nio.ByteBuffer; 8 | import java.nio.channels.FileChannel; 9 | 10 | public class Trace { 11 | 12 | public static int VERBOSE_MODE = 1; 13 | public static int WRITE_LOG = 0; 14 | 15 | private static FileChannel fc; 16 | private RandomAccessFile logfile; 17 | 18 | public static Trace getLogger(String classname){ 19 | Trace logger = new Trace(); 20 | if(WRITE_LOG == 1){ 21 | try { 22 | File f = new File(classname+"_Log.txt"); 23 | logger.logfile = new RandomAccessFile(f, "rw"); 24 | fc = logger.logfile.getChannel(); 25 | } catch (FileNotFoundException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | return logger; 30 | } 31 | public void trace(String str) { 32 | if(VERBOSE_MODE == 1) 33 | System.out.println(str); 34 | 35 | if(WRITE_LOG == 1){ 36 | try { 37 | fc.write(ByteBuffer.wrap((str+"\n").getBytes())); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | public void traceInline(String str) { 44 | if(VERBOSE_MODE == 1) 45 | System.out.print(str); 46 | 47 | if(WRITE_LOG == 1){ 48 | try { 49 | fc.write(ByteBuffer.wrap((str).getBytes())); 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } 53 | } 54 | } 55 | 56 | public void traceError(String str) { 57 | if(VERBOSE_MODE == 1) 58 | System.err.println(str); 59 | 60 | if(WRITE_LOG == 1){ 61 | try { 62 | fc.write(ByteBuffer.wrap(("Error: "+str+"\n").getBytes())); 63 | } catch (IOException e) { 64 | e.printStackTrace(); 65 | } 66 | } 67 | } 68 | } 69 | 70 | -------------------------------------------------------------------------------- /src/rstar/nodes/RStarLeaf.java: -------------------------------------------------------------------------------- 1 | package rstar.nodes; 2 | 3 | import rstar.dto.NodeDTO; 4 | import rstar.spatial.HyperRectangle; 5 | import rstar.spatial.SpatialPoint; 6 | import util.Constants; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class RStarLeaf extends RStarNode { 11 | public ArrayList loadedChildren; 12 | 13 | public RStarLeaf(int dimension) { 14 | createId(); 15 | _dimension = dimension; 16 | loadedChildren = new ArrayList(); 17 | childPointers = new ArrayList(); 18 | mbr = new HyperRectangle(dimension); 19 | } 20 | 21 | public RStarLeaf(NodeDTO dto, long nodeId) { 22 | this.nodeId = nodeId; 23 | this.setParentId(dto.parentId); 24 | _dimension = Constants.DIMENSION; 25 | childPointers = dto.children; 26 | loadedChildren = new ArrayList(); 27 | mbr = new HyperRectangle(dto.mbr); 28 | } 29 | 30 | @Override 31 | public boolean isLeaf() { 32 | return true; 33 | } 34 | 35 | @Override 36 | public boolean isNotFull() { 37 | return ((childPointers.size() + loadedChildren.size()) < Constants.MAX_CHILDREN); 38 | } 39 | 40 | @Override 41 | public int insert(T newChild) { 42 | if (this.isNotFull()) { 43 | loadedChildren.add((SpatialPoint) newChild); 44 | mbr.update((SpatialPoint) newChild); 45 | return 1; 46 | } 47 | else return -1; 48 | } 49 | 50 | @Override 51 | public HyperRectangle getMBR() { 52 | return mbr; 53 | } 54 | 55 | @Override 56 | public void setMbr(HyperRectangle mbr) { 57 | this.mbr = mbr; 58 | } 59 | 60 | @Override 61 | public NodeDTO toDTO() { 62 | return new NodeDTO(getParentId(), true, mbr.toDTO(), childPointers); 63 | } 64 | 65 | public boolean hasUnsavedPoints(){ 66 | return loadedChildren.size() > 0; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/rstar/nodes/RStarInternal.java: -------------------------------------------------------------------------------- 1 | package rstar.nodes; 2 | 3 | import rstar.dto.NodeDTO; 4 | import rstar.spatial.HyperRectangle; 5 | import rstar.spatial.SpatialPoint; 6 | import util.Constants; 7 | 8 | import java.util.ArrayList; 9 | 10 | public class RStarInternal extends RStarNode { 11 | // private ArrayList children; 12 | 13 | public RStarInternal(int dimension) { 14 | createId(); 15 | _dimension = dimension; 16 | childPointers = new ArrayList(Constants.MAX_CHILDREN); 17 | mbr = new HyperRectangle(dimension); 18 | } 19 | 20 | public RStarInternal(NodeDTO dto, long nodeId) { 21 | this.nodeId = nodeId; 22 | this.setParentId(dto.parentId); 23 | this.childPointers = dto.children; 24 | // children = new ArrayList(Constants.MAX_CHILDREN); 25 | this.mbr = new HyperRectangle(dto.mbr); 26 | } 27 | 28 | @Override 29 | public boolean isLeaf() { 30 | return false; 31 | } 32 | 33 | @Override 34 | public boolean isNotFull() { 35 | return childPointers.size() < Constants.MAX_CHILDREN; 36 | } 37 | 38 | @Override 39 | public int insert(T newChild) { 40 | if (this.isNotFull() && newChild instanceof RStarNode) { 41 | ((RStarNode) newChild).setParentId(this.nodeId); 42 | childPointers.add(((RStarNode) newChild).getNodeId()); 43 | mbr.update(((RStarNode) newChild).getMBR()); 44 | return 1; 45 | } 46 | else return -1; 47 | } 48 | 49 | @Override 50 | public HyperRectangle getMBR() { 51 | return mbr; 52 | } 53 | 54 | @Override 55 | public void setMbr(HyperRectangle mbr) { 56 | this.mbr = mbr; 57 | } 58 | 59 | public double deltaV_onInclusion(SpatialPoint newPoint) { 60 | HyperRectangle pointmbr = new HyperRectangle(_dimension); 61 | pointmbr.update(newPoint); 62 | return mbr.deltaV_onInclusion(pointmbr); 63 | } 64 | 65 | @Override 66 | public NodeDTO toDTO() { 67 | return new NodeDTO(getParentId(), false, mbr.toDTO(), childPointers); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/rstar/spatial/SpatialComparator.java: -------------------------------------------------------------------------------- 1 | package rstar.spatial; 2 | 3 | import rstar.nodes.RStarNode; 4 | import util.Constants; 5 | 6 | import java.util.Comparator; 7 | 8 | /** 9 | * Comparator for comparing two mbr's in a particular 10 | * dimension only either the high value or the lower value 11 | */ 12 | public class SpatialComparator implements Comparator { 13 | private int dimension; 14 | private int order; 15 | 16 | /** 17 | * @param dimension the dimension along which the points 18 | * are to be sorted 19 | * @param cordToSort the value(higher or lower) used for sorting 20 | * the given dimension 21 | * @see HyperRectangle#MAX_CORD 22 | * @see HyperRectangle#MIN_CORD 23 | */ 24 | public SpatialComparator(int dimension, int cordToSort) { 25 | this.dimension = dimension; 26 | this.order = cordToSort; 27 | } 28 | 29 | /** 30 | * Compares the two specified MBRs according to 31 | * the sorting dimension and the sorting co-ordinate for the dimension 32 | * of this Comparator. 33 | * 34 | * @param o1 the first MBR or SpatialPoint or RStarNode 35 | * @param o2 the second MBR or SpatialPoint or RStarNode 36 | * @return a negative integer, zero, or a positive integer as the 37 | * first argument is less than, equal to, or greater than the 38 | * second. 39 | */ 40 | @Override 41 | public int compare(Object o1, Object o2) { 42 | HyperRectangle mbr1, mbr2; 43 | if (o1 instanceof SpatialPoint) { 44 | SpatialPoint[] temp = new SpatialPoint[1]; 45 | temp[0] = (SpatialPoint) o1; 46 | mbr1 = new HyperRectangle(Constants.DIMENSION, temp); 47 | temp[0] = (SpatialPoint)o2; 48 | mbr2 = new HyperRectangle(Constants.DIMENSION, temp); 49 | } 50 | else if (o1 instanceof RStarNode) { 51 | mbr1 = ((RStarNode)o1).getMBR(); 52 | mbr2 = ((RStarNode)o2).getMBR(); 53 | } else { 54 | mbr1 = (HyperRectangle) o1; 55 | mbr2 = (HyperRectangle) o2; 56 | } 57 | 58 | int answer = 0; 59 | if (mbr1.getPoints()[dimension][order] < mbr2.getPoints()[dimension][order]) 60 | answer = -1; 61 | 62 | if (mbr1.getPoints()[dimension][order] > mbr2.getPoints()[dimension][order]) 63 | answer = 1; 64 | 65 | return answer; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/rstar/spatial/SpatialPoint.java: -------------------------------------------------------------------------------- 1 | package rstar.spatial; 2 | 3 | import rstar.dto.PointDTO; 4 | import rstar.interfaces.IDtoConvertible; 5 | 6 | public class SpatialPoint implements IDtoConvertible { 7 | private int _dimension; 8 | private float[] _cords; 9 | private float _oid; 10 | 11 | public SpatialPoint() { 12 | } 13 | 14 | public SpatialPoint(int dimension) { 15 | this._dimension = dimension; 16 | this._oid = -1; 17 | } 18 | 19 | public SpatialPoint(float[] cords) { 20 | this._cords = cords; 21 | this._dimension = cords.length; 22 | this._oid = -1; 23 | } 24 | 25 | public SpatialPoint(float[] cords, float oid) { 26 | this._cords = cords; 27 | this._dimension = cords.length; 28 | this._oid = oid; 29 | } 30 | 31 | public SpatialPoint(PointDTO dto) { 32 | this._cords = dto.coords; 33 | this._dimension = dto.coords.length; 34 | this._oid = dto.oid; 35 | } 36 | 37 | public int getDimension(){ 38 | return _dimension; 39 | } 40 | 41 | public void setCords(float[] data){ 42 | this._cords = data; 43 | } 44 | 45 | public float[] getCords() { 46 | return _cords; 47 | } 48 | 49 | public float getOid() { 50 | return _oid; 51 | } 52 | 53 | public void setOid(float oid) { 54 | this._oid = oid; 55 | } 56 | 57 | /** 58 | * calculate distance of this point with
otherPoint
59 | * @param otherPoint the point from which this point's 60 | * distance is to be calculated 61 | * @return distance from
otherPoint
62 | */ 63 | public float distance(SpatialPoint otherPoint) { 64 | float[] otherPoints = otherPoint.getCords(); 65 | float distance = 0; 66 | for (int i = 0; i < _cords.length; i++) { 67 | float tmp = (_cords[i] * _cords[i]) - (otherPoints[i] * otherPoints[i]); 68 | if(tmp < 0) 69 | tmp = -1 * tmp; 70 | 71 | distance += tmp; 72 | } 73 | return (float)Math.pow(distance, 0.5); 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | StringBuilder str = new StringBuilder("["); 79 | for (double cord : _cords) { 80 | str.append(cord).append(","); 81 | } 82 | str.append("]"); 83 | return str.toString(); 84 | } 85 | 86 | @Override 87 | public PointDTO toDTO() { 88 | return new PointDTO(_oid, _cords); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/rstar/spatial/HyperRectangle.java: -------------------------------------------------------------------------------- 1 | package rstar.spatial; 2 | 3 | import rstar.nodes.RStarNode; 4 | import rstar.dto.MbrDTO; 5 | import rstar.interfaces.IDtoConvertible; 6 | import util.Constants; 7 | 8 | import java.util.List; 9 | 10 | public class HyperRectangle implements IDtoConvertible { 11 | private int _dimension; 12 | /** 13 | * points is a 2D double array containing 14 | * the max and min values for each dimension 15 | * in the rectangle. 16 | */ 17 | private float[][] points; 18 | public static final int MAX_CORD = 0; 19 | public static final int MIN_CORD = 1; 20 | 21 | public float[][] getPoints() { 22 | return points; 23 | } 24 | 25 | public void setPoints(float[][] points) { 26 | this.points = points; 27 | } 28 | 29 | public HyperRectangle(int dimension) { 30 | this._dimension = dimension; 31 | points = new float[dimension][2]; 32 | } 33 | 34 | public HyperRectangle(int dimension, SpatialPoint[] points) { 35 | this._dimension = dimension; 36 | this.points = new float[dimension][2]; 37 | 38 | update(points); 39 | } 40 | 41 | public HyperRectangle(int dimension, List points) { 42 | this._dimension = dimension; 43 | this.points = new float[dimension][2]; 44 | update(points); 45 | /*if (points.get(0) instanceof RStarNode) 46 | update(List points); 47 | else 48 | update((List) points);*/ 49 | } 50 | 51 | public HyperRectangle(MbrDTO dto) { 52 | this._dimension = Constants.DIMENSION; 53 | this.points = dto.points; 54 | } 55 | 56 | public HyperRectangle(float[] cords) { 57 | this._dimension = cords.length; 58 | points = new float[_dimension][2]; 59 | for (int i = 0; i < _dimension; i++) { 60 | points[i][MAX_CORD] = cords[i]; 61 | points[i][MIN_CORD] = cords[i]; 62 | } 63 | } 64 | 65 | public void update(SpatialPoint newPoint) { 66 | SpatialPoint[] newPoints = new SpatialPoint[1]; 67 | newPoints[0] = newPoint; 68 | update(newPoints); 69 | } 70 | 71 | private void update(SpatialPoint[] newPoints) { 72 | for (SpatialPoint newPoint : newPoints) { 73 | float[] cord = newPoint.getCords(); 74 | assert cord.length == _dimension; 75 | for (int i = 0; i < cord.length; i++) { 76 | if (points[i][MAX_CORD] == 0 || points[i][MAX_CORD] < cord[i]) { 77 | points[i][MAX_CORD] = cord[i]; 78 | } 79 | if (points[i][MIN_CORD] == 0 || points[i][MIN_CORD] > cord[i]) { 80 | points[i][MIN_CORD] = cord[i]; 81 | } 82 | } 83 | } 84 | } 85 | 86 | private void update(List newPoints) { 87 | if (newPoints.get(0) instanceof SpatialPoint) { 88 | for (T newPoint : newPoints) { 89 | float[] cord = ((SpatialPoint) newPoint).getCords(); 90 | assert cord.length == _dimension; 91 | for (int i = 0; i < cord.length; i++) { 92 | if (points[i][MAX_CORD] == 0 || points[i][MAX_CORD] < cord[i]) { 93 | points[i][MAX_CORD] = cord[i]; 94 | } 95 | if (points[i][MIN_CORD] == 0 || points[i][MIN_CORD] > cord[i]) { 96 | points[i][MIN_CORD] = cord[i]; 97 | } 98 | } 99 | } 100 | } else if (newPoints.get(0) instanceof RStarNode) { 101 | for (T node : newPoints) { 102 | update(((RStarNode) node).getMBR()); 103 | } 104 | } 105 | } 106 | 107 | private void update(RStarNode[] nodes) { 108 | for (RStarNode node : nodes) { 109 | update(node.getMBR()); 110 | } 111 | } 112 | 113 | // private void update(List nodes) { 114 | // for (RStarNode node : nodes) { 115 | // update(node.getMBR()); 116 | // } 117 | // } 118 | 119 | public void update(HyperRectangle addedRegion) { 120 | float[][] newPoints = addedRegion.getPoints(); 121 | assert newPoints.length == _dimension; 122 | for (int j = 0; j < _dimension; j++) { 123 | if (points[j][MAX_CORD] == 0 || points[j][MAX_CORD] < newPoints[j][MAX_CORD]) { 124 | points[j][MAX_CORD] = newPoints[j][MAX_CORD]; 125 | } 126 | if (points[j][MIN_CORD] == 0 || points[j][MIN_CORD] > newPoints[j][MIN_CORD]) { 127 | points[j][MIN_CORD] = newPoints[j][MIN_CORD]; 128 | } 129 | } 130 | } 131 | 132 | /** 133 | * finds the intersecting region of this MBR with otherMBR 134 | * @param otherMBR the mbr with which this mbr's intersection 135 | * is to be calculated 136 | * @return the intersecting region, null if not intersecting 137 | */ 138 | public HyperRectangle getIntersection(HyperRectangle otherMBR) { 139 | float[][] interPoints = new float[_dimension][2]; 140 | float[][] newPoints = otherMBR.getPoints(); 141 | assert newPoints.length == _dimension; 142 | 143 | boolean intersectExists = true; 144 | for (int i = 0; i < _dimension; i++) { 145 | if ((points[i][MAX_CORD] < newPoints[i][MIN_CORD]) || (points[i][MIN_CORD] > newPoints[i][MAX_CORD])) { 146 | intersectExists = false; 147 | break; 148 | } 149 | interPoints[i][MAX_CORD] = Math.min(newPoints[i][MAX_CORD], points[i][MAX_CORD]); 150 | interPoints[i][MIN_CORD] = Math.max(newPoints[i][MIN_CORD], points[i][MIN_CORD]); 151 | } 152 | 153 | if (!intersectExists) { 154 | return null; 155 | } 156 | HyperRectangle intersect = new HyperRectangle(_dimension); 157 | intersect.setPoints(interPoints); 158 | return intersect; 159 | } 160 | 161 | /** 162 | * finds the increment in volume of the newMbr is 163 | * added 164 | * @return 0 if no incement 165 | */ 166 | public double deltaV_onInclusion(HyperRectangle newmbr) { 167 | HyperRectangle tempMbr = new HyperRectangle(_dimension); 168 | tempMbr.setPoints(points); 169 | tempMbr.update(newmbr); 170 | 171 | return tempMbr.volume() - this.volume(); 172 | } 173 | 174 | /** 175 | * Computes the volume of this MBR. 176 | * 177 | * @return the volume of this MBR 178 | */ 179 | public double volume() { 180 | double vol = 1; 181 | for (float[] point : points) { 182 | vol *= point[MAX_CORD] - point[MIN_CORD]; 183 | } 184 | return vol; 185 | } 186 | 187 | /** 188 | * Computes the margin of this MBR. 189 | * 190 | * @return the margin of this MBR 191 | */ 192 | public double margin() { 193 | double margin = 0; 194 | for (float[] point : points) { 195 | margin += point[MAX_CORD] - point[MIN_CORD]; 196 | } 197 | return margin; 198 | } 199 | 200 | /** 201 | * Computes the volume of the overlapping box between this MBR and the given MBR 202 | * and return the relation between the volume of the overlapping box and the volume of both MBRs. 203 | * 204 | * @param mbr the MBR for which the intersection volume with this MBR should be computed 205 | * @return the relation between the volume of the overlapping box and the volume of this MBR 206 | * and the given MBR 207 | */ 208 | public double overlap(HyperRectangle mbr) { 209 | HyperRectangle intersect = this.getIntersection(mbr); 210 | if (intersect == null) { 211 | return 0; 212 | } 213 | return intersect.volume(); 214 | } 215 | 216 | /** 217 | * Computes the union MBR of this MBR and the given MBR. 218 | * 219 | * @param mbr the MBR to be united with this MBR 220 | * @return the union MBR of this MBR and the given MBR 221 | */ 222 | public HyperRectangle union(HyperRectangle mbr) { 223 | if (this._dimension != mbr._dimension) 224 | throw new IllegalArgumentException("This MBR and the given MBR need same dimensionality"); 225 | 226 | float[][] otherPoints = mbr.getPoints(); 227 | float[][] unionPoints = new float[_dimension][2]; 228 | 229 | for (int i = 0; i < this._dimension; i++) { 230 | unionPoints[i][MIN_CORD] = Math.min(this.points[i][MIN_CORD], otherPoints[i][MIN_CORD]); 231 | unionPoints[i][MAX_CORD] = Math.max(this.points[i][MAX_CORD], otherPoints[i][MAX_CORD]); 232 | } 233 | HyperRectangle union = new HyperRectangle(_dimension); 234 | union.setPoints(unionPoints); 235 | return union; 236 | } 237 | 238 | @Override 239 | public MbrDTO toDTO() { 240 | return new MbrDTO(points); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/rstar/StorageManager.java: -------------------------------------------------------------------------------- 1 | package rstar; 2 | 3 | import rstar.dto.NodeDTO; 4 | import rstar.dto.PointDTO; 5 | import rstar.dto.TreeDTO; 6 | import rstar.interfaces.IDiskQuery; 7 | import rstar.nodes.RStarInternal; 8 | import rstar.nodes.RStarLeaf; 9 | import rstar.nodes.RStarNode; 10 | import util.Constants; 11 | 12 | import java.io.*; 13 | import java.nio.ByteBuffer; 14 | import java.nio.channels.FileChannel; 15 | 16 | /** 17 | * provides all disk related functionality like 18 | * loading and saving of nodes, points and tree. 19 | */ 20 | public class StorageManager implements IDiskQuery { 21 | RandomAccessFile dataStore; 22 | FileChannel dataChannel; 23 | 24 | public StorageManager() { 25 | try { 26 | dataStore = new RandomAccessFile(Constants.DATA_FILE, "rw"); 27 | dataChannel = dataStore.getChannel(); 28 | } catch (FileNotFoundException e) { 29 | System.err.println("Data File failed to be loaded/created. Exiting"); 30 | System.exit(1); 31 | } 32 | } 33 | 34 | @Override 35 | public void saveNode(RStarNode node) { 36 | if (node.isLeaf()) { 37 | try { 38 | RStarLeaf leaf = (RStarLeaf) node; 39 | 40 | if (leaf.hasUnsavedPoints()) { 41 | //save unsaved points to disk first. 42 | for (int i = leaf.loadedChildren.size() - 1; i >= 0; i--) { 43 | leaf.childPointers.add(savePoint(leaf.loadedChildren.remove(i).toDTO())); 44 | } 45 | } 46 | 47 | FileOutputStream fos = new FileOutputStream(new File(constructFilename(leaf.getNodeId()))); 48 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 49 | ObjectOutputStream oos = new ObjectOutputStream(bos); 50 | oos.writeObject(leaf.toDTO()); 51 | oos.flush(); 52 | 53 | fos.write(bos.toByteArray()); 54 | oos.close(); 55 | fos.close(); 56 | 57 | } catch (FileNotFoundException e) { 58 | System.err.println("Exception while saving node to disk"); 59 | } catch (IOException e) { 60 | System.err.println("Exception while saving node to disk"); 61 | } 62 | } else { 63 | try { 64 | RStarInternal internal = (RStarInternal) node; 65 | 66 | FileOutputStream fos = new FileOutputStream(new File(constructFilename(internal.getNodeId()))); 67 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 68 | ObjectOutputStream oos = new ObjectOutputStream(bos); 69 | oos.writeObject(internal.toDTO()); 70 | oos.flush(); 71 | 72 | fos.write(bos.toByteArray()); 73 | oos.close(); 74 | fos.close(); 75 | 76 | } catch (FileNotFoundException e) { 77 | System.err.println("Exception while saving node to disk"); 78 | } catch (IOException e) { 79 | System.err.println("Exception while saving node to disk"); 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public RStarNode loadNode(long nodeId) throws FileNotFoundException { 86 | return nodeFromDisk(constructFilename(nodeId)); 87 | } 88 | 89 | /** 90 | * saves a Spatial Point to dataFile on disk and 91 | * returns the offset of the point in the file. 92 | * 93 | * @param pointDTO DTO of the point to be saved 94 | * @return the location where the point was saved in 95 | * datafile 96 | */ 97 | @Override 98 | public long savePoint(PointDTO pointDTO) { 99 | try { 100 | long pos = dataStore.length(); 101 | dataStore.seek(pos); 102 | 103 | ByteArrayOutputStream bos = new ByteArrayOutputStream(); 104 | ObjectOutputStream oos = new ObjectOutputStream(bos); 105 | oos.writeObject(pointDTO); 106 | oos.flush(); 107 | 108 | dataChannel.write(ByteBuffer.wrap(bos.toByteArray())); 109 | oos.close(); 110 | return pos; 111 | } catch (IOException e) { 112 | System.err.println("Exception occurred while saving data to disk."); 113 | return -1; 114 | } 115 | } 116 | 117 | /** 118 | * loads a SpatialPoint from dataFile 119 | * @param pointer the offset of the point 120 | * in dataFile 121 | * @return DTO of the point. Full SpatialPoint 122 | * can be easily constructed from the DTO 123 | */ 124 | @Override 125 | public PointDTO loadPoint(long pointer) { 126 | try { 127 | dataStore.seek(pointer); 128 | ObjectInputStream ois = getPointObjectStream(); 129 | PointDTO pointDTO = (PointDTO) ois.readObject(); 130 | ois.close(); 131 | return pointDTO; 132 | 133 | } catch (IOException e) { 134 | System.err.println("Exception occurred while loading point from disk."); 135 | } catch (ClassNotFoundException e) { 136 | System.err.println("Exception occurred while loading point from disk."); 137 | } 138 | return null; 139 | } 140 | 141 | private RStarNode nodeFromDisk(String filename) throws FileNotFoundException { 142 | try { 143 | FileInputStream fis = new FileInputStream(filename); 144 | ObjectInputStream ois = new ObjectInputStream(fis); 145 | NodeDTO dto = (NodeDTO) ois.readObject(); 146 | ois.close(); 147 | 148 | RStarNode result; 149 | if (dto.isLeaf) 150 | result = new RStarLeaf(dto, nodeIdFromFilename(filename)); 151 | else 152 | result = new RStarInternal(dto, nodeIdFromFilename(filename)); 153 | 154 | return result; 155 | } catch (IOException e) { 156 | System.err.println(e.getMessage()); 157 | } catch (ClassNotFoundException e) { 158 | System.err.println("ClassNotFoundException occurred while loading node from disk"); 159 | } 160 | return null; 161 | } 162 | 163 | /** 164 | * saves the R* Tree to saveFile. 165 | * doesn't use RandomAccessFile 166 | * @param tree the DTO of the tree to be saved 167 | * @param saveFile saveNode file location 168 | * @return 1 is successful, else -1 169 | */ 170 | @Override 171 | public int saveTree(TreeDTO tree, File saveFile) { 172 | int status = -1; 173 | try { 174 | if(saveFile.exists()) { 175 | saveFile.delete(); 176 | } 177 | 178 | FileOutputStream fos = new FileOutputStream(saveFile); 179 | ObjectOutputStream oos = new ObjectOutputStream(fos); 180 | 181 | oos.writeObject(tree); 182 | oos.flush(); 183 | oos.close(); 184 | status = 1; // successful saveNode 185 | } catch (IOException e) { 186 | System.err.println("Error while saving Tree to " + saveFile.toURI()); 187 | } 188 | return status; 189 | } 190 | 191 | /** 192 | * loads a R* Tree from disk 193 | * @param saveFile the file to loadNode the tree from 194 | * @return DTO of the loaded R* Tree, null if none found 195 | * @throws FileNotFoundException 196 | */ 197 | @Override 198 | public TreeDTO loadTree(File saveFile) { 199 | try { 200 | FileInputStream fis = new FileInputStream(saveFile); 201 | ObjectInputStream ois = new ObjectInputStream(fis); 202 | 203 | return (TreeDTO) ois.readObject(); 204 | 205 | } catch (IOException e) { 206 | System.err.println("Exception while loading tree from " + saveFile); 207 | } catch (ClassNotFoundException e) { 208 | System.err.println("Exception while loading tree from " + saveFile); 209 | } 210 | return null; 211 | } 212 | 213 | public String constructFilename(long nodeId) { 214 | return Constants.TREE_DATA_DIRECTORY + "/" + Constants.NODE_FILE_PREFIX + nodeId + Constants.NODE_FILE_SUFFIX; 215 | } 216 | 217 | public long nodeIdFromFilename(String filename) { 218 | int i2 = filename.indexOf(Constants.NODE_FILE_SUFFIX); 219 | assert i2 != -1; 220 | return Long.parseLong(filename.substring((Constants.TREE_DATA_DIRECTORY+"/"+Constants.NODE_FILE_PREFIX).length(), i2)); 221 | } 222 | 223 | private ObjectInputStream getPointObjectStream() throws IOException { 224 | return new ObjectInputStream(new InputStream() { 225 | @Override 226 | public int read() throws IOException { 227 | return dataStore.read(); 228 | } 229 | 230 | @Override 231 | public int read(byte[] b, int off, int len) throws IOException { 232 | return dataStore.read(b, off, len); 233 | } 234 | }); 235 | } 236 | 237 | public void createDataDir(File saveFile) { 238 | // check for the node-data directory. create one if doesn't exist 239 | File dataDir = new File(saveFile.getParentFile(), Constants.TREE_DATA_DIRECTORY); 240 | if (!dataDir.exists() || !dataDir.isDirectory()) { 241 | if (!dataDir.mkdir()) { 242 | System.err.println("Failed to create data directory of the tree. Exiting.."); 243 | System.exit(1); 244 | } 245 | System.out.println("Data directory created"); 246 | } 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /src/Q1.java: -------------------------------------------------------------------------------- 1 | import rstar.RStarTree; 2 | import rstar.spatial.SpatialPoint; 3 | import util.Trace; 4 | import util.Utils; 5 | 6 | import java.io.*; 7 | import java.util.ArrayList; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | import static util.Utils.getMedian; 12 | 13 | public class Q1 { 14 | 15 | private RStarTree tree; 16 | private int dimension; 17 | private String inputFile; 18 | private String resultFile; 19 | private List insertRunTime; 20 | private List searchRunTime; 21 | private List rangeRuntime; 22 | private List knnRuntime; 23 | private Trace logger; 24 | 25 | public static void main(String[] args) { 26 | Q1 controller = new Q1(args); 27 | 28 | System.out.println("Reading input file ..."); 29 | controller.processInput(); 30 | System.out.println("Finished Processing file ..."); 31 | 32 | controller.writeRuntimeToFile(controller.insertRunTime, "Insertion_runtime.txt"); 33 | controller.writeRuntimeToFile(controller.searchRunTime, "Search_runtime.txt"); 34 | controller.writeRuntimeToFile(controller.rangeRuntime, "RangeSearch_runtime.txt"); 35 | controller.writeRuntimeToFile(controller.knnRuntime, "KNNSearch_runtime.txt"); 36 | 37 | controller.printResults(); 38 | } 39 | 40 | public Q1(String[] args) { 41 | if(args.length >= 2){ 42 | this.inputFile = args[0]; 43 | this.dimension = Integer.parseInt(args[1]); 44 | 45 | if (args.length >= 3) 46 | this.resultFile = args[2]; 47 | else 48 | this.resultFile = this.getClass().getSimpleName()+ "_Results.txt"; 49 | 50 | } else { 51 | this.printUsage(); 52 | System.exit(1); 53 | } 54 | tree = new RStarTree(dimension); 55 | this.insertRunTime = new ArrayList(); 56 | this.searchRunTime = new ArrayList(); 57 | this.rangeRuntime = new ArrayList(); 58 | this.knnRuntime = new ArrayList(); 59 | logger = Trace.getLogger(this.getClass().getSimpleName()); 60 | } 61 | 62 | 63 | protected void processInput() { 64 | float opType, oid, k; 65 | double range; 66 | float[] point; 67 | long start, end; 68 | int lineNum = 0; 69 | 70 | try { 71 | BufferedReader input = new BufferedReader(new FileReader(this.inputFile)); 72 | String line; 73 | String[] lineSplit; 74 | 75 | while ((line = input.readLine()) != null) { 76 | lineNum++; 77 | lineSplit = line.split(","); 78 | opType = Float.parseFloat(lineSplit[0]); 79 | 80 | switch ((int)opType) { 81 | case 0: 82 | { //insertion 83 | try { 84 | if (lineSplit.length != (this.dimension + 2)) { 85 | throw new AssertionError(); 86 | } 87 | 88 | oid = Float.parseFloat(lineSplit[1]); 89 | point = extractPoint(lineSplit, 2); 90 | 91 | start = System.currentTimeMillis(); 92 | tree.insert(new SpatialPoint(point, oid)); 93 | end = System.currentTimeMillis(); 94 | 95 | this.updateTimeTaken(opType, (end - start)); 96 | break; 97 | 98 | } catch (Exception e) { 99 | logger.traceError("Exception while processing line " + lineNum + 100 | ". Skipped Insertion. message: "+e.getMessage()); 101 | break; 102 | } 103 | catch (AssertionError error){ 104 | logger.traceError("Error while processing line " + lineNum + 105 | ".Skipped Insertion. message: "+ error.getMessage()); 106 | break; 107 | } 108 | } 109 | 110 | case 1: 111 | { //point search 112 | try{ 113 | if (lineSplit.length != this.dimension + 1) { 114 | throw new AssertionError(); 115 | } 116 | point = extractPoint(lineSplit, 1); 117 | 118 | start = System.currentTimeMillis(); 119 | oid = tree.pointSearch(new SpatialPoint(point)); 120 | end = System.currentTimeMillis(); 121 | 122 | logger.trace("search result: " + oid); 123 | this.updateTimeTaken(opType, (end - start)); 124 | } catch (Exception e) { 125 | logger.traceError("Exception while processing line " + lineNum + 126 | ". Skipped Point Search. message: "+e.getMessage()); 127 | } 128 | catch (AssertionError error){ 129 | logger.traceError("Error while processing line " + lineNum + 130 | ". Skipped Point Search. message: "+error.getMessage()); 131 | } 132 | break; 133 | } 134 | 135 | case 2: 136 | { //range search 137 | try{ 138 | if (lineSplit.length != this.dimension + 2) { 139 | throw new AssertionError(); 140 | } 141 | 142 | point = extractPoint(lineSplit, 1); 143 | range = Double.parseDouble(lineSplit[this.dimension + 1]); 144 | SpatialPoint center = new SpatialPoint(point); 145 | 146 | start = System.currentTimeMillis(); 147 | List result = tree.rangeSearch(center, range); 148 | end = System.currentTimeMillis(); 149 | 150 | logger.trace("Range Search(" + range + ", " + center + "): " + Utils.SpatialPointListToString(result)); 151 | this.updateTimeTaken(opType, (end - start)); 152 | } catch (Exception e) { 153 | logger.traceError("Exception while processing line " + lineNum + 154 | ". Skipped range search. message: "+e.getMessage()); 155 | } 156 | catch (AssertionError error){ 157 | logger.traceError("Error while processing line " + lineNum + 158 | ". Skipped range search. message: "+error.getMessage()); 159 | } 160 | break; 161 | } 162 | 163 | case 3: 164 | { //knn search 165 | try{ 166 | if (lineSplit.length != this.dimension + 2) { 167 | throw new AssertionError(); 168 | } 169 | 170 | point = extractPoint(lineSplit, 1); 171 | k = Float.parseFloat(lineSplit[this.dimension + 1]); 172 | SpatialPoint center = new SpatialPoint(point); 173 | 174 | start = System.currentTimeMillis(); 175 | List result = tree.knnSearch(center, (int)k); 176 | end = System.currentTimeMillis(); 177 | 178 | logger.trace("Knn Search(" + k + ", " + center + "): " + Utils.SpatialPointListToString(result)); 179 | this.updateTimeTaken(opType, (end - start)); 180 | } catch (Exception e) { 181 | logger.traceError("Exception while processing line " + lineNum + 182 | ". Skipped knn search. message: "+e.getMessage()); 183 | } 184 | catch (AssertionError error){ 185 | logger.traceError("Error while processing line " + lineNum + 186 | ". Skipped knn search. message: "+error.getMessage()); 187 | } 188 | break; 189 | } 190 | 191 | default: 192 | logger.traceError("Invalid query type " + opType + " at line " + lineNum + ". Skipped .. "); 193 | break; 194 | } 195 | } 196 | input.close(); 197 | tree.save(); 198 | } 199 | catch (Exception e) { 200 | logger.traceError("Error while reading input file. Line " + lineNum + " Skipped\nError Details:"); 201 | } 202 | } 203 | 204 | private float[] extractPoint(String[] points, int startPos) throws NumberFormatException 205 | { 206 | float[] tmp = new float[this.dimension]; 207 | for (int i = startPos, lineSplitLength = points.length; 208 | ((i < lineSplitLength) && (i < (startPos + this.dimension))); i++) 209 | { 210 | tmp[i-startPos] = Float.parseFloat(points[i]); 211 | } 212 | return tmp; 213 | } 214 | 215 | protected void updateTimeTaken(float type, long time) { 216 | switch ((int)type) { 217 | case 0: 218 | insertRunTime.add(time); 219 | break; 220 | case 1: 221 | searchRunTime.add(time); 222 | break; 223 | case 2: 224 | rangeRuntime.add(time); 225 | break; 226 | case 3: 227 | knnRuntime.add(time); 228 | break; 229 | default: 230 | logger.traceError("Invalid Query type encountered. Skipped.."); 231 | break; 232 | } 233 | } 234 | 235 | protected void printResults() { 236 | logger.trace("\nPerforming Run Time calculations.."); 237 | 238 | List combined = new ArrayList(); 239 | combined.addAll(insertRunTime); 240 | combined.addAll(searchRunTime); 241 | combined.addAll(rangeRuntime); 242 | combined.addAll(knnRuntime); 243 | 244 | String result = "\n"+this.getClass().getSimpleName()+" --RESULTS--"; 245 | 246 | String temp = "\n\nInsertion operations:(in milliseconds) "+ generateRuntimeReport(insertRunTime); 247 | logger.trace(temp); 248 | result += temp; 249 | temp = "\n\nSearch operations:(in milliseconds) "+ generateRuntimeReport(searchRunTime); 250 | logger.trace(temp); 251 | result += temp; 252 | temp = "\n\nRange search operations: (in milliseconds) " + generateRuntimeReport(rangeRuntime); 253 | logger.trace(temp); 254 | result += temp; 255 | temp = "\n\nKNN search operations: (in milliseconds) " + generateRuntimeReport(knnRuntime); 256 | logger.trace(temp); 257 | result += temp; 258 | temp = "\n\nCombined operations:(in milliseconds) "+ generateRuntimeReport(combined); 259 | logger.trace( temp); 260 | result += temp; 261 | 262 | writeResultToFile(result); 263 | } 264 | 265 | protected String generateRuntimeReport(List runtime) { 266 | StringBuilder result = new StringBuilder(); 267 | int size = runtime.size(); 268 | 269 | if (size > 0) { 270 | Collections.sort(runtime); 271 | try { 272 | Long percent5th = runtime.get((int) (0.05 * size)); 273 | Long percent95th = runtime.get((int) (0.95 * size)); 274 | float median = getMedian(runtime); 275 | long sum = 0; 276 | for (Long aRuntime : runtime) { 277 | sum += aRuntime; 278 | } 279 | double avg = sum / (double) size; 280 | 281 | result.append("\nTotal ops = ").append(size); 282 | result.append("\nAvg time: ").append(avg); 283 | result.append("\n5th percentile: ").append(percent5th); 284 | result.append("\n95th percentile: ").append(percent95th); 285 | result.append("\nmedian: ").append(median); 286 | 287 | } catch (Exception e) { 288 | logger.traceError("Exception while generating runtime results"); 289 | e.printStackTrace(); 290 | } 291 | } 292 | 293 | return result.toString(); 294 | } 295 | 296 | protected void writeResultToFile(String result) { 297 | try { 298 | File outFile = new File(this.resultFile); 299 | if(outFile.exists()){ 300 | outFile.delete(); 301 | } 302 | BufferedWriter outBW = new BufferedWriter(new FileWriter(outFile)); 303 | try{ 304 | logger.trace("\nWriting results to file .. "); 305 | outBW.write(result); 306 | } 307 | finally{ 308 | outBW.close(); 309 | logger.trace("done"); 310 | } 311 | } 312 | catch (IOException e) { 313 | logger.traceError("IOException while writing results to " + resultFile); 314 | } 315 | } 316 | 317 | protected void writeRuntimeToFile(List runtime, String file) { 318 | try { 319 | File f = new File(file); 320 | if(f.exists()) 321 | f.delete(); 322 | 323 | BufferedWriter bf = new BufferedWriter(new FileWriter(f)); 324 | for(long i : runtime){ 325 | bf.write("" + i + "\n"); 326 | } 327 | bf.close(); 328 | } catch (IOException e) { 329 | logger.traceError("IOException while writing runtimes to file."); 330 | // e.printStackTrace(); 331 | } 332 | } 333 | 334 | protected void printUsage() { 335 | System.err.println("Usage: "+ this.getClass().getSimpleName() + 336 | " [output file].\noutput file is optional.\n"); 337 | } 338 | } -------------------------------------------------------------------------------- /src/rstar/RStarTree.java: -------------------------------------------------------------------------------- 1 | package rstar; 2 | 3 | import rstar.dto.PointDTO; 4 | import rstar.dto.TreeDTO; 5 | import rstar.interfaces.IDtoConvertible; 6 | import rstar.interfaces.ISpatialQuery; 7 | import rstar.nodes.RStarInternal; 8 | import rstar.nodes.RStarLeaf; 9 | import rstar.nodes.RStarNode; 10 | import rstar.nodes.RStarSplit; 11 | import rstar.spatial.HyperRectangle; 12 | import rstar.spatial.SpatialPoint; 13 | import util.Constants; 14 | 15 | import java.io.File; 16 | import java.io.FileNotFoundException; 17 | import java.util.ArrayList; 18 | import java.util.Collections; 19 | import java.util.Comparator; 20 | import java.util.List; 21 | 22 | public class RStarTree implements ISpatialQuery, IDtoConvertible { 23 | 24 | private int dimension; 25 | private File saveFile; 26 | private StorageManager storage; 27 | private RStarNode root; 28 | private long rootPointer = -1; 29 | private RStarSplit splitManager; 30 | 31 | private float _pointSearchResult = -1; 32 | private ArrayList _rangeSearchResult; 33 | private List _knnSearchResult; 34 | private int bestSortOrder = -1; 35 | 36 | public RStarTree(int dimension) { 37 | this.dimension = dimension; 38 | this.saveFile = new File(Constants.TREE_FILE); 39 | this.storage = new StorageManager(); 40 | this.splitManager = new RStarSplit(dimension, storage); 41 | 42 | storage.createDataDir(saveFile); 43 | setCapacities(); 44 | } 45 | 46 | private void setCapacities(){ 47 | Constants.DIMENSION = dimension; 48 | // Constants.MAX_CHILDREN = Constants.PAGESIZE/8; // M = (pagesize - mbr_size)/ (size of Long = 8) 49 | // Constants.MIN_CHILDREN = Constants.MAX_CHILDREN/3; // m = M/3 50 | Constants.MAX_CHILDREN = 10; 51 | Constants.MIN_CHILDREN = 4; 52 | } 53 | 54 | /* QUERY FUNCTIONS */ 55 | 56 | /** 57 | * inserts a point in the tree and saves it on disk 58 | * @param point the point to be inserted 59 | * @return 1 if successful, else -1 60 | */ 61 | @Override 62 | public int insert(SpatialPoint point) { 63 | System.out.println("inserting point with oid=" + point.getOid()); 64 | RStarLeaf target = chooseLeaf(point); 65 | 66 | if (target.isNotFull()) { 67 | target.insert(point); 68 | storage.saveNode(target); 69 | //adjust root reference 70 | if (target.getNodeId() == rootPointer) { 71 | root = target; 72 | } 73 | adjustParentOf(target); 74 | return 1; 75 | } else { 76 | return treatLeafOverflow(target, point); 77 | } 78 | } 79 | 80 | /** 81 | * inserts a RStar node in the node pointed by nodePointer 82 | * @param nodePointer pointer to node in which the given node 83 | * is to be inserted 84 | * @param nodeToInsert the node to be inserted 85 | * @return 1 of successful, else -1 86 | */ 87 | private int insertAt(Long nodePointer, RStarNode nodeToInsert) { 88 | storage.saveNode(nodeToInsert); 89 | RStarInternal target = (RStarInternal) loadNode(nodePointer); 90 | 91 | if (target.isNotFull()) { 92 | target.insert(nodeToInsert); 93 | 94 | if (target.getNodeId() == rootPointer) { 95 | root = target; 96 | } 97 | 98 | storage.saveNode(target); 99 | adjustParentOf(target); 100 | return 1; 101 | } else { 102 | return treatInternalOverflow(target, nodeToInsert); 103 | } 104 | } 105 | 106 | /** 107 | * searches for a spatial point in the tree and 108 | * returns its oid if its found. 109 | * @param point the point to be searched 110 | * @return oid of the point if found, else -1. 111 | */ 112 | @Override 113 | public float pointSearch(SpatialPoint point) { 114 | _pointSearchResult = -1; 115 | loadRoot(); 116 | _pointSearch(root, point); 117 | return _pointSearchResult; 118 | } 119 | 120 | private void _pointSearch(RStarNode start, SpatialPoint point) { 121 | HyperRectangle searchRegion = new HyperRectangle(point.getCords()); 122 | HyperRectangle intersection = start.getMBR().getIntersection(searchRegion); 123 | 124 | if(intersection != null) { 125 | if (start.isLeaf()) { 126 | float[] searchPoints = point.getCords(); 127 | 128 | //lazy loading of child points 129 | for (Long pointer : start.childPointers) { 130 | PointDTO dto = storage.loadPoint(pointer); 131 | 132 | float[] candidates = dto.coords; 133 | boolean found = true; 134 | for (int i = 0; i < candidates.length; i++) { 135 | if (candidates[i] != searchPoints[i]){ 136 | found = false; 137 | break; 138 | } 139 | } 140 | if (found) { 141 | _pointSearchResult = dto.oid; 142 | break; 143 | } 144 | } 145 | } else { 146 | for (Long pointer : start.childPointers) { 147 | if(_pointSearchResult != -1) // point found 148 | break; 149 | 150 | try { 151 | RStarNode childNode = storage.loadNode(pointer); //recurse down 152 | _pointSearch(childNode, point); 153 | 154 | } catch (FileNotFoundException e) { 155 | System.err.println("Exception while loading node from disk. message = "+e.getMessage()); 156 | } 157 | } 158 | } 159 | } 160 | } 161 | 162 | /** 163 | * searches for points in the given range of the center point 164 | * @param center center point of the search region. 165 | * @param range radius of the search region. 166 | * @return List of all the points found in the range 167 | */ 168 | @Override 169 | public List rangeSearch(SpatialPoint center, double range) { 170 | 171 | float[] points = center.getCords(); 172 | float[][] mbrPoints = new float[dimension][2]; 173 | for (int i = 0; i < dimension; i++) { 174 | mbrPoints[i][0] = points[i] + (float) range; 175 | mbrPoints[i][1] = points[i] - (float) range; 176 | } 177 | HyperRectangle searchRegion = new HyperRectangle(dimension); 178 | searchRegion.setPoints(mbrPoints); 179 | 180 | _rangeSearchResult = new ArrayList(); 181 | loadRoot(); 182 | _rangeSearch(root, searchRegion); 183 | return _rangeSearchResult; 184 | } 185 | 186 | private void _rangeSearch(RStarNode start, HyperRectangle searchRegion) { 187 | HyperRectangle intersection = start.getMBR().getIntersection(searchRegion); 188 | if (intersection != null) { 189 | if (start.isLeaf()) { 190 | for (Long pointer : start.childPointers) { 191 | PointDTO dto = storage.loadPoint(pointer); 192 | SpatialPoint spoint = new SpatialPoint(dto); 193 | HyperRectangle pointMbr = new HyperRectangle(dto.coords); 194 | 195 | if(pointMbr.getIntersection(searchRegion) != null) 196 | _rangeSearchResult.add(spoint); 197 | } 198 | } 199 | else { 200 | for (Long pointer : start.childPointers) { 201 | try { 202 | RStarNode childNode = storage.loadNode(pointer); //recurse down 203 | _rangeSearch(childNode, searchRegion); 204 | 205 | } catch (FileNotFoundException e) { 206 | System.err.println("Exception while loading node from disk"); 207 | } 208 | } 209 | } 210 | } 211 | } 212 | 213 | /** 214 | * searches for the k nearest neighbours of a center point 215 | * @param center SpatialPoint 216 | * @param k number of nearest neighbours required 217 | * @return List of the k nearest neighbours of center. 218 | */ 219 | @Override 220 | public List knnSearch(SpatialPoint center, int k) { 221 | loadRoot(); 222 | _knnSearch(root, center, k, 1); 223 | _rangeSearchResult = new ArrayList(); 224 | return _knnSearchResult; 225 | } 226 | 227 | private void _knnSearch(RStarNode start, SpatialPoint center, int k, float range) { 228 | _rangeSearchResult = new ArrayList(); 229 | 230 | float[] points = center.getCords(); 231 | float[][] mbrPoints = new float[dimension][2]; 232 | for (int i = 0; i < dimension; i++) { 233 | mbrPoints[i][0] = points[i] + range; 234 | mbrPoints[i][1] = points[i] - range; 235 | } 236 | HyperRectangle searchRegion = new HyperRectangle(dimension); 237 | searchRegion.setPoints(mbrPoints); 238 | 239 | _rangeSearch(start, searchRegion); 240 | 241 | if (_rangeSearchResult.size() < k) { 242 | _knnSearch(start, center, k, 2 * range); 243 | } else { 244 | final SpatialPoint fcenter = center; 245 | Comparator paramComparator = new Comparator() { 246 | @Override 247 | public int compare(SpatialPoint point1, SpatialPoint point2) { 248 | float deltaDist = fcenter.distance(point1) - fcenter.distance(point2); 249 | if(deltaDist == 0) 250 | return 0; 251 | else 252 | return (int)(deltaDist /(Math.abs(deltaDist))); 253 | } 254 | }; 255 | Collections.sort(_rangeSearchResult, paramComparator); 256 | _knnSearchResult = _rangeSearchResult.subList(0, k); 257 | } 258 | } 259 | 260 | private int treatLeafOverflow(RStarLeaf target, SpatialPoint point) { 261 | try { 262 | splitLeaf(target, point); 263 | return 1; 264 | } catch (AssertionError e) { 265 | return -1; 266 | } 267 | } 268 | 269 | private int treatInternalOverflow(RStarInternal fullNode, RStarNode newChild) { 270 | try { 271 | splitInternalNode(fullNode, newChild); 272 | return 1; 273 | } catch (AssertionError e) { 274 | return -1; 275 | } 276 | } 277 | 278 | /** 279 | * inserts point into and splits the target leafnode 280 | * @param splittingLeaf the leaf to split 281 | * @param newPoint the point to be inserted 282 | * @throws AssertionError when the target node does 283 | * not have any children 284 | */ 285 | private void splitLeaf(RStarLeaf splittingLeaf, SpatialPoint newPoint) throws AssertionError { 286 | RStarLeaf newChild = splitManager.splitLeaf(splittingLeaf, newPoint); 287 | if (splittingLeaf.getNodeId() == rootPointer) { 288 | //we just split root 289 | root = splittingLeaf; 290 | createRoot(newChild); 291 | } else { 292 | newChild.setParentId(splittingLeaf.getParentId()); 293 | insertAt(splittingLeaf.getParentId(), newChild); 294 | } 295 | } 296 | 297 | /** 298 | * splits an internal node and inserts a new node 299 | * @param splittingNode the node to be split 300 | * @param node the node to be inserted 301 | */ 302 | private void splitInternalNode(RStarInternal splittingNode, RStarNode node) { 303 | RStarNode createdNode; 304 | try { 305 | createdNode = splitManager.splitInternalNode(splittingNode, node); 306 | if (splittingNode.getNodeId() == rootPointer) { 307 | //we just split root 308 | root = splittingNode; 309 | createRoot(createdNode); 310 | } else { 311 | createdNode.setParentId(splittingNode.getParentId()); 312 | insertAt(splittingNode.getParentId(), createdNode); 313 | } 314 | } catch (FileNotFoundException e) { 315 | System.err.println("Exception while loading node from disk. message: "+e.getMessage()); 316 | } 317 | } 318 | 319 | /** 320 | * creates a new root and sets the old root (referred by this.root) 321 | * and siblingOfRoot its children 322 | * @param siblingOfRoot node created by splitting current root 323 | */ 324 | private void createRoot(RStarNode siblingOfRoot) { 325 | RStarInternal newRoot = new RStarInternal(dimension); 326 | newRoot.setParentId(newRoot.getNodeId()); 327 | newRoot.insert(root); 328 | newRoot.insert(siblingOfRoot); 329 | storage.saveNode(root); 330 | storage.saveNode(siblingOfRoot); 331 | storage.saveNode(newRoot); 332 | root = newRoot; 333 | rootPointer = newRoot.getNodeId(); 334 | } 335 | 336 | /** 337 | * finds the most appropriate leaf node to 338 | * insert the newPoint into 339 | * @param newPoint the point to be inserted 340 | * @return RStarLeaf the most appropriate leaf to insert 341 | * newPoint 342 | */ 343 | private RStarLeaf chooseLeaf(SpatialPoint newPoint) { 344 | loadRoot(); 345 | SpatialPoint[] temp = new SpatialPoint[1]; 346 | temp[0] = newPoint; 347 | return splitManager.chooseLeaf(root, new HyperRectangle(dimension, temp)); 348 | } 349 | 350 | /** 351 | * updates mbr of all ancestor of a node 352 | * @param target updation starts from the parent of target 353 | */ 354 | private void adjustParentOf(RStarNode target) { 355 | if (target.getNodeId() != rootPointer) { 356 | RStarNode parent = loadNode(target.getParentId()); 357 | HyperRectangle mbr = parent.getMBR(); 358 | mbr.update(target.getMBR()); 359 | parent.setMbr(mbr); 360 | storage.saveNode(parent); 361 | if (parent.getNodeId() == rootPointer) { 362 | root = parent; 363 | } 364 | adjustParentOf(parent); 365 | } 366 | } 367 | 368 | /* 369 | ***** DISK RELATED FUNCTIONS **** 370 | */ 371 | 372 | /** 373 | * loads root from disk if exists 374 | * otherwise creates a new LeafNode and 375 | * assigns it root. 376 | */ 377 | private void loadRoot() { 378 | if (root == null) { 379 | //empty tree 380 | root = loadNode(rootPointer); 381 | if (root == null) // still null -> empty tree 382 | { 383 | root = new RStarLeaf(dimension); 384 | root.setParentId(root.getNodeId()); 385 | } 386 | rootPointer = root.getNodeId(); 387 | } 388 | } 389 | 390 | /** 391 | * loads Nodes from disk using their nodeId 392 | * @param nodeId the nodeId attribute of the Node 393 | * to be loaded 394 | * @return the Node required, null uf it doesn't exist 395 | */ 396 | private RStarNode loadNode(long nodeId) { 397 | //check for valid nodeId 398 | if (nodeId != -1) { 399 | try { 400 | if (nodeId == rootPointer) { 401 | loadRoot(); 402 | return root; 403 | } else { 404 | return storage.loadNode(nodeId); 405 | } 406 | } catch (FileNotFoundException e) { 407 | System.err.println("Error while loading R* Tree node from file " + storage.constructFilename(nodeId)); 408 | } 409 | } 410 | return null; 411 | } 412 | 413 | /** 414 | * saves the tree details to disk 415 | * @return 1 if successful, -1 otherwise 416 | */ 417 | public int save() { 418 | return storage.saveTree(this.toDTO(), saveFile); 419 | } 420 | 421 | /** 422 | * converts this tree to its DTO representation 423 | * which in turn can be saved to disk. 424 | * @return TreeDTO object which is the DTO form of 425 | * this tree 426 | */ 427 | @Override 428 | public TreeDTO toDTO() { 429 | return new TreeDTO(dimension, Constants.PAGESIZE, rootPointer); 430 | } 431 | 432 | /*private void loadTree() { 433 | if (saveFile.exists() && saveFile.length() != 0) { 434 | try { 435 | TreeDTO treeData = storage.loadTree(saveFile); 436 | if (treeData != null) { //update tree fields from saveFile 437 | this.dimension = treeData.dimension; 438 | this.pagesize = treeData.pagesize; 439 | this.rootPointer = treeData.rootPointer; 440 | System.out.printf("Tree loaded successfully from %s. dimension = %d and pagesize = %d bytes%n", 441 | saveFile.getName(), dimension, pagesize); 442 | } 443 | } catch (FileNotFoundException e) { 444 | System.err.println("Failed to load R* Tree from "+saveFile.getName()); 445 | } 446 | 447 | } 448 | }*/ 449 | } 450 | -------------------------------------------------------------------------------- /src/rstar/nodes/RStarSplit.java: -------------------------------------------------------------------------------- 1 | package rstar.nodes; 2 | 3 | import rstar.StorageManager; 4 | import rstar.dto.PointDTO; 5 | import rstar.spatial.HyperRectangle; 6 | import rstar.spatial.SpatialComparator; 7 | import rstar.spatial.SpatialPoint; 8 | import util.Constants; 9 | 10 | import java.io.FileNotFoundException; 11 | import java.util.ArrayList; 12 | import java.util.Collections; 13 | 14 | import static java.util.Arrays.sort; 15 | 16 | /** 17 | * Provides functionality for splitting of RStarNodes 18 | */ 19 | public class RStarSplit { 20 | private int dimension; 21 | public int bestSortOrder; 22 | private StorageManager disk; 23 | 24 | public RStarSplit(int dimension, StorageManager storageManager) { 25 | this.dimension = dimension; 26 | this.disk = storageManager; 27 | this.bestSortOrder = -1; 28 | } 29 | 30 | public RStarLeaf chooseLeaf(RStarNode startNode, HyperRectangle newMbr) { 31 | if(startNode.isLeaf()) { 32 | return (RStarLeaf)startNode; 33 | } 34 | 35 | else { 36 | ArrayList childPointers = startNode.childPointers; 37 | assert childPointers.size() > 0; 38 | ArrayList children = new ArrayList(childPointers.size()); 39 | //load all children 40 | for (long childId : childPointers) { 41 | try { 42 | children.add(disk.loadNode(childId)); 43 | } catch (FileNotFoundException e) { 44 | System.err.println("Exception while loading node from disk. message = "+e.getMessage()); 45 | } 46 | } 47 | 48 | //check whether children are leaves 49 | if (children.get(0).isLeaf()) { 50 | //check for least overlap increment 51 | ArrayList minOverlap = new ArrayList(); 52 | // the candidate nodes for next recursive step 53 | ArrayList cands = new ArrayList(); 54 | 55 | for (RStarNode child : children) { 56 | HyperRectangle union = child.getMBR().union(newMbr); 57 | //find union's overlap with all other children 58 | double deltaOverlap = 0; 59 | 60 | for (RStarNode otherChild : children) { 61 | if (otherChild == child) { 62 | continue; 63 | } 64 | 65 | deltaOverlap += union.overlap(otherChild.getMBR()) - 66 | child.getMBR().overlap(otherChild.getMBR()); 67 | 68 | } 69 | 70 | if (minOverlap.size() == 0) { 71 | cands.add(child); 72 | minOverlap.add(deltaOverlap); 73 | } else { 74 | if (minOverlap.get(0) > deltaOverlap) { 75 | minOverlap.removeAll(minOverlap); 76 | cands.removeAll(cands); 77 | minOverlap.add(deltaOverlap); 78 | cands.add(child); 79 | } 80 | else if (minOverlap.get(0) == deltaOverlap) { 81 | minOverlap.add(deltaOverlap); 82 | cands.add(child); 83 | } 84 | } 85 | } 86 | 87 | if(cands.size() == 1) 88 | return chooseLeaf(cands.get(0), newMbr); 89 | //break ties 90 | else{ 91 | ArrayList minAreas = new ArrayList(); 92 | ArrayList cands2 = new ArrayList(); 93 | 94 | double deltaV; 95 | for (RStarNode candNode : cands) { 96 | deltaV = candNode.getMBR().deltaV_onInclusion(newMbr); 97 | if(minAreas.size() == 0 || minAreas.get(0) > deltaV) { 98 | minAreas.removeAll(minAreas); 99 | cands2.removeAll(cands2); 100 | minAreas.add(deltaV); 101 | cands2.add(candNode); 102 | } 103 | else if (minAreas.get(0) == deltaV) { 104 | minAreas.add(deltaV); 105 | cands2.add(candNode); 106 | } 107 | } 108 | 109 | if(cands2.size() == 1) 110 | return chooseLeaf(cands2.get(0), newMbr); 111 | else { 112 | //again break ties 113 | double minArea = Double.MAX_VALUE; 114 | RStarNode candidate = null; 115 | for (RStarNode candNode : cands2) { 116 | double vol = candNode.getMBR().volume(); 117 | if( vol < minArea ){ 118 | minArea = vol; 119 | candidate = candNode; 120 | } 121 | } 122 | return chooseLeaf(candidate, newMbr); 123 | } 124 | } 125 | } else { 126 | //check for least volume increment 127 | ArrayList minAreas = new ArrayList(); 128 | ArrayList cands = new ArrayList(); 129 | 130 | double deltaV; 131 | for (RStarNode candNode : children) { 132 | deltaV = candNode.getMBR().deltaV_onInclusion(newMbr); 133 | if(minAreas.size() == 0 || minAreas.get(0) > deltaV) { 134 | minAreas.removeAll(minAreas); 135 | cands.removeAll(cands); 136 | minAreas.add(deltaV); 137 | cands.add(candNode); 138 | } 139 | else if (minAreas.get(0) == deltaV) { 140 | minAreas.add(deltaV); 141 | cands.add(candNode); 142 | } 143 | } 144 | 145 | if(cands.size() == 1) 146 | return chooseLeaf(cands.get(0), newMbr); 147 | else { 148 | //again break ties 149 | double minArea = Double.MAX_VALUE; 150 | RStarNode candidate = null; 151 | for (RStarNode candNode : cands) { 152 | double vol = candNode.getMBR().volume(); 153 | if( vol < minArea ){ 154 | minArea = vol; 155 | candidate = candNode; 156 | } 157 | } 158 | return chooseLeaf(candidate, newMbr); 159 | } 160 | } 161 | } 162 | } 163 | 164 | 165 | /** 166 | * computes the split axis for the given list of entries 167 | * @param entries the points to be split 168 | * @return the index of the dimension perpendicular to which splitting 169 | * should be done 170 | */ 171 | public int chooseLeafSplitAxis(final ArrayList entries) { 172 | int splitAxis = 0; 173 | ArrayList maxSorting = (ArrayList) entries.clone(); 174 | ArrayList minSorting = (ArrayList) entries.clone(); 175 | 176 | // best value for total margin 177 | double minMargin = Double.MAX_VALUE; 178 | 179 | for (int i = 0; i < dimension; i++) { 180 | double margin = 0.0; 181 | // sort the entries according to their minimal and according to their maximal value 182 | final SpatialComparator compMin = new SpatialComparator(i, HyperRectangle.MIN_CORD); 183 | Collections.sort(minSorting, compMin); 184 | final SpatialComparator compMax = new SpatialComparator(i, HyperRectangle.MAX_CORD); 185 | Collections.sort(maxSorting, compMax); 186 | 187 | for (int k = 0; k <= (entries.size() - 2 * Constants.MIN_CHILDREN); k++) { 188 | HyperRectangle mbr1 = new HyperRectangle(dimension, minSorting.subList(0, Constants.MIN_CHILDREN + k)); 189 | HyperRectangle mbr2 = new HyperRectangle(dimension, minSorting.subList(Constants.MIN_CHILDREN + k, entries.size())); 190 | 191 | margin += mbr1.margin() + mbr2.margin(); 192 | 193 | mbr1 = new HyperRectangle(dimension, maxSorting.subList(0, Constants.MIN_CHILDREN + k)); 194 | mbr2 = new HyperRectangle(dimension, maxSorting.subList(Constants.MIN_CHILDREN + k, entries.size())); 195 | margin += mbr1.margin() + mbr2.margin(); 196 | } 197 | 198 | if (margin < minMargin) { 199 | splitAxis = i; 200 | minMargin = margin; 201 | } 202 | } 203 | return splitAxis; 204 | } 205 | 206 | public int chooseInternalSplitAxis(ArrayList children) { 207 | int splitAxis = 0; 208 | ArrayList maxSorting = (ArrayList) children.clone(); 209 | ArrayList minSorting = (ArrayList) children.clone(); 210 | 211 | // best value for total margin 212 | double minMargin = Double.MAX_VALUE; 213 | 214 | for (int i = 0; i < dimension; i++) { 215 | double margin = 0.0; 216 | // sort the entries according to their minimal and according to their maximal value 217 | final SpatialComparator compMin = new SpatialComparator(i, HyperRectangle.MIN_CORD); 218 | Collections.sort(minSorting, compMin); 219 | final SpatialComparator compMax = new SpatialComparator(i, HyperRectangle.MAX_CORD); 220 | Collections.sort(maxSorting, compMax); 221 | 222 | for (int k = 0; k <= (children.size() - 2 * Constants.MIN_CHILDREN); k++) { 223 | HyperRectangle mbr1 = new HyperRectangle(dimension, minSorting.subList(0, Constants.MIN_CHILDREN + k)); 224 | HyperRectangle mbr2 = new HyperRectangle(dimension, minSorting.subList(Constants.MIN_CHILDREN + k, children.size())); 225 | 226 | margin += mbr1.margin() + mbr2.margin(); 227 | 228 | mbr1 = new HyperRectangle(dimension, maxSorting.subList(0, Constants.MIN_CHILDREN + k)); 229 | mbr2 = new HyperRectangle(dimension, maxSorting.subList(Constants.MIN_CHILDREN + k, children.size())); 230 | margin += mbr1.margin() + mbr2.margin(); 231 | } 232 | 233 | if (margin < minMargin) { 234 | splitAxis = i; 235 | minMargin = margin; 236 | } 237 | } 238 | return splitAxis; 239 | } 240 | 241 | /** 242 | * computes the split point for the given list of entries 243 | * it sets bestSort to 0 or 1 depending upon whether splitting should be done 244 | * according to maximal or minimal value for the given splitAxis 245 | * @param entries the points to be split 246 | * @return the split point 247 | */ 248 | public int chooseLeafSplitpoint(final ArrayList entries, final int splitAxis) 249 | { 250 | int splitPoint; 251 | // numEntries 252 | int numEntries = entries.size(); 253 | 254 | ArrayList maxSorting = (ArrayList) entries.clone(); 255 | ArrayList minSorting = (ArrayList) entries.clone(); 256 | 257 | // sort upper and lower in the right dimension 258 | final SpatialComparator compMin = new SpatialComparator(splitAxis, HyperRectangle.MIN_CORD); 259 | Collections.sort(minSorting, compMin); 260 | final SpatialComparator compMax = new SpatialComparator(splitAxis, HyperRectangle.MAX_CORD); 261 | Collections.sort(maxSorting, compMax); 262 | 263 | // the split point (first set to minimum entries in the node) 264 | splitPoint = Constants.MIN_CHILDREN; 265 | // best value for the overlap 266 | double minOverlap = Double.MAX_VALUE; 267 | // the volume of mbr1 and mbr2 268 | double volume = 0.0; 269 | int minEntries = Constants.MIN_CHILDREN; 270 | 271 | bestSortOrder = -1; 272 | 273 | for (int i = 0; i <= numEntries - 2 * minEntries; i++) { 274 | // test the sorting with respect to the minimal values 275 | HyperRectangle mbr1 = new HyperRectangle(dimension, minSorting.subList(0, minEntries + i)); 276 | HyperRectangle mbr2 = new HyperRectangle(dimension, minSorting.subList(minEntries + i, entries.size())); 277 | 278 | double currentOverlap = mbr1.overlap(mbr2); 279 | if (currentOverlap < minOverlap || (currentOverlap == minOverlap && (mbr1.volume() + mbr2.volume()) < volume)) { 280 | minOverlap = currentOverlap; 281 | splitPoint = minEntries + i; 282 | bestSortOrder = HyperRectangle.MIN_CORD; 283 | volume = mbr1.volume() + mbr2.volume(); 284 | } 285 | // test the sorting with respect to the maximal values 286 | mbr1 = new HyperRectangle(dimension, maxSorting.subList(0, minEntries + i)); 287 | mbr2 = new HyperRectangle(dimension, maxSorting.subList(minEntries + i, entries.size())); 288 | 289 | currentOverlap = mbr1.overlap(mbr2); 290 | if (currentOverlap < minOverlap || (currentOverlap == minOverlap && (mbr1.volume() + mbr2.volume()) < volume)) { 291 | minOverlap = currentOverlap; 292 | splitPoint = minEntries + i; 293 | bestSortOrder = HyperRectangle.MAX_CORD; 294 | volume = mbr1.volume() + mbr2.volume(); 295 | } 296 | } 297 | return splitPoint; 298 | } 299 | 300 | public int chooseInternalSplitpoint(ArrayList children, int splitAxis) { 301 | int splitPoint; 302 | // numEntries 303 | int numEntries = children.size(); 304 | 305 | ArrayList maxSorting = (ArrayList) children.clone(); 306 | ArrayList minSorting = (ArrayList) children.clone(); 307 | 308 | // sort upper and lower in the right dimension 309 | final SpatialComparator compMin = new SpatialComparator(splitAxis, HyperRectangle.MIN_CORD); 310 | Collections.sort(minSorting, compMin); 311 | final SpatialComparator compMax = new SpatialComparator(splitAxis, HyperRectangle.MAX_CORD); 312 | Collections.sort(maxSorting, compMax); 313 | 314 | // the split point (first set to minimum entries in the node) 315 | splitPoint = Constants.MIN_CHILDREN; 316 | // best value for the overlap 317 | double minOverlap = Double.MAX_VALUE; 318 | // the volume of mbr1 and mbr2 319 | double volume = 0.0; 320 | int minEntries = Constants.MIN_CHILDREN; 321 | 322 | bestSortOrder = -1; 323 | 324 | for (int i = 0; i <= numEntries - 2 * minEntries; i++) { 325 | // test the sorting with respect to the minimal values 326 | HyperRectangle mbr1 = new HyperRectangle(dimension, minSorting.subList(0, minEntries + i)); 327 | HyperRectangle mbr2 = new HyperRectangle(dimension, minSorting.subList(minEntries + i, children.size())); 328 | 329 | double currentOverlap = mbr1.overlap(mbr2); 330 | if (currentOverlap < minOverlap || (currentOverlap == minOverlap && (mbr1.volume() + mbr2.volume()) < volume)) { 331 | minOverlap = currentOverlap; 332 | splitPoint = minEntries + i; 333 | bestSortOrder = HyperRectangle.MIN_CORD; 334 | volume = mbr1.volume() + mbr2.volume(); 335 | } 336 | // test the sorting with respect to the maximal values 337 | mbr1 = new HyperRectangle(dimension, maxSorting.subList(0, minEntries + i)); 338 | mbr2 = new HyperRectangle(dimension, maxSorting.subList(minEntries + i, children.size())); 339 | 340 | currentOverlap = mbr1.overlap(mbr2); 341 | if (currentOverlap < minOverlap || (currentOverlap == minOverlap && (mbr1.volume() + mbr2.volume()) < volume)) { 342 | minOverlap = currentOverlap; 343 | splitPoint = minEntries + i; 344 | bestSortOrder = HyperRectangle.MAX_CORD; 345 | volume = mbr1.volume() + mbr2.volume(); 346 | } 347 | } 348 | return splitPoint; 349 | } 350 | 351 | public RStarLeaf splitLeaf(RStarLeaf splittingLeaf, SpatialPoint newPoint) throws AssertionError{ 352 | ArrayList childPointers = splittingLeaf.childPointers; 353 | if (childPointers.size() <= 0) { 354 | throw new AssertionError(); 355 | } 356 | 357 | ArrayList children = new ArrayList(childPointers.size()); 358 | //load all children 359 | for (long childId : childPointers) { 360 | PointDTO dto = disk.loadPoint(childId); 361 | children.add(new SpatialPoint(dto)); 362 | } 363 | 364 | children.add(newPoint); 365 | int splitAxis = chooseLeafSplitAxis(children); 366 | int splitPoint = chooseLeafSplitpoint(children, splitAxis); 367 | 368 | Object[] sorting = children.toArray(); 369 | final SpatialComparator comp = new SpatialComparator(splitAxis, bestSortOrder); 370 | sort(sorting, comp); 371 | 372 | splittingLeaf.loadedChildren = new ArrayList(); 373 | splittingLeaf.childPointers = new ArrayList(); 374 | RStarLeaf newChild = new RStarLeaf(dimension); 375 | 376 | HyperRectangle newMbr1 = new HyperRectangle(dimension); //adjusted mbr for splittingLeaf 377 | HyperRectangle newMbr2 = new HyperRectangle(dimension); //adjusted mbr for newChild 378 | 379 | for (int i = 0; i < sorting.length; i++) { 380 | SpatialPoint spatialPoint = (SpatialPoint) sorting[i]; 381 | if (i < splitPoint) { 382 | if (spatialPoint == newPoint) { 383 | splittingLeaf.loadedChildren.add(spatialPoint); 384 | } else { 385 | splittingLeaf.childPointers.add(childPointers.get(children.indexOf(spatialPoint))); 386 | } 387 | newMbr1.update(spatialPoint); 388 | } else { 389 | if (spatialPoint == newPoint) { 390 | newChild.loadedChildren.add(spatialPoint); 391 | } else { 392 | newChild.childPointers.add(childPointers.get(children.indexOf(spatialPoint))); 393 | } 394 | newMbr2.update(spatialPoint); 395 | } 396 | } 397 | splittingLeaf.setMbr(newMbr1); 398 | newChild.setMbr(newMbr2); 399 | 400 | disk.saveNode(splittingLeaf); 401 | return newChild; 402 | } 403 | 404 | public RStarNode splitInternalNode(RStarInternal splittingNode, RStarNode node) throws FileNotFoundException { 405 | //load all children of target 406 | ArrayList childPointers = splittingNode.childPointers; 407 | if (childPointers.size() <= 0) { 408 | throw new AssertionError(); 409 | } 410 | 411 | ArrayList children = new ArrayList(childPointers.size()); 412 | //load all children 413 | for (long childNodeId : childPointers) { 414 | children.add(disk.loadNode(childNodeId)); 415 | } 416 | 417 | children.add(node); 418 | int splitAxis = chooseInternalSplitAxis(children); 419 | int splitPoint = chooseInternalSplitpoint(children, splitAxis); 420 | 421 | Object[] sorting = children.toArray(); 422 | final SpatialComparator comp = new SpatialComparator(splitAxis, bestSortOrder); 423 | sort(sorting, comp); 424 | 425 | splittingNode.childPointers = new ArrayList(); 426 | RStarInternal createdNode = new RStarInternal(dimension); 427 | 428 | HyperRectangle newMbr1 = new HyperRectangle(dimension); 429 | HyperRectangle newMbr2 = new HyperRectangle(dimension); 430 | 431 | for (int i = 0; i < sorting.length; i++) { 432 | RStarNode childNode = (RStarNode) sorting[i]; 433 | if (i < splitPoint) { 434 | splittingNode.childPointers.add(childNode.getNodeId()); 435 | childNode.setParentId(splittingNode.getNodeId()); 436 | newMbr1.update(childNode.getMBR()); 437 | } else { 438 | createdNode.childPointers.add(childNode.getNodeId()); 439 | childNode.setParentId(createdNode.getNodeId()); 440 | newMbr2.update(childNode.getMBR()); 441 | } 442 | disk.saveNode(childNode); //record the updates to disk 443 | } 444 | 445 | splittingNode.setMbr(newMbr1); 446 | createdNode.setMbr(newMbr2); 447 | 448 | disk.saveNode(splittingNode); 449 | return createdNode; 450 | } 451 | } 452 | --------------------------------------------------------------------------------