/*
* The JTS Topology Suite is a collection of Java classes that
* implement the fundamental operations required to validate a given
* geo-spatial data set to a known topological specification.
*
* Copyright (C) 2001 Vivid Solutions
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* For more information, contact:
*
* Vivid Solutions
* Suite #1A
* 2328 Government Street
* Victoria BC V8T 5G5
* Canada
*
* (250)385-6040
* www.vividsolutions.com
*/
package com.revolsys.geometry.index.intervalrtree;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import com.revolsys.util.ExitLoopException;
/**
* A static index on a set of 1-dimensional intervals,
* using an R-Tree packed based on the order of the interval midpoints.
* It supports range searching,
* where the range is an interval of the real line (which may be a single point).
* A common use is to index 1-dimensional intervals which
* are the projection of 2-D objects onto an axis of the coordinate system.
* <p>
* This index structure is <i>static</i>
* - items cannot be added or removed once the first query has been made.
* The advantage of this characteristic is that the index performance
* can be optimized based on a fixed set of items.
*
* @author Martin Davis
*/
public class SortedPackedIntervalRTree<V> {
private List<IntervalRTreeNode<V>> leaves = new ArrayList<>();
private int level = 0;
private IntervalRTreeNode<V> root = null;
public SortedPackedIntervalRTree() {
}
private void buildLevel(final List<IntervalRTreeNode<V>> src,
final List<IntervalRTreeNode<V>> dest) {
this.level++;
dest.clear();
for (int i = 0; i < src.size(); i += 2) {
final IntervalRTreeNode<V> n1 = src.get(i);
final IntervalRTreeNode<V> n2 = i + 1 < src.size() ? src.get(i) : null;
if (n2 == null) {
dest.add(n1);
} else {
final IntervalRTreeNode<V> node = new IntervalRTreeBranchNode<>(src.get(i), src.get(i + 1));
// printNode(node);
// System.out.println(node);
dest.add(node);
}
}
}
private synchronized void init() {
if (this.root == null) {
try {
// sort the leaf nodes
Collections.sort(this.leaves, new NodeComparator<V>());
// now group nodes into blocks of two and build tree up recursively
List<IntervalRTreeNode<V>> src = this.leaves;
List<IntervalRTreeNode<V>> dest = new ArrayList<>();
while (true) {
buildLevel(src, dest);
if (dest.size() == 1) {
this.root = dest.get(0);
return;
}
final List<IntervalRTreeNode<V>> temp = src;
src = dest;
dest = temp;
}
} finally {
this.leaves = null;
}
}
}
/**
* Adds an item to the index which is associated with the given interval
*
* @param min the lower bound of the item interval
* @param max the upper bound of the item interval
* @param item the item to insert
*
* @throws IllegalStateException if the index has already been queried
*/
public void insert(final double min, final double max, final V item) {
if (this.root != null) {
throw new IllegalStateException("Index cannot be added to once it has been queried");
}
this.leaves.add(new IntervalRTreeLeafNode<>(min, max, item));
}
/**
* Search for intervals in the index which intersect the given closed interval
* and apply the visitor to them.
*
* @param min the lower bound of the query interval
* @param max the upper bound of the query interval
* @param visitor the visitor to pass any matched items to
*/
public void query(final double min, final double max, final Consumer<V> visitor) {
init();
try {
this.root.query(min, max, visitor);
} catch (final ExitLoopException e) {
}
}
}