All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.geotoolkit.parameter.MatrixParameterDescriptors Maven / Gradle / Ivy

Go to download

Implementations of Coordinate Reference Systems (CRS), conversion and transformation services derived from ISO 19111.

There is a newer version: 3.20-geoapi-3.0
Show newest version
/*
 *    Geotoolkit.org - An Open Source Java GIS Toolkit
 *    http://www.geotoolkit.org
 *
 *    (C) 2001-2011, Open Source Geospatial Foundation (OSGeo)
 *    (C) 2009-2011, 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.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.util.Utilities;
import org.geotoolkit.util.ComparisonMode;
import org.geotoolkit.util.collection.UnmodifiableArrayList;
import org.geotoolkit.referencing.AbstractIdentifiedObject;
import org.geotoolkit.referencing.operation.matrix.MatrixFactory;
import org.geotoolkit.resources.Errors;

import static org.geotoolkit.util.Utilities.hash;
import static org.geotoolkit.util.ArgumentChecks.ensureNonNull;


/**
 * A parameter group for {@linkplain Matrix 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.18
 *
 * @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];

    /**
     * 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;

    /**
     * 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("num_row", DEFAULT_MATRIX_SIZE, 2, 50),
            DefaultParameterDescriptor.create("num_col", DEFAULT_MATRIX_SIZE, 2, 50)
        }, "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 indice.
     * @param  column The column indice
     * @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 indice.
     * @param  column The column indice
     * @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(
                Collections.singletonMap(NAME_KEY, prefix + row + separator + column),
                Double.class, null, (row == column) ? 1.0 : 0.0,
                null, null, Unit.ONE, true);
        if (index >= 0) {
            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 = MatrixFactory.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 - 2024 Weber Informatics LLC | Privacy Policy