Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorParameters.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorParameters.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorParameters.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorParameters.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -20,6 +20,7 @@ import java.util.Map; import java.util.HashMap; import java.util.List; import java.util.Arrays; +import java.util.Collections; import java.io.IOException; import java.io.Serializable; import java.io.ObjectInputStream; @@ -33,10 +34,13 @@ import org.opengis.parameter.GeneralPara import org.opengis.parameter.ParameterNotFoundException; import org.opengis.parameter.InvalidParameterNameException; import org.opengis.metadata.Identifier; +import org.opengis.metadata.citation.Citation; import org.opengis.referencing.operation.Matrix; +import org.apache.sis.referencing.NamedIdentifier; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.referencing.operation.matrix.Matrices; -import org.apache.sis.internal.util.UnmodifiableArrayList; +import org.apache.sis.internal.referencing.provider.Affine; +import org.apache.sis.internal.util.Constants; import org.apache.sis.metadata.iso.citation.Citations; import org.apache.sis.measure.NumberRange; import org.apache.sis.util.Numbers; @@ -53,9 +57,13 @@ import org.apache.sis.internal.jdk7.Obje * * <p>Each group of parameters contains the following elements:</p> * <ul> - * <li>A mandatory parameter for the number of rows ({@code "num_row"} in WKT 1).</li> - * <li>A mandatory parameter for the number of columns ({@code "num_col"} in WKT 1).</li> - * <li>(<i>etc.</i> for third-order or higher-order tensors).</li> + * <li>Parameters (usually mandatory) for the tensor dimensions: + * <ul> + * <li>number of rows (named {@code "num_row"} in {@linkplain #WKT1} conventions),</li> + * <li>number of columns (named {@code "num_col"} in WKT1 conventions),</li> + * <li><i>etc.</i> for third-order or higher-order tensors.</li> + * </ul> + * </li> * <li>A maximum of {@code num_row} × {@code num_col} × … optional parameters for the matrix or tensor element values. * Parameter names depend on the formatting convention.</li> * </ul> @@ -67,11 +75,35 @@ import org.apache.sis.internal.jdk7.Obje * <p><b>Parameters are not an efficient storage format for large tensors.</b> * Parameters are used only for small matrices/tensors to be specified in coordinate operations or processing libraries. * In particular, those parameters integrate well in <cite>Well Known Text</cite> (WKT) format. - * For a more efficient matrix storage, see {@link org.apache.sis.referencing.operation.matrix.MatrixSIS}.</p> + * For a more efficient matrix storage, + * see the {@linkplain org.apache.sis.referencing.operation.matrix matrix package}.</p> * * {@section Formatting} - * The parameters format for a matrix is typically like below: + * In the particular case of a tensor of {@linkplain #rank() rank} 2 (i.e. a matrix), + * the parameters are typically formatted as below. Note that in the EPSG convention, + * the matrix is implicitly {@linkplain Matrices#isAffine affine} and of dimension 3×3. * + * <table class="sis"> + * <caption>Well Known Text (WKT) formats for matrix parameters</caption> + * <tr> + * <th>Using EPSG:9624 names and identifiers</th> + * <th class="sep">Using OGC names</th> + * </tr> + * <tr><td> + * {@preformat wkt + * Parameter["A0", <value>, Id["EPSG", 8623]], + * Parameter["A1", <value>, Id["EPSG", 8624]], + * Parameter["A2", <value>, Id["EPSG", 8625]], + * Parameter["B0", <value>, Id["EPSG", 8639]], + * Parameter["B1", <value>, Id["EPSG", 8640]], + * Parameter["B2", <value>, Id["EPSG", 8641]] + * } + * + * <div class="note"><b>Note:</b> + * the EPSG database contains also A3, A4, A5, A6, A7, A8 and B3 parameters, + * but they are for polynomial transformations, not for affine transformations.</div> + * + * </td><td class="sep"> * {@preformat wkt * Parameter["num_row", 3], * Parameter["num_col", 3], @@ -84,28 +116,38 @@ import org.apache.sis.internal.jdk7.Obje * ... * Parameter["elt_<num_row-1>_<num_col-1>", <value>] * } + * </td></tr></table> * * Those groups are extensible, i.e. the number of <code>"elt_<var>row</var>_<var>col</var>"</code> parameters * depends on the {@code "num_row"} and {@code "num_col"} parameter values. For this reason, the descriptor of * matrix or tensor parameters is not immutable. * - * {@section Usage} + * {@section Usage examples} * For creating a new group of parameters for a matrix using the {@link #WKT1} naming conventions, - * on can use the following code: + * one can use the following code: * * {@preformat java - * Map<String,?> properties = Collections.singletonMap("name", "My operation"); + * Map<String,?> properties = Collections.singletonMap(ParameterValueGroup.NAME_KEY, "Affine"); * ParameterValueGroup p = TensorParameters.WKT1.createValueGroup(properties); * } * + * For setting the elements of a few values, then create a matrix from the parameter values: + * + * {@preformat java + * p.parameter("elt_0_0").setValue(4); // "A0" also accepted as a synonymous of "elt_0_0". + * p.parameter("elt_1_1").setValue(6); // "B1" also accepted as a synonymous of "elt_1_1". + * Matrix m = TensorParameters.WKT1.toMatrix(p); + * } + * * @param <E> The type of tensor element values. * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.4 - * @version 0.4 + * @version 0.6 * @module + * + * @see org.apache.sis.referencing.operation.matrix.Matrices */ -@SuppressWarnings("unchecked") public class TensorParameters<E> implements Serializable { /** * Serial number for inter-operability with different versions. @@ -113,17 +155,70 @@ public class TensorParameters<E> impleme private static final long serialVersionUID = -7386537348359343836L; /** - * Parses and creates parameters names for matrices matching the + * Parses and creates matrix parameters with alphanumeric names. + * {@linkplain DefaultParameterDescriptor#getName() Names} are made of a letter indicating the row + * (first row is {@code "A"}), followed by a digit indicating the column index (first column is {@code "0"}). + * {@linkplain DefaultParameterDescriptor#getAlias() Aliases} are the names as they were defined in version 1 + * of <cite>Well Known Text</cite> (WKT) format. + * + * <table class="sis"> + * <caption>Parameter names for a 3×3 matrix</caption> + * <tr> + * <th>Primary name</th> + * <th class="sep">Alias</th> + * </tr> + * <tr><td> + * {@preformat text + * ┌ ┐ + * │ A0 A1 A2 │ + * │ B0 B1 B2 │ + * │ C0 C1 C2 │ + * └ ┘ + * }</td><td class="sep"> + * {@preformat text + * ┌ ┐ + * │ elt_0_0 elt_0_1 elt_0_2 │ + * │ elt_1_0 elt_1_1 elt_1_2 │ + * │ elt_2_0 elt_2_1 elt_2_2 │ + * └ ┘ + * }</td></tr> + * </table> + * + * {@section Relationship with EPSG} + * The above-cited group of parameters are close, but not identical, to the definitions provided + * by the <cite>"Affine general parametric transformation"</cite> (EPSG:9624) operation method. + * The differences are: + * + * <ul> + * <li>EPSG:9624 is for matrices of size 3×3 and does not provide any way to specify the matrix size. + * This {@code ALPHANUM} convention extends the definition to matrices of arbitrary size and accepts + * {@code "num_row"} and {@code "num_col"} as optional parameters.</li> + * <li>EPSG:9624 is restricted to affine matrices and consequently define parameters only for the two + * first rows. This class accepts also parameters for the last row (namely {@code "C0"}, {@code "C1"} + * and {@code "C2"} in a 3×3 matrices).</li> + * </ul> + * + * Because of the above-cited extensions, this {@code TensorParameters} constant can not be named {@code EPSG}. + * + * @since 0.6 + */ + public static final TensorParameters<Double> ALPHANUM; + + /** + * Parses and creates matrix parameters with names matching the * <a href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html">Well Known Text</a> * version 1 (WKT 1) convention. * * <ul> * <li>First parameter is {@code "num_row"}.</li> * <li>Second parameter is {@code "num_col"}.</li> - * <li>All other parameters are of the form <code>"elt_<var>row</var>_<var>col</var>"</code>.</li> + * <li>All other parameters are of the form <code>"elt_</code><var>row</var><code>_</code><var>col</var><code>"</code>. + * Those parameters have alias of the form {@code "A0"}, {@code "A1"}, <i>etc.</i> where the letter indicates + * the row (first row is {@code "A"}) and the digit is the column index (first column is {@code "0"}).</li> * </ul> * - * <div class="note"><b>Example:</b> {@code "elt_2_1"} is the element name for the value at line 2 and row 1.</div> + * <div class="note"><b>Example:</b> {@code "elt_1_2"} is the element name for the value at row 1 and column 2. + * Its alias is {@code "B2"}, which is the EPSG name for the same parameter.</div> */ public static final TensorParameters<Double> WKT1; static { @@ -135,15 +230,29 @@ public class TensorParameters<E> impleme * elements. */ final NumberRange<Integer> valueDomain = NumberRange.create(1, true, 50, true); - final Integer defaultSize = 3; - final ParameterDescriptor<Integer> numRow, numCol; + final Integer defaultSize = Affine.EPSG_DIMENSION + 1; + /* + * For the WKT1 convention, the "num_row" and "num_col" parameters are mandatory. + */ final Map<String,Object> properties = new HashMap<String,Object>(4); properties.put(Identifier.AUTHORITY_KEY, Citations.OGC); - properties.put(Identifier.CODE_KEY, "num_row"); - numRow = new DefaultParameterDescriptor<Integer>(properties, 1, 1, Integer.class, valueDomain, null, defaultSize); - properties.put(Identifier.CODE_KEY, "num_col"); - numCol = new DefaultParameterDescriptor<Integer>(properties, 1, 1, Integer.class, valueDomain, null, defaultSize); - WKT1 = new TensorParameters<Double>(Double.class, "elt_", "_", numRow, numCol); + properties.put(Identifier.CODE_KEY, Constants.NUM_ROW); + ParameterDescriptor<Integer> numRow = new DefaultParameterDescriptor<Integer>( + properties, 1, 1, Integer.class, valueDomain, null, defaultSize); + properties.put(Identifier.CODE_KEY, Constants.NUM_COL); + ParameterDescriptor<Integer> numCol = new DefaultParameterDescriptor<Integer>( + properties, 1, 1, Integer.class, valueDomain, null, defaultSize); + WKT1 = new MatrixParameters(numRow, numCol); + /* + * For the EPSG convention, there is no "num_row" or "num_col" parameters since the matrix + * size if fixed to 3×3. However since we still need them, we will declare them as optional + * and we will hide them from the descriptor unless the matrix size is different than 3×3. + */ + numRow = new DefaultParameterDescriptor<Integer>(IdentifiedObjects.getProperties(numRow), + 0, 1, Integer.class, valueDomain, null, defaultSize); + numCol = new DefaultParameterDescriptor<Integer>(IdentifiedObjects.getProperties(numCol), + 0, 1, Integer.class, valueDomain, null, defaultSize); + ALPHANUM = new MatrixParametersAlphaNum(numRow, numCol); } /** @@ -206,7 +315,7 @@ public class TensorParameters<E> impleme * @param dimensions The parameter for the size of each dimension, usually in an array of length 2. * Length may be different if the caller wants to generalize usage of this class to tensors. */ - @SuppressWarnings("rawtypes") + @SuppressWarnings({"unchecked", "rawtypes"}) public TensorParameters(final Class<E> elementType, final String prefix, final String separator, final ParameterDescriptor<Integer>... dimensions) { @@ -234,7 +343,7 @@ public class TensorParameters<E> impleme * * <p>This method is invoked by constructor and on deserialization.</p> */ - @SuppressWarnings("rawtypes") + @SuppressWarnings({"unchecked", "rawtypes"}) private <T> ParameterDescriptor<T>[] createCache() { if (Number.class.isAssignableFrom(elementType)) try { one = (E) Numbers.wrap(1, (Class) elementType); @@ -243,7 +352,7 @@ public class TensorParameters<E> impleme // Ignore - zero and one will be left to null. } int length = 1; - for (int i = Math.min(dimensions.length, CACHE_RANK); --i >= 0;) { + for (int i = Math.min(rank(), CACHE_RANK); --i >= 0;) { length *= CACHE_SIZE; } return new ParameterDescriptor[length]; @@ -264,10 +373,10 @@ public class TensorParameters<E> impleme * * <table class="sis"> * <caption>Tensor types implied by rank</caption> - * <tr><th>Rank</th> <th>Type</th></tr> - * <tr><td>0</td> <td>scalar</td></tr> - * <tr><td>1</td> <td>vector</td></tr> - * <tr><td>2</td> <td>matrix</td></tr> + * <tr><th>Rank</th> <th>Type</th> <th>Used with</th></tr> + * <tr><td>0</td> <td>scalar</td> <td></td></tr> + * <tr><td>1</td> <td>vector</td> <td></td></tr> + * <tr><td>2</td> <td>matrix</td> <td>Affine general parametric transformation</td></tr> * <tr><td><var>k</var></td><td>rank <var>k</var> tensor</td></tr> * </table> * @@ -278,10 +387,23 @@ public class TensorParameters<E> impleme } /** + * Verifies that the length of the given array is equals to the tensor rank. + */ + private void verifyRank(final int[] indices) { + if (indices.length != rank()) { + throw new IllegalArgumentException(Errors.format( + Errors.Keys.UnexpectedArrayLength_2, rank(), indices.length)); + } + } + + /** * Returns the parameter descriptor for the dimension at the given index. * * @param i The dimension index, from 0 inclusive to {@link #rank()} exclusive. * @return The parameter descriptor for the dimension at the given index. + * + * @see #getElementDescriptor(int...) + * @see #getAllDescriptors(int...) */ public final ParameterDescriptor<Integer> getDimensionDescriptor(final int i) { return dimensions[i]; @@ -296,8 +418,12 @@ public class TensorParameters<E> impleme * @param indices The indices of the tensor element for which to get the descriptor. * @return The parameter descriptor for the given tensor element. * @throws IllegalArgumentException If the given array does not have the expected length or have illegal value. + * + * @see #getDimensionDescriptor(int) + * @see #getAllDescriptors(int...) */ public final ParameterDescriptor<E> getElementDescriptor(final int... indices) { + verifyRank(indices); final int cacheIndex = cacheIndex(indices); if (cacheIndex >= 0) { final ParameterDescriptor<E> param; @@ -310,7 +436,7 @@ public class TensorParameters<E> impleme } /* * Parameter not found in the cache. Create a new one and cache it for future reuse. - * Note that an other thread could have created the same descriptor in the main time, + * Note that an other thread could have created the same descriptor in the meantime, * so we will need to check again. */ final ParameterDescriptor<E> param = createElementDescriptor(indices); @@ -333,6 +459,7 @@ public class TensorParameters<E> impleme int cacheIndex = 0; for (int i=0; i<indices.length; i++) { final int index = indices[i]; + ArgumentChecks.ensurePositive("indices", index); if (i < CACHE_RANK) { if (index >= 0 && index < CACHE_SIZE) { cacheIndex = (cacheIndex * CACHE_SIZE) + index; @@ -351,47 +478,41 @@ public class TensorParameters<E> impleme * This method is invoked by {@link #getElementDescriptor(int[])} when a new descriptor needs * to be created. * - * {@section Default implementation} + * <p><b>Default implementation</b></p> * The default implementation converts the given indices to a parameter name by invoking the * {@link #indicesToName(int[])} method, then creates a descriptor for an optional parameter - * of that name. + * of that name. The default value is given by {@link #getDefaultValue(int[])}. * - * {@section Subclassing} + * <p><b>Subclassing</b></p> * Subclasses can override this method if they want more control on descriptor properties - * like identification information, value domain and default values. + * like identification information, aliases or value domain. * * @param indices The indices of the tensor element for which to create a parameter. * @return The parameter descriptor for the given tensor element. * @throws IllegalArgumentException If the given array does not have the expected length or have illegal value. * * @see #indicesToName(int[]) - * @see #nameToIndices(String) + * @see #getDefaultValue(int[]) */ protected ParameterDescriptor<E> createElementDescriptor(final int[] indices) throws IllegalArgumentException { - boolean isDiagonal = true; - for (int i=1; i<indices.length; i++) { - if (indices[i] != indices[i-1]) { - isDiagonal = false; - break; - } - } - final Map<String,Object> properties = new HashMap<String,Object>(4); - properties.put(Identifier.CODE_KEY, indicesToName(indices)); - properties.put(Identifier.AUTHORITY_KEY, dimensions[0].getName().getAuthority()); - return new DefaultParameterDescriptor<E>(properties, 0, 1, elementType, null, null, isDiagonal ? one : zero); + final Citation authority = dimensions[0].getName().getAuthority(); + final String name = indicesToName(indices); + return new DefaultParameterDescriptor<E>( + Collections.singletonMap(ParameterDescriptor.NAME_KEY, new NamedIdentifier(authority, name)), + 0, 1, elementType, null, null, getDefaultValue(indices)); } /** * Returns the parameter descriptor name of a matrix or tensor element at the given indices. * The returned name shall be parsable by the {@link #nameToIndices(String)} method. * - * {@section Default implementation} + * <p><b>Default implementation</b></p> * The default implementation requires an {@code indices} array having a length equals to the {@linkplain #rank() * rank}. That length is usually 2, where {@code indices[0]} is the <var>row</var> index and {@code indices[1]} is * the <var>column</var> index. Then this method builds a name with the “{@link #prefix} + <var>row</var> + * {@link #separator} + <var>column</var> + …” pattern (e.g. {@code "elt_0_0"}). * - * {@section Subclassing} + * <p><b>Subclassing</b></p> * If a subclass overrides this method for creating different names, then that subclass shall * also override {@link #nameToIndices(String)} for parsing those names. * @@ -400,10 +521,7 @@ public class TensorParameters<E> impleme * @throws IllegalArgumentException If the given array does not have the expected length or have illegal value. */ protected String indicesToName(final int[] indices) throws IllegalArgumentException { - if (indices.length != dimensions.length) { - throw new IllegalArgumentException(Errors.format( - Errors.Keys.UnexpectedArrayLength_2, dimensions.length, indices.length)); - } + verifyRank(indices); final StringBuilder name = new StringBuilder(); String s = prefix; for (final int i : indices) { @@ -417,7 +535,7 @@ public class TensorParameters<E> impleme * Returns the indices of matrix element for the given parameter name, or {@code null} if none. * This method is the converse of {@link #indicesToName(int[])}. * - * {@section Default implementation} + * <p><b>Default implementation</b></p> * The default implementation expects a name matching the “{@link #prefix} + <var>row</var> + {@link #separator} + * <var>column</var> + …” pattern and returns an array containing the <var>row</var>, <var>column</var> and other * indices, in that order. @@ -432,7 +550,7 @@ public class TensorParameters<E> impleme if (!name.regionMatches(true, 0, prefix, 0, s)) { return null; } - final int[] indices = new int[dimensions.length]; + final int[] indices = new int[rank()]; final int last = indices.length - 1; for (int i=0; i<last; i++) { final int split = name.indexOf(separator, s); @@ -447,9 +565,30 @@ public class TensorParameters<E> impleme } /** + * Returns the default value for the parameter descriptor at the given indices. + * The default implementation returns 1 if all indices are equals, or 0 otherwise. + * + * @param indices The indices of the tensor element for which to get the default value. + * @return The default value for the tensor element at the given indices, or {@code null} if none. + * + * @see DefaultParameterDescriptor#getDefaultValue() + * + * @since 0.6 + */ + protected E getDefaultValue(final int[] indices) { + for (int i=1; i<indices.length; i++) { + if (indices[i] != indices[i-1]) { + return zero; + } + } + return one; + } + + /** * Returns the descriptor in this group for the specified name. * - * @param name The case insensitive name of the parameter to search for. + * @param caller The {@link TensorValues} instance invoking this method, used only in case of errors. + * @param name The case insensitive name of the parameter to search for. * @param actualSize The current values of parameters that define the matrix (or tensor) dimensions. * @return The parameter for the given name. * @throws ParameterNotFoundException if there is no parameter for the given name. @@ -498,35 +637,52 @@ public class TensorParameters<E> impleme } /** + * Returns the number of elements (e.g. {@code "elt_0_0"}) when formatting the parameter descriptors + * for a tensor of the given size. This is the total number of elements in the tensor. + */ + private int numElements(final int[] actualSize) { + int n = 1; + for (int s : actualSize) { + ArgumentChecks.ensurePositive("actualSize", s); + n *= s; + } + return n; + } + + /** * Returns all parameters in this group for a tensor of the specified dimensions. + * The returned array contains all descriptors returned by {@link #getDimensionDescriptor(int)} + * and {@link #getElementDescriptor(int...)}. * - * @param actualSize The current values of parameters that define the matrix (or tensor) dimensions. - * It is caller's responsibility to ensure that this array does not contain negative values. - * @return The matrix parameters, including all elements. - */ - final List<GeneralParameterDescriptor> descriptors(final int[] actualSize) { - final int rank = dimensions.length; // 2 for a matrix, may be higher for a tensor. - int length = actualSize[0]; - for (int i=1; i<rank; i++) { - length *= actualSize[i]; - } - final GeneralParameterDescriptor[] parameters = new GeneralParameterDescriptor[rank + length]; - System.arraycopy(dimensions, 0, parameters, 0, rank); - final int[] indices = new int[rank]; + * @param actualSize The matrix (or tensor) dimensions for which to get the parameters. + * @return The tensor parameters, including all elements. + * + * @see #getDimensionDescriptor(int) + * @see #getElementDescriptor(int...) + * + * @since 0.6 + */ + public ParameterDescriptor<?>[] getAllDescriptors(final int... actualSize) { + verifyRank(actualSize); + final int numDimensions = actualSize.length; + final int numElements = numElements(actualSize); + final ParameterDescriptor<?>[] parameters = new ParameterDescriptor<?>[numDimensions + numElements]; + System.arraycopy(dimensions, 0, parameters, 0, numDimensions); + final int[] indices = new int[rank()]; /* * Iterates on all possible index values. Indes on the right side (usually the column index) * will vary faster, and index on the left side (usually the row index) will vary slowest. */ - for (int i=0; i<length; i++) { - parameters[rank + i] = getElementDescriptor(indices); - for (int j=rank; --j >= 0;) { + for (int i=0; i<numElements; i++) { + parameters[numDimensions + i] = getElementDescriptor(indices); + for (int j=indices.length; --j >= 0;) { if (++indices[j] < actualSize[j]) { break; } indices[j] = 0; // We have done a full turn at that dimension. Will increment next dimension. } } - return UnmodifiableArrayList.wrap(parameters); + return parameters; } /** @@ -583,7 +739,7 @@ public class TensorParameters<E> impleme * @return A new parameter group initialized to the given matrix. */ public ParameterValueGroup createValueGroup(final Map<String,?> properties, final Matrix matrix) { - if (dimensions.length != 2) { + if (rank() != 2) { throw new IllegalStateException(); } ArgumentChecks.ensureNonNull("matrix", matrix); @@ -601,7 +757,7 @@ public class TensorParameters<E> impleme * @throws InvalidParameterNameException if a parameter name was not recognized. */ public Matrix toMatrix(final ParameterValueGroup parameters) throws InvalidParameterNameException { - if (dimensions.length != 2) { + if (rank() != 2) { throw new IllegalStateException(); } ArgumentChecks.ensureNonNull("parameters", parameters);
Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -34,6 +34,7 @@ import org.apache.sis.referencing.Identi import org.apache.sis.referencing.operation.matrix.Matrices; import org.apache.sis.internal.referencing.WKTUtilities; import org.apache.sis.internal.util.Numerics; +import org.apache.sis.internal.util.UnmodifiableArrayList; import org.apache.sis.io.wkt.ElementKind; import org.apache.sis.io.wkt.Formatter; import org.apache.sis.util.Utilities; @@ -54,7 +55,7 @@ import org.apache.sis.util.resources.Err * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.4 - * @version 0.5 + * @version 0.6 * @module */ final class TensorValues<E> extends AbstractParameterDescriptor @@ -66,7 +67,11 @@ final class TensorValues<E> extends Abst private static final long serialVersionUID = -7747712999115044943L; /** - * A provider of matrix descriptors. + * A provider of descriptors for matrix parameters. This object is used like a collection of + * {@link ParameterDescriptor}s, even if it does not implement any standard collection API. + * + * @see TensorParameters#descriptor(ParameterDescriptorGroup, String, int[]) + * @see TensorParameters#getAllDescriptors(int[]) */ private final TensorParameters<E> descriptors; @@ -102,7 +107,7 @@ final class TensorValues<E> extends Abst * If {@code clone} is true, the new group will be a clone of the given group. * If {@code clone} is false, the new group will be initialized to default values. */ - private TensorValues(final TensorValues<E> other, final boolean clone) { + TensorValues(final TensorValues<E> other, final boolean clone) { super(other); descriptors = other.descriptors; dimensions = other.dimensions.clone(); @@ -166,13 +171,13 @@ final class TensorValues<E> extends Abst */ @Override public List<GeneralParameterDescriptor> descriptors() { - return descriptors.descriptors(dimensions()); + return UnmodifiableArrayList.<GeneralParameterDescriptor>wrap(descriptors.getAllDescriptors(size())); } /** - * Returns the current tensor dimensions. + * Returns the current tensor size for each dimensions. */ - private int[] dimensions() { + private int[] size() { final int[] indices = new int[dimensions.length]; for (int i=0; i<indices.length; i++) { indices[i] = dimensions[i].intValue(); @@ -191,7 +196,7 @@ final class TensorValues<E> extends Abst public GeneralParameterDescriptor descriptor(String name) throws ParameterNotFoundException { name = CharSequences.trimWhitespaces(name); ArgumentChecks.ensureNonEmpty("name", name); - return descriptors.descriptor(this, name, dimensions()); + return descriptors.descriptor(this, name, size()); } /** @@ -213,7 +218,7 @@ final class TensorValues<E> extends Abst cause = exception; } if (indices != null) { - final int[] actualSize = dimensions(); + final int[] actualSize = size(); if (TensorParameters.isInBounds(indices, actualSize)) { return parameter(indices, actualSize); } @@ -298,9 +303,13 @@ final class TensorValues<E> extends Abst */ @Override public List<GeneralParameterValue> values() { - final List<GeneralParameterValue> addTo = new ArrayList<GeneralParameterValue>(16); - addTo.addAll(Arrays.asList(dimensions)); - addValues(values, dimensions(), 0, addTo); + final List<GeneralParameterValue> addTo = new ArrayList<GeneralParameterValue>(); + for (final ParameterValue<Integer> dimension : dimensions) { + if (!isOmitted(dimension)) { + addTo.add(dimension); + } + } + addValues(values, size(), 0, addTo); return Collections.unmodifiableList(addTo); } @@ -319,9 +328,9 @@ final class TensorValues<E> extends Abst } } else { for (int i=0; i<length; i++) { - final Object value = values[i]; - if (value != null) { - addTo.add((ParameterValue<?>) value); + final ParameterValue<?> parameter = (ParameterValue<?>) values[i]; + if (parameter != null && !isOmitted(parameter)) { + addTo.add(parameter); } } } @@ -329,6 +338,19 @@ final class TensorValues<E> extends Abst } /** + * Returns {@code true} if the given parameter can be omitted. A parameter can be omitted + * if it is not mandatory and has a value equals to the default value. + */ + private static boolean isOmitted(final ParameterValue<?> parameter) { + final Object value = parameter.getValue(); + if (value == null) { // Implies that the default value is also null. + return true; + } + final ParameterDescriptor<?> descriptor = parameter.getDescriptor(); + return descriptor.getMinimumOccurs() == 0 && value.equals(descriptor.getDefaultValue()); + } + + /** * Always throws an exception since this group does not contain subgroups. */ @Override @@ -377,7 +399,7 @@ final class TensorValues<E> extends Abst * * @param matrix The matrix to copy in this group of parameters. */ - public void setMatrix(final Matrix matrix) { + final void setMatrix(final Matrix matrix) { final int numRow = matrix.getNumRow(); final int numCol = matrix.getNumCol(); dimensions[0].setValue(numRow); Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/AbstractIdentifiedObject.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -769,7 +769,7 @@ public class AbstractIdentifiedObject ex * @see IdentifiedObjects#isHeuristicMatchForName(IdentifiedObject, String) */ public boolean isHeuristicMatchForName(final String name) { - return IdentifiedObjects.isHeuristicMatchForName(this, alias, name); + return IdentifiedObjects.isHeuristicMatchForName(this.name, alias, name); } /** Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/Builder.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -30,7 +30,7 @@ import org.opengis.metadata.citation.Cit import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.ReferenceIdentifier; import org.apache.sis.metadata.iso.ImmutableIdentifier; -import org.apache.sis.metadata.iso.citation.Citations; +import org.apache.sis.internal.util.Citations; import org.apache.sis.util.resources.Errors; import static org.apache.sis.util.ArgumentChecks.ensureNonNull; @@ -191,8 +191,8 @@ public abstract class Builder<B extends protected Builder() { assert verifyParameterizedType(getClass()); properties = new HashMap<String,Object>(8); - aliases = new ArrayList<GenericName>(4); - identifiers = new ArrayList<ReferenceIdentifier>(4); + aliases = new ArrayList<GenericName>(); // Will often stay empty (default constructor handles those cases well). + identifiers = new ArrayList<ReferenceIdentifier>(); } /** @@ -465,7 +465,7 @@ public abstract class Builder<B extends */ public B addIdentifier(final Citation authority, final String identifier) { ensureNonNull("identifier", identifier); - identifiers.add(new ImmutableIdentifier(authority, Citations.getIdentifier(authority), identifier)); + identifiers.add(new ImmutableIdentifier(authority, Citations.getUnicodeIdentifier(authority), identifier)); return self(); } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/CommonCRS.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -65,11 +65,11 @@ import org.apache.sis.measure.Units; import static java.util.Collections.singletonMap; import static org.opengis.referencing.IdentifiedObject.NAME_KEY; -import static org.apache.sis.internal.referencing.HardCoded.CRS; -import static org.apache.sis.internal.referencing.HardCoded.EPSG; -import static org.apache.sis.internal.referencing.HardCoded.CRS27; -import static org.apache.sis.internal.referencing.HardCoded.CRS83; -import static org.apache.sis.internal.referencing.HardCoded.CRS84; +import static org.apache.sis.internal.util.Constants.CRS; +import static org.apache.sis.internal.util.Constants.EPSG; +import static org.apache.sis.internal.util.Constants.CRS27; +import static org.apache.sis.internal.util.Constants.CRS83; +import static org.apache.sis.internal.util.Constants.CRS84; /** Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/IdentifiedObjects.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -319,6 +319,10 @@ public final class IdentifiedObjects ext * * @param object The identified object, or {@code null}. * @return The first name, alias or identifier which is a valid Unicode identifier, or {@code null} if none. + * + * @see org.apache.sis.metadata.iso.ImmutableIdentifier + * @see org.apache.sis.metadata.iso.citation.Citations#getUnicodeIdentifier(Citation) + * @see org.apache.sis.util.CharSequences#isUnicodeIdentifier(CharSequence) */ public static String getUnicodeIdentifier(final IdentifiedObject object) { if (object != null) { @@ -393,31 +397,30 @@ public final class IdentifiedObjects ext return ((AbstractIdentifiedObject) object).isHeuristicMatchForName(name); } else { ensureNonNull("object", object); - return isHeuristicMatchForName(object, object.getAlias(), name); + return isHeuristicMatchForName(object.getName(), object.getAlias(), name); } } /** - * Returns {@code true} if the {@linkplain AbstractIdentifiedObject#getName() primary name} of the given object - * or one of the given alias matches the given name. The comparison ignores case, some Latin diacritical signs + * Returns {@code true} if the given {@linkplain AbstractIdentifiedObject#getName() primary name} or one + * of the given aliases matches the given name. The comparison ignores case, some Latin diacritical signs * and any characters that are not letters or digits. * - * @param object The object to check. - * @param aliases The list of alias in {@code object} (may be {@code null}). - * This method will never modify this list. Consequently, the - * given list can be a direct reference to an internal list. - * @param name The name for which to check for equality. + * @param name The name of the {@code IdentifiedObject} to check. + * @param aliases The list of alias in the {@code IdentifiedObject} (may be {@code null}). + * This method will never modify this list. Consequently, the + * given list can be a direct reference to an internal list. + * @param toSearch The name for which to check for equality. * @return {@code true} if the primary name or at least one alias matches the given {@code name}. */ - static boolean isHeuristicMatchForName(final IdentifiedObject object, final Collection<GenericName> aliases, - CharSequence name) + static boolean isHeuristicMatchForName(final Identifier name, final Collection<GenericName> aliases, + CharSequence toSearch) { - name = CharSequences.toASCII(name); - final Identifier id = object.getName(); - if (id != null) { // Paranoiac check. - final CharSequence code = CharSequences.toASCII(id.getCode()); + toSearch = CharSequences.toASCII(toSearch); + if (name != null) { // Paranoiac check. + final CharSequence code = CharSequences.toASCII(name.getCode()); if (code != null) { // Paranoiac check. - if (CharSequences.equalsFiltered(name, code, LETTERS_AND_DIGITS, true)) { + if (CharSequences.equalsFiltered(toSearch, code, LETTERS_AND_DIGITS, true)) { return true; } } @@ -426,7 +429,7 @@ public final class IdentifiedObjects ext for (final GenericName alias : aliases) { if (alias != null) { // Paranoiac check. final CharSequence tip = CharSequences.toASCII(alias.tip().toString()); - if (CharSequences.equalsFiltered(name, tip, LETTERS_AND_DIGITS, true)) { + if (CharSequences.equalsFiltered(toSearch, tip, LETTERS_AND_DIGITS, true)) { return true; } /* @@ -479,10 +482,10 @@ public final class IdentifiedObjects ext if (identifier instanceof ReferenceIdentifier) { cs = ((ReferenceIdentifier) identifier).getCodeSpace(); } - if (cs == null) { - cs = org.apache.sis.internal.util.Citations.getIdentifier(identifier.getAuthority()); + if (cs == null || cs.isEmpty()) { + cs = org.apache.sis.internal.util.Citations.getIdentifier(identifier.getAuthority(), true); } - if (cs != null && !cs.isEmpty()) { + if (cs != null) { return cs + DefaultNameSpace.DEFAULT_SEPARATOR + code; } return code; Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/NamedIdentifier.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -34,10 +34,12 @@ import org.opengis.referencing.Reference import org.opengis.parameter.InvalidParameterValueException; import org.apache.sis.internal.metadata.NameToIdentifier; import org.apache.sis.internal.system.DefaultFactories; -import org.apache.sis.metadata.iso.citation.Citations; +import org.apache.sis.metadata.iso.citation.Citations; // For javadoc import org.apache.sis.metadata.iso.ImmutableIdentifier; import org.apache.sis.util.collection.WeakValueHashMap; +import static org.apache.sis.internal.util.Citations.getUnicodeIdentifier; + // Branch-dependent imports import org.apache.sis.internal.jdk7.Objects; @@ -184,7 +186,7 @@ public class NamedIdentifier extends Imm * @param code The code. This parameter is mandatory. */ public NamedIdentifier(final Citation authority, final String code) { - super(authority, Citations.getIdentifier(authority), code); + super(authority, getUnicodeIdentifier(authority), code); } /** @@ -240,14 +242,14 @@ public class NamedIdentifier extends Imm */ private GenericName createName(final Citation authority, final CharSequence code) { final NameFactory factory = DefaultFactories.NAMES; - final String title = Citations.getIdentifier(authority); // Whitespaces trimed by Citations. + final String identifier = getUnicodeIdentifier(authority); // Whitespaces trimed by Citations. NameSpace scope = null; - if (title != null) { + if (identifier != null) { synchronized (SCOPES) { - scope = SCOPES.get(title); + scope = SCOPES.get(identifier); if (scope == null) { - scope = factory.createNameSpace(factory.createLocalName(null, title), null); - SCOPES.put(title, scope); + scope = factory.createNameSpace(factory.createLocalName(null, identifier), null); + SCOPES.put(identifier, scope); } } } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultGeographicCRS.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultGeographicCRS.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultGeographicCRS.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/crs/DefaultGeographicCRS.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -33,11 +33,11 @@ import org.apache.sis.referencing.Abstra import org.apache.sis.io.wkt.Formatter; import org.apache.sis.measure.Longitude; -import static org.apache.sis.internal.referencing.HardCoded.CRS; -import static org.apache.sis.internal.referencing.HardCoded.EPSG; -import static org.apache.sis.internal.referencing.HardCoded.CRS27; -import static org.apache.sis.internal.referencing.HardCoded.CRS83; -import static org.apache.sis.internal.referencing.HardCoded.CRS84; +import static org.apache.sis.internal.util.Constants.CRS; +import static org.apache.sis.internal.util.Constants.EPSG; +import static org.apache.sis.internal.util.Constants.CRS27; +import static org.apache.sis.internal.util.Constants.CRS83; +import static org.apache.sis.internal.util.Constants.CRS84; /** Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AbstractCS.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -360,6 +360,9 @@ public class AbstractCS extends Abstract /** * Returns a coordinate system of the same type than this CS but with different axes. * This method shall be overridden by all {@code AbstractCS} subclasses in this package. + * + * @param axes The set of axes to give to the new coordinate system. + * @return A new coordinate system of the same type than {@code this}, but using the given axes. */ AbstractCS createSameType(final Map<String,?> properties, final CoordinateSystemAxis[] axes) { return new AbstractCS(properties, axes); Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/AxesConvention.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -16,7 +16,8 @@ */ package org.apache.sis.referencing.cs; -import org.opengis.referencing.cs.AxisDirection; // For javadoc +import org.opengis.referencing.cs.AxisDirection; // For javadoc +import org.opengis.referencing.cs.CoordinateSystem; // For javadoc /** @@ -136,6 +137,7 @@ public enum AxesConvention { * changes are more difficult to handle by coordinate operation factories. * </div> * + * @see CoordinateSystems#normalize(CoordinateSystem) * @see org.apache.sis.referencing.CommonCRS#normalizedGeographic() */ NORMALIZED, Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/CoordinateSystems.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -46,7 +46,7 @@ import org.apache.sis.internal.jdk7.Obje * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.4 - * @version 0.4 + * @version 0.6 * @module */ public final class CoordinateSystems extends Static { @@ -296,4 +296,40 @@ public final class CoordinateSystems ext } return matrix; } + + /** + * Returns a coordinate system with {@linkplain AxesConvention#NORMALIZED normalized} axis order and units. + * This method is typically used together with {@link #swapAndScaleAxes swapAndScaleAxes} for the creation + * of a transformation step before some + * {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform math transform}. + * Example: + * + * {@preformat java + * Matrix step1 = swapAndScaleAxes(sourceCS, normalize(sourceCS)); + * Matrix step2 = ... some transform operating on standard axis ... + * Matrix step3 = swapAndScaleAxes(normalize(targetCS), targetCS); + * } + * + * A rational for normalized axis order and units is explained in the <cite>Axis units and + * direction</cite> section in the {@linkplain org.apache.sis.referencing.operation.projection + * description of map projection package}. + * + * @param cs The coordinate system. + * @return A constant similar to the specified {@code cs} with normalized axes. + * @throws IllegalArgumentException if the specified coordinate system can not be normalized. + * + * @see AxesConvention#NORMALIZED + * + * @since 0.6 + */ + public static CoordinateSystem normalize(final CoordinateSystem cs) throws IllegalArgumentException { + if (cs == null) { + return null; + } else if (cs instanceof AbstractCS) { + // User may have overridden the 'forConvention' method. + return ((AbstractCS) cs).forConvention(AxesConvention.NORMALIZED); + } else { + return Normalizer.normalize(cs); + } + } } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/cs/Normalizer.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -26,6 +26,7 @@ import javax.measure.converter.UnitConve import javax.measure.converter.ConversionException; import org.opengis.referencing.cs.RangeMeaning; import org.opengis.referencing.cs.AxisDirection; +import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.apache.sis.internal.referencing.AxisDirections; import org.apache.sis.referencing.IdentifiedObjects; @@ -207,7 +208,19 @@ final class Normalizer implements Compar } /** - * Reorder the axes in an attempt to get a right-handed system. + * Reorders the axes in an attempt to get a right-handed system. + * If no axis change is needed, then this method returns {@code cs} unchanged. + * + * @param cs The coordinate system to normalize. + * @return The normalized coordinate system. + */ + static CoordinateSystem normalize(final CoordinateSystem cs) { + final CoordinateSystemAxis[] axes = normalizeAxes(cs, true, true); + return (axes != null) ? createSameType(AbstractCS.castOrCopy(cs), axes) : cs; + } + + /** + * Reorders the axes in an attempt to get a right-handed system. * If no axis change is needed, then this method returns {@code cs} unchanged. * * @param cs The coordinate system to normalize. @@ -216,6 +229,22 @@ final class Normalizer implements Compar * @return The normalized coordinate system. */ static AbstractCS normalize(final AbstractCS cs, final boolean normalizeAxes, final boolean normalizeUnits) { + final CoordinateSystemAxis[] axes = normalizeAxes(cs, normalizeAxes, normalizeUnits); + return (axes != null) ? createSameType(cs, axes) : cs; + } + + /** + * Returns the normalized set of axes for the given coordinate system, + * or {@code null} if its axes were already normalized. + * + * @param cs The coordinate system to normalize. + * @param normalizeAxes {@code true} for normalizing axis directions. + * @param normalizeUnits {@code true} for normalizing units (currently ignored if {@code normalizeAxes} is {@code false}). + * @return The normalized set of coordinate system axes. + */ + private static CoordinateSystemAxis[] normalizeAxes(final CoordinateSystem cs, + final boolean normalizeAxes, final boolean normalizeUnits) + { boolean changed = false; final int dimension = cs.getDimension(); final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[dimension]; @@ -227,13 +256,21 @@ final class Normalizer implements Compar axes[i] = axis; } /* - * Sorts the axis in an attempt to create a right-handed system - * and creates a new Coordinate System if at least one axis changed. + * Sorts the axis in an attempt to create a right-handed system. + * Caller will create a new Coordinate System only if at least one axis changed. */ changed |= sort(axes); - if (!changed) { - return cs; - } + return changed ? axes : null; + } + + /** + * Creates a new coordinate system of the same type than the given one, but with the given axes. + * + * @param cs The coordinate system to copy. + * @param axes The set of axes to give to the new coordinate system. + * @return A new coordinate system of the same type than {@code cs}, but using the given axes. + */ + private static AbstractCS createSameType(final AbstractCS cs, final CoordinateSystemAxis[] axes) { final StringBuilder buffer = (StringBuilder) CharSequences.camelCaseToSentence(cs.getInterface().getSimpleName()); return cs.createSameType(singletonMap(AbstractCS.NAME_KEY, DefaultCompoundCS.createName(buffer, axes)), axes); } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultFormula.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -47,6 +47,7 @@ import org.apache.sis.internal.jdk7.Obje * * @see DefaultOperationMethod * @see org.apache.sis.referencing.operation.transform.AbstractMathTransform + * @see org.apache.sis.referencing.operation.transform.MathTransformProvider */ public class DefaultFormula extends FormattableObject implements Formula, Serializable { /** Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -25,7 +25,7 @@ import org.opengis.referencing.operation import org.opengis.referencing.operation.Projection; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.OperationMethod; -import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.SingleOperation; import org.opengis.parameter.ParameterDescriptorGroup; import org.apache.sis.util.Utilities; import org.apache.sis.util.ComparisonMode; @@ -45,12 +45,30 @@ import org.apache.sis.internal.jdk7.Obje /** - * Defines the algorithm and describes the parameters used to perform a coordinate operation. An {@code OperationMethod} - * contains an arbitrary amount of {@linkplain org.apache.sis.parameter.DefaultParameterDescriptor parameter descriptors}. - * Values for those parameters will be assigned by {@linkplain DefaultSingleOperation coordinate operations}. + * Describes the algorithm and parameters used to perform a coordinate operation. An {@code OperationMethod} + * is a kind of metadata: it does not perform any coordinate operation (e.g. map projection) by itself, but + * tells us what is needed in order to perform such operation. * - * <div class="note"><b>Departure from the ISO 19111 standard:</b> - * the following properties are mandatory according ISO 19111, + * <p>The most important parts of an {@code OperationMethod} are its {@linkplain #getName() name} and its + * {@linkplain #getParameters() group of parameter descriptors}. The parameter descriptors do not contain + * any value, but tell us what are the expected parameters, together with their units of measurement.</p> + * + * <div class="note"><b>Example:</b> + * An operation method named “<cite>Mercator (variant A)</cite>” (EPSG:9804) expects the following parameters: + * <ul> + * <li>“<cite>Latitude of natural origin</cite>” in degrees. Default value is 0°.</li> + * <li>“<cite>Longitude of natural origin</cite>” in degrees. Default value is 0°.</li> + * <li>“<cite>Scale factor at natural origin</cite>” as a dimensionless number. Default value is 1.</li> + * <li>“<cite>False easting</cite>” in metres. Default value is 0 m.</li> + * <li>“<cite>False northing</cite>” in metres. Default value is 0 m.</li> + * </ul></div> + * + * In Apache SIS implementation, the {@linkplain #getName() name} is the only mandatory property. However it is + * recommended to provide also {@linkplain #getIdentifiers() identifiers} (e.g. “EPSG:9804” in the above example) + * since names can sometime be ambiguous or be spelled in different ways. + * + * <div class="note"><b>Departure from the ISO 19111 standard</b><br> + * The following properties are mandatory according ISO 19111, * but may be missing under some conditions in Apache SIS: * <ul> * <li>The {@linkplain #getFormula() formula} if it has not been provided to the @@ -60,18 +78,44 @@ import org.apache.sis.internal.jdk7.Obje * constructor can not infer them.</li> * </ul></div> * + * {@section Relationship with other classes or interfaces} + * {@code OperationMethod} describes parameters without providing any value (except sometime default values). + * When values have been assigned to parameters, the result is a {@link SingleOperation}. + * Note that there is different kinds of {@code SingleOperation} depending on the nature and accuracy of the + * coordinate operation. See {@link #getOperationType()} for more information. + * + * <p>The interface performing the actual work of taking coordinates in the + * {@linkplain AbstractCoordinateOperation#getSourceCRS() source CRS} and calculating the new coordinates in the + * {@linkplain AbstractCoordinateOperation#getTargetCRS() target CRS} is {@link MathTransform}. + * In order to allow Apache SIS to instantiate those {@code MathTransform}s from given parameter values, + * {@code DefaultOperationMethod} subclasses should implement the + * {@link org.apache.sis.referencing.operation.transform.MathTransformProvider} interface.</p> + * + * {@section Immutability and thread safety} + * This class is immutable and thread-safe if all properties given to the constructor are also immutable and thread-safe. + * It is strongly recommended for all subclasses to be thread-safe, especially the + * {@link org.apache.sis.referencing.operation.transform.MathTransformProvider} implementations to be used with + * {@link org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory}. + * * @author Martin Desruisseaux (IRD, Geomatys) - * @version 0.5 + * @version 0.6 * @since 0.5 * @module * * @see DefaultSingleOperation + * @see org.apache.sis.referencing.operation.transform.MathTransformProvider */ public class DefaultOperationMethod extends AbstractIdentifiedObject implements OperationMethod { + /* + * NOTE FOR JAVADOC WRITER: + * The "method" word is ambiguous here, because it can be "Java method" or "coordinate operation method". + * In this class, we reserve the "method" word for "coordinate operation method" as much as possible. + */ + /** * Serial number for inter-operability with different versions. */ - private static final long serialVersionUID = -8181774670648793964L; + private static final long serialVersionUID = 2870579345991143357L; /** * Formula(s) or procedure used by this operation method. This may be a reference to a publication. @@ -85,14 +129,14 @@ public class DefaultOperationMethod exte * May be {@code null} if this method can work with any number of * source dimensions (e.g. <cite>Affine Transform</cite>). */ - private final Integer sourceDimension; + private final Integer sourceDimensions; /** * Number of dimensions in the target CRS of this operation method. * May be {@code null} if this method can work with any number of * target dimensions (e.g. <cite>Affine Transform</cite>). */ - private final Integer targetDimension; + private final Integer targetDimensions; /** * The set of parameters, or {@code null} if none. @@ -145,19 +189,19 @@ public class DefaultOperationMethod exte * The source and target dimensions may be {@code null} if this method can work * with any number of dimensions (e.g. <cite>Affine Transform</cite>). * - * @param properties Set of properties. Shall contain at least {@code "name"}. - * @param sourceDimension Number of dimensions in the source CRS of this operation method, or {@code null}. - * @param targetDimension Number of dimensions in the target CRS of this operation method, or {@code null}. - * @param parameters Description of parameters expected by this operation. + * @param properties Set of properties. Shall contain at least {@code "name"}. + * @param sourceDimensions Number of dimensions in the source CRS of this operation method, or {@code null}. + * @param targetDimensions Number of dimensions in the target CRS of this operation method, or {@code null}. + * @param parameters Description of parameters expected by this operation. */ public DefaultOperationMethod(final Map<String,?> properties, - final Integer sourceDimension, - final Integer targetDimension, + final Integer sourceDimensions, + final Integer targetDimensions, final ParameterDescriptorGroup parameters) { super(properties); - if (sourceDimension != null) ensurePositive("sourceDimension", sourceDimension); - if (targetDimension != null) ensurePositive("targetDimension", targetDimension); + if (sourceDimensions != null) ensurePositive("sourceDimensions", sourceDimensions); + if (targetDimensions != null) ensurePositive("targetDimensions", targetDimensions); ensureNonNull("parameters", parameters); Object value = properties.get(FORMULA_KEY); @@ -171,9 +215,9 @@ public class DefaultOperationMethod exte throw new IllegalArgumentException(Errors.getResources(properties) .getString(Errors.Keys.IllegalPropertyClass_2, FORMULA_KEY, value.getClass())); } - this.parameters = parameters; - this.sourceDimension = sourceDimension; - this.targetDimension = targetDimension; + this.parameters = parameters; + this.sourceDimensions = sourceDimensions; + this.targetDimensions = targetDimensions; } /** @@ -185,8 +229,8 @@ public class DefaultOperationMethod exte */ public DefaultOperationMethod(final MathTransform transform) { super(getProperties(transform)); - sourceDimension = transform.getSourceDimensions(); - targetDimension = transform.getTargetDimensions(); + sourceDimensions = transform.getSourceDimensions(); + targetDimensions = transform.getTargetDimensions(); if (transform instanceof Parameterized) { parameters = ((Parameterized) transform).getParameterDescriptors(); } else { @@ -250,15 +294,15 @@ public class DefaultOperationMethod exte */ protected DefaultOperationMethod(final OperationMethod method) { super(method); - formula = method.getFormula(); - parameters = method.getParameters(); - sourceDimension = method.getSourceDimensions(); - targetDimension = method.getTargetDimensions(); + formula = method.getFormula(); + parameters = method.getParameters(); + sourceDimensions = method.getSourceDimensions(); + targetDimensions = method.getTargetDimensions(); } /** * Returns a SIS operation method implementation with the same values than the given arbitrary implementation. - * If the given object is {@code null}, then this method returns {@code null}. + * If the given object is {@code null}, then {@code null} is returned. * Otherwise if the given object is already a SIS implementation, then the given object is returned unchanged. * Otherwise a new SIS implementation is created and initialized to the attribute values of the given object. * @@ -276,52 +320,140 @@ public class DefaultOperationMethod exte * The source and target dimensions may be {@code null} if this method can work with any number of dimensions * (e.g. <cite>Affine Transform</cite>). * - * @param method The operation method to copy. - * @param sourceDimension Number of dimensions in the source CRS of this operation method. - * @param targetDimension Number of dimensions in the target CRS of this operation method. + * @param method The operation method to copy. + * @param sourceDimensions Number of dimensions in the source CRS of this operation method. + * @param targetDimensions Number of dimensions in the target CRS of this operation method. */ private DefaultOperationMethod(final OperationMethod method, - final Integer sourceDimension, - final Integer targetDimension) + final Integer sourceDimensions, + final Integer targetDimensions) { super(method); this.formula = method.getFormula(); this.parameters = method.getParameters(); - this.sourceDimension = sourceDimension; - this.targetDimension = targetDimension; + this.sourceDimensions = sourceDimensions; + this.targetDimensions = targetDimensions; } /** - * Returns an operation method with the same values than the specified one except the dimensions. - * The source and target dimensions may be {@code null} if this method can work with any number of dimensions - * (e.g. <cite>Affine Transform</cite>). + * Returns an operation method with different dimensions, if we are allowed to change dimensionality. + * This method accepts to change a dimension only if the value specified by the original method + * is {@code null}. Otherwise an {@link IllegalArgumentException} is thrown. + * + * @param method The operation method to redimension. + * @param sourceDimensions The desired new source dimensions. + * @param methodSource The current number of source dimensions (may be {@code null}). + * @param targetDimensions The desired new target dimensions. + * @param methodTarget The current number of target dimensions (may be {@code null}). + * @throws IllegalArgumentException if the given dimensions are illegal for this operation method. + */ + private static OperationMethod redimension(final OperationMethod method, + final int sourceDimensions, final Integer methodSource, + final int targetDimensions, final Integer methodTarget) + { + boolean sourceValids = (methodSource != null) && (methodSource == sourceDimensions); + boolean targetValids = (methodTarget != null) && (methodTarget == targetDimensions); + if (sourceValids && targetValids) { + return method; + } + sourceValids |= (methodSource == null); + targetValids |= (methodTarget == null); + ensurePositive("sourceDimensions", sourceDimensions); + ensurePositive("targetDimensions", targetDimensions); + if (!sourceValids || !targetValids) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalOperationDimension_3, + method.getName().getCode(), sourceDimensions, targetDimensions)); + } + return new DefaultOperationMethod(method, sourceDimensions, targetDimensions); + } + + /** + * Returns an operation method with different dimensions, if we are allowed to change dimensionality. + * The need to change an {@code OperationMethod} dimensionality may occur in two contexts: + * + * <ul> + * <li><p>When the original method can work with any number of dimensions. Those methods do not know + * in advance the number of dimensions, which is fixed only after the actual {@link MathTransform} + * instance has been created. + * Example: <cite>Affine</cite> conversion.</p></li> + * <li><p>When a three-dimensional method can also be used in the two-dimensional case, typically by + * assuming that the ellipsoidal height is zero everywhere. + * Example: <cite>Molodensky</cite> transform.</p></li> + * </ul> + * + * This {@code redimension(…)} implementation performs the following choice: * - * @param method The operation method to redimension, or {@code null}. - * @param sourceDimension Number of dimensions in the source CRS of this operation method. - * @param targetDimension Number of dimensions in the target CRS of this operation method. - * @return The redimensioned operation method, or {@code method} if the given method was {@code null} - * or already had th given dimensions. + * <ul> + * <li><p>If the given method is an instance of {@code DefaultOperationMethod}, then delegate to + * {@link #redimension(int, int)} in order to allow subclasses to defines their own policy. + * For example the <cite>Molodensky</cite> method needs to override.</p></li> + * <li>Otherwise for each dimension (<var>source</var> and <var>target</var>): + * <ul> + * <li>If the corresponding dimension of the given method is {@code null}, then + * set that dimension to the given value in a new {@code OperationMethod}.</li> + * <li>Otherwise if the given value is not equal to the corresponding dimension + * in the given method, throw an {@link IllegalArgumentException}.</li> + * </ul> + * </li> + * </ul> + * + * @param method The operation method to redimension, or {@code null}. + * @param sourceDimensions The desired number of input dimensions. + * @param targetDimensions The desired number of output dimensions. + * @return The redimensioned operation method, or {@code null} if the given method was null, + * or {@code method} if no change is needed. + * @throws IllegalArgumentException if the given dimensions are illegal for the given operation method. */ public static OperationMethod redimension(OperationMethod method, - final Integer sourceDimension, - final Integer targetDimension) + final int sourceDimensions, final int targetDimensions) { - if (sourceDimension != null) ensurePositive("sourceDimension", sourceDimension); - if (targetDimension != null) ensurePositive("targetDimension", targetDimension); - if (method != null && !(Objects.equals(sourceDimension, method.getSourceDimensions()) - && Objects.equals(targetDimension, method.getTargetDimensions()))) - { - method = new DefaultOperationMethod(method, sourceDimension, targetDimension); + if (method != null) { + if (method instanceof DefaultOperationMethod) { + return ((DefaultOperationMethod) method).redimension(sourceDimensions, targetDimensions); + } else { + method = redimension(method, sourceDimensions, method.getSourceDimensions(), + targetDimensions, method.getTargetDimensions()); + } } return method; } /** + * Returns this operation method with different dimensions, if we are allowed to change dimensionality. + * See {@link #redimension(OperationMethod, int, int)} for more information. + * + * <p>The default implementation performs the following choice: + * for each dimension (<var>source</var> and <var>target</var>):</p> + * <ul> + * <li>If the corresponding dimension of the given method is {@code null}, then + * set that dimension to the given value in a new {@code OperationMethod}.</li> + * <li>Otherwise if the given value is not equal to the corresponding dimension + * in the given method, throw an {@link IllegalArgumentException}.</li> + * </ul> + * + * Subclasses should override this method if they can work with different number of dimensions. + * For example a <cite>Molodensky</cite> transform usually works in a three-dimensional space, + * but can also work in a two-dimensional space by assuming that the ellipsoidal height is zero + * everywhere. + * + * @param sourceDimensions The desired number of input dimensions. + * @param targetDimensions The desired number of output dimensions. + * @return The redimensioned operation method, or {@code this} if no change is needed. + * @throws IllegalArgumentException if the given dimensions are illegal for this operation method. + * + * @since 0.6 + */ + public OperationMethod redimension(final int sourceDimensions, final int targetDimensions) { + return redimension(this, sourceDimensions, this.sourceDimensions, + targetDimensions, this.targetDimensions); + } + + /** * Returns the GeoAPI interface implemented by this class. * The SIS implementation returns {@code OperationMethod.class}. * * <div class="note"><b>Note for implementors:</b> - * Subclasses usually do not need to override this method since GeoAPI does not define {@code OperationMethod} + * Subclasses usually do not need to override this information since GeoAPI does not define {@code OperationMethod} * sub-interface. Overriding possibility is left mostly for implementors who wish to extend GeoAPI with their * own set of interfaces.</div> * @@ -337,29 +469,31 @@ public class DefaultOperationMethod exte * The base {@code CoordinateOperation} interface is usually one of the following subtypes: * * <ul> - * <li>{@link org.opengis.referencing.operation.Transformation} + * <li><p>{@link org.opengis.referencing.operation.Transformation} * if the coordinate operation has some errors (typically of a few metres) because of the empirical process by * which the operation parameters were determined. Those errors do not depend on the floating point precision - * or the accuracy of the implementation algorithm.</li> - * <li>{@link org.opengis.referencing.operation.Conversion} + * or the accuracy of the implementation algorithm.</p></li> + * <li><p>{@link org.opengis.referencing.operation.Conversion} * if the coordinate operation is theoretically of infinite precision, ignoring the limitations of floating - * point arithmetic (including rounding errors) and the approximations implied by finite series expansions.</li> - * <li>{@link org.opengis.referencing.operation.Projection} + * point arithmetic (including rounding errors) and the approximations implied by finite series expansions.</p></li> + * <li><p>{@link org.opengis.referencing.operation.Projection} * if the coordinate operation is a conversion (as defined above) converting geodetic latitudes and longitudes * to plane (map) coordinates. This type can optionally be refined with one of the * {@link org.opengis.referencing.operation.CylindricalProjection}, * {@link org.opengis.referencing.operation.ConicProjection} or - * {@link org.opengis.referencing.operation.PlanarProjection} subtypes.</li> + * {@link org.opengis.referencing.operation.PlanarProjection} subtypes.</p></li> * </ul> * - * In case of doubt, this method can conservatively return the base type. - * The default implementation returns {@code CoordinateOperation.class}, + * In case of doubt, {@code getOperationType()} can conservatively return the base type. + * The default implementation returns {@code SingleOperation.class}, * which is the most conservative return value. * * @return Interface implemented by all coordinate operations that use this method. + * + * @see org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory#getAvailableMethods(Class) */ - public Class<? extends CoordinateOperation> getOperationType() { - return CoordinateOperation.class; + public Class<? extends SingleOperation> getOperationType() { + return SingleOperation.class; } /** @@ -371,6 +505,9 @@ public class DefaultOperationMethod exte * this property is mandatory according ISO 19111, but optional in Apache SIS.</div> * * @return The formula used by this method, or {@code null} if unknown. + * + * @see DefaultFormula + * @see org.apache.sis.referencing.operation.transform.MathTransformProvider */ @Override public Formula getFormula() { @@ -382,10 +519,12 @@ public class DefaultOperationMethod exte * May be null if unknown, as in an <cite>Affine Transform</cite>. * * @return The dimension of source CRS, or {@code null} if unknown. + * + * @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getSourceDimensions() */ @Override public Integer getSourceDimensions() { - return sourceDimension; + return sourceDimensions; } /** @@ -393,10 +532,12 @@ public class DefaultOperationMethod exte * May be null if unknown, as in an <cite>Affine Transform</cite>. * * @return The dimension of target CRS, or {@code null} if unknown. + * + * @see org.apache.sis.referencing.operation.transform.AbstractMathTransform#getTargetDimensions() */ @Override public Integer getTargetDimensions() { - return targetDimension; + return targetDimensions; } /** @@ -404,7 +545,7 @@ public class DefaultOperationMethod exte * * <div class="note"><b>Departure from the ISO 19111 standard:</b> * this property is mandatory according ISO 19111, but may be null in Apache SIS if the - * {@linkplain #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it.</div> + * {@link #DefaultOperationMethod(MathTransform)} constructor has been unable to infer it.</div> * * @return The parameters, or {@code null} if unknown. */ @@ -435,10 +576,10 @@ public class DefaultOperationMethod exte case STRICT: { // Name and identifiers have been compared by super.equals(object, mode). final DefaultOperationMethod that = (DefaultOperationMethod) object; - return Objects.equals(this.formula, that.formula) && - Objects.equals(this.sourceDimension, that.sourceDimension) && - Objects.equals(this.targetDimension, that.targetDimension) && - Objects.equals(this.parameters, that.parameters); + return Objects.equals(this.formula, that.formula) && + Objects.equals(this.sourceDimensions, that.sourceDimensions) && + Objects.equals(this.targetDimensions, that.targetDimensions) && + Objects.equals(this.parameters, that.parameters); } case BY_CONTRACT: { // Name and identifiers have been compared by super.equals(object, mode). @@ -487,7 +628,7 @@ public class DefaultOperationMethod exte */ @Override protected long computeHashCode() { - return super.computeHashCode() + Objects.hash(sourceDimension, targetDimension, parameters); + return super.computeHashCode() + Objects.hash(sourceDimensions, targetDimensions, parameters); } /** @@ -515,7 +656,7 @@ public class DefaultOperationMethod exte * Transformation, ConcatenatedOperation, PassThroughOperation, or any user-defined type that * do not extend Projection. All other operation types are accepted. */ - final Class<? extends CoordinateOperation> type = getOperationType(); + final Class<? extends SingleOperation> type = getOperationType(); if (Projection.class.isAssignableFrom(type) || type.isAssignableFrom(Projection.class)) { return "Projection"; } Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -67,6 +67,8 @@ import org.apache.sis.internal.jdk7.Obje * @since 0.4 * @version 0.5 * @module + * + * @see org.apache.sis.parameter.TensorParameters */ public final class Matrices extends Static { /** Modified: sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java URL: http://svn.apache.org/viewvc/sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java?rev=1661582&r1=1661581&r2=1661582&view=diff ============================================================================== --- sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] (original) +++ sis/trunk/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] Sun Feb 22 23:38:34 2015 @@ -41,6 +41,8 @@ import org.apache.sis.util.resources.Err * @since 0.4 * @version 0.4 * @module + * + * @see Matrices */ public abstract class MatrixSIS implements Matrix, LenientComparable, Cloneable, Serializable { /**
