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

org.bboxdb.storage.entity.CellGrid Maven / Gradle / Ivy

/*******************************************************************************
 *
 *    Copyright (C) 2015-2018 the BBoxDB project
 *  
 *    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 org.bboxdb.storage.entity;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.bboxdb.commons.math.Hyperrectangle;
import org.bboxdb.commons.math.DoubleInterval;
import org.bboxdb.storage.sstable.spatialindex.SpatialIndexEntry;
import org.bboxdb.storage.sstable.spatialindex.rtree.RTreeBuilder;

import com.google.common.collect.Lists;

public class CellGrid {
	
	/**
	 * The covering box
	 */
	protected Hyperrectangle coveringBox;
	
	/**
	 * All boxes of this grid
	 */
	protected final Set allBoxes;
	
	/**
	 * The spatial index
	 */
	protected final RTreeBuilder spatialIndexBuilder = new RTreeBuilder();

	/**
	 * Build the grid with fixed cell size
	 * @param coveringBox
	 * @param cellsPerDimension
	 * @return
	 */
	public static CellGrid buildWithFixedCellSize(final Hyperrectangle coveringBox, 
			final double cellSize) {
		
		Objects.requireNonNull(coveringBox);
		Objects.requireNonNull(cellSize);
		
		if(cellSize <= 0) {
			throw new IllegalArgumentException("Cell size has to be > 0");
		}
		
		// Fixed size
		return createCells(coveringBox, (d) -> cellSize);
	}
	
	/**
	 * Build the grid with a fixed amount of cells
	 * @param coveringBox
	 * @param cellsPerDimension
	 * @return
	 */
	public static CellGrid buildWithFixedAmountOfCells(final Hyperrectangle coveringBox, 
			final double cellsPerDimension) {
		
		Objects.requireNonNull(coveringBox);
		Objects.requireNonNull(cellsPerDimension);
		
		if(cellsPerDimension <= 0) {
			throw new IllegalArgumentException("Number of cells has to be > 0");
		}
		
		// Fixed amount of cells
		return createCells(coveringBox, 
				(d) -> coveringBox.getIntervalForDimension(d).getLength() / cellsPerDimension);
	}

	/**
	 * Create the cell intervals for the grid
	 * @param coveringBox
	 * @param cellSizeInDimension
	 * @return
	 */
	private static CellGrid createCells(final Hyperrectangle coveringBox,
			final Function cellSizeInDimension) {
		
		final List> cells = new ArrayList<>();
		
		// Generate all possible interval for each dimension
		for(int dimension = 0; dimension < coveringBox.getDimension(); dimension++) {
			final DoubleInterval baseInterval = coveringBox.getIntervalForDimension(dimension);
			final double cellSize = cellSizeInDimension.apply(dimension);
			final int cellsInDimension = (int) Math.ceil(baseInterval.getLength() / cellSize);

			if(cellsInDimension <= 0) {
				throw new IllegalArgumentException("Cells in dimension " + (dimension + 1) + " has to be > 0");
			}
			
			// List of intervals for this dimension
			final List intervals = new ArrayList<>();
			cells.add(intervals);
			
			for(int offset = 0; offset < cellsInDimension; offset++) {
				final double begin = baseInterval.getBegin() + (offset * cellSize);
				final double end = Math.min(
						baseInterval.getBegin() + ((offset+1) * cellSize),
						baseInterval.getEnd());
				
				// The last cell contains the end point
				final boolean endIncluded = (offset + 1 == cellsInDimension); 
				
				final DoubleInterval interval = new DoubleInterval(begin, end, true, endIncluded);
				intervals.add(interval);
			}
		}
		
		final Set allBoxes = convertListsToBoxes(cells);
		return new CellGrid(coveringBox, allBoxes);
	}

	/**
	 * The private constructor
	 * @param coveringBox
	 * @param allBoxes
	 */
	private CellGrid(final Hyperrectangle coveringBox, final Set allBoxes) {
		this.coveringBox = Objects.requireNonNull(coveringBox);
		this.allBoxes = allBoxes;		

		// Build spatial index
		final List indexEntries = allBoxes
				.stream()
				.map(b -> new SpatialIndexEntry(b, 1))
				.collect(Collectors.toList());
		
		spatialIndexBuilder.bulkInsert(indexEntries);
	}
	
	/**
	 * Get all cell bounding boxes that are covered by this box
	 * @param boundingBox
	 * @return
	 */
	public Set getAllInersectedBoundingBoxes(final Hyperrectangle boundingBox) {
		
		if(coveringBox.getDimension() != boundingBox.getDimension()){
			throw new IllegalArgumentException("Dimension of the cell is: " + coveringBox.getDimension() 
			+ " of the query object " + boundingBox.getDimension());
		}
		
		final List entries = spatialIndexBuilder.getEntriesForRegion(boundingBox);
		
		return entries.stream().map(e -> e.getBoundingBox()).collect(Collectors.toSet());
	}
	
	/**
	 * Convert the lists of intervals to bounding boxes
	 * @param cells
	 * @return 
	 */
	private static Set convertListsToBoxes(final List> cells) {
		final Set allBoxes = new HashSet<>();
		final List> intervallProduct = Lists.cartesianProduct(cells);
		
		for(List intervalls : intervallProduct) {
			final Hyperrectangle boundingBox = new Hyperrectangle(intervalls);
			allBoxes.add(boundingBox);
		}
		
		return allBoxes;
	}
	
	/**
	 * Get all cells of the grid
	 * @return
	 */
	public Set getAllCells() {
		return Collections.unmodifiableSet(allBoxes);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((allBoxes == null) ? 0 : allBoxes.hashCode());
		result = prime * result + ((coveringBox == null) ? 0 : coveringBox.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		CellGrid other = (CellGrid) obj;
		if (allBoxes == null) {
			if (other.allBoxes != null)
				return false;
		} else if (!allBoxes.equals(other.allBoxes))
			return false;
		if (coveringBox == null) {
			if (other.coveringBox != null)
				return false;
		} else if (!coveringBox.equals(other.coveringBox))
			return false;
		return true;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy