All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.uci.ics.jung.visualization.spatial.rtree.InnerNode Maven / Gradle / Ivy

package edu.uci.ics.jung.visualization.spatial.rtree;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * a non-leaf node of the R-Tree. Contains a list of non leaf or leaf node children
 *
 * @author Tom Nelson
 */
public class InnerNode extends RTreeNode implements Node {

  private static final Logger log = LoggerFactory.getLogger(InnerNode.class);

  private Optional bounds = Optional.empty();

  /** child nodes of this InnerNode */
  private List> children;

  /** true if the child nodes are LeafNodes. falise otherwise */
  private final boolean leafChildren;

  /**
   * create a new InnerNode with one child
   *
   * @param node the first child for the created Node
   * @param  the type of the node and children
   * @return the newly created InnerNode
   */
  public static  InnerNode create(Node node) {
    return new InnerNode(node);
  }

  /**
   * create a new InnerNode with one child
   *
   * @param node the first child of the created node
   * @param  the type of the Node
   * @return the newly created InnerNode
   */
  public static  InnerNode create(InnerNode node) {
    return new InnerNode(node);
  }

  /**
   * create a new InnerNode with the passed nodes as children
   *
   * @param nodes the children for the new InnerNode
   * @param  the type of the Node
   * @return the newly created InnerNode
   */
  public static  InnerNode create(Collection> nodes) {
    return new InnerNode(nodes);
  }

  /**
   * create an InnerNode with the passed Node as the first child
   *
   * @param node the first child for the InnerNode
   */
  InnerNode(Node node) {
    node.setParent(this);
    updateBounds(node.getBounds());
    leafChildren = node instanceof LeafNode;
    children = Lists.newArrayList(node);
  }

  /**
   * create an InnerNOde with the passed nodes as children
   *
   * @param nodes the children for the new InnerNode
   */
  InnerNode(Collection> nodes) {
    children = Lists.newArrayList();
    Node sample = null;
    for (Node node : nodes) {
      sample = node;
      node.setParent(this);
      updateBounds(node.getBounds());
      children.add(node);
    }
    leafChildren = sample instanceof LeafNode; // ugh
  }

  /**
   * true if the children are LeafNodes
   *
   * @return
   */
  @Override
  public boolean isLeafChildren() {
    return leafChildren;
  }

  /**
   * return the ith child node
   *
   * @param i the index of the child to return
   * @return the ith child
   */
  public Node get(int i) {
    return children.get(i);
  }

  /** @return an immutable collection of the child nodes */
  public List> getChildren() {
    return Collections.unmodifiableList(children);
  }

  /**
   * @return the bounding box of this InnerNode. A zero sized Rectangle is returned if this
   *     InnerNode is empty
   */
  @Override
  public Rectangle2D getBounds() {
    return bounds.orElse(new Rectangle2D.Double());
  }

  /**
   * recompute the bounding box for this InnerNode, then the recompute for parent node Climbs the
   * tree to the root as it recalcultes. This i required when a leaf node is removed.
   *
   * @return
   */
  @Override
  public Node recalculateBounds() {
    bounds = Optional.empty();
    int size = children.size();
    for (int i = 0; i < size; i++) {
      updateBounds(children.get(i).getBounds());
    }
    if (parent.isPresent()) {
      return parent.get().recalculateBounds();
    }
    return this;
  }

  /**
   * @param p the point to search
   * @return the element in the Leaf node that is contained by p
   */
  @Override
  public T getPickedObject(Point2D p) {
    T picked = null;
    if (getBounds().contains(p)) {
      int size = children.size();
      for (int i = 0; i < size; i++) {
        return children.get(i).getPickedObject(p);
      }
    }
    return picked;
  }

  /** @return the number of child nodes */
  @Override
  public int size() {
    return children.size();
  }

  private Node findElement(T o) {
    Node found = null;
    int size = children.size();
    for (int i = 0; i < size; i++) {
      Node kid = children.get(i);
      if (kid instanceof LeafNode) {
        return kid;
      } else {
        found = ((InnerNode) kid).findElement(o);
      }
    }
    return found;
  }

  /**
   * @param element the element to look for
   * @return the LeafNode that contains the element
   */
  @Override
  public LeafNode getContainingLeaf(T element) {
    LeafNode containingLeaf = null;
    int size = children.size();
    for (int i = 0; i < size; i++) {
      containingLeaf = children.get(i).getContainingLeaf(element);
      if (containingLeaf != null) {
        break;
      }
    }
    return containingLeaf;
  }

  /**
   * @param element the element to look for
   * @return the LeafNode that contains the element
   */
  LeafNode getContainingLeaf(T element, Rectangle2D bounds) {
    LeafNode containingLeaf = null;
    int size = children.size();
    for (int i = 0; i < size; i++) {
      Node node = children.get(i);
      if (node.getBounds().intersects(bounds)) {
        containingLeaf = node.getContainingLeaf(element);
        if (containingLeaf != null) {
          break;
        }
      }
    }
    return containingLeaf;
  }

  /**
   * @param p the point to look for
   * @return Collection of the LeafNodes that would contain the passed point
   */
  @Override
  public Set> getContainingLeafs(Set> containingLeafs, Point2D p) {
    return getContainingLeafs(containingLeafs, p.getX(), p.getY());
  }

  /**
   * @param x coordinate of a point to look for
   * @param y coordinate of a point to look for
   * @return Collection of the LeafNodes that would contain the passed coordinates
   */
  @Override
  public Set> getContainingLeafs(Set> containingLeafs, double x, double y) {
    if (getBounds().contains(x, y)) {
      int size = children.size();
      for (int i = 0; i < size; i++) {
        Node node = children.get(i);
        node.getContainingLeafs(containingLeafs, x, y);
      }
    }
    return containingLeafs;
  }

  /**
   * gather the RTree Node rectangles into a Collection
   *
   * @param list
   * @return
   */
  public Collection collectGrids(Collection list) {
    list.add(getBounds());
    int size = children.size();
    for (int i = 0; i < size; i++) {
      children.get(i).collectGrids(list);
    }
    log.trace(
        "in nonleaf {}, added {} so list size now {}",
        this.hashCode(),
        children.size(),
        list.size());
    return list;
  }

  /**
   * add Nodes directly to the children list
   *
   * @param collection
   */
  private void add(Collection> collection) {
    children.addAll(collection);
  }

  private void updateBounds(Rectangle2D r) {
    if (bounds.isPresent()) {
      bounds = Optional.of(bounds.get().createUnion(r));
    } else {
      bounds = Optional.of(r);
    }
    Rectangle2D b = bounds.get();
  }

  /**
   * @param splitterContext rules for splitting nodes
   * @param element the element to add
   * @param bounds the bounds of the element to add
   * @return the returned node or its parent
   */
  @Override
  public Node add(SplitterContext splitterContext, T element, Rectangle2D bounds) {
    // update bounds with the new element's bounds
    updateBounds(bounds);
    Optional> pathToFollow = splitterContext.splitter.chooseSubtree(this, element, bounds);
    if (pathToFollow.isPresent()) {
      Node node = pathToFollow.get().add(splitterContext, element, bounds);
      return node.getParent().orElse(node);
    }
    return null;
  }

  /**
   * remove the passed element. Find the LeafNode that contains the element, remove the element from
   * the LeafNode map
   *
   * @param element the element to remove
   * @return the parent node or this node
   */
  @Override
  public Node remove(T element) {
    LeafNode containingLeaf = getContainingLeaf(element);
    if (containingLeaf == null) {
      log.warn("{} is not in the tree! ", element);
      return this;
    }
    return containingLeaf.remove(element);
  }

  /**
   * diectly add a child node to this node.
   *
   * @param node
   */
  void addNode(Node node) {
    Preconditions.checkArgument(node != this, "Attempt to add self as child");
    Preconditions.checkArgument(!children.contains(node), "Attempt to add duplicate child");
    node.setParent(this);
    updateBounds(node.getBounds());
    children.add(node);
  }

  /**
   * directly remove a shild node from this node
   *
   * @param node
   */
  void removeNode(Node node) {
    children.remove(node);
  }

  InnerNode add(SplitterContext splitterContext, Node... nodes) {
    InnerNode top = this;
    for (Node node : nodes) {
      top = add(splitterContext, node);
    }
    if (top.getParent().isPresent()) {
      return (InnerNode) top.getParent().get();
    }
    return top;
  }
  /**
   * adding either a LeafNode or an InnerNode
   *
   * @param node
   * @return the parent, if exists, or this
   */
  private InnerNode add(SplitterContext splitterContext, Node node) {
    Preconditions.checkArgument(node != this, "Attempt to add self as child");

    updateBounds(node.getBounds());

    if (size() > M) {
      log.trace("splitting InnerNode {}", this);
      Pair> pair = splitterContext.splitter.split(children, node);

      if (parent.isPresent()) {
        InnerNode innerNodeParent = (InnerNode) parent.get();
        // sanity check
        Preconditions.checkArgument(
            this != pair.left && this != pair.right,
            "Pair left {} or right {} the same as this {}",
            pair.left,
            pair.right,
            this);
        innerNodeParent.removeNode(this);
        return innerNodeParent.add(splitterContext, pair.left, pair.right);
      } else {
        // create a new parent
        InnerNode innerNodeParent = InnerNode.create(pair.left);
        return innerNodeParent.add(splitterContext, pair.right);
      }

    } else {
      // no split required
      addNode(node);
      return (InnerNode) parent.orElse(this);
    }
  }

  /**
   * @param shape the shape to filter the visible elements
   * @return a collection of all elements that intersect with the passed shape
   */
  @Override
  public Set getVisibleElements(Set visibleElements, Shape shape) {
    if (shape.intersects(getBounds())) {
      int size = children.size();
      for (int i = 0; i < size; i++) {
        children.get(i).getVisibleElements(visibleElements, shape);
      }
    }
    log.trace("visibleElements of InnerNode inside {} are {}", shape, visibleElements);
    return visibleElements;
  }

  /**
   * descend into the tree and count all children
   *
   * @return
   */
  public int count() {
    int count = 0;
    int size = children.size();
    for (int i = 0; i < size; i++) {
      count += children.get(i).count();
    }
    return count;
  }
  // to string methods:

  private String asString() {
    return asString("");
  }

  @Override
  public String toString() {
    return this.asString();
  }

  public String asString(String margin) {
    StringBuilder s = new StringBuilder();
    s.append(margin);
    s.append("InnerNode:parent:").append(parent.isPresent() ? "yes" : "none");
    s.append(" bounds=");
    s.append(Node.asString(this.getBounds()));
    s.append('\n');
    for (Node child : this.children) {
      s.append(child.asString(margin + marginIncrement));
    }
    return s.toString();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy