sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Change the strategy in the way the DatumShiftGrid tree is contructed by NTv2 loader. In other classes, improve a bit the documentation and the toString() result.
Date Thu, 13 Feb 2020 19:12:32 GMT
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 b25ef83  Change the strategy in the way the DatumShiftGrid tree is contructed by
NTv2 loader. In other classes, improve a bit the documentation and the toString() result.
b25ef83 is described below

commit b25ef835206df9a6a4b83f760a2ce8541059b481
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Thu Feb 13 15:50:13 2020 +0100

    Change the strategy in the way the DatumShiftGrid tree is contructed by NTv2 loader.
    In other classes, improve a bit the documentation and the toString() result.
---
 .../provider/DatumShiftGridCompressed.java         |  4 +-
 .../referencing/provider/DatumShiftGridFile.java   | 94 +++++++++++++++++++++-
 .../sis/internal/referencing/provider/NTv2.java    | 70 ++++++++++------
 .../sis/referencing/datum/DatumShiftGrid.java      | 36 ++++++---
 .../operation/builder/LinearTransformBuilder.java  |  9 ++-
 .../transform/SpecializableTransform.java          |  2 +-
 6 files changed, 172 insertions(+), 43 deletions(-)

diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
index 3527244..2dca2c9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
@@ -44,13 +44,15 @@ final class DatumShiftGridCompressed<C extends Quantity<C>, T
extends Quantity<T
     private static final long serialVersionUID = 4847888093457104917L;
 
     /**
-     * Maximal grid index along the <var>y</var> axis.
+     * Maximal grid index along the <var>y</var> axis, inclusive.
      * This is the number of grid cells minus 2.
      */
     private final int ymax;
 
     /**
      * An "average" value for the offset in each dimension.
+     *
+     * @see #getCellMean(int)
      */
     private final double[] averages;
 
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
index ebc74ee..8c51f64 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
@@ -17,6 +17,7 @@
 package org.apache.sis.internal.referencing.provider;
 
 import java.util.Arrays;
+import java.util.Collection;
 import java.lang.reflect.Array;
 import java.nio.file.Path;
 import javax.measure.Unit;
@@ -24,9 +25,13 @@ import javax.measure.Quantity;
 import org.opengis.parameter.ParameterDescriptor;
 import org.opengis.parameter.ParameterDescriptorGroup;
 import org.opengis.parameter.GeneralParameterDescriptor;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.NoninvertibleTransformException;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.util.collection.Cache;
+import org.apache.sis.util.collection.TreeTable;
+import org.apache.sis.util.collection.TableColumn;
+import org.apache.sis.util.collection.DefaultTreeTable;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.datum.DatumShiftGrid;
 import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
@@ -41,7 +46,7 @@ import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
  * sharing data and for {@link #equals(Object)} and {@link #hashCode()} implementations.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @param <C>  dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
  * @param <T>  dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
@@ -103,6 +108,21 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
     double accuracy;
 
     /**
+     * The sub-grids, or {@code null} if none. The domain of validity of each sub-grid should
be contained
+     * in the domain of validity of this grid. Children do not change the way this {@code
DatumShiftGrid}
+     * performs its calculation; this list is used only at the time of building {@link MathTransform}
tree.
+     *
+     * <div class="note"><b>Design note:</b>
+     * we do not provide sub-grids functionality in the {@link DatumShiftGrid} parent class
because
+     * the {@link MathTransform} tree will depend on assumptions about {@link #getCoordinateToGrid()},
+     * in particular that it contains only translations and scales (no rotation, no shear).
+     * Those assumptions are enforced by the {@link DatumShiftGridFile} constructor.</div>
+     *
+     * @see #setSubGrids(Collection)
+     */
+    private DatumShiftGridFile<C,T>[] subgrids;
+
+    /**
      * Creates a new datum shift grid for the given grid geometry.
      * The actual offset values need to be provided by subclasses.
      *
@@ -137,6 +157,7 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
 
     /**
      * Creates a new datum shift grid with the same grid geometry than the given grid.
+     * This is used by {@link DatumShiftGridCompressed} for replacing a grid by another one.
      *
      * @param  other  the other datum shift grid from which to copy the grid geometry.
      */
@@ -146,6 +167,31 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
         files      = other.files;
         nx         = other.nx;
         accuracy   = other.accuracy;
+        subgrids   = other.subgrids;
+    }
+
+    /**
+     * Sets the sub-grids that are direct children of this grid.
+     * This method can be invoked only once.
+     */
+    @SuppressWarnings({"rawtypes", "unchecked"})
+    final void setSubGrids(final Collection<DatumShiftGridFile<C,T>> children)
{
+        if (subgrids != null) throw new IllegalStateException();
+        subgrids = children.toArray(new DatumShiftGridFile[children.size()]);
+    }
+
+    /**
+     * Returns the number of grids, including this grid and all sub-grids counted recursively.
+     * This is used for information purpose only.
+     */
+    private int getGridCount() {
+        int n = 1;
+        if (subgrids != null) {
+            for (final DatumShiftGridFile<C,T> subgrid : subgrids) {
+                n += subgrid.getGridCount();
+            }
+        }
+        return n;
     }
 
     /**
@@ -273,6 +319,34 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
         return super.hashCode() + Arrays.hashCode(files);
     }
 
+    /**
+     * Returns a string representation for debugging purpose.
+     * If this grid has children, it will be shown as a tree.
+     */
+    @Override
+    public final String toString() {
+        if (subgrids == null) {
+            return super.toString();
+        }
+        final TreeTable tree = new DefaultTreeTable(TableColumn.NAME);
+        toTree(tree.getRoot());
+        return tree.toString();
+    }
+
+    /**
+     * Formats this grid as a tree with its children.
+     */
+    private void toTree(final TreeTable.Node branch) {
+        String label = super.toString();
+        if (subgrids != null) {
+            label = label + " (" + getGridCount() + " grids)";
+            for (final DatumShiftGridFile<C,T> subgrid : subgrids) {
+                subgrid.toTree(branch.newChild());
+            }
+        }
+        branch.setValue(TableColumn.NAME, label);
+    }
+
 
 
 
@@ -308,6 +382,8 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
         /**
          * Creates a new datum shift grid with the given grid geometry, filename and number
of shift dimensions.
          * All {@code double} values given to this constructor will be converted from degrees
to radians.
+         *
+         * @param  dim  number of dimensions of translation vectors.
          */
         Float(final int dim,
               final Unit<C> coordinateUnit,
@@ -377,5 +453,21 @@ abstract class DatumShiftGridFile<C extends Quantity<C>, T extends
Quantity<T>>
         public final double getCellValue(final int dim, final int gridX, final int gridY)
{
             return DecimalFunctions.floatToDouble(offsets[dim][gridX + gridY*nx]);
         }
+
+        /**
+         * Returns the average translation parameters from source to target.
+         *
+         * @param  dim  the dimension for which to get an average value.
+         * @return a value close to the average for the given dimension.
+         */
+        @Override
+        public double getCellMean(final int dim) {
+            final float[] data = offsets[dim];
+            double sum = 0;
+            for (final float value : data) {
+                sum += value;
+            }
+            return sum / data.length;
+        }
     }
 }
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
index f99af94..7df539b 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/NTv2.java
@@ -271,7 +271,7 @@ public final class NTv2 extends AbstractProvider {
 
         /**
          * Keys of {@link #header} for entries that were declared in the overview header.
-         * This is used after {@link #readGrid(Map, List)} execution for discarding all
+         * This is used after {@link #readGrid(Map, Map)} execution for discarding all
          * entries specific to sub-grids, for avoiding to mix entries from two sub-grids.
          */
         private final String[] overviewKeys;
@@ -434,31 +434,51 @@ public final class NTv2 extends AbstractProvider {
          * sub-grids (if any) as children.
          */
         final DatumShiftGridFile<Angle,Angle> readAllGrids() throws IOException, FactoryException,
NoninvertibleTransformException {
-            final Map<String, DatumShiftGridFile<Angle,Angle>> grids = new HashMap<>(Containers.hashMapCapacity(numGrids));
-            final List<Object> parentChildPairs = new ArrayList<>(numGrids *
2);
+            final Map<String,      DatumShiftGridFile<Angle,Angle>>  grids  
 = new HashMap<>(Containers.hashMapCapacity(numGrids));
+            final Map<String, List<DatumShiftGridFile<Angle,Angle>>> children
= new LinkedHashMap<>();   // Should have few entries.
             while (grids.size() < numGrids) {
-                readGrid(grids, parentChildPairs);
+                readGrid(grids, children);
             }
             /*
-             * At this point all grids have been read. Now search the parent for each grid.
-             * We should have exactly one grid without parent.
+             * Assign the sub-grids to their parent only after we finished to read all grids.
+             * Doing this work last is more robust to cases where grids are in random order.
+             *
+             * Notes: if the parent-child graph contains cycles (deeper than a child declaring
itself as its parent),
+             *        the grids in cycles will be lost. This is because we need a grid without
parent for getting the
+             *        graph added in the roots list. There is currently no mechanism for
detecting those problems.
              */
-            DatumShiftGridFile<Angle,Angle> root = null;
-            for (int i = parentChildPairs.size(); i != 0;) {
-                @SuppressWarnings("unchecked")
-                final DatumShiftGridFile<Angle,Angle> grid = (DatumShiftGridFile<Angle,Angle>)
parentChildPairs.get(--i);
-                final DatumShiftGridFile<Angle,Angle> parent = grids.get((String) parentChildPairs.get(--i));
-                if (parent != null && parent != grid) {     // (parent == grid) if
PARENT and SUB_NAME were both null.
-// TODO             parent.addChild(head.grid);
+            final List<DatumShiftGridFile<Angle,Angle>> roots = new ArrayList<>();
+            for (final Map.Entry<String, List<DatumShiftGridFile<Angle,Angle>>>
entry : children.entrySet()) {
+                final DatumShiftGridFile<Angle,Angle> parent = grids.get(entry.getKey());
+                final List<DatumShiftGridFile<Angle,Angle>> subgrids = entry.getValue();
+                if (parent != null) {
+                    /*
+                     * Verify that the children does not declare themselves as their parent.
+                     * It may happen if SUB_GRID and PARENT have the same value, typically
a
+                     * null or empty value if those records were actually unspecified.
+                     */
+                    for (int i=subgrids.size(); --i >= 0;) {
+                        if (subgrids.get(i) == parent) {      // Want identity check, no
need for equals(Object).
+                            subgrids.remove(i);
+                            roots.add(parent);
+                            break;
+                        }
+                    }
+                    if (!subgrids.isEmpty()) {
+                        parent.setSubGrids(subgrids);
+                    }
                 } else {
-                    // TODO: if more than one root, create a synthetic grid.
-                    root = grid;
+                    roots.addAll(subgrids);
                 }
             }
-            if (root == null) {
-                throw new FactoryException(Errors.format(Errors.Keys.CanNotRead_1, file));
+            switch (roots.size()) {
+                case 0: throw new FactoryException(Errors.format(Errors.Keys.CanNotRead_1,
file));
+                case 1: return roots.get(0);
             }
-            return root;
+            /*
+             * If there is more than one root, creates a synthetic grid for hosting them.
+             */
+            return roots.get(0);     // TODO
         }
 
         /**
@@ -470,10 +490,11 @@ public final class NTv2 extends AbstractProvider {
          * <p>NTv2 grids contain also information about shifts accuracy. This is not
yet handled by SIS,
          * except for determining an approximate grid cell resolution.</p>
          *
-         * @param  addTo             the map where to add the grid with the grid name as
the key.
-         * @param  parentChildPairs  the list where to add (name of parent, child grid) tuples.
+         * @param  addTo     the map where to add the grid with the grid name as the key.
+         * @param  children  the map where to add children with the parent name as the key.
          */
-        private void readGrid(final Map<String, DatumShiftGridFile<Angle,Angle>>
addTo, final List<Object> parentChildPairs)
+        private void readGrid(final Map<String, DatumShiftGridFile<Angle,Angle>>
addTo,
+                final Map<String, List<DatumShiftGridFile<Angle,Angle>>>
children)
                 throws IOException, FactoryException, NoninvertibleTransformException
         {
             if (isV2) {
@@ -554,16 +575,15 @@ public final class NTv2 extends AbstractProvider {
                 grid.accuracy = Units.DEGREE.getConverterTo(unit).convert(Formulas.ANGULAR_TOLERANCE)
/ size;
             }
             /*
-             * Add the grid to two collection. The first collection maps this grid to its
name, and the
-             * second collection maps the grid to its parent. We do not try to resolve the
child-parent
+             * Add the grid to two collection. The first collection associates this grid
to its name, and the
+             * second collection associates the grid to its parent. We do not try to resolve
the child-parent
              * relationship here; we will do that after all sub-grids have been read.
              */
             final String name = (String) get("SUB_NAME", numGrids > 1);
             if (addTo.put(name, DatumShiftGridCompressed.compress(grid, null, precision /
size)) != null) {
                 throw new FactoryException(Errors.format(Errors.Keys.DuplicatedIdentifier_1,
name));
             }
-            parentChildPairs.add((String) get("PARENT", numGrids > 1));
-            parentChildPairs.add(grid);
+            children.computeIfAbsent((String) get("PARENT", numGrids > 1), (k) -> new
ArrayList<>()).add(grid);
             /*
              * End of grid parsing. Remove all header entries that are specific to this sub-grid.
              * After this operation, `header` will contain only overview records.
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
index ac74508..9ad33d9 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
@@ -117,6 +117,15 @@ import org.apache.sis.measure.Units;
  * in more than two dimensions. See the above <cite>datum shift by geocentric translations</cite>
use case for
  * an example.
  *
+ * <h2>Sub-grids</h2>
+ * Some datum shift grid files provide a grid valid on a wide region, refined with denser
sub-grids in smaller regions.
+ * For each point to transform, the {@link org.opengis.referencing.operation.MathTransform}
should search and use the
+ * densest sub-grid containing the point. This functionality is not supported directly by
{@code DatumShiftGrid},
+ * but can be achieved by organizing many transforms in a tree. The first step is to create
an instance of
+ * {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform} for each
{@code DatumShiftGrid}.
+ * Then, those transforms with their domain of validity can be given to
+ * {@link org.apache.sis.referencing.operation.transform.MathTransforms#specialize MathTransforms.specialize(…)}.
+ *
  * <h2>Serialization</h2>
  * Serialized objects of this class are not guaranteed to be compatible with future Apache
SIS releases.
  * Serialization support is appropriate for short term storage or RMI between applications
running the
@@ -124,7 +133,7 @@ import org.apache.sis.measure.Units;
  * NTv2 should be preferred.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
  *
  * @param <C>  dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
  * @param <T>  dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
@@ -270,7 +279,8 @@ public abstract class DatumShiftGrid<C extends Quantity<C>, T
extends Quantity<T
      * validity will still be accepted, but the extrapolated results may be very wrong.
      *
      * <p>The unit of measurement for the coordinate values in the returned envelope
is
-     * given by {@link #getCoordinateUnit()}. The envelope CRS is undefined.</p>
+     * given by {@link #getCoordinateUnit()}. The envelope CRS is not set, but its value
+     * is implicitly the CRS of grid input coordinates.</p>
      *
      * @return the domain covered by this grid.
      * @throws TransformException if an error occurred while computing the envelope.
@@ -726,22 +736,26 @@ public abstract class DatumShiftGrid<C extends Quantity<C>,
T extends Quantity<T
     public abstract void getParameterValues(Parameters parameters);
 
     /**
-     * Returns a string representation of this {@code DatumShiftGrid}. The default implementation
-     * formats the {@linkplain #getParameterValues(Parameters) parameter values}.
+     * Returns a string representation of this {@code DatumShiftGrid} for debugging purposes.
      *
-     * @return a string representation of the grid parameters.
+     * @return a string representation of this datum shift grid.
      *
      * @since 1.0
      */
     @Override
     public String toString() {
-        final ParameterDescriptorGroup d = getParameterDescriptors();
-        if (d != null) {
-            final Parameters p = Parameters.castOrWrap(d.createValue());
-            getParameterValues(p);
-            return p.toString();
+        final StringBuffer buffer = new StringBuffer("DatumShift[");
+        for (int i=0; i<gridSize.length; i++) {
+            if (i != 0) buffer.append(" × ");
+            buffer.append(gridSize[i]);
+        }
+        String s = String.valueOf(coordinateUnit);  if (s.isEmpty()) s = "1";
+        String t = String.valueOf(translationUnit); if (t.isEmpty()) t = "1";
+        buffer.append(" cells; units = ").append(s).append(" → ").append(t);
+        if (isCellValueRatio) {
+            buffer.append("∕cellSize");
         }
-        return super.toString();
+        return buffer.append(']').toString();
     }
 
     /**
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 1abb8f0..412f7d7 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
@@ -532,9 +532,10 @@ search: for (int j=numPoints; --j >= 0;) {
     }
 
     /**
-     * Sets all matching control point pairs, overwriting any previous setting. The source
positions are the keys in
-     * the given map, and the target positions are the associated values in the map. The
map should not contain two
-     * entries with the same source position. Coordinate reference systems are ignored.
+     * Sets all control point (source, target) pairs, overwriting any previous setting.
+     * The source positions are the keys in given map, and the target positions are the associated
values.
+     * The map should not contain two entries with the same source position.
+     * Coordinate reference systems are ignored.
      * Null positions are silently ignored.
      * Positions with NaN or infinite coordinates cause an exception to be thrown.
      *
@@ -575,7 +576,7 @@ search: for (int j=numPoints; --j >= 0;) {
             final DirectPosition tgt = position(entry.getValue()); if (tgt == null) continue;
             /*
              * The first time that we get a non-null source and target coordinate, allocate
the arrays.
-             * The sources arrays are allocated only if the source coordiantes are randomly
distributed.
+             * The sources arrays are allocated only if the source coordinates are randomly
distributed.
              */
             if (targets == null) {
                 tgtDim = tgt.getDimension();
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
index 9beb7dc..d22a9a4 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/SpecializableTransform.java
@@ -427,7 +427,7 @@ class SpecializableTransform extends AbstractMathTransform implements
Serializab
             int srcOff = src.offset;
             final MathTransform tr;
             if (domain == null) {
-                tr = global;                               // The transform to apply when
no specialization is found.
+                tr = global;                                // The transform to apply when
no specialization is found.
                 do {                                        // Count how many points will
use that transform.
                     src.offset += srcInc;
                     if (--numPts <= 0) break;


Mime
View raw message