org.geotoolkit.parameter.MatrixParameterDescriptors Maven / Gradle / Ivy
/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2001-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.parameter;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.Collections;
import javax.measure.unit.Unit;
import net.jcip.annotations.Immutable;
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.GeneralParameterDescriptor;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.InvalidParameterNameException;
import org.opengis.referencing.operation.Matrix;
import org.geotoolkit.metadata.iso.citation.Citations;
import org.geotoolkit.referencing.NamedIdentifier;
import org.geotoolkit.referencing.AbstractIdentifiedObject;
import org.geotoolkit.referencing.operation.matrix.Matrices;
import org.geotoolkit.util.collection.UnmodifiableArrayList;
import org.geotoolkit.util.ComparisonMode;
import org.geotoolkit.util.Utilities;
import org.geotoolkit.resources.Errors;
import static org.geotoolkit.util.Utilities.hash;
import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;
/**
* A parameter group for {@link Matrix} elements. The amount of
* {@linkplain FloatParameter parameter values} is extensible, i.e. it can
* grown or shrink according the value of {@code "num_row"} and {@code "num_col"}
* parameters. The parameters format may vary according the information provided to
* the constructor, but it is typically as below:
*
* {@preformat text
* num_row
* num_col
* elt_0_0
* elt_0_1
* ...
* elt_0_
* elt_1_0
* elt_1_1
* ...
* elt__
* }
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.20
*
* @see MatrixParameters
*
* @since 2.0
* @module
*/
@Immutable
public class MatrixParameterDescriptors extends DefaultParameterDescriptorGroup {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = -7386537348359343836L;
/**
* The default matrix size for the
* {@linkplain #MatrixParameterDescriptors(Map) one-argument constructor}.
*/
public static final int DEFAULT_MATRIX_SIZE = 3;
/**
* The height and weight of the matrix of {@link #parameters} to cache. Descriptors
* for row or column indices greater than or equal to this value will not be cached.
*/
private static final int CACHE_SIZE = 8;
/**
* The cached descriptors for each elements in a matrix. Descriptors do not depends
* on matrix element values. Consequently, the same descriptors can be reused for all
* {@link MatrixParameters} instances.
*/
@SuppressWarnings({"unchecked","rawtypes"})
private final ParameterDescriptor[] parameters = new ParameterDescriptor[CACHE_SIZE * CACHE_SIZE];
/**
* EPSG parameter names for a 3×3 affine transform.
*
* @since 3.20
*/
private static final String[] EPSG_NAMES = {
"A1", "A2", "A0",
"B1", "B2", "B0"
};
/**
* The descriptor for the {@code "num_row"} parameter.
*/
protected final ParameterDescriptor numRow;
/**
* The descriptor for the {@code "num_col"} parameter.
*/
protected final ParameterDescriptor numCol;
/**
* The prefix to insert in front of parameter name for each matrix elements.
*/
protected final String prefix;
/**
* The separator between the row and the column index in parameter names.
*/
protected final char separator;
/**
* Returns the property map for the given parameter name.
*/
private static Map properties(final String name) {
return Collections.singletonMap(NAME_KEY, new NamedIdentifier(Citations.OGC, name));
}
/**
* Constructs a parameter group with default name format matching
* Well
* Known Text usages.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
*/
public MatrixParameterDescriptors(final Map properties) {
/*
* Note: the upper limit given in the operation parameters is arbitrary. A high
* value doesn't make much sense anyway since matrix size for projective
* transform will usually not be much more than 5, and the storage scheme
* used in this implementation is inefficient for large amount of matrix
* elements.
*/
this(properties, new ParameterDescriptor>[] {
DefaultParameterDescriptor.create(properties("num_row"), DEFAULT_MATRIX_SIZE, 2, 50, true),
DefaultParameterDescriptor.create(properties("num_col"), DEFAULT_MATRIX_SIZE, 2, 50, true)
}, "elt_", '_');
}
/**
* Constructs a parameter group. The properties map is given unchanged to the
* {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
* The {@code parameters} array should contains parameters other
* than matrix elements. The first parameter is assumed to be the number of rows, and
* the second parameter the number of columns. All extra parameters are ignored.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param parameters The {@code "num_row"} and {@code "num_col"} parameters.
* @param prefix The prefix to insert in front of parameter name for each matrix elements.
* @param separator The separator between the row and the column index in parameter names.
*/
public MatrixParameterDescriptors(final Map properties,
ParameterDescriptor>[] parameters,
final String prefix,
final char separator)
{
super(properties, parameters);
if (parameters.length < 2) {
// TODO: provide a localized message
throw new IllegalArgumentException();
}
numRow = Parameters.cast(parameters[0], Integer.class);
numCol = Parameters.cast(parameters[1], Integer.class);
ensureNonNull("prefix", prefix);
this.prefix = prefix;
this.separator = separator;
}
/**
* Verify that the specified index is included in the expected range of values.
*
* @param name The parameter name. To be used for formatting error message.
* @param index The indices to check.
* @param upper The upper range value, exclusive.
* @throws IndexOutOfBoundsException if {@code index} is outside the expected range.
*/
static void checkIndice(final String name, final int index, final int upper)
throws IndexOutOfBoundsException
{
if (index<0 || index>=upper) {
throw new IndexOutOfBoundsException(Errors.format(
Errors.Keys.ILLEGAL_ARGUMENT_$2, name, index));
}
}
/**
* Returns the parameter in this group for the specified name. The name can be a matrix element
* if it uses the following syntax: "elt_row_col"
where
* {@code "elt_"} is the {@linkplain #prefix} for all matrix elements, and row
* and col are row and column indices respectively. For example {@code "elt_2_1"}
* is the element name for the value at line 2 and row 1. The row and column index are 0 based.
*
* @param name The case insensitive name of the parameter to search for.
* @return The parameter for the given name.
* @throws ParameterNotFoundException if there is no parameter for the given name.
*/
@Override
@SuppressWarnings("unchecked")
public final GeneralParameterDescriptor descriptor(final String name)
throws ParameterNotFoundException
{
return descriptor(name, ((Number) numRow.getMaximumValue()).intValue(),
((Number) numCol.getMaximumValue()).intValue());
}
/**
* Implementation of the {@link #descriptor(String)} method.
*
* @param name The case insensitive name of the parameter to search for.
* @param numRow The maximum number of rows.
* @param numCol The maximum number of columns.
* @return The parameter for the given name.
* @throws ParameterNotFoundException if there is no parameter for the given name.
*/
final GeneralParameterDescriptor descriptor(String name, final int numRow, final int numCol)
throws ParameterNotFoundException
{
ensureNonNull("name", name);
name = name.trim();
RuntimeException cause = null;
if (name.regionMatches(true, 0, prefix, 0, prefix.length())) {
final int split = name.indexOf(separator, prefix.length());
if (split >= 0) try {
final int row = Integer.parseInt(name.substring(prefix.length(), split));
final int col = Integer.parseInt(name.substring(split+1));
return descriptor(row, col, numRow, numCol);
} catch (NumberFormatException exception) {
cause = exception;
} catch (IndexOutOfBoundsException exception) {
cause = exception;
}
}
/*
* The parameter name is not a matrix element name. Search in the super
* class for other parameters, especially "num_row" and "num_col".
*/
try {
return super.descriptor(name);
} catch (ParameterNotFoundException exception) {
if (cause != null) try {
exception.initCause(cause);
} catch (IllegalStateException ignore) {
// A cause has already be given to the exception. Forget the cause then.
}
throw exception;
}
}
/**
* Returns the parameter in this group for a matrix element at the specified
* index. row and column indices are 0 based. Indices must be lower that the
* {@linkplain ParameterDescriptor#getMaximumValue maximum values}
* given to the {@link #numRow} and {@link #numCol} parameters.
*
* @param row The row index.
* @param column The column index
* @return The parameter descriptor for the specified matrix element.
* @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds.
*/
@SuppressWarnings("unchecked")
public final ParameterDescriptor descriptor(final int row, final int column)
throws IndexOutOfBoundsException
{
return descriptor(row, column, ((Number) numRow.getMaximumValue()).intValue(),
((Number) numCol.getMaximumValue()).intValue());
}
/**
* Implementation of the {@link #descriptor(int,int)} method.
*
* @param row The row index.
* @param column The column index
* @param numRow The maximum number of rows.
* @param numCol The maximum number of columns.
* @return The parameter descriptor for the specified matrix element.
* @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds.
*/
final ParameterDescriptor descriptor(final int row, final int column,
final int numRow, final int numCol)
throws IndexOutOfBoundsException
{
checkIndice("row", row, numRow);
checkIndice("column", column, numCol);
int index = -1;
ParameterDescriptor param;
if (row properties = properties(prefix + row + separator + column);
if (numRow == 3 && numCol == 3) {
final int i = row*3 + column;
if (i >= 0 && i < EPSG_NAMES.length) {
properties = new HashMap(properties);
properties.put(ALIAS_KEY, new NamedIdentifier(Citations.EPSG, EPSG_NAMES[i]));
}
}
param = new DefaultParameterDescriptor(properties,
Double.class, null, (row == column) ? 1.0 : 0.0,
null, null, Unit.ONE, true);
if (index >= 0) {
synchronized (parameters) {
// Paranoiac check in case another thread created the parameter
// descriptor in same time= return the shared instance.
final ParameterDescriptor existing = parameters[index];
if (existing != null) {
return existing;
}
parameters[index] = param;
}
}
return param;
}
/**
* Returns the parameters in this group. The number or elements is inferred from the
* {@linkplain ParameterDescriptor#getDefaultValue default values}
* given to the {@link #numRow} and {@link #numCol} parameters.
*
* @return The matrix parameters, including all elements.
*/
@Override
public final List descriptors() {
return descriptors(this.numRow.getDefaultValue().intValue(),
this.numCol.getDefaultValue().intValue());
}
/**
* Implementation of the {@link #descriptors()} method.
* Returns the parameters in this group for a matrix of the specified size.
*
* @param numRow The number of rows.
* @param numCol The number of columns.
* @return The matrix parameters, including all elements.
*/
final List descriptors(final int numRow, final int numCol) {
final GeneralParameterDescriptor[] parameters = new GeneralParameterDescriptor[numRow*numCol + 2];
int k = 0;
parameters[k++] = this.numRow;
parameters[k++] = this.numCol;
for (int j=0; j numRowParam = Parameters.getOrCreate(numRow, parameters);
final ParameterValue> numColParam = Parameters.getOrCreate(numCol, parameters);
final int numRow = numRowParam.intValue();
final int numCol = numColParam.intValue();
final Matrix matrix = Matrices.create(numRow, numCol);
final List params = parameters.values();
if (params != null) {
for (final GeneralParameterValue param : params) {
if (param==numRowParam || param==numColParam) {
continue;
}
RuntimeException cause = null;
final String name = param.getDescriptor().getName().toString();
if (name.regionMatches(true, 0, prefix, 0, prefix.length())) {
final int split = name.indexOf(separator, prefix.length());
if (split >= 0) try {
final int row = Integer.parseInt(name.substring(prefix.length(), split));
final int col = Integer.parseInt(name.substring(split+1));
matrix.setElement(row, col, ((ParameterValue>) param).doubleValue());
continue;
} catch (NumberFormatException exception) {
cause = exception;
} catch (IndexOutOfBoundsException exception) {
cause = exception;
}
}
final InvalidParameterNameException exception;
exception = new InvalidParameterNameException(Errors.format(
Errors.Keys.UNEXPECTED_PARAMETER_$1, name), name);
if (cause != null) {
exception.initCause(cause);
}
throw exception;
}
}
return matrix;
}
/**
* Compares the specified object with this parameter group for equality.
*
* @param object The object to compare to {@code this}.
* @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
* {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only properties
* relevant to transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final Object object, final ComparisonMode mode) {
if (object instanceof MatrixParameterDescriptors && super.equals(object, mode)) {
final MatrixParameterDescriptors that = (MatrixParameterDescriptors) object;
return separator == that.separator && Utilities.equals(prefix, that.prefix);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
protected int computeHashCode() {
return hash(prefix, hash(separator, super.computeHashCode()));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy