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

org.jaitools.media.jai.rangelookup.RangeLookupTable Maven / Gradle / Ivy

There is a newer version: 1.6.0
Show newest version
/* 
 *  Copyright (c) 2009-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.media.jai.rangelookup;

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

import org.jaitools.numeric.NumberOperations;
import org.jaitools.numeric.Range;
import org.jaitools.numeric.RangeUtils;


/**
 * A lookup table for the RangeLookup operation. 
 * It holds a collection of source value ranges, each mapped to a destination 
 * value. Instances of this class are immutable. 
 * 

* Use the associated Builder class to construct a new table: *


 * // type parameters indicate lookup (source) and result 
 * // (destination) types
 * RangeLookupTable.Builder<Double, Integer> builder = RangeLookupTable.builder();
 *
 * // map all values <= 0 to -1 and all values > 0 to 1
 * builder.add(Range.create(Double.NEGATIVE_INFINITY, false, 0.0, true), -1)
 *        .add(Range.create(0.0, false, Double.POSITIVE_INFINITY, false), 1);
 * 
 * RangeLookupTable<Double, Integer> table = builder.build();
 * 
* * @param type of the lookup (source) value range * @param type of the result (destination) value * * @author Michael Bedward * @author Simone Giannecchini, GeoSolutions * @since 1.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class RangeLookupTable, U extends Number & Comparable> { private final List> items; /** * Private constructor called from the Builder's build method. */ private RangeLookupTable(Builder builder) { this.items = new ArrayList>(builder.items); // Sort the lookup items on the basis of their source ranges Collections.sort(this.items, new LookupItemComparator()); } /** * Finds the LookupItem containing the given source value. * * @param srcValue source image value * * @return the LookupItem containing the source value or null if no matching * item exists */ public LookupItem getLookupItem(T srcValue) { if (items.isEmpty()) { return null; } else { /* * Binary search for source value in items sorted by source range */ int lo = 0; int hi = items.size() - 1; while (hi >= lo) { // update mid position, avoiding int overflow int mid = lo + (hi - lo) / 2; LookupItem item = items.get(mid); Range r = item.getRange(); if (r.contains(srcValue)) { return item; } else if (!r.isMinNegInf() && NumberOperations.compare(srcValue, r.getMin()) <= 0) { hi = mid - 1; } else { lo = mid + 1; } } return null; // no match } } @Override public String toString() { final StringBuilder sb = new StringBuilder(); for (LookupItem item : items) { sb.append(item).append("; "); } return sb.toString(); } /** * Package private method called by {@link RangeLookupRIF}. * * @return an unmodifiable view of the lookup table items */ List> getItems() { return Collections.unmodifiableList(items); } /** * Builder to create an immutable lookup table. * * @param lookup (source) value type * @param result (destination) valuetype */ public static class Builder, U extends Number & Comparable> { private final List> items; /** * Creates a new builder. */ public Builder() { this.items = new ArrayList>(); } /** * Creates a new table that will hold the lookup items added to * this builder. * * @return a new table instance */ public RangeLookupTable build() { return new RangeLookupTable(this); } /** * Adds a new lookup defined by a range of source values mapping to a * result value. * * A new lookup range that overlaps one or * more previously set ranges will be truncated or split into * non-overlapping intervals. For example, if the lookup [5, 10] => 1 * has previously been set, and a new lookup [0, 20] => 2 is added, * then the following lookups will result: *
         *     [0, 5) => 2
         *     [5, 10] => 1
         *     (10, 20] => 2
         * 
* Where a new range is completely overlapped by existing ranges it * will be ignored. *

* * Note that it is possible to end up with unintended gaps in lookup * coverage. If the first range in the above example had been the * half-open interval (5, 10] rather than the closed interval [5, 10] * then the following would have resulted: *

         *     [0, 5) => 2
         *     (5, 10] => 1
         *     (10, 20] => 2
         * 
In this case the value 5 would not be matched. * * @param srcRange the source value range * @param resultValue the destination value */ public Builder add(Range srcRange, U resultValue) { if (srcRange == null || resultValue == null) { throw new IllegalArgumentException("arguments must not be null"); } // Check for overlap with existing ranges for (LookupItem item : items) { if (srcRange.intersects(item.getRange())) { List> diffs = RangeUtils.subtract(item.getRange(), srcRange); for (Range diff : diffs) { add(diff, resultValue); } return this; } } items.add(new LookupItem(srcRange, resultValue)); return this; } } }