001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2003-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: * This package contains documentation from OpenGIS specifications.
018: * OpenGIS consortium's work is fully acknowledged here.
019: */
020: package org.geotools.referencing.operation;
021:
022: // J2SE dependencies and extensions
023: import java.util.Collection;
024: import java.util.Collections;
025: import java.util.Iterator;
026: import java.util.HashMap;
027: import java.util.Map;
028: import javax.units.SI;
029: import javax.units.Unit;
030:
031: // OpenGIS dependencies
032: import org.opengis.metadata.extent.Extent;
033: import org.opengis.metadata.quality.Result;
034: import org.opengis.metadata.quality.QuantitativeResult;
035: import org.opengis.metadata.quality.PositionalAccuracy;
036: import org.opengis.referencing.crs.CoordinateReferenceSystem;
037: import org.opengis.referencing.operation.ConcatenatedOperation;
038: import org.opengis.referencing.operation.CoordinateOperation;
039: import org.opengis.referencing.operation.MathTransform;
040: import org.opengis.referencing.operation.Transformation;
041: import org.opengis.referencing.operation.Operation;
042: import org.opengis.referencing.operation.Conversion;
043: import org.opengis.referencing.operation.Projection;
044: import org.opengis.referencing.operation.PlanarProjection;
045: import org.opengis.referencing.operation.CylindricalProjection;
046: import org.opengis.referencing.operation.ConicProjection;
047: import org.opengis.referencing.IdentifiedObject;
048: import org.opengis.util.InternationalString;
049:
050: // Geotools dependencies
051: import org.geotools.metadata.iso.quality.PositionalAccuracyImpl;
052: import org.geotools.referencing.AbstractIdentifiedObject;
053: import org.geotools.referencing.crs.AbstractDerivedCRS;
054: import org.geotools.referencing.wkt.Formatter;
055: import org.geotools.resources.Utilities;
056: import org.geotools.resources.i18n.Errors;
057: import org.geotools.resources.i18n.ErrorKeys;
058:
059: /**
060: * Establishes an association between a source and a target coordinate reference system,
061: * and provides a {@linkplain MathTransform transform} for transforming coordinates in
062: * the source CRS to coordinates in the target CRS. Many but not all coordinate operations (from
063: * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>A</VAR> to
064: * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>B</VAR>)
065: * also uniquely define the inverse operation (from
066: * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>B</VAR> to
067: * {@linkplain CoordinateReferenceSystem coordinate reference system} <VAR>A</VAR>).
068: * In some cases, the operation method algorithm for the inverse operation is the same
069: * as for the forward algorithm, but the signs of some operation parameter values must
070: * be reversed. In other cases, different algorithms are required for the forward and
071: * inverse operations, but the same operation parameter values are used. If (some)
072: * entirely different parameter values are needed, a different coordinate operation
073: * shall be defined.
074: * <p>
075: * This class is conceptually <cite>abstract</cite>, even if it is technically possible to
076: * instantiate it. Typical applications should create instances of the most specific subclass with
077: * {@code Default} prefix instead. An exception to this rule may occurs when it is not possible to
078: * identify the exact type.
079: *
080: * @since 2.1
081: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/referencing/src/main/java/org/geotools/referencing/operation/AbstractCoordinateOperation.java $
082: * @version $Id: AbstractCoordinateOperation.java 28264 2007-12-05 21:53:08Z desruisseaux $
083: * @author Martin Desruisseaux
084: */
085: public class AbstractCoordinateOperation extends
086: AbstractIdentifiedObject implements CoordinateOperation {
087: /**
088: * Serial number for interoperability with different versions.
089: */
090: private static final long serialVersionUID = 1237358357729193885L;
091:
092: /**
093: * An empty array of positional accuracy. This is usefull for fetching accuracies as an array,
094: * using the following idiom:
095: * <blockquote><pre>
096: * {@linkplain #getPositionalAccuracy()}.toArray(EMPTY_ACCURACY_ARRAY);
097: * </pre></blockquote>
098: */
099: public static final PositionalAccuracy[] EMPTY_ACCURACY_ARRAY = new PositionalAccuracy[0];
100:
101: /**
102: * List of localizable properties. To be given to {@link AbstractIdentifiedObject} constructor.
103: */
104: private static final String[] LOCALIZABLES = { SCOPE_KEY };
105:
106: /**
107: * The source CRS, or {@code null} if not available.
108: */
109: protected final CoordinateReferenceSystem sourceCRS;
110:
111: /**
112: * The target CRS, or {@code null} if not available.
113: */
114: protected final CoordinateReferenceSystem targetCRS;
115:
116: /**
117: * Version of the coordinate transformation
118: * (i.e., instantiation due to the stochastic nature of the parameters).
119: */
120: final String operationVersion;
121:
122: /**
123: * Estimate(s) of the impact of this operation on point accuracy, or {@code null}
124: * if none.
125: */
126: private final Collection/*<PositionalAccuracy>*/coordinateOperationAccuracy;
127:
128: /**
129: * Area in which this operation is valid, or {@code null} if not available.
130: */
131: protected final Extent domainOfValidity;
132:
133: /**
134: * Description of domain of usage, or limitations of usage, for which this operation is valid.
135: */
136: private final InternationalString scope;
137:
138: /**
139: * Transform from positions in the {@linkplain #getSourceCRS source coordinate reference system}
140: * to positions in the {@linkplain #getTargetCRS target coordinate reference system}.
141: */
142: protected final MathTransform transform;
143:
144: /**
145: * Constructs a new coordinate operation with the same values than the specified
146: * defining conversion, together with the specified source and target CRS. This
147: * constructor is used by {@link DefaultConversion} only.
148: */
149: AbstractCoordinateOperation(final Conversion definition,
150: final CoordinateReferenceSystem sourceCRS,
151: final CoordinateReferenceSystem targetCRS,
152: final MathTransform transform) {
153: super (definition);
154: this .sourceCRS = sourceCRS;
155: this .targetCRS = targetCRS;
156: this .operationVersion = definition.getOperationVersion();
157: this .coordinateOperationAccuracy = definition
158: .getCoordinateOperationAccuracy();
159: this .domainOfValidity = definition.getDomainOfValidity();
160: this .scope = definition.getScope();
161: this .transform = transform;
162: }
163:
164: /**
165: * Constructs a coordinate operation from a set of properties.
166: * The properties given in argument follow the same rules than for the
167: * {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
168: * Additionally, the following properties are understood by this construtor:
169: * <br><br>
170: * <table border='1'>
171: * <tr bgcolor="#CCCCFF" class="TableHeadingColor">
172: * <th nowrap>Property name</th>
173: * <th nowrap>Value type</th>
174: * <th nowrap>Value given to</th>
175: * </tr>
176: * <tr>
177: * <td nowrap> {@link #OPERATION_VERSION_KEY "operationVersion"} </td>
178: * <td nowrap> {@link String} </td>
179: * <td nowrap> {@link #getOperationVersion}</td>
180: * </tr>
181: * <tr>
182: * <td nowrap> {@link #COORDINATE_OPERATION_ACCURACY_KEY "coordinateOperationAccuracy"} </td>
183: * <td nowrap> <code>{@linkplain PositionalAccuracy}[]</code> </td>
184: * <td nowrap> {@link #getCoordinateOperationAccuracy}</td>
185: * </tr>
186: * <tr>
187: * <td nowrap> {@link #DOMAIN_OF_VALIDITY_KEY "domainOfValidity"} </td>
188: * <td nowrap> {@link Extent} </td>
189: * <td nowrap> {@link #getDomainOfValidity}</td>
190: * </tr>
191: * <tr>
192: * <td nowrap> {@link #SCOPE_KEY "scope"} </td>
193: * <td nowrap> {@link String} or {@link InternationalString} </td>
194: * <td nowrap> {@link #getScope}</td>
195: * </tr>
196: * </table>
197: *
198: * @param properties Set of properties. Should contains at least <code>"name"</code>.
199: * @param sourceCRS The source CRS.
200: * @param targetCRS The target CRS.
201: * @param transform Transform from positions in the {@linkplain #getSourceCRS source CRS}
202: * to positions in the {@linkplain #getTargetCRS target CRS}.
203: */
204: public AbstractCoordinateOperation(final Map properties,
205: final CoordinateReferenceSystem sourceCRS,
206: final CoordinateReferenceSystem targetCRS,
207: final MathTransform transform) {
208: this (properties, new HashMap(), sourceCRS, targetCRS, transform);
209: }
210:
211: /**
212: * Work around for RFE #4093999 in Sun's bug database
213: * ("Relax constraint on placement of this()/super() call in constructors").
214: */
215: private AbstractCoordinateOperation(final Map properties,
216: final Map subProperties,
217: final CoordinateReferenceSystem sourceCRS,
218: final CoordinateReferenceSystem targetCRS,
219: final MathTransform transform) {
220: super (properties, subProperties, LOCALIZABLES);
221: PositionalAccuracy[] positionalAccuracy;
222: domainOfValidity = (Extent) subProperties
223: .get(DOMAIN_OF_VALIDITY_KEY);
224: scope = (InternationalString) subProperties.get(SCOPE_KEY);
225: operationVersion = (String) subProperties
226: .get(OPERATION_VERSION_KEY);
227: positionalAccuracy = (PositionalAccuracy[]) subProperties
228: .get(COORDINATE_OPERATION_ACCURACY_KEY);
229: if (positionalAccuracy == null
230: || positionalAccuracy.length == 0) {
231: positionalAccuracy = null;
232: } else {
233: positionalAccuracy = (PositionalAccuracy[]) positionalAccuracy
234: .clone();
235: for (int i = 0; i < positionalAccuracy.length; i++) {
236: ensureNonNull(COORDINATE_OPERATION_ACCURACY_KEY,
237: positionalAccuracy, i);
238: }
239: }
240: this .coordinateOperationAccuracy = asSet(positionalAccuracy);
241: this .sourceCRS = sourceCRS;
242: this .targetCRS = targetCRS;
243: this .transform = transform;
244: if (!(this instanceof Conversion && transform == null
245: && sourceCRS == null && targetCRS == null)) {
246: // Null values authorized only for conversions, and all of them must be null together.
247: ensureNonNull("sourceCRS", transform);
248: ensureNonNull("targetCRS", transform);
249: ensureNonNull("transform", transform);
250: checkDimension("sourceCRS", sourceCRS, transform
251: .getSourceDimensions());
252: checkDimension("targetCRS", targetCRS, transform
253: .getTargetDimensions());
254: }
255: }
256:
257: /**
258: * Check if a reference coordinate system has the expected number of dimensions.
259: *
260: * @param name The argument name.
261: * @param crs The coordinate reference system to check.
262: * @param expected The expected number of dimensions.
263: */
264: private static void checkDimension(final String name,
265: final CoordinateReferenceSystem crs, final int expected) {
266: final int actual = crs.getCoordinateSystem().getDimension();
267: if (actual != expected) {
268: throw new IllegalArgumentException(Errors.format(
269: ErrorKeys.MISMATCHED_DIMENSION_$3, name,
270: new Integer(actual), new Integer(expected)));
271: }
272: }
273:
274: /**
275: * Returns the source CRS.
276: */
277: public CoordinateReferenceSystem getSourceCRS() {
278: return sourceCRS;
279: }
280:
281: /**
282: * Returns the target CRS.
283: */
284: public CoordinateReferenceSystem getTargetCRS() {
285: return targetCRS;
286: }
287:
288: /**
289: * Version of the coordinate transformation (i.e., instantiation due to the stochastic
290: * nature of the parameters). Mandatory when describing a transformation, and should not
291: * be supplied for a conversion.
292: *
293: * @return The coordinate operation version, or {@code null} in none.
294: */
295: public String getOperationVersion() {
296: return operationVersion;
297: }
298:
299: /**
300: * Estimate(s) of the impact of this operation on point accuracy. Gives
301: * position error estimates for target coordinates of this coordinate
302: * operation, assuming no errors in source coordinates.
303: *
304: * @return The position error estimates, or an empty collection if not available.
305: *
306: * @see #getAccuracy()
307: *
308: * @since 2.4
309: */
310: public Collection/*<PositionalAccuracy>*/getCoordinateOperationAccuracy() {
311: return (coordinateOperationAccuracy != null) ? coordinateOperationAccuracy
312: : Collections.EMPTY_SET;
313: }
314:
315: /**
316: * Estimate(s) of the impact of this operation on point accuracy. Gives
317: * position error estimates for target coordinates of this coordinate
318: * operation, assuming no errors in source coordinates.
319: *
320: * @return The position error estimates, or an empty collection if not available.
321: *
322: * @see #getAccuracy()
323: *
324: * @since 2.4
325: * @deprecated Renamed as {@link #getCoordinateOperationAccuracy}.
326: */
327: public Collection/*<PositionalAccuracy>*/getPositionalAccuracy() {
328: return (coordinateOperationAccuracy != null) ? coordinateOperationAccuracy
329: : Collections.EMPTY_SET;
330: }
331:
332: /**
333: * Convenience method returning the accuracy in meters. The default implementation delegates
334: * to <code>{@linkplain #getAccuracy(CoordinateOperation) getAccuracy}(this)</code>. Subclasses
335: * should override this method if they can provide a more accurate algorithm.
336: *
337: * @since 2.2
338: */
339: public double getAccuracy() {
340: return getAccuracy0(this );
341: }
342:
343: /**
344: * Convenience method returning the accuracy in meters for the specified operation. This method
345: * try each of the following procedures and returns the first successful one:
346: *
347: * <ul>
348: * <li>If a {@linkplain QuantitativeResult quantitative} positional accuracy is found with a
349: * linear unit, then this accuracy estimate is converted to {@linkplain SI#METER meters}
350: * and returned.</li>
351: *
352: * <li>Otherwise, if the operation is a {@linkplain Conversion conversion}, then returns
353: * 0 since a conversion is by definition accurates up to rounding errors.</li>
354: *
355: * <li>Otherwise, if the operation is a {@linkplain Transformation transformation}, then
356: * checks if the datum shift were applied with the help of Bursa-Wolf parameters.
357: * This procedure looks for Geotools-specific
358: * {@link PositionalAccuracyImpl#DATUM_SHIFT_APPLIED DATUM_SHIFT_APPLIED} and
359: * {@link PositionalAccuracyImpl#DATUM_SHIFT_OMITTED DATUM_SHIFT_OMITTED} metadata.
360: * If a datum shift has been applied, returns 25 meters. If a datum shift should have
361: * been applied but has been omitted, returns 1000 meters. The 1000 meters value is
362: * higher than the highest value (999 meters) found in the EPSG database version 6.7.
363: * The 25 meters value is the next highest value found in the EPSG database for a
364: * significant number of transformations.
365: *
366: * <li>Otherwise, if the operation is a {@linkplain ConcatenatedOperation concatenated one},
367: * returns the sum of the accuracy of all components.</li>
368: * </ul>
369: *
370: * @param operation The operation to inspect for accuracy.
371: * @return The accuracy estimate (always in meters), or NaN if unknow.
372: *
373: * @since 2.2
374: */
375: public static double getAccuracy(final CoordinateOperation operation) {
376: if (operation instanceof AbstractCoordinateOperation) {
377: // Maybe the user overridden this method...
378: return ((AbstractCoordinateOperation) operation)
379: .getAccuracy();
380: }
381: return getAccuracy0(operation);
382: }
383:
384: /**
385: * Implementation of {@code getAccuracy} methods, both the ordinary and the
386: * static member variants. The {@link #getAccuracy()} method can't invoke
387: * {@link #getAccuracy(CoordinateOperation)} directly since it would cause
388: * never-ending recursive calls.
389: */
390: private static double getAccuracy0(
391: final CoordinateOperation operation) {
392: final Collection accuracies = operation.getPositionalAccuracy();
393: for (final Iterator it = accuracies.iterator(); it.hasNext();) {
394: final Collection results = ((PositionalAccuracy) it.next())
395: .getResults();
396: for (final Iterator it2 = results.iterator(); it2.hasNext();) {
397: final Result accuracy = (Result) it2.next();
398: if (accuracy instanceof QuantitativeResult) {
399: final QuantitativeResult quantity = (QuantitativeResult) accuracy;
400: final Collection r = quantity.getValues();
401: if (r != null) {
402: final Unit unit = quantity.getValueUnit();
403: if (unit != null && SI.METER.isCompatible(unit)) {
404: for (final Iterator i = r.iterator(); i
405: .hasNext();) {
406: final Number d = (Number) i.next();
407: if (d != null) {
408: double value = d.doubleValue();
409: value = unit.getConverterTo(
410: SI.METER).convert(value);
411: return value;
412: }
413: }
414: }
415: }
416: }
417: }
418: }
419: /*
420: * No quantitative, linear accuracy were found. If the coordinate operation is actually
421: * a conversion, the accuracy is up to rounding error (i.e. conceptually 0) by definition.
422: */
423: if (operation instanceof Conversion) {
424: return 0;
425: }
426: /*
427: * If the coordinate operation is actually a transformation, checks if Bursa-Wolf
428: * parameters were available for the datum shift. This is Geotools-specific.
429: * See javadoc for a rational about the return values choosen.
430: */
431: if (operation instanceof Transformation) {
432: if (!accuracies
433: .contains(PositionalAccuracyImpl.DATUM_SHIFT_OMITTED)) {
434: if (accuracies
435: .contains(PositionalAccuracyImpl.DATUM_SHIFT_APPLIED)) {
436: return 25;
437: }
438: }
439: return 1000;
440: }
441: /*
442: * If the coordinate operation is a compound of other coordinate operations, returns
443: * the sum of their accuracy, skipping unknow ones.
444: */
445: double accuracy = Double.NaN;
446: if (operation instanceof ConcatenatedOperation) {
447: final Collection components = ((ConcatenatedOperation) operation)
448: .getOperations();
449: for (final Iterator it = components.iterator(); it
450: .hasNext();) {
451: final double candidate = Math
452: .abs(getAccuracy((CoordinateOperation) it
453: .next()));
454: if (!Double.isNaN(candidate)) {
455: if (Double.isNaN(accuracy)) {
456: accuracy = candidate;
457: } else {
458: accuracy += candidate;
459: }
460: }
461: }
462: }
463: return accuracy;
464: }
465:
466: /**
467: * Area or region or timeframe in which this coordinate operation is valid.
468: * Returns {@code null} if not available.
469: *
470: * @since 2.4
471: */
472: public Extent getDomainOfValidity() {
473: return domainOfValidity;
474: }
475:
476: /**
477: * Area in which this operation is valid.
478: *
479: * @return Coordinate operation valid area, or {@code null} if not available.
480: *
481: * @deprecated Renamed {@link #getDomainOfValidity}.
482: */
483: public Extent getValidArea() {
484: return domainOfValidity;
485: }
486:
487: /**
488: * Description of domain of usage, or limitations of usage, for which this operation is valid.
489: */
490: public InternationalString getScope() {
491: return scope;
492: }
493:
494: /**
495: * Gets the math transform. The math transform will transform positions in the
496: * {@linkplain #getSourceCRS source coordinate reference system} into positions
497: * in the {@linkplain #getTargetCRS target coordinate reference system}.
498: */
499: public MathTransform getMathTransform() {
500: return transform;
501: }
502:
503: /**
504: * Returns the most specific GeoAPI interface implemented by the specified operation.
505: *
506: * @param object A coordinate operation.
507: * @return The most specific GeoAPI interface
508: * (e.g. <code>{@linkplain Transformation}.class</code>).
509: */
510: public static Class getType(final CoordinateOperation object) {
511: if (object instanceof Transformation)
512: return Transformation.class;
513: if (object instanceof ConicProjection)
514: return ConicProjection.class;
515: if (object instanceof CylindricalProjection)
516: return CylindricalProjection.class;
517: if (object instanceof PlanarProjection)
518: return PlanarProjection.class;
519: if (object instanceof Projection)
520: return Projection.class;
521: if (object instanceof Conversion)
522: return Conversion.class;
523: if (object instanceof Operation)
524: return Operation.class;
525: return CoordinateOperation.class;
526: }
527:
528: /**
529: * Compares this coordinate operation with the specified object for equality.
530: * If {@code compareMetadata} is {@code true}, then all available properties are
531: * compared including {@linkplain #getValidArea valid area} and {@linkplain #getScope scope}.
532: *
533: * @param object The object to compare to {@code this}.
534: * @param compareMetadata {@code true} for performing a strict comparaison, or
535: * {@code false} for comparing only properties relevant to transformations.
536: * @return {@code true} if both objects are equal.
537: */
538: public boolean equals(final AbstractIdentifiedObject object,
539: final boolean compareMetadata) {
540: if (object == this ) {
541: return true; // Slight optimization.
542: }
543: if (super .equals(object, compareMetadata)) {
544: final AbstractCoordinateOperation that = (AbstractCoordinateOperation) object;
545: if (equals(this .sourceCRS, that.sourceCRS, compareMetadata)
546: && Utilities.equals(this .transform, that.transform))
547: // See comment in DefaultOperation.equals(...) about why we compare MathTransform.
548: {
549: if (compareMetadata) {
550: if (!Utilities.equals(this .domainOfValidity,
551: that.domainOfValidity)
552: || !Utilities
553: .equals(this .scope, that.scope)
554: || !Utilities.equals(
555: this .coordinateOperationAccuracy,
556: that.coordinateOperationAccuracy)) {
557: return false;
558: }
559: }
560: /*
561: * Avoid never-ending recursivity: AbstractDerivedCRS has a 'conversionFromBase'
562: * field that is set to this AbstractCoordinateOperation.
563: */
564: final Boolean comparing = (Boolean) AbstractDerivedCRS._COMPARING
565: .get();
566: if (comparing != null && comparing.booleanValue()) {
567: return true;
568: }
569: try {
570: AbstractDerivedCRS._COMPARING.set(Boolean.TRUE);
571: return equals(this .targetCRS, that.targetCRS,
572: compareMetadata);
573: } finally {
574: AbstractDerivedCRS._COMPARING.set(Boolean.FALSE);
575: // TODO: use _COMPARING.remove() when we will be allowed to compile for J2SE 1.5.
576: }
577: }
578: }
579: return false;
580: }
581:
582: /**
583: * Returns a hash code value for this coordinate operation.
584: */
585: public int hashCode() {
586: int code = (int) serialVersionUID;
587: if (sourceCRS != null)
588: code ^= sourceCRS.hashCode();
589: if (targetCRS != null)
590: code ^= targetCRS.hashCode();
591: if (transform != null)
592: code ^= transform.hashCode();
593: return code;
594: }
595:
596: /**
597: * Format this operation as a pseudo-WKT format. No WKT format were defined for coordinate
598: * operation at the time this method was written. This method may change in any future version
599: * until a standard format is found.
600: *
601: * @param formatter The formatter to use.
602: * @return The WKT element name.
603: */
604: protected String formatWKT(final Formatter formatter) {
605: append(formatter, sourceCRS, "SOURCE");
606: append(formatter, targetCRS, "TARGET");
607: return super .formatWKT(formatter);
608: }
609:
610: /**
611: * Append the identifier for the specified object name (possibly {@code null}) to the specified
612: * formatter.
613: *
614: * @param formatter The formatter where to append the object name.
615: * @param object The object to append, or {@code null} if none.
616: * @param type The label to put in front of the object name.
617: */
618: static void append(final Formatter formatter,
619: final IdentifiedObject object, final String type) {
620: if (object != null) {
621: final Map properties = new HashMap(4);
622: properties.put(IdentifiedObject.NAME_KEY, formatter
623: .getName(object));
624: properties.put(IdentifiedObject.IDENTIFIERS_KEY, formatter
625: .getIdentifier(object));
626: formatter
627: .append((IdentifiedObject) new AbstractIdentifiedObject(
628: properties) {
629: protected String formatWKT(
630: final Formatter formatter) {
631: /*
632: * Do not invoke super.formatWKT(formatter), since it doesn't do anything
633: * more than invoking 'formatter.setInvalidWKT(...)' (we ignore the value
634: * returned). This method will rather be invoked by the enclosing class.
635: */
636: return type;
637: }
638: });
639: }
640: }
641: }
|