/* 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.cursors; import java.util.Comparator; import java.util.Iterator; import xxl.core.collections.sweepAreas.AbstractSAImplementor; import xxl.core.collections.sweepAreas.ImplementorBasedSweepArea; import xxl.core.collections.sweepAreas.SweepArea; import xxl.core.cursors.AbstractCursor; import xxl.core.cursors.Cursor; import xxl.core.cursors.joins.SortMergeJoin; import xxl.core.cursors.sources.EmptyCursor; import xxl.core.functions.Function; import xxl.core.functions.Tuplify; import xxl.core.predicates.Predicates; import xxl.core.spatial.KPE; import xxl.core.spatial.points.DoublePoint; import xxl.core.spatial.rectangles.DoublePointRectangle; import xxl.core.spatial.rectangles.Rectangle; //import xxl.core.predicates.Predicate; /** * Plane Sweep for two-dimensional data. The Sweepline is hash-based. * See Lars Arge et.al. VLDB 1998. * * (prerelease for prerelease of XXL) * */ public class PlaneSweep extends SortMergeJoin { /** * Compares two Objects with respect to the first dimension of ther left borders in ascending order. * This is useful for a computational geometry plane sweep. The input data is assumed to be of type * KPE. * * @see xxl.core.spatial.KPE * @see xxl.core.spatial.cursors.PlaneSweep * */ public static class KPEPlaneSweepComparator implements Comparator<KPE>{ /** dimensionality used for comparisons. */ final protected int dim; /** Point used for comparisons (false: lower-left, true: upper-right). */ protected boolean upper; /** Default instance of this class. Sets the sweeping dimension to 0. */ public static final KPEPlaneSweepComparator DEFAULT_INSTANCE = new KPEPlaneSweepComparator(0); /** Creates a new KPEPlaneSweepComparator. * * @param upper which point to use for comparisons (false: lower-left, true: upper-right) * @param dim dimensionality used for comparisons. */ public KPEPlaneSweepComparator(boolean upper, int dim){ this.upper = upper; this.dim = dim; } /** Creates a new KPEPlaneSweepComparator. * * @param dim dimensionality used for comparisons. */ public KPEPlaneSweepComparator(int dim){ this(false,dim); } /** Compares its two arguments for order w.r.t. to the specified dimension of this object. * @param object1 the first object * @param object2 the second object * @return an integer as it is defined for a comparator */ public int compare(KPE object1, KPE object2){ double left = ((Rectangle)(object1).getData()).getCorner(upper).getValue(dim); double right = ((Rectangle)(object2).getData()).getCorner(upper).getValue(dim); return left < right ? -1 : (left > right ? +1 : 0); } } /** * Compares two Objects with respect to a user specified dimension * of their left or right borders in ascending order. * This is useful for a computational geometry plane sweep. * * @see xxl.core.spatial.cursors.PlaneSweep * @see xxl.core.spatial.rectangles.Rectangle */ public static class PlaneSweepComparator implements Comparator { /** Dimensionality used for comparisons. */ protected int dim; /** Point used for comparisons. */ protected boolean upper; /** Default instance of this class. Uses dimension 0 as the sweep dimension. */ public static final PlaneSweepComparator DEFAULT_INSTANCE = new PlaneSweepComparator(0); /** Creates a new PlaneSweepComparator. * * @param upper which point to use for comparisons (false: lower-left, true: upper-right) * @param dim dimensionality used for comparisons. */ public PlaneSweepComparator(boolean upper, int dim){ this.upper = upper; this.dim = dim; } /** Creates a new PlaneSweepComparator. * * @param dim dimensionality used for comparisons. */ public PlaneSweepComparator(int dim){ this(false,dim); } /** Compares its two arguments for order w.r.t. to the specified dimension of this object. * @param object1 the first object * @param object2 the second object * @return an integer as it is defined for a comparator */ public int compare(Object object1, Object object2){ double left = ((Rectangle) object1).getCorner(upper).getValue(dim); double right = ((Rectangle) object2).getCorner(upper).getValue(dim); return left < right ? -1 : (left == right ? 0 : +1); } } /** A SweepArea based on hash-buckets */ public static class PlaneSA extends ImplementorBasedSweepArea { /** * A SweepArea implementor based on hash-buckets. */ static class PlaneSAImplementor extends AbstractSAImplementor { /** A bucket of the sweeparea. */ class HashBucket { /** Number of item contained in the sweep area. */ protected int size = 0; /** The data held by this sweep area. */ protected KPE[] data; /** The y-extension of the sweep area */ protected Rectangle descriptor1D; /** Creates a new HashBucket. * @param initialSize initial size of the array usedfor storing elements * @param descriptor1D the y-extension of the sweep area */ public HashBucket(int initialSize, Rectangle descriptor1D){ data = new KPE[initialSize]; this.descriptor1D = descriptor1D; } /** Inserts the given argument object into the hash bucket * @param o the object */ public void insert(final Object o){ _size++; data[size++] = (KPE) o; } /** Queries the hash bucket using the given argument object. * @param o the object * @return a cursor containing all elements that overlap the given query object * @throws IllegalArgumentException Throws an IllegalArgumentException * if something goes wrong due to the passed arguments during retrieval. */ public Iterator query(final Object o) { return new AbstractCursor(){ protected int t = -1; final protected KPE k = (KPE) o; final protected Rectangle r1 = (Rectangle) k.getData(); private Object next; public boolean hasNextObject() { for(t++; t<size; t++){ //while ( (rightCorner, first dimension) smaller (leftCorner, first dimension) ) for( final double value = r1.getCorner(false).getValue(0); ((Rectangle) data[t].getData()).getCorner(true).getValue(0) < value; ){ //remove old elements if(t < --size ){ //t is not the last element in the array data[t] = data[size]; //remove element _size--; } else return false; //return since end of array reached } Rectangle r0 = (Rectangle) data[t].getData(); //check element at position t for overlap with element <o> if (( r0.getCorner(false).getValue(1) <= r1.getCorner(true).getValue(1) ) && ( r1.getCorner(false).getValue(1) <= r0.getCorner(true).getValue(1) ) ){ //Reference Point Method (RPM), i.e. do not report possible duplicates double min = Math.max( r0.getCorner(false).getValue(1), r1.getCorner(false).getValue(1)); if( (descriptor1D.getCorner(false).getValue(0) < min) && (min <= descriptor1D.getCorner(true).getValue(0)) ){ next = new KPE[]{ data[t], k }; return true; } } } return false; } public Object nextObject() { return next; } }; } } /** The data space (universe) rectangle which contains all data objects. */ protected Rectangle universe; /** The initial bucket size of the hash buckets */ protected int initialBucketSize; /** The lower y-boreder of the sweep area */ protected double yoffset; /** The extension of a hash-bucket in y-dimension. */ protected double ydelta; /** The size of the sweep area */ protected int _size; /** * This is a constructor for the class * @param noOfBuckets the number of buckets of the hashtable * @param initialBucketSize the initial size of the buckets of the hashtable * @param universe the bounding box of all objects */ public PlaneSAImplementor(final int noOfBuckets, final int initialBucketSize, final Rectangle universe) { double[] ll = (double[]) universe.getCorner(false).getPoint(); double[] ur = (double[]) universe.getCorner(true).getPoint(); ll[1] -= 0.0001; ur[1] += 0.0001; //enlarge universe slightly DoublePoint llp = new DoublePoint(ll); DoublePoint urp = new DoublePoint(ur); this.universe = new DoublePointRectangle(llp,urp); //create universe this.initialBucketSize = initialBucketSize; this.buckets = new HashBucket[noOfBuckets]; //array initialisieren double start = yoffset = this.universe.getCorner(false).getValue(1); //yoffset setzen; double delta = ydelta = (this.universe.getCorner(true).getValue(1) - this.universe.getCorner(false).getValue(1)) / noOfBuckets; //y-extension for a single bucket for(int i=0; i<buckets.length;i++){ //initialize HashBuckets buckets[i] = new HashBucket(initialBucketSize, new DoublePointRectangle(new DoublePoint(new double[]{start}), new DoublePoint(new double[]{start+=delta}))); } } /** An array containing the hash buckets. */ protected HashBucket[] buckets; /** * Inserts the object into the sweep area. * @param o The object is assumed being from class KPE (@see KPE) and is inserted into the hashtable. */ public void insert(Object o) /* throws IllegalArgumentException */ { KPE k = (KPE)o; int start = (int)( (((Rectangle)k.getData()).getCorner(false).getValue(1) - yoffset) / ydelta ); int end = (int)( (((Rectangle)k.getData()).getCorner(true ).getValue(1) - yoffset) / ydelta ); //insert object into all those hash-buckets that have some overlap: while(start<=end) buckets[start++].insert(o); } /** This operation throws an UnSupportedOperationException * @param o * @return */ public boolean remove(Object o) /* throws IllegalArgumentException */ { throw new UnsupportedOperationException(); } /** This operation throws an UnSupportedOperationException * @param o1 * @param o2 * @return */ public Object update(Object o1, Object o2) /* throws IllegalArgumentException, UnsupportedOperationException */ { throw new UnsupportedOperationException(); } /** * This method initializes the buckets of the hashtable. */ public void clear() { double start = yoffset = this.universe.getCorner(false).getValue(1); //yoffset setzen; double delta = ydelta = (this.universe.getCorner(true).getValue(1) - this.universe.getCorner(false).getValue(1)) / buckets.length; //y-extension for a single bucket for(int i=0; i<buckets.length;i++){ //initialize HashBuckets buckets[i] = new HashBucket(initialBucketSize, new DoublePointRectangle(new DoublePoint(new double[]{start}), new DoublePoint(new double[]{start+=delta}))); } } /** * The hashtable is removed (and the occupied storage is returned to the system) */ public void close() { buckets = null; } /** * @return size of the SweepArea (in the number of elements) */ public int size() { return _size; } /** This operation throws an UnSupportedOperationException * @return */ public Iterator iterator() { throw new UnsupportedOperationException(); } /** Queries the sweep area using the given object. * * @param o the object (of class KPE) * @param ID currently not used in the implementation of the method * @return an iterator containing all elements that overlap the given query object */ public Iterator query(final Object o, int ID) /* throws IllegalArgumentException */ { return new AbstractCursor(){ protected KPE k = (KPE)o; protected int start = (int)( ( ((Rectangle)k.getData()).getCorner(false).getValue(1) - yoffset) / ydelta); protected int end = (int)( ( ((Rectangle)k.getData()).getCorner(true ).getValue(1) - yoffset) / ydelta); private Iterator it = EmptyCursor.DEFAULT_INSTANCE; public boolean hasNextObject() { if (it.hasNext()) return true; while(start <= end){ Iterator result = buckets[start++].query(k); if(result.hasNext()){ it = result; return true; } } return false; } public Object nextObject() { return it.next(); } }; } } /** * A constructor for the class PlaneSA * @param noOfBuckets the number of buckets of the hashtable * @param initialBucketSize the initial size of the buckets * @param universe is a bounding box of the data space */ public PlaneSA(int noOfBuckets, int initialBucketSize, Rectangle universe) { super(new PlaneSAImplementor(noOfBuckets, initialBucketSize, universe), 0, false, Predicates.FALSE, 2); } /** * Reorganization is performed during the query-phase. Hence, * the implementation of this method is empty. * * @param currentStatus The object containing the necessary information * to perform the reorganization step. * @param ID An ID determining from which input this reorganization step * is triggered. * @throws UnsupportedOperationException An UnsupportedOperationException is thrown, if * is method is not supported by this SweepArea. * @throws IllegalStateException Throws an IllegalStateException if * this method is called at an invalid state. */ public void reorganize(Object currentStatus, int ID) throws UnsupportedOperationException, IllegalStateException { } } /** * This is the top-level constructor for the class PlaneSweep.two inputs, does not require the input to be sorted (two inputs). * @param input0 the first input * @param input1 the second input * @param newSorter0 a function that returns a sorting method for the first input * @param newSorter1 a function that returns a sorting method for the second input * @param sweepArea0 the SweepArea of the first input * @param sweepArea1 the SweepArea of the second input * @param comparator the Comparator for comparing two elements from the inputs * @param newResult a function that creates an object from the output of the join */ public PlaneSweep (Cursor input0, Cursor input1, Function newSorter0, Function newSorter1, SweepArea sweepArea0, SweepArea sweepArea1, Comparator comparator, Function newResult) { super( (Cursor)newSorter0.invoke(input0), (Cursor)newSorter1.invoke(input1), sweepArea0, sweepArea1, comparator, newResult); } /** * This is another top-level constructir of the class PlaneSweep * @param input0 the first input * @param input1 the second input * @param newSorter0 a function that returns a sorting method for the first input * @param newSorter1 a function that returns a sorting method for the second input * @param comparator the Comparator for comparing two elements from the inputs * @param universe a bounding box of the data space * @param noOfBuckets the number of buckets of the hashtable * @param initialBucketSize the initial size of the buckets of the hashtable */ public PlaneSweep(Cursor input0, Cursor input1, Function newSorter0, Function newSorter1, Comparator comparator, final Rectangle universe, final int noOfBuckets, final int initialBucketSize){ this(input0, input1, newSorter0, newSorter1, new PlaneSA(noOfBuckets,initialBucketSize, universe), new PlaneSA(noOfBuckets,initialBucketSize, universe), comparator, Tuplify.DEFAULT_INSTANCE ); } }