/* XXL: The eXtensible and fleXible Library for data processing Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger Head of the Database Research Group Department of Mathematics and Computer Science University of Marburg Germany 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 3 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, see <http://www.gnu.org/licenses/>. http://code.google.com/p/xxl/ */ package xxl.core.spatial.geometries.cursors; import java.util.Comparator; import java.util.Iterator; import xxl.core.collections.queues.DynamicHeap; import xxl.core.collections.queues.Heap; import xxl.core.comparators.ComparableComparator; import xxl.core.cursors.AbstractCursor; import xxl.core.functions.AbstractFunction; import xxl.core.functions.Function; import xxl.core.indexStructures.ORTree; import xxl.core.indexStructures.RTree; import xxl.core.indexStructures.Tree.Query.Candidate; import xxl.core.spatial.KPE; import xxl.core.spatial.geometries.Geometry2D; import xxl.core.spatial.rectangles.Rectangle; /** The NearestNeighborQuery is a priority based query which returns all neighbors of a given * query object sorted by their distance to the query object. * There is already a NearestNeighborQuery implemented in {@link ORTree} but this operator just * considers the distance of the index-entries' MBRs, but does not take into consideratoin that there * may be a difference between this distance and the distance of the indexed geometries. * Therefor in this implementation the geometries extracted from the leafs are organised * in a priority queue ordered by the distance, too. If the distance of the first element of * this queue is less then the distance of the head of the candidate-queue this geometry * is returned, otherwise the geommetry of the candidate is extracted and inserted into * the geometry-queue. * */ public class NearestNeighborQuery extends AbstractCursor<DistanceWeightedKPE>{ /** an iterator over the candidate queue */ private Iterator query; /** the heap which sorts the geometries based on their distance to the query object */ private Heap<DistanceWeightedKPE> resultQueue; /** the heap which sorts the candidateQueue based on their distance to the query object */ private Heap<Candidate> candidateQueue; /** a function to extract the geometries from a candidate */ private Function<KPE, Geometry2D> getGeometry; /** the query object */ private Geometry2D queryObject; /** this function returns the distance of a candidate-descriptor to the query-descriptor */ private Function<Candidate, Double> getDistanceToQueryObject; /** the next Geometry2D to return */ private DistanceWeightedKPE nextObject; /** Returns a distance based comparator, which compares two objects according to their distance * to the query object * @return a distance based comparator */ public Comparator<Candidate> getDistanceBasedCandidateComparator(){ return new Comparator<Candidate>(){ public int compare(Candidate c1, Candidate c2) { return Double.compare( getDistanceToQueryObject.invoke(c1), getDistanceToQueryObject.invoke(c2) ); } }; } /** Intitializes a new NearestNeighborQuery with the given parameters. * @param index the {@link RTree} which indexes the spatial data * @param queryObject the query object * @param getGeometry a Function which determines how to extract the geometries from the trees' leafs */ public NearestNeighborQuery( RTree index, final Geometry2D queryObject, Function<KPE, Geometry2D> getGeometry){ this.queryObject = queryObject; this.getGeometry = getGeometry; getDistanceToQueryObject = new AbstractFunction<Candidate, Double>(){ Rectangle queryDescriptor = queryObject.getMBR(); public Double invoke(Candidate c){ return ((Rectangle)c.descriptor()).distance(queryDescriptor, queryDescriptor.dimensions()); } }; candidateQueue = new DynamicHeap<Candidate>(getDistanceBasedCandidateComparator()); resultQueue = new DynamicHeap<DistanceWeightedKPE>(new ComparableComparator()); query = index.query(candidateQueue); } /** Computes the next result of this query, if there is one. * Candidates are expanded to geometries as long as the head of the * candidate is less distant to the query- object than the head of * the geometry queue. */ @Override protected boolean hasNextObject() { while( !candidateQueue.isEmpty() && ( resultQueue.isEmpty() || ( getDistanceToQueryObject.invoke(candidateQueue.peek()) < resultQueue.peek().getDistance() ) ) ){ KPE k = (KPE)((Candidate) query.next()).entry(); resultQueue.enqueue( new DistanceWeightedKPE(k, getGeometry.invoke(k).distance(queryObject) )); } nextObject = (!resultQueue.isEmpty() ? resultQueue.dequeue() : null); return nextObject != null; } /** Returns the next geometry of this query */ @Override protected DistanceWeightedKPE nextObject() { return nextObject; } /** This method is not supported */ public void remove() { throw new UnsupportedOperationException(); } }