/** Returns the cost of children only, not including the {@linkplain #tile} in this node. */ private long childrenCost() { long c = 0; SelectedNode child = (SelectedNode) firstChildren(); while (child != null) { c += child.cost; child = (SelectedNode) child.nextSibling(); } assert cost >= c; return c; }
/** Removes all children. */ @Override public void removeChildren() { final long removed = childrenCost(); if (removed != 0) { SelectedNode parent = this; do { parent.cost -= removed; } while ((parent = (SelectedNode) parent.getParent()) != null); } super.removeChildren(); }
/** * Removes all children from the given map. Note that we do not removes this node directly because * the corresponding Map.Entry is presumed already used by an other node. */ private void removeFrom(final Map<Rectangle, SelectedNode> overlaps) { SelectedNode child = (SelectedNode) firstChildren(); while (child != null) { child.removeFrom(overlaps); final SelectedNode existing = overlaps.remove(child); if (existing != null && existing != child) { overlaps.put(existing, existing); } child = (SelectedNode) child.nextSibling(); } }
/** * Adds the given tile as a child of this tile. * * @throws ClassCastException if the given child is not an instance of {@code SelectedNode}. This * is an intentional restriction in order to avoid more subtile bugs later. */ @Override public void addChild(final TreeNode child) throws ClassCastException { super.addChild(child); if (child != null) { final long added = ((SelectedNode) child).cost; if (added != 0) { SelectedNode parent = this; do { parent.cost += added; } while ((parent = (SelectedNode) parent.getParent()) != null); } } }
/** Returns {@code true} if this node has a lower cost than the specified one. */ public final boolean isCheaperThan(final SelectedNode other) { if (cost < other.cost) { return true; } if (cost == other.cost) { return getTileCount() < other.getTileCount(); } return false; }
/** * Removes the nodes having the same bounding box than this tile, then process recursively for * children. If such matchs are found, they are probably tiles at a different resolution. Retains * the one which minimize the disk reading, and discards the other ones. * * <p>This check is not generic since we search for an exact match, but this case is common * enough. Handling it with a {@link java.util.HashMap} will help to reduce the amount of tiles to * handle in a more costly way later. * * <p>As a side effect, this method trims the bounding box of selected nodes to the tiles that * they contain. * * @param overlaps An initially empty map. Will be filled through recursive invocation of this * method while we iterate down the tree. */ final void removeTrivialOverlaps(final Map<Rectangle, SelectedNode> overlaps) { /* * Must process children first because if any of them are removed, it will lower * the cost and consequently can change the decision taken at the end of this method. */ SelectedNode child = (SelectedNode) firstChildren(); while (child != null) { // Must ask for next sibling before to filter since the later // may set it to null if the child is removed from the tree. final SelectedNode next = (SelectedNode) child.nextSibling(); child.removeTrivialOverlaps(overlaps); child = next; } /* * If this node is just a container for other nodes, trims the bounding box to the tiles * that it contains. We would not need to do that if the size of parent nodes was always * a multiple of child nodes. But sometime they are not, in which case some child nodes * may live on the boundary between two parent nodes. It is not easy to predict in which * parent such child will end up and what will be its bounding box since the computation * involves intersection, but the end result is that two parents may have identical bbox * while their child do not overlaps at all. * * We don't perform this computation if this node contains a tile, because in such case * we assume that the bounding box was carefully choosen by the user. This is different * than a null tile in which case the bounding box was calculated by our code. */ if (tile == null) { child = (SelectedNode) firstChildren(); if (child != null) { int xmin = x, ymin = y, w = width, h = height; width = height = -1; do { assert !child.isEmpty() : child; add(child); child = (SelectedNode) child.nextSibling(); } while (child != null); if (x < xmin) { w -= (xmin - x); x = xmin; } if (y < ymin) { h -= (ymin - y); y = ymin; } if (width > w) width = w; if (height > h) height = h; } } /* * Now searchs for overlaps. */ SelectedNode existing = overlaps.put(this, this); if (existing != null && existing != this) { if (!isCheaperThan(existing)) { /* * A cheaper tiles existed for the same bounds. Reinsert the previous tile in the * map. We will delete this node from the tree later, except if the previous node * is a children of this node. In the later case, we can't remove completly this * node since it would remove its children as well, so we just nullify the tile. */ if (existing.getParent() == this) { if (tile != null) { tile = null; cost = childrenCost(); } return; } overlaps.put(existing, existing); existing = this; } existing.remove(); existing.removeFrom(overlaps); assert overlaps.get(existing) != existing; } }