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 2021/03/23 16:03:43 UTC
[sis] branch geoapi-4.0 updated (a59613b -> 6caf39f)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from a59613b JTS : update version from 1.18.0 to 1.18.1
new 00cfd43 Complete upgrate of JTS version.
new 6caf39f Refactor `GridGeometry.linearize(…)` method in a separated `DomainLinearizer` class. The intent is to make linearization more parametrable, e.g. with a scale factor.
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../apache/sis/coverage/grid/DomainLinearizer.java | 205 +++++++++++++++++++++
.../org/apache/sis/coverage/grid/GridGeometry.java | 79 +++-----
.../operation/builder/LinearTransformBuilder.java | 2 +-
ide-project/NetBeans/nbproject/project.properties | 2 +-
4 files changed, 228 insertions(+), 60 deletions(-)
create mode 100644 core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DomainLinearizer.java
[sis] 01/02: Complete upgrate of JTS version.
Posted by de...@apache.org.
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
commit 00cfd43f21eaf0d75b5ddfd586cbfc7890a7aed9
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Mar 23 15:22:19 2021 +0100
Complete upgrate of JTS version.
---
ide-project/NetBeans/nbproject/project.properties | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ide-project/NetBeans/nbproject/project.properties b/ide-project/NetBeans/nbproject/project.properties
index db3a8df..c1d4e2c 100644
--- a/ide-project/NetBeans/nbproject/project.properties
+++ b/ide-project/NetBeans/nbproject/project.properties
@@ -114,7 +114,7 @@ geographlib.version = 1.51
guava.version = 28.0-jre
re2j.version = 1.3
esri.api.version = 2.2.4
-jts.version = 1.18.0
+jts.version = 1.18.1
jdom1.version = 1.0
jdom2.version = 2.0.4
jee.version = 8.0.1
[sis] 02/02: Refactor `GridGeometry.linearize(…)` method in a separated `DomainLinearizer` class. The intent is to make linearization more parametrable, e.g. with a scale factor.
Posted by de...@apache.org.
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
commit 6caf39fbe6d503936073a0c3e8052ea8088d68dc
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Mar 23 17:02:24 2021 +0100
Refactor `GridGeometry.linearize(…)` method in a separated `DomainLinearizer` class.
The intent is to make linearization more parametrable, e.g. with a scale factor.
---
.../apache/sis/coverage/grid/DomainLinearizer.java | 205 +++++++++++++++++++++
.../org/apache/sis/coverage/grid/GridGeometry.java | 79 +++-----
.../operation/builder/LinearTransformBuilder.java | 2 +-
3 files changed, 227 insertions(+), 59 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DomainLinearizer.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DomainLinearizer.java
new file mode 100644
index 0000000..ce90603
--- /dev/null
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/DomainLinearizer.java
@@ -0,0 +1,205 @@
+/*
+ * 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.coverage.grid;
+
+import java.util.Arrays;
+import org.opengis.util.FactoryException;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
+import org.apache.sis.referencing.operation.builder.LinearTransformBuilder;
+import org.apache.sis.referencing.operation.transform.LinearTransform;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.Envelopes;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * Method for replacing a non-linear "grid to CRS" conversion by a linear conversion (affine transform).
+ * The {@code GridGeometry} class allows non-linear {@linkplain GridGeometry#getGridToCRS "grid to CRS"}
+ * conversions, but some {@code GridGeometry} usages are restricted to linear (affine) conversions.
+ * The {@code DomainLinearizer} class encapsulates the method used for replacing non-linear conversions
+ * by a linear approximation.
+ *
+ * <p>The same instance can be reused by invoking {@code apply(…)} methods for many {@link GridCoverage}
+ * or {@link GridGeometry} instances.</p>
+ *
+ * <h2>Limitations</h2>
+ * Current implementation is designed for two-dimensional grid geometries.
+ * Support for higher dimensions is not guaranteed.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ *
+ * @see LinearTransformBuilder#approximate(MathTransform, Envelope)
+ *
+ * @since 1.1
+ * @module
+ */
+public class DomainLinearizer {
+ /**
+ * Whether to force lower grid coordinates to (0,0,…).
+ *
+ * @see #getGridStartsAtZero()
+ */
+ private boolean gridStartsAtZero;
+
+ /**
+ * Scale factor applied on grid coordinates, or 1 if none.
+ */
+ private double scale = 1;
+
+ /**
+ * Creates a new linearizer.
+ */
+ public DomainLinearizer() {
+ }
+
+ /**
+ * Returns whether {@code GridExtent} should have their lower grid coordinates set to zero.
+ * If {@code true}, then {@code DomainLinearizer} will opportunistically apply translation
+ * on the {@linkplain GridGeometry#getGridToCRS "grid to CRS"} conversion in such a way that
+ * {@link GridExtent#getLow()} is 0 for all dimensions.
+ *
+ * @return whether to force lower grid coordinates to (0,0,…).
+ *
+ * @see GridExtent#startsAtZero()
+ */
+ public boolean getGridStartsAtZero() {
+ return gridStartsAtZero;
+ }
+
+ /**
+ * Sets whether {@code GridExtent} should have their lower grid coordinates set to zero.
+ * The default value is {@code false}.
+ *
+ * @param force whether to force lower grid coordinates to (0,0,…).
+ */
+ public void setGridStartsAtZero(final boolean force) {
+ gridStartsAtZero = force;
+ }
+
+ /**
+ * Returns the scale factor applied on coordinates in all dimensions.
+ *
+ * @return scale factor applied on coordinates in all dimensions, or 1 if none.
+ */
+ public double getUniformScale() {
+ return scale;
+ }
+
+ /**
+ * Sets the scale factor to apply on coordinates in all dimensions.
+ * The default value is 1.
+ *
+ * @param factor scale factor applied on coordinates in all dimensions, or 1 if none.
+ */
+ public void setUniformScale(final double factor) {
+ ArgumentChecks.ensureStrictlyPositive("factor", factor);
+ scale = factor;
+ }
+
+ /**
+ * Returns a grid coverage with a linear approximation of the <cite>grid to CRS</cite> conversion.
+ * The linear approximation is computed by {@link #apply(GridGeometry)}. If the <cite>grid to CRS</cite>
+ * conversion of the given coverage is already linear, then this method returns {@code coverage}.
+ *
+ * @param coverage the grid coverage in which to make the <cite>grid to CRS</cite> conversion linear.
+ * @return a grid coverage with a linear approximation of the <cite>grid to CRS</cite> conversion.
+ * @throws TransformException if some cell coordinates can not be computed.
+ */
+ public GridCoverage apply(final GridCoverage coverage) throws TransformException {
+ ArgumentChecks.ensureNonNull("coverage", coverage);
+ final GridGeometry gg = coverage.getGridGeometry();
+ final GridGeometry linearized = apply(gg);
+ if (gg.equals(linearized)) {
+ return coverage;
+ }
+ final GridCoverageProcessor processor = new GridCoverageProcessor();
+ return processor.resample(coverage, linearized);
+ }
+
+ /**
+ * Creates a grid geometry with a linear approximation of the <cite>grid to CRS</cite> conversion.
+ * The approximation is computed by <cite>Least Mean Squares</cite> method: the affine transform
+ * coefficients are chosen in way making the average value of (<var>position</var> − <var>linear
+ * approximation of position</var>)² as small as possible for all cells in given grid geometry.
+ * If the <cite>grid to CRS</cite> conversion of the given grid geometry is already linear,
+ * then this method returns {@code gg}.
+ *
+ * @param gg the grid geometry in which to make the <cite>grid to CRS</cite> conversion linear.
+ * @return a grid geometry with a linear approximation of the <cite>grid to CRS</cite> conversion.
+ * @throws TransformException if some cell coordinates can not be computed.
+ */
+ public GridGeometry apply(final GridGeometry gg) throws TransformException {
+ ArgumentChecks.ensureNonNull("gg", gg);
+ if (gg.nonLinears != 0) try {
+ MathTransform gridToCRS = gg.requireGridToCRS(true);
+ GeneralEnvelope domain = gg.extent.toCRS(null, null, null);
+ MathTransform approximate = modify(LinearTransformBuilder.approximate(gridToCRS, domain));
+ MathTransform gridToGrid = MathTransforms.concatenate(gridToCRS, approximate.inverse());
+ domain = Envelopes.transform(gridToGrid, domain);
+ final int dimension = domain.getDimension();
+ final long[] coordinates = new long[dimension * 2];
+ final double[] shift = new double[dimension];
+ for (int i=0; i<dimension; i++) {
+ long low = Math.round(domain.getMinimum(i));
+ long high = Math.round(domain.getMaximum(i));
+ high = Math.max(low, Math.decrementExact(high));
+ if (gridStartsAtZero) {
+ high = Math.subtractExact(high, low);
+ shift[i] = low;
+ } else {
+ coordinates[i] = low;
+ }
+ coordinates[i + dimension] = high;
+ }
+ approximate = MathTransforms.concatenate(MathTransforms.translation(shift), approximate);
+ if (!approximate.equals(gridToCRS)) {
+ return new GridGeometry(new GridExtent(gg.extent, coordinates), PixelInCell.CELL_CENTER,
+ approximate, gg.envelope.getCoordinateReferenceSystem());
+ }
+ } catch (FactoryException e) {
+ final Throwable cause = e.getCause();
+ if (cause instanceof TransformException) {
+ throw (TransformException) cause;
+ }
+ throw new TransformException(e);
+ }
+ return gg;
+ }
+
+ /**
+ * Callback for custom modification of linear approximation. This method is invoked by {@link #apply(GridGeometry)}
+ * after a linear "grid to CRS" approximation has been computed by the <cite>Least Mean Squares</cite> method.
+ * Subclasses can override this method for example in order to scale the conversion by some arbitrary factor.
+ *
+ * <div class="note"><b>Tip:</b>
+ * scales (if desired) should be applied <em>before</em> {@code gridToCRS}, i.e. on grid coordinates.
+ * Scales applied after the transform (i.e. on "real world" coordinates) may give unexpected results
+ * if the conversion contains a rotation.</div>
+ *
+ * @param gridToCRS an approximation of the "grid to CRS" conversion computed by {@code DomainLinearizer}.
+ * @return the approximation to use for creating a new {@link GridGeometry}. Should be linear.
+ */
+ private MathTransform modify(final LinearTransform gridToCRS) {
+ final double[] factors = new double[gridToCRS.getTargetDimensions()];
+ Arrays.fill(factors, scale);
+ return MathTransforms.concatenate(MathTransforms.scale(factors), gridToCRS);
+ }
+}
diff --git a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
index 768ab7e..cc50d6b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/coverage/grid/GridGeometry.java
@@ -51,7 +51,6 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.matrix.MatrixSIS;
import org.apache.sis.referencing.operation.transform.MathTransforms;
import org.apache.sis.referencing.operation.transform.PassThroughTransform;
-import org.apache.sis.referencing.operation.builder.LinearTransformBuilder;
import org.apache.sis.internal.referencing.DirectPositionView;
import org.apache.sis.internal.referencing.TemporalAccessor;
import org.apache.sis.internal.referencing.AxisDirections;
@@ -100,15 +99,30 @@ import org.apache.sis.xml.NilReason;
* <li>An {@linkplain #isConversionLinear indication of whether conversion for some axes is linear or not}.</li>
* </ul>
*
- * The first three properties should be mandatory, but are allowed to be temporarily absent during
- * grid coverage construction. Temporarily absent properties are allowed because they may be inferred
- * from a wider context. For example a grid geometry know nothing about {@link RenderedImage},
- * but {@code GridCoverage2D} does and may use that information for providing a missing grid extent.
+ * The first three properties should be mandatory,
+ * but are allowed to be temporarily absent during grid coverage construction.
+ * Temporarily absent properties are allowed because they may be inferred from a wider context.
+ * For example a {@code GridGeometry} knows nothing about {@link RenderedImage},
+ * but {@code GridCoverage2D} has this information and may use it for providing a missing grid extent.
* By default, any request for an undefined property will throw an {@link IncompleteGridGeometryException}.
* In order to check if a property is defined, use {@link #isDefined(int)}.
*
- * <p>{@code GridGeometry} instances are immutable and thread-safe.
- * The same instance can be shared by different {@link GridCoverage} instances.</p>
+ * <h2>Non-linear referencing</h2>
+ * A key property is the {@linkplain #getGridToCRS(PixelInCell) "grid to CRS"} conversion,
+ * which defines how to map pixel coordinates to "real world" coordinates such as latitudes and longitudes.
+ * This relationship is often linear (an affine transform), but {@linkplain #isConversionLinear not necessarily};
+ * {@code GridGeometry} accepts non-linear conversions as well. Non-linear conversions may occur with images
+ * using {@linkplain org.apache.sis.referencing.operation.builder.LocalizationGridBuilder localization grids},
+ * but non-linear conversions should not be used for expressing map projections (projections should be specified
+ * in the {@linkplain #getCoordinateReferenceSystem() Coordinate Reference System} (CRS) instead).
+ *
+ * <p>Some applications can not handle non-linear "grid to CRS" conversions.
+ * For example encoding an image in a GeoTIFF file is much simpler if the "grid to CRS" conversion is linear.
+ * The {@link DomainLinearizer} class can be used for replacing non-linear conversions by linear approximations.</p>
+ *
+ * <h2>Multi-threading</h2>
+ * {@code GridGeometry} instances are immutable and thread-safe.
+ * The same instance can be shared by different {@link GridCoverage} instances.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 1.1
@@ -1291,57 +1305,6 @@ public class GridGeometry implements LenientComparable, Serializable {
}
/**
- * Creates a grid geometry with a linear approximation of the <cite>grid to CRS</cite> transform.
- * The approximation is computed by <cite>Least Mean Squares</cite> method: the affine transform
- * coefficients are chosen in way making the average value of (<var>position</var> − <var>linear
- * approximation of position</var>)² as small as possible for all cells in this grid geometry.
- * If the <cite>grid to CRS</cite> transform of this grid geometry is already linear, then this
- * method returns {@code this}.
- *
- * @param forceLowerToZero whether to force lower grid coordinates to (0,0,…).
- * @return a grid geometry with a linear approximation of the <cite>grid to CRS</cite> transform.
- * @throws TransformException if some cell coordinates can not be computed.
- *
- * @see LinearTransformBuilder#approximate(MathTransform, Envelope)
- * @see GridExtent#startsAtZero()
- */
- public GridGeometry linearize(final boolean forceLowerToZero) throws TransformException {
- if (nonLinears != 0 && extent != null) try {
- GeneralEnvelope domain = extent.toCRS(null, null, null);
- MathTransform approximate = LinearTransformBuilder.approximate(gridToCRS, domain);
- MathTransform gridToGrid = MathTransforms.concatenate(gridToCRS, approximate.inverse());
- domain = Envelopes.transform(gridToGrid, domain);
- final int dimension = domain.getDimension();
- final long[] coordinates = new long[dimension * 2];
- final double[] shift = new double[dimension];
- for (int i=0; i<dimension; i++) {
- long low = Math.round(domain.getMinimum(i));
- long high = Math.round(domain.getMaximum(i));
- high = Math.max(low, Math.decrementExact(high));
- if (forceLowerToZero) {
- high = Math.subtractExact(high, low);
- shift[i] = low;
- } else {
- coordinates[i] = low;
- }
- coordinates[i + dimension] = high;
- }
- approximate = MathTransforms.concatenate(MathTransforms.translation(shift), approximate);
- if (!approximate.equals(gridToCRS)) {
- return new GridGeometry(new GridExtent(extent, coordinates), PixelInCell.CELL_CENTER,
- approximate, envelope.getCoordinateReferenceSystem());
- }
- } catch (FactoryException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof TransformException) {
- throw (TransformException) cause;
- }
- throw new TransformException(e);
- }
- return this;
- }
-
- /**
* Creates a transform from cell coordinates in this grid to cell coordinates in the given grid.
* The returned transform handles change of Coordinate Reference System and wraparound axes
* (e.g. longitude axis crossing the ±180° meridian) if applicable.
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 fc78b3e..aeca54a 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
@@ -260,7 +260,7 @@ public class LinearTransformBuilder extends TransformBuilder {
* @throws FactoryException if the transform approximation can not be computed.
*
* @see #setControlPoints(MathTransform)
- * @see org.apache.sis.coverage.grid.GridGeometry#linearize(boolean)
+ * @see org.apache.sis.coverage.grid.DomainLinearizer
*
* @since 1.1
*/