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

com.metamx.collections.spatial.split.GutmanSplitStrategy Maven / Gradle / Ivy

/*
 * Copyright 2011 - 2015 Metamarkets Group Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.metamx.collections.spatial.split;

import com.google.common.collect.Lists;
import com.metamx.collections.bitmap.BitmapFactory;
import com.metamx.collections.spatial.Node;
import com.metamx.collections.spatial.RTreeUtils;

import java.util.Arrays;
import java.util.List;

/**
 */
public abstract class GutmanSplitStrategy implements SplitStrategy
{
  private final int minNumChildren;
  private final int maxNumChildren;
  private final BitmapFactory bf;

  protected GutmanSplitStrategy(int minNumChildren, int maxNumChildren, BitmapFactory b)
  {
    this.minNumChildren = minNumChildren;
    this.maxNumChildren = maxNumChildren;
    this.bf = b;
  }

  @Override
  public boolean needToSplit(Node node)
  {
    return (node.getChildren().size() > maxNumChildren);
  }

  /**
   * This algorithm is from the original paper.
   *
   * Algorithm Split. Divide a set of M+1 index entries into two groups.
   *
   * S1. [Pick first entry for each group]. Apply Algorithm {@link #pickSeeds(java.util.List)} to choose
   * two entries to be the first elements of the groups. Assign each to a group.
   *
   * S2. [Check if done]. If all entries have been assigned, stop. If one group has so few entries that all the rest
   * must be assigned to it in order for it to have the minimum number m, assign them and stop.
   *
   * S3. [Select entry to assign]. Invoke Algorithm {@link #pickNext(java.util.List, com.metamx.collections.spatial.Node[])}
   * to choose the next entry to assign. Add it to the group whose covering rectangle will have to be enlarged least to
   * accommodate it. Resolve ties by adding the entry to the group smaller area, then to the one with fewer entries, then
   * to either. Repeat from S2.
   */
  @Override
  public Node[] split(Node node)
  {
    List children = Lists.newArrayList(node.getChildren());
    Node[] seeds = pickSeeds(children);

    node.clear();
    node.addChild(seeds[0]);
    node.addToBitmapIndex(seeds[0]);

    Node group1 = new Node(
        Arrays.copyOf(seeds[1].getMinCoordinates(), seeds[1].getMinCoordinates().length),
        Arrays.copyOf(seeds[1].getMaxCoordinates(), seeds[1].getMaxCoordinates().length),
        Lists.newArrayList(seeds[1]),
        node.isLeaf(),
        node.getParent(),
        bf.makeEmptyMutableBitmap()
    );
    group1.addToBitmapIndex(seeds[1]);
    if (node.getParent() != null) {
      node.getParent().addChild(group1);
    }
    Node[] groups = new Node[]{
        node, group1
    };

    RTreeUtils.enclose(groups);

    while (!children.isEmpty()) {
      for (Node group : groups) {
        if (group.getChildren().size() + children.size() <= minNumChildren) {
          for (Node child : children) {
            group.addToBitmapIndex(child);
            group.addChild(child);
          }
          RTreeUtils.enclose(groups);
          return groups;
        }
      }

      Node nextToAssign = pickNext(children, groups);
      double group0ExpandedArea = RTreeUtils.getEnclosingArea(groups[0], nextToAssign);
      double group1ExpandedArea = RTreeUtils.getEnclosingArea(groups[1], nextToAssign);

      Node optimal;
      if (group0ExpandedArea < group1ExpandedArea) {
        optimal = groups[0];
      } else if (group0ExpandedArea == group1ExpandedArea) {
        if (groups[0].getArea() < groups[1].getArea()) {
          optimal = groups[0];
        } else {
          optimal = groups[1];
        }
      } else {
        optimal = groups[1];
      }

      optimal.addToBitmapIndex(nextToAssign);
      optimal.addChild(nextToAssign);
      optimal.enclose();
    }

    return groups;
  }

  public abstract Node[] pickSeeds(List nodes);

  public abstract Node pickNext(List nodes, Node[] groups);
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy