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));
}
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;
}
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;
}
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;
}
}
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;
}
Aggregations