This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit b4d5305eec7694ed140062941f3f3da10fa73907 Author: Martin Desruisseaux <[email protected]> AuthorDate: Tue Jun 28 15:43:54 2022 +0200 Add a `GridGeometry.createImageCRS(…)` method. It gives a CRS that we can use with `CRS.findOperation(…)`. --- .../org/apache/sis/coverage/grid/GridExtent.java | 10 +- .../apache/sis/coverage/grid/GridExtentCRS.java | 266 ++++++++++++++++++--- .../org/apache/sis/coverage/grid/GridGeometry.java | 39 +++ .../org/apache/sis/internal/feature/Resources.java | 5 + .../sis/internal/feature/Resources.properties | 1 + .../sis/internal/feature/Resources_fr.properties | 1 + .../apache/sis/coverage/grid/GridGeometryTest.java | 28 ++- .../org/apache/sis/parameter/ParameterFormat.java | 18 +- .../org/apache/sis/parameter/package-info.java | 2 +- .../main/java/org/apache/sis/io/package-info.java | 2 +- 10 files changed, 331 insertions(+), 41 deletions(-) diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java index 838e443331..6b40ce9100 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtent.java @@ -961,6 +961,14 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable return Optional.ofNullable((types != null) ? types[index] : null); } + /** + * Returns the {@link #types} array or a default array of arbitrary length if {@link #types} is null. + * This method returns directly the arrays without cloning; do not modify. + */ + final DimensionNameType[] getAxisTypes() { + return (types != null) ? types : DEFAULT_TYPES; + } + /** * Returns the axis number followed by the localized axis type if available. * This is used for error messages only. @@ -1028,7 +1036,7 @@ public class GridExtent implements GridEnvelope, LenientComparable, Serializable final GeneralEnvelope envelope = toEnvelope(cornerToCRS, cornerToCRS, null); final Matrix gridToCRS = MathTransforms.getMatrix(cornerToCRS); if (gridToCRS != null && Matrices.isAffine(gridToCRS)) try { - envelope.setCoordinateReferenceSystem(GridExtentCRS.build(gridToCRS, (types != null) ? types : DEFAULT_TYPES, null)); + envelope.setCoordinateReferenceSystem(GridExtentCRS.forExtentAlone(gridToCRS, getAxisTypes())); } catch (FactoryException e) { throw new TransformException(e); } diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java index 887e02d002..72e9e2a838 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridExtentCRS.java @@ -17,51 +17,200 @@ package org.apache.sis.coverage.grid; import java.util.Map; +import java.util.HashMap; import java.util.Collections; import java.util.Locale; -import org.opengis.metadata.spatial.DimensionNameType; import org.opengis.util.FactoryException; +import org.opengis.util.InternationalString; +import org.opengis.metadata.spatial.DimensionNameType; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.parameter.ParameterDescriptor; +import org.opengis.parameter.ParameterDescriptorGroup; +import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.cs.CSFactory; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.CRSFactory; +import org.opengis.referencing.crs.SingleCRS; +import org.opengis.referencing.crs.CompoundCRS; +import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.crs.EngineeringCRS; -import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; +import org.opengis.referencing.operation.Conversion; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.MathTransform; +import org.opengis.referencing.operation.NoninvertibleTransformException; +import org.apache.sis.metadata.iso.extent.DefaultExtent; +import org.apache.sis.metadata.iso.citation.Citations; +import org.apache.sis.parameter.ParameterBuilder; import org.apache.sis.referencing.cs.AbstractCS; import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.referencing.NamedIdentifier; +import org.apache.sis.referencing.crs.DefaultDerivedCRS; +import org.apache.sis.referencing.operation.DefaultConversion; +import org.apache.sis.referencing.operation.DefaultOperationMethod; +import org.apache.sis.referencing.operation.transform.TransformSeparator; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.referencing.AxisDirections; +import org.apache.sis.internal.feature.Resources; import org.apache.sis.util.resources.Vocabulary; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.Characters; +import org.apache.sis.util.Classes; import org.apache.sis.util.iso.Types; import org.apache.sis.measure.Units; -import org.apache.sis.util.Characters; /** - * Builds the engineering coordinate reference system of a {@link GridExtent}. - * This is used only in the rare cases where we need to represent an extent as an envelope. - * This class converts {@link DimensionNameType} codes into axis names, abbreviations and directions. - * It is the converse of {@link GridExtent#typeFromAxes(CoordinateReferenceSystem, int)}. + * Builder for coordinate reference system which is derived from the coverage CRS by the inverse + * of the "grid to CRS" transform. Those CRS describe coordinates associated to the grid extent. + * This class provides two factory methods: + * + * <ul> + * <li>{@link #forCoverage(String, GridGeometry)}</li> + * <li>{@link #forExtentAlone(Matrix, DimensionNameType[])}</li> + * </ul> * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.2 + * @version 1.3 * @since 1.0 * @module */ final class GridExtentCRS { + /** + * Name of the parameter where to store the grid coverage name. + */ + private static final String NAME_PARAM = "Target grid name"; + + /** + * Name of the parameter specifying the way image indices are + * associated with the coverage data attributes. + */ + private static final String ANCHOR_PARAM = "Pixel in cell"; + + /** + * Description of "CRS to grid indices" operation method. + */ + private static final OperationMethod METHOD; + static { + final ParameterBuilder b = new ParameterBuilder().setRequired(true); + final ParameterDescriptor<?> name = b.addName(NAME_PARAM).create(String.class, null); + final ParameterDescriptor<?> anchor = b.addName(ANCHOR_PARAM).create(PixelInCell.class, PixelInCell.CELL_CENTER); + final ParameterDescriptorGroup params = b.addName("CRS to grid indices").createGroup(name, anchor); + METHOD = new DefaultOperationMethod(properties(params.getName()), params); + } + + /** + * Scope of usage of the CRS. + * This is a localized text saying "Conversions from coverage CRS to grid cell indices." + */ + private static final InternationalString SCOPE = Resources.formatInternational(Resources.Keys.CrsToGridConversion); + + /** + * Name of the coordinate systems created by this class. + */ + private static final NamedIdentifier CS_NAME = new NamedIdentifier( + Citations.SIS, Vocabulary.formatInternational(Vocabulary.Keys.GridExtent)); + /** * Do not allow instantiation of this class. */ private GridExtentCRS() { } + /** + * Creates a derived CRS for the grid extent of a grid coverage. + * + * <h4>Limitation</h4> + * If the CRS is compound, then this method takes only the first single CRS element. + * This is a restriction imposed by {@link DerivedCRS} API. + * As a result, the returned CRS may cover only the 2 or 3 first grid dimensions. + * + * @param name name of the CRS to create. + * @param gg grid geometry of the coverage. + * @param anchor the cell part to map (center or corner). + * @param locale locale to use for axis names, or {@code null} for default. + * @return a derived CRS for coordinates (cell indices) associated to the grid extent. + * @throws FactoryException if an error occurred during the use of {@link CSFactory} or {@link CRSFactory}. + */ + static DerivedCRS forCoverage(final String name, final GridGeometry gg, final PixelInCell anchor, final Locale locale) + throws FactoryException, NoninvertibleTransformException + { + /* + * Get the first `SingleCRS` instance (see "limitations" in method javadoc). + */ + CoordinateReferenceSystem crs = gg.getCoordinateReferenceSystem(); + boolean reduce = false; + while (!(crs instanceof SingleCRS)) { + if (!(crs instanceof CompoundCRS)) { + throw unsupported(locale, crs); + } + crs = ((CompoundCRS) crs).getComponents().get(0); + reduce = true; + } + /* + * If we took only a subset of CRS dimensions, take the same subset + * of "grid to CRS" dimensions and list of grid axes. + */ + MathTransform gridToCRS = gg.getGridToCRS(anchor); + DimensionNameType[] types = gg.getExtent().getAxisTypes(); + if (reduce) { + final TransformSeparator s = new TransformSeparator(gridToCRS); + s.addTargetDimensionRange(0, crs.getCoordinateSystem().getDimension()); + gridToCRS = s.separate(); + final int[] src = s.getSourceDimensions(); + final DimensionNameType[] allTypes = types; + types = new DimensionNameType[src.length]; + for (int i=0; i<src.length; i++) { + final int j = src[i]; + if (j < allTypes.length) { + types[i] = allTypes[j]; + } + } + } + /* + * Build the coordinate system assuming a null (identity) "grid to CRS" matrix + * because we are building the CS for the grid, not for the transformed envelope. + */ + final CoordinateSystem cs = createCS(gridToCRS.getSourceDimensions(), null, types, locale); + if (cs == null) { + throw unsupported(locale, crs); + } + /* + * Put everything together: parameters, conversion and finally the derived CRS. + */ + final HashMap<String,Object> properties = new HashMap<>(8); + properties.put(IdentifiedObject.NAME_KEY, METHOD.getName()); + properties.put(DefaultConversion.LOCALE_KEY, locale); + properties.put(Conversion.SCOPE_KEY, SCOPE); + gg.getGeographicExtent().ifPresent((domain) -> { + properties.put(Conversion.DOMAIN_OF_VALIDITY_KEY, + new DefaultExtent(null, domain, null, null)); + }); + final ParameterValueGroup params = METHOD.getParameters().createValue(); + params.parameter(NAME_PARAM).setValue(name); + params.parameter(ANCHOR_PARAM).setValue(anchor); + final Conversion conversion = new DefaultConversion(properties, METHOD, gridToCRS.inverse(), params); + properties.put(IdentifiedObject.NAME_KEY, name); + return DefaultDerivedCRS.create(properties, (SingleCRS) crs, conversion, cs); + } + + /** + * Returns the exception to throw for an unsupported CRS. + */ + private static FactoryException unsupported(final Locale locale, final CoordinateReferenceSystem crs) { + return new FactoryException(Errors.getResources(locale) + .getString(Errors.Keys.UnsupportedType_1, Classes.getShortClassName(crs))); + } + /** * Creates a properties map to give to CS, CRS or datum constructors. */ private static Map<String,?> properties(final Object name) { - return Collections.singletonMap(CoordinateSystemAxis.NAME_KEY, name); + return Collections.singletonMap(IdentifiedObject.NAME_KEY, name); } /** @@ -85,22 +234,24 @@ final class GridExtentCRS { } /** - * Builds a coordinate reference system for the given axis types. The CRS type is always engineering. - * We can not create temporal CRS because we do not know the temporal datum origin. + * Creates the coordinate system for engineering CRS. * + * @param tgtDim number of dimensions of the coordinate system to create. * @param gridToCRS matrix of the transform used for converting grid cell indices to envelope coordinates. * It does not matter whether it maps pixel center or corner (translation coefficients are ignored). + * A {@code null} means to handle as an identity transform. * @param types the value of {@link GridExtent#types} or a default value (shall not be {@code null}). * @param locale locale to use for axis names, or {@code null} for default. - * @return CRS for the grid, or {@code null}. - * - * @see GridExtent#typeFromAxes(CoordinateReferenceSystem, int) + * @return coordinate system for the grid extent, or {@code null} if it can not be inferred. + * @throws FactoryException if an error occurred during the use of {@link CSFactory}. */ - static EngineeringCRS build(final Matrix gridToCRS, final DimensionNameType[] types, final Locale locale) - throws FactoryException + private static CoordinateSystem createCS(final int tgtDim, final Matrix gridToCRS, + final DimensionNameType[] types, final Locale locale) throws FactoryException { - final int tgtDim = gridToCRS.getNumRow() - 1; - final int srcDim = Math.min(gridToCRS.getNumCol() - 1, types.length); + int srcDim = types.length; // Used only for inspecting names. No need to be accurate. + if (gridToCRS != null) { + srcDim = Math.min(gridToCRS.getNumCol() - 1, srcDim); + } final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[tgtDim]; final CSFactory csFactory = DefaultFactories.forBuildin(CSFactory.class); boolean hasVertical = false; @@ -115,20 +266,23 @@ final class GridExtentCRS { * Current version does not accept scale factors, but we could revisit * in a future version if there is a need for it. */ - int target = -1; + int target = i; double scale = 0; - for (int j=0; j<tgtDim; j++) { - final double m = gridToCRS.getElement(j, i); - if (m != 0) { - if (target >= 0 || axes[j] != null || Math.abs(m) != 1) { - return null; + if (gridToCRS != null) { + target = -1; + for (int j=0; j<tgtDim; j++) { + final double m = gridToCRS.getElement(j, i); + if (m != 0) { + if (target >= 0 || axes[j] != null || Math.abs(m) != 1) { + return null; + } + target = j; + scale = m; } - target = j; - scale = m; } - } - if (target < 0) { - return null; + if (target < 0) { + return null; + } } /* * This hard-coded set of axis directions is the converse of @@ -189,7 +343,7 @@ final class GridExtentCRS { * If no specialized type seems to fit, use an unspecified ("abstract") * coordinate system type in last resort. */ - final Map<String,?> properties = properties("Grid extent"); + final Map<String,?> properties = properties(CS_NAME); final CoordinateSystem cs; if (hasOther || (tgtDim > (hasTime ? 1 : 3))) { cs = new AbstractCS(properties, axes); @@ -205,9 +359,55 @@ final class GridExtentCRS { } break; } - case 2: cs = csFactory.createAffineCS(properties, axes[0], axes[1]); break; - case 3: cs = csFactory.createAffineCS(properties, axes[0], axes[1], axes[2]); break; - default: return null; + case 2: { + /* + * A null `gridToCRS` means that we are creating a CS for the grid, which is assumed a + * Cartesian space. A non-null value means that we are creating a CRS for a transformed + * envelope, in which case the CS type is not really known. + */ + cs = (gridToCRS == null) + ? csFactory.createCartesianCS(properties, axes[0], axes[1]) + : csFactory.createAffineCS (properties, axes[0], axes[1]); + break; + } + case 3: { + cs = (gridToCRS == null) + ? csFactory.createCartesianCS(properties, axes[0], axes[1], axes[2]) + : csFactory.createAffineCS (properties, axes[0], axes[1], axes[2]); + break; + } + default: { + cs = null; + break; + } + } + return cs; + } + + /** + * Builds the engineering coordinate reference system of a {@link GridExtent}. + * This is used only in the rare cases where we need to represent an extent as an envelope. + * This class converts {@link DimensionNameType} codes into axis names, abbreviations and directions. + * It is the converse of {@link GridExtent#typeFromAxes(CoordinateReferenceSystem, int)}. + * + * <p>The CRS type is always engineering. + * We can not create temporal CRS because we do not know the temporal datum origin.</p> + * + * @param gridToCRS matrix of the transform used for converting grid cell indices to envelope coordinates. + * It does not matter whether it maps pixel center or corner (translation coefficients are ignored). + * @param types the value of {@link GridExtent#types} or a default value (shall not be {@code null}). + * @param locale locale to use for axis names, or {@code null} for default. + * @return CRS for the grid, or {@code null}. + * @throws FactoryException if an error occurred during the use of {@link CSFactory} or {@link CRSFactory}. + * + * @see GridExtent#typeFromAxes(CoordinateReferenceSystem, int) + */ + static EngineeringCRS forExtentAlone(final Matrix gridToCRS, final DimensionNameType[] types) + throws FactoryException + { + final CoordinateSystem cs = createCS(gridToCRS.getNumRow() - 1, gridToCRS, types, null); + if (cs == null) { + return null; } return DefaultFactories.forBuildin(CRSFactory.class).createEngineeringCRS( properties(cs.getName()), CommonCRS.Engineering.GRID.datum(), cs); diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java index dffde720ea..f3b1730a0e 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java +++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java @@ -35,7 +35,9 @@ import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.NoninvertibleTransformException; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.cs.CoordinateSystem; import org.apache.sis.math.MathFunctions; @@ -1468,6 +1470,43 @@ public class GridGeometry implements LenientComparable, Serializable { return this; } + /** + * Creates a one-, two- or three-dimensional coordinate reference system for cell indices in the grid. + * This method returns a CRS which is derived from the "real world" CRS or a subset of it. + * If the "real world" CRS is an instance of {@link org.opengis.referencing.crs.SingleCRS}, + * then the derived CRS has the following properties: + * + * <ul> + * <li>{@link DerivedCRS#getBaseCRS()} is {@link #getCoordinateReferenceSystem()}.</li> + * <li>{@link DerivedCRS#getConversionFromBase()} is the inverse of {@link #getGridToCRS(PixelInCell)}.</li> + * </ul> + * + * Otherwise if the "real world" CRS is an instance of {@link org.opengis.referencing.crs.CompoundCRS}, + * then only the first {@link org.opengis.referencing.crs.SingleCRS} (the head) is used. + * This is usually (but not necessarily) the horizontal component of the spatial CRS. + * The result is usually two-dimensional, but 1 and 3 dimensions are also possible. + * + * <p>Because of above relationship, it is possible to use the derived CRS in a chain of operations + * with (for example) {@link org.apache.sis.referencing.CRS#findOperation CRS.findOperation(…)}.</p> + * + * @param name name of the CRS to create. + * @param anchor the cell part to map (center or corner). + * @return a derived CRS for coordinates (cell indices) associated to the grid extent. + * @throws IncompleteGridGeometryException if the CRS, grid extent or "grid to CRS" transform is missing. + * + * @since 1.3 + */ + public DerivedCRS createImageCRS(final String name, final PixelInCell anchor) { + ArgumentChecks.ensureNonEmpty("name", name); + try { + return GridExtentCRS.forCoverage(name, this, anchor, null); + } catch (FactoryException e) { + throw new BackingStoreException(e); + } catch (NoninvertibleTransformException e) { + throw new IllegalStateException(e); + } + } + /** * Creates a transform from cell coordinates in this grid to cell coordinates in the given grid. * The returned transform handles change of Coordinate Reference System and wraparound axes diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java index 71d22be341..d3072e00a9 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.java @@ -149,6 +149,11 @@ public final class Resources extends IndexedResourceBundle { */ public static final short CharacteristicsNotFound_2 = 17; + /** + * Conversions from coverage CRS to grid cell indices. + */ + public static final short CrsToGridConversion = 79; + /** * Operation “{0}” requires a “{1}” property, but no such property has been found in “{2}”. */ diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties index b2a78dd762..87a953b6da 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources.properties @@ -37,6 +37,7 @@ CanNotVisit_2 = Can not visit a \u201c{1}\u201d {0,choice,0# CategoryRangeOverlap_4 = The two categories \u201c{0}\u201d and \u201c{2}\u201d have overlapping ranges: {1} and {3} respectively. CharacteristicsAlreadyExists_2 = Characteristics \u201c{1}\u201d already exists in attribute \u201c{0}\u201d. CharacteristicsNotFound_2 = No characteristics named \u201c{1}\u201d has been found in \u201c{0}\u201d attribute. +CrsToGridConversion = Conversions from coverage CRS to grid cell indices. DependencyNotFound_3 = Operation \u201c{0}\u201d requires a \u201c{1}\u201d property, but no such property has been found in \u201c{2}\u201d. EmptyImage = Image has zero pixel. EmptyTileOrImageRegion = Empty tile or image region. diff --git a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties index 40d9f79027..951a7ad66b 100644 --- a/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties +++ b/core/sis-feature/src/main/java/org/apache/sis/internal/feature/Resources_fr.properties @@ -42,6 +42,7 @@ CanNotVisit_2 = Ne peut pas visiter {0,choice,0#un filtre|1# CategoryRangeOverlap_4 = Les deux cat\u00e9gories \u00ab\u202f{0}\u202f\u00bb et \u00ab\u202f{2}\u202f\u00bb ont des plages de valeurs qui se chevauchent\u00a0: {1} et {3} respectivement. CharacteristicsAlreadyExists_2 = La caract\u00e9ristique \u00ab\u202f{1}\u202f\u00bb existe d\u00e9j\u00e0 dans l\u2019attribut \u00ab\u202f{0}\u202f\u00bb. CharacteristicsNotFound_2 = Aucune caract\u00e9ristique nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb n\u2019a \u00e9t\u00e9 trouv\u00e9e dans l\u2019attribut \u00ab\u202f{0}\u202f\u00bb. +CrsToGridConversion = Conversions des coordonn\u00e9es de la couverture de donn\u00e9es vers les indices des cellules de la grille. DependencyNotFound_3 = L\u2019op\u00e9ration \u00ab\u202f{0}\u202f\u00bb n\u00e9cessite une propri\u00e9t\u00e9 \u00ab\u202f{1}\u202f\u00bb, mais cette propri\u00e9t\u00e9 n\u2019a pas \u00e9t\u00e9 trouv\u00e9e dans \u00ab\u202f{2}\u202f\u00bb. EmptyImage = L\u2019image a z\u00e9ro pixel. EmptyTileOrImageRegion = La tuile ou la r\u00e9gion de l\u2019image est vide. diff --git a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java index be70a646f1..87ef02e1ac 100644 --- a/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java +++ b/core/sis-feature/src/test/java/org/apache/sis/coverage/grid/GridGeometryTest.java @@ -18,6 +18,7 @@ package org.apache.sis.coverage.grid; import org.opengis.geometry.Envelope; import org.opengis.metadata.spatial.DimensionNameType; +import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.datum.PixelInCell; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; @@ -61,7 +62,7 @@ public final strictfp class GridGeometryTest extends TestCase { * Verifies the shift between the two {@code gridToCRS} transforms. * This method should be invoked when the transforms are linear. * - * @param grid the grid geoemtry to validate. + * @param grid the grid geometry to validate. */ private static void verifyGridToCRS(final GridGeometry grid) { final Matrix tr1 = MathTransforms.getMatrix(grid.getGridToCRS(PixelInCell.CELL_CENTER)); @@ -602,6 +603,31 @@ public final strictfp class GridGeometryTest extends TestCase { assertMatrixEquals("gridToCRS", new Matrix2(0, 3, 0, 1), MathTransforms.getMatrix(tr), STRICT); } + /** + * Tests {@link GridGeometry#createImageCRS(String, PixelInCell)}. + */ + @Test + public void testCreateImageCRS() { + final GridGeometry gg = new GridGeometry( + new GridExtent(null, null, new long[] {17, 10, 4}, true), + PixelInCell.CELL_CENTER, + MathTransforms.linear(new Matrix4( + 1, 0, 0, -7, + 0, -1, 0, 50, + 0, 0, 8, 20, + 0, 0, 0, 1)), + HardCodedCRS.WGS84_WITH_TIME); + + final DerivedCRS crs = gg.createImageCRS("Horizontal part", PixelInCell.CELL_CENTER); + assertEquals("Horizontal part", crs.getName().getCode()); + final Matrix mt = MathTransforms.getMatrix(crs.getConversionFromBase().getMathTransform()); + assertSame(HardCodedCRS.WGS84, crs.getBaseCRS()); + assertMatrixEquals("CRS to grid", + new Matrix3(1, 0, 7, // Opposite sign because this is the inverse transform. + 0, -1, 50, // Opposite sign cancelled by -1 scale factor. + 0, 0, 1), mt, STRICT); + } + /** * Tests {@link GridGeometry#createTransformTo(GridGeometry, PixelInCell)}. * diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java index 15c32e6bda..1f5b3b8b2d 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/ParameterFormat.java @@ -39,6 +39,8 @@ import javax.measure.Unit; import org.opengis.parameter.*; import org.opengis.util.ScopedName; import org.opengis.util.GenericName; +import org.opengis.util.InternationalString; +import org.opengis.util.ControlledVocabulary; import org.opengis.metadata.Identifier; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.operation.OperationMethod; @@ -47,6 +49,7 @@ import org.apache.sis.measure.Range; import org.apache.sis.io.wkt.Colors; import org.apache.sis.io.TableAppender; import org.apache.sis.io.TabularFormat; +import org.apache.sis.util.iso.Types; import org.apache.sis.util.CharSequences; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.resources.Errors; @@ -105,7 +108,7 @@ import static org.apache.sis.util.collection.Containers.hashMapCapacity; * </ul> * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 0.6 + * @version 1.3 * @since 0.4 * @module */ @@ -692,7 +695,7 @@ public class ParameterFormat extends TabularFormat<Object> { table.setCellAlignment(alignment); final int length = row.values.size(); for (int i=0; i<length; i++) { - Object value = row.values.get(i); + final Object value = row.values.get(i); if (value != null) { if (i != 0) { /* @@ -715,13 +718,20 @@ public class ParameterFormat extends TabularFormat<Object> { * + unit tuple. */ final Format format = getFormat(value.getClass()); + final CharSequence text; if (format != null) { if (format instanceof NumberFormat && value instanceof Number) { configure((NumberFormat) format, Math.abs(((Number) value).doubleValue())); } - value = format.format(value, buffer, dummyFP); + text = format.format(value, buffer, dummyFP); + } else if (value instanceof ControlledVocabulary) { + text = Types.getCodeTitle((ControlledVocabulary) value).toString(getLocale()); + } else if (value instanceof InternationalString) { + text = ((InternationalString) value).toString(getLocale()); + } else { + text = value.toString(); } - table.append(value.toString()); + table.append(text); buffer.setLength(0); int pad = unitWidth; final String unit = (String) row.units.get(i); diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/package-info.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/package-info.java index 24297290e0..1f12c19016 100644 --- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/package-info.java +++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/package-info.java @@ -84,7 +84,7 @@ * if the given value is not assignable to the expected class or is not inside the value domain. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.3 * @since 0.4 * @module */ diff --git a/core/sis-utility/src/main/java/org/apache/sis/io/package-info.java b/core/sis-utility/src/main/java/org/apache/sis/io/package-info.java index 70ff2333a9..13d69245ce 100644 --- a/core/sis-utility/src/main/java/org/apache/sis/io/package-info.java +++ b/core/sis-utility/src/main/java/org/apache/sis/io/package-info.java @@ -41,7 +41,7 @@ * Unicode supplementary characters}. * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 1.1 + * @version 1.3 * @since 0.3 * @module */
