All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jaitools.numeric.RangeUtils Maven / Gradle / Ivy

Go to download

Provides a single jar containing all JAITools modules which you can use instead of including individual modules in your project. Note: It does not include the Jiffle scripting language or Jiffle image operator.

There is a newer version: 1.4.0
Show newest version
/* 
 *  Copyright (c) 2010-2013, Michael Bedward. All rights reserved. 
 *   
 *  Redistribution and use in source and binary forms, with or without modification, 
 *  are permitted provided that the following conditions are met: 
 *   
 *  - Redistributions of source code must retain the above copyright notice, this  
 *    list of conditions and the following disclaimer. 
 *   
 *  - Redistributions in binary form must reproduce the above copyright notice, this 
 *    list of conditions and the following disclaimer in the documentation and/or 
 *    other materials provided with the distribution.   
 *   
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 *  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 *  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */   
package org.jaitools.numeric;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Provides static helper methods to transform, sort and merge {@code Range} objects.
 *
 * @author Michael Bedward
 * @author Daniele Romagnoli, GeoSolutions S.A.S.
 * @since 1.0
 * @version $Id$
 */
public class RangeUtils {


    /**
     * Creates the complement of a {@code Range}. This is equivalent to subtracting the
     * input from the infinite interval.
     * 

* If the input is a finite interval or point, the result will be a list of * two {@code Ranges}. For example: the complement of [-5, 5) is made up of * (-Inf, -5) and [5, Inf). *

* If the input is an interval open at one end, the result list will contain * a single {@code Range}. For example: the complement of (-Inf, 5) is [5, Inf). *

* If the input is a point at positive or negative infinity its complement is, * by convention, (-Inf, Inf). *

* If the input is the infinite interval (-Inf, Inf) the result list will be * empty. * * @param the value type * @param range input range * * @return a list of 0, 1 or 2 {@code Ranges} which form the complement * * @see #createComplement(java.util.Collection) */ public static List> createComplement(Range range) { return subtract(range, new Range(null, false, null, false)); } /** * Creates the complement of the given list of {@code Ranges}. This method first * calls {@linkplain #simplify} on the inputs and then subtracts each of the * resulting {@code Ranges} from the whole number line. * * @param value type * @param ranges input ranges * * @return a list of {@code Ranges} which form the complement (may be empty) * * @see #createComplement(Range) */ public static List> createComplement(Collection> ranges) { List> inputs = simplify(ranges); List> complements = new ArrayList>(); /* * Start with the whole number line, then subtract each of * the input ranges from it */ complements.add(new Range(null, true, null, true)); for (int i = 0; i < inputs.size(); i++) { boolean changed = false; Range rin = inputs.get(i); for (int j = 0; j < complements.size() && !changed; j++) { Range rc = complements.get(j); List> diff = subtract(rin, rc); final int ndiff = diff.size(); switch (ndiff) { case 0: complements.remove(j); changed = true; break; case 1: if (!diff.get(0).equals(rc)) { complements.remove(j); complements.add(diff.get(0)); changed = true; } break; case 2: complements.remove(j); complements.addAll(diff); changed = true; break; } } } return complements; } /** * Sorts a collection of ranges into ascending order of min value, then max value. * Returns a new List of sorted ranges, leaving the input collection unmodified. * * @param the value type * @param ranges the ranges to sort * * @return sorted ranges as a {@code List} */ public static List> sort(Collection> ranges) { List> copy = new ArrayList>(ranges); Collections.sort(copy, new RangeComparator(new RangeExtendedComparator())); return copy; } /** * Sorts a list of ranges into ascending order of min value, then max value. * * @param the value type * @param ranges the ranges to sort */ public static void sortInPlace(List> ranges) { Collections.sort(ranges, new RangeComparator(new RangeExtendedComparator())); } /** * Simplifies a collection of ranges by merging those which overlap. * * @param value type * @param ranges input ranges to simplify * * @return simplified ranges sorted by min, then max end-points */ public static List> simplify(Collection> ranges) { List> inputs = new ArrayList>(ranges); RangeExtendedComparator comparator = new RangeExtendedComparator(); boolean changed; do { changed = false; for (int i = 0; i < inputs.size()-1 && !changed; i++) { Range r1 = inputs.get(i); for (int j = i+1; j < inputs.size() && !changed; j++) { Range r2 = inputs.get(j); RangeExtendedComparator.Result result = comparator.compare(r1, r2); if (RangeExtendedComparator.isIntersection(result)) { switch (result) { case EEEE: // r1 and r2 are equal points case EEGG: // r2 is a point at min of r1 case LEEG: // equal intervals case LEGG: // r1 contains r2 case LLEE: // r2 is a point at max of r1 case LLEG: // r1 contains r2 case LLGG: // r1 contains r2 inputs.remove(j); break; case EGEG: // r1 is a point at max of r2 case LELE: // r1 is a point at min of r2 case LELG: // r1 is contained in r2 case LGEG: // r1 is contained in r2 case LGLG: // r1 is contained in r2 inputs.remove(i); break; case EGGG: // r1 extends from max of r2 case LGGG: // r1 starts within and extends beyond r2 inputs.remove(j); inputs.remove(i); inputs.add(0, new Range(r2.getMin(), r2.isMinIncluded(), r1.getMax(), r1.isMaxIncluded())); break; case LLLE: // r1 extends to min of r2 case LLLG: // r1 extends into r2 inputs.remove(j); inputs.remove(i); inputs.add(0, new Range(r1.getMin(), r1.isMinIncluded(), r2.getMax(), r2.isMaxIncluded())); break; } changed = true; } } } } while (changed); /* * Next, look for any pairs of the form [A, B) [B, C] that can be joined as [A, C] */ Collections.sort(inputs, new RangeComparator(comparator)); do { changed = false; for (int i = 0; i < inputs.size() - 1 && !changed; i++) { Range r1 = inputs.get(i); if (r1.isMaxClosed()) { for (int j = i + 1; j < inputs.size() && !changed; j++) { Range r2 = inputs.get(j); if (r2.isMinClosed()) { if (r1.getMax().compareTo(r2.getMin()) == 0) { inputs.remove(j); inputs.remove(i); inputs.add(i, new Range(r1.getMin(), r1.isMinIncluded(), r2.getMax(), r2.isMaxIncluded())); changed = true; } } } } } } while (changed); return inputs; } /** * Gets the intersection of two ranges. * * @param value type * @param r1 first range * @param r2 second range * * @return the intersection as a new range; or {@code null} if there was no intersection */ public static Range intersection(Range r1, Range r2) { if (r1.isPoint()) { if (r2.isPoint()) { if (r1.equals(r2)) { return new Range(r1); } else { return null; } } else { // r2 is an interval if ((r1.isMinInf() && r2.isMaxOpen()) || (r1.isMinNegInf() && r2.isMinOpen()) || r2.contains(r1.getMin())) { return new Range(r1); } else { return null; } } } else if (r2.isPoint()) { // r1 is an interval if ((r2.isMinInf() && r1.isMaxOpen()) || (r2.isMinNegInf() && r1.isMinOpen()) || r1.contains(r2.getMin())) { return new Range(r2); } else { return null; } } /* * From here, we are comparing two interval ranges */ RangeExtendedComparator rc = new RangeExtendedComparator(); RangeExtendedComparator.Result result = rc.compare(r1, r2); if (RangeExtendedComparator.isIntersection(result)) { T min; boolean minIncluded; switch (result.getAt(RangeExtendedComparator.MIN_MIN)) { case RangeExtendedComparator.LT: min = r2.getMin(); minIncluded = r2.isMinIncluded(); break; case RangeExtendedComparator.GT: min = r1.getMin(); minIncluded = r1.isMinIncluded(); break; default: min = r1.getMin(); minIncluded = r1.isMinIncluded() || r2.isMinIncluded(); break; } T max; boolean maxIncluded; switch (result.getAt(RangeExtendedComparator.MAX_MAX)) { case RangeExtendedComparator.LT: max = r1.getMax(); maxIncluded = r1.isMaxIncluded(); break; case RangeExtendedComparator.GT: max = r2.getMax(); maxIncluded = r2.isMaxIncluded(); break; default: max = r1.getMax(); maxIncluded = r1.isMaxIncluded() || r2.isMaxIncluded(); break; } return new Range(min, minIncluded, max, maxIncluded); } return null; } /** * Subtracts the first range from the second. If the two inputs do not intersect * the result will be equal to the second. If the two inputs are equal, or the first * input encloses the second, the result will be an empty list. If the first input * is strictly contained within the second the result will be two ranges. * * @param value type * @param r1 the first range * @param r2 the second range * * @return 0, 1 or 2 ranges representing the result of {@code r2 - r1} */ public static List> subtract(Range r1, Range r2) { List> difference = new ArrayList>(); /* * Check for equality between inputs */ if (r1.equals(r2)) { return difference; // empty list } Range common = intersection(r1, r2); /* * Check for no overlap between inputs */ if (common == null) { difference.add( new Range(r2) ); return difference; } /* * Check if r1 enclosed r2 */ if (common.equals(r2)) { return difference; // empty list } RangeExtendedComparator rc = new RangeExtendedComparator(); RangeExtendedComparator.Result result = rc.compare(common, r2); int minComp = result.getAt(RangeExtendedComparator.MIN_MIN); int maxComp = result.getAt(RangeExtendedComparator.MAX_MAX); if (minComp == RangeExtendedComparator.EQ) { difference.add(new Range(common.getMax(), !common.isMaxIncluded(), r2.getMax(), r2.isMaxIncluded())); } else { // minComp == GT if (maxComp == RangeExtendedComparator.EQ) { difference.add(new Range(r2.getMin(), r2.isMinIncluded(), common.getMin(), !common.isMinIncluded())); } else { // common lies within r2 difference.add(new Range(r2.getMin(), r2.isMinIncluded(), common.getMin(), !common.isMinIncluded())); difference.add(new Range(common.getMax(), !common.isMaxIncluded(), r2.getMax(), r2.isMaxIncluded())); } } return difference; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy