blob: 2be6ff4e803865bc1f61e631f426c7fcb87e8050 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.sis.metadata.iso.content;
import java.util.Collection;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlSeeAlso;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import javax.measure.Unit;
import org.opengis.metadata.content.Band;
import org.opengis.metadata.content.CoverageContentType;
import org.opengis.metadata.content.TransferFunctionType;
import org.opengis.metadata.content.RangeElementDescription;
import org.opengis.util.Record;
import org.opengis.util.RecordType;
import org.apache.sis.measure.ValueRange;
import org.apache.sis.xml.bind.gco.GO_Real;
import org.apache.sis.xml.bind.gco.GO_Integer;
import org.apache.sis.xml.bind.gco.GO_Record;
import org.apache.sis.xml.bind.gco.GO_RecordType;
import org.apache.sis.xml.bind.metadata.MI_RangeElementDescription;
import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
// Specific to the geoapi-3.1 and geoapi-4.0 branches:
import org.opengis.metadata.content.SampleDimension;
/**
* The characteristic of each dimension (layer) included in the resource.
* The following property is conditional (i.e. mandatory under some circumstances)
* in a well-formed metadata according ISO 19115:
*
* <div class="preformat">{@code MD_SampleDimension}
* {@code   └─units………………………} Units of data in each dimension included in the resource.</div>
*
* <h2>Terminology</h2>
* <i>Data values</i> should be physical values expressed in the unit of measurement
* given by {@link #getUnits()}. <i>Cell values</i> are values stored in the device,
* before conversion to data values by application of {@linkplain #getScaleFactor() scale
* factor} and {@linkplain #getOffset() offset}.
*
* <h2>Limitations</h2>
* <ul>
* <li>Instances of this class are not synchronized for multi-threading.
* Synchronization, if needed, is caller's responsibility.</li>
* <li>Serialized objects of this class are not guaranteed to be compatible with future Apache SIS releases.
* Serialization support is appropriate for short term storage or RMI between applications running the
* same version of Apache SIS. For long term storage, use {@link org.apache.sis.xml.XML} instead.</li>
* </ul>
*
* @author Rémi Maréchal (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @author Cullen Rombach (Image Matters)
* @version 1.4
* @since 0.5
*/
@XmlType(name = "MD_SampleDimension_Type", propOrder = {
"maxValue",
"minValue",
"units",
// New in ISO 19115-3
"scaleFactor",
"offset",
"meanValue",
"numberOfValues",
"standardDeviation",
"otherPropertyType",
"otherProperty",
"bitsPerValue",
"rangeElementDescriptions"
})
@XmlRootElement(name = "MD_SampleDimension")
@XmlSeeAlso({DefaultBand.class, DefaultRangeDimension.class})
public class DefaultSampleDimension extends DefaultRangeDimension implements SampleDimension {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 4517148689016920767L;
/**
* Number of values used in a thematic classification resource.
* This value should be expressed in the unit of measurement given by {@link #units}.
*/
private Integer numberOfValues;
/**
* Minimum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #units}.
*/
private Double minValue;
/**
* Maximum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #units}.
*/
private Double maxValue;
/**
* Mean value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #units}.
*/
private Double meanValue;
/**
* Standard deviation of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #units}.
*/
private Double standardDeviation;
/**
* Units of data in each dimension included in the resource.
*/
@SuppressWarnings("serial")
private Unit<?> units;
/**
* Scale factor which has been applied to the cell value.
*/
private Double scaleFactor;
/**
* Physical value corresponding to a cell value of zero.
*/
private Double offset;
/**
* Type of transfer function to be used when scaling a physical value for a given element.
*/
private TransferFunctionType transferFunctionType;
/**
* Maximum number of significant bits in the uncompressed representation
* for the value in each band of each pixel.
*/
private Integer bitsPerValue;
/**
* Provides the description and values of the specific range elements of a sample dimension.
*/
@SuppressWarnings("serial")
private Collection<RangeElementDescription> rangeElementDescriptions;
/**
* Smallest distance between which separate points can be distinguished, as specified in
* instrument design.
*/
private Double nominalSpatialResolution;
/**
* Type of other attribute description.
*/
@SuppressWarnings("serial")
private RecordType otherPropertyType;
/**
* Instance of other/attributeType that defines attributes not explicitly
* included in {@link CoverageContentType}.
*/
@SuppressWarnings("serial")
private Record otherProperty;
/**
* Constructs an initially empty sample dimension.
*/
public DefaultSampleDimension() {
}
/**
* Constructs a new instance initialized with the values from the specified metadata object.
* This is a <em>shallow</em> copy constructor, because the other metadata contained in the
* given object are not recursively copied.
*
* <h4>Note on properties validation</h4>
* This constructor does not verify the property values of the given metadata (e.g. whether it contains
* unexpected negative values). This is because invalid metadata exist in practice, and verifying their
* validity in this copy constructor is often too late. Note that this is not the only hole, as invalid
* metadata instances can also be obtained by unmarshalling an invalid XML document.
*
* @param object the metadata to copy values from, or {@code null} if none.
*
* @see #castOrCopy(SampleDimension)
*/
public DefaultSampleDimension(final SampleDimension object) {
super(object);
if (object != null) {
minValue = object.getMinValue();
maxValue = object.getMaxValue();
meanValue = object.getMeanValue();
numberOfValues = object.getNumberOfValues();
standardDeviation = object.getStandardDeviation();
units = object.getUnits();
scaleFactor = object.getScaleFactor();
offset = object.getOffset();
transferFunctionType = object.getTransferFunctionType();
bitsPerValue = object.getBitsPerValue();
nominalSpatialResolution = object.getNominalSpatialResolution();
otherPropertyType = object.getOtherPropertyType();
otherProperty = object.getOtherProperty();
}
}
/**
* Returns a SIS metadata implementation with the values of the given arbitrary implementation.
* This method performs the first applicable action in the following choices:
*
* <ul>
* <li>If the given object is {@code null}, then this method returns {@code null}.</li>
* <li>Otherwise if the given object is is an instance of {@link Band}, then this
* method delegates to the {@code castOrCopy(…)} method of the corresponding SIS subclass.</li>
* <li>Otherwise if the given object is already an instance of
* {@code DefaultSampleDimension}, then it is returned unchanged.</li>
* <li>Otherwise a new {@code DefaultSampleDimension} instance is created using the
* {@linkplain #DefaultSampleDimension(SampleDimension) copy constructor} and returned.
* Note that this is a <em>shallow</em> copy operation, because the other
* metadata contained in the given object are not recursively copied.</li>
* </ul>
*
* @param object the object to get as a SIS implementation, or {@code null} if none.
* @return a SIS implementation containing the values of the given object (may be the
* given object itself), or {@code null} if the argument was null.
*/
public static DefaultSampleDimension castOrCopy(final SampleDimension object) {
if (object instanceof Band) {
return DefaultBand.castOrCopy((Band) object);
}
//-- Intentionally tested after the sub-interfaces.
if (object == null || object instanceof DefaultSampleDimension) {
return (DefaultSampleDimension) object;
}
return new DefaultSampleDimension(object);
}
/**
* Returns the number of values used in a thematic classification resource.
*
* @return the number of values used in a thematic classification resource, or {@code null} if none.
*/
@Override
@ValueRange(minimum = 0)
@XmlElement(name = "numberOfValues")
@XmlJavaTypeAdapter(GO_Integer.Since2014.class)
public Integer getNumberOfValues() {
return numberOfValues;
}
/**
* Sets the number of values used in a thematic classification resource.
*
* @param newValue the new number of values used in a thematic classification resource.
* @throws IllegalArgumentException if the given value is negative.
*/
public void setNumberOfValues(final Integer newValue) {
checkWritePermission(numberOfValues);
if (ensurePositive(DefaultSampleDimension.class, "numberOfValues", false, newValue)) {
numberOfValues = newValue;
}
}
/**
* Returns the minimum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @return minimum value of data values in each dimension included in the resource, or {@code null} if unspecified.
*/
@Override
@XmlElement(name = "minValue")
public Double getMinValue() {
return minValue;
}
/**
* Sets the minimum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @param newValue the new new minimum value.
*/
public void setMinValue(final Double newValue) {
checkWritePermission(minValue);
minValue = newValue;
}
/**
* Returns the maximum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @return maximum value of data values in each dimension included in the resource, or {@code null} if unspecified.
*/
@Override
@XmlElement(name = "maxValue")
public Double getMaxValue() {
return maxValue;
}
/**
* Sets the maximum value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @param newValue the new new maximum value.
*/
public void setMaxValue(final Double newValue) {
checkWritePermission(maxValue);
maxValue = newValue;
}
/**
* Returns the mean value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @return the mean value of data values in each dimension included in the resource, or {@code null} if none.
*/
@Override
@XmlElement(name = "meanValue")
@XmlJavaTypeAdapter(GO_Real.Since2014.class)
public Double getMeanValue() {
return meanValue;
}
/**
* Sets the mean value of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @param newValue the new mean value of data values in each dimension included in the resource.
*/
public void setMeanValue(final Double newValue) {
checkWritePermission(meanValue);
meanValue = newValue;
}
/**
* Returns the standard deviation of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @return standard deviation of data values in each dimension included in the resource, or {@code null} if none.
*/
@Override
@XmlElement(name = "standardDeviation")
@XmlJavaTypeAdapter(GO_Real.Since2014.class)
public Double getStandardDeviation() {
return standardDeviation;
}
/**
* Sets the standard deviation of data values in each dimension included in the resource.
* This value should be expressed in the unit of measurement given by {@link #getUnits()}.
*
* @param newValue the new standard deviation of data values in each dimension included in the resource.
*/
public void setStandardDeviation(final Double newValue) {
checkWritePermission(standardDeviation);
standardDeviation = newValue;
}
/**
* Returns the units of data in the dimension.
*
* @return the units of data in the dimension, or {@code null} if unspecified.
*/
@Override
@XmlElement(name = "units")
public Unit<?> getUnits() {
return units;
}
/**
* Sets the units of data in the dimension.
*
* @param newValue the new units of data in the dimension.
*/
public void setUnits(final Unit<?> newValue) {
checkWritePermission(units);
units = newValue;
}
/**
* Returns the scale factor which has been applied to the cell value.
*
* @return scale factor which has been applied to the cell value, or {@code null} if none.
*/
@Override
@XmlElement(name = "scaleFactor")
public Double getScaleFactor() {
return scaleFactor;
}
/**
* Sets the scale factor which has been applied to the cell value.
*
* @param newValue the new scale factor which has been applied to the cell value.
*/
public void setScaleFactor(final Double newValue) {
checkWritePermission(scaleFactor);
scaleFactor = newValue;
}
/**
* Returns the physical value corresponding to a cell value of zero.
*
* @return the physical value corresponding to a cell value of zero, or {@code null} if none.
*/
@Override
@XmlElement(name = "offset")
public Double getOffset() {
return offset;
}
/**
* Sets the physical value corresponding to a cell value of zero.
*
* @param newValue the new physical value corresponding to a cell value of zero.
*/
public void setOffset(final Double newValue) {
checkWritePermission(offset);
offset = newValue;
}
/**
* Returns type of transfer function to be used when scaling a physical value for a given element.
*
* <h4>Note on XML marshalling</h4>
* ISO 19115-2 defines this property in {@linkplain DefaultBand a subtype} for historical reasons.
* Apache SIS moves this property up in the hierarchy since this property can apply to any sample dimension,
* not only the measurements in the electromagnetic spectrum. However, this property will not appear in XML
* documents unless this {@code SampleDimension} is actually a {@code Band}.
*
* @return type of transfer function, or {@code null}.
*/
@Override
public TransferFunctionType getTransferFunctionType() {
return transferFunctionType;
}
/**
* Sets the type of transfer function to be used when scaling a physical value for a given element.
*
* @param newValue the new transfer function value.
*/
public void setTransferFunctionType(final TransferFunctionType newValue) {
checkWritePermission(transferFunctionType);
transferFunctionType = newValue;
}
/**
* Returns the maximum number of significant bits in the uncompressed representation
* for the value in each band of each pixel.
*
* @return maximum number of significant bits in the uncompressed representation
* for the value in each band of each pixel, or {@code null} if none.
*/
@Override
@ValueRange(minimum = 1)
@XmlElement(name = "bitsPerValue")
public Integer getBitsPerValue() {
return bitsPerValue;
}
/**
* Sets the maximum number of significant bits in the uncompressed representation
* for the value in each band of each pixel.
*
* @param newValue the new maximum number of significant bits.
* @throws IllegalArgumentException if the given value is zero or negative.
*/
public void setBitsPerValue(final Integer newValue) {
checkWritePermission(bitsPerValue);
if (ensurePositive(DefaultSampleDimension.class, "bitsPerValue", true, newValue)) {
bitsPerValue = newValue;
}
}
/**
* Provides the description and values of the specific range elements of a sample dimension.
* Example: missing data.
*
* @return description and values of the specific range elements.
*
* @since 1.3
*/
@Override
@XmlElement(name = "rangeElementDescription")
@XmlJavaTypeAdapter(MI_RangeElementDescription.Since2014.class)
public Collection<RangeElementDescription> getRangeElementDescriptions() {
return rangeElementDescriptions = nonNullCollection(rangeElementDescriptions, RangeElementDescription.class);
}
/**
* Sets the description and values of the specific range elements of a sample dimension.
*
* @param newValues the new range element description.
*
* @since 1.3
*/
public void setRangeElementDescriptions(final Collection<? extends RangeElementDescription> newValues) {
rangeElementDescriptions = writeCollection(newValues, rangeElementDescriptions, RangeElementDescription.class);
}
/**
* Returns the smallest distance between which separate points can be distinguished,
* as specified in instrument design.
*
* <h4>Note on XML marshalling</h4>
* ISO 19115-2 defines this property in {@linkplain DefaultBand a subtype} for historical reasons.
* Apache SIS moves this property up in the hierarchy since this property can apply to any sample dimension,
* not only the measurements in the electromagnetic spectrum. However, this property will not appear in XML
* documents unless this {@code SampleDimension} is actually a {@code Band}.
*
* @return smallest distance between which separate points can be distinguished, or {@code null}.
*/
@Override
@ValueRange(minimum = 0, isMinIncluded = false)
public Double getNominalSpatialResolution() {
return nominalSpatialResolution;
}
/**
* Sets the smallest distance between which separate points can be distinguished,
* as specified in instrument design.
*
* @param newValue the new nominal spatial resolution.
* @throws IllegalArgumentException if the given value is negative.
*/
public void setNominalSpatialResolution(final Double newValue) {
checkWritePermission(nominalSpatialResolution);
if (ensurePositive(DefaultSampleDimension.class, "nominalSpatialResolution", true, newValue)) {
nominalSpatialResolution = newValue;
}
}
/**
* Returns type of other attribute description.
*
* @return type of other attribute description, or {@code null} if none.
*/
@Override
@XmlElement(name = "otherPropertyType")
@XmlJavaTypeAdapter(GO_RecordType.Since2014.class)
public RecordType getOtherPropertyType() {
return otherPropertyType;
}
/**
* Sets a new type of other attribute description.
*
* @param newValue the new type of other attribute description.
*/
public void setOtherPropertyType(final RecordType newValue) {
checkWritePermission(otherPropertyType);
otherPropertyType = newValue;
}
/**
* Returns instance of other/attributeType that defines attributes not explicitly
* included in {@link CoverageContentType}, or {@code null} if none.
*
* @return instance of other/attributeType that defines attributes, or {@code null} if none.
*/
@Override
@XmlElement(name = "otherProperty")
@XmlJavaTypeAdapter(GO_Record.Since2014.class)
public Record getOtherProperty() {
return otherProperty;
}
/**
* Sets a new instance of other/attributeType that defines attributes not explicitly
* included in {@link CoverageContentType}.
*
* @param newValue the new instance of other/attributeType.
*/
public void setOtherProperty(final Record newValue) {
checkWritePermission(otherProperty);
otherProperty = newValue;
}
}