@VisibleForTesting List<Quadrant> quadrantsByDepth(NodeId node, final int maxDepth) { final Envelope nodeBounds = node.value(); List<Integer> bucketsByDepth = bucketsByDepth(nodeBounds, maxDepth); List<Quadrant> quads = new ArrayList<>(bucketsByDepth.size()); for (int depthIndex = 0; depthIndex < bucketsByDepth.size(); depthIndex++) { int bucket = bucketsByDepth.get(depthIndex).intValue(); if (bucket == unpromotableBucketIndex(depthIndex)) { break; } Quadrant quad = Quadrant.VALUES[bucket]; quads.add(quad); } return quads; }
public void collapse(final DAG dag) { if (dag.getState() != STATE.CHANGED) { return; } final int numBuckets = dag.numBuckets(); if (numBuckets == 0) { return; } final List<TreeId> bucketIds = dag.bucketList(); if (numBuckets == 1) { final TreeId replaceId = bucketIds.get(0); DAG child = getOrCreateDAG(replaceId); setParent(child, dag); // for (TreeId childId : dag.bucketList()) { // child = getOrCreateDAG(childId); // collapse(dag, child); // } } else { for (TreeId bucketId : bucketIds) { DAG child = getOrCreateDAG(bucketId); collapse(child); } } }
/** * Returns the bucket index in the range 0-3 corresponding to this node at the specified depth * (i.e. the bucket index represents a quadrant), or {@code -1} if the spatial bounds of this * node don't fit on a single child quadrant and hence the node shall be kept at the current * tree node (hence creating a mixed {@link RevTree} with both direct children and buckets). */ public @Override int bucket(final NodeId nodeId, final int depthIndex) { final Envelope nodeBounds = nodeId.value(); final Quadrant quadrantAtDepth = computeQuadrant(nodeBounds, depthIndex); if (quadrantAtDepth == null) { return -1; } return quadrantAtDepth.ordinal(); }
for (int i = 1; i <= 1 + orig.normalizedSizeLimit(); i++) { Envelope bounds = new Envelope(i, i, 1, 1); Node node = support.createNode("node # " + i, bounds); orig.put(node); assertEquals("DAG depth should be 2 (root and one quad)", 2, orig.depth(orig.root)); assertEquals("DAG should have been collapsed", 1, orig.depth(orig.buildRoot())); assertTrue(update.remove(node1)); assertTrue(update.remove(node2)); assertTrue(update.remove(node3));
final int maxDepth = orig.getMaxDepth(); SW); final int splitSize = 1 + orig.normalizedSizeLimit(); NodeId nid = orig.computeId(n); List<Quadrant> quadrantsByDepth = orig.quadrantsByDepth(nid, maxDepth); assertEquals(level11Path, quadrantsByDepth); expectedDepth, orig.depth(orig.root)); orig.buildRoot();
Quadrant quadrant = computeQuadrant(treeBounds, childDepthIndex); if (quadrant == null) { final int unpromotableBucketIndex = unpromotableBucketIndex(); final boolean rootIsCanonical = rootId.contains(unpromotableBucketIndex); final int normalizedSizeLimit; normalizedSizeLimit = normalizedSizeLimit(); if (treeBucket.isPresent()) { Envelope bucketBounds = treeBucket.get().bounds().orNull(); Quadrant bucketQuad = computeQuadrant(bucketBounds, childDepthIndex); if (bucketQuad == null) { isValidQuad = false;
final long size = original.size(); final Envelope treeBounds = RevObjects.boundsOf(original); final List<Integer> quadrantsByDepth = bucketsByDepth(treeBounds, maxDepth); TreeId targetId; if (quadrantsByDepth.size() <= dagId.depthLength()) { } else { targetId = TreeId.valueOf(quadrantsByDepth); boolean overflown = size > normalizedSizeLimit(); if (overflown) { int unpromotableBucketIndex = unpromotableBucketIndex(); if (targetId.leafBucket() != unpromotableBucketIndex) { targetId = targetId.newChild(unpromotableBucketIndex);
public @Test void testCollapseRootUnpromotables() { // unpromotable at root because it overlaps the 4 quadrants final Envelope unpromotableBounds = new Envelope(-1, 1, -1, 1); QuadTreeClusteringStrategy strategy = support.newStrategy(); for (int i = 0; i < 130; i++) { Node node = support.createNode(String.valueOf(i), unpromotableBounds); strategy.put(node); } assertEquals(130, strategy.root.getTotalChildCount()); assertEquals(1, strategy.root.numBuckets()); assertEquals(2, strategy.depth(strategy.root)); DAG unpromotables = support.findDAG(strategy, "[4]"); assertEquals(12, unpromotables.numBuckets()); assertNotNull(unpromotables); strategy.collapse(strategy.root); assertEquals(1, strategy.depth(strategy.root)); assertEquals(130, strategy.root.getTotalChildCount()); assertEquals(12, strategy.root.numBuckets()); }
@Test public void test_overMaxlevel() { QuadTreeClusteringStrategy quadStrategy = support.newStrategy(); int maxDepth = quadStrategy.getMaxDepth(); // random path to depth Random rand = new Random(); List<Quadrant> quads = new ArrayList<>(8); for (int t = 0; t < maxDepth; t++) { quads.add(Quadrant.values()[rand.nextInt(3)]); } Node n = support.createNode("node", quads); NodeId quadID = quadStrategy.computeId(n); Envelope nodeBounds = quadID.value(); assertEquals(quads.get(maxDepth - 1), quadStrategy.computeQuadrant(nodeBounds, maxDepth - 1)); assertNull(quadStrategy.computeQuadrant(nodeBounds, maxDepth)); assertNull(quadStrategy.computeQuadrant(nodeBounds, maxDepth + 1)); assertEquals(maxDepth, quadStrategy.quadrantsByDepth(quadID, maxDepth + 10).size()); for (int t = 0; t < maxDepth; t++) { assertEquals(quads.get(t).getBucketNumber(), quadStrategy.bucket(quadID, t)); } } }
for (int i = 1; i <= 1 + orig.normalizedSizeLimit(); i++) { Envelope bounds = new Envelope(i, i, 1, 1); Node node = support.createNode("node # " + i, bounds); orig.put(node); assertEquals(1, update.update(node1, node1Update)); assertEquals(1, update.update(node2, node2Update)); assertEquals(1, update.update(node3, node3Update));
final int maxDepth = orig.getMaxDepth(); final int nodeCount = 1 + orig.normalizedSizeLimit(); List<Node> nodes = support.createNodes(nodeCount, Collections.nCopies(maxDepth, NE)); (maxDepth + 2), orig.depth(orig.root)); orig.buildRoot(); assertEquals("DAG should have been collapsed", 1, orig.depth(orig.root)); assertTrue(update.remove(node1)); assertTrue(update.remove(node2)); assertTrue(update.remove(node3));
final int maxDepth = builder.getMaxDepth(); final TreeId sourceBucketId = TreeId.valueOf(builder.bucketsByDepth(geomBounds, maxDepth)); final TreeId targetBucketId = TreeId .valueOf(builder.bucketsByDepth(updateBounds, maxDepth)); DAG root = builder.buildRoot(); assertEquals(130, root.getTotalChildCount()); support.updateNodes(updateBuilder, nodes, updateNodes); DAG newRoot = builder.buildRoot(); assertEquals(130, newRoot.getTotalChildCount());
public Node putNode(QuadTreeClusteringStrategy quad, Quadrant... location) { Preconditions.checkNotNull(location); long fnumb = quad.root == null ? 0 : quad.root.getTotalChildCount(); String quadInfo = Arrays.toString(location); Node n = createNode("node # " + fnumb + ", at " + quadInfo, location); quad.put(n); return n; }
/** * Override to collapse root DAG's that are one single bucket to the first DAG that has more * than one bucket */ @Override public DAG buildRoot() { if (ENABLE_EXPAND_COLLAPSE) { final long size = root.getTotalChildCount(); collapse(root); final long resultSize = root.getTotalChildCount(); checkState(size == resultSize, "expected size of %s, but got %s after collapse()", size, resultSize); } return root; }
@Test public void testInitOriginalSinglePointFeature() { QuadTreeClusteringStrategy orig = support.newStrategy(); Node node = support.createNode("1", new Envelope(1, 1, 1, 1)); orig.put(node); RevTree quadTree = DAGTreeBuilder.build(orig, support.store()); assertEquals(1, quadTree.size()); QuadTreeClusteringStrategy update = support.newStrategy(quadTree); Node node2 = support.createNode("2", new Envelope(2, 2, 2, 2)); update.remove(node); update.put(node2); RevTree quadTree2 = DAGTreeBuilder.build(update, support.store()); assertEquals(1, quadTree2.size()); List<Node> lnodes = findNode("2", quadTree2, support.store()); assertEquals(1, lnodes.size()); assertEquals(node2, lnodes.get(0)); }
/** * Parent becomes child */ private void setParent(DAG child, DAG parent) { // find deepest 1-bucket child while (child.numBuckets() == 1) { child = getOrCreateDAG(child.bucketList().get(0)); } // replace contents of parent with the contents of child parent.init(child); if (LOG.isDebugEnabled()) { LOG.debug("{} collapsed to {}", child.getId(), parent.getId()); } // System.err.println(child.getId() + " collapsed to " + parent.getId()); List<TreeId> buckets = parent.bucketList(); parent.clearBuckets(); for (TreeId id : buckets) { DAG deepChild = getOrCreateDAG(id); TreeId newChildId = parent.getId().newChild(id.leafBucket()); DAG newChild = getOrCreateDAG(newChildId); parent.addBucket(newChildId); setParent(deepChild, newChild); } }
assertEquals(1 + quadStrategy.normalizedSizeLimit(), quadStrategy.root.getTotalChildCount()); assertEquals(0, quadStrategy.root.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount()); assertEquals(1, dag.numBuckets()); assertEquals(0, dag.numChildren()); assertEquals(1 + quadStrategy.normalizedSizeLimit(), dag.getTotalChildCount());
/** * @see #normalizedSizeLimit() */ @Override public int normalizedSizeLimit(final int depthIndex) { return normalizedSizeLimit(); }