sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1792744 - in /sis/branches/JDK8/core: sis-referencing/src/main/java/org/apache/sis/internal/referencing/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/ sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql...
Date Wed, 26 Apr 2017 13:33:18 GMT
Author: desruisseaux
Date: Wed Apr 26 13:33:18 2017
New Revision: 1792744

URL: http://svn.apache.org/viewvc?rev=1792744&view=rev
Log:
Avoid loading unneeded datum shift grid files (SIS-327).

Added:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
  (with props)
Modified:
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
    sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java

Added: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java?rev=1792744&view=auto
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
(added)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.referencing;
+
+import java.util.Map;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
+import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
+
+
+/**
+ * Place-holder for a {@code CoordinateOperation} whose creation is deferred. Used for iterating
on instances returned by
+ * {@link org.apache.sis.referencing.factory.sql.EPSGDataAccess#createFromCoordinateReferenceSystemCodes(String,
String)}
+ * where many operations may exist but only one (typically) will be retained.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 0.8
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-327">SIS-327</a>
+ *
+ * @since 0.8
+ * @module
+ */
+@SuppressWarnings("serial")
+public final class DeferredCoordinateOperation extends AbstractCoordinateOperation {
+    /**
+     * The factory to use for creating the actual coordinate operation.
+     */
+    private final CoordinateOperationAuthorityFactory factory;
+
+    /**
+     * Creates a deferred coordinate operation.
+     *
+     * @param  properties  the properties to be given to the identified object.
+     * @param  sourceCRS   the source CRS, or {@code null} if unspecified.
+     * @param  targetCRS   the target CRS, or {@code null} if unspecified.
+     * @param  factory     the factory to use for creating the actual coordinate operation.
+     */
+    public DeferredCoordinateOperation(final Map<String,?>             properties,
+                                       final CoordinateReferenceSystem sourceCRS,
+                                       final CoordinateReferenceSystem targetCRS,
+                                       final CoordinateOperationAuthorityFactory factory)
+    {
+        super(properties, sourceCRS, targetCRS, null, null);
+        this.factory = factory;
+    }
+
+    /**
+     * Creates the actual coordinate operation.
+     *
+     * @return the coordinate operation.
+     * @throws FactoryException if the factory failed to create the coordinate operation.
+     */
+    public CoordinateOperation create() throws FactoryException {
+        return factory.createCoordinateOperation(getIdentifiers().iterator().next().getCode());
+    }
+}

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/DeferredCoordinateOperation.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -92,7 +92,7 @@ import org.apache.sis.util.resources.Mes
  * Subclasses should select the interfaces that they choose to implement.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 0.7
+ * @version 0.8
  *
  * @param <DAO>  the type of factory used as Data Access Object (DAO).
  *
@@ -640,6 +640,8 @@ public abstract class ConcurrentAuthorit
      *
      * @param  factory  the Data Access Object which is about to be closed.
      * @return {@code true} if the given Data Access Object can be closed.
+     *
+     * @see #close()
      */
     protected boolean canClose(DAO factory) {
         return true;
@@ -1632,9 +1634,9 @@ public abstract class ConcurrentAuthorit
      * creation is delegated to the {@linkplain #getDataAccess() Data Access Object}.
      * The result is then stored in the cache and returned.
      *
-     * @param  <T>   The type of the object to be returned.
-     * @param  proxy The proxy to use for creating the object.
-     * @param  code  The code of the object to create.
+     * @param  <T>    the type of the object to be returned.
+     * @param  proxy  the proxy to use for creating the object.
+     * @param  code   the code of the object to create.
      * @return the object extracted from the cache or created.
      * @throws FactoryException if an error occurred while creating the object.
      */
@@ -1655,7 +1657,9 @@ public abstract class ConcurrentAuthorit
                     } finally {
                         release(null, type, code);
                     }
-                    value = result;                                     // For the finally
block below.
+                    if (isCacheable(code, result)) {
+                        value = result;                                 // For the finally
block below.
+                    }
                     return result;
                 }
             } finally {
@@ -1935,11 +1939,36 @@ public abstract class ConcurrentAuthorit
     }
 
     /**
+     * Returns whether the given object can be cached. This method is invoked after the
+     * {@linkplain #newDataAccess() Data Access Object} created a new object not previously
in the cache.
+     * If this {@code isCacheable(…)} method returns {@code true}, then the newly created
object will be cached so
+     * that next calls to the same {@code createFoo(String)} method with the same code may
return the same object.
+     * If this method returns {@code false}, then the newly created object will not be cached
and next call to
+     * the {@code createFoo(String)} method with the same code will return a new object.
+     *
+     * <p>The default implementation always returns {@code true}.
+     * Subclasses can override this method for filtering the objects to store in the cache.</p>
+     *
+     * @param  code    the authority code specified by the caller for creating an object.
+     * @param  object  the object created for the given authority code.
+     * @return whether the given object should be cached.
+     *
+     * @see #printCacheContent(PrintWriter)
+     *
+     * @since 0.8
+     */
+    protected boolean isCacheable(String code, Object object) {
+        return true;
+    }
+
+    /**
      * Prints the cache content to the given writer.
      * Keys are sorted by numerical order if possible, or alphabetical order otherwise.
      * This method is used for debugging purpose only.
      *
      * @param  out  the output printer, or {@code null} for the {@linkplain System#out standard
output stream}.
+     *
+     * @see #isCacheable(String, Object)
      */
     @Debug
     public void printCacheContent(final PrintWriter out) {
@@ -2057,6 +2086,8 @@ public abstract class ConcurrentAuthorit
      * depending which event happen first.</p>
      *
      * @throws FactoryException if an error occurred while closing the Data Access Objects.
+     *
+     * @see #canClose(GeodeticAuthorityFactory)
      */
     @Override
     public void close() throws FactoryException {
@@ -2081,6 +2112,8 @@ public abstract class ConcurrentAuthorit
      * The string returned by this method may change in any future SIS version.
      *
      * @return a string representation for debugging purpose.
+     *
+     * @see #printCacheContent(PrintWriter)
      */
     @Debug
     @Override

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/IdentifiedObjectSet.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -315,12 +315,10 @@ public class IdentifiedObjectSet<T exten
                      * object but we do not put it in this IdentifiedObjectSet. This behavior
is as if this method
                      * has been invoked before the concurrent removal happened.
                      */
-                    if (objects.containsKey(code)) {
+                    if (objects.containsKey(code)) {        // Needed because code may be
associated to null value.
                         final T c = objects.putIfAbsent(code, object);
                         if (c != null) {
                             object = c;                     // The object has been created
concurrently.
-                        } else {
-                            codes = null;
                         }
                     }
                 } else if (objects.remove(code, null)) {    // Do not remove if a concurrent
thread succeeded.

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -71,6 +71,7 @@ import org.apache.sis.internal.metadata.
 import org.apache.sis.internal.metadata.TransformationAccuracy;
 import org.apache.sis.internal.metadata.WKTKeywords;
 import org.apache.sis.internal.metadata.sql.SQLUtilities;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.DeprecatedCode;
 import org.apache.sis.internal.referencing.EPSGParameterDomain;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
@@ -2822,12 +2823,10 @@ next:               while (r.next()) {
                      * (it was checked by getInteger(code, result, …) above in this method)
but optional
                      * for concatenated operations. Fetching parameter values is part of
this block.
                      */
-                    OperationMethod method;
-                    ParameterValueGroup parameters;
-                    if (methodCode == null) {
-                        method      = null;
-                        parameters  = null;
-                    } else {
+                    final boolean       isDeferred = Semaphores.query(Semaphores.METADATA_ONLY);
+                    ParameterValueGroup parameters = null;
+                    OperationMethod     method     = null;
+                    if (methodCode != null && !isDeferred) {
                         method = owner.createOperationMethod(methodCode.toString());
                         if (isDimensionKnown) {
                             method = DefaultOperationMethod.redimension(method, sourceDimensions,
targetDimensions);
@@ -2858,7 +2857,9 @@ next:               while (r.next()) {
                      */
                     final CoordinateOperation operation;
                     final CoordinateOperationFactory copFactory = owner.copFactory;
-                    if (isConversion && (sourceCRS == null || targetCRS == null))
{
+                    if (isDeferred) {
+                        operation = new DeferredCoordinateOperation(opProperties, sourceCRS,
targetCRS, owner);
+                    } else if (isConversion && (sourceCRS == null || targetCRS ==
null)) {
                         operation = copFactory.createDefiningConversion(opProperties, method,
parameters);
                     } else if (isConcatenated) {
                         /*
@@ -3031,7 +3032,9 @@ next:               while (r.next()) {
          * Before to return the set, tests the creation of 1 object in order to report early
(i.e. now)
          * any problems with SQL statements. Remaining operations will be created only when
first needed.
          */
-        set.resolve(1);
+        if (!Semaphores.query(Semaphores.METADATA_ONLY)) {
+            set.resolve(1);
+        }
         return set;
     }
 

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/factory/sql/EPSGFactory.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -38,6 +38,7 @@ import org.opengis.referencing.operation
 import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
 import org.opengis.referencing.operation.MathTransformFactory;
 import org.apache.sis.internal.metadata.sql.Initializer;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.system.DefaultFactories;
 import org.apache.sis.internal.util.Constants;
@@ -507,4 +508,17 @@ public class EPSGFactory extends Concurr
     protected boolean canClose(final EPSGDataAccess factory) {
         return factory.canClose();
     }
+
+    /**
+     * Returns whether the given object can be cached.
+     * This method is invoked after {@link EPSGDataAccess} created a new object not previously
in the cache.
+     *
+     * @return whether the given object should be cached.
+     *
+     * @since 0.8
+     */
+    @Override
+    protected boolean isCacheable(String code, Object object) {
+        return !(object instanceof DeferredCoordinateOperation);
+    }
 }

Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -18,10 +18,10 @@ package org.apache.sis.referencing.opera
 
 import java.util.Map;
 import java.util.HashMap;
-import java.util.Set;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Iterator;
 import java.util.Objects;
 import java.util.logging.Level;
@@ -59,11 +59,13 @@ import org.apache.sis.referencing.factor
 import org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException;
 import org.apache.sis.metadata.iso.extent.Extents;
 import org.apache.sis.internal.referencing.CoordinateOperations;
+import org.apache.sis.internal.referencing.DeferredCoordinateOperation;
 import org.apache.sis.internal.referencing.PositionalAccuracyConstant;
 import org.apache.sis.internal.referencing.ReferencingUtilities;
 import org.apache.sis.internal.referencing.provider.Affine;
 import org.apache.sis.internal.referencing.Resources;
 import org.apache.sis.internal.metadata.ReferencingServices;
+import org.apache.sis.internal.system.Semaphores;
 import org.apache.sis.internal.system.Loggers;
 import org.apache.sis.internal.util.Citations;
 import org.apache.sis.util.ArgumentChecks;
@@ -385,20 +387,25 @@ class CoordinateOperationRegistry {
             return null;
         }
         final boolean inverse;
-        Set<CoordinateOperation> operations;
+        Collection<CoordinateOperation> operations;
+        Semaphores.queryAndSet(Semaphores.METADATA_ONLY);           // See comment for the
same call inside the loop.
         try {
-            operations = registry.createFromCoordinateReferenceSystemCodes(sourceID, targetID);
-            inverse = Containers.isNullOrEmpty(operations);
-            if (inverse) {
-                /*
-                 * No operation from 'source' to 'target' available. But maybe there is an
inverse operation.
-                 * This is typically the case when the user wants to convert from a projected
to a geographic CRS.
-                 * The EPSG database usually contains transformation paths for geographic
to projected CRS only.
-                 */
-                operations = registry.createFromCoordinateReferenceSystemCodes(targetID,
sourceID);
-                if (Containers.isNullOrEmpty(operations)) {
-                    return null;
+            try {
+                operations = registry.createFromCoordinateReferenceSystemCodes(sourceID,
targetID);
+                inverse = Containers.isNullOrEmpty(operations);
+                if (inverse) {
+                    /*
+                     * No operation from 'source' to 'target' available. But maybe there
is an inverse operation.
+                     * This is typically the case when the user wants to convert from a projected
to a geographic CRS.
+                     * The EPSG database usually contains transformation paths for geographic
to projected CRS only.
+                     */
+                    operations = registry.createFromCoordinateReferenceSystemCodes(targetID,
sourceID);
+                    if (Containers.isNullOrEmpty(operations)) {
+                        return null;
+                    }
                 }
+            } finally {
+                Semaphores.clear(Semaphores.METADATA_ONLY);
             }
         } catch (NoSuchAuthorityCodeException | MissingFactoryResourceException exception)
{
             /*
@@ -410,72 +417,56 @@ class CoordinateOperationRegistry {
             return null;
         }
         /*
-         * We will loop over all coordinate operations and select the one having the largest
intersection
-         * with the area of interest. Note that if the user did not specified an area of
interest himself,
-         * then we need to get one from the CRS. This is necessary for preventing the transformation
from
-         * NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later has
a larger area).
+         * This outer loop is executed exactly once in most case. It may be executed more
than once if an
+         * ignoreable error occurred while creating the CoordinateOperation, in which case
we will fallback
+         * on the next best choice until we succeed.
          */
-        double largestArea = 0;
-        double finestAccuracy = Double.POSITIVE_INFINITY;
-        CoordinateOperation bestChoice = null;
-        boolean stopAtFirstDeprecated = false;
-        for (final Iterator<CoordinateOperation> it=operations.iterator(); it.hasNext();)
{
-            CoordinateOperation candidate;
-            try {
-                candidate = it.next();
-            } catch (BackingStoreException exception) {
-                FactoryException cause = exception.unwrapOrRethrow(FactoryException.class);
-                if (cause instanceof MissingFactoryResourceException) {
-                    log(cause);
-                    continue;
-                }
-                throw cause;
-            }
-            if (candidate != null) {
+        CoordinateOperation bestChoice;
+        while (true) {
+            /*
+             * We will loop over all coordinate operations and select the one having the
largest intersection
+             * with the area of interest. Note that if the user did not specified an area
of interest himself,
+             * then we need to get one from the CRS. This is necessary for preventing the
transformation from
+             * NAD27 to NAD83 in Idaho to select the transform for Alaska (since the later
has a larger area).
+             */
+            bestChoice = null;
+            double largestArea = 0;
+            double finestAccuracy = Double.POSITIVE_INFINITY;
+            boolean stopAtFirstDeprecated = false;
+            for (final Iterator<CoordinateOperation> it=operations.iterator();;) {
+                CoordinateOperation candidate;
                 /*
-                 * If we found at least one non-deprecated operation, we will stop the search
at
-                 * the first deprecated one (assuming that deprecated operations are sorted
last).
+                 * Some pair of CRS have a lot of coordinate operations backed by datum shift
grids.
+                 * We do not want to load all of them until we found the right coordinate
operation.
+                 * The non-public Semaphores.METADATA_ONLY mechanism instructs EPSGDataAccess
to
+                 * instantiate DeferredCoordinateOperation instead of full coordinate operations.
                  */
-                final boolean isDeprecated = (candidate instanceof Deprecable) &&
((Deprecable) candidate).isDeprecated();
-                if (isDeprecated && stopAtFirstDeprecated) {
-                    break;
+                Semaphores.queryAndSet(Semaphores.METADATA_ONLY);
+                try {
+                    try {
+                        if (!it.hasNext()) break;
+                        candidate = it.next();
+                    } finally {
+                        Semaphores.clear(Semaphores.METADATA_ONLY);
+                    }
+                } catch (BackingStoreException exception) {
+                    throw exception.unwrapOrRethrow(FactoryException.class);
                 }
-                final double area = Extents.area(Extents.intersection(
-                        Extents.getGeographicBoundingBox(areaOfInterest),
-                        Extents.getGeographicBoundingBox(candidate.getDomainOfValidity())));
-                if (bestChoice == null || area >= largestArea) {
-                    final double accuracy = CRS.getLinearAccuracy(candidate);
-                    if (bestChoice == null || area != largestArea || accuracy < finestAccuracy)
{
-                        /*
-                         * Inverse the operation only after we verified the metadata (domain
of validity,
-                         * accuracy, etc.) since the creation of inverse operation is not
guaranteed to
-                         * preserve all metadata.
-                         */
-                        if (inverse) try {
-                            candidate = inverse(candidate);
-                        } catch (NoninvertibleTransformException exception) {
-                            // It may be a normal failure - the operation is not required
to be invertible.
-                            Logging.recoverableException(Logging.getLogger(Loggers.COORDINATE_OPERATION),
-                                    CoordinateOperationRegistry.class, "createOperation",
exception);
-                            continue;
-                        } catch (MissingFactoryResourceException e) {
-                            log(e);
-                            continue;
-                        }
-                        /*
-                         * It is possible that the CRS given to this method were not quite
right.  For example the user
-                         * may have created his CRS from a WKT using a different axis order
than the order specified by
-                         * the authority and still (wrongly) call those CRS "EPSG:xxxx".
 So we check if the source and
-                         * target CRS for the operation we just created are equivalent to
the CRS specified by the user.
-                         *
-                         * NOTE: FactoryException may be thrown if we failed to create a
transform from the user-provided
-                         * CRS to the authority-provided CRS. That transform should have
been only an identity transform,
-                         * or a simple affine transform if the user specified wrong CRS as
explained in above paragraph.
-                         * If we failed here, we are likely to fail for all other transforms.
So we are better to let
-                         * the FactoryException propagate.
-                         */
-                        candidate = complete(candidate, sourceCRS, targetCRS);
-                        if (filter == null || filter.test(candidate)) {
+                if (candidate != null) {
+                    /*
+                     * If we found at least one non-deprecated operation, we will stop the
search at
+                     * the first deprecated one (assuming that deprecated operations are
sorted last).
+                     */
+                    final boolean isDeprecated = (candidate instanceof Deprecable) &&
((Deprecable) candidate).isDeprecated();
+                    if (isDeprecated && stopAtFirstDeprecated) {
+                        break;
+                    }
+                    final double area = Extents.area(Extents.intersection(
+                            Extents.getGeographicBoundingBox(areaOfInterest),
+                            Extents.getGeographicBoundingBox(candidate.getDomainOfValidity())));
+                    if (bestChoice == null || area >= largestArea) {
+                        final double accuracy = CRS.getLinearAccuracy(candidate);
+                        if (bestChoice == null || area != largestArea || accuracy < finestAccuracy)
{
                             bestChoice = candidate;
                             if (!Double.isNaN(area)) {
                                 largestArea = area;
@@ -486,6 +477,59 @@ class CoordinateOperationRegistry {
                     }
                 }
             }
+            /*
+             * At this point we filtered a CoordinateOperation by looking only at its metadata.
+             * Code following this point will need the full coordinate operation, including
its
+             * MathTransform. So if we got a deferred operation, we need to resolve it now.
+             * Conversely, we should not use metadata below this point because the call to
+             * inverse(CoordinateOperation) is not guaranteed to preserve all metadata.
+             */
+            if (bestChoice == null) break;
+            final CoordinateOperation deferred = bestChoice;
+            try {
+                if (bestChoice instanceof DeferredCoordinateOperation) {
+                    bestChoice = ((DeferredCoordinateOperation) bestChoice).create();
+                }
+                if (inverse) {
+                    bestChoice = inverse(bestChoice);
+                }
+            } catch (NoninvertibleTransformException | MissingFactoryResourceException e)
{
+                /*
+                 * If we failed to get the real CoordinateOperation instance, remove it from
the collection
+                 * and try again in order to get the next best choice. The Apache SIS implementation
allows
+                 * to remove directly from the Set<CoordinateOperation>, but that removal
may fail with non-SIS
+                 * implementations, in which case we copy the CoordinateOperation in a temporary
list before
+                 * removal. We do not perform that copy unconditionally in order to avoid
lazy initialization
+                 * of unneeded CoordinateOperations like the deprecated ones.
+                 */
+                boolean removed;
+                try {
+                    removed = operations.remove(deferred);
+                } catch (UnsupportedOperationException ignored) {
+                    operations = new ArrayList<>(operations);
+                    removed = operations.remove(deferred);
+                }
+                if (removed) {
+                    log(e);
+                    continue;                                   // Try again with the next
best case.
+                }
+                // Should never happen, but if happen anyway we should fail for avoiding
never-ending loop.
+                throw (e instanceof FactoryException) ? (FactoryException) e : new FactoryException(e);
+            }
+            /*
+             * It is possible that the CRS given to this method were not quite right.  For
example the user
+             * may have created his CRS from a WKT using a different axis order than the
order specified by
+             * the authority and still (wrongly) call those CRS "EPSG:xxxx".  So we check
if the source and
+             * target CRS for the operation we just created are equivalent to the CRS specified
by the user.
+             *
+             * NOTE: FactoryException may be thrown if we fail to create a transform from
the user-provided
+             * CRS to the authority-provided CRS. That transform should have been only an
identity transform,
+             * or a simple affine transform if the user specified wrong CRS as explained
in above paragraph.
+             * If we fail here, we are likely to fail for all other transforms. So we are
better to let the
+             * FactoryException propagate.
+             */
+            bestChoice = complete(bestChoice, sourceCRS, targetCRS);
+            if (filter == null || filter.test(bestChoice)) break;
         }
         return bestChoice;
     }
@@ -1079,9 +1123,18 @@ class CoordinateOperationRegistry {
      *
      * @param exception  the exception which occurred.
      */
-    private static void log(final FactoryException exception) {
+    private static void log(final Exception exception) {
         final LogRecord record = new LogRecord(Level.WARNING, exception.getLocalizedMessage());
         record.setLoggerName(Loggers.COORDINATE_OPERATION);
+        /*
+         * We usually do not log the stack trace since this method should be invoked only
for exceptions
+         * like NoSuchAuthorityCodeException or MissingFactoryResourceException, for which
the message
+         * is descriptive enough. But we make a special case for NoninvertibleTransformException
since
+         * its cause may have deeper root.
+         */
+        if (exception instanceof NoninvertibleTransformException) {
+            record.setThrown(exception);
+        }
         Logging.log(CoordinateOperationFinder.class, "createOperation", record);
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java?rev=1792744&r1=1792743&r2=1792744&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/internal/system/Semaphores.java
[UTF-8] Wed Apr 26 13:33:18 2017
@@ -21,37 +21,46 @@ import org.apache.sis.util.Workaround;
 
 /**
  * Thread-local booleans that need to be shared across different packages. Each thread has
its own set of booleans.
- * The {@link #clear(byte)} method <strong>must</strong> be invoked after the
{@link #queryAndSet(byte)} method in
+ * The {@link #clear(int)} method <strong>must</strong> be invoked after the
{@link #queryAndSet(int)} method in
  * a {@code try ... finally} block.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 0.8
  * @since   0.5
  * @module
  */
 public final class Semaphores {
     /**
+     * A flag to indicate that empty collections should be returned as {@code null}. Returning
null
+     * collections is not a recommended practice, but is useful in some situations like marshalling
+     * a XML document with JAXB, when we want to omit empty XML blocks.
+     */
+    public static final int NULL_COLLECTION = 1;
+
+    /**
+     * A flag to indicate that only metadata are desired and that there is no need to create
costly objects.
+     * This flag is used during iteration over many coordinate operations before to select
a single one by
+     * inspecting only their metadata.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/SIS-327">SIS-327</a>
+     */
+    public static final int METADATA_ONLY = 2;
+
+    /**
      * A lock for avoiding never-ending recursivity in the {@code equals} method of {@code
AbstractDerivedCRS}
      * and {@link org.apache.sis.referencing.operation.AbstractCoordinateOperation}.
      * It is set to {@code true} when a comparison is in progress. This lock is necessary
because
      * {@code AbstractDerivedCRS} objects contain a {@code conversionFromBase} field, which
contains a
      * {@code DefaultConversion.targetCRS} field referencing back the {@code AbstractDerivedCRS}
object.
      */
-    public static final byte CONVERSION_AND_CRS = 1;
+    public static final int CONVERSION_AND_CRS = 4;
 
     /**
      * A flag to indicate that {@link org.apache.sis.referencing.operation.AbstractCoordinateOperation}
      * is querying parameters of a {@code MathTransform} enclosed in the operation. This
is often in the
      * intend to format WKT of a {@code "ProjectedCRS"} element.
      */
-    public static final byte ENCLOSED_IN_OPERATION = 2;
-
-    /**
-     * A flag to indicate that empty collections should be returned as {@code null}. Returning
null
-     * collections is not a recommended practice, but is useful in some situations like marshalling
-     * a XML document with JAXB, when we want to omit empty XML blocks.
-     */
-    public static final byte NULL_COLLECTION = 4;
+    public static final int ENCLOSED_IN_OPERATION = 8;
 
     /**
      * A flag to indicate that a parameter value outside its domain of validity should not
cause an exception
@@ -62,7 +71,7 @@ public final class Semaphores {
      * <p><b>Example:</b> EPSG:3752 was a Mercator (variant A) projection
but set the latitude of origin to 41°S.</p>
      */
     @Workaround(library = "EPSG:3752", version = "8.9")        // Deprecated in 2007 but
still present in 2016.
-    public static final byte SUSPEND_PARAMETER_CHECK = 8;
+    public static final int SUSPEND_PARAMETER_CHECK = 16;
 
     /**
      * The flags per running thread.
@@ -72,7 +81,7 @@ public final class Semaphores {
     /**
      * The bit flags.
      */
-    private byte flags;
+    private int flags;
 
     /**
      * For internal use only.
@@ -86,7 +95,7 @@ public final class Semaphores {
      * @param  flag  one of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or
other constants.
      * @return {@code true} if the given flag is set.
      */
-    public static boolean query(final byte flag) {
+    public static boolean query(final int flag) {
         final Semaphores s = FLAGS.get();
         return (s != null) && (s.flags & flag) != 0;
     }
@@ -97,7 +106,7 @@ public final class Semaphores {
      * @param  flag  one of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or
other constants.
      * @return {@code true} if the given flag was already set.
      */
-    public static boolean queryAndSet(final byte flag) {
+    public static boolean queryAndSet(final int flag) {
         Semaphores s = FLAGS.get();
         if (s == null) {
             s = new Semaphores();
@@ -113,7 +122,7 @@ public final class Semaphores {
      *
      * @param  flag  one of {@link #CONVERSION_AND_CRS}, {@link #ENCLOSED_IN_OPERATION} or
other constants.
      */
-    public static void clear(final byte flag) {
+    public static void clear(final int flag) {
         final Semaphores s = FLAGS.get();
         if (s != null) {
             s.flags &= ~flag;



Mime
View raw message