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

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

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

import static edu.uci.ics.jung.visualization.spatial.rtree.Node.M;
import static edu.uci.ics.jung.visualization.spatial.rtree.Node.area;
import static edu.uci.ics.jung.visualization.spatial.rtree.Node.m;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import java.awt.geom.Rectangle2D;
import java.util.List;
import java.util.Optional;

public class QuadraticSplitter extends AbstractSplitter implements Splitter {

  public Pair> split(List> children, Node newEntry) {
    return quadraticSplit(children, newEntry);
  }

  private Pair> quadraticSplit(List> children, Node newEntry) {
    // make a collection of kids from leafNode that also include the new element
    // items will be removed from the entryList as they are distributed
    List> entryList = Lists.newArrayList(children);
    entryList.add(newEntry);
    // get the best pair to split on trom the leafNode elements
    Pair> pickedSeeds = pickSeeds(entryList);
    // these currently have no parent set....
    while (entryList.size() > 0
        && pickedSeeds.left.size() < M - m + 1
        && pickedSeeds.right.size() < M - m + 1) {
      distributeEntry(entryList, pickedSeeds);
    }
    if (entryList.size() > 0) {
      // take care of entries that were not distributed
      if (pickedSeeds.left.size() >= M - m + 1) {
        // left side too big, give them to the right side
        for (Node entry : entryList) {
          pickedSeeds.right.addNode(entry);
        }
      } else {
        // right side too big, give them to the left side
        for (Node entry : entryList) {
          pickedSeeds.left.addNode(entry);
        }
      }
    }
    return pickedSeeds;
  }

  private void distributeEntry(List> entries, Pair> pickedSeeds) {

    Optional> nextOptional = pickNext(entries, pickedSeeds);
    if (nextOptional.isPresent()) {
      Node next = nextOptional.get();
      // which of the picked seeds should it be added to?
      Rectangle2D leftBounds = pickedSeeds.left.getBounds();
      Rectangle2D rightBounds = pickedSeeds.right.getBounds();
      // which rectangle is enlarged the least?
      double leftArea = area(leftBounds);
      double rightArea = area(rightBounds);
      double leftEnlargement = area(leftBounds.createUnion(next.getBounds())) - leftArea;
      double rightEnlargement = area(rightBounds.createUnion(next.getBounds())) - rightArea;
      if (leftEnlargement == rightEnlargement) {
        // a tie. consider the smaller area
        if (leftArea == rightArea) {
          // another tie. consider the one with the fewest kids
          int leftKids = pickedSeeds.left.size();
          int rightKids = pickedSeeds.right.size();
          if (leftKids < rightKids) {
            pickedSeeds.left.addNode(next);
          } else {
            pickedSeeds.right.addNode(next);
          }
        } else if (leftArea < rightArea) {
          pickedSeeds.left.addNode(next);
        } else {
          pickedSeeds.right.addNode(next);
        }
      } else if (leftEnlargement < rightEnlargement) {
        pickedSeeds.left.addNode(next);
      } else {
        pickedSeeds.right.addNode(next);
      }
    }
  }

  private Pair> pickSeeds(List> entryList) {
    double largestArea = 0;
    Optional>> winningPair = Optional.empty();
    for (int i = 0; i < entryList.size(); i++) {
      for (int j = i + 1; j < entryList.size(); j++) {
        Pair> entryPair = new Pair<>(entryList.get(i), entryList.get(j));
        Rectangle2D union = entryPair.left.getBounds().createUnion(entryPair.right.getBounds());
        double area =
            area(union) - area(entryPair.left.getBounds()) - area(entryPair.right.getBounds());
        if (!winningPair.isPresent()) {
          winningPair = Optional.of(entryPair);
          largestArea = area;
        } else if (area > largestArea) {
          winningPair = Optional.of(entryPair);
        }
      }
    }
    Preconditions.checkArgument(winningPair.isPresent(), "No winning pair returned");
    Node leftEntry = winningPair.get().left;
    InnerNode leftNode = InnerNode.create(leftEntry);
    Node rightEntry = winningPair.get().right;
    InnerNode rightNode = InnerNode.create(rightEntry);
    entryList.remove(leftEntry);
    entryList.remove(rightEntry);
    return new Pair<>(leftNode, rightNode);
  }

  private Optional> pickNext(List> entries, Pair> pickedSeeds) {
    double maxDifference = 0;
    Optional> winner = Optional.empty();
    entries.removeAll(pickedSeeds.left.getChildren());
    entries.removeAll(pickedSeeds.right.getChildren());
    // for each entry
    for (Node entry : entries) {
      // ... that is not already in the leaf node....
      if (!pickedSeeds.left.getChildren().contains(entry)
          && !pickedSeeds.right.getChildren().contains(entry)) {
        // calculate area increase that would happen
        InnerNode leftNode = pickedSeeds.left;
        InnerNode rightNode = pickedSeeds.right;
        double leftArea = area(leftNode.getBounds());
        double rightArea = area(rightNode.getBounds());
        Rectangle2D leftUnion = leftNode.getBounds().createUnion(entry.getBounds());
        Rectangle2D rightUnion = rightNode.getBounds().createUnion(entry.getBounds());
        double leftAreaIncrease = area(leftUnion) - leftArea;
        double rightAreaIncrease = area(rightUnion) - rightArea;
        double difference = leftAreaIncrease - rightAreaIncrease;
        // make sure it is positive
        difference = difference < 0 ? -difference : difference;
        if (!winner.isPresent()) {
          winner = Optional.of(entry);
          maxDifference = difference;
        } else if (difference > maxDifference) {
          maxDifference = difference;
          winner = Optional.of(entry);
        }
      }
    }
    winner.ifPresent(entries::remove);
    return winner;
  }

  public Optional> chooseSubtree(InnerNode nodeToSplit, T element, Rectangle2D bounds) {
    return leastEnlargementThenAreaThenKids(nodeToSplit, bounds);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy