001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: *
005: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
006: * (C) 2001, Institut de Recherche pour le Développement
007: *
008: * This library is free software; you can redistribute it and/or
009: * modify it under the terms of the GNU Lesser General Public
010: * License as published by the Free Software Foundation; either
011: * version 2.1 of the License, or (at your option) any later version.
012: *
013: * This library is distributed in the hope that it will be useful,
014: * but WITHOUT ANY WARRANTY; without even the implied warranty of
015: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
016: * Lesser General Public License for more details.
017: *
018: * This package contains documentation from OpenGIS specifications.
019: * OpenGIS consortium's work is fully acknowledged here.
020: */
021: package org.geotools.referencing.operation.transform;
022:
023: // J2SE dependencies and extensions
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.Point2D;
026: import java.io.Serializable;
027: import java.util.Arrays;
028: import java.util.HashMap;
029: import java.util.Map;
030: import javax.vecmath.SingularMatrixException;
031: import javax.units.NonSI;
032:
033: // OpenGIS dependencies
034: import org.opengis.parameter.ParameterDescriptor;
035: import org.opengis.parameter.ParameterDescriptorGroup;
036: import org.opengis.parameter.ParameterNotFoundException;
037: import org.opengis.parameter.ParameterValueGroup;
038: import org.opengis.referencing.operation.Conversion;
039: import org.opengis.referencing.operation.MathTransform;
040: import org.opengis.referencing.operation.Matrix;
041: import org.opengis.referencing.operation.NoninvertibleTransformException;
042: import org.opengis.referencing.operation.OperationMethod;
043: import org.opengis.geometry.DirectPosition;
044:
045: // Geotools dependencies
046: import org.geotools.metadata.iso.citation.Citations;
047: import org.geotools.parameter.MatrixParameterDescriptors;
048: import org.geotools.parameter.MatrixParameters;
049: import org.geotools.referencing.NamedIdentifier;
050: import org.geotools.referencing.operation.LinearTransform;
051: import org.geotools.referencing.operation.MathTransformProvider;
052: import org.geotools.referencing.operation.matrix.MatrixFactory;
053: import org.geotools.referencing.operation.matrix.GeneralMatrix;
054: import org.geotools.referencing.operation.matrix.XMatrix;
055: import org.geotools.resources.i18n.VocabularyKeys;
056: import org.geotools.resources.i18n.Vocabulary;
057: import org.geotools.resources.i18n.ErrorKeys;
058: import org.geotools.resources.i18n.Errors;
059:
060: /**
061: * A usually affine, or otherwise a projective transform. A projective transform is capable of
062: * mapping an arbitrary quadrilateral into another arbitrary quadrilateral, while preserving the
063: * straightness of lines. In the special case where the transform is affine, the parallelism of
064: * lines in the source is preserved in the output.
065: * <p>
066: * Such a coordinate transformation can be represented by a square {@linkplain GeneralMatrix matrix}
067: * of an arbitrary size. Point coordinates must have a dimension equals to
068: * <code>{@linkplain Matrix#getNumCol}-1</code>. For example, for square matrix of size 4×4,
069: * coordinate points are three-dimensional. The transformed points <code>(x',y',z')</code> are
070: * computed as below (note that this computation is similar to
071: * {@link javax.media.jai.PerspectiveTransform} in <cite>Java Advanced Imaging</cite>):
072: *
073: * <blockquote><pre>
074: * [ u ] [ m<sub>00</sub> m<sub>01</sub> m<sub>02</sub> m<sub>03</sub> ] [ x ]
075: * [ v ] = [ m<sub>10</sub> m<sub>11</sub> m<sub>12</sub> m<sub>13</sub> ] [ y ]
076: * [ w ] [ m<sub>20</sub> m<sub>21</sub> m<sub>22</sub> m<sub>23</sub> ] [ z ]
077: * [ t ] [ m<sub>30</sub> m<sub>31</sub> m<sub>32</sub> m<sub>33</sub> ] [ 1 ]
078: *
079: * x' = u/t
080: * y' = v/t
081: * y' = w/t
082: * </pre></blockquote>
083: *
084: * In the special case of an affine transform, the last row contains only zero
085: * values except in the last column, which contains 1.
086: *
087: * @since 2.0
088: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/transform/ProjectiveTransform.java $
089: * @version $Id: ProjectiveTransform.java 24925 2007-03-27 20:12:08Z jgarnett $
090: * @author Martin Desruisseaux
091: *
092: * @see javax.media.jai.PerspectiveTransform
093: * @see java.awt.geom.AffineTransform
094: * @see <A HREF="http://mathworld.wolfram.com/AffineTransformation.html">Affine transformation on MathWorld</A>
095: */
096: public class ProjectiveTransform extends AbstractMathTransform
097: implements LinearTransform, Serializable {
098: /**
099: * Serial number for interoperability with different versions.
100: */
101: private static final long serialVersionUID = -2104496465933824935L;
102:
103: /**
104: * The number of rows.
105: */
106: private final int numRow;
107:
108: /**
109: * The number of columns.
110: */
111: private final int numCol;
112:
113: /**
114: * Elements of the matrix. Column indice vary fastest.
115: */
116: private final double[] elt;
117:
118: /**
119: * The inverse transform. Will be created only when first needed.
120: */
121: private transient ProjectiveTransform inverse;
122:
123: /**
124: * Constructs a transform from the specified matrix.
125: * The matrix should be affine, but it will not be verified.
126: *
127: * @param matrix The matrix.
128: */
129: protected ProjectiveTransform(final Matrix matrix) {
130: numRow = matrix.getNumRow();
131: numCol = matrix.getNumCol();
132: elt = new double[numRow * numCol];
133: int index = 0;
134: for (int j = 0; j < numRow; j++) {
135: for (int i = 0; i < numCol; i++) {
136: elt[index++] = matrix.getElement(j, i);
137: }
138: }
139: }
140:
141: /**
142: * Creates a transform for the specified matrix.
143: * The matrix should be affine, but it is not be verified.
144: */
145: public static LinearTransform create(final Matrix matrix) {
146: final int dimension = matrix.getNumRow() - 1;
147: if (dimension == matrix.getNumCol() - 1) {
148: if (matrix.isIdentity()) {
149: return IdentityTransform.create(dimension);
150: }
151: final GeneralMatrix m = toGMatrix(matrix);
152: if (m.isAffine()) {
153: switch (dimension) {
154: case 1:
155: return LinearTransform1D.create(m.getElement(0, 0),
156: m.getElement(0, 1));
157: case 2:
158: return create(m.toAffineTransform2D());
159: }
160: }
161: }
162: return new ProjectiveTransform(matrix);
163: }
164:
165: /**
166: * Creates a transform for the specified matrix as a Java2D object.
167: * This method is provided for interoperability with
168: * <A HREF="http://java.sun.com/products/java-media/2D/index.jsp">Java2D</A>.
169: */
170: public static LinearTransform create(final AffineTransform matrix) {
171: if (matrix.isIdentity()) {
172: return IdentityTransform.create(2);
173: }
174: return new AffineTransform2D(matrix);
175: }
176:
177: /**
178: * Creates a transform that apply a uniform scale along all axis.
179: *
180: * @param dimension The input and output dimensions.
181: * @param scale The scale factor.
182: *
183: * @since 2.3
184: */
185: public static LinearTransform createScale(final int dimension,
186: final double scale) {
187: if (scale == 1) {
188: return IdentityTransform.create(dimension);
189: }
190: final Matrix matrix = new GeneralMatrix(dimension + 1);
191: for (int i = 0; i < dimension; i++) {
192: matrix.setElement(i, i, scale);
193: }
194: return create(matrix);
195: }
196:
197: /**
198: * Creates a transform that apply the same translation along all axis.
199: *
200: * @param dimension The input and output dimensions.
201: * @param offset The translation.
202: *
203: * @since 2.3
204: */
205: public static LinearTransform createTranslation(
206: final int dimension, final double offset) {
207: if (offset == 0) {
208: return IdentityTransform.create(dimension);
209: }
210: final Matrix matrix = new GeneralMatrix(dimension + 1);
211: for (int i = 0; i < dimension; i++) {
212: matrix.setElement(i, dimension, offset);
213: }
214: return create(matrix);
215: }
216:
217: /**
218: * Creates a matrix that keep only a subset of the ordinate values.
219: * The dimension of source coordinates is {@code sourceDim} and
220: * the dimension of target coordinates is {@code toKeep.length}.
221: *
222: * @param sourceDim the dimension of source coordinates.
223: * @param toKeep the indices of ordinate values to keep.
224: * @return The matrix to give to the {@link #create(Matrix)}
225: * method in order to create the transform.
226: * @throws IndexOutOfBoundsException if a value of {@code toKeep}
227: * is lower than 0 or not smaller than {@code sourceDim}.
228: */
229: public static Matrix createSelectMatrix(final int sourceDim,
230: final int[] toKeep) throws IndexOutOfBoundsException {
231: final int targetDim = toKeep.length;
232: final XMatrix matrix = MatrixFactory.create(targetDim + 1,
233: sourceDim + 1);
234: matrix.setZero();
235: for (int j = 0; j < targetDim; j++) {
236: matrix.setElement(j, toKeep[j], 1);
237: }
238: matrix.setElement(targetDim, sourceDim, 1);
239: return matrix;
240: }
241:
242: /**
243: * Returns the parameter descriptors for this math transform.
244: */
245: public ParameterDescriptorGroup getParameterDescriptors() {
246: return ProviderAffine.PARAMETERS;
247: }
248:
249: /**
250: * Returns the matrix elements as a group of parameters values. The number of parameters
251: * depends on the matrix size. Only matrix elements different from their default value
252: * will be included in this group.
253: *
254: * @param matrix The matrix to returns as a group of parameters.
255: * @return A copy of the parameter values for this math transform.
256: */
257: static ParameterValueGroup getParameterValues(final Matrix matrix) {
258: final MatrixParameters values;
259: values = (MatrixParameters) ProviderAffine.PARAMETERS
260: .createValue();
261: values.setMatrix(matrix);
262: return values;
263: }
264:
265: /**
266: * Returns the matrix elements as a group of parameters values. The number of parameters
267: * depends on the matrix size. Only matrix elements different from their default value
268: * will be included in this group.
269: *
270: * @return A copy of the parameter values for this math transform.
271: */
272: public ParameterValueGroup getParameterValues() {
273: return getParameterValues(getMatrix());
274: }
275:
276: /**
277: * Transforms an array of floating point coordinates by this matrix. Point coordinates
278: * must have a dimension equals to <code>{@link Matrix#getNumCol}-1</code>. For example,
279: * for square matrix of size 4×4, coordinate points are three-dimensional and
280: * stored in the arrays starting at the specified offset ({@code srcOff}) in the order
281: * <code>[x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>,
282: * x<sub>1</sub>, y<sub>1</sub>, z<sub>1</sub>...,
283: * x<sub>n</sub>, y<sub>n</sub>, z<sub>n</sub>]</code>.
284: *
285: * @param srcPts The array containing the source point coordinates.
286: * @param srcOff The offset to the first point to be transformed in the source array.
287: * @param dstPts The array into which the transformed point coordinates are returned.
288: * @param dstOff The offset to the location of the first transformed point that is stored
289: * in the destination array. The source and destination array sections can
290: * be overlaps.
291: * @param numPts The number of points to be transformed
292: */
293: public void transform(float[] srcPts, int srcOff,
294: final float[] dstPts, int dstOff, int numPts) {
295: final int inputDimension = numCol - 1; // The last ordinate will be assumed equals to 1.
296: final int outputDimension = numRow - 1;
297: final double[] buffer = new double[numRow];
298: if (srcPts == dstPts) {
299: // We are going to write in the source array. Checks if
300: // source and destination sections are going to clash.
301: final int upperSrc = srcOff + numPts * inputDimension;
302: if (upperSrc > dstOff) {
303: if (inputDimension >= outputDimension ? dstOff > srcOff
304: : dstOff + numPts * outputDimension > upperSrc) {
305: // If source overlaps destination, then the easiest workaround is
306: // to copy source data. This is not the most efficient however...
307: srcPts = new float[numPts * inputDimension];
308: System.arraycopy(dstPts, srcOff, srcPts, 0,
309: srcPts.length);
310: srcOff = 0;
311: }
312: }
313: }
314: while (--numPts >= 0) {
315: int mix = 0;
316: for (int j = 0; j < numRow; j++) {
317: double sum = elt[mix + inputDimension];
318: for (int i = 0; i < inputDimension; i++) {
319: sum += srcPts[srcOff + i] * elt[mix++];
320: }
321: buffer[j] = sum;
322: mix++;
323: }
324: final double w = buffer[outputDimension];
325: for (int j = 0; j < outputDimension; j++) {
326: // 'w' is equals to 1 if the transform is affine.
327: dstPts[dstOff++] = (float) (buffer[j] / w);
328: }
329: srcOff += inputDimension;
330: }
331: }
332:
333: /**
334: * Transforms an array of floating point coordinates by this matrix. Point coordinates
335: * must have a dimension equals to <code>{@link Matrix#getNumCol}-1</code>. For example,
336: * for square matrix of size 4×4, coordinate points are three-dimensional and
337: * stored in the arrays starting at the specified offset ({@code srcOff}) in the order
338: * <code>[x<sub>0</sub>, y<sub>0</sub>, z<sub>0</sub>,
339: * x<sub>1</sub>, y<sub>1</sub>, z<sub>1</sub>...,
340: * x<sub>n</sub>, y<sub>n</sub>, z<sub>n</sub>]</code>.
341: *
342: * @param srcPts The array containing the source point coordinates.
343: * @param srcOff The offset to the first point to be transformed in the source array.
344: * @param dstPts The array into which the transformed point coordinates are returned.
345: * @param dstOff The offset to the location of the first transformed point that is stored
346: * in the destination array. The source and destination array sections can
347: * be overlaps.
348: * @param numPts The number of points to be transformed
349: */
350: public void transform(double[] srcPts, int srcOff,
351: final double[] dstPts, int dstOff, int numPts) {
352: final int inputDimension = numCol - 1; // The last ordinate will be assumed equals to 1.
353: final int outputDimension = numRow - 1;
354: final double[] buffer = new double[numRow];
355: if (srcPts == dstPts) {
356: // We are going to write in the source array. Checks if
357: // source and destination sections are going to clash.
358: final int upperSrc = srcOff + numPts * inputDimension;
359: if (upperSrc > dstOff) {
360: if (inputDimension >= outputDimension ? dstOff > srcOff
361: : dstOff + numPts * outputDimension > upperSrc) {
362: // If source overlaps destination, then the easiest workaround is
363: // to copy source data. This is not the most efficient however...
364: srcPts = new double[numPts * inputDimension];
365: System.arraycopy(dstPts, srcOff, srcPts, 0,
366: srcPts.length);
367: srcOff = 0;
368: }
369: }
370: }
371: while (--numPts >= 0) {
372: int mix = 0;
373: for (int j = 0; j < numRow; j++) {
374: double sum = elt[mix + inputDimension];
375: for (int i = 0; i < inputDimension; i++) {
376: sum += srcPts[srcOff + i] * elt[mix++];
377: }
378: buffer[j] = sum;
379: mix++;
380: }
381: final double w = buffer[outputDimension];
382: for (int j = 0; j < outputDimension; j++) {
383: // 'w' is equals to 1 if the transform is affine.
384: dstPts[dstOff++] = buffer[j] / w;
385: }
386: srcOff += inputDimension;
387: }
388: }
389:
390: /**
391: * Gets the derivative of this transform at a point.
392: * For a matrix transform, the derivative is the
393: * same everywhere.
394: */
395: public Matrix derivative(final Point2D point) {
396: return derivative((DirectPosition) null);
397: }
398:
399: /**
400: * Gets the derivative of this transform at a point.
401: * For a matrix transform, the derivative is the
402: * same everywhere.
403: */
404: public Matrix derivative(final DirectPosition point) {
405: final GeneralMatrix matrix = getGeneralMatrix();
406: matrix.setSize(numRow - 1, numCol - 1);
407: return matrix;
408: }
409:
410: /**
411: * Returns a copy of the matrix.
412: */
413: public Matrix getMatrix() {
414: return getGeneralMatrix();
415: }
416:
417: /**
418: * Returns a copy of the matrix.
419: */
420: private GeneralMatrix getGeneralMatrix() {
421: return new GeneralMatrix(numRow, numCol, elt);
422: }
423:
424: /**
425: * Gets the dimension of input points.
426: */
427: public int getSourceDimensions() {
428: return numCol - 1;
429: }
430:
431: /**
432: * Gets the dimension of output points.
433: */
434: public int getTargetDimensions() {
435: return numRow - 1;
436: }
437:
438: /**
439: * Tests whether this transform does not move any points.
440: */
441: public boolean isIdentity() {
442: if (numRow != numCol) {
443: return false;
444: }
445: int index = 0;
446: for (int j = 0; j < numRow; j++) {
447: for (int i = 0; i < numCol; i++) {
448: if (elt[index++] != (i == j ? 1 : 0)) {
449: return false;
450: }
451: }
452: }
453: assert isIdentity(0);
454: return true;
455: }
456:
457: /**
458: * Tests whether this transform does not move any points by using the provided tolerance.
459: * This method work in the same way than
460: * {@link org.geotools.referencing.operation.matrix.XMatrix#isIdentity(double)}.
461: *
462: * @since 2.4
463: */
464: public boolean isIdentity(double tolerance) {
465: tolerance = Math.abs(tolerance);
466: if (numRow != numCol) {
467: return false;
468: }
469: int index = 0;
470: for (int j = 0; j < numRow; j++) {
471: for (int i = 0; i < numCol; i++) {
472: double e = elt[index++];
473: if (i == j) {
474: e--;
475: }
476: // Uses '!' in order to catch NaN values.
477: if (!(Math.abs(e) <= tolerance)) {
478: return false;
479: }
480: }
481: }
482: return true;
483: }
484:
485: /**
486: * Creates the inverse transform of this object.
487: */
488: public MathTransform inverse()
489: throws NoninvertibleTransformException {
490: // No need to synchronize. This is not a big deal if the same object is created twice.
491: if (inverse == null) {
492: if (isIdentity()) {
493: inverse = this ;
494: } else {
495: final XMatrix matrix = getGeneralMatrix();
496: try {
497: matrix.invert();
498: } catch (SingularMatrixException exception) {
499: throw new NoninvertibleTransformException(Errors
500: .format(ErrorKeys.NONINVERTIBLE_TRANSFORM),
501: exception);
502: }
503: inverse = new ProjectiveTransform(matrix);
504: inverse.inverse = this ;
505: }
506: }
507: return inverse;
508: }
509:
510: /**
511: * Creates an inverse transform using the specified matrix.
512: * To be overridden by {@link GeocentricAffineTransform}.
513: */
514: MathTransform createInverse(final Matrix matrix) {
515: return new ProjectiveTransform(matrix);
516: }
517:
518: /**
519: * Returns a hash value for this transform.
520: * This value need not remain consistent between
521: * different implementations of the same class.
522: */
523: public int hashCode() {
524: long code = serialVersionUID;
525: for (int i = elt.length; --i >= 0;) {
526: code = code * 37 + Double.doubleToLongBits(elt[i]);
527: }
528: return (int) (code >>> 32) ^ (int) code;
529: }
530:
531: /**
532: * Compares the specified object with
533: * this math transform for equality.
534: */
535: public boolean equals(final Object object) {
536: if (object == this ) {
537: // Slight optimization
538: return true;
539: }
540: if (super .equals(object)) {
541: final ProjectiveTransform that = (ProjectiveTransform) object;
542: return this .numRow == that.numRow
543: && this .numCol == that.numCol
544: && Arrays.equals(this .elt, that.elt);
545: }
546: return false;
547: }
548:
549: /**
550: * The provider for the "<cite>Affine general parametric transformation</cite>" (EPSG 9624).
551: * The OGC's name is {@code "Affine"}. The default matrix size is
552: * {@value org.geotools.parameter.MatrixParameterDescriptors#DEFAULT_MATRIX_SIZE}×{@value
553: * org.geotools.parameter.MatrixParameterDescriptors#DEFAULT_MATRIX_SIZE}.
554: * <p>
555: * Note that affine transform is a special case of projective transform.
556: *
557: * @version $Id: ProjectiveTransform.java 24925 2007-03-27 20:12:08Z jgarnett $
558: * @author Martin Desruisseaux
559: */
560: public static final class ProviderAffine extends
561: MathTransformProvider {
562: /**
563: * Serial number for interoperability with different versions.
564: */
565: private static final long serialVersionUID = 649555815622129472L;
566:
567: /**
568: * The set of predefined providers.
569: */
570: private static final ProviderAffine[] methods = new ProviderAffine[8];
571:
572: /**
573: * The parameters group.
574: *
575: * @todo We should register EPSG parameter identifiers (A0, A1, A2, B0, B1, B2) as well.
576: */
577: static final ParameterDescriptorGroup PARAMETERS;
578: static {
579: final NamedIdentifier name = new NamedIdentifier(
580: Citations.OGC, "Affine");
581: final Map properties = new HashMap(4, 0.8f);
582: properties.put(NAME_KEY, name);
583: properties.put(IDENTIFIERS_KEY, name);
584: properties
585: .put(
586: ALIAS_KEY,
587: new NamedIdentifier[] {
588: name,
589: new NamedIdentifier(Citations.EPSG,
590: "Affine general parametric transformation"),
591: new NamedIdentifier(Citations.EPSG,
592: "9624"),
593: new NamedIdentifier(
594: Citations.GEOTOOLS,
595: Vocabulary
596: .formatInternational(VocabularyKeys.AFFINE_TRANSFORM)) });
597: PARAMETERS = new MatrixParameterDescriptors(properties);
598: }
599:
600: /**
601: * Creates a provider for affine transform with a default matrix size.
602: */
603: public ProviderAffine() {
604: this (MatrixParameterDescriptors.DEFAULT_MATRIX_SIZE - 1,
605: MatrixParameterDescriptors.DEFAULT_MATRIX_SIZE - 1);
606: methods[MatrixParameterDescriptors.DEFAULT_MATRIX_SIZE - 2] = this ;
607: }
608:
609: /**
610: * Creates a provider for affine transform with the specified dimensions.
611: */
612: private ProviderAffine(final int sourceDimensions,
613: final int targetDimensions) {
614: super (sourceDimensions, targetDimensions, PARAMETERS);
615: }
616:
617: /**
618: * Returns the operation type.
619: */
620: public Class getOperationType() {
621: return Conversion.class;
622: }
623:
624: /**
625: * Creates a projective transform from the specified group of parameter values.
626: *
627: * @param values The group of parameter values.
628: * @return The created math transform.
629: * @throws ParameterNotFoundException if a required parameter was not found.
630: */
631: protected MathTransform createMathTransform(
632: final ParameterValueGroup values)
633: throws ParameterNotFoundException {
634: final MathTransform transform;
635: transform = create(((MatrixParameterDescriptors) getParameters())
636: .getMatrix(values));
637: return new Delegate(transform, getProvider(transform
638: .getSourceDimensions(), transform
639: .getTargetDimensions()));
640: }
641:
642: /**
643: * Returns the operation method for the specified source and target dimensions.
644: * This method provides different methods for different matrix sizes.
645: */
646: public static ProviderAffine getProvider(
647: final int sourceDimensions, final int targetDimensions) {
648: if (sourceDimensions == targetDimensions) {
649: final int i = sourceDimensions - 1;
650: if (i >= 0 && i < methods.length) {
651: ProviderAffine method = methods[i];
652: if (method == null) {
653: methods[i] = method = new ProviderAffine(
654: sourceDimensions, targetDimensions);
655: }
656: return method;
657: }
658: }
659: return new ProviderAffine(sourceDimensions,
660: targetDimensions);
661: }
662: }
663:
664: /**
665: * The provider for the "<cite>Longitude rotation</cite>" (EPSG 9601).
666: *
667: * @version $Id: ProjectiveTransform.java 24925 2007-03-27 20:12:08Z jgarnett $
668: * @author Martin Desruisseaux
669: */
670: public static final class ProviderLongitudeRotation extends
671: MathTransformProvider {
672: /**
673: * Serial number for interoperability with different versions.
674: */
675: private static final long serialVersionUID = -2104496465933824935L;
676:
677: /**
678: * The longitude offset.
679: */
680: public static final ParameterDescriptor OFFSET = createDescriptor(
681: new NamedIdentifier[] { new NamedIdentifier(
682: Citations.EPSG, "Longitude offset") },
683: Double.NaN, -180, +180, NonSI.DEGREE_ANGLE);
684:
685: /**
686: * The parameters group.
687: */
688: static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(
689: new NamedIdentifier[] {
690: new NamedIdentifier(Citations.EPSG,
691: "Longitude rotation"),
692: new NamedIdentifier(Citations.EPSG, "9601") },
693: new ParameterDescriptor[] { OFFSET });
694:
695: /**
696: * Constructs a provider with default parameters.
697: */
698: public ProviderLongitudeRotation() {
699: super (2, 2, PARAMETERS);
700: }
701:
702: /**
703: * Returns the operation type.
704: */
705: public Class getOperationType() {
706: return Conversion.class;
707: }
708:
709: /**
710: * Creates a transform from the specified group of parameter values.
711: *
712: * @param values The group of parameter values.
713: * @return The created math transform.
714: * @throws ParameterNotFoundException if a required parameter was not found.
715: */
716: protected MathTransform createMathTransform(
717: final ParameterValueGroup values)
718: throws ParameterNotFoundException {
719: final double offset = doubleValue(OFFSET, values);
720: return create(AffineTransform.getTranslateInstance(offset,
721: 0));
722: }
723: }
724: }
|