You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2017/04/26 13:33:18 UTC
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...
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(\u2026)} 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, \u2026) 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;