Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CRSPair.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -18,6 +18,7 @@ package org.apache.sis.referencing.opera import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.IdentifiedObject; +import org.apache.sis.referencing.AbstractIdentifiedObject; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.util.CharSequences; import org.apache.sis.util.Classes; @@ -44,8 +45,8 @@ final class CRSPair { /** * Creates a {@code CRSPair} for the specified source and target CRS. */ - public CRSPair(final CoordinateReferenceSystem sourceCRS, - final CoordinateReferenceSystem targetCRS) + CRSPair(final CoordinateReferenceSystem sourceCRS, + final CoordinateReferenceSystem targetCRS) { this.sourceCRS = sourceCRS; this.targetCRS = targetCRS; @@ -78,24 +79,34 @@ final class CRSPair { } /** - * Returns a name for the given object, truncating it if needed. + * Returns the name of the GeoAPI interface implemented by the specified object, + * followed by the name between brackets. */ - static String shortName(final IdentifiedObject object) { - String name = IdentifiedObjects.getName(object, null); - if (name == null) { - name = Classes.getShortClassName(object); + static String label(final IdentifiedObject object) { + if (object == null) { + return null; + } + Class<? extends IdentifiedObject> type; + if (object instanceof AbstractIdentifiedObject) { + type = ((AbstractIdentifiedObject) object).getInterface(); } else { - int i = 30; // Arbitrary length threshold. + type = Classes.getLeafInterfaces(object.getClass(), IdentifiedObject.class)[0]; + } + String label = Classes.getShortName(type); + String name = IdentifiedObjects.getName(object, null); + if (name != null) { + int i = 30; // Arbitrary length threshold. if (name.length() >= i) { - while (i > 15) { // Arbitrary minimal length. + while (i > 15) { // Arbitrary minimal length. final int c = name.codePointBefore(i); if (Character.isSpaceChar(c)) break; i -= Character.charCount(c); } name = CharSequences.trimWhitespaces(name, 0, i).toString() + '…'; } + label = label + "[“" + name + "”]"; } - return name; + return label; } /** @@ -103,6 +114,6 @@ final class CRSPair { */ @Override public String toString() { - return shortName(sourceCRS) + " → " + shortName(targetCRS); + return label(sourceCRS) + " → " + label(targetCRS); } }
Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationContext.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -19,11 +19,15 @@ package org.apache.sis.referencing.opera import java.io.Serializable; import org.opengis.metadata.extent.Extent; import org.opengis.metadata.extent.GeographicBoundingBox; +import org.opengis.referencing.operation.CoordinateOperation; import org.apache.sis.metadata.iso.extent.DefaultExtent; import org.apache.sis.metadata.iso.extent.Extents; import org.apache.sis.internal.util.CollectionsExt; import org.apache.sis.util.ArgumentChecks; +// Branch-dependent imports +import org.apache.sis.internal.jdk8.Predicate; + /** * Optional information about the context in which a requested coordinate operation will be used. @@ -38,7 +42,7 @@ import org.apache.sis.util.ArgumentCheck * to choose the most suitable coordinate transformation between two CRS. * * <div class="note"><b>Example:</b> - * if a transformation from NAD27 to NAD83 is requested without providing context, then Apache SIS will return the + * if a transformation from NAD27 to WGS84 is requested without providing context, then Apache SIS will return the * transformation applicable to the widest North American surface. But if the user provides a context saying that * he wants to transform coordinates in Texas, then Apache SIS may return another coordinate transformation with * different {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa-Wolf parameters} more suitable @@ -66,11 +70,6 @@ public class CoordinateOperationContext private Extent areaOfInterest; /** - * The geographic component of the area of interest, computed when first needed. - */ - private transient GeographicBoundingBox bbox; - - /** * The desired accuracy in metres, or 0 for the best accuracy available. * See {@link #getDesiredAccuracy()} for more details about what we mean by <cite>"best accuracy"</cite>. */ @@ -99,6 +98,8 @@ public class CoordinateOperationContext * Returns the spatio-temporal area of interest, or {@code null} if none. * * @return The spatio-temporal area of interest, or {@code null} if none. + * + * @see Extents#getGeographicBoundingBox(Extent) */ public Extent getAreaOfInterest() { return areaOfInterest; @@ -109,32 +110,24 @@ public class CoordinateOperationContext * * @param area The spatio-temporal area of interest, or {@code null} if none. */ - public void setAreaOfInterest(final Extent area) { - areaOfInterest = area; - } - - /** - * Returns the geographic component of the area of interest, or {@code null} if none. - * This convenience method extracts the bounding box from the spatio-temporal {@link Extent}. - * - * @return The geographic area of interest, or {@code null} if none. - */ - public GeographicBoundingBox getGeographicBoundingBox() { - if (bbox == null) { - bbox = Extents.getGeographicBoundingBox(areaOfInterest); + public void setAreaOfInterest(Extent area) { + if (area != null) { + area = new DefaultExtent(area); } - return bbox; + areaOfInterest = area; } /** * Sets the geographic component of the area of interest, or {@code null} if none. * This convenience method set the bounding box into the spatio-temporal {@link Extent}. * + * <p>The reverse operation can be done with <code>{@linkplain Extents#getGeographicBoundingBox(Extent) + * Extents.getGeographicBoundingBox}({@linkplain #getAreaOfInterest()})</code>.</p> + * * @param area The geographic area of interest, or {@code null} if none. */ - public void setGeographicBoundingBox(final GeographicBoundingBox area) { + public void setAreaOfInterest(final GeographicBoundingBox area) { areaOfInterest = setGeographicBoundingBox(areaOfInterest, area); - bbox = area; } /** @@ -177,4 +170,13 @@ public class CoordinateOperationContext ArgumentChecks.ensurePositive("accuracy", accuracy); desiredAccuracy = accuracy; } + + /** + * Returns a filter that can be used for applying additional restrictions on the coordinate operation. + * + * @todo Not yet implemented. + */ + Predicate<CoordinateOperation> getOperationFilter() { + return null; + } } Copied: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java (from r1740146, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java) URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java?p2=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java&p1=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java&r1=1740146&r2=1740152&rev=1740152&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationFinder.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -158,8 +158,8 @@ public class CoordinateOperationFinder e final CoordinateOperationContext context) throws FactoryException { super(registry, factory, context); - identifierOfStepCRS = new HashMap<>(8); - previousSearches = new HashMap<>(8); + identifierOfStepCRS = new HashMap<Identifier,Object>(8); + previousSearches = new HashMap<CRSPair,Boolean>(8); } /** @@ -642,7 +642,9 @@ public class CoordinateOperationFinder e final Matrix matrix; try { matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS); - } catch (IllegalArgumentException | ConversionException exception) { + } catch (IllegalArgumentException exception) { + throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception); + } catch (ConversionException exception) { throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception); } return createFromAffineTransform(AXIS_CHANGES, sourceCRS, targetCRS, matrix); @@ -688,7 +690,9 @@ public class CoordinateOperationFinder e final Matrix matrix; try { matrix = CoordinateSystems.swapAndScaleAxes(sourceCS, targetCS); - } catch (IllegalArgumentException | ConversionException exception) { + } catch (IllegalArgumentException exception) { + throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception); + } catch (ConversionException exception) { throw new OperationNotFoundException(notFoundMessage(sourceCRS, targetCRS), exception); } final int translationColumn = matrix.getNumCol() - 1; // Paranoiac check: should always be 1. @@ -902,7 +906,7 @@ public class CoordinateOperationFinder e if (main instanceof SingleOperation) { final SingleOperation op = (SingleOperation) main; final MathTransform mt = factorySIS.getMathTransformFactory().createConcatenatedTransform(mt1, mt2); - main = createFromMathTransform(new HashMap<>(IdentifiedObjects.getProperties(main)), + main = createFromMathTransform(new HashMap<String,Object>(IdentifiedObjects.getProperties(main)), sourceCRS, targetCRS, mt, op.getMethod(), op.getParameterValues(), (main instanceof Transformation) ? Transformation.class : (main instanceof Conversion) ? Conversion.class : SingleOperation.class); @@ -922,7 +926,7 @@ public class CoordinateOperationFinder e break; } } - main = createFromMathTransform(new HashMap<>(IdentifiedObjects.getProperties(main)), + main = createFromMathTransform(new HashMap<String,Object>(IdentifiedObjects.getProperties(main)), main.getSourceCRS(), main.getTargetCRS(), main.getMathTransform(), null, null, type); } return main; @@ -988,7 +992,7 @@ public class CoordinateOperationFinder e identifierOfStepCRS.put(newID, oldID); identifierOfStepCRS.put(oldID, count); - final Map<String,Object> properties = new HashMap<>(4); + final Map<String,Object> properties = new HashMap<String,Object>(4); properties.put(IdentifiedObject.NAME_KEY, newID); properties.put(IdentifiedObject.REMARKS_KEY, Vocabulary.formatInternational( Vocabulary.Keys.DerivedFrom_1, CRSPair.label(object))); Copied: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java (from r1740146, sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java) URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?p2=sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java&p1=sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java&r1=1740146&r2=1740152&rev=1740152&view=diff ============================================================================== --- sis/branches/JDK7/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -74,7 +74,7 @@ import org.apache.sis.util.resources.Voc import org.apache.sis.util.resources.Errors; // Branch-dependent imports -import java.util.Objects; +import org.apache.sis.internal.jdk7.Objects; import org.apache.sis.internal.jdk8.JDK8; import org.apache.sis.internal.jdk8.Predicate; @@ -303,7 +303,10 @@ class CoordinateOperationRegistry { } return operation; } - } catch (IllegalArgumentException | ConversionException e) { + } catch (IllegalArgumentException e) { + throw new FactoryException(Errors.format( + Errors.Keys.CanNotInstantiate_1, new CRSPair(sourceCRS, targetCRS)), e); + } catch (ConversionException e) { throw new FactoryException(Errors.format( Errors.Keys.CanNotInstantiate_1, new CRSPair(sourceCRS, targetCRS)), e); } @@ -366,7 +369,7 @@ class CoordinateOperationRegistry { return null; } } - } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException exception) { + } catch (NoSuchAuthorityCodeException exception) { /* * sourceCode or targetCode is unknown to the underlying authority factory. * Ignores the exception and fallback on the generic algorithm provided by @@ -374,6 +377,9 @@ class CoordinateOperationRegistry { */ log(exception); return null; + } catch (MissingFactoryResourceException exception) { + log(exception); + return null; } /* * We will loop over all coordinate operations and select the one having the largest intersection @@ -634,7 +640,7 @@ class CoordinateOperationRegistry { CoordinateReferenceSystem crs; if (Utilities.equalsApproximatively(sourceCRS, crs = operation.getSourceCRS())) sourceCRS = crs; if (Utilities.equalsApproximatively(targetCRS, crs = operation.getTargetCRS())) targetCRS = crs; - final Map<String,Object> properties = new HashMap<>(derivedFrom(operation)); + final Map<String,Object> properties = new HashMap<String,Object>(derivedFrom(operation)); /* * Determine whether the operation to create is a Conversion or a Transformation * (could also be a Conversion subtype like Projection, but this is less important). @@ -668,8 +674,11 @@ class CoordinateOperationRegistry { try { method = factory.getOperationMethod(method.getName().getCode()); method = DefaultOperationMethod.redimension(method, sourceDimensions, targetDimensions); - } catch (NoSuchIdentifierException | IllegalArgumentException se) { - ex.addSuppressed(se); + } catch (NoSuchIdentifierException se) { + // ex.addSuppressed(se) on the JDK7 branch. + throw ex; + } catch (IllegalArgumentException se) { + // ex.addSuppressed(se) on the JDK7 branch. throw ex; } } @@ -704,7 +713,7 @@ class CoordinateOperationRegistry { private CoordinateOperation propagateVertical(final CoordinateOperation operation, final boolean source3D, final boolean target3D) throws IllegalArgumentException, FactoryException { - final List<CoordinateOperation> operations = new ArrayList<>(); + final List<CoordinateOperation> operations = new ArrayList<CoordinateOperation>(); if (operation instanceof ConcatenatedOperation) { operations.addAll(((ConcatenatedOperation) operation).getOperations()); } else { @@ -863,7 +872,7 @@ class CoordinateOperationRegistry { * @return a modifiable map containing the given name. Callers can put other entries in this map. */ static Map<String,Object> properties(final Identifier name) { - final Map<String,Object> properties = new HashMap<>(4); + final Map<String,Object> properties = new HashMap<String,Object>(4); properties.put(CoordinateOperation.NAME_KEY, name); if ((name == DATUM_SHIFT) || (name == ELLIPSOID_CHANGE)) { properties.put(CoordinateOperation.COORDINATE_OPERATION_ACCURACY_KEY, new PositionalAccuracy[] { Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultConcatenatedOperation.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -17,36 +17,31 @@ package org.apache.sis.referencing.operation; import java.util.Map; -import java.util.Set; import java.util.List; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashSet; import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.opengis.util.FactoryException; -import org.opengis.metadata.quality.PositionalAccuracy; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.ConcatenatedOperation; import org.opengis.referencing.operation.Transformation; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; +import org.apache.sis.internal.referencing.PositionalAccuracyConstant; import org.apache.sis.internal.system.DefaultFactories; import org.apache.sis.internal.util.UnmodifiableArrayList; -import org.apache.sis.internal.util.CollectionsExt; -import org.apache.sis.util.collection.Containers; import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.resources.Errors; +import org.apache.sis.io.wkt.Formatter; import static org.apache.sis.util.Utilities.deepEquals; // Branch-dependent imports import org.apache.sis.internal.jdk7.Objects; -import org.apache.sis.io.wkt.Formatter; /** @@ -114,9 +109,8 @@ final class DefaultConcatenatedOperation { super(properties); ArgumentChecks.ensureNonNull("operations", operations); - final boolean setAccuracy = (coordinateOperationAccuracy == null); final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(operations.length); - initialize(properties, operations, flattened, mtFactory, setAccuracy); + initialize(properties, operations, flattened, mtFactory, (coordinateOperationAccuracy == null)); if (flattened.size() < 2) { throw new IllegalArgumentException(Errors.getResources(properties).getString( Errors.Keys.TooFewOccurrences_2, 2, CoordinateOperation.class)); @@ -125,10 +119,6 @@ final class DefaultConcatenatedOperation this.operations = UnmodifiableArrayList.wrap(operations); this.sourceCRS = operations[0].getSourceCRS(); this.targetCRS = operations[operations.length - 1].getTargetCRS(); - if (setAccuracy) { - coordinateOperationAccuracy = CollectionsExt.unmodifiableOrCopy( - (Set<PositionalAccuracy>) coordinateOperationAccuracy); - } checkDimensions(properties); } @@ -147,11 +137,11 @@ final class DefaultConcatenatedOperation * in the given list. This should not happen according ISO 19111 standard, but we try to be safe. * * <div class="section">How coordinate operation accuracy is determined</div> - * If {@code setAccuracy} is {@code true}, then this method collects all accuracy information found in the - * {@link Transformation} instances. This method ignores instances of other kinds for the following reason: + * If {@code setAccuracy} is {@code true}, then this method copies accuracy information found in the single + * {@link Transformation} instance. This method ignores instances of other kinds for the following reason: * some {@link Conversion} instances declare an accuracy, which is typically close to zero. If a concatenated * operation contains such conversion together with a transformation with unknown accuracy, then we do not want - * to declare that a 0 meter error as the concatenated operation accuracy; it would be a false information. + * to declare "0 meter" as the concatenated operation accuracy; it would be a false information. * An other reason is that a concatenated operation typically contains an arbitrary amount of conversions, * but only one transformation. So considering only transformations usually means to pickup only one operation * in the given {@code operations} list, which make things clearer. @@ -174,7 +164,7 @@ final class DefaultConcatenatedOperation final CoordinateOperation[] operations, final List<CoordinateOperation> flattened, final MathTransformFactory mtFactory, - final boolean setAccuracy) + boolean setAccuracy) throws FactoryException { CoordinateReferenceSystem previous = null; @@ -218,16 +208,22 @@ final class DefaultConcatenatedOperation transform = (transform != null) ? mtFactory.createConcatenatedTransform(transform, step) : step; } /* - * Optionally update the coordinate operation accuracy. - * See javadoc for a rational about why we take only transformations in account. + * Optionally copy the coordinate operation accuracy from the transformation (or from a concatenated + * operation on the assumption that its accuracy was computed by the same algorithm than this method). + * See javadoc for a rational about why we take only transformations in account. If more than one + * transformation is found, clear the collection and abandon the attempt to set the accuracy information. + * Instead the user will get a better result by invoking PositionalAccuracyConstant.getLinearAccuracy(…) + * since that method conservatively computes the sum of all linear accuracy. */ - if (setAccuracy && op instanceof Transformation) { - Collection<PositionalAccuracy> candidates = op.getCoordinateOperationAccuracy(); - if (!Containers.isNullOrEmpty(candidates)) { - if (coordinateOperationAccuracy == null) { - coordinateOperationAccuracy = new LinkedHashSet<PositionalAccuracy>(); + if (setAccuracy && (op instanceof Transformation || op instanceof ConcatenatedOperation)) { + if (coordinateOperationAccuracy == null) { + setAccuracy = (PositionalAccuracyConstant.getLinearAccuracy(op) > 0); + if (setAccuracy) { + coordinateOperationAccuracy = op.getCoordinateOperationAccuracy(); } - coordinateOperationAccuracy.addAll(candidates); + } else { + coordinateOperationAccuracy = null; + setAccuracy = false; } } } @@ -337,6 +333,7 @@ final class DefaultConcatenatedOperation protected String formatTo(final Formatter formatter) { super.formatTo(formatter); for (final CoordinateOperation component : operations) { + formatter.newLine(); formatter.append(castOrCopy(component)); } formatter.setInvalidWKT(this, null); @@ -382,8 +379,7 @@ final class DefaultConcatenatedOperation */ private void setSteps(final CoordinateOperation[] steps) throws FactoryException { final List<CoordinateOperation> flattened = new ArrayList<CoordinateOperation>(steps.length); - initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class), true); + initialize(null, steps, flattened, DefaultFactories.forBuildin(MathTransformFactory.class), coordinateOperationAccuracy == null); operations = UnmodifiableArrayList.wrap(flattened.toArray(new CoordinateOperation[flattened.size()])); - coordinateOperationAccuracy = CollectionsExt.unmodifiableOrCopy((Set<PositionalAccuracy>) coordinateOperationAccuracy); } } 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=1740152&r1=1740151&r2=1740152&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] Wed Apr 20 14:53:31 2016 @@ -25,16 +25,20 @@ import org.opengis.util.NoSuchIdentifier import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.referencing.operation.*; +import org.opengis.referencing.AuthorityFactory; 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.opengis.referencing.crs.SingleCRS; +import org.opengis.referencing.crs.CRSFactory; +import org.opengis.referencing.cs.CSFactory; 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.Constants; import org.apache.sis.referencing.CRS; import org.apache.sis.referencing.factory.InvalidGeodeticParameterException; import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; @@ -44,6 +48,7 @@ import org.apache.sis.util.iso.AbstractF import org.apache.sis.util.resources.Errors; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.CharSequences; +import org.apache.sis.util.Classes; import org.apache.sis.util.NullArgumentException; import org.apache.sis.util.Utilities; @@ -77,6 +82,12 @@ import org.apache.sis.util.Utilities; */ public class DefaultCoordinateOperationFactory extends AbstractFactory implements CoordinateOperationFactory { /** + * Whether this class is allowed to use the EPSG authority factory for searching coordinate operation paths. + * This flag should always be {@code true}, except temporarily for testing purposes. + */ + static final boolean USE_EPSG_FACTORY = true; + + /** * The default properties, or an empty map if none. This map shall not change after construction in * order to allow usage without synchronization in multi-thread context. But we do not need to wrap * in a unmodifiable map since {@code DefaultCoordinateOperationFactory} does not provide public @@ -85,6 +96,22 @@ public class DefaultCoordinateOperationF private final Map<String,?> defaultProperties; /** + * The factory to use if {@link CoordinateOperationFinder} needs to create CRS for intermediate steps. + * Will be created only when first needed. + * + * @see #getCRSFactory() + */ + private volatile CRSFactory crsFactory; + + /** + * The factory to use if {@link CoordinateOperationFinder} needs to create CS for intermediate steps. + * Will be created only when first needed. + * + * @see #getCSFactory() + */ + private volatile CSFactory csFactory; + + /** * The math transform factory. Will be created only when first needed. * * @see #getMathTransformFactory() @@ -101,8 +128,7 @@ public class DefaultCoordinateOperationF * Constructs a factory with no default properties. */ public DefaultCoordinateOperationFactory() { - defaultProperties = Collections.emptyMap(); - pool = new WeakHashSet<IdentifiedObject>(IdentifiedObject.class); + this(null, null); } /** @@ -110,19 +136,36 @@ public class DefaultCoordinateOperationF * {@code DefaultCoordinateOperationFactory} will fallback on the map given to this constructor * for any property not present in the map provided to a {@code createFoo(Map<String,?>, …)} method. * - * @param properties The default properties, or {@code null} if none. - * @param mtFactory The factory to use for creating + * @param properties the default properties, or {@code null} if none. + * @param factory the factory to use for creating * {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform math transforms}, * or {@code null} for the default factory. */ - public DefaultCoordinateOperationFactory(Map<String,?> properties, final MathTransformFactory mtFactory) { + public DefaultCoordinateOperationFactory(Map<String,?> properties, final MathTransformFactory factory) { if (properties == null || properties.isEmpty()) { properties = Collections.emptyMap(); } else { - properties = CollectionsExt.compact(new HashMap<String,Object>(properties)); + String key = null; + Object value = null; + properties = new HashMap<String,Object>(properties); + /* + * Following use of properties is an undocumented feature for now. Current version documents only + * MathTransformFactory because math transforms are intimately related to coordinate operations. + */ + try { + crsFactory = (CRSFactory) (value = properties.remove(key = ReferencingServices.CRS_FACTORY)); + csFactory = (CSFactory) (value = properties.remove(key = ReferencingServices.CS_FACTORY)); + mtFactory = (MathTransformFactory) (value = properties.remove(key = ReferencingServices.MT_FACTORY)); + } catch (ClassCastException e) { + throw new IllegalArgumentException(Errors.getResources(properties) + .getString(Errors.Keys.IllegalPropertyValueClass_2, key, Classes.getClass(value))); + } + properties = CollectionsExt.compact(properties); } defaultProperties = properties; - this.mtFactory = mtFactory; + if (factory != null) { + mtFactory = factory; + } pool = new WeakHashSet<IdentifiedObject>(IdentifiedObject.class); } @@ -148,6 +191,28 @@ public class DefaultCoordinateOperationF } /** + * Returns the factory to use if {@link CoordinateOperationFinder} needs to create CRS for intermediate steps. + */ + final CRSFactory getCRSFactory() { + CRSFactory factory = crsFactory; + if (factory == null) { + crsFactory = factory = DefaultFactories.forBuildin(CRSFactory.class); + } + return factory; + } + + /** + * Returns the factory to use if {@link CoordinateOperationFinder} needs to create CS for intermediate steps. + */ + final CSFactory getCSFactory() { + CSFactory factory = csFactory; + if (factory == null) { + csFactory = factory = DefaultFactories.forBuildin(CSFactory.class); + } + return factory; + } + + /** * Returns the underlying math transform factory. This factory is used for constructing {@link MathTransform} * dependencies for all {@linkplain AbstractCoordinateOperation coordinate operations} instances. * @@ -487,7 +552,7 @@ next: for (int i=components.size(); -- } /* * 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 + * requested type, we will instantiate a 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. * @@ -567,6 +632,9 @@ next: for (int i=components.size(); -- public CoordinateOperation createConcatenatedOperation(final Map<String,?> properties, final CoordinateOperation... operations) throws FactoryException { + if (operations != null && operations.length == 1) { + return operations[0]; + } final CoordinateOperation op; try { op = new DefaultConcatenatedOperation(properties, operations, getMathTransformFactory()); @@ -604,14 +672,16 @@ next: for (int i=components.size(); -- * 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> + * <p>The default implementation is equivalent to the following code + * (omitting the {@code registry} type check and cast for brevity):</p> * * {@preformat java - * return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS); + * CoordinateOperationAuthorityFactory registry = CRS.getAuthorityFactory("EPSG"); // Actually needs cast + * return new CoordinateOperationFinder(registry, this, context).createOperation(sourceCRS, targetCRS); * } * * Subclasses can override this method if they need, for example, to use a custom - * {@link CoordinateOperationInference} implementation. + * {@link CoordinateOperationFinder} implementation. * * @param sourceCRS input coordinate reference system. * @param targetCRS output coordinate reference system. @@ -620,7 +690,7 @@ next: for (int i=components.size(); -- * @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 + * @see CoordinateOperationFinder * * @since 0.7 */ @@ -629,7 +699,9 @@ next: for (int i=components.size(); -- final CoordinateOperationContext context) throws OperationNotFoundException, FactoryException { - return new CoordinateOperationInference(this, context).createOperation(sourceCRS, targetCRS); + final AuthorityFactory registry = USE_EPSG_FACTORY ? CRS.getAuthorityFactory(Constants.EPSG) : null; + return new CoordinateOperationFinder((registry instanceof CoordinateOperationAuthorityFactory) ? + (CoordinateOperationAuthorityFactory) registry : null, this, context).createOperation(sourceCRS, targetCRS); } /** Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultOperationMethod.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -259,7 +259,7 @@ public class DefaultOperationMethod exte formula = new DefaultFormula((CharSequence) value); } else { throw new IllegalArgumentException(Errors.getResources(properties) - .getString(Errors.Keys.IllegalPropertyClass_2, FORMULA_KEY, value.getClass())); + .getString(Errors.Keys.IllegalPropertyValueClass_2, FORMULA_KEY, value.getClass())); } this.parameters = parameters; this.sourceDimensions = sourceDimensions; Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/DefaultPassThroughOperation.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -33,6 +33,8 @@ import org.apache.sis.util.UnsupportedIm import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.resources.Errors; +import org.apache.sis.io.wkt.FormattableObject; +import org.apache.sis.io.wkt.Formatter; import static org.apache.sis.util.Utilities.deepEquals; @@ -249,6 +251,33 @@ public class DefaultPassThroughOperation return super.computeHashCode() + 31 * operation.hashCode(); } + /** + * Formats this coordinate operation in a pseudo-Well Known Text (WKT) format. + * Current format is specific to Apache SIS and may change in any future version + * if a standard format for pass through operations is defined. + * + * @param formatter The formatter to use. + * @return Currently {@code "PassThroughOperation"} (may change in any future version). + * + * @since 0.7 + */ + @Override + protected String formatTo(final Formatter formatter) { + super.formatTo(formatter); + formatter.append(new FormattableObject() { + @Override protected String formatTo(final Formatter formatter) { + for (final int i : getModifiedCoordinates()) { + formatter.append(i); + } + return "ModifiedCoordinates"; + } + }); + formatter.newLine(); + formatter.append(castOrCopy(getOperation())); + formatter.setInvalidWKT(this, null); + return "PassThroughOperation"; + } + Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/InverseOperationMethod.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -19,12 +19,24 @@ package org.apache.sis.referencing.opera import java.util.Map; import java.util.HashMap; import javax.xml.bind.annotation.XmlTransient; -import org.opengis.metadata.Identifier; +import javax.measure.unit.Unit; +import org.opengis.util.InternationalString; +import org.opengis.parameter.ParameterValue; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.parameter.ParameterDescriptor; +import org.opengis.parameter.GeneralParameterValue; +import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.referencing.operation.OperationMethod; +import org.opengis.referencing.operation.SingleOperation; +import org.apache.sis.internal.metadata.ReferencingServices; +import org.apache.sis.internal.referencing.SignReversalComment; import org.apache.sis.internal.referencing.provider.AbstractProvider; import org.apache.sis.metadata.iso.ImmutableIdentifier; import org.apache.sis.util.Deprecable; +// Branch-dependent imports +import org.opengis.metadata.Identifier; + /** * Description of the inverse of another method. This class should be used only when no operation is defined @@ -56,24 +68,92 @@ final class InverseOperationMethod exten } /** - * Returns or create the inverse of the given operation method. + * Returns {@code true} if the given method flags itself as invertible. + */ + private static boolean isInvertible(final OperationMethod method) { + return method instanceof AbstractProvider && ((AbstractProvider) method).isInvertible(); + } + + /** + * Returns or create the inverse of the given operation method. If the same operation method can be used + * for the inverse operation either with the exact same parameter values or with the sign of some values + * reversed, then the given method is returned as-is. Otherwise a synthetic method is created. */ static OperationMethod create(final OperationMethod method) { if (method instanceof InverseOperationMethod) { return ((InverseOperationMethod) method).inverse; } - if (method instanceof AbstractProvider && ((AbstractProvider) method).isInvertible()) { - return method; + if (!isInvertible(method)) { + boolean useSameParameters = false; + for (final GeneralParameterDescriptor descriptor : method.getParameters().descriptors()) { + useSameParameters = (descriptor.getRemarks() instanceof SignReversalComment); + if (!useSameParameters) break; + } + if (!useSameParameters) { + Identifier name = method.getName(); + name = new ImmutableIdentifier(null, null, "Inverse of " + name.getCode()); + final Map<String,Object> properties = new HashMap<String,Object>(6); + properties.put(NAME_KEY, name); + properties.put(FORMULA_KEY, method.getFormula()); + properties.put(REMARKS_KEY, method.getRemarks()); + if (method instanceof Deprecable) { + properties.put(DEPRECATED_KEY, ((Deprecable) method).isDeprecated()); + } + return new InverseOperationMethod(properties, method); + } } - Identifier name = method.getName(); - name = new ImmutableIdentifier(null, name.getCodeSpace(), "Inverse " + name.getCode()); - final Map<String,Object> properties = new HashMap<String,Object>(6); - properties.put(NAME_KEY, name); - properties.put(FORMULA_KEY, method.getFormula()); - properties.put(REMARKS_KEY, method.getRemarks()); - if (method instanceof Deprecable) { - properties.put(DEPRECATED_KEY, ((Deprecable) method).isDeprecated()); + return method; + } + + /** + * If the inverse of the given operation can be represented by inverting the sign of all numerical + * parameter values, copies those parameters in a {@code "parameters"} entry in the given map. + * Otherwise does nothing. + * + * @param source the operation for which to get the inverse parameters. + * @param target where to store the inverse parameters. + */ + static void putParameters(final SingleOperation source, final Map<String,Object> target) { + final boolean isInvertible = isInvertible(source.getMethod()); + final ParameterValueGroup parameters = source.getParameterValues(); + final ParameterValueGroup copy = parameters.getDescriptor().createValue(); + for (final GeneralParameterValue gp : parameters.values()) { + if (gp instanceof ParameterValue<?>) { + final ParameterValue<?> src = (ParameterValue<?>) gp; + final Object value = src.getValue(); + if (value instanceof Number) { + final ParameterDescriptor<?> descriptor = src.getDescriptor(); + final InternationalString remarks = descriptor.getRemarks(); + if (remarks != SignReversalComment.SAME) { + boolean isOpposite = (remarks == SignReversalComment.OPPOSITE); + if (!isOpposite) { + /* + * If the parameter descriptor does not contain an information about whether the + * inverse operation uses values of opposite sign or not, use heuristic rules. + */ + if (!isInvertible) { + return; // Can not create inverse parameter values - abandon. + } + final Comparable<?> minimum = descriptor.getMinimumValue(); + isOpposite = (minimum == null || (minimum instanceof Number && ((Number) minimum).doubleValue() < 0)); + } + if (isOpposite) { + final ParameterValue<?> tgt = copy.parameter(descriptor.getName().getCode()); + final Unit<?> unit = src.getUnit(); + if (unit != null) { + tgt.setValue(-src.doubleValue(), unit); + } else if (value instanceof Integer || value instanceof Short || value instanceof Byte) { + tgt.setValue(-src.intValue()); + } else { + tgt.setValue(-src.doubleValue()); + } + continue; + } + } + } + } + copy.values().add(gp); } - return new InverseOperationMethod(properties, method); + target.put(ReferencingServices.PARAMETERS_KEY, copy); } } Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/AffineTransforms2D.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -178,7 +178,7 @@ public final class AffineTransforms2D ex * * @return The direct transform of the {@code bounds} rectangle, or {@code null} if {@code bounds} was null. * - * @see org.apache.sis.referencing.CRS#transform(MathTransform2D, Rectangle2D, Rectangle2D) + * @see org.apache.sis.geometry.Envelopes#transform(MathTransform2D, Rectangle2D, Rectangle2D) */ public static Rectangle2D transform(final AffineTransform transform, final Rectangle2D bounds, final Rectangle2D dest) Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -29,6 +29,7 @@ import org.apache.sis.util.ComparisonMod import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.util.Numerics; +import org.apache.sis.internal.util.DoubleDouble; import org.apache.sis.internal.metadata.AxisDirections; import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix; @@ -559,8 +560,9 @@ public final class Matrices extends Stat * * <p>The given sub-matrix shall have the following properties:</p> * <ul> - * <li>The last column contains translation terms, except in the last row.</li> - * <li>The last row often (but not necessarily) contains 0 values except in the last column.</li> + * <li>The last row often (but not necessarily) contains 0 values everywhere except in the last column.</li> + * <li>Values in the last column are translation terms, except in the last row.</li> + * <li>All other values are scale or shear terms.</li> * </ul> * * A square matrix complying with the above conditions is often {@linkplain #isAffine(Matrix) affine}, @@ -608,22 +610,34 @@ public final class Matrices extends Stat * @param firstAffectedOrdinate The lowest index of the affected ordinates. * @param subMatrix The matrix to use for affected ordinates. * @param numTrailingOrdinates Number of trailing ordinates to pass through. - * @return A matrix + * @return A matrix for the same transform than the given matrix, + * augmented with leading and trailing pass-through coordinates. * * @see org.apache.sis.referencing.operation.DefaultMathTransformFactory#createPassThroughTransform(int, MathTransform, int) */ public static MatrixSIS createPassThrough(final int firstAffectedOrdinate, final Matrix subMatrix, final int numTrailingOrdinates) { + ArgumentChecks.ensureNonNull ("subMatrix", subMatrix); ArgumentChecks.ensurePositive("firstAffectedOrdinate", firstAffectedOrdinate); ArgumentChecks.ensurePositive("numTrailingOrdinates", numTrailingOrdinates); final int expansion = firstAffectedOrdinate + numTrailingOrdinates; - int sourceDimensions = subMatrix.getNumCol(); + int sourceDimensions = subMatrix.getNumCol(); // Will become the number of dimensions later. int targetDimensions = subMatrix.getNumRow(); + /* + * Get data from the source matrix, together with the error terms if present. + * The 'stride' and 'length' values will be used for computing indices in that array. + * The DoubleDouble temporary object is used only if the array contains error terms. + */ + final int stride = sourceDimensions; + final int length = sourceDimensions * targetDimensions; + final double[] sources = getExtendedElements(subMatrix); + final DoubleDouble transfer = (sources.length > length) ? new DoubleDouble() : null; final MatrixSIS matrix = createZero(targetDimensions-- + expansion, - sourceDimensions-- + expansion); + sourceDimensions-- + expansion, + transfer != null); /* - * Following code process for upper row to lower row. + * Following code processes from upper row to lower row. * First, set the diagonal elements on leading new dimensions. */ for (int j=0; j<firstAffectedOrdinate; j++) { @@ -634,12 +648,14 @@ public final class Matrices extends Stat * which are unconditionally stored in the last column. */ final int lastColumn = sourceDimensions + expansion; - for (int j=0; j<targetDimensions; j++) { - for (int i=0; i<sourceDimensions; i++) { - matrix.setElement(firstAffectedOrdinate + j, firstAffectedOrdinate + i, subMatrix.getElement(j, i)); - } - matrix.setElement(firstAffectedOrdinate + j, lastColumn, subMatrix.getElement(j, sourceDimensions)); - } + matrix.setElements(sources, length, stride, transfer, + 0, 0, // Source (row, colum) + firstAffectedOrdinate, firstAffectedOrdinate, // Target (row, column) + targetDimensions, sourceDimensions); // Number of rows and columns to copy. + matrix.setElements(sources, length, stride, transfer, + 0, sourceDimensions, // Source (row, colum): last column + firstAffectedOrdinate, lastColumn, // Target (row, column): part of last column + targetDimensions, 1); // Copy some rows of only 1 column. /* * Set the pseudo-diagonal elements on the trailing new dimensions. * 'diff' is zero for a square matrix and non-zero for rectangular matrix. @@ -653,14 +669,70 @@ public final class Matrices extends Stat * this row contains only 0 element except for the last one, which is 1. */ final int lastRow = targetDimensions + expansion; - for (int i=0; i<sourceDimensions; i++) { - matrix.setElement(lastRow, i + firstAffectedOrdinate, subMatrix.getElement(targetDimensions, i)); - } - matrix.setElement(lastRow, lastColumn, subMatrix.getElement(targetDimensions, sourceDimensions)); + matrix.setElements(sources, length, stride, transfer, + targetDimensions, 0, // Source (row, colum): last row + lastRow, firstAffectedOrdinate, // Target (row, column): part of last row + 1, sourceDimensions); // Copy some columns of only 1 row. + matrix.setElements(sources, length, stride, transfer, + targetDimensions, sourceDimensions, + lastRow, lastColumn, + 1, 1); return matrix; } /** + * Returns a matrix with the same content than the given matrix but a different size, assuming an affine transform. + * This method can be invoked for adding or removing the <strong>last</strong> dimensions of an affine transform. + * More specifically: + * + * <ul class="verbose"> + * <li>If the given {@code numCol} is <var>n</var> less than the number of columns in the given matrix, + * then the <var>n</var> columns <em>before the last column</em> are removed. + * The last column is left unchanged because it is assumed to contain the translation terms.</li> + * <li>If the given {@code numCol} is <var>n</var> more than the number of columns in the given matrix, + * then <var>n</var> columns are inserted <em>before the last column</em>. + * All values in the new columns will be zero.</li> + * <li>If the given {@code numRow} is <var>n</var> less than the number of rows in the given matrix, + * then the <var>n</var> rows <em>before the last row</em> are removed. + * The last row is left unchanged because it is assumed to contain the usual [0 0 0 … 1] terms.</li> + * <li>If the given {@code numRow} is <var>n</var> more than the number of rows in the given matrix, + * then <var>n</var> rows are inserted <em>before the last row</em>. + * The corresponding offset and scale factors will be 0 and 1 respectively. + * In other words, new dimensions are propagated unchanged.</li> + * </ul> + * + * @param matrix the matrix to resize. This matrix will never be changed. + * @param numRow the new number of rows. This is equal to the desired number of target dimensions plus 1. + * @param numCol the new number of columns. This is equal to the desired number of source dimensions plus 1. + * @return a new matrix of the given size, or the given {@code matrix} if no resizing was needed. + */ + public static Matrix resizeAffine(final Matrix matrix, int numRow, int numCol) { + ArgumentChecks.ensureNonNull ("matrix", matrix); + ArgumentChecks.ensureStrictlyPositive("numRow", numRow); + ArgumentChecks.ensureStrictlyPositive("numCol", numCol); + int srcRow = matrix.getNumRow(); + int srcCol = matrix.getNumCol(); + if (numRow == srcRow && numCol == srcCol) { + return matrix; + } + final int stride = srcCol; + final int length = srcCol * srcRow; + final double[] sources = getExtendedElements(matrix); + final DoubleDouble transfer = (sources.length > length) ? new DoubleDouble() : null; + final MatrixSIS resized = createZero(numRow, numCol, transfer != null); + final int copyRow = Math.min(--numRow, --srcRow); + final int copyCol = Math.min(--numCol, --srcCol); + for (int j=copyRow; j<numRow; j++) { + resized.setElement(j, j, 1); + } + resized.setElements(sources, length, stride, transfer, 0, 0, 0, 0, copyRow, copyCol); // Shear and scale terms. + resized.setElements(sources, length, stride, transfer, 0, srcCol, 0, numCol, copyRow, 1); // Translation column. + resized.setElements(sources, length, stride, transfer, srcRow, 0, numRow, 0, 1, copyCol); // Last row. + resized.setElements(sources, length, stride, transfer, srcRow, srcCol, numRow, numCol, 1, 1); // Last row. + return resized; + } + + /** * Returns {@code true} if the given matrix is likely to use extended precision. * A value of {@code true} is not a guarantee that the matrix uses extended precision, * but a value of {@code false} is a guarantee that it does not. @@ -671,6 +743,17 @@ public final class Matrices extends Stat } /** + * Returns the elements of the given matrix, together with error terms if available. + */ + private static double[] getExtendedElements(final Matrix matrix) { + if (matrix instanceof ExtendedPrecisionMatrix) { + return ((ExtendedPrecisionMatrix) matrix).getExtendedElements(); + } else { + return MatrixSIS.castOrCopy(matrix).getElements(); + } + } + + /** * Creates a new matrix which is a copy of the given matrix. * * <div class="note"><b>Implementation note:</b> Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/MatrixSIS.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -249,6 +249,41 @@ public abstract class MatrixSIS implemen public abstract void setElements(final double[] elements); /** + * Sets elements in a sub-region of this matrix, optionally including error terms. + * + * @param source Row-major values as given by {@link ExtendedPrecisionMatrix#getExtendedElements()}. + * @param length Number of elements ({@code numRow} × {@code numCol}) in the source matrix, not including error terms. + * @param stride Number of columns in the source matrix, used for computing indices in {@code source} array. + * @param srcRow Index of the first row from the {@code source} to copy in {@code this}. + * @param srcCol Index of the first column from the {@code source} to copy in {@code this}. + * @param dstRow Index of the first row in {@code this} where to copy the {@code source} values. + * @param dstCol Index of the first column in {@code this} where to copy the {@code source} values. + * @param numRow Number of rows to copy. + * @param numCol Number of columns to copy. + * @param transfer If both {@code source} and {@code this} use extended precision, + * the temporary object to use for transferring values. Otherwise {@code null}. + */ + final void setElements(final double[] source, final int length, final int stride, final DoubleDouble transfer, + int srcRow, final int srcCol, + int dstRow, final int dstCol, + int numRow, final int numCol) + { + while (--numRow >= 0) { + final int valueOffset = srcRow*stride + srcCol; + for (int i=0; i<numCol; i++) { + if (transfer != null) { + transfer.setFrom(source, valueOffset + i, length); + set(dstRow, dstCol + i, transfer); + } else { + setElement(dstRow, dstCol + i, source[valueOffset + i]); + } + } + srcRow++; + dstRow++; + } + } + + /** * Sets this matrix to the values of another matrix. * The given matrix must have the same size. * Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/package-info.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -56,7 +56,7 @@ * <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. + * may vary depending on the area of interest, like in the transformations from NAD27 to WGS84. * 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>). * @@ -86,7 +86,7 @@ * 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}. + * {@linkplain org.apache.sis.referencing.operation.CoordinateOperationFinder 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. * Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractMathTransform1D.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -150,4 +150,82 @@ public abstract class AbstractMathTransf public MathTransform1D inverse() throws NoninvertibleTransformException { return (MathTransform1D) super.inverse(); } + + /** + * Base class for implementation of inverse math transforms. + * This inner class is the inverse of the enclosing {@link AbstractMathTransform1D}. + * + * <div class="section">Serialization</div> + * Instances of this class are serializable only if the enclosing math transform is also serializable. + * Serialized math transforms are not guaranteed to be compatible with future SIS versions. + * Serialization, if allowed, should be used only for short term storage or RMI between applications + * running the same SIS version. + * + * @author Martin Desruisseaux (Geomatys) + * @since 0.7 + * @version 0.7 + * @module + */ + protected abstract class Inverse extends AbstractMathTransform.Inverse implements MathTransform1D { + /** + * Serial number for inter-operability with different versions. + */ + private static final long serialVersionUID = 2018412413506158560L; + + /** + * Constructs an inverse math transform. + */ + protected Inverse() { + AbstractMathTransform1D.this.super(); + } + + /** + * Returns the enclosing math transform. + */ + @Override + public MathTransform1D inverse() { + return (MathTransform1D) super.inverse(); + } + + /** + * Transforms a single point in the given array and opportunistically computes its derivative if requested. + * The default implementation delegates to {@link #transform(double)} and potentially to {@link #derivative(double)}. + * Subclasses may override this method for performance reason. + * + * @return {@inheritDoc} + * @throws TransformException {@inheritDoc} + */ + @Override + public Matrix transform(final double[] srcPts, final int srcOff, + final double[] dstPts, final int dstOff, + final boolean derivate) throws TransformException + { + final double ordinate = srcPts[srcOff]; + if (dstPts != null) { + dstPts[dstOff] = transform(ordinate); + } + return derivate ? new Matrix1(derivative(ordinate)) : null; + } + + /** + * Gets the derivative of this transform at a point. The default implementation ensures that + * {@code point} is one-dimensional, then delegates to {@link #derivative(double)}. + * + * @param point The coordinate point where to evaluate the derivative, or {@code null}. + * @return The derivative at the specified point (never {@code null}). + * @throws MismatchedDimensionException if {@code point} does not have the expected dimension. + * @throws TransformException if the derivative can not be evaluated at the specified point. + */ + @Override + public Matrix derivative(final DirectPosition point) throws TransformException { + final double ordinate; + if (point == null) { + ordinate = Double.NaN; + } else { + ensureDimensionMatches("point", 1, point); + ordinate = point.getOrdinate(0); + } + return new Matrix1(derivative(ordinate)); + } + } } Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -61,6 +61,7 @@ import org.apache.sis.internal.referenci import org.apache.sis.internal.referencing.j2d.ParameterizedAffine; import org.apache.sis.internal.referencing.provider.AbstractProvider; import org.apache.sis.internal.referencing.provider.VerticalOffset; +import org.apache.sis.internal.referencing.provider.Providers; import org.apache.sis.internal.system.Loggers; import org.apache.sis.metadata.iso.citation.Citations; import org.apache.sis.parameter.DefaultParameterValueGroup; @@ -270,7 +271,7 @@ public class DefaultMathTransformFactory * * Wrapping the ServiceLoader in a LazySet avoid this issue. */ - this(new LazySet<OperationMethod>(ServiceLoader.load(OperationMethod.class))); + this(new Providers()); } /** @@ -387,7 +388,10 @@ public class DefaultMathTransformFactory ArgumentChecks.ensureNonEmpty("identifier", identifier); OperationMethod method = methodsByName.get(identifier); if (method == null) { - method = ReferencingServices.getInstance().getOperationMethod(methods, identifier); + final ReferencingServices services = ReferencingServices.getInstance(); + synchronized (methods) { + method = services.getOperationMethod(methods, identifier); + } if (method == null) { throw new NoSuchIdentifierException(Errors.format(Errors.Keys.NoSuchOperationMethod_1, identifier), identifier); } @@ -845,25 +849,60 @@ public class DefaultMathTransformFactory * if available. This method writes semi-major and semi-minor parameter values only if they do not * already exists in the given parameters. * + * <p>The given method and parameters are stored in the {@link #provider} and {@link #parameters} + * fields respectively. The actual stored values may differ from the values given to this method.</p> + * * @param method Description of the transform to be created, or {@code null} if unknown. * @return The exception if the operation failed, or {@code null} if none. This exception is not thrown now * because the caller may succeed in creating the transform anyway, or otherwise may produce a more * informative exception. * @throws IllegalArgumentException if the operation fails because a parameter has a unrecognized name or an * illegal value. + * + * @see #getCompletedParameters() */ - final RuntimeException setEllipsoids(final OperationMethod method) throws IllegalArgumentException { - ensureCompatibleParameters(false); + @SuppressWarnings("null") + final RuntimeException completeParameters(OperationMethod method, final ParameterValueGroup userParams) + throws IllegalArgumentException + { + provider = method; + parameters = userParams; + /* + * Get the operation method for the appropriate number of dimensions. For example the default Molodensky + * operation expects two-dimensional source and target CRS. If a given CRS is three-dimensional, we need + * a provider variant which will not concatenate a "geographic 3D to 2D" operation before the Molodensky + * one. It is worth to perform this check only if the provider is a subclass of DefaultOperationMethod, + * since it needs to override the 'redimension(int, int)' method. + */ + if (method instanceof DefaultOperationMethod && method.getClass() != DefaultOperationMethod.class) { + final Integer sourceDim = (sourceCS != null) ? sourceCS.getDimension() : method.getSourceDimensions(); + final Integer targetDim = (targetCS != null) ? targetCS.getDimension() : method.getTargetDimensions(); + if (sourceDim != null && targetDim != null) { + method = ((DefaultOperationMethod) method).redimension(sourceDim, targetDim); + if (method instanceof MathTransformProvider) { + provider = method; + } + } + } + ensureCompatibleParameters(false); // Invoke only after we set 'provider' to its final instance. + /* + * Get a mask telling us if we need to set parameters for the source and/or target ellipsoid. + * This information should preferably be given by the provider. But if the given provider is + * not a SIS implementation, use as a fallback whether ellipsoids are provided. This fallback + * may be less reliable. + */ int n; - if (method instanceof AbstractProvider) { - n = ((AbstractProvider) method).getEllipsoidsMask(); + if (provider instanceof AbstractProvider) { + n = ((AbstractProvider) provider).getEllipsoidsMask(); } else { - // Fallback used only when the information is not available in - // a more reliable way from AbstractProvider.getEllipsoidsMask(). n = 0; if (sourceEllipsoid != null) n = 1; if (targetEllipsoid != null) n |= 2; } + /* + * Set the ellipsoid axis-length parameter values. Those parameters may appear in the source + * ellipsoid, in the target ellipsoid or in both ellipsoids. + */ switch (n) { case 0: return null; case 1: return setEllipsoid(getSourceEllipsoid(), Constants.SEMI_MAJOR, Constants.SEMI_MINOR, true, null); @@ -872,7 +911,7 @@ public class DefaultMathTransformFactory RuntimeException failure = null; if (sourceCS != null) try { ensureCompatibleParameters(true); - final ParameterValue<?> p = parameters.parameter("dim"); + final ParameterValue<?> p = parameters.parameter("dim"); // Really 'parameters', not 'userParams'. if (p.getValue() == null) { p.setValue(sourceCS.getDimension()); } @@ -987,10 +1026,9 @@ public class DefaultMathTransformFactory * since the standard place where to provide this information is in the ellipsoid object. */ if (context != null) { - context.provider = method; - context.parameters = parameters; - failure = context.setEllipsoids(method); + failure = context.completeParameters(method, parameters); parameters = context.parameters; + method = context.provider; } transform = ((MathTransformProvider) method).createMathTransform(this, parameters); } catch (RuntimeException exception) { // (IllegalArgumentException | IllegalStateException) on the JDK7 branch. @@ -1389,9 +1427,9 @@ public class DefaultMathTransformFactory public void reload() { synchronized (methods) { methodsByName.clear(); - Iterable<? extends OperationMethod> m = methods; + final Iterable<? extends OperationMethod> m = methods; if (m instanceof LazySet<?>) { // Workaround for JDK bug. See DefaultMathTransformFactory() constructor. - m = ((LazySet<? extends OperationMethod>) m).reload(); + ((LazySet<? extends OperationMethod>) m).reload(); } if (m instanceof ServiceLoader<?>) { ((ServiceLoader<?>) m).reload(); Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -47,7 +47,7 @@ import static java.lang.Double.doubleToR * * @author Martin Desruisseaux (IRD, Geomatys) * @since 0.5 - * @version 0.6 + * @version 0.7 * @module * * @see LogarithmicTransform1D @@ -60,6 +60,11 @@ class LinearTransform1D extends Abstract private static final long serialVersionUID = -7595037195668813000L; /** + * A transform that just reverse the sign of input values. + */ + static final LinearTransform1D NEGATE = new LinearTransform1D(-1, 0); + + /** * The value which is multiplied to input values. */ final double scale; @@ -99,8 +104,9 @@ class LinearTransform1D extends Abstract * @see MathTransforms#linear(double, double) */ public static LinearTransform1D create(final double scale, final double offset) { - if (offset == 0 && scale == 1) { - return IdentityTransform1D.INSTANCE; + if (offset == 0) { + if (scale == +1) return IdentityTransform1D.INSTANCE; + if (scale == -1) return NEGATE; } if (scale == 0) { if (offset == 0) return ConstantTransform1D.ZERO; @@ -111,6 +117,19 @@ class LinearTransform1D extends Abstract } /** + * Creates a constant function having value <var>y</var>, and for which the inverse is <var>x</var>. + * + * @since 0.7 + */ + static LinearTransform1D constant(final double x, final double y) { + final LinearTransform1D tr = create(0, y); + if (!Double.isNaN(x)) { + tr.inverse = create(0, x); + } + return tr; + } + + /** * Returns the parameter descriptors for this math transform. */ @Override Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MathTransforms.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -147,6 +147,33 @@ public final class MathTransforms extend } /** + * Creates a transform for the <i>y=f(x)</i> function where <var>y</var> are computed by a linear interpolation. + * Both {@code preimage} (the <var>x</var>) and {@code values} (the <var>y</var>) arguments can be null: + * + * <ul> + * <li>If both {@code preimage} and {@code values} arrays are non-null, then the must have the same length.</li> + * <li>If both {@code preimage} and {@code values} arrays are null, then this method returns the identity transform.</li> + * <li>If only {@code preimage} is null, then the <var>x</var> values are taken as {0, 1, 2, …, {@code values.length} - 1}.</li> + * <li>If only {@code values} is null, then the <var>y</var> values are taken as {0, 1, 2, …, {@code preimage.length} - 1}.</li> + * </ul> + * + * All {@code preimage} elements shall be real numbers (not NaN) sorted in increasing or decreasing order. + * Elements in the {@code values} array do not need to be ordered, but the returned transform will be invertible + * only if all values are real numbers sorted in increasing or decreasing order. + * Furthermore the returned transform is affine (i.e. implement the {@link LinearTransform} interface) + * if the interval between each {@code preimage} and {@code values} element is constant. + * + * @param preimage the input values (<var>x</var>) in the function domain, or {@code null}. + * @param values the output values (<var>y</var>) in the function range, or {@code null}. + * @return the <i>y=f(x)</i> function. + * + * @since 0.7 + */ + public static MathTransform1D interpolate(final double[] preimage, final double[] values) { + return LinearInterpolator1D.create(preimage, values); + } + + /** * Puts together a list of independent math transforms, each of them operating on a subset of ordinate values. * This method is often used for defining 4-dimensional (<var>x</var>,<var>y</var>,<var>z</var>,<var>t</var>) * transform as an aggregation of 3 simpler transforms operating on (<var>x</var>,<var>y</var>), (<var>z</var>) Modified: sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java URL: http://svn.apache.org/viewvc/sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java?rev=1740152&r1=1740151&r2=1740152&view=diff ============================================================================== --- sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] (original) +++ sis/branches/JDK6/core/sis-referencing/src/main/java/org/apache/sis/referencing/package-info.java [UTF-8] Wed Apr 20 14:53:31 2016 @@ -26,19 +26,41 @@ * postal address. This package is the root for both kinds, with an emphasis on the one for coordinates.</p> * * <div class="section">Fetching geodetic object instances</div> - * Geodetic objects can be instantiated either directly by specifying all information to a factory method - * or constructor, or indirectly by specifying the identifier of an entry in a database. In particular, - * the <a href="http://www.epsg.org">EPSG</a> database provides definitions for many geodetic objects, + * Geodetic objects can be instantiated either + * {@linkplain org.apache.sis.referencing.factory.GeodeticObjectFactory directly by specifying all information to a factory method or constructor}, or + * {@linkplain org.apache.sis.referencing.factory.GeodeticAuthorityFactory indirectly by specifying the identifier of an entry in a database}. + * In particular, the <a href="http://www.epsg.org">EPSG</a> database provides definitions for many geodetic objects, * and Apache SIS provides convenience shortcuts for some of them in the - * {@link org.apache.sis.referencing.CommonCRS} enumerations. + * {@link org.apache.sis.referencing.CommonCRS} enumerations. Other convenience methods are + * {@link org.apache.sis.referencing.CRS#forCode(String)}, + * {@link org.apache.sis.referencing.CRS#fromWKT(String)} and + * {@link org.apache.sis.referencing.CRS#fromXML(String)} + * + * <div class="section">Usage example</div> + * The following example projects a (<var>latitude</var>, <var>longitude</var>) coordinate to + * a <cite>Universal Transverse Mercator</cite> projection in the zone of the coordinate: + * + * {@preformat java + * GeographicCRS source = CommonCRS.WGS84.geographic(); + * ProjectedCRS target = CommonCRS.WGS84.UTM(20, 30); // 20°N 30°E (watch out axis order!) + * CoordinateOperation operation = CRS.findOperation(source, target, null); + * if (CRS.getLinearAccuracy(operation) > 100) { + * // If the accuracy is coarser than 100 metres (or any other threshold at application choice) + * // maybe the operation is not suitable. Decide here what to do (throw an exception, etc). + * } + * MathTransform mt = operation.getMathTransform(); + * DirectPosition position = new DirectPosition2D(20, 30); // 20°N 30°E (watch out axis order!) + * position = mt.transform(position, position); + * System.out.println(position); + * } * * <div class="section">The EPSG database</div> * The EPSG geodetic parameter dataset is a structured database required to: * * <ul> - * <li>define {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem Coordinate Reference Systems} + * <li>define {@linkplain org.apache.sis.referencing.crs.AbstractCRS Coordinate Reference Systems} * (CRS) such that coordinates describe positions unambiguously;</li> - * <li>define {@linkplain org.opengis.referencing.operation.CoordinateOperation Coordinate Operations} + * <li>define {@linkplain org.apache.sis.referencing.operation.AbstractCoordinateOperation Coordinate Operations} * that allow coordinates to be changed from one CRS to another CRS.</li> * </ul> *
