/*
* Copyright (c) 2016 Fraunhofer IGD
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Fraunhofer IGD <http://www.igd.fraunhofer.de/>
*/
package de.fhg.igd.geom.indices;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import de.fhg.igd.geom.BoundingBox;
import de.fhg.igd.geom.Localizable;
import de.fhg.igd.geom.Verifier;
/**
* Implements an R-Tree
*
* @author Michel Kraemer
* @param <T> the type of the objects stored in the tree
*/
public class RTree<T extends Localizable> implements SpatialIndex<T> {
/**
*
*/
private static final double DEFAULT_NON_DIMEMSION_SIZE = 0.0001;
/**
* The root node
*/
private Node<T> _root;
/**
* The maximum number of entries a child node can contain
*/
private int _pageSize;
/**
* The number of Localizables indexed in this R-Tree
*/
private int _size;
/**
* the size assumed for non-present dimensions - only important for
* optimization of mixed dimensionality, should be > 0
*/
private final double _nonDimensionSize;
/**
* Default constructor
*
* @param pageSize the page size (number of children that can be attached to
* this node before it gets splitted). Must be even and greater
* than or equal to 4.
*/
public RTree(int pageSize) {
this(pageSize, DEFAULT_NON_DIMEMSION_SIZE);
}
/**
* Default constructor
*
* @param pageSize the page size (number of children that can be attached to
* this node before it gets splitted). Must be even and greater
* than or equal to 4.
* @param nonDimemsionSize the size assumed for non-present dimensions -
* only important for optimization of mixed dimensionality,
* should be > 0
*/
public RTree(int pageSize, double nonDimemsionSize) {
Preconditions.checkArgument(pageSize >= 4 && pageSize % 2 == 0 && nonDimemsionSize > 0);
_nonDimensionSize = nonDimemsionSize;
_pageSize = pageSize;
flush();
}
/**
* @return the root node
*/
public Node<T> getRoot() {
return _root;
}
/**
* Sets the new root node. Package visible, because only Node shall access
* it.
*
* @param root the new root node
*/
void setRoot(Node<T> root) {
_root = root;
}
/**
* @see SpatialIndex#setBoundingBox(BoundingBox)
*/
@Override
public void setBoundingBox(BoundingBox bb) {
// this method is not needed, since the R-Tree grows dynamically
}
/**
* @see SpatialIndex#flush()
*/
@Override
public void flush() {
_root = new Node<T>(_pageSize, null, this);
_size = 0;
}
/**
* @see SpatialIndex#insert(Localizable)
*/
@Override
public void insert(T loc) {
BoundingBox box = loc.getBoundingBox();
if (!box.checkIntegrity()) {
throw new IllegalArgumentException(
"You may not insert a " + "Localizable object with a invalid BoundingBox");
}
_root.insert(loc);
++_size;
}
/**
* @see SpatialIndex#delete(Localizable)
*/
@Override
public boolean delete(T loc) {
if (_root.delete(loc)) {
--_size;
return true;
}
return false;
}
/**
* @see SpatialIndex#size()
*/
@Override
public int size() {
return _size;
}
/**
* @see SpatialIndex#query(Localizable, Verifier)
*/
@Override
public <L extends Localizable> Set<T> query(L loc, Verifier<? super T, L> verifier) {
List<T> candidates = _root.find(loc);
return processQuery(loc, verifier, candidates);
}
/**
* @see SpatialIndex#query2D(Localizable, Verifier)
*/
@Override
public <L extends Localizable> Set<T> query2D(L loc, Verifier<? super T, L> verifier) {
List<T> candidates = _root.find2D(loc);
return processQuery(loc, verifier, candidates);
}
/**
* Traverses through the given list of candidates and finds those that match
* the given verifier
*
* @param <L> the type of localizables to search
* @param loc the localizable to match against
* @param verifier the verifier
* @param candidates the candidates to search
* @return the candidates that match the given verifier
*/
private <L extends Localizable> Set<T> processQuery(L loc, Verifier<? super T, L> verifier,
List<T> candidates) {
if (candidates.size() > 0) {
Set<T> verified = new HashSet<T>();
for (T cand : candidates) {
if (verifier.verify(cand, loc)) {
verified.add(cand);
}
}
return verified;
}
return Collections.emptySet();
}
/**
* @see SpatialIndex#nearestNeighbor(int, Localizable, NNComparator)
*/
@Override
public Set<T> nearestNeighbor(int k, Localizable loc, NNComparator nnc) {
nnc.setLoc(loc);
Set<T> ordered_by_nnc = new TreeSet<T>(nnc);
ordered_by_nnc.addAll(_root.findNeighborhood(k * 2, loc, 0.5));
return ordered_by_nnc;
}
/**
* @return the size assumed for non-present dimensions - only important for
* optimization of mixed dimensionality, should be > 0
*/
public double getNonDimensionSize() {
return _nonDimensionSize;
}
}