Search in sources :

Example 1 with SampleDimension

use of org.apache.sis.coverage.SampleDimension in project sis by apache.

the class GridCoverageBuilder method setValues.

/**
 * Sets a two-dimensional slice of sample values as a Java2D data buffer.
 * The {@linkplain DataBuffer#getNumBanks() number of banks} will be the number of bands in the image.
 * If {@linkplain #setRanges(SampleDimension...) sample dimensions are specified}, then the number of
 * bands must be equal to the number of sample dimensions.
 *
 * @param  data  the data buffer to be wrapped in a {@code GridCoverage}. Can not be {@code null}.
 * @param  size  the image size in pixels, or {@code null} if unspecified. If null, then the image
 *               size will be taken from the {@linkplain GridGeometry#getExtent() grid extent}.
 * @return {@code this} for method invocation chaining.
 * @throws IllegalArgumentException if {@code width} or {@code height} is negative or equals to zero.
 */
public GridCoverageBuilder setValues(final DataBuffer data, Dimension size) {
    ArgumentChecks.ensureNonNull("data", data);
    if (size != null) {
        size = new Dimension(size);
        ArgumentChecks.ensureStrictlyPositive("width", size.width);
        ArgumentChecks.ensureStrictlyPositive("height", size.height);
    }
    this.size = size;
    buffer = data;
    image = null;
    raster = null;
    return this;
}
Also used : SampleDimension(org.apache.sis.coverage.SampleDimension) Dimension(java.awt.Dimension)

Example 2 with SampleDimension

use of org.apache.sis.coverage.SampleDimension in project sis by apache.

the class ConvertedGridCoverage method getBandType.

/**
 * Returns the data type for range of values of given sample dimensions.
 * This data type applies to each band, not to a packed sample model
 * (e.g. we assume no packing of 4 byte values in a single 32-bits integer).
 *
 * @param  targets    the sample dimensions for which to get the data type.
 * @param  converted  whether the image will hold converted or packed values.
 * @param  source     if the type can not be determined, coverage from which to inherit the type as a fallback.
 * @return the data type (never null).
 *
 * @see GridCoverage#getBandType()
 */
static DataType getBandType(final List<SampleDimension> targets, final boolean converted, final GridCoverage source) {
    NumberRange<?> union = null;
    boolean allowsNaN = false;
    for (final SampleDimension dimension : targets) {
        final Optional<NumberRange<?>> c = dimension.getSampleRange();
        if (c.isPresent()) {
            final NumberRange<?> range = c.get();
            if (union == null) {
                union = range;
            } else {
                /*
                     * We do not want unit conversions for this union, because the union is used
                     * only for determining a data type having the capacity to store the values.
                     * The physical meaning of those values is not relevant here.
                     */
                if (union instanceof MeasurementRange<?>) {
                    union = new NumberRange<>(union);
                }
                union = union.unionAny(range);
            }
        }
        if (!allowsNaN)
            allowsNaN = dimension.allowsNaN();
    }
    if (union == null) {
        return source.getBandType();
    }
    DataType type = DataType.forRange(union, !converted);
    if (allowsNaN) {
        type = type.toFloat();
    }
    return type;
}
Also used : NumberRange(org.apache.sis.measure.NumberRange) MeasurementRange(org.apache.sis.measure.MeasurementRange) DataType(org.apache.sis.image.DataType) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 3 with SampleDimension

use of org.apache.sis.coverage.SampleDimension in project sis by apache.

the class ColorizerTest method testSampleDimension.

/**
 * Tests the creation of an index color model using {@link Colorizer#Colorizer(Function)}
 * and an initialization with a {@link SampleDimension}.
 *
 * @throws TransformException if a sample value can not be converted.
 */
@Test
public void testSampleDimension() throws TransformException {
    final SampleDimension sd = new SampleDimension.Builder().addQualitative("No data", Float.NaN).addQuantitative("Low temperature", -5, 24, Units.CELSIUS).addQuantitative("Hot temperature", 25, 40, Units.CELSIUS).addQualitative("Error", MathFunctions.toNanFloat(3)).setName("Temperature").build();
    final Colorizer colorizer = new Colorizer(Colorizer.GRAYSCALE);
    assertTrue("initialize", colorizer.initialize(sd));
    // Must be first.
    final IndexColorModel cm = (IndexColorModel) colorizer.compactColorModel(1, 0);
    /*
         * Test conversion of a few sample values to packed values.
         */
    final MathTransform1D tr = colorizer.getSampleToIndexValues();
    assertFalse("isIdentity", tr.isIdentity());
    assertEquals(0, tr.transform(Float.NaN), STRICT);
    assertEquals(1, tr.transform(MathFunctions.toNanFloat(3)), STRICT);
    assertEquals(2, tr.transform(-5), 1E-14);
    assertEquals(255, tr.transform(40), 1E-14);
    /*
         * Verifies a few values from the color map. We test about 1/16 of values.
         * The color map is a simple grayscale, except the two first colors which
         * are transparent.
         */
    assertEquals("mapSize", 256, cm.getMapSize());
    assertEquals("transparentPixel", 0, cm.getTransparentPixel());
    final int[] expected = { 0, 0x00000000, 1, 0x00000000, 2, 0xFF000000, 16, 0xFF161616, 32, 0xFF2E2E2E, 48, 0xFF474747, 64, 0xFF5F5F5F, 80, 0xFF787878, 96, 0xFF909090, 112, 0xFFA9A9A9, 128, 0xFFC2C2C2, 144, 0xFFDADADA, 160, 0xFFF3F3F3, 176, 0xFF151515, 192, 0xFF444444, 208, 0xFF747474, 224, 0xFFA3A3A3, 240, 0xFFD3D3D3, 255, 0xFFFFFFFF };
    for (int k = 0; k < expected.length; ) {
        final int i = expected[k++];
        final int e = expected[k++];
        assertEquals(e, cm.getRGB(i));
    }
}
Also used : MathTransform1D(org.opengis.referencing.operation.MathTransform1D) SampleDimension(org.apache.sis.coverage.SampleDimension) IndexColorModel(java.awt.image.IndexColorModel) Test(org.junit.Test)

Example 4 with SampleDimension

use of org.apache.sis.coverage.SampleDimension in project sis by apache.

the class RecoloredImage method stretchColorRamp.

/**
 * Returns an image with the same sample values than the given image, but with its color ramp stretched
 * between specified or inferred bounds. The mapping applied by this method is conceptually a linear
 * transform applied on sample values before they are mapped to their colors.
 *
 * <p>Current implementation can stretch gray scale and {@linkplain IndexColorModel index color models}).
 * If this method can not stretch the color ramp, for example because the given image is an RGB image,
 * then the image is returned unchanged.</p>
 *
 * @param  processor  the processor to use for computing statistics if needed.
 * @param  source     the image to recolor (can be {@code null}).
 * @param  modifiers  modifiers for narrowing the range of values, or {@code null} if none.
 * @return the image with color ramp stretched between the automatic bounds,
 *         or {@code image} unchanged if the operation can not be applied on the given image.
 *
 * @see ImageProcessor#stretchColorRamp(RenderedImage, Map)
 */
static RenderedImage stretchColorRamp(final ImageProcessor processor, final RenderedImage source, final Map<String, ?> modifiers) {
    /*
         * Images having more than one band (without any band marked as the single band to show) are probably
         * RGB images. It would be possible to stretch the Red, Green and Blue bands separately, but current
         * implementation don't do that because we do not have yet a clear use case.
         */
    final int visibleBand = ImageUtilities.getVisibleBand(source);
    if (visibleBand < 0) {
        return source;
    }
    /*
         * Main use case: color model is (probably) an IndexColorModel or ScaledColorModel instance,
         * or something we can handle in the same way.
         */
    RenderedImage statsSource = source;
    Statistics[] statsAllBands = null;
    Statistics statistics = null;
    Shape areaOfInterest = null;
    Number[] nodataValues = null;
    SampleDimension range = null;
    double minimum = Double.NaN;
    double maximum = Double.NaN;
    double deviations = Double.POSITIVE_INFINITY;
    /*
         * Extract and validate parameter values.
         * No calculation started at this stage.
         */
    if (modifiers != null) {
        final Number minValue = Containers.property(modifiers, "minimum", Number.class);
        final Number maxValue = Containers.property(modifiers, "maximum", Number.class);
        if (minValue != null)
            minimum = minValue.doubleValue();
        if (maxValue != null)
            maximum = maxValue.doubleValue();
        if (minimum >= maximum) {
            throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalRange_2, minValue, maxValue));
        }
        {
            // For keeping `value` in local scope.
            final Number value = Containers.property(modifiers, "multStdDev", Number.class);
            if (value != null) {
                deviations = value.doubleValue();
                ArgumentChecks.ensureStrictlyPositive("multStdDev", deviations);
            }
        }
        areaOfInterest = Containers.property(modifiers, "areaOfInterest", Shape.class);
        Object value = modifiers.get("nodataValues");
        if (value != null) {
            if (value instanceof Number) {
                nodataValues = new Number[] { (Number) value };
            } else if (value instanceof Number[]) {
                nodataValues = (Number[]) value;
            } else {
                throw illegalPropertyType(modifiers, "nodataValues", value);
            }
        }
        value = modifiers.get("statistics");
        if (value != null) {
            if (value instanceof RenderedImage) {
                statsSource = (RenderedImage) value;
            } else if (value instanceof Statistics) {
                statistics = (Statistics) value;
            } else if (value instanceof Statistics[]) {
                statsAllBands = (Statistics[]) value;
            } else {
                throw illegalPropertyType(modifiers, "statistics", value);
            }
        }
        value = modifiers.get("sampleDimensions");
        if (value != null) {
            if (value instanceof List<?>) {
                final List<?> ranges = (List<?>) value;
                if (visibleBand < ranges.size()) {
                    value = ranges.get(visibleBand);
                }
            }
            if (value != null) {
                if (value instanceof SampleDimension) {
                    range = (SampleDimension) value;
                } else {
                    throw illegalPropertyType(modifiers, "sampleDimensions", value);
                }
            }
        }
    }
    /*
         * If minimum and maximum values were not explicitly specified, compute them from statistics.
         * If the range is not valid, then the image will be silently returned as-is.
         */
    if (Double.isNaN(minimum) || Double.isNaN(maximum)) {
        if (statistics == null) {
            if (statsAllBands == null) {
                final DoubleUnaryOperator[] sampleFilters = new DoubleUnaryOperator[visibleBand + 1];
                sampleFilters[visibleBand] = processor.filterNodataValues(nodataValues);
                statsAllBands = processor.valueOfStatistics(statsSource, areaOfInterest, sampleFilters);
            }
            if (statsAllBands != null && visibleBand < statsAllBands.length) {
                statistics = statsAllBands[visibleBand];
            }
        }
        if (statistics != null) {
            deviations *= statistics.standardDeviation(true);
            final double mean = statistics.mean();
            if (Double.isNaN(minimum))
                minimum = Math.max(statistics.minimum(), mean - deviations);
            if (Double.isNaN(maximum))
                maximum = Math.min(statistics.maximum(), mean + deviations);
        }
    }
    if (!(minimum < maximum)) {
        // Use ! for catching NaN.
        return source;
    }
    /*
         * Finished to collect information. Derive a new color model from the existing one.
         */
    final ColorModel cm;
    if (source.getColorModel() instanceof IndexColorModel) {
        /*
             * Get the range of indices of RGB values than can be used for interpolations.
             * We want to exclude qualitative categories (no data, clouds, forests, etc.).
             * In the vast majority of cases, we have at most one quantitative category.
             * But if there is 2 or more, then we select the one having largest intersection
             * with the [minimum … maximum] range.
             */
        final IndexColorModel icm = (IndexColorModel) source.getColorModel();
        final int size = icm.getMapSize();
        int validMin = 0;
        // Inclusive.
        int validMax = size - 1;
        if (range != null) {
            double span = 0;
            for (final Category category : range.getCategories()) {
                if (category.isQuantitative()) {
                    final NumberRange<?> r = category.getSampleRange();
                    final double min = Math.max(r.getMinDouble(true), 0);
                    final double max = Math.min(r.getMaxDouble(true), size - 1);
                    // Intersection.
                    final double s = Math.min(max, maximum) - Math.max(min, minimum);
                    if (s > span) {
                        validMin = (int) min;
                        validMax = (int) max;
                        span = s;
                    }
                }
            }
        }
        /*
             * Create a copy of RGB codes and replace values in the range of the quantitative category.
             * Values for other categories (qualitative) are left unmodified.
             */
        final int start = Math.max((int) minimum, validMin);
        // Inclusive.
        final int end = Math.min((int) maximum, validMax);
        final int[] ARGB = new int[size];
        // Initialize to a copy of current colors.
        icm.getRGBs(ARGB);
        // Part of quantitative category outside the new range.
        Arrays.fill(ARGB, validMin, start, icm.getRGB(validMin));
        Arrays.fill(ARGB, end + 1, validMax + 1, icm.getRGB(validMax));
        final float scale = (float) ((validMax - validMin) / (maximum - minimum));
        for (int i = start; i <= end; i++) {
            final float s = (i - start) * scale + validMin;
            ARGB[i] = icm.getRGB(Math.round(s));
        }
        final SampleModel sm = source.getSampleModel();
        cm = ColorModelFactory.createIndexColorModel(sm.getNumBands(), visibleBand, ARGB, icm.hasAlpha(), icm.getTransparentPixel());
    } else {
        /*
             * Wraps the given image with its colors ramp scaled between the given bounds. If the given image is
             * already using a color ramp for the given range of values, then that image is returned unchanged.
             */
        final SampleModel sm = source.getSampleModel();
        cm = ColorModelFactory.createGrayScale(sm.getDataType(), sm.getNumBands(), visibleBand, minimum, maximum);
    }
    return create(source, cm);
}
Also used : Shape(java.awt.Shape) Category(org.apache.sis.coverage.Category) Statistics(org.apache.sis.math.Statistics) SampleDimension(org.apache.sis.coverage.SampleDimension) SampleModel(java.awt.image.SampleModel) ColorModel(java.awt.image.ColorModel) IndexColorModel(java.awt.image.IndexColorModel) List(java.util.List) RenderedImage(java.awt.image.RenderedImage) DoubleUnaryOperator(java.util.function.DoubleUnaryOperator) IndexColorModel(java.awt.image.IndexColorModel)

Example 5 with SampleDimension

use of org.apache.sis.coverage.SampleDimension in project sis by apache.

the class CoverageStylingApp method createCategoryTable.

/**
 * Creates a table with arbitrary categories to show.
 */
private static TableView<Category> createCategoryTable() {
    final SampleDimension band = new SampleDimension.Builder().addQualitative("Background", 0).addQualitative("Cloud", 1).addQualitative("Land", 2).addQuantitative("Temperature", 5, 255, 0.15, -5, Units.CELSIUS).setName("Sea Surface Temperature").build();
    final CoverageStyling styling = new CoverageStyling(null);
    styling.setARGB(band.getCategories().get(1), new int[] { 0xFF607080 });
    final TableView<Category> table = styling.createCategoryTable(Resources.forLocale(null), Vocabulary.getResources((Locale) null));
    table.getItems().setAll(band.getCategories());
    return table;
}
Also used : Locale(java.util.Locale) Category(org.apache.sis.coverage.Category) SampleDimension(org.apache.sis.coverage.SampleDimension)

Aggregations

SampleDimension (org.apache.sis.coverage.SampleDimension)26 GridGeometry (org.apache.sis.coverage.grid.GridGeometry)5 Category (org.apache.sis.coverage.Category)4 GridExtent (org.apache.sis.coverage.grid.GridExtent)4 MathTransform1D (org.opengis.referencing.operation.MathTransform1D)4 RenderedImage (java.awt.image.RenderedImage)3 Test (org.junit.Test)3 IndexColorModel (java.awt.image.IndexColorModel)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 DoubleUnaryOperator (java.util.function.DoubleUnaryOperator)2 MeasurementRange (org.apache.sis.measure.MeasurementRange)2 NumberRange (org.apache.sis.measure.NumberRange)2 DataStoreContentException (org.apache.sis.storage.DataStoreContentException)2 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)2 TransformException (org.opengis.referencing.operation.TransformException)2 Color (java.awt.Color)1 Dimension (java.awt.Dimension)1 Shape (java.awt.Shape)1 BufferedImage (java.awt.image.BufferedImage)1