001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2004-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2001, Institut de Recherche pour le Développement
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.parameter;
018:
019: // J2SE dependencies
020: import java.util.Collections;
021: import java.util.Iterator;
022: import java.util.List;
023: import java.util.Map;
024: import javax.units.Unit;
025:
026: // OpenGIS dependencies
027: import org.opengis.parameter.GeneralParameterDescriptor;
028: import org.opengis.parameter.GeneralParameterValue;
029: import org.opengis.parameter.InvalidParameterNameException;
030: import org.opengis.parameter.ParameterDescriptor;
031: import org.opengis.parameter.ParameterNotFoundException;
032: import org.opengis.parameter.ParameterValue;
033: import org.opengis.parameter.ParameterValueGroup;
034: import org.opengis.referencing.operation.Matrix;
035:
036: // Geotools dependencies
037: import org.geotools.referencing.AbstractIdentifiedObject;
038: import org.geotools.referencing.operation.matrix.MatrixFactory;
039: import org.geotools.resources.UnmodifiableArrayList;
040: import org.geotools.resources.Utilities;
041: import org.geotools.resources.i18n.Errors;
042: import org.geotools.resources.i18n.ErrorKeys;
043:
044: /**
045: * A parameter group for {@linkplain Matrix matrix} elements. The amount of
046: * {@linkplain ParameterValue parameter values} is extensible, i.e. it can grown or
047: * shrink according the value of <code>"num_row"</code> and <code>"num_col"</code>
048: * parameters. The parameters format may vary according the information provided to
049: * the constructor, but it is typically as below:
050: * <blockquote><pre>
051: * num_row
052: * num_col
053: * elt_0_0
054: * elt_0_1
055: * ...
056: * elt_0_<var><num_col-1></var>
057: * elt_1_0
058: * elt_1_1
059: * ...
060: * elt_<var><num_row-1></var>_<var><num_col-1></var>
061: * </pre></blockquote>
062: *
063: * @since 2.1
064: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/parameter/MatrixParameterDescriptors.java $
065: * @version $Id: MatrixParameterDescriptors.java 20874 2006-08-07 10:00:01Z jgarnett $
066: * @author Martin Desruisseaux
067: *
068: * @see MatrixParameters
069: */
070: public class MatrixParameterDescriptors extends
071: DefaultParameterDescriptorGroup {
072: /**
073: * Serial number for interoperability with different versions.
074: */
075: private static final long serialVersionUID = -7386537348359343836L;
076:
077: /**
078: * The default matrix size for the
079: * {@linkplain #MatrixParameterDescriptors(Map) one-argument constructor}.
080: */
081: public static final int DEFAULT_MATRIX_SIZE = 3;
082:
083: /**
084: * The height and weight of the matrix of {@link #parameters} to cache. Descriptors
085: * for row or column indices greater than or equals to this value will not be cached.
086: */
087: private static final int CACHE_SIZE = 8;
088:
089: /**
090: * The cached descriptors for each elements in a matrix. Descriptors do not depends
091: * on matrix element values. Consequently, the same descriptors can be reused for all
092: * {@link MatrixParameters} instances.
093: */
094: private final ParameterDescriptor[] parameters = new ParameterDescriptor[CACHE_SIZE
095: * CACHE_SIZE];
096:
097: /**
098: * The descriptor for the <code>"num_row"</code> parameter.
099: */
100: protected final ParameterDescriptor numRow;
101:
102: /**
103: * The descriptor for the <code>"num_col"</code> parameter.
104: */
105: protected final ParameterDescriptor numCol;
106:
107: /**
108: * The prefix to insert in front of parameter name for each matrix elements.
109: */
110: protected final String prefix;
111:
112: /**
113: * The separator between the row and the column index in parameter names.
114: */
115: protected final char separator;
116:
117: /**
118: * Constructs a parameter group with default name format matching
119: * <cite><A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html">Well
120: * Known Text</A></cite> usages.
121: *
122: * @param properties Set of properties. Should contains at least <code>"name"</code>.
123: */
124: public MatrixParameterDescriptors(final Map properties) {
125: /*
126: * Note: the upper limit given in the operation parameters is arbitrary. A high
127: * value doesn't make much sense anyway since matrix size for projective
128: * transform will usually not be much more than 5, and the storage scheme
129: * used in this implementation is inefficient for large amount of matrix
130: * elements.
131: */
132: this (properties, new ParameterDescriptor[] {
133: new DefaultParameterDescriptor("num_row",
134: DEFAULT_MATRIX_SIZE, 2, 50),
135: new DefaultParameterDescriptor("num_col",
136: DEFAULT_MATRIX_SIZE, 2, 50) }, "elt_", '_');
137: }
138:
139: /**
140: * Constructs a parameter group. The properties map is given unchanged to the
141: * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
142: * The {@code parameters} array should contains parameters <strong>other</strong>
143: * than matrix elements. The first parameter is assumed to be the number of rows, and
144: * the second parameter the number of columns. All extra parameters are ignored.
145: *
146: * @param properties Set of properties. Should contains at least <code>"name"</code>.
147: * @param parameters The <code>"num_row"</code> and <code>"num_col"</code> parameters.
148: * @param prefix The prefix to insert in front of parameter name for each matrix elements.
149: * @param separator The separator between the row and the column index in parameter names.
150: */
151: public MatrixParameterDescriptors(final Map properties,
152: ParameterDescriptor[] parameters, final String prefix,
153: final char separator) {
154: super (properties, parameters);
155: if (parameters.length < 2) {
156: // TODO: provide a localized message
157: throw new IllegalArgumentException();
158: }
159: numRow = parameters[0];
160: numCol = parameters[1];
161: ensureNonNull("prefix", prefix);
162: this .prefix = prefix;
163: this .separator = separator;
164: }
165:
166: /**
167: * Verify that the specified index is included in the expected range of values.
168: *
169: * @param name The parameter name. To be used for formatting error message.
170: * @param index The indice to check.
171: * @param upper The upper range value, exclusive.
172: * @throws IndexOutOfBoundsException if {@code index} is outside the expected range.
173: */
174: static void checkIndice(final String name, final int index,
175: final int upper) throws IndexOutOfBoundsException {
176: if (index < 0 || index >= upper) {
177: throw new IndexOutOfBoundsException(Errors.format(
178: ErrorKeys.ILLEGAL_ARGUMENT_$2, name, new Integer(
179: index)));
180: }
181: }
182:
183: /**
184: * Returns the parameter in this group for the specified name. The name can be a matrix element
185: * if it uses the following syntax: <code>"elt_<var>row</var>_<var>col</var>"</code> where
186: * <code>"elt_"</code> is the {@linkplain #prefix} for all matrix elements, and <var>row</var>
187: * and <var>col</var> are row and column indices respectively. For example
188: * <code>"elt_2_1"</code> is the element name for the value at line 2 and row 1. The row and
189: * column index are 0 based.
190: *
191: * @param name The case insensitive name of the parameter to search for.
192: * @return The parameter for the given name.
193: * @throws ParameterNotFoundException if there is no parameter for the given name.
194: */
195: public final GeneralParameterDescriptor descriptor(final String name)
196: throws ParameterNotFoundException {
197: return descriptor(name, ((Number) numRow.getMaximumValue())
198: .intValue(), ((Number) numCol.getMaximumValue())
199: .intValue());
200: }
201:
202: /**
203: * Implementation of the {@link #descriptor(String)} method.
204: *
205: * @param name The case insensitive name of the parameter to search for.
206: * @param numRow The maximum number of rows.
207: * @param numCol The maximum number of columns.
208: * @return The parameter for the given name.
209: * @throws ParameterNotFoundException if there is no parameter for the given name.
210: */
211: final GeneralParameterDescriptor descriptor(String name,
212: final int numRow, final int numCol)
213: throws ParameterNotFoundException {
214: ensureNonNull("name", name);
215: name = name.trim();
216: RuntimeException cause = null;
217: if (name.regionMatches(true, 0, prefix, 0, prefix.length())) {
218: final int split = name.indexOf(separator, prefix.length());
219: if (split >= 0)
220: try {
221: final int row = Integer.parseInt(name.substring(
222: prefix.length(), split));
223: final int col = Integer.parseInt(name
224: .substring(split + 1));
225: return descriptor(row, col, numRow, numCol);
226: } catch (NumberFormatException exception) {
227: cause = exception;
228: } catch (IndexOutOfBoundsException exception) {
229: cause = exception;
230: }
231: }
232: /*
233: * The parameter name is not a matrix element name. Search in the super
234: * class for other parameters, especially "num_row" and "num_col".
235: */
236: try {
237: return super .descriptor(name);
238: } catch (ParameterNotFoundException exception) {
239: if (cause != null)
240: try {
241: exception.initCause(cause);
242: } catch (IllegalStateException ignore) {
243: // A cause has already be given to the exception. Forget the cause then.
244: }
245: throw exception;
246: }
247: }
248:
249: /**
250: * Returns the parameter in this group for a matrix element at the specified
251: * index. row and column indices are 0 based. Indices must be lower that the
252: * {@linkplain ParameterDescriptor#getMaximumValue maximum values}
253: * given to the {@link #numRow} and {@link #numCol} parameters.
254: *
255: * @param row The row indice.
256: * @param column The column indice
257: * @return The parameter descriptor for the specified matrix element.
258: * @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds.
259: */
260: public final ParameterDescriptor descriptor(final int row,
261: final int column) throws IndexOutOfBoundsException {
262: return descriptor(row, column, ((Number) numRow
263: .getMaximumValue()).intValue(), ((Number) numCol
264: .getMaximumValue()).intValue());
265: }
266:
267: /**
268: * Implementation of the {@link #descriptor(int,int)} method.
269: *
270: * @param row The row indice.
271: * @param column The column indice
272: * @param numRow The maximum number of rows.
273: * @param numCol The maximum number of columns.
274: * @return The parameter descriptor for the specified matrix element.
275: * @throws IndexOutOfBoundsException if {@code row} or {@code column} is out of bounds.
276: */
277: final ParameterDescriptor descriptor(final int row,
278: final int column, final int numRow, final int numCol)
279: throws IndexOutOfBoundsException {
280: checkIndice("row", row, numRow);
281: checkIndice("column", column, numCol);
282: int index = -1;
283: ParameterDescriptor param;
284: if (row < CACHE_SIZE && column < CACHE_SIZE) {
285: index = row * CACHE_SIZE + column;
286: param = parameters[index];
287: if (param != null) {
288: return param;
289: }
290: }
291: /*
292: * Parameter not found in the cache. Create a new one and cache it for future reuse.
293: * Note that this cache is shared by all MatrixParameterDescriptors instance. There
294: * is no need to synchronize since it is not a big deal if the same parameter is
295: * constructed twice.
296: */
297: param = new DefaultParameterDescriptor(Collections
298: .singletonMap(NAME_KEY, prefix + row + separator
299: + column), Double.TYPE, null,
300: org.geotools.parameter.Parameter
301: .wrap(row == column ? 1.0 : 0.0), null, null,
302: Unit.ONE, true);
303: if (index >= 0) {
304: parameters[index] = param;
305: }
306: return param;
307: }
308:
309: /**
310: * Returns the parameters in this group. The number or elements is inferred from the
311: * {@linkplain ParameterDescriptor#getDefaultValue default values}
312: * given to the {@link #numRow} and {@link #numCol} parameters.
313: *
314: * @return The matrix parameters, including all elements.
315: */
316: public final List/*<GeneralParameterDescriptor>*/descriptors() {
317: return descriptors(((Number) this .numRow.getDefaultValue())
318: .intValue(), ((Number) this .numCol.getDefaultValue())
319: .intValue());
320: }
321:
322: /**
323: * Implementation of the {@link #descriptors()} method.
324: * Returns the parameters in this group for a matrix of the specified size.
325: *
326: * @param numRow The number of rows.
327: * @param numCol The number of columns.
328: * @return The matrix parameters, including all elements.
329: */
330: final List/*<GeneralParameterDescriptor>*/descriptors(
331: final int numRow, final int numCol) {
332: final ParameterDescriptor[] parameters = new ParameterDescriptor[numRow
333: * numCol + 2];
334: int k = 0;
335: parameters[k++] = this .numRow;
336: parameters[k++] = this .numCol;
337: for (int j = 0; j < numRow; j++) {
338: for (int i = 0; i < numCol; i++) {
339: parameters[k++] = descriptor(j, i, numRow, numCol);
340: }
341: }
342: assert k == parameters.length : k;
343: return new UnmodifiableArrayList(parameters);
344: }
345:
346: /**
347: * Creates a new instance of {@linkplain MatrixParameters parameter values} with
348: * elements initialized to the 1 on the diagonal, and 0 everywere else. The returned
349: * parameter group is extensible, i.e. the number of elements will depends upon the
350: * value associated to the {@link #numRow} and {@link #numCol numCol} parameters.
351: */
352: public GeneralParameterValue createValue() {
353: return new MatrixParameters(this );
354: }
355:
356: /**
357: * Constructs a matrix from a group of parameters.
358: *
359: * @param parameters The group of parameters.
360: * @return A matrix constructed from the specified group of parameters.
361: * @throws InvalidParameterNameException if a parameter name was not recognized.
362: */
363: public Matrix getMatrix(final ParameterValueGroup parameters)
364: throws InvalidParameterNameException {
365: if (parameters instanceof MatrixParameters) {
366: // More efficient implementation
367: return ((MatrixParameters) parameters).getMatrix();
368: }
369: // Fallback on the general case (others implementations)
370: final ParameterValue numRowParam = parameters.parameter(numRow
371: .getName().toString());
372: final ParameterValue numColParam = parameters.parameter(numCol
373: .getName().toString());
374: final int numRow = numRowParam.intValue();
375: final int numCol = numColParam.intValue();
376: final Matrix matrix = MatrixFactory.create(numRow, numCol);
377: final List/*<GeneralParameterValue>*/params = parameters
378: .values();
379: if (params != null) {
380: for (final Iterator it = params.iterator(); it.hasNext();) {
381: final GeneralParameterValue param = (GeneralParameterValue) it
382: .next();
383: if (param == numRowParam || param == numColParam) {
384: continue;
385: }
386: RuntimeException cause = null;
387: final String name = param.getDescriptor().getName()
388: .toString();
389: if (name.regionMatches(true, 0, prefix, 0, prefix
390: .length())) {
391: final int split = name.indexOf(separator, prefix
392: .length());
393: if (split >= 0)
394: try {
395: final int row = Integer.parseInt(name
396: .substring(prefix.length(), split));
397: final int col = Integer.parseInt(name
398: .substring(split + 1));
399: matrix.setElement(row, col,
400: ((ParameterValue) param)
401: .doubleValue());
402: continue;
403: } catch (NumberFormatException exception) {
404: cause = exception;
405: } catch (IndexOutOfBoundsException exception) {
406: cause = exception;
407: }
408: }
409: final InvalidParameterNameException exception;
410: exception = new InvalidParameterNameException(
411: Errors
412: .format(
413: ErrorKeys.UNEXPECTED_PARAMETER_$1,
414: name), name);
415: if (cause != null) {
416: exception.initCause(cause);
417: }
418: throw exception;
419: }
420: }
421: return matrix;
422: }
423:
424: /**
425: * Compares the specified object with this parameter group for equality.
426: *
427: * @param object The object to compare to {@code this}.
428: * @param compareMetadata {@code true} for performing a strict comparaison, or
429: * {@code false} for comparing only properties relevant to transformations.
430: * @return {@code true} if both objects are equal.
431: */
432: public boolean equals(final AbstractIdentifiedObject object,
433: final boolean compareMetadata) {
434: if (super .equals(object, compareMetadata)) {
435: final MatrixParameterDescriptors that = (MatrixParameterDescriptors) object;
436: return this .separator == that.separator
437: && Utilities.equals(this .prefix, that.prefix);
438: }
439: return false;
440: }
441:
442: /**
443: * Returns a hash value for this parameter.
444: *
445: * @return The hash code value. This value doesn't need to be the same
446: * in past or future versions of this class.
447: */
448: public int hashCode() {
449: return super .hashCode() ^ prefix.hashCode() ^ 37 * separator;
450: }
451: }
|