com.vividsolutions.jts.index.quadtree.Quadtree Maven / Gradle / Ivy
/*
* 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.vividsolutions.jts.index.quadtree;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.index.*;
/**
* A Quadtree is a spatial index structure for efficient range querying
* of items bounded by 2D rectangles.
* {@link Geometry}s can be indexed by using their
* {@link Envelope}s.
* Any type of Object can also be indexed as
* long as it has an extent that can be represented by an {@link Envelope}.
*
* This Quadtree index provides a primary filter
* for range rectangle queries. The various query methods return a list of
* all items which may intersect the query rectangle. Note that
* it may thus return items which do not in fact intersect the query rectangle.
* A secondary filter is required to test for actual intersection
* between the query rectangle and the envelope of each candidate item.
* The secondary filter may be performed explicitly,
* or it may be provided implicitly by subsequent operations executed on the items
* (for instance, if the index query is followed by computing a spatial predicate
* between the query geometry and tree items,
* the envelope intersection check is performed automatically.
*
* This implementation does not require specifying the extent of the inserted
* items beforehand. It will automatically expand to accomodate any extent
* of dataset.
*
* This data structure is also known as an MX-CIF quadtree
* following the terminology of Samet and others.
*
* @version 1.7
*/
public class Quadtree
implements SpatialIndex, Serializable
{
private static final long serialVersionUID = -7461163625812743604L;
/**
* Ensure that the envelope for the inserted item has non-zero extents.
* Use the current minExtent to pad the envelope, if necessary
*/
public static Envelope ensureExtent(Envelope itemEnv, double minExtent)
{
//The names "ensureExtent" and "minExtent" are misleading -- sounds like
//this method ensures that the extents are greater than minExtent.
//Perhaps we should rename them to "ensurePositiveExtent" and "defaultExtent".
//[Jon Aquino]
double minx = itemEnv.getMinX();
double maxx = itemEnv.getMaxX();
double miny = itemEnv.getMinY();
double maxy = itemEnv.getMaxY();
// has a non-zero extent
if (minx != maxx && miny != maxy) return itemEnv;
// pad one or both extents
if (minx == maxx) {
minx = minx - minExtent / 2.0;
maxx = minx + minExtent / 2.0;
}
if (miny == maxy) {
miny = miny - minExtent / 2.0;
maxy = miny + minExtent / 2.0;
}
return new Envelope(minx, maxx, miny, maxy);
}
private Root root;
/**
* minExtent is the minimum envelope extent of all items
* inserted into the tree so far. It is used as a heuristic value
* to construct non-zero envelopes for features with zero X and/or Y 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;
/**
* Constructs a Quadtree with zero items.
*/
public Quadtree()
{
root = new Root();
}
/**
* Returns the number of levels in the tree.
*/
public int depth()
{
//I don't think it's possible for root to be null. Perhaps we should
//remove the check. [Jon Aquino]
//Or make an assertion [Jon Aquino 10/29/2003]
if (root != null) return root.depth();
return 0;
}
/**
* Tests whether the index contains any items.
*
* @return true if the index does not contain any items
*/
public boolean isEmpty()
{
if (root == null) return true;
return false;
}
/**
* Returns the number of items in the tree.
*
* @return the number of items in the tree
*/
public int size()
{
if (root != null) return root.size();
return 0;
}
public void insert(Envelope itemEnv, Object item)
{
collectStats(itemEnv);
Envelope insertEnv = ensureExtent(itemEnv, minExtent);
root.insert(insertEnv, item);
}
/**
* Removes a single item from the tree.
*
* @param itemEnv the Envelope of the item to be removed
* @param item the item to remove
* @return true
if the item was found (and thus removed)
*/
public boolean remove(Envelope itemEnv, Object item)
{
Envelope posEnv = ensureExtent(itemEnv, minExtent);
return root.remove(posEnv, item);
}
/*
public List OLDquery(Envelope searchEnv)
{
/**
* the items that are matched are the items in quads which
* overlap the search envelope
*/
/*
List foundItems = new ArrayList();
root.addAllItemsFromOverlapping(searchEnv, foundItems);
return foundItems;
}
*/
/**
* Queries the tree and returns items which may lie in the given search envelope.
* Precisely, the items that are returned are all items in the tree
* whose envelope may intersect the search Envelope.
* Note that some items with non-intersecting envelopes may be returned as well;
* the client is responsible for filtering these out.
* In most situations there will be many items in the tree which do not
* intersect the search envelope and which are not returned - thus
* providing improved performance over a simple linear scan.
*
* @param searchEnv the envelope of the desired query area.
* @return a List of items which may intersect the search envelope
*/
public List query(Envelope searchEnv)
{
/**
* the items that are matched are the items in quads which
* overlap the search envelope
*/
ArrayListVisitor visitor = new ArrayListVisitor();
query(searchEnv, visitor);
return visitor.getItems();
}
/**
* Queries the tree and visits items which may lie in the given search envelope.
* Precisely, the items that are visited are all items in the tree
* whose envelope may intersect the search Envelope.
* Note that some items with non-intersecting envelopes may be visited as well;
* the client is responsible for filtering these out.
* In most situations there will be many items in the tree which do not
* intersect the search envelope and which are not visited - thus
* providing improved performance over a simple linear scan.
*
* @param searchEnv the envelope of the desired query area.
* @param visitor a visitor object which is passed the visited items
*/
public void query(Envelope searchEnv, ItemVisitor visitor)
{
/**
* the items that are matched are the items in quads which
* overlap the search envelope
*/
root.visit(searchEnv, visitor);
}
/**
* Return a list of all items in the Quadtree
*/
public List queryAll()
{
List foundItems = new ArrayList();
root.addAllItems(foundItems);
return foundItems;
}
private void collectStats(Envelope itemEnv)
{
double delX = itemEnv.getWidth();
if (delX < minExtent && delX > 0.0)
minExtent = delX;
double delY = itemEnv.getHeight();
if (delY < minExtent && delY > 0.0)
minExtent = delY;
}
}