This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new c8c5eb84db When writing a GeoTIFF file, does not scan the thousands of 
coordinate operations in the EPSG database for finding an authority code. 
Document when searching the EPSG code of a CoordinateOperation is slow.
c8c5eb84db is described below

commit c8c5eb84dbca930687a6d2d198911607630559fa
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Aug 16 19:43:38 2026 +0200

    When writing a GeoTIFF file, does not scan the thousands of coordinate 
operations in the EPSG database for finding an authority code.
    Document when searching the EPSG code of a CoordinateOperation is slow.
---
 .../apache/sis/referencing/IdentifiedObjects.java  |  7 ++-
 .../factory/IdentifiedObjectFinder.java            |  2 +-
 .../referencing/factory/sql/EPSGCodeFinder.java    |  9 ++-
 .../sis/storage/geotiff/writer/GeoEncoder.java     | 66 ++++++++++++++++------
 4 files changed, 61 insertions(+), 23 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
index cabc1040d9..ebfa40973a 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/IdentifiedObjects.java
@@ -613,8 +613,9 @@ public final class IdentifiedObjects {
     }
 
     /**
-     * Looks up an EPSG code, such as {@code 4326}, of the specified object. 
This method searches in EPSG factories
-     * known to SIS for an object {@linkplain 
org.apache.sis.util.ComparisonMode#APPROXIMATE approximately equals}
+     * Looks up an <abbr>EPSG</abbr> code, such as {@code 4326}, of the 
specified object.
+     * This method searches in <abbr>EPSG</abbr> factories known to 
<abbr>SIS</abbr> for
+     * an object {@linkplain org.apache.sis.util.ComparisonMode#APPROXIMATE 
approximately equals}
      * to the specified object. If such an object is found, then its EPSG 
identifier is returned.
      * Otherwise or if there is ambiguity, this method returns {@code null}.
      *
@@ -634,7 +635,7 @@ public final class IdentifiedObjects {
      * @param  object  the object (usually a {@linkplain 
org.apache.sis.referencing.crs.AbstractCRS
      *         coordinate reference system}) whose EPSG code is to be found, 
or {@code null}.
      * @return the EPSG code, or {@code null} if none was found without 
ambiguity or if the given object was null.
-     * @throws UnavailableFactoryException if the EPSG factory is not 
available.
+     * @throws UnavailableFactoryException if the <abbr>EPSG</abbr> factory is 
not available.
      * @throws FactoryException if the lookup failed for another reason.
      *
      * @see #newFinder(String)
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
index 890c76a9e5..49c7bd77f8 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/IdentifiedObjectFinder.java
@@ -108,7 +108,7 @@ public class IdentifiedObjectFinder {
         /**
          * Lookup based on declared identifiers and on non-deprecated objects 
known to the factory.
          * First, a fast lookup is performed as described in {@link 
#DECLARATION}.
-         * If the last lookup found some matches, those matches are returned 
without scanning the rest of the database.
+         * If the fast lookup found some matches, those matches are returned 
without scanning the rest of the database.
          * It may be an incomplete set compared to what {@link 
#EXHAUSTIVE_VALID_DATASET} would have returned.
          * If the fast lookup gave no result, only then an exhaustive search 
is performed by scanning
          * the content of the geodetic dataset.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
index bd3a8b00b5..2741d40904 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGCodeFinder.java
@@ -414,7 +414,14 @@ crs:    if (isInstance(CoordinateReferenceSystem.class, 
object)) {
                 new FloatCondition("SEMI_MAJOR_AXIS", ((Ellipsoid) 
object).getSemiMajorAxis())
             };
         } else try {
-            // Not a supported type. Returns all codes if not too expensive.
+            /*
+             * Not a CRS, Datum, Ellipsoid or other supported type. Returns 
the authority codes of all objects
+             * that may be of the same type as the object to search. The 
caller will need to filter these codes
+             * by instantiating them and comparing them with `object`. Note 
that this is particularly expensive
+             * in the case of `CoordinateOperation` which has thousands of 
entries. But it is difficult to find
+             * a SQL statement capable to filter `CoordinateOperation` 
definitions because the given object may
+             * use operation method and parameter values that ar different 
while equivalent to EPSG definitions.
+             */
             return dao.getAuthorityCodes(object, addTo);
         } catch (SQLException exception) {
             throw databaseFailure(exception);
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
index cf30e5ab7e..2ddbcecedb 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/writer/GeoEncoder.java
@@ -63,6 +63,7 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.StringBuilders;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.internal.shared.Strings;
+import org.apache.sis.util.internal.shared.Constants;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.IdentifiedObjects;
@@ -71,6 +72,7 @@ import org.apache.sis.referencing.cs.CoordinateSystems;
 import org.apache.sis.referencing.operation.matrix.Matrices;
 import org.apache.sis.referencing.operation.provider.MapProjection;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.factory.IdentifiedObjectFinder;
 import org.apache.sis.referencing.factory.UnavailableFactoryException;
 import org.apache.sis.referencing.internal.shared.AxisDirections;
 import org.apache.sis.referencing.internal.shared.CoordinateOperations;
@@ -242,9 +244,15 @@ public final class GeoEncoder {
     private int citationLengthIndex;
 
     /**
-     * Whether to disable attempts to write <abbr>EPSG</abbr> codes. This is 
set to {@code true} on the first
-     * failed attempt to use the <abbr>EPSG</abbr> database. This is used for 
avoiding many retries which will
-     * continue to fail.
+     * The object to use for searching for <abbr>EPSG</abbr> codes, of {@code 
null} if none.
+     * This is set to {@code null} on the first failed attempt to use the 
<abbr>EPSG</abbr>
+     * database in order to avoid many retries which will continue to fail.
+     */
+    private IdentifiedObjectFinder epsgCodeFinder;
+
+    /**
+     * Whether to temporarily disable the use of <abbr>EPSG</abbr> codes.
+     * This is used when {@link #isPseudoProjection} is {@code true}.
      */
     private boolean disableEPSG;
 
@@ -263,6 +271,11 @@ public final class GeoEncoder {
         keyDirectory[0] = 1;            // Directory version.
         keyDirectory[1] = 1;            // Revision major number. We implement 
GeoTIFF 1.1.
         keyDirectory[2] = 1;            // Revision minor number. We implement 
GeoTIFF 1.1.
+        try {
+            epsgCodeFinder = IdentifiedObjects.newFinder(Constants.EPSG);
+        } catch (FactoryException e) {
+            listeners.warning(e);
+        }
     }
 
     /**
@@ -423,11 +436,11 @@ public final class GeoEncoder {
      * @throws IncompatibleResourceException if a unit of measurement cannot 
be encoded.
      */
     private void writeCRS(final VerticalCRS crs) throws FactoryException, 
IncompatibleResourceException {
-        if (writeEPSG(GeoKeys.Vertical, crs)) {
+        if (writeEPSG(GeoKeys.Vertical, crs, true)) {
             writeName(GeoKeys.VerticalCitation, null, crs);
             addUnits(UnitKey.VERTICAL, crs.getCoordinateSystem());
             final VerticalDatum datum = DatumOrEnsemble.asDatum(crs);
-            if (writeEPSG(GeoKeys.VerticalDatum, datum)) {
+            if (writeEPSG(GeoKeys.VerticalDatum, datum, true)) {
                 /*
                  * OGC requirement 25.5 said "VerticalCitationGeoKey SHALL be 
populated."
                  * But how? Using the same multiple-names convention as for 
geodetic CRS?
@@ -471,7 +484,7 @@ public final class GeoEncoder {
          * Start writing GeoTIFF keys for the geodetic CRS.
          */
         writeModelType(isBaseCRS ? GeoCodes.ModelTypeProjected : type);
-        if (writeEPSG(GeoKeys.GeodeticCRS, crs)) {
+        if (writeEPSG(GeoKeys.GeodeticCRS, crs, true)) {
             writeName(GeoKeys.GeodeticCitation, "GCS Name", isPseudoProjection 
? null : crs);
             writeDatum(DatumOrEnsemble.asDatum(crs));
         } else if (isBaseCRS) {
@@ -494,14 +507,13 @@ public final class GeoEncoder {
     private void writeDatum(final GeodeticDatum datum)
             throws FactoryException, IncommensurableException, 
IncompatibleResourceException
     {
-        if (writeEPSG(GeoKeys.GeodeticDatum, datum)) {
+        if (writeEPSG(GeoKeys.GeodeticDatum, datum, true)) {
             appendName(WKTKeywords.Datum, datum);
             final boolean previous = disableEPSG;
-            disableEPSG &= !isPseudoProjection;     // Re-enable the use of 
EPSG codes for the prime meridian.
-
+            disableEPSG = false;    // Re-enable the use of EPSG codes for the 
prime meridian.
             double longitude = 0;   // Means "do not write prime meridian".
             final PrimeMeridian primem = datum.getPrimeMeridian();
-            if (writeEPSG(GeoKeys.PrimeMeridian, primem)) {
+            if (writeEPSG(GeoKeys.PrimeMeridian, primem, true)) {
                 appendName(WKTKeywords.PrimeM, datum);
                 longitude = primem.getGreenwichLongitude();
             }
@@ -532,7 +544,7 @@ public final class GeoEncoder {
         final UnitConverter toLinear   = axisUnit.getConverterToAny(linearUnit 
!= null ? linearUnit : axisUnit);
         writeUnit(UnitKey.LINEAR);     // Must be after the `units` map has 
been updated.
         writeUnit(UnitKey.ANGULAR);
-        if (writeEPSG(GeoKeys.Ellipsoid, ellipsoid)) {
+        if (writeEPSG(GeoKeys.Ellipsoid, ellipsoid, true)) {
             appendName(WKTKeywords.Ellipsoid, ellipsoid);
             double axisLength = toLinear.convert(ellipsoid.getSemiMajorAxis());
             writeDouble(GeoKeys.SemiMajorAxis, axisLength);
@@ -572,10 +584,10 @@ public final class GeoEncoder {
         disableEPSG = isPseudoProjection;
         writeCRS(crs.getBaseCRS(), true);
         disableEPSG = previous;
-        if (writeEPSG(GeoKeys.ProjectedCRS, crs)) {
+        if (writeEPSG(GeoKeys.ProjectedCRS, crs, true)) {
             writeName(GeoKeys.ProjectedCitation, null, crs);
             addUnits(UnitKey.PROJECTED, crs.getCoordinateSystem());
-            if (writeEPSG(GeoKeys.Projection, projection)) {
+            if (writeEPSG(GeoKeys.Projection, projection, false)) {
                 final short projCode = getGeoCode(0, method);
                 writeShort(GeoKeys.ProjMethod, projCode);
                 writeUnit(UnitKey.PROJECTED);
@@ -762,14 +774,25 @@ public final class GeoEncoder {
 
     /**
      * Writes the <abbr>EPSG</abbr> code of the given object, or {@value 
GeoCodes#userDefined} if none.
-     * Returns whether the caller should write user-defined object in 
replacement or in addition to EPSG code.
+     * Returns whether the caller should write user-defined object in addition 
or in replacement to the
+     * <abbr>EPSG</abbr> code.
+     *
+     * <p>The {@code search} argument specifies whether to search in the 
<abbr>EPSG</abbr> database
+     * if the given object does not declare a code or if its code seems 
incorrect. Search should be
+     * enabled for <abbr>CRS</abbr> and for datum, because datum name and code 
are the only ways to
+     * differentiate them. The search should be disabled for the projection 
parameters because they
+     * are usually implied by the projected <abbr>CRS</abbr> and the search 
for coordinate operation
+     * can be very expansive.</p>
      *
      * @param  key     the numeric identifier of the GeoTIFF key.
      * @param  object  the object for which to get the EPSG code.
+     * @param  search  whether to allow a potentially expensive search in the 
<abbr>EPSG</abbr> database.
      * @return whether the caller should write user-defined object.
      * @throws FactoryException if an error occurred while fetching the EPSG 
code.
      */
-    private boolean writeEPSG(final short key, final IdentifiedObject object) 
throws FactoryException {
+    private boolean writeEPSG(final short key, final IdentifiedObject object, 
final boolean search)
+            throws FactoryException
+    {
         if (object == null) {
             writeShort(key, GeoCodes.undefined);
             missingValue(key);
@@ -784,11 +807,18 @@ public final class GeoEncoder {
          * specifications become stricter, so we are already "strict" 
regarding usages of EPSG codes.
          */
         short epsg = GeoCodes.userDefined;
-        if (!disableEPSG) try {
-            epsg = toShortEPSG(IdentifiedObjects.lookupEPSG(object));
+        if (epsgCodeFinder != null && !disableEPSG) try {
+            epsgCodeFinder.setSearchDomain(search ? 
IdentifiedObjectFinder.Domain.VALID_DATASET
+                                                  : 
IdentifiedObjectFinder.Domain.DECLARATION);
+            Identifier id = 
IdentifiedObjects.getIdentifier(epsgCodeFinder.findSingleton(object), 
Citations.EPSG);
+            if (id != null) try {
+                epsg = toShortEPSG(Integer.valueOf(id.getCode()));
+            } catch (NumberFormatException e) {
+                listeners.warning(e);
+            }
         } catch (UnavailableFactoryException e) {
             listeners.warning(Level.FINE, null, e);
-            disableEPSG = true;
+            epsgCodeFinder = null;
         }
         writeShort(key, epsg);
         return REDUNDANT || (epsg == GeoCodes.userDefined);

Reply via email to