/*
* This file is part of JGrasstools (http://www.jgrasstools.org)
* (C) HydroloGIS - www.hydrologis.com
*
* JGrasstools is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.jgrasstools.gears.io.las.index.strtree;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.index.strtree.AbstractNode;
import com.vividsolutions.jts.index.strtree.Boundable;
import com.vividsolutions.jts.index.strtree.ItemBoundable;
import com.vividsolutions.jts.index.strtree.ItemDistance;
import com.vividsolutions.jts.util.PriorityQueue;
import java.util.Iterator;
import java.util.List;
/**
* A pair of {@link com.vividsolutions.jts.index.strtree.Boundable}s, whose leaf items
* support a distance metric between them.
* Used to compute the distance between the members,
* and to expand a member relative to the other
* in order to produce new branches of the
* Branch-and-Bound evaluation tree.
* Provides an ordering based on the distance between the members,
* which allows building a priority queue by minimum distance.
*
* @author Martin Davis
*
*/
class BoundablePair implements Comparable {
private Boundable boundable1;
private Boundable boundable2;
private double distance;
private ItemDistance itemDistance;
// private double maxDistance = -1.0;
public BoundablePair( Boundable boundable1, Boundable boundable2, ItemDistance itemDistance ) {
this.boundable1 = boundable1;
this.boundable2 = boundable2;
this.itemDistance = itemDistance;
distance = distance();
}
/**
* Gets one of the member {@link com.vividsolutions.jts.index.strtree.Boundable}s in the pair
* (indexed by [0, 1]).
*
* @param i the index of the member to return (0 or 1)
* @return the chosen member
*/
public Boundable getBoundable( int i ) {
if (i == 0)
return boundable1;
return boundable2;
}
/**
* Computes the distance between the {@link com.vividsolutions.jts.index.strtree.Boundable}s in this pair.
* The boundables are either composites or leaves.
* If either is composite, the distance is computed as the minimum distance
* between the bounds.
* If both are leaves, the distance is computed by {@link #itemDistance(com.vividsolutions.jts.index.strtree.ItemBoundable, com.vividsolutions.jts.index.strtree.ItemBoundable)}.
*
* @return
*/
private double distance() {
// if items, compute exact distance
if (isLeaves()) {
return itemDistance.distance((ItemBoundable) boundable1, (ItemBoundable) boundable2);
}
// otherwise compute distance between bounds of boundables
return ((Envelope) boundable1.getBounds()).distance(((Envelope) boundable2.getBounds()));
}
/*
public double getMaximumDistance()
{
if (maxDistance < 0.0)
maxDistance = maxDistance();
return maxDistance;
}
*/
/*
private double maxDistance()
{
return maximumDistance(
(Envelope) boundable1.getBounds(),
(Envelope) boundable2.getBounds());
}
private static double maximumDistance(Envelope env1, Envelope env2)
{
double minx = Math.min(env1.getMinX(), env2.getMinX());
double miny = Math.min(env1.getMinY(), env2.getMinY());
double maxx = Math.max(env1.getMaxX(), env2.getMaxX());
double maxy = Math.max(env1.getMaxY(), env2.getMaxY());
Coordinate min = new Coordinate(minx, miny);
Coordinate max = new Coordinate(maxx, maxy);
return min.distance(max);
}
*/
/**
* Gets the minimum possible distance between the Boundables in
* this pair.
* If the members are both items, this will be the
* exact distance between them.
* Otherwise, this distance will be a lower bound on
* the distances between the items in the members.
*
* @return the exact or lower bound distance for this pair
*/
public double getDistance() {
return distance;
}
/**
* Compares two pairs based on their minimum distances
*/
public int compareTo( Object o ) {
BoundablePair nd = (BoundablePair) o;
if (distance < nd.distance)
return -1;
if (distance > nd.distance)
return 1;
return 0;
}
/**
* Tests if both elements of the pair are leaf nodes
*
* @return true if both pair elements are leaf nodes
*/
public boolean isLeaves() {
return !(isComposite(boundable1) || isComposite(boundable2));
}
public static boolean isComposite( Object item ) {
return (item instanceof AbstractNode);
}
private static double area( Boundable b ) {
return ((Envelope) b.getBounds()).getArea();
}
/**
* For a pair which is not a leaf
* (i.e. has at least one composite boundable)
* computes a list of new pairs
* from the expansion of the larger boundable.
*
*/
public void expandToQueue( PriorityQueue priQ, double minDistance ) {
boolean isComp1 = isComposite(boundable1);
boolean isComp2 = isComposite(boundable2);
/**
* HEURISTIC: If both boundable are composite,
* choose the one with largest area to expand.
* Otherwise, simply expand whichever is composite.
*/
if (isComp1 && isComp2) {
if (area(boundable1) > area(boundable2)) {
expand(boundable1, boundable2, priQ, minDistance);
return;
} else {
expand(boundable2, boundable1, priQ, minDistance);
return;
}
} else if (isComp1) {
expand(boundable1, boundable2, priQ, minDistance);
return;
} else if (isComp2) {
expand(boundable2, boundable1, priQ, minDistance);
return;
}
throw new IllegalArgumentException("neither boundable is composite");
}
private void expand( Boundable bndComposite, Boundable bndOther, PriorityQueue priQ, double minDistance ) {
List children = ((AbstractNode) bndComposite).getChildBoundables();
for( Iterator i = children.iterator(); i.hasNext(); ) {
Boundable child = (Boundable) i.next();
BoundablePair bp = new BoundablePair(child, bndOther, itemDistance);
// only add to queue if this pair might contain the closest points
// MD - it's actually faster to construct the object rather than called distance(child,
// bndOther)!
if (bp.getDistance() < minDistance) {
priQ.add(bp);
}
}
}
}