/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2001-2008, Open Source Geospatial Foundation (OSGeo) * * 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.geotools.parameter; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Set; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.parameter.ParameterValue; import org.opengis.referencing.ReferenceIdentifier; import org.opengis.referencing.operation.Matrix; import org.opengis.util.InternationalString; import org.opengis.util.GenericName; import org.geotools.io.TableWriter; import org.geotools.referencing.operation.matrix.MatrixFactory; import org.geotools.resources.UnmodifiableArrayList; import org.geotools.resources.XArray; import org.geotools.util.Utilities; /** * The values for a group of {@linkplain MatrixParameterDescriptors matrix parameters}. This value * group is extensible, i.e. the number of <code>"elt_<var>row</var>_<var>col</var>"</code> * parameters depends on the <code>"num_row"</code> and <code>"num_col"</code> parameter values. * Consequently, this {@linkplain ParameterGroup parameter value group} is also its own mutable * {@linkplain ParameterDescriptorGroup operation parameter group}. * * @since 2.1 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * * @see MatrixParameterDescriptors */ public class MatrixParameters extends ParameterGroup implements ParameterDescriptorGroup { /** * Serial number for interoperability with different versions. */ private static final long serialVersionUID = -7747712999115044943L; /** * The parameter values. Will be constructed only when first requested. */ private ParameterValue<Double>[][] matrixValues; /** * The value for the {@link MatrixParameterDescriptors#numRow} parameter. * Consider this field as final. It is not only for {@link #clone} implementation. */ private ParameterValue<Integer> numRow; /** * The value for the {@link MatrixParameterDescriptors#numCol} parameter. * Consider this field as final. It is not only for {@link #clone} implementation. */ private ParameterValue<Integer> numCol; /** * Constructs default values for the specified * {@linkplain MatrixParameterDescriptors matrix parameter descriptors}. * * @param descriptor The descriptor for this group of parameters. */ public MatrixParameters(final MatrixParameterDescriptors descriptor) { super(descriptor); numRow = Parameters.cast((ParameterValue) parameter(0), Integer.class); numCol = Parameters.cast((ParameterValue) parameter(1), Integer.class); } /** * Returns a description of this parameter value group. Returns always {@code this}, * since the description depends on <code>"num_row"</code> and <code>"num_col"</code> * parameter values. */ @Override public ParameterDescriptorGroup getDescriptor() { return this; } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public ReferenceIdentifier getName() { return descriptor.getName(); } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public Collection<GenericName> getAlias() { return descriptor.getAlias(); } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public Set<ReferenceIdentifier> getIdentifiers() { return descriptor.getIdentifiers(); } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public InternationalString getRemarks() { return descriptor.getRemarks(); } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public int getMinimumOccurs() { return descriptor.getMinimumOccurs(); } /** * Forward the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public int getMaximumOccurs() { return descriptor.getMaximumOccurs(); } /** * Returns the parameter in this group for the specified name. The name can be a matrix element * if it uses the following syntax: <code>"elt_<var>row</var>_<var>col</var>"</code> where * <code>"elt_"</code> is the {@linkplain MatrixParameterDescriptors#prefix prefix} for all * matrix elements, and <var>row</var> and <var>col</var> are row and column indices * respectively. For example <code>"elt_2_1"</code> 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. */ public GeneralParameterDescriptor descriptor(final String name) throws ParameterNotFoundException { return ((MatrixParameterDescriptors) descriptor).descriptor(name, numRow.intValue(), numCol.intValue()); } /** * Returns the value in this group for the specified name. The name can be a matrix element * if it uses the following syntax: <code>"elt_<var>row</var>_<var>col</var>"</code> where * <code>"elt_"</code> is the {@linkplain MatrixParameterDescriptors#prefix prefix} for all * matrix elements, and <var>row</var> and <var>col</var> are row and column indices * respectively. For example <code>"elt_2_1"</code> 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 value for the given name. * @throws ParameterNotFoundException if there is no parameter for the given name. */ @Override public ParameterValue<?> parameter(String name) throws ParameterNotFoundException { ensureNonNull("name", name); name = name.trim(); final MatrixParameterDescriptors descriptor = ((MatrixParameterDescriptors) this.descriptor); final String prefix = descriptor.prefix; RuntimeException cause = null; if (name.regionMatches(true, 0, prefix, 0, prefix.length())) { final int split = name.indexOf(descriptor.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 parameter(row, col); } 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.parameter(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 value in this group for a matrix element at the specified index. * Row and column index are 0 based. * * @param row The row indice. * @param column The column indice * @return The parameter value for the specified matrix element (never {@code null}). * @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds. */ public final ParameterValue<Double> parameter(final int row, final int column) throws IndexOutOfBoundsException { return parameter(row, column, numRow.intValue(), numCol.intValue()); } /** * Implementation of {@link #parameter(int,int)}. * * @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 value for the specified matrix element. * @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds. */ @SuppressWarnings("unchecked") // Because of array creation private ParameterValue<Double> parameter(final int row, final int column, final int numRow, final int numCol) throws IndexOutOfBoundsException { MatrixParameterDescriptors.checkIndice("row", row, numRow); MatrixParameterDescriptors.checkIndice("column", column, numCol); if (matrixValues == null) { matrixValues = new ParameterValue[numRow][]; } if (row >= matrixValues.length) { matrixValues = XArray.resize(matrixValues, numRow); } ParameterValue<Double>[] rowValues = matrixValues[row]; if (rowValues == null) { matrixValues[row] = rowValues = new ParameterValue[numCol]; } if (column >= rowValues.length) { matrixValues[row] = rowValues = XArray.resize(rowValues, numCol); } ParameterValue<Double> param = rowValues[column]; if (param == null) { rowValues[column] = param = new FloatParameter( ((MatrixParameterDescriptors) descriptor).descriptor(row, column, numRow, numCol)); } return param; } /** * Returns the parameters descriptors in this group. The amount of parameters depends * on the value of <code>"num_row"</code> and <code>"num_col"</code> parameters. */ public List<GeneralParameterDescriptor> descriptors() { return ((MatrixParameterDescriptors) descriptor).descriptors( numRow.intValue(), numCol.intValue()); } /** * Returns the parameters values in this group. The amount of parameters depends * on the value of <code>"num_row"</code> and <code>"num_col"</code> parameters. * The parameter array will contains only matrix elements which have been requested at * least once by one of {@code parameter(...)} methods. Never requested elements * are left to their default value and omitted from the returned array. */ @Override public List<GeneralParameterValue> values() { final int numRow = this.numRow.intValue(); final int numCol = this.numCol.intValue(); final ParameterValue[] parameters = new ParameterValue[numRow*numCol + 2]; int k = 0; parameters[k++] = this.numRow; parameters[k++] = this.numCol; if (matrixValues != null) { final int maxRow = Math.min(numRow, matrixValues.length); for (int j=0; j<maxRow; j++) { final ParameterValue[] rowValues = matrixValues[j]; if (rowValues != null) { final int maxCol = Math.min(numCol, rowValues.length); for (int i=0; i<maxCol; i++) { final ParameterValue value = rowValues[i]; if (value != null) { parameters[k++] = value; } } } } } return UnmodifiableArrayList.wrap((GeneralParameterValue[]) XArray.resize(parameters, k)); } /** * Forwards the call to the {@linkplain MatrixParameterDescriptors matrix parameter descriptors} * specified at construction time. */ public ParameterValueGroup createValue() { return (ParameterValueGroup) descriptor.createValue(); } /** * Creates a matrix from this group of parameters. * * @return A matrix created from this group of parameters. */ public Matrix getMatrix() { final int numRow = this.numRow.intValue(); final int numCol = this.numCol.intValue(); final Matrix matrix = MatrixFactory.create(numRow, numCol); if (matrixValues != null) { for (int j=0; j<numRow; j++) { final ParameterValue[] row = matrixValues[j]; if (row != null) { for (int i=0; i<numCol; i++) { final ParameterValue element = row[i]; if (element != null) { matrix.setElement(j, i, element.doubleValue()); } } } } } return matrix; } /** * Sets all parameter values to the element value in the specified matrix. * After this method call, {@link #values} will returns only the elements * different from the default value. * * @param matrix The matrix to copy in this group of parameters. */ @SuppressWarnings("unchecked") // Because of array creation public void setMatrix(final Matrix matrix) { final MatrixParameterDescriptors matrixDescriptor = ((MatrixParameterDescriptors) this.descriptor); final int numRow = matrix.getNumRow(); final int numCol = matrix.getNumCol(); this.numRow.setValue(numRow); this.numCol.setValue(numCol); for (int row=0; row<numRow; row++) { for (int col=0; col<numCol; col++) { final double element = matrix.getElement(row,col); ParameterDescriptor<Double> descriptor = matrixDescriptor.descriptor(row, col); final double value = descriptor.getDefaultValue(); if (Double.doubleToLongBits(element) == Double.doubleToLongBits(value)) { /* * Value matches the default value, so there is no need to keep it. * Remove entry to keep things sparse. */ if (matrixValues != null && matrixValues[row] != null) { matrixValues[row][col] = null; } continue; } if (matrixValues == null) { matrixValues = new ParameterValue[numRow][]; } if (matrixValues[row] == null ){ matrixValues[row] = new ParameterValue[numCol]; } matrixValues[row][col] = new FloatParameter(descriptor, element); } } } /** * Compare this object with the specified one for equality. */ @Override public boolean equals(final Object object) { if (object == this) { return true; // Slight optimization. } if (super.equals(object)) { final MatrixParameters that = (MatrixParameters) object; final int numRow = this.numRow.intValue(); final int numCol = this.numCol.intValue(); for (int j=0; j<numRow; j++) { for (int i=0; i<numCol; i++) { if (!Utilities.equals(this.parameter(j,i, numRow, numCol), that.parameter(j,i, numRow, numCol))) { return false; } } } return true; } return false; } /** * Returns a clone of this parameter group. */ @Override @SuppressWarnings("unchecked") public MatrixParameters clone() { final MatrixParameters copy = (MatrixParameters) super.clone(); if (copy.matrixValues != null) { copy.numRow = (ParameterValue) copy.parameter(0); copy.numCol = (ParameterValue) copy.parameter(1); copy.matrixValues = copy.matrixValues.clone(); for (int j=0; j<copy.matrixValues.length; j++) { ParameterValue[] array = copy.matrixValues[j]; if (array != null) { copy.matrixValues[j] = array = array.clone(); for (int i=0; i<array.length; i++) { if (array[i] != null) { array[i] = array[i].clone(); } } } } } return copy; } /** * Writes the content of this parameter to the specified table. * * @param table The table where to format the parameter value. * @throws IOException if an error occurs during output operation. */ @Override protected void write(final TableWriter table) throws IOException { table.write(getName(descriptor)); table.nextColumn(); table.write('='); table.nextColumn(); table.write(getMatrix().toString()); table.nextLine(); } }