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

edu.uci.ics.jung.visualization.spatial.rtree.QuadraticLeafSplitter 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.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * splits the passed entries using the quadratic method (for R-Tree)
 *
 * @param 
 */
public class QuadraticLeafSplitter implements LeafSplitter {

  @Override
  public Pair> split(
      Collection> entries, Map.Entry newEntry) {
    return quadraticSplit(entries, newEntry);
  }

  private Optional> pickNext(
      List> entries, Pair> pickedSeeds) {
    double maxDifference = 0;
    Optional> winner = Optional.empty();
    entries.removeAll(pickedSeeds.left.map.entrySet());
    entries.removeAll(pickedSeeds.right.map.entrySet());
    // for each entry
    for (Map.Entry entry : entries) {
      // ... that is not already in the leaf node....
      if (!pickedSeeds.left.map.containsKey(entry.getKey())
          && !pickedSeeds.right.map.containsKey(entry.getKey())) {
        // calculate area increase that would happen
        LeafNode leftNode = pickedSeeds.left;
        LeafNode rightNode = pickedSeeds.right;
        double leftArea = area(leftNode.getBounds());
        double rightArea = area(rightNode.getBounds());
        Rectangle2D leftUnion = leftNode.getBounds().createUnion(entry.getValue());
        Rectangle2D rightUnion = rightNode.getBounds().createUnion(entry.getValue());
        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;
  }

  /**
   * from the list of entries, return the pair that represent the largest increase in area
   *
   * @param entryList
   * @return
   */
  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.getValue().createUnion(entryPair.right.getValue());
        double area =
            Node.area(union)
                - Node.area(entryPair.left.getValue())
                - Node.area(entryPair.right.getValue());
        if (!winningPair.isPresent()) {
          winningPair = Optional.of(entryPair);
          largestArea = area;
        } else if (area > largestArea) {
          winningPair = Optional.of(entryPair);
        }
      }
    }
    Preconditions.checkArgument(winningPair.isPresent(), "Winning pair not found");
    Map.Entry leftEntry = winningPair.get().left;
    LeafNode leftNode = LeafNode.create(leftEntry);
    Map.Entry rightEntry = winningPair.get().right;
    LeafNode rightNode = LeafNode.create(rightEntry);

    return Pair.of(leftNode, rightNode);
  }

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

    Optional> nextOptional = pickNext(entries, pickedSeeds);
    if (nextOptional.isPresent()) {
      Map.Entry 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 = Node.area(leftBounds);
      double rightArea = Node.area(rightBounds);
      double leftEnlargement = Node.area(leftBounds.createUnion(next.getValue())) - leftArea;
      double rightEnlargement = Node.area(rightBounds.createUnion(next.getValue())) - 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.map.put(next.getKey(), next.getValue());
          } else {
            pickedSeeds.right.map.put(next.getKey(), next.getValue());
          }
        } else if (leftArea < rightArea) {
          pickedSeeds.left.map.put(next.getKey(), next.getValue());
        } else {
          pickedSeeds.right.map.put(next.getKey(), next.getValue());
        }
      } else if (leftEnlargement < rightEnlargement) {
        pickedSeeds.left.map.put(next.getKey(), next.getValue());
      } else {
        pickedSeeds.right.map.put(next.getKey(), next.getValue());
      }
    }
  }

  /**
   * combine the existing map elements with the new one and make a pair of leaf nodes to distrubute
   * the entries into
   *
   * @param entries Collection of entries to split
   * @param newEntry
   * @return
   */
  private Pair> quadraticSplit(
      Collection> entries, Map.Entry 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(entries);
    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 (Map.Entry entry : entryList) {
          pickedSeeds.right.map.put(entry.getKey(), entry.getValue());
        }
      } else {
        // right side too big, give them to the left side
        for (Map.Entry entry : entryList) {
          pickedSeeds.left.map.put(entry.getKey(), entry.getValue());
        }
      }
    }
    return pickedSeeds;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy