Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.github.davidmoten.rtree.Context;
import com.github.davidmoten.rtree.Entry;
import com.github.davidmoten.rtree.Leaf;
import com.github.davidmoten.rtree.Node;
import com.github.davidmoten.rtree.NonLeaf;
import com.github.davidmoten.rtree.geometry.Geometry;
Expand Down Expand Up @@ -110,14 +111,42 @@ public static <T, S extends Geometry> NodeAndEntries<T, S> delete(
else {
List<Node<T, S>> nodes = Util.remove(children, removeTheseNodes);
nodes.addAll(addTheseNodes);
if (nodes.isEmpty())
return new NodeAndEntries<>(Optional.empty(), addTheseEntries,
countDeleted);
else {
if (nodes.size() < node.context().minChildren()) {
// this node has fallen below minChildren so it must be
// dissolved: all entries in its remaining children (which may
// themselves be non-leaf nodes) are collected and passed up
// to be redistributed (re-added) higher up the tree, exactly
// as happens for leaves in LeafHelper.delete
List<Entry<T, S>> entries = new ArrayList<Entry<T, S>>(addTheseEntries);
entries.addAll(getEntries(nodes));
return new NodeAndEntries<>(Optional.empty(), entries, countDeleted);
} else {
NonLeaf<T, S> nd = node.context().factory().createNonLeaf(nodes, node.context());
return new NodeAndEntries<>(of(nd), addTheseEntries, countDeleted);
}
}
}

private static <T, S extends Geometry> List<Entry<T, S>> getEntries(
List<? extends Node<T, S>> nodes) {
List<Entry<T, S>> list = new ArrayList<Entry<T, S>>();
for (Node<T, S> node : nodes) {
addEntries(node, list);
}
return list;
}

private static <T, S extends Geometry> void addEntries(Node<T, S> node,
List<Entry<T, S>> list) {
if (node instanceof Leaf) {
list.addAll(((Leaf<T, S>) node).entries());
} else if (node instanceof NonLeaf) {
NonLeaf<T, S> n = (NonLeaf<T, S>) node;
int count = n.count();
for (int i = 0; i < count; i++) {
addEntries((Node<T, S>) n.child(i), list);
}
}
}

}
45 changes: 45 additions & 0 deletions src/test/java/com/github/davidmoten/rtree/RTreeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1073,6 +1073,51 @@ public void testDeleteIssue81f() {
assertEquals(0, t.size());
}

@Test
public void testDeleteIssue196() {
// non-leaf nodes must dissolve (not just be kept as-is) once their
// child count falls below minChildren, cascading up through the tree
int maxChildren = 3;
int minChildren = 2;
RTree<Object, Rectangle> tree = RTree.maxChildren(maxChildren).minChildren(minChildren)
.<Object, Rectangle>create();
List<Entry<Object, Rectangle>> entries = new ArrayList<Entry<Object, Rectangle>>();
for (int i = 1; i <= 30; i++) {
entries.add(e(i));
}
for (Entry<Object, Rectangle> entry : entries) {
tree = tree.add(entry);
}
assertTrue(tree.calculateDepth() > 2);
for (int i = 0; i < entries.size() - 1; i++) {
tree = tree.delete(entries.get(i));
assertFalse(tree.entries().contains(entries.get(i)).toBlocking().single());
assertMinChildrenRespected(tree.root(), minChildren);
}
assertEquals(1, tree.size());
}

private static void assertMinChildrenRespected(Optional<? extends Node<Object, Rectangle>> root,
int minChildren) {
if (root.isPresent()) {
assertMinChildrenRespected(root.get(), minChildren, true);
}
}

private static void assertMinChildrenRespected(Node<Object, Rectangle> node, int minChildren,
boolean isRoot) {
if (node instanceof NonLeaf) {
NonLeaf<Object, Rectangle> n = (NonLeaf<Object, Rectangle>) node;
if (!isRoot) {
assertTrue("non-leaf node has " + n.count() + " children, less than minChildren="
+ minChildren, n.count() >= minChildren);
}
for (int i = 0; i < n.count(); i++) {
assertMinChildrenRespected(n.child(i), minChildren, false);
}
}
}

private static Func2<Point, Circle, Double> distanceCircleToPoint = new Func2<Point, Circle, Double>() {
@Override
public Double call(Point point, Circle circle) {
Expand Down