This is an automated email from the ASF dual-hosted git repository. jsorel pushed a commit to branch feat/exportableTransform in repository https://gitbox.apache.org/repos/asf/sis.git
commit e0e0ff90a49b4d8223c93d4c9c25945e6f9c2f66 Author: jsorel <[email protected]> AuthorDate: Tue Nov 5 15:41:42 2024 +0100 Add ExportableTransform interface --- .../sis/referencing/ExportableTransform.java | 55 +++++++ .../operation/projection/ConformalProjection.java | 8 + .../projection/EquidistantCylindrical.java | 32 ++++ .../projection/LambertConicConformal.java | 85 +++++++++++ .../operation/projection/LongitudeWraparound.java | 15 +- .../referencing/operation/projection/Mercator.java | 81 ++++++++++ .../operation/projection/NormalizedProjection.java | 19 ++- .../operation/projection/PolarStereographic.java | 86 +++++++++++ .../operation/projection/TransverseMercator.java | 138 ++++++++++++++++- .../operation/transform/ConcatenatedTransform.java | 36 ++++- .../transform/EllipsoidToCentricTransform.java | 167 ++++++++++++++++----- .../operation/transform/LinearTransform.java | 53 ++++++- .../operation/transform/PassThroughTransform.java | 45 +++++- 13 files changed, 777 insertions(+), 43 deletions(-) diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/ExportableTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/ExportableTransform.java new file mode 100644 index 0000000000..52f9238575 --- /dev/null +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/ExportableTransform.java @@ -0,0 +1,55 @@ +/* + * 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.referencing; + +/** + * Experimental API to export transforms to other syntaxes. + * + * @author Johann Sorel (Geomatys) + */ +public interface ExportableTransform { + + /** + * The produced code should transform one value only. + * Input array is expected to be named src. + * Ouput array is expected to be named dat. + * + * Example of expected produced code : + * <pre> + * {@code + * { + * _CONSTANT_1: 123.456, + * _CONSTANT_2: 234.567, + * + * transform(src) { + * //complex math using _CONSTANT_1 and _internalFunction1 + * return dst; + * }, + * + * _internalFunction1(val1, val2) { + * //does something + * } + * } + * } + * </pre> + * + * + * @return ECMAScript code + * @throws UnsupportedOperationException if export is not possible or not available + */ + String toECMAScript() throws UnsupportedOperationException; +} diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ConformalProjection.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ConformalProjection.java index 4fd8672176..58582e4e58 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ConformalProjection.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/ConformalProjection.java @@ -328,4 +328,12 @@ abstract class ConformalProjection extends NormalizedProjection { final double dy_dφ(final double sinφ, final double cosφ) { return (1 / cosφ) - eccentricitySquared * cosφ / (1 - eccentricitySquared * (sinφ*sinφ)); } + + final double[] getExpansionFirstTerms() { + return new double[]{c2χ, c4χ, c6χ, c8χ}; + } + + final boolean isUseIterations() { + return useIterations; + } } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java index ed5269ad00..166af4d24b 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/EquidistantCylindrical.java @@ -258,4 +258,36 @@ public class EquidistantCylindrical extends NormalizedProjection { dstPts[dstOff ] = srcPts[srcOff]; dstPts[dstOff+1] = y + μ; } + + @Override + protected String toECMAScript(boolean inverse) { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst φ = src[1];\n" + + "\t\tconst sinθ = Math.sin(2*φ);\n" + + "\t\tconst cosθ = Math.cos(2*φ);\n" + + "\t\tconst y = (((((("+Double.toString(cf7)+"*cosθ + "+Double.toString(cf6)+")*cosθ + "+Double.toString(cf5)+")*cosθ + "+Double.toString(cf4)+")*cosθ + "+Double.toString(cf3)+")*cosθ + "+Double.toString(cf2)+")*cosθ + "+Double.toString(cf1)+")*sinθ + "+Double.toString(c0)+"*φ;\n" + + "\t\treturn [src[0], y];\n" + + "\t}\n" + ); + } else { + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst μ = src[1] / "+Double.toString(c0)+";\n" + + "\t\tconst sinθ = Math.sin(2*μ);\n" + + "\t\tconst cosθ = Math.cos(2*μ);\n" + + "\t\tconst y = (((((("+Double.toString(ci7)+"*cosθ + "+Double.toString(ci6)+")*cosθ + "+Double.toString(ci5)+")*cosθ + "+Double.toString(ci4)+")*cosθ + "+Double.toString(ci3)+")*cosθ + "+Double.toString(ci2)+")*cosθ + "+Double.toString(ci1)+")*sinθ;\n" + + "\t\treturn [src[0], y + μ];\n" + + "\t}\n" + ); + } + + sb.append("}"); + return sb.toString(); + } + } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LambertConicConformal.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LambertConicConformal.java index 7428ce5a6a..2e3b05d1b9 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LambertConicConformal.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LambertConicConformal.java @@ -496,6 +496,91 @@ public class LambertConicConformal extends ConformalProjection { } + @Override + protected String toECMAScript(boolean inverse) { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + //constants + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + sb.append("\t_ANGULAR_TOLERANCE : ").append(Double.toString(ANGULAR_TOLERANCE)).append(",\n"); + //utils functions + sb.append( + "\t_expΨ : function(φ, ℯsinφ) {\n" + + "\t\treturn Math.tan(Math.PI/4 + 0.5*φ) * Math.pow((1 - ℯsinφ) / (1 + ℯsinφ), 0.5*this._eccentricity);\n" + + "\t},\n"); + + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst n = " + Double.toString(n) + ";\n" + + "\t\tconst θ = src[0]; // θ = Δλ⋅n\n" + + "\t\tconst φ = src[1]; // Sign may be reversed\n" + + "\t\tconst absφ = Math.abs(φ);\n" + + "\t\tconst sinθ = Math.sin(θ);\n" + + "\t\tconst cosθ = Math.cos(θ);\n" + + "\t\tlet sinφ = 0.0;\n" + + "\t\tlet ρ = 0.0; // EPSG guide uses \"r\", but we keep the symbol from Snyder p. 108 for consistency with PolarStereographic.\n" + + "\t\tif (absφ < Math.PI/2) {\n" + + "\t\t sinφ = Math.sin(φ);\n" + + "\t\t ρ = Math.pow(this._expΨ(φ, this._eccentricity*sinφ), n);\n" + + "\t\t} else if (absφ < Math.PI/2 + this._ANGULAR_TOLERANCE) {\n" + + "\t\t sinφ = 1;\n" + + "\t\t ρ = (φ*n >= 0) ? Number.POSITIVE_INFINITY : 0;\n" + + "\t\t} else {\n" + + "\t\t ρ = sinφ = Number.NaN;\n" + + "\t\t}\n" + + "\t\tconst x = ρ * sinθ;\n" + + "\t\tconst y = ρ * cosθ;\n" + + "\t\treturn [x,y];\n" + + "\t}\n"); + } else { + + //utils functions + sb.append( + "\t_fastHypot : function(x, y) {\n" + + "\t\treturn Math.sqrt(x*x + y*y);\n" + + "\t},\n"); + final double[] exp = getExpansionFirstTerms(); + sb.append( + "\t_φ : function(rexpΨ) {\n" + + "\t\tlet φ = (Math.PI/2) - 2*Math.atan(rexpΨ);\n" + + "\t\tconst sin_2φ = Math.sin(2*φ);\n" + + "\t\tconst cos_2φ = Math.cos(2*φ);\n" + + "\t\tφ += sin_2φ * ("+Double.toString(exp[0])+" + cos_2φ * ("+Double.toString(exp[1])+" + cos_2φ * ("+Double.toString(exp[2])+" + cos_2φ * "+Double.toString(exp[3])+")));\n"); + + if (!isUseIterations()) { + sb.append( + "\t\treturn φ;\n" + + "\t},\n"); + } else { + sb.append( + "\t\tconst hℯ = 0.5 * this._eccentricity;\n" + + "\t\tfor (let it=0; it<this._MAXIMUM_ITERATIONS; it++) {\n" + + "\t\t\tconst ℯsinφ = this._eccentricity * Math.sin(φ);\n" + + "\t\t\tconst Δφ = φ - (φ = Math.PI/2 - 2*Math.atan(rexpΨ * Math.pow((1 - ℯsinφ)/(1 + ℯsinφ), hℯ)));\n" + + "\t\t\tif (!(Math.abs(Δφ) > this._ITERATION_TOLERANCE)) { // Use '!' for accepting NaN.\n" + + "\t\t\t\treturn φ;\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t\treturn Number.NaN;\n" + + "\t},\n"); + } + + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst n = " + Double.toString(n) + ";\n" + + "\t\tconst x = src[0];\n" + + "\t\tconst y = src[1];\n" + + "\t\tconst d0 = Math.atan2(x, y); // Really (x,y), not (y,x)\n" + + "\t\tconst d1 = -this._φ(Math.pow(this._fastHypot(x, y), 1.0/n)); // Equivalent to φ(pow(hypot(x,y), -1/n)) but more accurate for n>0.\n" + + "\t\treturn [d0,d1];\n" + + "\t}\n"); + } + + sb.append("}"); + return sb.toString(); + } /** * Provides the transform equations for the spherical case of the Lambert Conformal projection. * diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LongitudeWraparound.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LongitudeWraparound.java index a47f4e7790..1d53d76c3b 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LongitudeWraparound.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/LongitudeWraparound.java @@ -34,6 +34,7 @@ import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.privy.DoubleDouble; import org.apache.sis.util.privy.Numerics; import org.apache.sis.measure.Longitude; +import org.apache.sis.referencing.ExportableTransform; /** @@ -68,7 +69,7 @@ import org.apache.sis.measure.Longitude; * @see org.apache.sis.referencing.operation.transform.WraparoundTransform * @see <a href="https://issues.apache.org/jira/browse/SIS-486">SIS-486</a> */ -final class LongitudeWraparound extends AbstractMathTransform2D implements Serializable { +final class LongitudeWraparound extends AbstractMathTransform2D implements Serializable, ExportableTransform { /** * For cross-version compatibility. */ @@ -226,13 +227,18 @@ final class LongitudeWraparound extends AbstractMathTransform2D implements Seria return inverse; } + @Override + public String toECMAScript() throws UnsupportedOperationException { + return projection.toECMAScript(); + } + /** * Longitude wraparound applied on reverse projection. * This is a copy of {@code NormalizedProjection.Inverse} with longitude wraparound added after conversion. * * @author Martin Desruisseaux (Geomatys) */ - private static final class Inverse extends AbstractMathTransform2D.Inverse implements Serializable { + private static final class Inverse extends AbstractMathTransform2D.Inverse implements Serializable, ExportableTransform { /** * For cross-version compatibility. */ @@ -312,6 +318,11 @@ final class LongitudeWraparound extends AbstractMathTransform2D implements Seria } } } + + @Override + public String toECMAScript() throws UnsupportedOperationException { + return forward.projection.toECMAScript(true); + } } /* diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mercator.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mercator.java index 29452d770e..f732abb301 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mercator.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/Mercator.java @@ -482,6 +482,87 @@ subst: if (variant.spherical || eccentricity == 0) { dstPts[dstOff+1] = φ(exp(-y)); } + @Override + protected String toECMAScript(boolean inverse) { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + //constants + sb.append("\t_ANGULAR_TOLERANCE : ").append(Double.toString(ANGULAR_TOLERANCE)).append(",\n"); + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + //utils functions + sb.append( + "\t_expΨ : function(φ, ℯsinφ) {\n" + + "\t\treturn Math.tan(Math.PI/4 + 0.5*φ) * Math.pow((1 - ℯsinφ) / (1 + ℯsinφ), 0.5*this._eccentricity);\n" + + "\t},\n"); + sb.append( + "\t_copySign : function(x, y) {\n" + + "\t\treturn Math.sign(x) === Math.sign(y) ? x : -x;\n" + + "\t},\n"); + + sb.append( + "\ttransform : function(src){\n" + + "\t\tlet φ = src[1];\n"+ + "\t\tlet sinφ = Math.sin(φ);\n"+ + "\t\tlet y = 0.0;\n"+ + "\t\tlet a = 0.0;\n"+ + "\t\tif (φ == 0.0) {\n"+ + "\t\t\ty = φ;\n"+ + "\t\t} else {\n"+ + "\t\t\ta = Math.abs(φ);\n"+ + "\t\t\tif (a < Math.PI / 2.0) {\n"+ + "\t\t\t\ty = Math.log(this._expΨ(φ, this._eccentricity * sinφ));\n"+ + "\t\t\t} else if (a <= (Math.PI/2 + this._ANGULAR_TOLERANCE)) {\n"+ + "\t\t\t\ty = this._copySign(Number.POSITIVE_INFINITY, φ);\n"+ + "\t\t\t} else {\n"+ + "\t\t\t\ty = NaN;\n"+ + "\t\t\t}\n"+ + "\t\t}\n"+ + "\t\treturn [src[0], y];\n" + + "\t}\n" + ); + + } else { + + final double[] exp = getExpansionFirstTerms(); + sb.append( + "\t_φ : function(rexpΨ) {\n" + + "\t\tlet φ = (Math.PI/2) - 2*Math.atan(rexpΨ);\n" + + "\t\tconst sin_2φ = Math.sin(2*φ);\n" + + "\t\tconst cos_2φ = Math.cos(2*φ);\n" + + "\t\tφ += sin_2φ * ("+Double.toString(exp[0])+" + cos_2φ * ("+Double.toString(exp[1])+" + cos_2φ * ("+Double.toString(exp[2])+" + cos_2φ * "+Double.toString(exp[3])+")));\n"); + + if (!isUseIterations()) { + sb.append( + "\t\treturn φ;\n" + + "\t},\n"); + } else { + sb.append( + "\t\tconst hℯ = 0.5 * this._eccentricity;\n" + + "\t\tfor (let it=0; it<this._MAXIMUM_ITERATIONS; it++) {\n" + + "\t\t\tconst ℯsinφ = this._eccentricity * Math.sin(φ);\n" + + "\t\t\tconst Δφ = φ - (φ = Math.PI/2 - 2*Math.atan(rexpΨ * Math.pow((1 - ℯsinφ)/(1 + ℯsinφ), hℯ)));\n" + + "\t\t\tif (!(Math.abs(Δφ) > this._ITERATION_TOLERANCE)) { // Use '!' for accepting NaN.\n" + + "\t\t\t\treturn φ;\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t\treturn Number.NaN;\n" + + "\t},\n"); + } + + + sb.append( + "\ttransform : function(src){\n" + + "\t\tlet y = src[1];\n"+ + "\t\treturn [src[0], this._φ(Math.exp(-y))];\n" + + "\t}\n" + ); + } + + sb.append("}"); + return sb.toString(); + } /** * Provides the transform equations for the spherical case of the Mercator projection. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java index 67e21ff34f..25bc92847f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java @@ -47,6 +47,7 @@ import org.apache.sis.referencing.operation.transform.ContextualParameters; import org.apache.sis.referencing.operation.transform.MathTransformProvider; import org.apache.sis.referencing.operation.transform.DomainDefinition; import org.apache.sis.referencing.operation.provider.MapProjection; +import org.apache.sis.referencing.ExportableTransform; import org.apache.sis.referencing.privy.Formulas; import org.apache.sis.system.Modules; import org.apache.sis.util.privy.Constants; @@ -123,7 +124,7 @@ import org.opengis.referencing.ReferenceIdentifier; * @see ContextualParameters * @see <a href="https://mathworld.wolfram.com/MapProjection.html">Map projections on MathWorld</a> */ -public abstract class NormalizedProjection extends AbstractMathTransform2D implements Serializable { +public abstract class NormalizedProjection extends AbstractMathTransform2D implements Serializable, ExportableTransform { /** * For cross-version compatibility. */ @@ -771,6 +772,15 @@ public abstract class NormalizedProjection extends AbstractMathTransform2D imple return inverse; } + protected String toECMAScript(boolean inverse) { + throw new UnsupportedOperationException(); + } + + @Override + public String toECMAScript() throws UnsupportedOperationException { + return toECMAScript(false); + } + /** * Reverse of a normalized map projection. * Note that a slightly modified copy of this class is in {@code LongitudeWraparound.Inverse}. @@ -778,7 +788,7 @@ public abstract class NormalizedProjection extends AbstractMathTransform2D imple * * @author Martin Desruisseaux (Geomatys) */ - private static final class Inverse extends AbstractMathTransform2D.Inverse implements Serializable { + private static final class Inverse extends AbstractMathTransform2D.Inverse implements Serializable, ExportableTransform { /** * For cross-version compatibility. */ @@ -854,6 +864,11 @@ public abstract class NormalizedProjection extends AbstractMathTransform2D imple super.tryConcatenate(context); } } + + @Override + public String toECMAScript() throws UnsupportedOperationException { + return forward.toECMAScript(true); + } } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/PolarStereographic.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/PolarStereographic.java index 62573b1b42..8fab459424 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/PolarStereographic.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/PolarStereographic.java @@ -372,6 +372,92 @@ public class PolarStereographic extends ConformalProjection { dstPts[dstOff+1] = -φ(fastHypot(x, y)); } + @Override + protected String toECMAScript(boolean inverse) { + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + //constants + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + //utils functions + sb.append( + "\t_expΨ : function(φ, ℯsinφ) {\n" + + "\t\treturn Math.tan(Math.PI/4 + 0.5*φ) * Math.pow((1 - ℯsinφ) / (1 + ℯsinφ), 0.5*this._eccentricity);\n" + + "\t},\n"); + } else { + //constants + if (isUseIterations()) { + sb.append("\t_MAXIMUM_ITERATIONS : ").append(MAXIMUM_ITERATIONS).append(",\n"); + sb.append("\t_ITERATION_TOLERANCE : ").append(Double.toString(ITERATION_TOLERANCE)).append(",\n"); + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + } + //utils functions + sb.append( + "\t_fastHypot : function(x, y) {\n" + + "\t\treturn Math.sqrt(x*x + y*y);\n" + + "\t},\n"); + + + final double[] exp = getExpansionFirstTerms(); + sb.append( + "\t_φ : function(rexpΨ) {\n" + + "\t\tlet φ = (Math.PI/2) - 2*Math.atan(rexpΨ);\n" + + "\t\tconst sin_2φ = Math.sin(2*φ);\n" + + "\t\tconst cos_2φ = Math.cos(2*φ);\n" + + "\t\tφ += sin_2φ * ("+Double.toString(exp[0])+" + cos_2φ * ("+Double.toString(exp[1])+" + cos_2φ * ("+Double.toString(exp[2])+" + cos_2φ * "+Double.toString(exp[3])+")));\n"); + + if (!isUseIterations()) { + sb.append( + "\t\treturn φ;\n" + + "\t},\n"); + } else { + sb.append( + "\t\tconst hℯ = 0.5 * this._eccentricity;\n" + + "\t\tfor (let it=0; it<this._MAXIMUM_ITERATIONS; it++) {\n" + + "\t\t\tconst ℯsinφ = this._eccentricity * Math.sin(φ);\n" + + "\t\t\tconst Δφ = φ - (φ = Math.PI/2 - 2*Math.atan(rexpΨ * Math.pow((1 - ℯsinφ)/(1 + ℯsinφ), hℯ)));\n" + + "\t\t\tif (!(Math.abs(Δφ) > this._ITERATION_TOLERANCE)) { // Use '!' for accepting NaN.\n" + + "\t\t\t\treturn φ;\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t\treturn Number.NaN;\n" + + "\t},\n"); + } + } + + + //transform function + if (!inverse) { + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst θ = src[0];\n" + + "\t\tconst φ = src[1];\n" + + "\t\tconst sinθ = Math.sin(θ);\n" + + "\t\tconst cosθ = Math.cos(θ);\n" + + "\t\tconst sinφ = Math.sin(φ);\n" + + "\t\tconst t = this._expΨ(φ, this._eccentricity*sinφ);\n" + + "\t\tconst x = t * sinθ;\n" + + "\t\tconst y = t * cosθ;\n" + + "\t\treturn [x,y];\n" + + "\t}\n" + ); + } else { + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst x = src[0];\n" + + "\t\tconst y = src[1];\n" + + "\t\tconst d0 = Math.atan2(x, y);\n" + + "\t\tconst d1 = -this._φ(this._fastHypot(x, y));\n" + + "\t\treturn [d0, d1];\n" + + "\t}\n" + ); + } + + sb.append("}"); + return sb.toString(); + } /** * Provides the transform equations for the spherical case of the polar stereographic projection. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/TransverseMercator.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/TransverseMercator.java index be6579a88a..b489105465 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/TransverseMercator.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/TransverseMercator.java @@ -733,8 +733,144 @@ public class TransverseMercator extends NormalizedProjection { throw new ProjectionException(Resources.format(Resources.Keys.NoConvergence)); } + @Override + protected String toECMAScript(boolean inverse) { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + //constants + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + sb.append("\t_cf2 : ").append(Double.toString(cf2)).append(",\n"); + sb.append("\t_cf4 : ").append(Double.toString(cf4)).append(",\n"); + sb.append("\t_cf6 : ").append(Double.toString(cf6)).append(",\n"); + sb.append("\t_cf8 : ").append(Double.toString(cf8)).append(",\n"); + + sb.append( + "\ttransform : function(src){\n" + + "\t\tlet dst = new Array("+getTargetDimensions()+");\n" + + "\t\tconst λ = src[0];\n" + + "\t\tconst φ = src[1];\n" + + "\t\tif (Math.abs(λ) > 90 * (Math.PI/180) && Math.abs(Math.IEEEremainder(λ, 2*PI)) > 90 * (Math.PI/180)) {\n" + + "\t\t\tdst[0] = Number.NaN;\n" + + "\t\t\tdst[1] = Number.NaN;\n" + + "\t\t} else {\n" + + "\t\t\tconst sinλ = Math.sin(λ);\n" + + "\t\t\tconst ℯsinφ = Math.sin(φ) * this._eccentricity;\n" + + "\t\t\tconst Q = Math.asinh(Math.tan(φ)) - Math.atanh(ℯsinφ) * this._eccentricity;\n" + + "\t\t\tconst coshQ = Math.cosh(Q);\n" + + "\t\t\tconst η0 = Math.atanh(sinλ / coshQ);\n" + + "\t\t\tconst coshη0 = Math.cosh(η0);\n" + + "\t\t\tconst ξ0 = Math.asin(Math.tanh(Q) * coshη0);\n" + + "\t\t\tconst sin_2ξ0 = Math.sin(2*ξ0);\n" + + "\t\t\tconst cos_2ξ0 = Math.cos(2*ξ0);\n" + + "\t\t\tconst sin2 = sin_2ξ0 * sin_2ξ0;\n" + + "\t\t\tconst cos2 = cos_2ξ0 * cos_2ξ0;\n" + + "\t\t\tconst sin_4ξ0 = sin_2ξ0 * cos_2ξ0;\n" + + "\t\t\tconst cos_4ξ0 = (cos2 - sin2) * 0.5;\n" + + "\t\t\tconst sin_6ξ0 = (0.75 - sin2) * sin_2ξ0;\n" + + "\t\t\tconst cos_6ξ0 = (cos2 - 0.75) * cos_2ξ0;\n" + + "\t\t\tconst sin_8ξ0 = sin_4ξ0 * cos_4ξ0;\n" + + "\t\t\tconst cos_8ξ0 = 0.125 - sin_4ξ0 * sin_4ξ0;\n" + + "\t\t\tconst sinh_2η0 = Math.sinh(2*η0);\n" + + "\t\t\tconst cosh_2η0 = Math.cosh(2*η0);\n" + + "\t\t\tconst sinh2 = sinh_2η0 * sinh_2η0;\n" + + "\t\t\tconst cosh2 = cosh_2η0 * cosh_2η0;\n" + + "\t\t\tconst cosh_4η0 = (cosh2 + sinh2) * 0.5;\n" + + "\t\t\tconst sinh_4η0 = cosh_2η0 * sinh_2η0;\n" + + "\t\t\tconst cosh_6η0 = cosh_2η0 * (cosh2 - 0.75);\n" + + "\t\t\tconst sinh_6η0 = sinh_2η0 * (sinh2 + 0.75);\n" + + "\t\t\tconst cosh_8η0 = sinh_4η0 * sinh_4η0 + 0.125;\n" + + "\t\t\tconst sinh_8η0 = sinh_4η0 * cosh_4η0;\n" + + "\t\t\t// η(λ,φ)\n" + + "\t\t\tdst[0] = this._cf8 * cos_8ξ0 * sinh_8η0\n" + + "\t\t\t + this._cf6 * cos_6ξ0 * sinh_6η0\n" + + "\t\t\t + this._cf4 * cos_4ξ0 * sinh_4η0\n" + + "\t\t\t + this._cf2 * cos_2ξ0 * sinh_2η0\n" + + "\t\t\t + η0;\n" + + "\t\t\t// ξ(λ,φ)\n" + + "\t\t\tdst[1] = this._cf8 * sin_8ξ0 * cosh_8η0\n" + + "\t\t\t + this._cf6 * sin_6ξ0 * cosh_6η0\n" + + "\t\t\t + this._cf4 * sin_4ξ0 * cosh_4η0\n" + + "\t\t\t + this._cf2 * sin_2ξ0 * cosh_2η0\n" + + "\t\t\t + ξ0;\n" + + "\t\t}\n" + + "\t\treturn dst;\n" + + "\t}\n" + ); + } else { + //constants + sb.append("\t_ITERATION_TOLERANCE : ").append(Double.toString(ITERATION_TOLERANCE)).append(",\n"); + sb.append("\t_MAXIMUM_ITERATIONS : ").append(MAXIMUM_ITERATIONS).append(",\n"); + sb.append("\t_eccentricity : ").append(Double.toString(eccentricity)).append(",\n"); + sb.append("\t_ci2 : ").append(Double.toString(ci2)).append(",\n"); + sb.append("\t_ci4 : ").append(Double.toString(ci4)).append(",\n"); + sb.append("\t_ci6 : ").append(Double.toString(ci6)).append(",\n"); + sb.append("\t_ci8 : ").append(Double.toString(ci8)).append(",\n"); + + sb.append( + "\ttransform : function(src){\n" + + "\t\tlet dst = new Array("+getTargetDimensions()+");\n" + + "\t\tconst η = src[0];\n" + + "\t\tconst ξ = src[1];\n" + + "\t\tconst sin_2ξ = Math.sin(2*ξ);\n" + + "\t\tconst cos_2ξ = Math.cos(2*ξ);\n" + + "\t\tconst sinh_2η = Math.sinh(2*η);\n" + + "\t\tconst cosh_2η = Math.cosh(2*η);\n" + + "\t\tconst sin2 = sin_2ξ * sin_2ξ;\n" + + "\t\tconst cos2 = cos_2ξ * cos_2ξ;\n" + + "\t\tconst sin_4ξ = sin_2ξ * cos_2ξ;\n" + + "\t\tconst cos_4ξ = (cos2 - sin2) * 0.5;\n" + + "\t\tconst sin_6ξ = (0.75 - sin2) * sin_2ξ;\n" + + "\t\tconst cos_6ξ = (cos2 - 0.75) * cos_2ξ;\n" + + "\t\tconst sin_8ξ = sin_4ξ * cos_4ξ;\n" + + "\t\tconst cos_8ξ = 0.125 - sin_4ξ * sin_4ξ;\n" + + "\t\t\n" + + "\t\tconst sinh2 = sinh_2η * sinh_2η;\n" + + "\t\tconst cosh2 = cosh_2η * cosh_2η;\n" + + "\t\tconst cosh_4η = (cosh2 + sinh2) * 0.5;\n" + + "\t\tconst sinh_4η = cosh_2η * sinh_2η;\n" + + "\t\tconst cosh_6η = cosh_2η * (cosh2 - 0.75);\n" + + "\t\tconst sinh_6η = sinh_2η * (sinh2 + 0.75);\n" + + "\t\tconst cosh_8η = sinh_4η * sinh_4η + 0.125;\n" + + "\t\tconst sinh_8η = sinh_4η * cosh_4η;\n" + + "\t\tconst ξ0 = ξ - (this._ci8 * sin_8ξ * cosh_8η\n" + + "\t\t + this._ci6 * sin_6ξ * cosh_6η\n" + + "\t\t + this._ci4 * sin_4ξ * cosh_4η\n" + + "\t\t + this._ci2 * sin_2ξ * cosh_2η);\n" + + "\t\t\n" + + "\t\tconst η0 = η - (this._ci8 * cos_8ξ * sinh_8η\n" + + "\t\t + this._ci6 * cos_6ξ * sinh_6η\n" + + "\t\t + this._ci4 * cos_4ξ * sinh_4η\n" + + "\t\t + this._ci2 * cos_2ξ * sinh_2η);\n" + + "\t\t\n" + + "\t\tconst β = Math.asin(Math.sin(ξ0) / Math.cosh(η0));\n" + + "\t\tconst Q = Math.asinh(Math.tan(β));\n" + + "\t\tlet p = this._eccentricity * Math.atanh(this._eccentricity * Math.tanh(Q));\n" + + "\t\tlet Qp = Q + p;\n" + + "\t\tlet found = false;\n" + + "\t\tfor (let it=0; !found && it<this._MAXIMUM_ITERATIONS; it++) {\n" + + "\t\t\tconst c = this._eccentricity * Math.atanh(this._eccentricity * Math.tanh(Qp));\n" + + "\t\t\tQp = Q + c;\n" + + "\t\t\tif (Math.abs(c - p) <= this._ITERATION_TOLERANCE) {\n" + + "\t\t\t\tdst[0] = Math.asin(Math.tanh(η0) / Math.cos(β));\n" + + "\t\t\t\tdst[1] = Math.atan(Math.sinh(Qp));\n" + + "\t\t\t\tfound = true;\n" + + "\t\t\t}\n" + + "\t\t\tp = c;\n" + + "\t\t}\n" + + "\t\tif (!found) {\n" + + "\t\t\tdst[0] = Number.NaN;\n" + + "\t\t\tdst[1] = Number.NaN;\n" + + "\t\t}\n" + + "\t\treturn dst;\n" + + "\t}\n" + ); + } - + sb.append("}"); + return sb.toString(); + } /** * Provides the transform equations for the spherical case of the Transverse Mercator projection. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java index 6388514777..25b86cab24 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/ConcatenatedTransform.java @@ -46,6 +46,7 @@ import org.apache.sis.util.privy.Strings; import org.apache.sis.io.wkt.Convention; import org.apache.sis.io.wkt.Formatter; import org.apache.sis.io.wkt.FormattableObject; +import org.apache.sis.referencing.ExportableTransform; import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Errors; @@ -65,7 +66,7 @@ import org.opengis.geometry.MismatchedDimensionException; * * @see MathTransformFactory#createConcatenatedTransform(MathTransform, MathTransform) */ -class ConcatenatedTransform extends AbstractMathTransform implements Serializable { +class ConcatenatedTransform extends AbstractMathTransform implements Serializable, ExportableTransform { /** * Serial number for inter-operability with different versions. */ @@ -841,6 +842,39 @@ class ConcatenatedTransform extends AbstractMathTransform implements Serializabl } while (numPts != 0); } + @Override + public String toECMAScript() throws UnsupportedOperationException { + final int sub1DimSource = transform1.getSourceDimensions(); + final int sub2DimSource = transform2.getSourceDimensions(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (transform1 instanceof ExportableTransform) { + final ExportableTransform exp = (ExportableTransform) transform1; + sb.append("\t_sub1 : ").append(exp.toECMAScript().replace("\n", "\n\t")).append(",\n"); + } else { + throw new UnsupportedOperationException(transform1.getClass().getName() + " is not an ExportableTransform."); + } + + if (transform2 instanceof ExportableTransform) { + final ExportableTransform exp = (ExportableTransform) transform2; + sb.append("\t_sub2 : ").append(exp.toECMAScript().replace("\n", "\n\t")).append(",\n"); + } else { + throw new UnsupportedOperationException(transform2.getClass().getName() + " is not an ExportableTransform."); + } + + sb.append( + "\ttransform : function(src) {\n" + + "\t\tsrc = this._sub1.transform(src);\n" + + "\t\tsrc = this._sub2.transform(src);\n" + + "\t\treturn src;\n" + + "\t}\n"); + + sb.append("}"); + return sb.toString(); + } + /** * Gets the derivative of this transform at a point. * diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java index 49cc90c86c..46ef6c71e6 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/EllipsoidToCentricTransform.java @@ -16,50 +16,51 @@ */ package org.apache.sis.referencing.operation.transform; -import java.util.Map; -import java.util.List; -import java.util.Arrays; import java.io.Serializable; import static java.lang.Math.*; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import javax.measure.Unit; import javax.measure.quantity.Length; -import org.opengis.util.FactoryException; +import org.apache.sis.metadata.iso.citation.Citations; +import org.apache.sis.parameter.ParameterBuilder; +import org.apache.sis.parameter.Parameters; +import org.apache.sis.referencing.ExportableTransform; +import org.apache.sis.referencing.datum.DefaultEllipsoid; +import org.apache.sis.referencing.internal.Resources; +import org.apache.sis.referencing.operation.matrix.Matrices; +import org.apache.sis.referencing.operation.matrix.Matrix3; +import org.apache.sis.referencing.operation.matrix.MatrixSIS; +import static org.apache.sis.referencing.operation.provider.GeocentricAffineBetweenGeographic.DIMENSION; +import org.apache.sis.referencing.operation.provider.GeocentricToGeographic; +import org.apache.sis.referencing.operation.provider.Geographic3Dto2D; +import org.apache.sis.referencing.operation.provider.GeographicToGeocentric; +import org.apache.sis.referencing.operation.provider.MapProjection; +import static org.apache.sis.referencing.operation.provider.MapProjection.ECCENTRICITY; +import org.apache.sis.referencing.privy.DirectPositionView; +import org.apache.sis.referencing.privy.Formulas; +import org.apache.sis.referencing.privy.ReferencingUtilities; +import org.apache.sis.util.ArgumentChecks; +import org.apache.sis.util.ComparisonMode; +import org.apache.sis.util.Debug; +import org.apache.sis.util.privy.Constants; +import org.apache.sis.util.privy.DoubleDouble; +import org.apache.sis.util.privy.Numerics; +import org.apache.sis.util.resources.Errors; import org.opengis.geometry.DirectPosition; -import org.opengis.parameter.ParameterValueGroup; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.referencing.cs.CartesianCS; +import org.opengis.referencing.cs.CoordinateSystem; +import org.opengis.referencing.cs.SphericalCS; import org.opengis.referencing.datum.Ellipsoid; -import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.MathTransformFactory; +import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.TransformException; -import org.opengis.referencing.cs.CoordinateSystem; -import org.opengis.referencing.cs.CartesianCS; -import org.opengis.referencing.cs.SphericalCS; -import org.apache.sis.util.Debug; -import org.apache.sis.util.ComparisonMode; -import org.apache.sis.util.ArgumentChecks; -import org.apache.sis.util.privy.Numerics; -import org.apache.sis.util.privy.Constants; -import org.apache.sis.util.privy.DoubleDouble; -import org.apache.sis.util.resources.Errors; -import org.apache.sis.referencing.privy.Formulas; -import org.apache.sis.referencing.privy.DirectPositionView; -import org.apache.sis.referencing.internal.Resources; -import org.apache.sis.referencing.datum.DefaultEllipsoid; -import org.apache.sis.referencing.operation.matrix.Matrix3; -import org.apache.sis.referencing.operation.matrix.Matrices; -import org.apache.sis.referencing.operation.matrix.MatrixSIS; -import org.apache.sis.referencing.operation.provider.MapProjection; -import org.apache.sis.referencing.operation.provider.GeocentricToGeographic; -import org.apache.sis.referencing.operation.provider.GeographicToGeocentric; -import org.apache.sis.referencing.operation.provider.Geographic3Dto2D; -import org.apache.sis.referencing.privy.ReferencingUtilities; -import org.apache.sis.parameter.ParameterBuilder; -import org.apache.sis.parameter.Parameters; -import org.apache.sis.metadata.iso.citation.Citations; -import static org.apache.sis.referencing.operation.provider.MapProjection.ECCENTRICITY; -import static org.apache.sis.referencing.operation.provider.GeocentricAffineBetweenGeographic.DIMENSION; +import org.opengis.util.FactoryException; /** @@ -103,7 +104,7 @@ import static org.apache.sis.referencing.operation.provider.GeocentricAffineBetw * @version 1.5 * @since 0.7 */ -public class EllipsoidToCentricTransform extends AbstractMathTransform implements Serializable { +public class EllipsoidToCentricTransform extends AbstractMathTransform implements Serializable, ExportableTransform { /** * Serial number for inter-operability with different versions. */ @@ -946,8 +947,98 @@ public class EllipsoidToCentricTransform extends AbstractMathTransform implement return false; } + @Override + public String toECMAScript() throws UnsupportedOperationException { + return toECMAScript(false); + } + + private String toECMAScript(boolean inverse) throws UnsupportedOperationException { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + if (!inverse) { + sb.append( + "\ttransform : function(src){\n" + + "\t\tconst eccentricitySquared = " + Double.toString(eccentricitySquared) + ";\n" + + "\t\tconst λ = src[0];// Longitude\n" + + "\t\tconst φ = src[1];// Latitude\n" + + "\t\tconst h = " + (withHeight ? "src[2]" : "0.0") + ";// Height above the ellipsoid\n" + + "\t\tconst sinφ = Math.sin(φ);\n" + + "\t\tconst ν = 1.0/Math.sqrt(1 - eccentricitySquared * (sinφ*sinφ)); // Prime vertical radius of curvature at latitude φ\n" + + "\t\tconst rcosφ = (ν + h) * Math.cos(φ);\n" + + "\t\tconst d0 = rcosφ * Math.cos(λ); // X: Toward prime meridian\n" + + "\t\tconst d1 = rcosφ * Math.sin(λ); // Y: Toward 90° east\n" + + "\t\tconst d2 = (ν * (1 - eccentricitySquared) + h) * sinφ; // Z: Toward north pole\n" + + "\t\treturn [d0,d1,d2];\n" + + "\t}\n" + ); + } else { + //constants + sb.append("\t_ANGULAR_TOLERANCE : ").append(Double.toString(Formulas.ANGULAR_TOLERANCE)).append(",\n"); + sb.append("\t_MAXIMUM_ITERATIONS : ").append(Formulas.MAXIMUM_ITERATIONS).append(",\n"); + sb.append("\t_eccentricitySquared : ").append(Double.toString(eccentricitySquared)).append(",\n"); + sb.append("\t_axisRatio : ").append(Double.toString(axisRatio)).append(",\n"); + //utils functions + sb.append( + "\t_copySign : function(x, y) {\n" + + "\t\treturn Math.sign(x) === Math.sign(y) ? x : -x;\n" + + "\t},\n"); + sb.append( + "\ttransform : function(src){\n" + + "\t\tlet dst = new Array("+getTargetDimensions()+");\n" + + "\t\tconst X = src[0];\n" + + "\t\tconst Y = src[1];\n" + + "\t\tconst Z = src[2];\n" + + "\t\tconst p = Math.hypot(X, Y);\n" + + "\t\tconst tanq = Z / (p*this._axisRatio);\n" + + "\t\tconst cos2q = 1.0/(1.0 + tanq*tanq);\n" + + "\t\tconst sin2q = 1.0 - cos2q;\n" + + "\t\tlet φ = Math.atan((Z + this._copySign(this._eccentricitySquared * sin2q*Math.sqrt(sin2q), tanq) / this._axisRatio) /\n" + + " (p - this._eccentricitySquared * cos2q*Math.sqrt(cos2q)));\n" + + "\t\t\n"); + if (!useIterations) { + sb.append( + "\t\tdst[0] = Math.atan2(Y, X);\n" + + "\t\tdst[1] = φ;\n"); + if (withHeight) { + sb.append( + "\t\tconst sinφ = Math.sin(φ);\n" + + "\t\tconst ν = 1.0/Math.sqrt(1 - this._eccentricitySquared * (sinφ*sinφ));\n" + + "\t\tdst[2] = p/Math.cos(φ) - ν;\n"); + } + } else { + sb.append( + "\t\tlet found = false;\n" + + "\t\tfor (let it = this._MAXIMUM_ITERATIONS; !found && it >= 0; it--) {\n" + + "\t\t\tconst sinφ = Math.sin(φ);\n" + + "\t\t\tconst ν = 1/sqrt(1 - this._eccentricitySquared * (sinφ*sinφ));\n" + + "\t\t\tconst Δφ = φ - (φ = atan((Z + this._eccentricitySquared * ν * sinφ) / p));\n" + + "\t\t\tif (!(abs(Δφ) >= this._ANGULAR_TOLERANCE * (Math.PI/180) * 0.25)) { // Use ! for accepting NaN.\n" + + "\t\t\t\tdst[0] = Math.atan2(Y, X);\n" + + "\t\t\t\tdst[1] = φ;\n"); + if (withHeight) { + sb.append( + "\t\t\t\tdst[2] = p/Math.cos(φ) - ν;\n"); + } + sb.append( + "\t\t\t\tfound = true;\n" + + "\t\t\t}\n" + + "\t\t}\n" + + "\t\tif (!found) {\n" + + "\t\t\tdst[0] = Number.NaN;\n" + + "\t\t\tdst[1] = Number.NaN;\n" + + "\t\t}\n" + ); + } + sb.append( + "\t\treturn dst;\n" + + "\t}\n"); + } + sb.append("}"); + return sb.toString(); + } /** * The descriptor of the inverse transform. @@ -963,7 +1054,7 @@ public class EllipsoidToCentricTransform extends AbstractMathTransform implement * * @author Martin Desruisseaux (IRD, Geomatys) */ - private final class Inverse extends AbstractMathTransform.Inverse implements Serializable { + private final class Inverse extends AbstractMathTransform.Inverse implements Serializable, ExportableTransform { /** * Serial number for inter-operability with different versions. */ @@ -1125,6 +1216,12 @@ public class EllipsoidToCentricTransform extends AbstractMathTransform implement } return index; } + + + @Override + public String toECMAScript() throws UnsupportedOperationException { + return EllipsoidToCentricTransform.this.toECMAScript(true); + } } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/LinearTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/LinearTransform.java index 52fb32f769..693b7f00fb 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/LinearTransform.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/LinearTransform.java @@ -16,6 +16,7 @@ */ package org.apache.sis.referencing.operation.transform; +import org.apache.sis.referencing.ExportableTransform; import org.opengis.referencing.operation.Matrix; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; @@ -71,7 +72,7 @@ import org.opengis.referencing.operation.NoninvertibleTransformException; * * @since 0.4 */ -public interface LinearTransform extends MathTransform { +public interface LinearTransform extends MathTransform, ExportableTransform{ /** * Returns {@code true} if this transform is affine. * An affine transform preserves parallelism and has the same @@ -134,4 +135,54 @@ public interface LinearTransform extends MathTransform { */ @Override LinearTransform inverse() throws NoninvertibleTransformException; + + @Override + public default String toECMAScript() throws UnsupportedOperationException { + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + sb.append("\ttransform : function(src) {\n"); + sb.append("\t\tlet dst = new Array(").append(getTargetDimensions()).append(");\n"); + + + final Matrix matrix = getMatrix(); + sb.append("\t\t/*\n\t\t").append(matrix.toString().replaceAll("\n", "\n\t\t")).append("*/\n"); + + final int sourceDimensions = getSourceDimensions(); + final int numCol = matrix.getNumCol(); + final int targetDimensions = getTargetDimensions(); + + for (int k = 0; k < targetDimensions; k++) { + sb.append("\t\tdst[").append(k).append("] = "); + boolean first = true; + for (int c = 0; c < numCol; c++) { + final double element = matrix.getElement(k, c); + if (element != 0.0) { + if (first) { + first = false; + } else { + sb.append(" + "); + } + if (c >= sourceDimensions) { + //implicite * 1 + sb.append(element); + } else { + sb.append(element).append(" * src[").append(c).append("]"); + } + } + } + if (first) { + //all matrix row values are 0.0 + sb.append("0.0"); + } + + sb.append(";\n"); + } + sb.append("\t\treturn dst;\n\t}\n"); + + sb.append("}"); + return sb.toString(); + } + + } diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java index 04746075a6..56ce03283f 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/transform/PassThroughTransform.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.BitSet; import java.io.Serializable; import java.lang.reflect.Array; +import java.util.List; import org.opengis.util.FactoryException; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.operation.Matrix; @@ -38,6 +39,7 @@ import org.apache.sis.util.ArraysExt; import org.apache.sis.util.privy.Numerics; import org.apache.sis.geometry.GeneralDirectPosition; import org.apache.sis.io.wkt.Formatter; +import org.apache.sis.referencing.ExportableTransform; import org.apache.sis.util.resources.Errors; // Specific to the main branch: @@ -75,7 +77,7 @@ import org.opengis.geometry.MismatchedDimensionException; * * @since 0.5 */ -public class PassThroughTransform extends AbstractMathTransform implements Serializable { +public class PassThroughTransform extends AbstractMathTransform implements Serializable, ExportableTransform { /** * Serial number for inter-operability with different versions. */ @@ -676,6 +678,47 @@ public class PassThroughTransform extends AbstractMathTransform implements Seria return inverse; } + @Override + public String toECMAScript() throws UnsupportedOperationException { + + final int subDimSource = subTransform.getSourceDimensions(); + final int subDimTarget = subTransform.getTargetDimensions(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + + final List<MathTransform> steps = MathTransforms.getSteps(subTransform); + + final StringBuilder trsSb = new StringBuilder(); + trsSb.append( + "\ttransform : function(src) {\n" + + "\t\tlet subsrc = [...src.slice(" + firstAffectedCoordinate +"," + (firstAffectedCoordinate+subDimSource) +")];\n"); + + for (int i = 0, n = steps.size(); i < n; i++) { + final MathTransform step = steps.get(i); + + final String stepObj; + if (step instanceof ExportableTransform) { + final ExportableTransform exp = (ExportableTransform) step; + stepObj = exp.toECMAScript(); + } else { + throw new UnsupportedOperationException(step.getClass().getName() + " is not an ExportableTransform."); + } + sb.append("\t_step").append(i).append(" : ").append(stepObj.replaceAll("\n", "\n\t")).append(",\n"); + trsSb.append("\t\tsubsrc = this._step").append(i).append(".transform(subsrc);\n"); + } + + trsSb.append( + "\t\tconst before = src.slice(0," +firstAffectedCoordinate+");\n"+ + "\t\tconst after = src.slice(-"+numTrailingCoordinates+");\n"+ + "\t\treturn before.concat(subsrc).concat(after);\n" + + "\t}\n"); + + sb.append(trsSb.toString()); + sb.append("}"); + return sb.toString(); + } + /** * If the given matrix to be concatenated to this transform, can be concatenated to the * sub-transform instead, returns the matrix to be concatenated to the sub-transform.
