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

desruisseaux 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 038be25  Allow `Linearizer` to use "Polar Stereographic" projection 
instead of "Universal Transverse Mercator" if the raster is too close to a pole.
038be25 is described below

commit 038be2598e312f20858a1baa925f79b6178c4920
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sun Jan 9 01:08:55 2022 +0100

    Allow `Linearizer` to use "Polar Stereographic" projection instead of 
"Universal Transverse Mercator" if the raster is too close to a pole.
---
 .../sis/internal/referencing/AxisDirections.java   |  18 ++-
 .../apache/sis/internal/referencing/Resources.java |   5 +
 .../sis/internal/referencing/Resources.properties  |   1 +
 .../internal/referencing/Resources_fr.properties   |   1 +
 .../operation/builder/LinearTransformBuilder.java  |   5 +-
 .../operation/builder/ProjectedTransformTry.java   |   2 +-
 ide-project/NetBeans/nbproject/genfiles.properties |   4 +-
 ide-project/NetBeans/nbproject/project.xml         |   1 +
 .../apache/sis/internal/earth/netcdf/GCOM_C.java   |   2 +-
 .../apache/sis/internal/earth/netcdf/GCOM_W.java   |   2 +-
 .../java/org/apache/sis/internal/netcdf/Grid.java  |   6 +-
 .../apache/sis/internal/netcdf/GridCacheValue.java |  22 +++-
 .../org/apache/sis/internal/netcdf/Linearizer.java | 126 +++++++++++++++++----
 13 files changed, 155 insertions(+), 40 deletions(-)

diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
index a1f1ab2..2dd092b 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AxisDirections.java
@@ -44,7 +44,7 @@ import static org.apache.sis.util.CharSequences.*;
  * Utilities methods related to {@link AxisDirection}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   0.4
  * @module
  */
@@ -265,6 +265,22 @@ public final class AxisDirections extends Static {
     }
 
     /**
+     * Returns {@code true} if the given axis is such as "South along 90°E".
+     *
+     * <p><b>This is a temporary method to be removed!</b>
+     * Latest ISO 19111 revision uses another mechanism for declaring such 
axis direction.
+     * This method will be removed after we upgraded the API accordingly.
+     *
+     * @param  dir  the axis to test, or {@code null}.
+     * @return {@code true} if the given axis is such as "South along 90°E".
+     *
+     * @since 1.2
+     */
+    public static boolean isAlongMeridian(final AxisDirection dir) {
+        return (dir != null) && !isCompass(dir);
+    }
+
+    /**
      * Returns {@code true} if the given direction is {@code UP} or {@code 
DOWN}.
      *
      * @param  dir  the direction to test, or {@code null}.
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
index 5992138..5d523d6 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.java
@@ -117,6 +117,11 @@ public final class Resources extends IndexedResourceBundle 
{
         public static final short CanNotInstantiateGeodeticObject_1 = 5;
 
         /**
+         * Can not linearize the localization grid.
+         */
+        public static final short CanNotLinearizeLocalizationGrid = 100;
+
+        /**
          * Can not map an axis from the specified coordinate system to the 
“{0}” direction.
          */
         public static final short CanNotMapAxisToDirection_1 = 6;
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
index 0f2a5dd..ad2e8c7 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources.properties
@@ -54,6 +54,7 @@ CanNotCreateObjectAsInstanceOf_2  = Can not create an object 
of type \u201c{1}\u
 CanNotFindCommonCRS               = Can not find a coordinate reference system 
common to all given envelopes.
 CanNotInferGridSizeFromValues_1   = Can not infer a grid size from the given 
values in {0} range.
 CanNotInstantiateGeodeticObject_1 = Can not instantiate geodetic object for 
\u201c{0}\u201d.
+CanNotLinearizeLocalizationGrid   = Can not linearize the localization grid.
 CanNotMapAxisToDirection_1        = Can not map an axis from the specified 
coordinate system to the \u201c{0}\u201d direction.
 CanNotParseCombinedReference_2    = Can not parse component {1} in the 
combined {0,choice,0#URN|1#URL}.
 CanNotSeparateCRS_1               = Can not separate the \u201c{0}\u201d 
coordinate reference system into sub-components.
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
index 17d1c77..b647712 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/Resources_fr.properties
@@ -59,6 +59,7 @@ CanNotCreateObjectAsInstanceOf_2  = Ne peut pas cr\u00e9er un 
objet du type \u00
 CanNotFindCommonCRS               = Ne peut pas trouver un syst\u00e8me de 
r\u00e9f\u00e9rence des coordonn\u00e9es commun pour toutes les enveloppes 
donn\u00e9es.
 CanNotInferGridSizeFromValues_1   = Ne peut pas inf\u00e9rer une taille de 
grille \u00e0 partir des valeurs donn\u00e9es dans la plage {0}.
 CanNotInstantiateGeodeticObject_1 = Ne peut pas cr\u00e9er l\u2019objet 
g\u00e9od\u00e9tique pour \u00ab\u202f{0}\u202f\u00bb.
+CanNotLinearizeLocalizationGrid   = Ne peut pas lin\u00e9ariser la grille de 
localisation.
 CanNotMapAxisToDirection_1        = Aucun axe du syst\u00e8me de 
coordonn\u00e9es sp\u00e9cifi\u00e9 n\u2019a pu \u00eatre associ\u00e9 \u00e0 
la direction \u00ab\u202f{0}\u202f\u00bb.
 CanNotSeparateCRS_1               = Ne peut pas s\u00e9parer le syst\u00e8me 
de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb en sous-composantes.
 CanNotSeparateTransform_3         = Ne peut pas s\u00e9parer la transformation 
parce-que le r\u00e9sultat aurait {2} dimension{2,choice,1#|2#s} en 
{0,choice,0#entr\u00e9|1#sortie} au lieu de {1}.
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
index 2df12fe..0f5838f 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/LinearTransformBuilder.java
@@ -89,7 +89,7 @@ import org.apache.sis.util.Classes;
  * That selected projection is given by {@link #linearizer()}.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  *
  * @see LocalizationGridBuilder
  * @see LinearTransform
@@ -1469,7 +1469,8 @@ search:         for (int j=domain(); --j >= 0;) {
                  * Finished to try all transforms. If all of them failed, wrap 
the `TransformException`.
                  */
                 if (bestTransform == null) {
-                    throw new 
FactoryException(ProjectedTransformTry.getError(linearizers));
+                    throw new 
FactoryException(Resources.format(Resources.Keys.CanNotLinearizeLocalizationGrid),
+                                               
ProjectedTransformTry.getError(linearizers));
                 }
                 if (needTargetReplace) {
                     transformedArrays = 
appliedLinearizer.replaceTransformed(targets, transformedArrays);
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ProjectedTransformTry.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ProjectedTransformTry.java
index 64e6931..547fb73 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ProjectedTransformTry.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/builder/ProjectedTransformTry.java
@@ -369,7 +369,7 @@ final class ProjectedTransformTry implements 
Comparable<ProjectedTransformTry>,
 
     /**
      * Returns the first error in the given list of linearizers. Errors after 
the first one are added
-     * as suppressed exception. If no error are found, this method returns 
{@code null}.
+     * as suppressed exceptions. If no error are found, this method returns 
{@code null}.
      */
     static TransformException getError(final List<ProjectedTransformTry> 
linearizers) {
         TransformException error = null;
diff --git a/ide-project/NetBeans/nbproject/genfiles.properties 
b/ide-project/NetBeans/nbproject/genfiles.properties
index c909012..26927a9 100644
--- a/ide-project/NetBeans/nbproject/genfiles.properties
+++ b/ide-project/NetBeans/nbproject/genfiles.properties
@@ -3,6 +3,6 @@
 build.xml.data.CRC32=58e6b21c
 build.xml.script.CRC32=462eaba0
 build.xml.stylesheet.CRC32=28e38971@1.53.1.46
-nbproject/build-impl.xml.data.CRC32=7361a050
+nbproject/build-impl.xml.data.CRC32=d3e84221
 nbproject/build-impl.xml.script.CRC32=6b1e829a
-nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.100.0.48
+nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.101.0.48
diff --git a/ide-project/NetBeans/nbproject/project.xml 
b/ide-project/NetBeans/nbproject/project.xml
index 1fa69a1..00659e4 100644
--- a/ide-project/NetBeans/nbproject/project.xml
+++ b/ide-project/NetBeans/nbproject/project.xml
@@ -99,6 +99,7 @@
             <word>initially</word>
             <word>javadoc</word>
             <word>kilometre</word>
+            <word>linearization</word>
             <word>linearizer</word>
             <word>linearizers</word>
             <word>loggings</word>
diff --git 
a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
 
b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
index 3e23d77..e81c9ef 100644
--- 
a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
+++ 
b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_C.java
@@ -315,7 +315,7 @@ public final class GCOM_C extends Convention {
      */
     @Override
     public Set<Linearizer> linearizers(final Decoder decoder) {
-        return Collections.singleton(new Linearizer(CommonCRS.WGS84, 
Linearizer.Type.UTM));
+        return Collections.singleton(new Linearizer(CommonCRS.WGS84, 
Linearizer.Type.UNIVERSAL));
     }
 
     /**
diff --git 
a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
 
b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
index b129195..343984e 100644
--- 
a/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
+++ 
b/profiles/sis-japan-profile/src/main/java/org/apache/sis/internal/earth/netcdf/GCOM_W.java
@@ -198,7 +198,7 @@ public final class GCOM_W extends Convention {
      */
     @Override
     public Set<Linearizer> linearizers(final Decoder decoder) {
-        return Collections.singleton(new Linearizer(CommonCRS.WGS84, 
Linearizer.Type.UTM));
+        return Collections.singleton(new Linearizer(CommonCRS.WGS84, 
Linearizer.Type.UNIVERSAL));
     }
 
     /**
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
index 9f67fa8..d19b8ce 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Grid.java
@@ -215,7 +215,7 @@ public abstract class Grid extends NamedElement {
                 /*
                  * If an axis has a "wraparound" range (for example a 
longitude axis where the next value after +180°
                  * may be -180°), we will examine it last. The reason is that 
if a wraparound occurs in the middle of
-                 * the localization grid, it will confuse the computation 
based on 'coordinateForAxis(…)' calls below.
+                 * the localization grid, it will confuse the computation 
based on `coordinateForAxis(…)` calls below.
                  * We are better to resolve the latitude axis first, and then 
resolve the longitude axis with the code
                  * path checking for dimension collisions, without using 
coordinateForAxis(…) on longitude axis.
                  */
@@ -449,7 +449,7 @@ findFree:       for (int srcDim : 
axis.gridDimensionIndices) {
              * axes come in pairs.
              */
             final List<GridCacheValue> linearizations = new ArrayList<>();
-            for (int i=0; i<nonLinears.size(); i++) {         // Length of 
'nonLinears' may change in this loop.
+            for (int i=0; i<nonLinears.size(); i++) {         // Length of 
`nonLinears` may change in this loop.
                 if (nonLinears.get(i) == null) {
                     for (int j=i; ++j < nonLinears.size();) {
                         if (nonLinears.get(j) == null) {
@@ -487,7 +487,7 @@ findFree:       for (int srcDim : 
axis.gridDimensionIndices) {
                                 if (grid.linearizationTarget != null) {
                                     linearizations.add(grid);
                                 }
-                                break;                                      // 
Continue the 'i' loop.
+                                break;                                      // 
Continue the `i` loop.
                             }
                         }
                     }
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridCacheValue.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridCacheValue.java
index 3f08c59..75c41af 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridCacheValue.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/GridCacheValue.java
@@ -20,9 +20,9 @@ import java.util.Map;
 import java.util.Set;
 import java.util.Optional;
 import org.opengis.util.FactoryException;
+import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.MathTransformFactory;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
 
 
@@ -32,26 +32,34 @@ import 
org.apache.sis.referencing.operation.builder.LocalizationGridBuilder;
  * {@code GridCacheValue}s are associated to {@link GridCacheKey}s in a hash 
map.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  * @since   1.1
  * @module
  */
 final class GridCacheValue {
+
     /**
      * The transform from grid coordinates to geographic or projected 
coordinates.
      */
     final MathTransform gridToCRS;
 
     /**
+     * If a linearization has been applied, the linearization type. Otherwise 
{@code null}.
+     * This field together with {@link #linearizationTarget} and {@link 
#axisSwap} fields are copies of
+     * {@link Linearizer} fields, copied for making sure that this {@code 
GridCacheValue} is immutable.
+     */
+    final Linearizer.Type linearizationType;
+
+    /**
      * The target CRS of {@link #gridToCRS} if different than the CRS inferred 
by {@link CRSBuilder}.
      * This field is non-null if the target CRS has been changed by 
application of a linearizer.
      */
-    final CoordinateReferenceSystem linearizationTarget;
+    final SingleCRS linearizationTarget;
 
     /**
      * Whether axes need to be swapped in order to have the same direction 
before and after linearization.
-     * For example if input coordinates stored in the localization grid have 
(east, north) directions, then
-     * {@link #linearizationTarget} coordinates shall have (east, north) 
directions as well.
+     * For example if input coordinates stored in the localization grid have 
(east, north) directions,
+     * then {@link #linearizationTarget} coordinates shall have (east, north) 
directions as well.
      * This flag specifies whether input coordinates must be swapped for 
making above condition true.
      *
      * <p>This flag assumes that {@link #linearizationTarget} has two 
dimensions.</p>
@@ -70,12 +78,14 @@ final class GridCacheValue {
             final String name = e.get().getKey();
             for (final Linearizer linearizer : linearizers) {
                 if (name.equals(linearizer.name())) {
+                    linearizationType   = linearizer.type;
                     linearizationTarget = linearizer.getTargetCRS();
-                    axisSwap = linearizer.axisSwap();
+                    axisSwap            = linearizer.axisSwap();
                     return;
                 }
             }
         }
+        linearizationType   = null;
         linearizationTarget = null;
         axisSwap = false;
     }
diff --git 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
index dc5bc4f..48ff2d6 100644
--- 
a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
+++ 
b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Linearizer.java
@@ -23,7 +23,7 @@ import java.util.List;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.crs.ProjectedCRS;
-import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.cs.CoordinateSystem;
 import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
@@ -49,8 +49,10 @@ import org.apache.sis.util.ArraysExt;
  * order determined by {@link #targetCRS}. In other words, netCDF dimension 
order shall be ignored if a
  * linearization is applied.</p>
  *
+ * <p>A new instance of this class shall be created for each netCDF file is 
read.</p>
+ *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
  *
  * @see 
org.apache.sis.referencing.operation.builder.LocalizationGridBuilder#addLinearizers(Map,
 boolean, int...)
  *
@@ -59,6 +61,13 @@ import org.apache.sis.util.ArraysExt;
  */
 public final class Linearizer {
     /**
+     * Number of dimensions of the grid.
+     *
+     * @see 
org.apache.sis.referencing.operation.builder.ResidualGrid#SOURCE_DIMENSION
+     */
+    private static final int SOURCE_DIMENSION = 2;
+
+    /**
      * The datum to use as one of the predefined constants. The ellipsoid size 
do not matter
      * because a linear regression will be applied anyway. However the 
eccentricity matter.
      *
@@ -74,14 +83,62 @@ public final class Linearizer {
 
     /**
      * The type of projection to create.
-     * Current implementation supports only Universal Transverse Mercator 
(UTM) projection,
+     * Current implementation supports only Universal Transverse Mercator 
(UTM) and stereographic projections,
      * but we nevertheless define this enumeration as a place-holder for more 
types in the future.
      */
     public enum Type {
         /**
-         * Universal Transverse Mercator projection.
+         * Universal Transverse Mercator (UTM) or Polar Stereographic 
projection, depending on whether the image
+         * is close to a pole or not. The CRS is selected by a call to {@link 
CommonCRS#universal(double, double)}.
+         * The point given is the {@code universal(…)} is determined as below:
+         *
+         * <ul>
+         *   <li>If the image is far enough from equator (at a latitude of 
{@value #POLAR_THRESHOLD} or further),
+         *       then the point will be in the center of the border closest to 
the pole.</li>
+         *   <li>Otherwise the point is in the middle of the image.</li>
+         * </ul>
+         *
+         * <div class="note"><b>Rational:</b>
+         * the intend is to increase the chances to get the Polar 
Stereographic projection for images close to pole.
+         * This is necessary because longitude values may become far from 
central meridian at latitudes such as 88°,
+         * causing the Transverse Mercator projection to produce NaN 
numbers.</div>
          */
-        UTM
+        UNIVERSAL;
+
+        /**
+         * Minimal latitude (in degrees) for forcing the {@link #UNIVERSAL} 
mode to consider a point
+         * on the border closest to pole for deciding whether to use UTM or 
stereographic projection.
+         * 60° is the limit of the domain of validity of Polar Stereographic 
methods.
+         */
+        static final int POLAR_THRESHOLD = 60;
+
+        /**
+         * Returns a coordinate system containing the axes to search and 
replace in order to build
+         * a "CRS after linearization" from a "CRS before linearization". The 
caller will use only
+         * the axis directions (not the names) of the returned coordinate 
system.
+         *
+         * <h4>Rational</h4>
+         * Usually, the source CRS is geographic and the target CRS is 
projected.
+         * We can generally associate a source axis to a target axis by 
looking at their directions.
+         * For example the "Longitude" source axis is approximately colinear 
with the "Easting" target axis.
+         * {@link org.opengis.referencing.cs.AxisDirection#EAST} can be used 
as a criterion for mapping them.
+         * Note however that axis names can not be used, because they differ 
in geographic and projected CRS.
+         *
+         * <p>However there is an exception where target axis directions will 
not work neither.
+         * If the projection is a polar projection with axis directions such 
as "South along 90°E",
+         * then {@link AxisDirections#indicesOfColinear(CoordinateSystem, 
CoordinateSystem)} will
+         * not find a match. We can workaround by using arbitrary (East, 
North) directions instead.</p>
+         *
+         * @param  targetCS  coordinate system of {@link Linearizer#targetCRS}.
+         */
+        final CoordinateSystem getAxisReplacement(final CoordinateSystem 
targetCS) {
+            for (int i = targetCS.getDimension(); --i >= 0;) {
+                if 
(AxisDirections.isAlongMeridian(targetCS.getAxis(i).getDirection())) {
+                    return CommonCRS.defaultGeographic().getCoordinateSystem();
+                }
+            }
+            return targetCS;
+        }
     }
 
     /**
@@ -93,7 +150,7 @@ public final class Linearizer {
      * The target coordinate reference system after application of the 
non-linear transform.
      * May depend on the netCDF file being read (for example for choosing a 
UTM zone).
      */
-    private CoordinateReferenceSystem targetCRS;
+    private SingleCRS targetCRS;
 
     /**
      * Whether axes need to be swapped in order to have the same direction 
before and after the transform.
@@ -124,7 +181,7 @@ public final class Linearizer {
     /**
      * Returns the target CRS computed by {@link #gridToTargetCRS 
gridToTargetCRS(…)}.
      */
-    final CoordinateReferenceSystem getTargetCRS() {
+    final SingleCRS getTargetCRS() {
         return targetCRS;
     }
 
@@ -168,16 +225,36 @@ public final class Linearizer {
                 throw new AssertionError(type);
             }
             /*
-             * Create a Universal Transverse Mercator (UTM) projection for the 
zone containing a point in
-             * the middle of the grid. We apply `Math.signum(…)` on the 
latitude for avoiding stereographic
-             * projections near poles and for avoiding Norway and Svalbard 
special cases.
+             * Create a Universal Transverse Mercator (UTM) projection for the 
zone containing a point in the grid.
+             * First, we compute an estimation of the bounding box in 
geographic coordinates (using grid corners).
+             * Then if the box is far enough from equator, we use the point on 
the side closest to the pole.
              */
-            case UTM: {
+            case UNIVERSAL: {
                 final Envelope bounds = grid.getSourceEnvelope(false);
-                final double[] median = grid.getControlPoint(
-                        (int) Math.round(bounds.getMedian(0)),
-                        (int) Math.round(bounds.getMedian(1)));
-                final ProjectedCRS crs = 
datum.universal(Math.signum(median[ydim]), median[xdim]);
+                double x, y, ymin, ymax;
+                {   // For keeping `median` variable local.
+                    final double[] median = grid.getControlPoint(
+                            (int) Math.round(bounds.getMedian(0)),
+                            (int) Math.round(bounds.getMedian(1)));
+                    x = median[xdim];
+                    y = median[ydim];
+                    ymin = ymax = y;
+                }
+                final int[] gc = new int[SOURCE_DIMENSION];
+                for (int i=0; i<4; i++) {
+                    for (int d=0; d<SOURCE_DIMENSION; d++) {
+                        gc[d] = (int) Math.round(((i & (1 << d)) == 0) ? 
bounds.getMinimum(d) : bounds.getMaximum(d));
+                    }
+                    final double yp = grid.getControlPoint(gc[0], gc[1])[ydim];
+                    if (yp < ymin) ymin = yp;
+                    if (yp > ymax) ymax = yp;
+                }
+                /*
+                 * If the image is far from equator, replace the middle point 
by a point close to pole.
+                 */
+                     if (ymin >= +Type.POLAR_THRESHOLD) y = ymax;
+                else if (ymax <= -Type.POLAR_THRESHOLD) y = ymin;
+                final ProjectedCRS crs = datum.universal(y, x);
                 assert 
ReferencingUtilities.startsWithNorthEast(crs.getBaseCRS().getCoordinateSystem());
                 transform = crs.getConversionFromBase().getMathTransform();
                 targetCRS = crs;
@@ -245,7 +322,7 @@ public final class Linearizer {
      * <p>This static method is defined here for keeping in a single class all 
codes related to linearization.</p>
      *
      * @param  components        the components of the compound CRS that 
{@link CRSBuilder} inferred.
-     * @param  replacements      the {@link #targetCRS} of linearizations.
+     * @param  replacements      the {@link #targetCRS} of linearizations. 
Usually a list of size 1.
      * @param  reorderGridToCRS  an affine transform doing a final step in a 
"grid to CRS" transform for ordering axes.
      *         Not used by this method, but modified for taking in account 
axis order changes caused by replacements.
      */
@@ -253,14 +330,16 @@ public final class Linearizer {
                                      final Matrix reorderGridToCRS) throws 
DataStoreReferencingException
     {
         Matrix original = null;
-search: for (final GridCacheValue cache : replacements) {
-            final CoordinateReferenceSystem targetCRS = 
cache.linearizationTarget;
+search: for (final GridCacheValue replacement : replacements) {
+            final SingleCRS targetCRS = replacement.linearizationTarget;
+            final CoordinateSystem targetCS = 
replacement.linearizationType.getAxisReplacement(targetCRS.getCoordinateSystem());
             int firstDimension = 0;
             for (int i=0; i < components.length; i++) {
                 final SingleCRS sourceCRS = components[i];
-                final int[] r = 
AxisDirections.indicesOfColinear(sourceCRS.getCoordinateSystem(), 
targetCRS.getCoordinateSystem());
+                final int[] r = 
AxisDirections.indicesOfColinear(sourceCRS.getCoordinateSystem(), targetCS);
                 if (r != null) {
-                    if (cache.axisSwap) {
+                    components[i] = targetCRS;
+                    if (replacement.axisSwap) {
                         ArraysExt.swap(r, 0, 1);
                     }
                     for (int j=0; j<r.length; j++) {
@@ -275,13 +354,14 @@ search: for (final GridCacheValue cache : replacements) {
                             }
                         }
                     }
-                    components[i] = (ProjectedCRS) targetCRS;
                     continue search;
                 }
                 firstDimension += 
sourceCRS.getCoordinateSystem().getDimension();
             }
-            // If a replacement can not be applied, fail CRS construction.
-            // May be relaxed in a future version if we have a use case.
+            /*
+             * If a replacement can not be applied, fail CRS construction.
+             * May be relaxed in a future version if we have a use case.
+             */
             throw new DataStoreReferencingException(Resources.format(
                     Resources.Keys.CanNotInjectComponent_1, 
IdentifiedObjects.getName(targetCRS, null)));
         }

Reply via email to