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

edu.ucr.cs.bdlab.beast.indexing.CellPartitioner Maven / Gradle / Ivy

There is a newer version: 0.10.1-RC2
Show newest version
/*
 * Copyright 2018 University of California, Riverside
 *
 * 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 edu.ucr.cs.bdlab.beast.indexing;

import edu.ucr.cs.bdlab.beast.geolite.EnvelopeNDLite;
import edu.ucr.cs.bdlab.beast.geolite.GeometryHelper;
import edu.ucr.cs.bdlab.beast.synopses.AbstractHistogram;
import edu.ucr.cs.bdlab.beast.synopses.Summary;
import edu.ucr.cs.bdlab.beast.util.IntArray;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

/**
 * A partitioner that partitions a file according to an existing set of cells (MBBs). It internally builds an in-memory
 * {@link RRStarTree} for the partitions to speed up the search.
 * @author Ahmed Eldawy
 *
 */
@SpatialPartitioner.Metadata(
    disjointSupported = false,
    description = "Partitions the space based on existing set of cells, e.g., another file",
    extension = "cells"
)
public class CellPartitioner implements SpatialPartitioner {

  /**An R-tree that indexes the set of existing partition for efficient search*/
  protected RTreeGuttman partitions;

  /**The list of cells that this partitioner can choose from*/
  protected PartitionInfo[] cells;

  /**The degree of the R-Tree index that we use to speed up node lookup.*/
  protected static final int RTreeDegree = 32;

  /**The MBR of the input space*/
  protected final EnvelopeNDLite envelope = new EnvelopeNDLite(2);

  /**A cached value of whether this partitioner is disjoint or not. {@code null} means not set.*/
  protected Boolean disjoint;

  /**
   * A default constructor to be able to dynamically instantiate it
   * and deserialize it
   */
  public CellPartitioner() {
  }

  public CellPartitioner(PartitionInfo ... cells) {
    this.cells = new PartitionInfo[cells.length];
    // We have to explicitly create each object as PartitionInfo to ensure that write/readFields will work correctly
    for (int i = 0; i < cells.length; i++) {
      this.cells[i] = new PartitionInfo(cells[i]);
      this.cells[i].partitionId = i;
    }
    initialize(cells);
  }

  @Override
  public void construct(Summary summary, double[][] sample, AbstractHistogram histogram, int numPartitions) {
    throw new RuntimeException("Not supported! Please use the method initialize(PartitionInfo[]) to create.");
  }

  /**
   * Initialize the R-tree from the list of cells.
   * @param cells
   */
  private void initialize(EnvelopeNDLite[] cells) {
    double[] x1s = new double[cells.length];
    double[] y1s = new double[cells.length];
    double[] x2s = new double[cells.length];
    double[] y2s = new double[cells.length];

    envelope.setEmpty();

    for (int i = 0; i < cells.length; i++) {
      x1s[i] = cells[i].getMinCoord(0);
      y1s[i] = cells[i].getMinCoord(1);
      x2s[i] = cells[i].getMaxCoord(0);
      y2s[i] = cells[i].getMaxCoord(1);
      envelope.setMinCoord(0, Math.min(envelope.getMinCoord(0), x1s[i]));
      envelope.setMinCoord(1, Math.min(envelope.getMinCoord(1), y1s[i]));
      envelope.setMaxCoord(0, Math.min(envelope.getMinCoord(0), x2s[i]));
      envelope.setMaxCoord(1, Math.min(envelope.getMinCoord(1), y2s[i]));
    }

    // The RR*-tree paper recommends setting m = 0.2 M
    partitions = new RRStarTree(RTreeDegree / 5, RTreeDegree);
    partitions.initializeHollowRTree(x1s, y1s, x2s, y2s);
    disjoint = null;
  }

  @Override
  public void writeExternal(ObjectOutput out) throws IOException {
    GeometryHelper.writeIEnvelope(this.envelope, out);
    out.writeInt(cells.length);
    for (PartitionInfo cell : cells)
      cell.writeExternal(out);
  }

  @Override
  public void readExternal(ObjectInput in) throws IOException {
    GeometryHelper.readIEnvelope(this.envelope, in);
    int numCells = in.readInt();
    if (cells == null || cells.length != numCells) {
      // Initialize the array of cells only if needed
      cells = new PartitionInfo[numCells];
      for (int i = 0; i < numCells; i++)
        cells[i] = new PartitionInfo();
    }
    for (int i = 0; i < numCells; i++)
      cells[i].readExternal(in);

    // Re-initialize the R-tree
    initialize(cells);
  }
  
  @Override
  public int getPartitionCount() {
    return cells == null ? 0 : cells.length;
  }

  @Override
  public void overlapPartitions(EnvelopeNDLite mbr, IntArray matchedPartitions) {
    matchedPartitions.clear();
    for (RTreeGuttman.Entry e : partitions.search(mbr))
      matchedPartitions.add(e.id);
  }

  @Override
  public int overlapPartition(EnvelopeNDLite geomMBR) {
    // TODO avoid construction of the IntArray multiple times
    IntArray tempPartitions = new IntArray();
    double[] mbrMin = new double[geomMBR.getCoordinateDimension()];
    double[] mbrMax = new double[geomMBR.getCoordinateDimension()];
    for (int $d = 0; $d < geomMBR.getCoordinateDimension(); $d++) {
      mbrMin[$d] = geomMBR.getMinCoord($d);
      mbrMax[$d] = geomMBR.getMaxCoord($d);
    }
    partitions.search(mbrMin, mbrMax, tempPartitions);
    int chosenCellIndex;
    if (tempPartitions.size() == 1) {
      // Only one overlapping node, return it
      chosenCellIndex = tempPartitions.peek();
    } else if (tempPartitions.size() > 0) {
      // More than one overlapping cells, choose the best between them
      chosenCellIndex = -1;
      double minVol = Double.POSITIVE_INFINITY;
      double minPerim = Double.POSITIVE_INFINITY;
      for (int overlappingCellIndex : tempPartitions) {
        EnvelopeNDLite overlappingCell = cells[overlappingCellIndex];
        double vol = overlappingCell.getArea();
        if (vol < minVol) {
          minVol = vol;
          minPerim = overlappingCell.getSideLength(0) + overlappingCell.getSideLength(1);
          chosenCellIndex = overlappingCellIndex;
        } else if (vol == minVol) {
          // This also covers the case of vol == minVol == 0
          double cellPerimeter = overlappingCell.getSideLength(0) + overlappingCell.getSideLength(1);
          if (cellPerimeter < minPerim) {
            minPerim = cellPerimeter;
            chosenCellIndex = overlappingCellIndex;
          }
        }
      }
    } else {
      // No overlapping cells, follow the (fake) insert choice
      chosenCellIndex = partitions.noInsert(new double[] {geomMBR.getMinCoord(0), geomMBR.getMinCoord(1)},
          new double[] {geomMBR.getMaxCoord(0), geomMBR.getMaxCoord(1)});
    }
    return cells[chosenCellIndex].partitionId;
  }

  @Override
  public void getPartitionMBR(int partitionID, EnvelopeNDLite mbr) {
    mbr.set(cells[partitionID]);
  }

  @Override
  public boolean isDisjoint() {
    if (disjoint == null) {
      // Make a self join between the cells and return true if the result is empty
      for (int i = 0; i < cells.length && disjoint == null; i++) {
        for (int j = i + 1; j < cells.length && disjoint == null; j++) {
          if (cells[i].intersectsEnvelope(cells[j]))
            disjoint = false;
        }
      }
      if (disjoint == null)
        disjoint = true;
    }
    return disjoint;
  }

  @Override
  public int getCoordinateDimension() {
    return cells[0].getCoordinateDimension();
  }

  @Override
  public EnvelopeNDLite getEnvelope() {
    return envelope;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy