Search in sources :

Example 21 with SampleDimension

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

the class GridCoverage2DTest method createTestCoverage.

/**
 * Sames as {@link #createTestCoverage()} except that the "grid to CRS" transform can be specified.
 * The domain of source grid indices is the [0 … 1] range in all dimensions.
 */
private GridCoverage createTestCoverage(final MathTransform gridToCRS) {
    final GridGeometry grid = new GridGeometry(new GridExtent(GRID_SIZE, GRID_SIZE), PixelInCell.CELL_CENTER, gridToCRS, HardCodedCRS.WGS84);
    final MathTransform1D toUnits = (MathTransform1D) MathTransforms.linear(0.5, 100);
    final SampleDimension sd = new SampleDimension.Builder().setName("Some kind of height").addQuantitative("data", NumberRange.create(-10, true, 10, true), toUnits, Units.METRE).build();
    return createTestCoverage(grid, Collections.singletonList(sd));
}
Also used : MathTransform1D(org.opengis.referencing.operation.MathTransform1D) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 22 with SampleDimension

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

the class GridCoverageBuilderTest method testBuilder.

/**
 * Tests {@link GridCoverageBuilder#build()} with various properties defined.
 * Before to invoke this method, caller must invoke a {@code GridCoverageBuilder.setValues(…)} method
 * with an image or raster of size 5×8 pixels. This method starts by an attempt to build the coverage
 * with no other property set, then add properties like sample dimensions and grid extent one by one.
 *
 * @param  builder   the builder to test. Values must be already defined.
 * @param  numBands  the expected number of sample dimensions in the coverage.
 * @return the grid coverage created by the given builder.
 */
private static GridCoverage testBuilder(final GridCoverageBuilder builder, final int numBands) {
    /*
         * Test creation with no properties other than data explicity set.
         * A default list of sample dimensions should be created.
         * Grid geometry should be undefined except for grid extent.
         */
    {
        final GridCoverage coverage = builder.build();
        assertEquals("numBands", numBands, coverage.getSampleDimensions().size());
        final GridGeometry gg = coverage.getGridGeometry();
        assertFalse("isDefined(CRS)", gg.isDefined(GridGeometry.CRS));
        assertFalse("isDefined(ENVELOPE)", gg.isDefined(GridGeometry.ENVELOPE));
        assertFalse("isDefined(GRID_TO_CRS)", gg.isDefined(GridGeometry.GRID_TO_CRS));
        assertTrue("isDefined(EXTENT)", gg.isDefined(GridGeometry.EXTENT));
    }
    /*
         * Test creation with the sample dimensions specified. First, we try with a wrong
         * number of sample dimensions; that construction shall fail. Then try again with
         * the right number of sample dimensions. That time, construction shall succeed
         * and the coverage shall contain the sample dimensions that we specified.
         */
    {
        assertSame(builder, builder.setRanges(new SampleDimension.Builder().setName(0).build()));
        try {
            builder.build();
            fail("Wrong number of sample dimensions, build() should fail.");
        } catch (IllegalStateException ex) {
            assertNotNull(ex.getMessage());
        }
        final SampleDimension[] ranges = new SampleDimension[numBands];
        for (int i = 0; i < numBands; i++) {
            ranges[i] = new SampleDimension.Builder().setName(i).build();
        }
        assertSame(builder, builder.setRanges(ranges));
        final GridCoverage coverage = builder.build();
        assertArrayEquals("sampleDimensions", ranges, coverage.getSampleDimensions().toArray());
    }
    /*
         * Test creation with grid extent and envelope specified. First, we try with a
         * wrong grid extent; that construction shall fail. Then try again with correct
         * grid extent.
         */
    final GridCoverage coverage = testSetDomain(builder, 5, 8);
    final Matrix gridToCRS = MathTransforms.getMatrix(coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER));
    assertEquals(2.0, gridToCRS.getElement(0, 0), STRICT);
    assertEquals(0.5, gridToCRS.getElement(1, 1), STRICT);
    return coverage;
}
Also used : Matrix(org.opengis.referencing.operation.Matrix) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 23 with SampleDimension

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

the class ImageFileDirectory method createMetadata.

/**
 * Builds the metadata with the information stored in the fields of this IFD.
 * This method is invoked only if the user requested the ISO 19115 metadata.
 *
 * @throws DataStoreException if an error occurred while reading metadata from the data store.
 */
@Override
protected Metadata createMetadata() throws DataStoreException {
    final ImageMetadataBuilder metadata = this.metadata;
    if (metadata == null) {
        /*
             * We enter in this block only if an exception occurred during the first attempt to build metadata.
             * If the user insists for getting metadata, fallback on the default (less complete) implementation.
             */
        return super.createMetadata();
    }
    // Clear now in case an exception happens.
    this.metadata = null;
    /*
         * Add information about sample dimensions.
         *
         * Destination: metadata/contentInfo/attributeGroup/attribute
         */
    final boolean isIndexValid = !isReducedResolution();
    metadata.newCoverage(isIndexValid && reader.store.customizer.isElectromagneticMeasurement(index));
    final List<SampleDimension> sampleDimensions = getSampleDimensions();
    for (int band = 0; band < samplesPerPixel; band++) {
        metadata.addNewBand(sampleDimensions.get(band));
        metadata.setBitPerSample(bitsPerSample);
        if (!metadata.hasSampleValueRange()) {
            if (isMinSpecified)
                metadata.addMinimumSampleValue(minValues.doubleValue(Math.min(band, minValues.size() - 1)));
            if (isMaxSpecified)
                metadata.addMaximumSampleValue(maxValues.doubleValue(Math.min(band, maxValues.size() - 1)));
        }
    }
    /*
         * Add Coordinate Reference System built from GeoTIFF tags.
         * Note that the CRS may not exist.
         *
         * Destination: metadata/spatialRepresentationInfo and others.
         */
    if (referencing != null) {
        final GridGeometry gridGeometry = getGridGeometry();
        if (gridGeometry.isDefined(GridGeometry.ENVELOPE))
            try {
                metadata.addExtent(gridGeometry.getEnvelope());
            } catch (TransformException e) {
                warning(e);
            }
        referencing.completeMetadata(gridGeometry, metadata);
    }
    /*
         * End of metadata construction from TIFF tags.
         */
    metadata.finish(this);
    final DefaultMetadata md = metadata.build(false);
    if (isIndexValid) {
        final Metadata c = reader.store.customizer.customize(index, md);
        md.transitionTo(DefaultMetadata.State.FINAL);
        if (c != null)
            return c;
    }
    return md;
}
Also used : GridGeometry(org.apache.sis.coverage.grid.GridGeometry) TransformException(org.opengis.referencing.operation.TransformException) DefaultMetadata(org.apache.sis.metadata.iso.DefaultMetadata) DefaultMetadata(org.apache.sis.metadata.iso.DefaultMetadata) Metadata(org.opengis.metadata.Metadata) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 24 with SampleDimension

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

the class ImageFileDirectory method getSampleDimensions.

/**
 * Returns the ranges of sample values together with the conversion from samples to real values.
 *
 * <h4>Thread-safety</h4>
 * This method is thread-safe because it can be invoked directly by user.
 */
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<SampleDimension> getSampleDimensions() throws DataStoreContentException {
    synchronized (getSynchronizationLock()) {
        if (sampleDimensions == null) {
            final SampleDimension[] dimensions = new SampleDimension[samplesPerPixel];
            final SampleDimension.Builder builder = new SampleDimension.Builder();
            final boolean isIndexValid = !isReducedResolution();
            for (int band = 0; band < dimensions.length; band++) {
                NumberRange<?> sampleRange = null;
                if (minValues != null && maxValues != null) {
                    sampleRange = NumberRange.createBestFit(sampleFormat == FLOAT, minValues.get(Math.min(band, minValues.size() - 1)), true, maxValues.get(Math.min(band, maxValues.size() - 1)), true);
                }
                builder.setName(band + 1).setBackground(getFillValue(true));
                final SampleDimension sd;
                if (isIndexValid) {
                    sd = reader.store.customizer.customize(index, band, sampleRange, builder);
                } else {
                    sd = builder.build();
                }
                dimensions[band] = sd;
                builder.clear();
            }
            sampleDimensions = UnmodifiableArrayList.wrap(dimensions);
        }
        // Safe because unmodifiable.
        return sampleDimensions;
    }
}
Also used : MetadataBuilder(org.apache.sis.internal.storage.MetadataBuilder) SampleDimension(org.apache.sis.coverage.SampleDimension)

Example 25 with SampleDimension

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

the class RasterResource method createSampleDimension.

/**
 * Creates a single sample dimension for the given variable.
 *
 * @param  builder  the builder to use for creating the sample dimension.
 * @param  band     the data for which to create a sample dimension.
 * @param  index    index in the variable dimension identified by {@link #bandDimension}.
 */
private SampleDimension createSampleDimension(final SampleDimension.Builder builder, final Variable band, final int index) {
    /*
         * Take the minimum and maximum values as determined by Apache SIS through the Convention class.  The UCAR library
         * is used only as a fallback. We give precedence to the range computed by Apache SIS instead of the range given
         * by UCAR because we need the range of packed values instead of the range of converted values.
         */
    NumberRange<?> range;
    if (!createEnumeration(builder, band) && (range = band.getValidRange()) != null)
        try {
            final MathTransform1D mt = band.getTransferFunction().getTransform();
            if (!mt.isIdentity() && range instanceof MeasurementRange<?>) {
                /*
                 * Heuristic rule defined in UCAR documentation (see EnhanceScaleMissingUnsigned):
                 * if the type of the range is equal to the type of the scale, and the type of the
                 * data is not wider, then assume that the minimum and maximum are real values.
                 * This is identified in Apache SIS by the range given as a MeasurementRange.
                 */
                final MathTransform1D inverse = mt.inverse();
                boolean isMinIncluded = range.isMinIncluded();
                boolean isMaxIncluded = range.isMaxIncluded();
                double minimum = inverse.transform(range.getMinDouble());
                double maximum = inverse.transform(range.getMaxDouble());
                if (maximum < minimum) {
                    final double swap = maximum;
                    maximum = minimum;
                    minimum = swap;
                    final boolean sb = isMaxIncluded;
                    isMaxIncluded = isMinIncluded;
                    isMinIncluded = sb;
                }
                if (band.getDataType().number <= Numbers.LONG && minimum >= Long.MIN_VALUE && maximum <= Long.MAX_VALUE) {
                    range = NumberRange.create(Math.round(minimum), isMinIncluded, Math.round(maximum), isMaxIncluded);
                } else {
                    range = NumberRange.create(minimum, isMinIncluded, maximum, isMaxIncluded);
                }
            }
            /*
             * Range may be empty if min/max values declared in the netCDF files are erroneous,
             * or if we have not read them correctly (edu.ucar:cdm:4.6.13 sometime confuses an
             * unsigned integer with a signed one).
             */
            if (range.isEmpty()) {
                band.warning(RasterResource.class, "getSampleDimensions", Resources.Keys.IllegalValueRange_4, band.getFilename(), band.getName(), range.getMinValue(), range.getMaxValue());
            } else {
                String name = band.getDescription();
                if (name == null)
                    name = band.getName();
                if (band.getRole() == VariableRole.DISCRETE_COVERAGE) {
                    builder.addQualitative(name, range);
                } else {
                    builder.addQuantitative(name, range, mt, band.getUnit());
                }
            }
        } catch (TransformException e) {
            /*
             * This exception may happen in the call to `inverse.transform`, when we tried to convert
             * a range of measurement values (in the unit of measurement) to a range of sample values.
             * If we failed to do that, we will not add quantitative category. But we still can add
             * qualitative categories for "no data" sample values in the rest of this method.
             */
            warning(e);
        }
    /*
         * Adds the "missing value" or "fill value" as qualitative categories.  If a value has both roles, use "missing value"
         * as category name. If the sample values are already real values, then the "no data" values have been replaced by NaN
         * values by Variable.replaceNaN(Object). The qualitative categories constructed below must be consistent with the NaN
         * values created by `replaceNaN`.
         */
    boolean setBackground = true;
    int ordinal = band.hasRealValues() ? 0 : -1;
    final CharSequence[] names = new CharSequence[2];
    for (final Map.Entry<Number, Object> entry : band.getNodataValues().entrySet()) {
        final Number n;
        if (ordinal >= 0) {
            // Must be consistent with Variable.replaceNaN(Object).
            n = MathFunctions.toNanFloat(ordinal++);
        } else {
            // Should be real number, made unique by the HashMap.
            n = entry.getKey();
        }
        CharSequence name;
        final Object label = entry.getValue();
        if (label instanceof Integer) {
            // Bit 0 set (value 1) = pad value, bit 1 set = missing value.
            final int role = (Integer) label;
            // i=1 if role is only pad value, i=0 otherwise.
            final int i = (role == Convention.FILL_VALUE_MASK) ? 1 : 0;
            name = names[i];
            if (name == null) {
                name = Vocabulary.formatInternational(i == 0 ? Vocabulary.Keys.MissingValue : Vocabulary.Keys.FillValue);
                names[i] = name;
            }
            if (setBackground & (role & Convention.FILL_VALUE_MASK) != 0) {
                // Declare only one fill value.
                setBackground = false;
                builder.setBackground(name, n);
                continue;
            }
        } else {
            name = (CharSequence) label;
        }
        builder.addQualitative(name, n, n);
    }
    /*
         * At this point we have the list of all categories to put in the sample dimension.
         * Now create the sample dimension using the variable short name as dimension name.
         * The index is appended to the name only if bands are all in the same variable.
         */
    String name = band.getName();
    if (bandDimension >= 0) {
        name = Strings.toIndexed(name, index);
    }
    builder.setName(name);
    SampleDimension sd;
    try {
        sd = builder.build();
    } catch (IllegalSampleDimensionException e) {
        /*
             * This error may happen if we have overlapping ranges of sample values.
             * Abandon all categories. We do not keep the quantitative category because
             * using it without taking in account the "no data" values may be dangerous.
             */
        builder.categories().clear();
        sd = builder.build();
        warning(e);
    }
    return sd;
}
Also used : MeasurementRange(org.apache.sis.measure.MeasurementRange) TransformException(org.opengis.referencing.operation.TransformException) SampleDimension(org.apache.sis.coverage.SampleDimension) IllegalSampleDimensionException(org.apache.sis.coverage.IllegalSampleDimensionException) MathTransform1D(org.opengis.referencing.operation.MathTransform1D) HashMap(java.util.HashMap) Map(java.util.Map)

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