/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.index.bintree;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* An <code>BinTree</code> (or "Binary Interval Tree")
* is a 1-dimensional version of a quadtree.
* It indexes 1-dimensional intervals (which may
* be the projection of 2-D objects on an axis).
* It supports range searching
* (where the range may be a single point).
* This structure is dynamic -
* new items can be added at any time,
* and it will support deletion of items
* (although this is not currently implemented).
* <p>
* This implementation does not require specifying the extent of the inserted
* items beforehand. It will automatically expand to accommodate any extent
* of dataset.
* <p>
* The bintree structure is used to provide a primary filter
* for interval queries. The query() method returns a list of
* all objects which <i>may</i> intersect the query interval.
* Note that it may return objects which do not in fact intersect.
* A secondary filter is required to test for exact intersection.
* Of course, this secondary filter may consist of other tests besides
* intersection, such as testing other kinds of spatial relationships.
* <p>
* This index is different to the Interval Tree of Edelsbrunner
* or the Segment Tree of Bentley.
*
* @version 1.7
*/
public class Bintree
{
/**
* Ensure that the Interval for the inserted item has non-zero extents.
* Use the current minExtent to pad it, if necessary
*/
public static Interval ensureExtent(Interval itemInterval, double minExtent)
{
double min = itemInterval.getMin();
double max = itemInterval.getMax();
// has a non-zero extent
if (min != max) return itemInterval;
// pad extent
if (min == max) {
min = min - minExtent / 2.0;
max = min + minExtent / 2.0;
}
return new Interval(min, max);
}
private Root root;
/**
* Statistics
*
* minExtent is the minimum extent of all items
* inserted into the tree so far. It is used as a heuristic value
* to construct non-zero extents for features with zero extent.
* Start with a non-zero extent, in case the first feature inserted has
* a zero extent in both directions. This value may be non-optimal, but
* only one feature will be inserted with this value.
**/
private double minExtent = 1.0;
public Bintree()
{
root = new Root();
}
public int depth()
{
if (root != null) return root.depth();
return 0;
}
public int size()
{
if (root != null) return root.size();
return 0;
}
/**
* Compute the total number of nodes in the tree
*
* @return the number of nodes in the tree
*/
public int nodeSize()
{
if (root != null) return root.nodeSize();
return 0;
}
public void insert(Interval itemInterval, Object item)
{
collectStats(itemInterval);
Interval insertInterval = ensureExtent(itemInterval, minExtent);
//int oldSize = size();
root.insert(insertInterval, item);
/* DEBUG
int newSize = size();
System.out.println("BinTree: size = " + newSize + " node size = " + nodeSize());
if (newSize <= oldSize) {
System.out.println("Lost item!");
root.insert(insertInterval, item);
System.out.println("reinsertion size = " + size());
}
*/
}
/**
* Removes a single item from the tree.
*
* @param itemEnv the Envelope of the item to be removed
* @param item the item to remove
* @return <code>true</code> if the item was found (and thus removed)
*/
public boolean remove(Interval itemInterval, Object item)
{
Interval insertInterval = ensureExtent(itemInterval, minExtent);
return root.remove(insertInterval, item);
}
public Iterator iterator()
{
List foundItems = new ArrayList();
root.addAllItems(foundItems);
return foundItems.iterator();
}
public List query(double x)
{
return query(new Interval(x, x));
}
/**
* Queries the tree to find all candidate items which
* may overlap the query interval.
* If the query interval is <tt>null</tt>, all items in the tree are found.
*
* min and max may be the same value
*/
public List query(Interval interval)
{
/**
* the items that are matched are all items in intervals
* which overlap the query interval
*/
List foundItems = new ArrayList();
query(interval, foundItems);
return foundItems;
}
/**
* Adds items in the tree which potentially overlap the query interval
* to the given collection.
* If the query interval is <tt>null</tt>, add all items in the tree.
*
* @param interval a query interval, or null
* @param resultItems the candidate items found
*/
public void query(Interval interval, Collection foundItems)
{
root.addAllItemsFromOverlapping(interval, foundItems);
}
private void collectStats(Interval interval)
{
double del = interval.getWidth();
if (del < minExtent && del > 0.0)
minExtent = del;
}
}