/* * 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.strtree; import java.util.List; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.util.PriorityQueue; /** * A pair of {@link 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<I> implements Comparable<BoundablePair<I>> { private static double area(final Boundable<BoundingBox, ?> b) { return b.getBounds().getArea(); } /** * Computes the distance between the {@link 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(ItemBoundable, ItemBoundable)}. * @param itemDistance * * @return */ public static <ITEM> double distance(final ItemDistance<ITEM> itemDistance, final Boundable<BoundingBox, ITEM> boundable1, final Boundable<BoundingBox, ITEM> boundable2) { if (boundable1.isNode() || boundable2.isNode()) { final BoundingBox bounds1 = boundable1.getBounds(); final BoundingBox bounds2 = boundable2.getBounds(); return bounds1.distance(bounds2); } else { final ItemBoundable<BoundingBox, ITEM> item1 = (ItemBoundable<BoundingBox, ITEM>)boundable1; final ItemBoundable<BoundingBox, ITEM> item2 = (ItemBoundable<BoundingBox, ITEM>)boundable2; return itemDistance.distance(item1, item2); } } public static boolean isLeaves(final Boundable<?, ?> boundable1, final Boundable<?, ?> boundable2) { return !(boundable1.isNode() || boundable2.isNode()); } private final Boundable<BoundingBox, I> boundable1; private final Boundable<BoundingBox, I> boundable2; private final double distance; public BoundablePair(final Boundable<BoundingBox, I> boundable1, final Boundable<BoundingBox, I> boundable2, final double distance) { this.boundable1 = boundable1; this.boundable2 = boundable2; this.distance = distance; } public BoundablePair(final Boundable<BoundingBox, I> boundable1, final Boundable<BoundingBox, I> boundable2, final ItemDistance<I> itemDistance) { this.boundable1 = boundable1; this.boundable2 = boundable2; this.distance = distance(itemDistance, this.boundable1, this.boundable2); } /** * Compares two pairs based on their minimum distances */ @Override public int compareTo(final BoundablePair<I> nd) { if (this.distance < nd.distance) { return -1; } else if (this.distance > nd.distance) { return 1; } else { return 0; } } private void expand(final Boundable<BoundingBox, I> bndComposite, final Boundable<BoundingBox, I> bndOther, final PriorityQueue<BoundablePair<I>> priQ, final ItemDistance<I> itemDistance, final double minDistance) { final int childCount = bndComposite.getChildCount(); final List<Boundable<BoundingBox, I>> children = bndComposite.getChildren(); for (int i = 0; i < childCount; i++) { final Boundable<BoundingBox, I> child = children.get(i); final double distance = BoundablePair.distance(itemDistance, child, bndOther); // only add to queue if this pair might contain the closest points if (distance < minDistance) { final BoundablePair<I> bp = new BoundablePair<>(child, bndOther, distance); priQ.add(bp); } } } /** * 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(final PriorityQueue<BoundablePair<I>> priQ, final ItemDistance<I> itemDistance, final double minDistance) { /* * HEURISTIC: If both boundable are composite, choose the one with largest area to expand. * Otherwise, simply expand whichever is composite. */ if (this.boundable1.isNode()) { if (this.boundable2.isNode()) { if (area(this.boundable1) > area(this.boundable2)) { expand(this.boundable1, this.boundable2, priQ, itemDistance, minDistance); } else { expand(this.boundable2, this.boundable1, priQ, itemDistance, minDistance); } } else { expand(this.boundable1, this.boundable2, priQ, itemDistance, minDistance); } } else { if (this.boundable2.isNode()) { expand(this.boundable2, this.boundable1, priQ, itemDistance, minDistance); } else { throw new IllegalArgumentException("neither boundable is composite"); } } } /** * Gets one of the member {@link 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<BoundingBox, I> getBoundable(final int i) { if (i == 0) { return this.boundable1; } return this.boundable2; } /** * 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 this.distance; } /** * Tests if both elements of the pair are leaf nodes * * @return true if both pair elements are leaf nodes */ public boolean isLeaves() { return isLeaves(this.boundable1, this.boundable2); } }