/* * 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); } } } }