以下为R树插入和删除Java代码实现,共7个类文件
package com.njupt.rtree;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import com.njupt.constants.Constants;
/**
* @ClassName RTree
* @Description
*/
public class RTree
{
/**
* 根节点
*/
private RTNode root;
/**
* 树类型
*/
private int tree_type;
/**
* 结点容量
*/
private int nodeCapacity = -1;
/**
* 结点填充因子
*/
private float fillFactor = -1;
private int dimension ;
public RTree(int capacity, float fillFactor, int type, int dimension)
{
this.fillFactor = fillFactor;
tree_type = type;
nodeCapacity = capacity;
this.dimension = dimension;
root = new RTDataNode(this,Constants.NULL);
}
/**
* @return RTree的维度
*/
public int getDimension()
{
return dimension;
}
public void setRoot(RTNode root)
{
this.root = root;
}
public float getFillFactor()
{
return fillFactor;
}
/**
* @return 返回结点容量
*/
public int getNodeCapacity()
{
return nodeCapacity;
}
/**
* @return 返回树的类型
*/
public int getTreeType()
{
return tree_type;
}
/**
* 向Rtree中插入Rectangle
* 1、先找到合适的叶节点
* 2、再向此叶节点中插入
* @param rectangle
*/
public boolean insert(Rectangle rectangle)
{
if(rectangle == null)
throw new IllegalArgumentException("Rectangle cannot be null.");
if(rectangle.getHigh().getDimension() != getDimension())
{
throw new IllegalArgumentException("Rectangle dimension different than RTree dimension.");
}
RTDataNode leaf = root.chooseLeaf(rectangle);
return leaf.insert(rectangle);
}
/**
* 从R树中删除Rectangle
* 1、寻找包含记录的结点--调用算法findLeaf()来定位包含此记录的叶子结点L,如果没有找到则算法终止。
* 2、删除记录--将找到的叶子结点L中的此记录删除
* 3、调用算法condenseTree
* @param rectangle
* @return
*/
public int delete(Rectangle rectangle)
{
if(rectangle == null)
{
throw new IllegalArgumentException("Rectangle cannot be null.");
}
if(rectangle.getHigh().getDimension() != getDimension())
{
throw new IllegalArgumentException("Rectangle dimension different than RTree dimension.");
}
RTDataNode leaf = root.findLeaf(rectangle);
if(leaf != null)
{
return leaf.delete(rectangle);
}
return -1;
}
/**
* 从给定的结点root开始遍历所有的结点
* @param node
* @return 所有遍历的结点集合
*/
public List traversePostOrder(RTNode root)
{
if(root == null)
throw new IllegalArgumentException("Node cannot be null.");
List list = new ArrayList();
list.add(root);
if(! root.isLeaf())
{
for(int i = 0; i < root.usedSpace; i ++)
{
List a = traversePostOrder(((RTDirNode)root).getChild(i));
for(int j = 0; j < a.size(); j ++)
{
list.add(a.get(j));
}
}
}
return list;
}
public static void main(String[] args) throws Exception
{
// RTree tree = new RTree(3, 0.4f, Constants.RTREE_QUADRATIC, 2);
//
// float[] f = {10, 20, 40, 70, //1
// 30, 10, 70, 15,
// 100, 70, 110, 80, //3
// 0, 50, 30, 55,
// 13, 21, 54, 78, //5
// 3, 8, 23, 34,
// 200, 29, 202, 50,
// 34, 1, 35, 1, //8
// 201, 200, 234, 203,
// 56, 69, 58, 70, //10
// 2, 67, 270, 102,
// 1, 10, 310, 20, //12
// 23, 12, 345, 120,
// 5, 34, 100, 340,
// 19,100,450,560, //15
// 12,340,560,450,
// 34,45,190,590,
// 24,47,770,450, //18
//
// 91,99,390,980,
// 89,10,99,100, //20
// 10,29,400,990,
// 110,220,220,330,
// 123,24,234,999 //23
// };
//
// //插入结点
// for(int i = 0; i < f.length;)
// {
// Point p1 = new Point(new float[]{f[i++],f[i++]});
// Point p2 = new Point(new float[]{f[i++],f[i++]});
// final Rectangle rectangle = new Rectangle(p1, p2);
// tree.insert(rectangle);
//
// Rectangle[] rectangles = tree.root.datas;
// System.out.println(tree.root.level);
// for(int j = 0; j < rectangles.length; j ++)
// System.out.println(rectangles[j]);
// }
// System.out.println("---------------------------------");
// System.out.println("Insert finished.");
// //删除结点
// System.out.println("---------------------------------");
// System.out.println("Begin delete.");
//
// for(int i = 0; i < f.length;)
// {
// Point p1 = new Point(new float[]{f[i++],f[i++]});
// Point p2 = new Point(new float[]{f[i++],f[i++]});
// final Rectangle rectangle = new Rectangle(p1, p2);
// tree.delete(rectangle);
//
// Rectangle[] rectangles = tree.root.datas;
// System.out.println(tree.root.level);
// for(int j = 0; j < rectangles.length; j ++)
// System.out.println(rectangles[j]);
// }
//
// System.out.println("---------------------------------");
// System.out.println("Delete finished.");
// Rectangle[] rectangles = tree.root.datas;
// for(int i = 0; i < rectangles.length; i ++)
// System.out.println(rectangles[i]);
System.out.println("---------------------------------");
RTree tree = new RTree(5, 0.4f, Constants.RTREE_QUADRATIC,2);
BufferedReader reader = new BufferedReader(new FileReader(new File("d:\\LB.txt")));
String line ;
while((line = reader.readLine()) != null)
{
String[] splits = line.split(" ");
float lx = Float.parseFloat(splits[1]);
float ly = Float.parseFloat(splits[2]);
float hx = Float.parseFloat(splits[3]);
float hy = Float.parseFloat(splits[4]);
Point p1 = new Point(new float[]{lx,ly});
Point p2 = new Point(new float[]{hx,hy});
final Rectangle rectangle = new Rectangle(p1, p2);
tree.insert(rectangle);
Rectangle[] rectangles = tree.root.datas;
System.out.println(tree.root.level);
for(int j = 0; j < rectangles.length; j ++)
System.out.println(rectangles[j]);
}
//删除结点
System.out.println("---------------------------------");
System.out.println("Begin delete.");
reader = new BufferedReader(new FileReader(new File("d:\\LB.txt")));
while((line = reader.readLine()) != null)
{
String[] splits = line.split(" ");
float lx = Float.parseFloat(splits[1]);
float ly = Float.parseFloat(splits[2]);
float hx = Float.parseFloat(splits[3]);
float hy = Float.parseFloat(splits[4]);
Point p1 = new Point(new float[]{lx,ly});
Point p2 = new Point(new float[]{hx,hy});
final Rectangle rectangle = new Rectangle(p1, p2);
tree.delete(rectangle);
Rectangle[] rectangles = tree.root.datas;
System.out.println(tree.root.level);
for(int j = 0; j < rectangles.length; j ++)
System.out.println(rectangles[j]);
}
System.out.println("---------------------------------");
System.out.println("Delete finished.");
}
}
package com.njupt.rtree;
import java.util.List;
import com.njupt.constants.Constants;
import com.njupt.spatial.INode;
/**
* @ClassName RTNode
* @Description
*/
public abstract class RTNode
{
/**
* 结点所在的树
*/
protected RTree rtree;
/**
* 结点所在的层
*/
protected int level;
/**
* 相当于条目
*/
protected Rectangle[] datas;
// /**
// * 结点的容量
// */
// protected int capacity;
/**
* 父节点
*/
protected RTNode parent;
/**
* 结点已用的空间
*/
protected int usedSpace;
/**
* 记录插入的搜索路径索引
*/
protected int insertIndex;
/**
* 记录删除的查找路径索引
*/
protected int deleteIndex;
public RTNode(RTree rtree, RTNode parent, int level)
{
this.rtree = rtree;
this.parent = parent;
this.level = level;
// this.capacity = capacity;
datas = new Rectangle[rtree.getNodeCapacity() + 1];//多出的一个用于结点分裂
usedSpace = 0;
}
/**
* @return 返回父节点
*/
public RTNode getParent()
{
return parent;
}
// /**
// * @return 结点容量
// */
// public int getNodeCapacity()
// {
// return capacity;
// }
/**
* 向结点中添加Rectangle,即添加条目
* @param rectangle
*/
protected void addData(Rectangle rectangle)
{
if(usedSpace == rtree.getNodeCapacity())
{
throw new IllegalArgumentException("Node is full.");
}
datas[usedSpace ++] = rectangle;
}
/**
* 删除结点中的第i个条目
* @param i
*/
protected void deleteData(int i)
{
if(datas[i + 1] != null)
{
System.arraycopy(datas, i + 1, datas, i, usedSpace - i -1);
datas[usedSpace - 1] = null;
}
else
datas[i] = null;
usedSpace --;
}
/**
* 叶节点L中刚刚删除了一个条目,如果这个结点的条目数太少下溢,则删除该结点,同时将该结点中剩余的条目重定位到其他结点中。
* 如果有必要,要逐级向上进行这种删除,调整向上传递的路径上的所有外包矩形,使其尽可能小,直到根节点。
* @param list 存储删除结点中剩余条目
*/
protected void condenseTree(List list)
{
if(isRoot())
{
//根节点只有一个条目了,即只有左孩子或者右孩子
if(! isLeaf() && usedSpace == 1)
{
RTDirNode root = (RTDirNode) this;
RTNode child = root.getChild(0);
root.children.remove(this);//gc
child.parent = null;
// if(child.level > 0)
// child.level --;
rtree.setRoot(child);
// //所有结点的level减1,即树降低一层
// List nodes = rtree.traversePostOrder(child);
// for(int i = 0; i < nodes.size(); i ++)
// {
// nodes.get(i).level -= 1;
// }
}
}else{
RTNode parent = getParent();
int min = Math.round(rtree.getNodeCapacity() * rtree.getFillFactor());
if(usedSpace < min)
{
parent.deleteData(parent.deleteIndex);//其父节点中删除此条目
((RTDirNode)parent).children.remove(this);
this.parent = null;
list.add(this);//之前已经把数据删除了
}else{
parent.datas[parent.deleteIndex] = getNodeRectangle();
}
parent.condenseTree(list);
}
}
/**
* 分裂结点的平方算法
* 1、为两个组选择第一个条目--调用算法pickSeeds()来为两个组选择第一个元素,分别把选中的两个条目分配到两个组当中。
* 2、检查是否已经分配完毕,如果一个组中的条目太少,为避免下溢,将剩余的所有条目全部分配到这个组中,算法终止
* 3、调用pickNext来选择下一个进行分配的条目--计算把每个条目加入每个组之后面积的增量,选择两个组面积增量差最大的条目索引,
* 如果面积增量相等则选择面积较小的组,若面积也相等则选择条目数更少的组
* @param rectangle 导致分裂的溢出Rectangle
* @return 两个组中的条目的索引
*/
protected int[][] quadraticSplit(Rectangle rectangle)
{
if(rectangle == null)
{
throw new IllegalArgumentException("Rectangle cannot be null.");
}
datas[usedSpace] = rectangle; //先添加进去
// if(this instanceof RTDirNode)
// {
// (RTDirNode)(this).children.add()
// }
int total = usedSpace + 1; //结点总数
//标记访问的条目
int[] mask = new int[total];
for(int i = 0; i < total; i ++)
{
mask[i] = 1;
}
//每个组只是有total/2个条目
int c = total/2 + 1;
//每个结点最小条目个数
int minNodeSize = Math.round(rtree.getNodeCapacity() * rtree.getFillFactor());
//至少有两个
if(minNodeSize < 2)
minNodeSize = 2;
//记录没有被检查的条目的个数
int rem = total;
int[] group1 = new int[c];//记录分配的条目的索引
int[] group2 = new int[c];//记录分配的条目的索引
//跟踪被插入每个组的条目的索引
int i1 = 0, i2 = 0;
int[] seed = pickSeeds();
group1[i1 ++] = seed[0];
group2[i2 ++] = seed[1];
rem -=2;
mask[group1[0]] = -1;
mask[group2[0]] = -1;
while(rem > 0)
{
//将剩余的所有条目全部分配到group1组中,算法终止
if(minNodeSize - i1 == rem)
{
for(int i = 0; i < total; i ++)//总共rem个
{
if(mask[i] != -1)//还没有被分配
{
group1[i1 ++] = i;
mask[i] = -1;
rem --;
}
}
//将剩余的所有条目全部分配到group1组中,算法终止
}else if(minNodeSize - i2 == rem)
{
for(int i = 0; i < total; i ++)//总共rem个
{
if(mask[i] != -1)//还没有被分配
{
group2[i2 ++] = i;
mask[i] = -1;
rem --;
}
}
}else
{
//求group1中所有条目的最小外包矩形
Rectangle mbr1 = (Rectangle) datas[group1[0]].clone();
for(int i = 1; i < i1; i ++)
{
mbr1 = mbr1.getUnionRectangle(datas[group1[i]]);
}
//求group2中所有条目的外包矩形
Rectangle mbr2 = (Rectangle) datas[group2[0]].clone();
for(int i = 1; i < i2; i ++)
{
mbr2 = mbr2.getUnionRectangle(datas[group2[i]]);
}
//找出下一个进行分配的条目
double dif = Double.NEGATIVE_INFINITY;
double areaDiff1 = 0, areaDiff2 = 0;
int sel = -1;
for(int i = 0; i < total; i ++)
{
if(mask[i] != -1)//还没有被分配的条目
{
//计算把每个条目加入每个组之后面积的增量,选择两个组面积增量差最大的条目索引
Rectangle a = mbr1.getUnionRectangle(datas[i]);
areaDiff1 = a.getArea() - mbr1.getArea();
Rectangle b = mbr2.getUnionRectangle(datas[i]);
areaDiff2 = b.getArea() - mbr2.getArea();
if(Math.abs(areaDiff1 - areaDiff2) > dif)
{
dif = Math.abs(areaDiff1 - areaDiff2);
sel = i;
}
}
}
if(areaDiff1 < areaDiff2)//先比较面积增量
{
group1[i1 ++] = sel;
}else if(areaDiff1 > areaDiff2)
{
group2[i2 ++] = sel;
}else if(mbr1.getArea() < mbr2.getArea())//再比较自身面积
{
group1[i1 ++] = sel;
}else if(mbr1.getArea() > mbr2.getArea())
{
group2[i2 ++] = sel;
}else if(i1 < i2)//最后比较条目个数
{
group1[i1 ++] = sel;
}else if(i1 > i2)
{
group2[i2 ++] = sel;
}else {
group1[i1 ++] = sel;
}
mask[sel] = -1;
rem --;
}
}//end while
int[][] ret = new int[2][];
ret[0] = new int[i1];
ret[1] = new int[i2];
for(int i = 0; i < i1; i ++)
{
ret[0][i] = group1[i];
}
for(int i = 0; i < i2; i ++)
{
ret[1][i] = group2[i];
}
return ret;
}
/**
* 1、对每一对条目E1和E2,计算包围它们的Rectangle J,计算d = area(J) - area(E1) - area(E2);
* 2、Choose the pair with the largest d
* @return 返回两个条目如果放在一起会有最多的冗余空间的条目索引
*/
protected int[] pickSeeds()
{
double inefficiency = Double.NEGATIVE_INFINITY;
int i1 = 0, i2 = 0;
//
for(int i = 0; i < usedSpace; i ++)
{
for(int j = i + 1; j <= usedSpace; j ++)//注意此处的j值
{
Rectangle rectangle = datas[i].getUnionRectangle(datas[j]);
double d = rectangle.getArea() - datas[i].getArea() - datas[j].getArea();
if(d > inefficiency)
{
inefficiency = d;
i1 = i;
i2 = j;
}
}
}
return new int[]{i1, i2};
}
/**
* @return 返回包含结点中所有条目的最小Rectangle
*/
public Rectangle getNodeRectangle()
{
if(usedSpace > 0)
{
Rectangle[] rectangles = new Rectangle[usedSpace];
System.arraycopy(datas, 0, rectangles, 0, usedSpace);
return Rectangle.getUnionRectangle(rectangles);
}else {
return new Rectangle(new Point(new float[]{0,0}), new Point(new float[]{0,0}));
}
}
/**
* @return 是否根节点
*/
public boolean isRoot()
{
return (parent == Constants.NULL);
}
/**
* @return 是否非叶子结点
*/
public boolean isIndex()
{
return (level != 0);
}
/**
* @return 是否叶子结点
*/
public boolean isLeaf()
{
return (level == 0);
}
/**
* 步骤CL1:初始化——记R树的根节点为N。
* 步骤CL2:检查叶节点——如果N是个叶节点,返回N
* 步骤CL3:选择子树——如果N不是叶节点,则从N中所有的条目中选出一个最佳的条目F,
* 选择的标准是:如果E加入F后,F的外廓矩形FI扩张最小,则F就是最佳的条目。如果有两个
* 条目在加入E后外廓矩形的扩张程度相等,则在这两者中选择外廓矩形较小的那个。
* 步骤CL4:向下寻找直至达到叶节点——记Fp指向的孩子节点为N,然后返回步骤CL2循环运算,
* 直至查找到叶节点。
* @param Rectangle
* @return RTDataNode
*/
protected abstract RTDataNode chooseLeaf(Rectangle rectangle);
/**
* R树的根节点为T,查找包含rectangle的叶子结点
* 1、如果T不是叶子结点,则逐个查找T中的每个条目是否包围rectangle,若包围则递归调用findLeaf()
* 2、如果T是一个叶子结点,则逐个检查T中的每个条目能否匹配rectangle
* @param rectangle
* @return 返回包含rectangle的叶节点
*/
protected abstract RTDataNode findLeaf(Rectangle rectangle);
}
package com.njupt.rtree;
import java.util.ArrayList;
import java.util.List;
import com.njupt.constants.Constants;
/**
* @ClassName RTDataNode
* @Description
*/
public class RTDataNode extends RTNode
{
public RTDataNode(RTree rTree, RTNode parent)
{
super(rTree, parent, 0);
}
/**
* 在叶节点中插入Rectangle,插入后如果其父节点不为空则需要向上调整树直到根节点;
* 若插入Rectangle之后超过结点容量则需要分裂结点
* @param rectangle
* @return
*/
public boolean insert(Rectangle rectangle)
{
if(usedSpace < rtree.getNodeCapacity())
{
datas[usedSpace ++] = rectangle;
RTDirNode parent = (RTDirNode) getParent();
if(parent != null)
parent.adjustTree(this, null);
return true;
}else{//超过结点容量
RTDataNode[] splitNodes = splitLeaf(rectangle);
RTDataNode l = splitNodes[0];
RTDataNode ll = splitNodes[1];
if(isRoot())
{
//根节点已满,需要分裂。创建新的根节点
RTDirNode rDirNode = new RTDirNode(rtree, Constants.NULL, level + 1);
rtree.setRoot(rDirNode);
rDirNode.addData(l.getNodeRectangle());
rDirNode.addData(ll.getNodeRectangle());
ll.parent = rDirNode;
l.parent = rDirNode;
rDirNode.children.add(l);
rDirNode.children.add(ll);
}else{//不是根节点
RTDirNode parentNode = (RTDirNode) getParent();
parentNode.adjustTree(l, ll);
}
}
return true;
}
/**
* 插入Rectangle之后超过容量需要分裂
* @param rectangle
* @return
*/
public RTDataNode[] splitLeaf(Rectangle rectangle)
{
int[][] group = null;
switch(rtree.getTreeType())
{
case Constants.RTREE_LINEAR:
break;
case Constants.RTREE_QUADRATIC:
group = quadraticSplit(rectangle);
break;
case Constants.RTREE_EXPONENTIAL:
break;
case Constants.RSTAR:
break;
default:
throw new IllegalArgumentException("Invalid tree type.");
}
RTDataNode l = new RTDataNode(rtree, parent);
RTDataNode ll = new RTDataNode(rtree, parent);
int[] group1 = group[0];
int[] group2 = group[1];
for(int i = 0; i < group1.length; i ++)
{
l.addData(datas[group1[i]]);
}
for(int i = 0; i < group2.length; i ++)
{
ll.addData(datas[group2[i]]);
}
return new RTDataNode[]{l, ll};
}
@Override
public RTDataNode chooseLeaf(Rectangle rectangle)
{
insertIndex = usedSpace;//记录插入路径的索引
return this;
}
/**
* 从叶节点中删除此条目rectangle
* 先删除此rectangle,再调用condenseTree()返回删除结点的集合,把其中的叶子结点中的每个条目重新插入;
* 非叶子结点就从此结点开始遍历所有结点,然后把所有的叶子结点中的所有条目全部重新插入
* @param rectangle
* @return
*/
protected int delete(Rectangle rectangle)
{
for(int i = 0; i < usedSpace; i ++)
{
if(datas[i].equals(rectangle))
{
deleteData(i);
List deleteEntriesList = new ArrayList();
condenseTree(deleteEntriesList);
//重新插入删除结点中剩余的条目
for(int j = 0; j < deleteEntriesList.size(); j ++)
{
RTNode node = deleteEntriesList.get(j);
if(node.isLeaf())//叶子结点,直接把其上的数据重新插入
{
for(int k = 0; k < node.usedSpace; k ++)
{
rtree.insert(node.datas[k]);
}
}else{//非叶子结点,需要先后序遍历出其上的所有结点
List traverseNodes = rtree.traversePostOrder(node);
//把其中的叶子结点中的条目重新插入
for(int index = 0; index < traverseNodes.size(); index ++)
{
RTNode traverseNode = traverseNodes.get(index);
if(traverseNode.isLeaf())
{
for(int t = 0; t < traverseNode.usedSpace; t ++)
{
rtree.insert(traverseNode.datas[t]);
}
}
}
}
}
return deleteIndex;
}//end if
}//end for
return -1;
}
@Override
protected RTDataNode findLeaf(Rectangle rectangle)
{
for(int i = 0; i < usedSpace; i ++)
{
if(datas[i].enclosure(rectangle))
{
deleteIndex = i;//记录搜索路径
return this;
}
}
return null;
}
}
package com.njupt.rtree;
import java.util.ArrayList;
import java.util.List;
import com.njupt.constants.Constants;
/**
* @ClassName RTDirNode
* @Description 非叶节点
*/
public class RTDirNode extends RTNode
{
/**
* 孩子结点
*/
protected List children;
public RTDirNode(RTree rtree, RTNode parent, int level)
{
super(rtree, parent, level);
children = new ArrayList();
}
/**
* @param index
* @return 对应索引下的孩子结点
*/
public RTNode getChild(int index)
{
return children.get(index);
}
@Override
public RTDataNode chooseLeaf(Rectangle rectangle)
{
int index;
switch (rtree.getTreeType())
{
case Constants.RTREE_LINEAR:
case Constants.RTREE_QUADRATIC:
case Constants.RTREE_EXPONENTIAL:
index = findLeastEnlargement(rectangle);
break;
case Constants.RSTAR:
if(level == 1)//即此结点指向叶节点
{
index = findLeastOverlap(rectangle);
}else{
index = findLeastEnlargement(rectangle);
}
break;
default:
throw new IllegalStateException("Invalid tree type.");
}
insertIndex = index;//记录插入路径的索引
return getChild(index).chooseLeaf(rectangle);
}
/**
* @param rectangle
* @return 返回最小重叠面积的结点的索引,如果重叠面积相等则选择加入此Rectangle后面积增量更小的,如果面积增量还相等则选择自身面积更小的
*/
private int findLeastOverlap(Rectangle rectangle)
{
float overlap = Float.POSITIVE_INFINITY;
int sel = -1;
for(int i = 0; i < usedSpace; i ++)
{
RTNode node = getChild(i);
float ol = 0;
for(int j = 0; j < node.datas.length; j ++)
{
ol += rectangle.intersectingArea(node.datas[j]);
}
if(ol < overlap)
{
overlap = ol;//记录重叠面积最小的
sel = i;//记录第几个孩子的索引
}else if(ol == overlap)//如果重叠面积相等则选择加入此Rectangle后面积增量更小的,如果面积增量还相等则选择自身面积更小的
{
double area1 = datas[i].getUnionRectangle(rectangle).getArea() - datas[i].getArea();
double area2 = datas[sel].getUnionRectangle(rectangle).getArea() - datas[sel].getArea();
if(area1 == area2)
{
sel = (datas[sel].getArea() <= datas[i].getArea()) ? sel : i;
}else{
sel = (area1 < area2) ? i : sel;
}
}
}
return sel;
}
/**
* @param rectangle
* @return 面积增量最小的结点的索引,如果面积增量相等则选择自身面积更小的
*/
private int findLeastEnlargement(Rectangle rectangle)
{
double area = Double.POSITIVE_INFINITY;
int sel = -1;
for(int i = 0; i < usedSpace; i ++)
{
double enlargement = datas[i].getUnionRectangle(rectangle).getArea() - datas[i].getArea();
if(enlargement < area)
{
area = enlargement;
sel = i;
}else if(enlargement == area)
{
sel = (datas[sel].getArea() < datas[i].getArea()) ? sel : i;
}
}
return sel;
}
/**
* 插入新的Rectangle后从插入的叶节点开始向上调整RTree,直到根节点
* @param node1 引起需要调整的孩子结点
* @param node2 分裂的结点,若未分裂则为null
*/
public void adjustTree(RTNode node1, RTNode node2)
{
//先要找到指向原来旧的结点(即未添加Rectangle之前)的条目的索引
datas[insertIndex] = node1.getNodeRectangle();//先用node1覆盖原来的结点
children.set(insertIndex, node1);//替换旧的结点
if(node2 != null)
{
insert(node2);//插入新的结点
}else if(! isRoot())//还没到达根节点
{
RTDirNode parent = (RTDirNode) getParent();
parent.adjustTree(this, null);//向上调整直到根节点
}
}
/**
* @param node
* @return 如果结点需要分裂则返回true
*/
protected boolean insert(RTNode node)
{
if(usedSpace < rtree.getNodeCapacity())
{
datas[usedSpace ++] = node.getNodeRectangle();
children.add(node);//新加的
node.parent = this;//新加的
RTDirNode parent = (RTDirNode) getParent();
if(parent != null)
{
parent.adjustTree(this, null);
}
return false;
}else{//非叶子结点需要分裂
RTDirNode[] a = splitIndex(node);
RTDirNode n = a[0];
RTDirNode nn = a[1];
if(isRoot())
{
//新建根节点,层数加1
RTDirNode newRoot = new RTDirNode(rtree, Constants.NULL, level + 1);
//还需要把原来结点的孩子添加到两个分裂的结点n和nn中,此时n和nn的孩子结点还为空
// for(int i = 0; i < n.usedSpace; i ++)
// {
// n.children.add(this.children.get(index));
// }
//
// for(int i = 0; i < nn.usedSpace; i ++)
// {
// nn.children.add(this.children.get(index));
// }
//把两个分裂的结点n和nn添加到根节点
newRoot.addData(n.getNodeRectangle());
newRoot.addData(nn.getNodeRectangle());
newRoot.children.add(n);
newRoot.children.add(nn);
//设置两个分裂的结点n和nn的父节点
n.parent = newRoot;
nn.parent = newRoot;
//最后设置rtree的根节点
rtree.setRoot(newRoot);//新加的
}else {
RTDirNode p = (RTDirNode) getParent();
p.adjustTree(n, nn);
}
}
return true;
}
/**
* 非叶子结点的分裂
*
* @param node
* @return
*/
private RTDirNode[] splitIndex(RTNode node)
{
int[][] group = null;
switch (rtree.getTreeType())
{
case Constants.RTREE_LINEAR:
break;
case Constants.RTREE_QUADRATIC:
group = quadraticSplit(node.getNodeRectangle());
children.add(node);//新加的
node.parent = this;//新加的
break;
case Constants.RTREE_EXPONENTIAL:
break;
case Constants.RSTAR:
break;
default:
throw new IllegalStateException("Invalid tree type.");
}
RTDirNode index1 = new RTDirNode(rtree, parent, level);
RTDirNode index2 = new RTDirNode(rtree, parent, level);
int[] group1 = group[0];
int[] group2 = group[1];
for(int i = 0; i < group1.length; i ++)
{
//为index1添加数据和孩子
index1.addData(datas[group1[i]]);
index1.children.add(this.children.get(group1[i]));//新加的
//让index1成为其父节点
this.children.get(group1[i]).parent = index1;//新加的
}
for(int i = 0; i < group2.length; i ++)
{
index2.addData(datas[group2[i]]);
index2.children.add(this.children.get(group2[i]));//新加的
this.children.get(group2[i]).parent = index2;//新加的
}
return new RTDirNode[]{index1,index2};
}
@Override
protected RTDataNode findLeaf(Rectangle rectangle)
{
for(int i = 0; i < usedSpace; i ++)
{
if(datas[i].enclosure(rectangle))
{
deleteIndex = i;//记录搜索路径
RTDataNode leaf = children.get(i).findLeaf(rectangle);
if(leaf != null)
return leaf;
}
}
return null;
}
}
package com.njupt.rtree;
/**
* 外包矩形
* @ClassName Rectangle
* @Description
*/
public class Rectangle implements Cloneable
{
private Point low;
private Point high;
public Rectangle(Point p1, Point p2)
{
if(p1 == null || p2 == null)
{
throw new IllegalArgumentException("Points cannot be null.");
}
if(p1.getDimension() != p2.getDimension())
{
throw new IllegalArgumentException("Points must be of same dimension.");
}
//先左下角后右上角
for(int i = 0; i < p1.getDimension(); i ++)
{
if(p1.getFloatCoordinate(i) > p2.getFloatCoordinate(i))
{
throw new IllegalArgumentException("坐标点为先左下角后右上角");
}
}
low = (Point) p1.clone();
high = (Point) p2.clone();
}
/**
* 返回Rectangle左下角的Point
* @return Point
*/
public Point getLow()
{
return (Point) low.clone();
}
/**
* 返回Rectangle右上角的Point
* @return Point
*/
public Point getHigh()
{
return high;
}
/**
* @param rectangle
* @return 包围两个Rectangle的最小Rectangle
*/
public Rectangle getUnionRectangle(Rectangle rectangle)
{
if(rectangle == null)
throw new IllegalArgumentException("Rectangle cannot be null.");
if(rectangle.getDimension() != getDimension())
{
throw new IllegalArgumentException("Rectangle must be of same dimension.");
}
float[] min = new float[getDimension()];
float[] max = new float[getDimension()];
for(int i = 0; i < getDimension(); i ++)
{
min[i] = Math.min(low.getFloatCoordinate(i), rectangle.low.getFloatCoordinate(i));
max[i] = Math.max(high.getFloatCoordinate(i), rectangle.high.getFloatCoordinate(i));
}
return new Rectangle(new Point(min), new Point(max));
}
/**
* @return 返回Rectangle的面积
*/
public float getArea()
{
float area = 1;
for(int i = 0; i < getDimension(); i ++)
{
area *= high.getFloatCoordinate(i) - low.getFloatCoordinate(i);
}
return area;
}
/**
* @param rectangles
* @return 包围一系列Rectangle的最小Rectangle
*/
public static Rectangle getUnionRectangle(Rectangle[] rectangles)
{
if(rectangles == null || rectangles.length == 0)
throw new IllegalArgumentException("Rectangle array is empty.");
Rectangle r0 = (Rectangle) rectangles[0].clone();
for(int i = 1; i < rectangles.length; i ++)
{
r0 = r0.getUnionRectangle(rectangles[i]);
}
return r0;
}
@Override
protected Object clone()
{
Point p1 = (Point) low.clone();
Point p2 = (Point) high.clone();
return new Rectangle(p1, p2);
}
@Override
public String toString()
{
return "Rectangle Low:" + low + " High:" + high;
}
public static void main(String[] args)
{
float[] f1 = {1.3f,2.4f};
float[] f2 = {3.4f,4.5f};
Point p1 = new Point(f1);
Point p2 = new Point(f2);
Rectangle rectangle = new Rectangle(p1, p2);
System.out.println(rectangle);
// Point point = rectangle.getHigh();
// point = p1;
// System.out.println(rectangle);
float[] f_1 = {0f,0f};
float[] f_2 = {-2f,2f};
float[] f_3 = {3f,3f};
float[] f_4 = {2.5f,2.5f};
float[] f_5 = {1.5f,1.5f};
p1 = new Point(f_1);
p2 = new Point(f_2);
Point p3 = new Point(f_3);
Point p4 = new Point(f_4);
Point p5 = new Point(f_5);
Rectangle re1 = new Rectangle(p1, p2);
Rectangle re2 = new Rectangle(p2, p3);
Rectangle re3 = new Rectangle(p4, p3);
// Rectangle re4 = new Rectangle(p3, p4);
Rectangle re5 = new Rectangle(p5, p4);
System.out.println(re1.isIntersection(re2));
System.out.println(re1.isIntersection(re3));
System.out.println(re1.intersectingArea(re2));
System.out.println(re1.intersectingArea(re5));
}
/**
* 两个Rectangle相交的面积
* @param rectangle Rectangle
* @return float
*/
public float intersectingArea(Rectangle rectangle)
{
if(! isIntersection(rectangle))
{
return 0;
}
float ret = 1;
for(int i = 0; i < rectangle.getDimension(); i ++)
{
float l1 = this.low.getFloatCoordinate(i);
float h1 = this.high.getFloatCoordinate(i);
float l2 = rectangle.low.getFloatCoordinate(i);
float h2 = rectangle.high.getFloatCoordinate(i);
//rectangle1在rectangle2的左边
if(l1 <= l2 && h1 <= h2)
{
ret *= (h1 - l1) - (l2 - l1);
}else if(l1 >= l2 && h1 >= h2)
//rectangle1在rectangle2的右边
{
ret *= (h2 - l2) - (l1 - l2);
}else if(l1 >= l2 && h1 <= h2)
//rectangle1在rectangle2里面
{
ret *= h1 - l1;
}else if(l1 <= l2 && h1 >= h2)
//rectangle1包含rectangle2
{
ret *= h2 - l2;
}
}
return ret;
}
/**
* @param rectangle
* @return 判断两个Rectangle是否相交
*/
public boolean isIntersection(Rectangle rectangle)
{
if(rectangle == null)
throw new IllegalArgumentException("Rectangle cannot be null.");
if(rectangle.getDimension() != getDimension())
{
throw new IllegalArgumentException("Rectangle cannot be null.");
}
for(int i = 0; i < getDimension(); i ++)
{
if(low.getFloatCoordinate(i) > rectangle.high.getFloatCoordinate(i) ||
high.getFloatCoordinate(i) < rectangle.low.getFloatCoordinate(i))
{
return false;
}
}
return true;
}
/**
* @return 返回Rectangle的维度
*/
private int getDimension()
{
return low.getDimension();
}
/**
* 判断rectangle是否被包围
* @param rectangle
* @return
*/
public boolean enclosure(Rectangle rectangle)
{
if(rectangle == null)
throw new IllegalArgumentException("Rectangle cannot be null.");
if(rectangle.getDimension() != getDimension())
throw new IllegalArgumentException("Rectangle dimension is different from current dimension.");
for(int i = 0; i < getDimension(); i ++)
{
if(rectangle.low.getFloatCoordinate(i) < low.getFloatCoordinate(i) ||
rectangle.high.getFloatCoordinate(i) > high.getFloatCoordinate(i))
return false;
}
return true;
}
@Override
public boolean equals(Object obj)
{
if(obj instanceof Rectangle)
{
Rectangle rectangle = (Rectangle) obj;
if(low.equals(rectangle.getLow()) && high.equals(rectangle.getHigh()))
return true;
}
return false;
}
}
package com.njupt.constants;
import com.njupt.rtree.RTNode;
public class Constants
{
public static final int MAX_NUMBER_OF_ENTRIES_IN_NODE = 20;//结点中的最大条目数
public static final int MIN_NUMBER_OF_ENTRIES_IN_NODE = 8;//结点中的最小条目数
public static final int RTDataNode_Dimension = 2;
/** Available RTree variants. */
public static final int RTREE_LINEAR = 0;
public static final int RTREE_QUADRATIC = 1;
public static final int RTREE_EXPONENTIAL = 2;
public static final int RSTAR = 3;
public static final int NIL = -1;
public static final RTNode NULL = null;
}
package com.njupt.rtree;
/**
* @ClassName Point
* @Description n维空间中的点,所有的维度被存储在一个float数组中
*/
public class Point implements Cloneable
{
private float[] data;
public Point(float[] data)
{
if(data == null)
{
throw new IllegalArgumentException("Coordinates cannot be null.");
}
if(data.length < 2)
{
throw new IllegalArgumentException("Point dimension should be greater than 1.");
}
this.data = new float[data.length];
System.arraycopy(data, 0, this.data, 0, data.length);
}
public Point(int[] data)
{
if(data == null)
{
throw new IllegalArgumentException("Coordinates cannot be null.");
}
if(data.length < 2)
{
throw new IllegalArgumentException("Point dimension should be greater than 1.");
}
this.data = new float[data.length];
for(int i = 0 ; i < data.length ; i ++)
{
this.data[i] = data[i];
}
}
@Override
protected Object clone()
{
float[] copy = new float[data.length];
System.arraycopy(data, 0, copy, 0, data.length);
return new Point(copy);
}
@Override
public String toString()
{
StringBuffer sBuffer = new StringBuffer("(");
for(int i = 0 ; i < data.length - 1 ; i ++)
{
sBuffer.append(data[i]).append(",");
}
sBuffer.append(data[data.length - 1]).append(")");
return sBuffer.toString();
}
public static void main(String[] args)
{
float[] test = {1.2f,2f,34f};
Point point1 = new Point(test);
System.out.println(point1);
int[] test2 = {1,2,3,4};
point1 = new Point(test2);
System.out.println(point1);
}
/**
* @return 返回Point的维度
*/
public int getDimension()
{
return data.length;
}
/**
* @param index
* @return 返回Point坐标第i位的float值
*/
public float getFloatCoordinate(int index)
{
return data[index];
}
/**
* @param index
* @return 返回Point坐标第i位的int值
*/
public int getIntCoordinate(int index)
{
return (int) data[index];
}
@Override
public boolean equals(Object obj)
{
if(obj instanceof Point)
{
Point point = (Point) obj;
if(point.getDimension() != getDimension())
throw new IllegalArgumentException("Points must be of equal dimensions to be compared.");
for(int i = 0; i < getDimension(); i ++)
{
if(getFloatCoordinate(i) != point.getFloatCoordinate(i))
return false;
}
}
if(! (obj instanceof Point))
return false;
return true;
}
}
原博客请看链接
> https://blog.csdn.net/renyisheng/article/details/40347223