Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -374,28 +374,30 @@ public class EPSGFactory extends Concurr if (ac) { connection.setAutoCommit(false); } - boolean success = false; try { - if (!"".equals(schema)) { // Schema may be null. - installer.setSchema(schema != null ? schema : Constants.EPSG); - if (catalog != null && !catalog.isEmpty()) { - installer.prependNamespace(catalog); + boolean success = false; + try { + if (!"".equals(schema)) { // Schema may be null. + installer.setSchema(schema != null ? schema : Constants.EPSG); + if (catalog != null && !catalog.isEmpty()) { + installer.prependNamespace(catalog); + } } - } - installer.run(scriptProvider, locale); - success = true; - } finally { - if (ac) { - if (success) { - connection.commit(); - } else { - connection.rollback(); + installer.run(scriptProvider, locale); + success = true; + } finally { + if (ac) { + if (success) { + connection.commit(); + } else { + connection.rollback(); + } + connection.setAutoCommit(true); } - connection.setAutoCommit(true); - } - if (!success) { - installer.logFailure(locale); } + } catch (IOException | SQLException e) { + installer.logFailure(locale, e); + throw e; } } }
Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGInstaller.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -34,6 +34,7 @@ import org.apache.sis.internal.metadata. import org.apache.sis.internal.metadata.sql.SQLUtilities; import org.apache.sis.internal.util.Fallback; import org.apache.sis.util.CharSequences; +import org.apache.sis.util.Exceptions; import org.apache.sis.util.resources.Messages; import org.apache.sis.util.logging.PerformanceLevel; import org.apache.sis.setup.InstallationResources; @@ -291,12 +292,13 @@ final class EPSGInstaller extends Script * lets the exception propagate. Another code (for example {@link org.apache.sis.referencing.CRS#forCode(String)}) * may catch that exception and log another record with the exception message. */ - final void logFailure(final Locale locale) { + final void logFailure(final Locale locale, final Exception cause) { String message = Messages.getResources(locale).getString(Messages.Keys.CanNotCreateSchema_1, EPSG); String status = status(locale); if (status != null) { message = message + ' ' + status; } + message = Exceptions.formatChainedMessages(locale, message, cause); InstallationScriptProvider.log(new LogRecord(Level.WARNING, message)); } } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/InstallationScriptProvider.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -383,7 +383,7 @@ public abstract class InstallationScript */ @Override protected InputStream openStream(final String name) throws IOException { - return Files.newInputStream(directory.resolve(name)); + return (directory != null) ? Files.newInputStream(directory.resolve(name)) : null; } } } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -32,6 +32,7 @@ import org.opengis.metadata.quality.Posi import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.crs.GeneralDerivedCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.operation.ConcatenatedOperation; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.MathTransform; @@ -651,16 +652,29 @@ check: for (int isTarget=0; ; isTar * {@linkplain #transform}, if possible. If no descriptor can be inferred from the math transform, * then this method fallback on the {@link OperationMethod} parameters. */ - ParameterDescriptorGroup getParameterDescriptors() throws UnsupportedOperationException { - MathTransform mt = transform; - while (mt != null) { - if (mt instanceof Parameterized) { + ParameterDescriptorGroup getParameterDescriptors() { + ParameterDescriptorGroup descriptor = getParameterDescriptors(transform); + if (descriptor == null) { + final OperationMethod method = getMethod(); + if (method != null) { + descriptor = method.getParameters(); + } + } + return descriptor; + } + + /** + * Returns the parameter descriptors for the given transform, or {@code null} if unknown. + */ + static ParameterDescriptorGroup getParameterDescriptors(MathTransform transform) { + while (transform != null) { + if (transform instanceof Parameterized) { final ParameterDescriptorGroup param; if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) { - throw new AssertionError(); // Should never happen. + throw new AssertionError(); // Should never happen. } try { - param = ((Parameterized) mt).getParameterDescriptors(); + param = ((Parameterized) transform).getParameterDescriptors(); } finally { Semaphores.clear(Semaphores.ENCLOSED_IN_OPERATION); } @@ -668,14 +682,13 @@ check: for (int isTarget=0; ; isTar return param; } } - if (mt instanceof PassThroughTransform) { - mt = ((PassThroughTransform) mt).getSubTransform(); + if (transform instanceof PassThroughTransform) { + transform = ((PassThroughTransform) transform).getSubTransform(); } else { break; } } - final OperationMethod method = getMethod(); - return (method != null) ? method.getParameters() : null; + return null; } /** @@ -686,13 +699,13 @@ check: for (int isTarget=0; ; isTar * @throws UnsupportedOperationException if the parameter values can not * be determined for the current math transform implementation. */ - ParameterValueGroup getParameterValues() { + ParameterValueGroup getParameterValues() throws UnsupportedOperationException { MathTransform mt = transform; while (mt != null) { if (mt instanceof Parameterized) { final ParameterValueGroup param; if (Semaphores.queryAndSet(Semaphores.ENCLOSED_IN_OPERATION)) { - throw new AssertionError(); // Should never happen. + throw new AssertionError(); // Should never happen. } try { param = ((Parameterized) mt).getParameterValues(); @@ -845,32 +858,45 @@ check: for (int isTarget=0; ; isTar protected String formatTo(final Formatter formatter) { super.formatTo(formatter); formatter.newLine(); - append(formatter, getSourceCRS(), WKTKeywords.SourceCRS); - append(formatter, getTargetCRS(), WKTKeywords.TargetCRS); - formatter.append(DefaultOperationMethod.castOrCopy(getMethod())); - ParameterValueGroup parameters; - try { - parameters = getParameterValues(); - } catch (UnsupportedOperationException e) { - final IdentifiedObject c = getParameterDescriptors(); - formatter.setInvalidWKT(c != null ? c : this, e); - parameters = null; + /* + * If the WKT is a component of a ConcatenatedOperation, do not format the source and target CRS. + * This decision SIS-specific since the WKT 2 specification does not define concatenated operations. + * The choice of content to omit may change in any future version. + */ + final boolean isComponent = (formatter.getEnclosingElement(1) instanceof ConcatenatedOperation); + if (!isComponent) { + append(formatter, getSourceCRS(), WKTKeywords.SourceCRS); + append(formatter, getTargetCRS(), WKTKeywords.TargetCRS); } - if (parameters != null) { - formatter.newLine(); - for (final GeneralParameterValue param : parameters.values()) { - WKTUtilities.append(param, formatter); + final OperationMethod method = getMethod(); + if (method != null) { + formatter.append(DefaultOperationMethod.castOrCopy(method)); + ParameterValueGroup parameters; + try { + parameters = getParameterValues(); + } catch (UnsupportedOperationException e) { + final IdentifiedObject c = getParameterDescriptors(); + formatter.setInvalidWKT(c != null ? c : this, e); + parameters = null; } - } - append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS); - final double accuracy = getLinearAccuracy(); - if (accuracy > 0) { - formatter.append(new FormattableObject() { - @Override protected String formatTo(final Formatter formatter) { - formatter.append(accuracy); - return WKTKeywords.OperationAccuracy; + if (parameters != null) { + formatter.newLine(); + for (final GeneralParameterValue param : parameters.values()) { + WKTUtilities.append(param, formatter); } - }); + } + } + if (!isComponent) { + append(formatter, getInterpolationCRS(), WKTKeywords.InterpolationCRS); + final double accuracy = getLinearAccuracy(); + if (accuracy > 0) { + formatter.append(new FormattableObject() { + @Override protected String formatTo(final Formatter formatter) { + formatter.append(accuracy); + return WKTKeywords.OperationAccuracy; + } + }); + } } if (formatter.getConvention().majorVersion() == 1) { formatter.setInvalidWKT(this, null); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/AbstractSingleOperation.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -225,14 +225,14 @@ class AbstractSingleOperation extends Ab * ignoring null java.lang.Integer instances. We do not specify whether the method * dimensions should include the interpolation dimensions or not, so we accept both. */ - int isTarget = 0; // 0 == false: the wrong dimension is the source one. + int isTarget = 0; // 0 == false: the wrong dimension is the source one. if (expected == null || (actual == expected) || (actual == expected + interpDim)) { actual = transform.getTargetDimensions(); expected = method.getTargetDimensions(); if (expected == null || (actual == expected) || (actual == expected + interpDim)) { return; } - isTarget = 1; // 1 == true: the wrong dimension is the target one. + isTarget = 1; // 1 == true: the wrong dimension is the target one. } /* * At least one dimension does not match. In principle this is an error, but we make an exception for the Copied: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java (from r1737071, sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java) URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java?p2=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java&p1=sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java&r1=1737071&r2=1737072&rev=1737072&view=diff ============================================================================== --- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -65,6 +65,7 @@ import static org.apache.sis.util.Utilit // Branch-dependent imports import java.util.Objects; +import org.apache.sis.internal.jdk8.JDK8; /** @@ -728,7 +729,7 @@ public class CoordinateOperationInferenc } properties.put(ReferencingServices.OPERATION_TYPE_KEY, type); if (Conversion.class.isAssignableFrom(type)) { - properties.replace(IdentifiedObject.NAME_KEY, AXIS_CHANGES, IDENTITY); + JDK8.replace(properties, IdentifiedObject.NAME_KEY, AXIS_CHANGES, IDENTITY); } return factorySIS.createSingleOperation(properties, sourceCRS, targetCRS, null, method, transform); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -46,6 +46,7 @@ import static org.apache.sis.util.Utilit // Branch-dependent imports import java.util.Objects; +import org.apache.sis.io.wkt.Formatter; /** @@ -325,6 +326,23 @@ final class DefaultConcatenatedOperation return super.computeHashCode() + 37 * Objects.hashCode(operations); } + /** + * Formats this coordinate operation in pseudo-WKT. This is specific to Apache SIS since + * there is no concatenated operation in the Well Known Text (WKT) version 2 format. + * + * @param formatter The formatter to use. + * @return {@code "ConcatenatedOperation"}. + */ + @Override + protected String formatTo(final Formatter formatter) { + super.formatTo(formatter); + for (final CoordinateOperation component : operations) { + formatter.append(castOrCopy(component)); + } + formatter.setInvalidWKT(this, null); + return "ConcatenatedOperation"; + } + Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConversion.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -254,12 +254,12 @@ public class DefaultConversion extends A * CoordinateReferenceSystem because the targetCRS is typically under construction when this * method in invoked, and attempts to use it can cause NullPointerException. */ - final DefaultMathTransformFactory.Context context = new DefaultMathTransformFactory.Context(); - context.setSource(source); + final DefaultMathTransformFactory.Context context; if (target instanceof GeneralDerivedCRS) { + context = ReferencingUtilities.createTransformContext(source, null, null); context.setTarget(target.getCoordinateSystem()); // Using 'target' would be unsafe here. } else { - context.setTarget(target); + context = ReferencingUtilities.createTransformContext(source, target, null); } transform = ((DefaultMathTransformFactory) factory).createParameterizedTransform(parameters, context); parameters = Parameters.unmodifiable(context.getCompletedParameters()); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -19,6 +19,7 @@ package org.apache.sis.referencing.opera import java.util.Map; import java.util.HashMap; import java.util.Collections; +import java.util.List; import org.opengis.util.FactoryException; import org.opengis.util.NoSuchIdentifierException; import org.opengis.parameter.ParameterValueGroup; @@ -28,11 +29,13 @@ import org.opengis.referencing.Identifie import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ProjectedCRS; +import org.opengis.referencing.crs.SingleCRS; +import org.opengis.referencing.datum.Datum; 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.internal.util.Utilities; +import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.factory.InvalidGeodeticParameterException; import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; import org.apache.sis.util.collection.WeakHashSet; @@ -42,6 +45,7 @@ import org.apache.sis.util.resources.Err import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.CharSequences; import org.apache.sis.util.NullArgumentException; +import org.apache.sis.util.Utilities; /** @@ -68,7 +72,7 @@ import org.apache.sis.util.NullArgumentE * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ public class DefaultCoordinateOperationFactory extends AbstractFactory implements CoordinateOperationFactory { @@ -149,7 +153,7 @@ public class DefaultCoordinateOperationF * * @return The underlying math transform factory. */ - private MathTransformFactory getMathTransformFactory() { + final MathTransformFactory getMathTransformFactory() { MathTransformFactory factory = mtFactory; if (factory == null) { mtFactory = factory = DefaultFactories.forBuildin(MathTransformFactory.class); @@ -158,6 +162,18 @@ public class DefaultCoordinateOperationF } /** + * Returns the Apache SIS implementation of math transform factory. + * This method is used only when we need SIS-specific methods. + */ + final DefaultMathTransformFactory getDefaultMathTransformFactory() { + MathTransformFactory factory = getMathTransformFactory(); + if (factory instanceof DefaultMathTransformFactory) { + return (DefaultMathTransformFactory) factory; + } + return DefaultFactories.forBuildin(MathTransformFactory.class, DefaultMathTransformFactory.class); + } + + /** * Returns the operation method of the given name. The given argument shall be either a method * {@linkplain DefaultOperationMethod#getName() name} (e.g. <cite>"Transverse Mercator"</cite>) * or one of its {@linkplain DefaultOperationMethod#getIdentifiers() identifiers} (e.g. {@code "EPSG:9807"}). @@ -314,6 +330,39 @@ public class DefaultCoordinateOperationF } /** + * Returns {@code true} if the given CRS are using equivalent (ignoring metadata) datum. + * If the CRS are {@link CompoundCRS}, then this method verifies that all datum in the + * target CRS exists in the source CRS, but not necessarily in the same order. + * The target CRS may have less datum than the source CRS. + * + * @param sourceCRS The target CRS. + * @param targetCRS The source CRS. + * @return {@code true} if all datum in the {@code targetCRS} exists in the {@code sourceCRS}. + */ + private static boolean isConversion(final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS) + { + List<SingleCRS> components = CRS.getSingleComponents(sourceCRS); + int n = components.size(); // Number of remaining datum from sourceCRS to verify. + final Datum[] datum = new Datum[n]; + for (int i=0; i<n; i++) { + datum[i] = components.get(i).getDatum(); + } + components = CRS.getSingleComponents(targetCRS); +next: for (int i=components.size(); --i >= 0;) { + final Datum d = components.get(i).getDatum(); + for (int j=n; --j >= 0;) { + if (Utilities.equalsIgnoreMetadata(d, datum[j])) { + System.arraycopy(datum, j+1, datum, j, --n - j); // Remove the datum from the list. + continue next; + } + } + return false; // Datum from 'targetCRS' not found in 'sourceCRS'. + } + return true; + } + + /** * 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 @@ -401,7 +450,7 @@ public class DefaultCoordinateOperationF } if (method instanceof DefaultOperationMethod) { final Class<? extends SingleOperation> c = ((DefaultOperationMethod) method).getOperationType(); - if (c != null) { // Paranoiac check (above method should not return null). + if (c != null) { // Paranoiac check (above method should not return null). if (baseType.isAssignableFrom(c)) { baseType = c; } else if (!c.isAssignableFrom(baseType)) { @@ -424,7 +473,7 @@ public class DefaultCoordinateOperationF * could be different, which we want to allow. */ if (baseType == SingleOperation.class) { - if (OperationPathFinder.isConversion(sourceCRS, targetCRS)) { + if (isConversion(sourceCRS, targetCRS)) { if (interpolationCRS == null && sourceCRS instanceof GeographicCRS && targetCRS instanceof ProjectedCRS) { @@ -473,7 +522,7 @@ public class DefaultCoordinateOperationF op = new AbstractSingleOperation(properties, sourceCRS, targetCRS, interpolationCRS, method, transform); } if (!baseType.isInstance(op)) { - throw new FactoryException(Errors.format(Errors.Keys.CanNotInstantiate_1, baseType)); + throw new FactoryException(Errors.format(Errors.Keys.CanNotCreateObjectAsInstanceOf_2, baseType, op.getName())); } return pool.unique(op); } @@ -528,57 +577,87 @@ public class DefaultCoordinateOperationF } /** - * Not yet implemented. - * - * @param sourceCRS Input coordinate reference system. - * @param targetCRS Output coordinate reference system. - * @param method the algorithmic method for conversion or transformation. - * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}. - * @throws FactoryException if the operation creation failed. + * Finds or creates an operation for conversion or transformation between two coordinate reference systems. + * If an operation exists, it is returned. If more than one operation exists, the operation having the widest + * domain of validity is returned. If no operation exists, then an exception is thrown. + * + * <p>The default implementation delegates to <code>{@linkplain #createOperation(CoordinateReferenceSystem, + * CoordinateReferenceSystem, CoordinateOperationContext) createOperation}(sourceCRS, targetCRS, null)}</code>.</p> + * + * @param sourceCRS input coordinate reference system. + * @param targetCRS output coordinate reference system. + * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}. + * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}. + * @throws FactoryException if the operation creation failed for some other reason. */ @Override public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS, - final CoordinateReferenceSystem targetCRS, - final OperationMethod method) - throws FactoryException + final CoordinateReferenceSystem targetCRS) + throws OperationNotFoundException, FactoryException { - return delegate().createOperation(sourceCRS, targetCRS, method); + return createOperation(sourceCRS, targetCRS, (CoordinateOperationContext) null); } /** - * Not yet implemented. + * Finds or creates an operation for conversion or transformation between two coordinate reference systems. + * If an operation exists, it is returned. If more than one operation exists, then the operation having the + * widest intersection between its {@linkplain AbstractCoordinateOperation#getDomainOfValidity() domain of + * validity} and the {@linkplain CoordinateOperationContext#getAreaOfInterest() area of interest} is returned. + * + * <p>The default implementation is as below:</p> * - * @param sourceCRS Input coordinate reference system. - * @param targetCRS Output coordinate reference system. - * @return A coordinate operation from {@code sourceCRS} to {@code targetCRS}. + * {@preformat java + * return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS); + * } + * + * Subclasses can override this method if they need, for example, to use a custom + * {@link CoordinateOperationInference} implementation. + * + * @param sourceCRS input coordinate reference system. + * @param targetCRS output coordinate reference system. + * @param context area of interest and desired accuracy, or {@code null}. + * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}. * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}. * @throws FactoryException if the operation creation failed for some other reason. + * + * @see CoordinateOperationInference + * + * @since 0.7 */ - @Override public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS, - final CoordinateReferenceSystem targetCRS) + final CoordinateReferenceSystem targetCRS, + final CoordinateOperationContext context) throws OperationNotFoundException, FactoryException { - return delegate().createOperation(sourceCRS, targetCRS); + return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS); } /** - * Temporarily returns a third-party factory for operation not yet implemented by this class. - * This method will be removed when the missing implementation will have been ported to SIS. + * Returns an operation using a particular method for conversion or transformation between + * two coordinate reference systems. If an operation exists using the given method, then it + * is returned. If no operation using the given method is found, then the implementation has + * the option of inferring the operation from the argument objects. + * + * <p>Current implementation ignores the {@code method} argument. + * This behavior may change in a future Apache SIS version.</p> + * + * @param sourceCRS input coordinate reference system. + * @param targetCRS output coordinate reference system. + * @param method the algorithmic method for conversion or transformation. + * @return a coordinate operation from {@code sourceCRS} to {@code targetCRS}. + * @throws OperationNotFoundException if no operation path was found from {@code sourceCRS} to {@code targetCRS}. + * @throws FactoryException if the operation creation failed for some other reason. + * + * @deprecated Replaced by {@link #createOperation(CoordinateReferenceSystem, CoordinateReferenceSystem, CoordinateOperationContext)}. */ - private synchronized CoordinateOperationFactory delegate() throws FactoryException { - if (delegate != null) { - return delegate; - } - for (final CoordinateOperationFactory factory : java.util.ServiceLoader.load(CoordinateOperationFactory.class)) { - if (!Utilities.isSIS(factory.getClass())) { - delegate = factory; - return factory; - } - } - throw new FactoryException(Errors.format(Errors.Keys.MissingRequiredModule_1, "geotk-referencing")); // This is temporary. + @Override + @Deprecated + public CoordinateOperation createOperation(final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS, + final OperationMethod method) + throws FactoryException + { + ArgumentChecks.ensureNonNull("method", method); // As a matter of principle. + return createOperation(sourceCRS, targetCRS, (CoordinateOperationContext) null); } - - /** Temporary, to be deleted in a future SIS version. */ - private transient CoordinateOperationFactory delegate; } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/GeneralMatrix.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -191,7 +191,7 @@ class GeneralMatrix extends MatrixSIS im */ static int indexOfErrors(final int numRow, final int numCol, final double[] elements) { assert elements.length % (numRow * numCol) == 0; - return (numRow * numCol) % elements.length; // A % B is for getting 0 without branching if A == B. + return (numRow * numCol) % elements.length; // A % B is for getting 0 without branching if A == B. } /** @@ -337,7 +337,7 @@ class GeneralMatrix extends MatrixSIS im if (copy) { elements = elements.clone(); } - return elements; // Internal array already uses extended precision. + return elements; // Internal array already uses extended precision. } else { elements = Arrays.copyOf(elements, length); } @@ -415,7 +415,7 @@ class GeneralMatrix extends MatrixSIS im * @see Matrices#create(int, int, Number[]) */ final boolean setElements(final Number[] newValues) { - final int numRow = this.numRow; // Protection against accidental changes. + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; final int length = numRow * numCol; if (newValues.length != length) { @@ -494,9 +494,18 @@ class GeneralMatrix extends MatrixSIS im */ @Override public final boolean isAffine() { - final int numRow = this.numRow; // Protection against accidental changes. + return isAffine(true); + } + + /** + * Implementation of {@link #isAffine()} with control on whether we require the matrix to be square. + * + * @param square {@code true} if the matrix must be square, or {@code false} for allowing non-square matrices. + */ + final boolean isAffine(final boolean square) { + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; - if (numRow == numCol) { + if (numRow == numCol || !square) { int i = numRow * numCol; if (elements[--i] == 1) { final int base = (numRow - 1) * numCol; @@ -526,7 +535,7 @@ class GeneralMatrix extends MatrixSIS im */ @Override public final boolean isIdentity() { - final int numRow = this.numRow; // Protection against accidental changes. + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; if (numRow != numCol) { return false; @@ -553,9 +562,9 @@ class GeneralMatrix extends MatrixSIS im */ @Override public void transpose() { - final int numRow = this.numRow; // Protection against accidental changes. + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; - final int errors = indexOfErrors(numRow, numCol, elements); // Where error values start, or 0 if none. + final int errors = indexOfErrors(numRow, numCol, elements); // Where error values start, or 0 if none. for (int j=0; j<numRow; j++) { for (int i=0; i<j; i++) { final int lo = j*numCol + i; @@ -574,7 +583,7 @@ class GeneralMatrix extends MatrixSIS im * The matrix sizes much match - this is not verified unless assertions are enabled. */ final void setToProduct(final Matrix A, final Matrix B) { - final int numRow = this.numRow; // Protection against accidental changes. + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; final int nc = A.getNumCol(); assert B.getNumRow() == nc; @@ -585,7 +594,7 @@ class GeneralMatrix extends MatrixSIS im */ final double[] eltA = getExtendedElements(A, numRow, nc, false); final double[] eltB = getExtendedElements(B, nc, numCol, false); - final int errorOffset = numRow * numCol; // Where error terms start. + final int errorOffset = numRow * numCol; // Where error terms start. final int errA = numRow * nc; final int errB = nc * numCol; /* @@ -597,20 +606,20 @@ class GeneralMatrix extends MatrixSIS im for (int i=0; i<numCol; i++) { sum.clear(); double max = 0; - int iB = i; // Index of values in a single column of B. - int iA = j * nc; // Index of values in a single row of A. + int iB = i; // Index of values in a single column of B. + int iA = j * nc; // Index of values in a single row of A. final int nextRow = iA + nc; while (iA < nextRow) { dot.setFrom (eltA, iA, errA); dot.multiply(eltB, iB, errB); sum.add(dot); - iB += numCol; // Move to next row of B. - iA++; // Move to next column of A. + iB += numCol; // Move to next row of B. + iA++; // Move to next column of A. final double value = Math.abs(dot.value); if (value > max) max = value; } if (Math.abs(sum.value) < Math.ulp(max) * ZERO_THRESHOLD) { - sum.clear(); // Sum is not significant according double arithmetic. + sum.clear(); // Sum is not significant according double arithmetic. } sum.storeTo(elements, k++, errorOffset); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -68,7 +68,7 @@ import java.util.Objects; * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.4 - * @version 0.6 + * @version 0.7 * @module * * @see org.apache.sis.parameter.TensorParameters @@ -735,7 +735,7 @@ public final class Matrices extends Stat */ public static MatrixSIS multiply(final Matrix m1, final Matrix m2) throws MismatchedMatrixSizeException { if (m1 instanceof MatrixSIS) { - return ((MatrixSIS) m1).multiply(m2); // Maybe the subclass override that method. + return ((MatrixSIS) m1).multiply(m2); // Maybe the subclass overrides that method. } final int nc = m2.getNumCol(); MatrixSIS.ensureNumRowMatch(m1.getNumCol(), m2.getNumRow(), nc); @@ -776,7 +776,6 @@ public final class Matrices extends Stat * @return {@code true} if the matrix represents an affine transform. * * @see MatrixSIS#isAffine() - * @see AffineTransforms2D#castOrCopy(Matrix) */ public static boolean isAffine(final Matrix matrix) { if (matrix instanceof MatrixSIS) { @@ -787,6 +786,32 @@ public final class Matrices extends Stat } /** + * Returns {@code true} if the given matrix represents a translation. + * This method returns {@code true} if the given matrix {@linkplain #isAffine(Matrix) is affine} + * and differs from the identity matrix only in the last column. + * + * @param matrix The matrix to test. + * @return {@code true} if the matrix represents a translation. + * + * @since 0.7 + */ + public static boolean isTranslation(final Matrix matrix) { + if (!isAffine(matrix)) { + return false; + } + final int numRow = matrix.getNumRow() - 1; // Excluding translation column. + final int numCol = matrix.getNumCol() - 1; // Excluding last row in affine transform. + for (int j=0; j<numRow; j++) { + for (int i=0; i<numCol; i++) { + if (matrix.getElement(j,i) != ((i == j) ? 1 : 0)) { + return false; + } + } + } + return true; + } + + /** * Returns {@code true} if the given matrix is close to an identity matrix, given a tolerance threshold. * This method is equivalent to computing the difference between the given matrix and an identity matrix * of identical size, and returning {@code true} if and only if all differences are smaller than or equal Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/NonSquareMatrix.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -227,28 +227,39 @@ next: do { } /** - * Inverse a matrix for a transform where target points has more ordinates than source points. + * Inverses a matrix for a transform where target points has more ordinates than source points. * In other words, the target matrices will be a transform that discard some ordinate values. * We will discard the ones for which the row contains only 0 or NaN elements. + * + * <p>In the special case where the last row is of the form [0 0 … 0 1] as in affine transforms, + * this method also omits rows that contain only a translation term. We allow that because if we + * do not omit those rows, then the matrix will be non-invertible anyway. This is true only when + * the last row contains only zero except in the last column ([0 0 … 0 n] where <var>n</var> can + * be any value). We restrict <var>n</var> to 1 for now because a different value may indicate a + * matrix created for another purpose than coordinate conversions.</p> */ private MatrixSIS inverseDimensionIncrease() throws NoninvertibleMatrixException { - final int numRow = this.numRow; // Protection against accidental changes. + final int numRow = this.numRow; // Protection against accidental changes. final int numCol = this.numCol; int j = numRow; - int oi = numRow - numCol; // Initialized to the maximal amount of rows that we may discard. + int oi = numRow - numCol; // Initialized to the maximal amount of rows that we may discard. final int[] omitted = new int[oi]; + final boolean ignoreTranslation = isAffine(false); + if (ignoreTranslation) j--; // Last row already verified by isAffine(). next: do { if (--j < 0) { - throw nonInvertible(); // Not enough rows that we can omit. + throw nonInvertible(); // Not enough rows that we can omit. } final int offset = j * numCol; - for (int i=offset + numCol; --i >= offset;) { + int i = offset + numCol; + if (ignoreTranslation) i--; + while (--i >= offset) { final double element = elements[i]; if (element != 0 && !Double.isNaN(element)) { continue next; } } - omitted[--oi] = j; // Found a row which contains only 0 or NaN elements. + omitted[--oi] = j; // Found a row which contains only 0 or NaN elements. } while (oi != 0); /* * Found enough rows containing only zero elements. Create a square matrix omitting those rows, @@ -258,7 +269,7 @@ next: do { int i = 0; for (j=0; j<numRow; j++) { if (oi != omitted.length && j == omitted[oi]) oi++; - else copyRow(this, j, squareMatrix, i++); // Copy only if not skipped. + else copyRow(this, j, squareMatrix, i++); // Copy only if not skipped. } if (indexOfErrors(numRow, numCol, elements) == 0) { inferErrors(squareMatrix.elements); @@ -329,6 +340,7 @@ next: do { * {@inheritDoc} */ @Override + @SuppressWarnings("CloneDoesntCallSuperClone") public MatrixSIS clone() { return new NonSquareMatrix(this); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Solver.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -38,7 +38,7 @@ import org.apache.sis.util.ArraysExt; * @module */ @SuppressWarnings("CloneInNonCloneableClass") -final class Solver implements Matrix { // Not Cloneable, despite the clone() method. +final class Solver implements Matrix { // Not Cloneable, despite the clone() method. /** * The size of the (i, j, s) tuples used internally by {@link #solve(Matrix, Matrix, double[], int, int)} * for storing information about the NaN values. @@ -143,7 +143,7 @@ final class Solver implements Matrix { / if (Y instanceof GeneralMatrix) { eltY = ((GeneralMatrix) Y).elements; if (eltY.length == size * innerSize) { - eltY = null; // Matrix does not contains error terms. + eltY = null; // Matrix does not contains error terms. } } return solve(X, Y, eltY, size, innerSize, true); @@ -248,7 +248,7 @@ searchNaN: for (int flatIndex = (size - } indexOfNaN[indexCount++] = i; indexOfNaN[indexCount++] = j; - indexOfNaN[indexCount++] = columnOfScale; // May be -1 (while uncommon) + indexOfNaN[indexCount++] = columnOfScale; // May be -1 (while uncommon) assert (indexCount % TUPLE_SIZE) == 0; } } @@ -262,7 +262,7 @@ searchNaN: for (int flatIndex = (size - final int j = indexOfNaN[k+1]; final int flatIndex = j*size + i; LU[flatIndex] = (i == lastRowOrColumn) ? 0 : 1; - LU[flatIndex + size*size] = 0; // Error term (see 'errorLU') in next method. + LU[flatIndex + size*size] = 0; // Error term (see 'errorLU') in next method. } } /* @@ -280,9 +280,9 @@ searchNaN: for (int flatIndex = (size - final int s = indexOfNaN[k++]; if (i != lastRowOrColumn) { // Found a scale factor to set to NaN. - matrix.setElement(i, j, Double.NaN); // Note that i,j indices are interchanged. + matrix.setElement(i, j, Double.NaN); // Note that i,j indices are interchanged. if (matrix.getElement(i, lastRowOrColumn) != 0) { - matrix.setElement(i, lastRowOrColumn, Double.NaN); // = -offset/scale, so 0 stay 0. + matrix.setElement(i, lastRowOrColumn, Double.NaN); // = -offset/scale, so 0 stay 0. } } else if (s >= 0) { // Found a translation factory to set to NaN. @@ -323,17 +323,17 @@ searchNaN: for (int flatIndex = (size - for (int j=0; j<size; j++) { pivot[j] = j; } - final double[] column = new double[size * 2]; // [0 … size-1] : column values; [size … 2*size-1] : error terms. - final DoubleDouble acc = new DoubleDouble(); // Temporary variable for sum ("accumulator") and subtraction. - final DoubleDouble rat = new DoubleDouble(); // Temporary variable for products and ratios. + final double[] column = new double[size * 2]; // [0 … size-1] : column values; [size … 2*size-1] : error terms. + final DoubleDouble acc = new DoubleDouble(); // Temporary variable for sum ("accumulator") and subtraction. + final DoubleDouble rat = new DoubleDouble(); // Temporary variable for products and ratios. for (int i=0; i<size; i++) { /* * Make a copy of the i-th column. */ for (int j=0; j<size; j++) { final int k = j*size + i; - column[j] = LU[k]; // Value - column[j + size] = LU[k + errorLU]; // Error + column[j] = LU[k]; // Value + column[j + size] = LU[k + errorLU]; // Error } /* * Apply previous transformations. This part is equivalent to the following code, @@ -372,7 +372,7 @@ searchNaN: for (int flatIndex = (size - if (p != i) { final int pRow = p*size; final int iRow = i*size; - for (int k=0; k<size; k++) { // Swap two full rows. + for (int k=0; k<size; k++) { // Swap two full rows. DoubleDouble.swap(LU, pRow + k, iRow + k, errorLU); } ArraysExt.swap(pivot, p, i); @@ -435,10 +435,10 @@ searchNaN: for (int flatIndex = (size - * elements[loRowOffset + i] -= (elements[rowOffset + i] * LU[luRowOffset + k]); */ for (int k=0; k<size; k++) { - final int rowOffset = k*innerSize; // Offset of row computed by current iteration. + final int rowOffset = k*innerSize; // Offset of row computed by current iteration. for (int j=k; ++j < size;) { - final int loRowOffset = j*innerSize; // Offset of some row after the current row. - final int luRowOffset = j*size; // Offset of the corresponding row in the LU matrix. + final int loRowOffset = j*innerSize; // Offset of some row after the current row. + final int luRowOffset = j*size; // Offset of the corresponding row in the LU matrix. for (int i=0; i<innerSize; i++) { acc.setFrom (elements, loRowOffset + i, errorOffset); rat.setFrom (elements, rowOffset + i, errorOffset); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/UnmodifiableMatrix.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -17,6 +17,7 @@ package org.apache.sis.referencing.operation.matrix; import org.opengis.referencing.operation.Matrix; +import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix; import org.apache.sis.util.resources.Errors; @@ -26,10 +27,10 @@ import org.apache.sis.util.resources.Err * * @author Martin Desruisseaux (Geomatys) * @since 0.6 - * @version 0.6 + * @version 0.7 * @module */ -final class UnmodifiableMatrix extends MatrixSIS { +final class UnmodifiableMatrix extends MatrixSIS implements ExtendedPrecisionMatrix { /** * For cross-version compatibility. */ @@ -91,6 +92,18 @@ final class UnmodifiableMatrix extends M } } + /** + * Returns elements together with their error terms if available, or just the elements otherwise. + */ + @Override + public double[] getExtendedElements() { + if (matrix instanceof ExtendedPrecisionMatrix) { + return ((ExtendedPrecisionMatrix) matrix).getExtendedElements(); + } else { + return getElements(); + } + } + /** * Returns the exception to throw when a setter method is invoked. */ Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -53,6 +53,43 @@ * for converting the longitude axis of source and target CRS to degrees before this operation is applied.</li> * </ul> * + * <div class="section"><cite>Early binding</cite> versus <cite>late binding</cite> implementations</div> + * There is sometime multiple ways of transforming coordinates for a given pair of source and target CRS. + * For example the {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa-Wolf parameters} + * may vary depending on the area of interest, like in the transformations from NAD27 to NAD83. + * Even for a fixed set of Bursa-Wolf parameter, there is various ways to use them (<cite>Molodensky</cite>, + * <cite>Abridged Molodensky</cite>, <cite>Geocentric translation</cite>, <cite>etc.</cite>). + * + * <p>EPSG identifies two approaches for addressing this multiplicity problem. + * Quoting the GIGS guideline:</p> + * + * <blockquote> + * <ul class="verbose"> + * <li><b>Early binding:</b> + * A priori association of a coordinate transformation with a geodetic CRS. + * The association is usually made at start-up of the session or project, as that is defined in the software, + * but always before any data is associated with the ‘CRS’. In general the ‘coordinate transformation’ specified + * uses the ‘CRS’ of the data as the source ‘CRS’ and WGS 84 as the target ‘CRS’.</li> + * + * <li><b>Late binding:</b> + * Association at run time of a coordinate transformation with a CRS. + * Late binding allows the user to select the appropriate transformation upon import of ‘geospatial data’ + * or merge of two geospatial datasets. This means that, in cases where there are multiple existing transformations, + * the user can choose the appropriate one, possibly aided by additional information.</li> + * </ul> + * <p style="text-align:right; font-size:small"><b>Source:</b> + * <u>Geospatial Integrity of Geoscience Software Part 1 – GIGS guidelines.</u> + * <i>OGP publication, Report No. 430-1, September 2011</i></p> + * </blockquote> + * + * Apache SIS is a <cite>late binding</cite> implementation, while a little trace for <cite>early binding</cite> + * exists in the form of the {@link org.apache.sis.referencing.datum.DefaultGeodeticDatum#getBursaWolfParameters()} + * method for those who really need it. This means that when searching for a coordinate operation between a given + * pair of CRS, Apache SIS will query {@link org.apache.sis.referencing.factory.sql.EPSGFactory} before to try to + * {@linkplain org.apache.sis.referencing.operation.CoordinateOperationInference infer the operation path by itelf}. + * The {@link org.apache.sis.referencing.operation.CoordinateOperationContext} can be used for further refinements, + * for example by specifying the area of interest. + * * @author Martin Desruisseaux (IRD, Geomatys) * @version 0.7 * @since 0.6 Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -251,6 +251,8 @@ public abstract class AbstractMathTransf * <li>Delegates to the {@link #transform(double[], int, double[], int, boolean)} method.</li> * </ul> * + * This method does not update the associated {@link org.opengis.referencing.crs.CoordinateReferenceSystem} value. + * * @param ptSrc the coordinate point to be transformed. * @param ptDst the coordinate point that stores the result of transforming {@code ptSrc}, or {@code null}. * @return the coordinate point after transforming {@code ptSrc} and storing the result in {@code ptDst}, Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -316,8 +316,7 @@ class ConcatenatedTransform extends Abst } } /* - * If one transform is the inverse of the - * other, returns the identity transform. + * If one transform is the inverse of the other, return the identity transform. */ if (areInverse(tr1, tr2) || areInverse(tr2, tr1)) { assert tr1.getSourceDimensions() == tr2.getTargetDimensions(); @@ -325,7 +324,7 @@ class ConcatenatedTransform extends Abst return MathTransforms.identity(tr1.getSourceDimensions()); // Returns a cached instance. } /* - * Gives a chance to AbstractMathTransform to returns an optimized object. + * Give a chance to AbstractMathTransform to returns an optimized object. * The main use case is Logarithmic vs Exponential transforms. */ if (tr1 instanceof AbstractMathTransform) { @@ -348,17 +347,17 @@ class ConcatenatedTransform extends Abst * Returns a name for the specified math transform. */ private static String getName(final MathTransform transform) { + ParameterValueGroup params = null; if (transform instanceof AbstractMathTransform) { - ParameterValueGroup params; params = ((AbstractMathTransform) transform).getContextualParameters(); - if (params == null) { - params = ((AbstractMathTransform) transform).getParameterValues(); - if (params != null) { - String name = params.getDescriptor().getName().getCode(); - if (name != null && !(name = name.trim()).isEmpty()) { - return name; - } - } + } + if (params == null && (transform instanceof Parameterized)) { + params = ((Parameterized) transform).getParameterValues(); + } + if (params != null) { + String name = params.getDescriptor().getName().getCode(); + if (name != null && !(name = name.trim()).isEmpty()) { + return name; } } return Classes.getShortClassName(transform); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/ContextualParameters.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -460,7 +460,7 @@ public class ContextualParameters extend offset = new DoubleDouble(-λ0); offset.multiply(toRadians); } - final MatrixSIS normalize = (MatrixSIS) this.normalize; // Must be the same instance, not a copy. + final MatrixSIS normalize = (MatrixSIS) this.normalize; // Must be the same instance, not a copy. normalize.convertBefore(0, toRadians, offset); normalize.convertBefore(1, toRadians, null); return normalize; @@ -485,7 +485,7 @@ public class ContextualParameters extend public synchronized MatrixSIS denormalizeGeographicOutputs(final double λ0) { ensureModifiable(); final DoubleDouble toDegrees = DoubleDouble.createRadiansToDegrees(); - final MatrixSIS denormalize = (MatrixSIS) this.denormalize; // Must be the same instance, not a copy. + final MatrixSIS denormalize = (MatrixSIS) this.denormalize; // Must be the same instance, not a copy. denormalize.convertAfter(0, toDegrees, (λ0 != 0) ? λ0 : null); denormalize.convertAfter(1, toDegrees, null); return denormalize; @@ -549,7 +549,7 @@ public class ContextualParameters extend final Map<ParameterDescriptor<?>, ParameterValue<?>> parameters = new IdentityHashMap<>(values.length); for (ParameterValue<?> p : values) { if (p == null) { - break; // The first null value in the array indicates the end of sequence. + break; // The first null value in the array indicates the end of sequence. } p = DefaultParameterValue.unmodifiable(p); final ParameterDescriptor<?> desc = p.getDescriptor(); Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -453,13 +453,18 @@ public class DefaultMathTransformFactory * {@link DefaultMathTransformFactory} uses this information for: * * <ul> - * <li>Complete some parameters if they were not provided. In particular, the {@linkplain #getSourceEllipsoid() + * <li>Completing some parameters if they were not provided. In particular, the {@linkplain #getSourceEllipsoid() * source ellipsoid} can be used for providing values for the {@code "semi_major"} and {@code "semi_minor"} * parameters in map projections.</li> - * <li>{@linkplain CoordinateSystems#swapAndScaleAxes Swap and scale axes} if the source or the target + * <li>{@linkplain CoordinateSystems#swapAndScaleAxes Swapping and scaling axes} if the source or the target * coordinate systems are not {@linkplain AxesConvention#NORMALIZED normalized}.</li> * </ul> * + * By default this class does <strong>not</strong> handle change of + * {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum#getPrimeMeridian() prime meridian} + * or anything else related to datum. Datum changes have dedicated {@link OperationMethod}, + * for example <cite>"Longitude rotation"</cite> (EPSG:9601) for changing the prime meridian. + * * @author Martin Desruisseaux (Geomatys) * @version 0.7 * @since 0.7 @@ -505,17 +510,6 @@ public class DefaultMathTransformFactory } /** - * Sets the source ellipsoid to the given value. - * The source coordinate system is unconditionally set to {@code null}. - * - * @param ellipsoid The ellipsoid to set as the source (can be {@code null}). - */ - public void setSource(final Ellipsoid ellipsoid) { - sourceEllipsoid = ellipsoid; - sourceCS = null; - } - - /** * Sets the source coordinate system to the given value. * The source ellipsoid is unconditionally set to {@code null}. * @@ -527,26 +521,23 @@ public class DefaultMathTransformFactory } /** - * Sets the source ellipsoid and coordinate system to values inferred from the given CRS. - * The source ellipsoid will be non-null only if the given CRS is geographic (not geocentric). + * Sets the source coordinate system and its associated ellipsoid to the given value. * - * @param crs The source coordinate reference system (can be {@code null}). - */ - public void setSource(final CoordinateReferenceSystem crs) { - sourceCS = (crs != null) ? crs.getCoordinateSystem() : null; - sourceEllipsoid = ReferencingUtilities.getEllipsoidOfGeographicCRS(crs); - // Ellipsoid is intentionally null for GeocentricCRS. - } - - /** - * Sets the target ellipsoid to the given value. - * The target coordinate system is unconditionally set to {@code null}. + * <div class="note"><b>Design note:</b> + * ellipsoidal coordinate systems and ellipsoids are associated indirectly, through a geodetic CRS. + * However this method expects those two components to be given explicitely instead than inferring + * them from a {@code CoordinateReferenceSystem} for making clear that {@code MathTransformFactory} + * does not perform any {@linkplain org.apache.sis.referencing.datum.DefaultGeodeticDatum geodetic + * datum} analysis. For coordinate operations that take datum changes in account (including change + * of prime meridian), see {@link org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory}. + * This policy helps to enforce a separation of concerns.</div> * - * @param ellipsoid The ellipsoid to set as the target (can be {@code null}). + * @param cs The coordinate system to set as the source, or {@code null}. + * @param ellipsoid The ellipsoid associated to the given coordinate system, or {@code null}. */ - public void setTarget(final Ellipsoid ellipsoid) { - targetEllipsoid = ellipsoid; - targetCS = null; + public void setSource(final EllipsoidalCS cs, final Ellipsoid ellipsoid) { + sourceCS = cs; + sourceEllipsoid = ellipsoid; } /** @@ -561,15 +552,17 @@ public class DefaultMathTransformFactory } /** - * Sets the target ellipsoid and coordinate system to values inferred from the given CRS. - * The target ellipsoid will be non-null only if the given CRS is geographic (not geocentric). + * Sets the target coordinate system and its associated ellipsoid to the given value. + * + * <div class="note"><b>Design note:</b> + * see {@link #setSource(EllipsoidalCS, Ellipsoid)}.</div> * - * @param crs The target coordinate reference system (can be {@code null}). + * @param cs The coordinate system to set as the source, or {@code null}. + * @param ellipsoid The ellipsoid associated to the given coordinate system, or {@code null}. */ - public void setTarget(final CoordinateReferenceSystem crs) { - targetCS = (crs != null) ? crs.getCoordinateSystem() : null; - targetEllipsoid = ReferencingUtilities.getEllipsoidOfGeographicCRS(crs); - // Ellipsoid is intentionally null for GeocentricCRS. + public void setTarget(final EllipsoidalCS cs, final Ellipsoid ellipsoid) { + targetCS = cs; + targetEllipsoid = ellipsoid; } /** @@ -611,6 +604,66 @@ public class DefaultMathTransformFactory } /** + * Returns the matrix that represent the affine transform to concatenate before or after + * the parameterized transform. The {@code role} argument specifies which matrix is desired: + * + * <ul class="verbose"> + * <li>{@link org.apache.sis.referencing.operation.transform.ContextualParameters.MatrixRole#NORMALIZATION + * NORMALIZATION} for the conversion from the {@linkplain #getSourceCS() source coordinate system} to + * a {@linkplain AxesConvention#NORMALIZED normalized} coordinate system, usually with + * (<var>longitude</var>, <var>latitude</var>) axis order in degrees or + * (<var>easting</var>, <var>northing</var>) in metres. + * This normalization needs to be applied <em>before</em> the parameterized transform.</li> + * + * <li>{@link org.apache.sis.referencing.operation.transform.ContextualParameters.MatrixRole#DENORMALIZATION + * DENORMALIZATION} for the conversion from a normalized coordinate system to the + * {@linkplain #getTargetCS() target coordinate system}, for example with + * (<var>latitude</var>, <var>longitude</var>) axis order. + * This denormalization needs to be applied <em>after</em> the parameterized transform.</li> + * + * <li>{@link org.apache.sis.referencing.operation.transform.ContextualParameters.MatrixRole#INVERSE_NORMALIZATION INVERSE_NORMALIZATION} and + * {@link org.apache.sis.referencing.operation.transform.ContextualParameters.MatrixRole#INVERSE_DENORMALIZATION INVERSE_DENORMALIZATION} + * are also supported but rarely used.</li> + * </ul> + * + * This method is invoked by {@link DefaultMathTransformFactory#swapAndScaleAxes(MathTransform, Context)}. + * Users an override this method if they need to customize the normalization process. + * + * @param role Whether the normalization or denormalization matrix is desired. + * @return The requested matrix, or {@code null} if this {@code Context} has no information about the coordinate system. + * @throws FactoryException if an error occurred while computing the matrix. + * + * @see DefaultMathTransformFactory#createAffineTransform(Matrix) + * @see DefaultMathTransformFactory#createParameterizedTransform(ParameterValueGroup, Context) + */ + @SuppressWarnings("fallthrough") + public Matrix getMatrix(final ContextualParameters.MatrixRole role) throws FactoryException { + final CoordinateSystem specified; + boolean inverse = false; + switch (role) { + default: throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "role", role)); + case INVERSE_NORMALIZATION: inverse = true; // Fall through + case NORMALIZATION: specified = getSourceCS(); break; + case INVERSE_DENORMALIZATION: inverse = true; // Fall through + case DENORMALIZATION: inverse = !inverse; + specified = getTargetCS(); break; + } + if (specified == null) { + return null; + } + final CoordinateSystem normalized = CoordinateSystems.replaceAxes(specified, AxesConvention.NORMALIZED); + try { + if (inverse) { + return CoordinateSystems.swapAndScaleAxes(normalized, specified); + } else { + return CoordinateSystems.swapAndScaleAxes(specified, normalized); + } + } catch (IllegalArgumentException | ConversionException cause) { + throw new InvalidGeodeticParameterException(cause.getLocalizedMessage(), cause); + } + } + + /** * Returns the parameter values used for the math transform creation, including the parameters completed * by the factory. * @@ -968,7 +1021,7 @@ public class DefaultMathTransformFactory /** * Given a transform between normalized spaces, - * create a transform taking in account axis directions and units of measurement. + * creates a transform taking in account axis directions, units of measurement and longitude rotation. * This method {@linkplain #createConcatenatedTransform concatenates} the given parameterized transform * with any other transform required for performing units changes and ordinates swapping. * @@ -984,6 +1037,10 @@ public class DefaultMathTransformFactory * both of them with ({@linkplain org.opengis.referencing.cs.AxisDirection#EAST East}, * {@linkplain org.opengis.referencing.cs.AxisDirection#NORTH North}) axis orientations.</div> * + * <div class="section">Controlling the normalization process</div> + * Users who need a different normalized space than the default one way find more convenient to + * override the {@link Context#getMatrix Context.getMatrix(ContextualParameters.MatrixRole)} method. + * * @param parameterized A transform for normalized input and output coordinates. * @param context Source and target coordinate systems in which the transform is going to be used. * @return A transform taking in account unit conversions and axis swapping. @@ -991,25 +1048,20 @@ public class DefaultMathTransformFactory * * @see org.apache.sis.referencing.cs.AxesConvention#NORMALIZED * @see org.apache.sis.referencing.operation.DefaultConversion#DefaultConversion(Map, OperationMethod, MathTransform, ParameterValueGroup) + * + * @since 0.7 */ public MathTransform swapAndScaleAxes(final MathTransform parameterized, final Context context) throws FactoryException { ArgumentChecks.ensureNonNull("parameterized", parameterized); ArgumentChecks.ensureNonNull("context", context); - final CoordinateSystem sourceCS = context.getSourceCS(); - final CoordinateSystem targetCS = context.getTargetCS(); /* * Computes matrix for swapping axis and performing units conversion. * There is one matrix to apply before projection on (longitude,latitude) * coordinates, and one matrix to apply after projection on (easting,northing) * coordinates. */ - final Matrix swap1, swap3; - try { - swap1 = (sourceCS != null) ? CoordinateSystems.swapAndScaleAxes(sourceCS, CoordinateSystems.replaceAxes(sourceCS, AxesConvention.NORMALIZED)) : null; - swap3 = (targetCS != null) ? CoordinateSystems.swapAndScaleAxes(CoordinateSystems.replaceAxes(targetCS, AxesConvention.NORMALIZED), targetCS) : null; - } catch (IllegalArgumentException | ConversionException cause) { - throw new InvalidGeodeticParameterException(cause.getLocalizedMessage(), cause); - } + final Matrix swap1 = context.getMatrix(ContextualParameters.MatrixRole.NORMALIZATION); + final Matrix swap3 = context.getMatrix(ContextualParameters.MatrixRole.DENORMALIZATION); /* * Prepares the concatenation of the matrices computed above and the projection. * Note that at this stage, the dimensions between each step may not be compatible. @@ -1089,8 +1141,7 @@ public class DefaultMathTransformFactory ArgumentChecks.ensureNonNull("baseCRS", baseCRS); ArgumentChecks.ensureNonNull("parameters", parameters); ArgumentChecks.ensureNonNull("derivedCS", derivedCS); - final Context context = new Context(); - context.setSource(baseCRS); + final Context context = ReferencingUtilities.createTransformContext(baseCRS, null, null); context.setTarget(derivedCS); return createParameterizedTransform(parameters, context); } @@ -1125,6 +1176,25 @@ public class DefaultMathTransformFactory } /** + * Creates a math transform that represent a change of coordinate system. + * + * @param source the source coordinate system. + * @param target the target coordinate system. + * @return a conversion from the given source to the given target coordinate system. + * @throws FactoryException if the conversion can not be created. + * + * @since 0.7 + */ + public MathTransform createCoordinateSystemChange(final CoordinateSystem source, final CoordinateSystem target) + throws FactoryException + { + ArgumentChecks.ensureNonNull("source", source); + ArgumentChecks.ensureNonNull("target", target); + return CoordinateSystemTransform.create(this, source, target); + // No need to use unique(…) here. + } + + /** * Creates an affine transform from a matrix. If the transform input dimension is {@code M}, * and output dimension is {@code N}, then the matrix will have size {@code [N+1][M+1]}. The * +1 in the matrix dimensions allows the matrix to do a shift, as well as a rotation. The @@ -1140,7 +1210,11 @@ public class DefaultMathTransformFactory */ @Override public MathTransform createAffineTransform(final Matrix matrix) throws FactoryException { - lastMethod.remove(); // To be strict, we should set the ProjectiveTransform provider. + /* + * Performance note: we could set lastMethod to the "Affine" operation method provider, but we do not + * because setting this value is not free (e.g. it depends on matrix size) and it is rarely needed. + */ + lastMethod.remove(); return unique(MathTransforms.linear(matrix)); } Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -46,6 +46,7 @@ import org.apache.sis.internal.referenci import org.apache.sis.internal.referencing.provider.GeocentricToGeographic; import org.apache.sis.internal.referencing.provider.GeographicToGeocentric; import org.apache.sis.internal.referencing.provider.Geographic3Dto2D; +import org.apache.sis.internal.metadata.ReferencingServices; import org.apache.sis.parameter.DefaultParameterDescriptorGroup; import org.apache.sis.metadata.iso.ImmutableIdentifier; import org.apache.sis.referencing.operation.matrix.Matrix3; @@ -347,6 +348,19 @@ public class EllipsoidToCentricTransform final double semiMajor, final double semiMinor, final Unit<Length> unit, final boolean withHeight, final TargetType target) throws FactoryException { + if (Math.abs(semiMajor - semiMinor) <= semiMajor * (Formulas.LINEAR_TOLERANCE / ReferencingServices.AUTHALIC_RADIUS)) { + /* + * If semi-major axis length is almost equal to semi-minor axis length, uses spherical equations instead. + * We need to add the sphere radius to the elevation before to perform spherical to Cartesian conversion. + */ + final MatrixSIS translate = Matrices.createDiagonal(4, withHeight ? 4 : 3); + translate.setElement(2, withHeight ? 3 : 2, semiMajor); + if (!withHeight) { + translate.setElement(3, 2, 1); + } + final MathTransform tr = SphericalToCartesian.INSTANCE.completeTransform(factory); + return factory.createConcatenatedTransform(factory.createAffineTransform(translate), tr); + } EllipsoidToCentricTransform tr = new EllipsoidToCentricTransform(semiMajor, semiMinor, unit, withHeight, target); return tr.context.completeTransform(factory, tr); } @@ -615,7 +629,7 @@ public class EllipsoidToCentricTransform /* * NOTE: we do not bother to override the methods expecting a 'float' array because those methods should * be rarely invoked. Since there is usually LinearTransforms before and after this transform, the - * conversion between float and double will be handle by those LinearTransforms. If nevertheless + * conversion between float and double will be handled by those LinearTransforms. If nevertheless * this EllipsoidToCentricTransform is at the beginning or the end of a transformation chain, * the methods inherited from the subclass will work (but may be slightly slower). */ Modified: sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java URL: http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java?rev=1737072&r1=1737071&r2=1737072&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java [UTF-8] (original) +++ sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedMolodenskyTransform.java [UTF-8] Tue Mar 29 22:51:40 2016 @@ -305,7 +305,7 @@ public class InterpolatedMolodenskyTrans /* * NOTE: we do not bother to override the methods expecting a 'float' array because those methods should * be rarely invoked. Since there is usually LinearTransforms before and after this transform, the - * conversion between float and double will be handle by those LinearTransforms. If nevertheless + * conversion between float and double will be handled by those LinearTransforms. If nevertheless * this MolodenskyTransform is at the beginning or the end of a transformation chain, the methods * inherited from the subclass will work (but may be slightly slower). */
