Author: desruisseaux Date: Mon Jul 6 11:13:05 2015 New Revision: 1689367 URL: http://svn.apache.org/r1689367 Log: Merge CoordinateOperation parsing from JDK7 branch.
Added: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/OperationPathFinder.java - copied unchanged from r1689365, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/OperationPathFinder.java Modified: sis/branches/JDK6/ (props changed) sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ElementTest.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/AffineTest.java sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java Propchange: sis/branches/JDK6/ ------------------------------------------------------------------------------ --- svn:mergeinfo (original) +++ svn:mergeinfo Mon Jul 6 11:13:05 2015 @@ -1,4 +1,4 @@ /sis/branches/Android:1430670-1480699 -/sis/branches/JDK7:1394913-1689050 -/sis/branches/JDK8:1584960-1689049 +/sis/branches/JDK7:1394913-1689365 +/sis/branches/JDK8:1584960-1689363 /sis/trunk:1394364-1508466,1519089-1519674 Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/internal/metadata/ReferencingServices.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -24,6 +24,7 @@ import org.opengis.geometry.Envelope; import org.opengis.metadata.Identifier; import org.opengis.parameter.ParameterDescriptor; import org.opengis.referencing.IdentifiedObject; +import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.SingleCRS; import org.opengis.referencing.crs.DerivedCRS; import org.opengis.referencing.crs.VerticalCRS; @@ -36,7 +37,9 @@ import org.opengis.referencing.operation import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.CoordinateOperationFactory; import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.TransformException; +import org.opengis.util.FactoryException; import org.apache.sis.metadata.iso.extent.DefaultExtent; import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent; import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent; @@ -92,6 +95,15 @@ public class ReferencingServices extends public static final String PARAMETERS_KEY = "parameters"; /** + * The key for specifying the base type of the coordinate operation to create. This optional entry + * is used by {@code DefaultCoordinateOperationFactory.createSingleOperation(…)}. Apache SIS tries + * to infer this value automatically, but this entry may help SIS to perform a better choice in + * some cases. For example an "Affine" operation can be both a conversion or a transformation + * (the later is used in datum shift in geocentric coordinates). + */ + public static final String OPERATION_TYPE_KEY = "operationType"; + + /** * The key for specifying a {@linkplain org.opengis.referencing.operation.MathTransformFactory} * instance to use for the construction of a geodetic object. This is usually not needed for CRS * construction, except in the special case of a derived CRS created from a defining conversion. @@ -422,6 +434,32 @@ public class ReferencingServices extends } /** + * Creates a single operation from the given properties. + * This method is provided here because not yet available in GeoAPI interfaces. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param interpolationCRS The CRS of additional coordinates needed for the operation, or {@code null} if none. + * @param method The coordinate operation method (mandatory in all cases). + * @param factory The factory to use. + * @return The coordinate operation created from the given arguments. + * @throws FactoryException if the object creation failed. + * + * @since 0.6 + */ + public SingleOperation createSingleOperation( + final Map<String,?> properties, + final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS, + final CoordinateReferenceSystem interpolationCRS, + final OperationMethod method, + final CoordinateOperationFactory factory) throws FactoryException + { + throw moduleNotFound(); + } + + /** * Returns the coordinate operation factory to use for the given properties and math transform factory. * If the given properties are empty and the {@code mtFactory} is the system default, then this method * returns the system default {@code CoordinateOperationFactory} instead of creating a new one. Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -211,7 +211,12 @@ final class Element { } lower = upper + n; // After the closing quote. } while (lower < text.length() && text.codePointAt(lower) == closingQuote); - list.add(content.toString()); + /* + * Leading and trailing spaces should be ignored according ISO 19162 §B.4. + * Note that the specification suggests also to replace consecutive white + * spaces by a single space, but we don't do that yet. + */ + list.add(CharSequences.trimWhitespaces(content).toString()); } else if (!Character.isUnicodeIdentifierStart(firstChar)) { /* * Try to parse the next element as a date or a number. We will attempt such parsing Modified: sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/GeodeticObjectParser.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -669,7 +669,7 @@ final class GeodeticObjectParser extends final Element element = parent.pullElement(OPTIONAL, WKTKeywords.CS); if (element != null) { final String expected = type; - type = CharSequences.trimWhitespaces(element.pullVoidElement("type").keyword); + type = element.pullVoidElement("type").keyword; dimension = element.pullInteger("dimension"); csProperties = new HashMap<String,Object>(parseMetadataAndClose(element, "CS", null)); if (expected != null) { @@ -952,7 +952,7 @@ final class GeodeticObjectParser extends * expressed by a syntax like AXIS[“South along 90°W”, SOUTH, MERIDIAN[-90, UNIT["deg"]]]. Note that * the meridian is relative to the prime meridian of the enclosing geodetic CRS. */ - String name = CharSequences.trimWhitespaces(element.pullString("name")); + String name = element.pullString("name"); final Element orientation = element.pullVoidElement("orientation"); Unit<?> unit = parseUnit(element); if (unit == null) { @@ -1251,7 +1251,7 @@ final class GeodeticObjectParser extends } name = parent.pullString("name"); } - OperationMethod method = parseMethod(parent, WKTKeywords.Method, WKTKeywords.Projection); + final OperationMethod method = parseMethod(parent, WKTKeywords.Method, WKTKeywords.Projection); Map<String,?> properties = this.properties; // Same properties then OperationMethod, with ID removed. /* * Set the list of parameters. @@ -2078,15 +2078,20 @@ final class GeodeticObjectParser extends final CoordinateReferenceSystem sourceCRS = parseCoordinateReferenceSystem(element, MANDATORY, WKTKeywords.SourceCRS); final CoordinateReferenceSystem targetCRS = parseCoordinateReferenceSystem(element, MANDATORY, WKTKeywords.TargetCRS); final CoordinateReferenceSystem interpolationCRS = parseCoordinateReferenceSystem(element, OPTIONAL, WKTKeywords.InterpolationCRS); - final OperationMethod method = parseMethod(parent, WKTKeywords.Method); - final Element accuracy = parent.pullElement(OPTIONAL, WKTKeywords.OperationAccuracy); - final Map<String,Object> properties = parseMetadataAndClose(parent, name, method); + final OperationMethod method = parseMethod(element, WKTKeywords.Method); + final Element accuracy = element.pullElement(OPTIONAL, WKTKeywords.OperationAccuracy); + final Map<String,Object> properties = parseMetadataAndClose(element, name, method); final ParameterValueGroup parameters = method.getParameters().createValue(); - parseParameters(parent, parameters, null, null); + parseParameters(element, parameters, null, null); + properties.put(ReferencingServices.PARAMETERS_KEY, parameters); if (accuracy != null) { accuracy.pullDouble("accuracy"); // TODO: share the code from EPSG factory. accuracy.close(ignoredElements); } - return null; // Not yet implemented. + try { + return referencing.createSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, opFactory); + } catch (FactoryException e) { + throw element.parseFailed(e); + } } } Modified: sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ElementTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ElementTest.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ElementTest.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-metadata/src/test/java/org/apache/sis/io/wkt/ElementTest.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -83,10 +83,10 @@ public final strictfp class ElementTest assertEquals("value", "World Geodetic System 1984", element.pullString("value")); element.close(null); - // Spaces inside quotes should be preserved. + // Leading and trailing spaces inside quotes should be ignored (ISO 19162 §B.4). element = parse(" Datum [ \" World Geodetic System 1984 \" ] "); assertEquals("keyword", "Datum", element.keyword); - assertEquals("value", " World Geodetic System 1984 ", element.pullString("value")); + assertEquals("value", "World Geodetic System 1984", element.pullString("value")); element.close(null); // Consecutive values. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/ServicesForMetadata.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -41,6 +41,7 @@ import org.opengis.referencing.operation import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.CoordinateOperationFactory; import org.opengis.metadata.extent.GeographicBoundingBox; @@ -570,6 +571,39 @@ public final class ServicesForMetadata e } /** + * Creates a single operation from the given properties. + * This method is provided here because not yet available in GeoAPI interfaces. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param interpolationCRS The CRS of additional coordinates needed for the operation, or {@code null} if none. + * @param method The coordinate operation method (mandatory in all cases). + * @param factory The factory to use. + * @return The coordinate operation created from the given arguments. + * @throws FactoryException if the object creation failed. + * + * @since 0.6 + */ + @Override + public SingleOperation createSingleOperation( + final Map<String,?> properties, + final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS, + final CoordinateReferenceSystem interpolationCRS, + final OperationMethod method, + final CoordinateOperationFactory factory) throws FactoryException + { + final DefaultCoordinateOperationFactory df; + if (factory instanceof DefaultCoordinateOperationFactory) { + df = (DefaultCoordinateOperationFactory) factory; + } else { + df = DefaultFactories.forBuildin(CoordinateOperationFactory.class, DefaultCoordinateOperationFactory.class); + } + return df.createSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, null); + } + + /** * Returns the coordinate operation factory to use for the given properties and math transform factory. * If the given properties are empty and the {@code mtFactory} is the system default, then this method * returns the system default {@code CoordinateOperationFactory} instead of creating a new one. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Affine.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -23,7 +23,6 @@ import org.opengis.parameter.ParameterVa import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.referencing.operation.Matrix; -import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.operation.OperationMethod; @@ -168,15 +167,13 @@ public final class Affine extends Abstra } } - /** - * Returns the type of operations created by this provider. - * - * @return Always {@code Conversion.class} for this provider. + /* + * Do not override the 'getOperationType()' method. We want to inherit the super-type value, which is + * SingleOperation.class, because we do not know if this operation method will be used for a Conversion + * or a Transformation. When applied on geocentric coordinates, this method applies a transformation + * (indeeded, the EPSG method name is "Affine parametric transformation"). But this method can also + * be applied for unit conversions or axis swapping for examples, which are conversions. */ - @Override - public Class<Conversion> getOperationType() { - return Conversion.class; - } /** * Creates a projective transform from the specified group of parameter values. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConicProjection.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -16,12 +16,17 @@ */ package org.apache.sis.referencing.operation; +import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.opengis.util.FactoryException; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.ConicProjection; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; /** @@ -43,6 +48,24 @@ final class DefaultConicProjection exten private static final long serialVersionUID = -8717453834398763963L; /** + * Creates a projection from the given properties. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param method The coordinate operation method. + * @param transform Transform from positions in the source CRS to positions in the target CRS. + */ + public DefaultConicProjection(final Map<String,?> properties, + final GeographicCRS sourceCRS, + final ProjectedCRS targetCRS, + final OperationMethod method, + final MathTransform transform) + { + super(properties, sourceCRS, targetCRS, method, transform); + } + + /** * Creates a new projection with the same values than the specified one, together with the * specified source and target CRS. While the source conversion can be an arbitrary one, it * is typically a defining conversion. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -26,16 +26,20 @@ import org.opengis.parameter.ParameterDe import org.opengis.referencing.operation.*; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; import org.apache.sis.internal.referencing.MergedProperties; import org.apache.sis.internal.metadata.ReferencingServices; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; import org.apache.sis.util.collection.WeakHashSet; +import org.apache.sis.util.collection.Containers; import org.apache.sis.util.iso.AbstractFactory; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.CharSequences; +import org.apache.sis.util.NullArgumentException; /** @@ -203,7 +207,7 @@ public class DefaultCoordinateOperationF * </tr> * <tr> * <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td> - * <td>{@link Identifier} or {@link String}</td> + * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> * <td>{@link DefaultOperationMethod#getName()}</td> * </tr> * <tr> @@ -213,7 +217,7 @@ public class DefaultCoordinateOperationF * </tr> * <tr> * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> - * <td>{@link Identifier} (optionally as array)</td> + * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> * <td>{@link DefaultOperationMethod#getIdentifiers()}</td> * </tr> * <tr> @@ -247,7 +251,7 @@ public class DefaultCoordinateOperationF } /** - * Constructs a defining conversion from the given operation parameters. + * Creates a defining conversion from the given operation parameters. * This conversion has no source and target CRS since those elements are usually unknown at this stage. * The source and target CRS will become known later, at the * {@linkplain org.apache.sis.referencing.crs.DefaultDerivedCRS Derived CRS} or @@ -308,6 +312,171 @@ public class DefaultCoordinateOperationF } /** + * Creates a transformation or conversion from the given properties. + * This method infers by itself if the operation to create is a + * {@link Transformation}, a {@link Conversion} or a {@link Projection} sub-type + * ({@link CylindricalProjection}, {@link ConicProjection} or {@link PlanarProjection}) + * using the {@linkplain DefaultOperationMethod#getOperationType() information provided by the given method}. + * + * <p>The properties given in argument follow the same rules than for the + * {@linkplain AbstractCoordinateOperation#AbstractCoordinateOperation(Map, CoordinateReferenceSystem, + * CoordinateReferenceSystem, CoordinateReferenceSystem, MathTransform) coordinate operation} constructor. + * The following table is a reminder of main (not all) properties:</p> + * + * <table class="sis"> + * <caption>Recognized properties (non exhaustive list)</caption> + * <tr> + * <th>Property name</th> + * <th>Value type</th> + * <th>Returned by</th> + * </tr> + * <tr> + * <td>{@value org.opengis.referencing.IdentifiedObject#NAME_KEY}</td> + * <td>{@link org.opengis.metadata.Identifier} or {@link String}</td> + * <td>{@link DefaultConversion#getName()}</td> + * </tr> + * <tr> + * <td>{@value org.opengis.referencing.IdentifiedObject#IDENTIFIERS_KEY}</td> + * <td>{@link org.opengis.metadata.Identifier} (optionally as array)</td> + * <td>{@link DefaultConversion#getIdentifiers()}</td> + * </tr> + * <tr> + * <td>{@value org.opengis.referencing.operation.CoordinateOperation#DOMAIN_OF_VALIDITY_KEY}</td> + * <td>{@link org.opengis.metadata.extent.Extent}</td> + * <td>{@link DefaultConversion#getDomainOfValidity()}</td> + * </tr> + * </table> + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param interpolationCRS The CRS of additional coordinates needed for the operation, or {@code null} if none. + * @param method The coordinate operation method (mandatory in all cases). + * @param transform Transform from positions in the source CRS to positions in the target CRS. + * @return The coordinate operation created from the given arguments. + * @throws FactoryException if the object creation failed. + * + * @see DefaultOperationMethod#getOperationType() + * @see DefaultTransformation + * @see DefaultConversion + */ + public SingleOperation createSingleOperation( + final Map<String,?> properties, + final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS, + final CoordinateReferenceSystem interpolationCRS, + final OperationMethod method, + MathTransform transform) throws FactoryException + { + ArgumentChecks.ensureNonNull("sourceCRS", sourceCRS); + ArgumentChecks.ensureNonNull("targetCRS", targetCRS); + ArgumentChecks.ensureNonNull("method", method); + /* + * Undocumented (for now) feature: if the 'transform' argument is null but parameters are + * found in the given properties, create the MathTransform instance from those parameters. + * This is needed for WKT parsing of CoordinateOperation[…] among others. + */ + if (transform == null) { + final ParameterValueGroup parameters = Containers.property(properties, + ReferencingServices.PARAMETERS_KEY, ParameterValueGroup.class); + if (parameters == null) { + throw new NullArgumentException(Errors.format(Errors.Keys.NullArgument_1, "transform")); + } + transform = mtFactory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem()); + } + /* + * The "operationType" property is currently undocumented. The intend is to help this factory method in + * situations where the given operation method is not an Apache SIS implementation or does not override + * getOperationType(), or the method is ambiguous (e.g. "Affine" can be used for both a transformation + * or a conversion). + * + * If we have both a 'baseType' and a Method.getOperationType(), take the most specific type. + * An exception will be thrown if the two types are incompatible. + */ + Class<?> baseType = Containers.property(properties, ReferencingServices.OPERATION_TYPE_KEY, Class.class); + if (baseType == null) { + baseType = SingleOperation.class; + } + if (method instanceof DefaultOperationMethod) { + final Class<? extends SingleOperation> c = ((DefaultOperationMethod) method).getOperationType(); + if (c != null) { // Paranoiac check (above method should not return null). + if (baseType.isAssignableFrom(c)) { + baseType = c; + } else if (!c.isAssignableFrom(baseType)) { + throw new IllegalArgumentException(Errors.format(Errors.Keys.IncompatiblePropertyValue_1, + ReferencingServices.OPERATION_TYPE_KEY)); + } + } + } + /* + * If the base type is still abstract (probably because it was not specified neither in the given OperationMethod + * or in the properties), then try to find a concrete type using the following rules derived from the definitions + * given in ISO 19111: + * + * - If the two CRS uses the same datum (ignoring metadata), assume that we have a Conversion. + * - Otherwise we have a datum change, which implies that we have a Transformation. + * + * In the case of Conversion, we can specialize one step more if the conversion is going from a geographic CRS + * to a projected CRS. It may seems that we should check if ProjectedCRS.getBaseCRS() is equals (ignoring meta + * data) to source CRS. But we already checked the datum, which is the important part. The axis order and unit + * could be different, which we want to allow. + */ + if (baseType == SingleOperation.class) { + if (OperationPathFinder.isConversion(sourceCRS, targetCRS)) { + if (interpolationCRS == null && sourceCRS instanceof GeographicCRS + && targetCRS instanceof ProjectedCRS) + { + baseType = Projection.class; + } else { + baseType = Conversion.class; + } + } else { + baseType = Transformation.class; + } + } + /* + * Now create the coordinate operation of the requested type. If we can not find a concrete class for the + * requested type, we will instantiate an SingleOperation in last resort. The later action is a departure + * from ISO 19111 since 'SingleOperation' is conceptually abstract. But we do that as a way to said that + * we are missing this important piece of information but still go ahead. + * + * It is unconvenient to guarantee that the created operation is an instance of 'baseType' since the user + * could have specified an implementation class or a custom sub-interface. We will perform the type check + * only after object creation. + */ + final AbstractSingleOperation op; + if (Transformation.class.isAssignableFrom(baseType)) { + op = new DefaultTransformation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); + } else if (Projection.class.isAssignableFrom(baseType)) { + ArgumentChecks.ensureCanCast("sourceCRS", GeographicCRS.class, sourceCRS); + ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS .class, targetCRS); + if (interpolationCRS != null) { + throw new IllegalArgumentException(Errors.format( + Errors.Keys.ForbiddenAttribute_2, "interpolationCRS", baseType)); + } + final GeographicCRS baseCRS = (GeographicCRS) sourceCRS; + final ProjectedCRS crs = (ProjectedCRS) targetCRS; + if (CylindricalProjection.class.isAssignableFrom(baseType)) { + op = new DefaultCylindricalProjection(properties, baseCRS, crs, method, transform); + } else if (ConicProjection.class.isAssignableFrom(baseType)) { + op = new DefaultConicProjection(properties, baseCRS, crs, method, transform); + } else if (PlanarProjection.class.isAssignableFrom(baseType)) { + op = new DefaultPlanarProjection(properties, baseCRS, crs, method, transform); + } else { + op = new DefaultProjection(properties, baseCRS, crs, method, transform); + } + } else if (Conversion.class.isAssignableFrom(baseType)) { + op = new DefaultConversion(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); + } else { // See above comment about this last-resort fallback. + op = new AbstractSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); + } + if (!baseType.isInstance(op)) { + throw new FactoryException(Errors.format(Errors.Keys.CanNotInstantiate_1, baseType)); + } + return pool.unique(op); + } + + /** * Creates an ordered sequence of two or more single coordinate operations. * The sequence of operations is constrained by the requirement that the source coordinate reference system * of step (<var>n</var>+1) must be the same as the target coordinate reference system of step (<var>n</var>). Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCylindricalProjection.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -16,12 +16,17 @@ */ package org.apache.sis.referencing.operation; +import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.opengis.util.FactoryException; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.CylindricalProjection; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; /** @@ -43,6 +48,24 @@ final class DefaultCylindricalProjection private static final long serialVersionUID = -969486613826553580L; /** + * Creates a projection from the given properties. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param method The coordinate operation method. + * @param transform Transform from positions in the source CRS to positions in the target CRS. + */ + public DefaultCylindricalProjection(final Map<String,?> properties, + final GeographicCRS sourceCRS, + final ProjectedCRS targetCRS, + final OperationMethod method, + final MathTransform transform) + { + super(properties, sourceCRS, targetCRS, method, transform); + } + + /** * Creates a new projection with the same values than the specified one, together with the * specified source and target CRS. While the source conversion can be an arbitrary one, it * is typically a defining conversion. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPlanarProjection.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -16,12 +16,17 @@ */ package org.apache.sis.referencing.operation; +import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.opengis.util.FactoryException; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.PlanarProjection; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.crs.GeographicCRS; +import org.opengis.referencing.crs.ProjectedCRS; /** @@ -43,6 +48,24 @@ final class DefaultPlanarProjection exte private static final long serialVersionUID = 8171256287775067736L; /** + * Creates a projection from the given properties. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param method The coordinate operation method. + * @param transform Transform from positions in the source CRS to positions in the target CRS. + */ + public DefaultPlanarProjection(final Map<String,?> properties, + final GeographicCRS sourceCRS, + final ProjectedCRS targetCRS, + final OperationMethod method, + final MathTransform transform) + { + super(properties, sourceCRS, targetCRS, method, transform); + } + + /** * Creates a new projection with the same values than the specified one, together with the * specified source and target CRS. While the source conversion can be an arbitrary one, it * is typically a defining conversion. Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultProjection.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -16,10 +16,13 @@ */ package org.apache.sis.referencing.operation; +import java.util.Map; import javax.xml.bind.annotation.XmlTransient; import org.opengis.util.FactoryException; import org.opengis.referencing.operation.Conversion; import org.opengis.referencing.operation.Projection; +import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.crs.GeographicCRS; @@ -58,6 +61,24 @@ class DefaultProjection extends DefaultC private static final long serialVersionUID = -7176751851369816864L; /** + * Creates a projection from the given properties. + * + * @param properties The properties to be given to the identified object. + * @param sourceCRS The source CRS. + * @param targetCRS The target CRS. + * @param method The coordinate operation method. + * @param transform Transform from positions in the source CRS to positions in the target CRS. + */ + public DefaultProjection(final Map<String,?> properties, + final GeographicCRS sourceCRS, + final ProjectedCRS targetCRS, + final OperationMethod method, + final MathTransform transform) + { + super(properties, sourceCRS, targetCRS, null, method, transform); + } + + /** * Creates a new projection with the same values than the specified one, together with the * specified source and target CRS. While the source conversion can be an arbitrary one, it * is typically a defining conversion. Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/AffineTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/AffineTest.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/AffineTest.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/AffineTest.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -17,6 +17,7 @@ package org.apache.sis.internal.referencing.provider; import org.opengis.parameter.GeneralParameterDescriptor; +import org.opengis.referencing.operation.SingleOperation; import org.opengis.referencing.operation.Matrix; import org.apache.sis.referencing.operation.matrix.Matrices; import org.apache.sis.test.DependsOnMethod; @@ -38,6 +39,17 @@ import static org.apache.sis.test.Metada @DependsOn(org.apache.sis.parameter.TensorValuesTest.class) public final strictfp class AffineTest extends TestCase { /** + * Verifies that {@link Affine#getOperationType()} is {@link SingleOperation}. + * The {@code Affine} class can not return one of the above, because we do not + * know if this operation method will be used for a transformation ora conversion + * (it can be used for both). + */ + @Test + public void testOperationType() { + assertEquals(SingleOperation.class, new Affine().getOperationType()); + } + + /** * Tests {@link Affine#getParameters()} on a standard EPSG:9624 instance. */ @Test Modified: sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java?rev=1689367&r1=1689366&r2=1689367&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactoryTest.java [UTF-8] Mon Jul 6 11:13:05 2015 @@ -120,7 +120,7 @@ public final strictfp class DefaultMathT assertFalse(transforms .isEmpty()); assertFalse(conversions.isEmpty()); assertFalse(projections.isEmpty()); - assertTrue (conversions.contains(factory.getOperationMethod(Constants.AFFINE))); + assertTrue (transforms.contains(factory.getOperationMethod(Constants.AFFINE))); /* * Following tests will force instantiation of all remaining OperationMethod. */