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

jaitools.numeric.RangeUtils Maven / Gradle / Ivy

Go to download

Provides a single jar containing all JAI-tools 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.

The newest version!
/*
 * Copyright 2010 Michael Bedward
 *
 * This file is part of jai-tools.
 *
 * jai-tools 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.
 *
 * jai-tools 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 jai-tools.  If not, see .
 *
 */
package 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: RangeUtils.java 1504 2011-03-05 10:56:10Z michael.bedward $
 */
public class RangeUtils {

    private static class RangeSortComparator implements Comparator> {
        private RangeComparator rc;

        public RangeSortComparator(RangeComparator rc) {
            this.rc = rc;
        }

        public int compare(Range r1, Range r2) {
            RangeComparator.Result result = rc.compare(r1, r2);
            switch (result.getAt(RangeComparator.MIN_MIN)) {
                case RangeComparator.LT:
                    return -1;

                case RangeComparator.GT:
                    return 1;

                default:
                    switch (result.getAt(RangeComparator.MAX_MAX)) {
                        case RangeComparator.LT:
                            return -1;

                        case RangeComparator.GT:
                            return 1;

                        default:
                            return 0;
                    }
            }
        }

    }


    /**
     * 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(jaitools.numeric.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. * * @param the value type * @param ranges the ranges to sort * * @return sorted ranges as a {@code List} */ public static List> sort(Collection> ranges) { List> inputs = new ArrayList>(ranges); Collections.sort(inputs, new RangeSortComparator(new RangeComparator())); return inputs; } /** * 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); RangeComparator comparator = new RangeComparator(); 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); RangeComparator.Result result = comparator.compare(r1, r2); if (RangeComparator.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 RangeSortComparator(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 */ RangeComparator rc = new RangeComparator(); RangeComparator.Result result = rc.compare(r1, r2); if (RangeComparator.isIntersection(result)) { T min; boolean minIncluded; switch (result.getAt(RangeComparator.MIN_MIN)) { case RangeComparator.LT: min = r2.getMin(); minIncluded = r2.isMinIncluded(); break; case RangeComparator.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(RangeComparator.MAX_MAX)) { case RangeComparator.LT: max = r1.getMax(); maxIncluded = r1.isMaxIncluded(); break; case RangeComparator.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 } RangeComparator rc = new RangeComparator(); RangeComparator.Result result = rc.compare(common, r2); int minComp = result.getAt(RangeComparator.MIN_MIN); int maxComp = result.getAt(RangeComparator.MAX_MAX); if (minComp == RangeComparator.EQ) { difference.add(new Range(common.getMax(), !common.isMaxIncluded(), r2.getMax(), r2.isMaxIncluded())); } else { // minComp == GT if (maxComp == RangeComparator.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 - 2025 Weber Informatics LLC | Privacy Policy