Author: desruisseaux
Date: Tue Mar 29 11:24:13 2016
New Revision: 1737000
URL: http://svn.apache.org/viewvc?rev=1737000&view=rev
Log:
Test the GeographicCRS to ProjectedCRS case without datum or axis changes.
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java?rev=1737000&r1=1736999&r2=1737000&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/AbstractParser.java
[UTF-8] Tue Mar 29 11:24:13 2016
@@ -34,10 +34,13 @@ import javax.measure.unit.UnitFormat;
import org.opengis.util.FactoryException;
import org.opengis.util.InternationalString;
import org.apache.sis.internal.system.Loggers;
+import org.apache.sis.internal.util.LocalizedParseException;
import org.apache.sis.internal.util.StandardDateFormat;
import org.apache.sis.measure.Units;
import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.CharSequences;
import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
@@ -55,7 +58,7 @@ import static org.apache.sis.util.Argume
* @author Rémi Eve (IRD)
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.6
- * @version 0.6
+ * @version 0.7
* @module
*/
abstract class AbstractParser implements Parser {
@@ -227,6 +230,20 @@ abstract class AbstractParser implements
}
/**
+ * Returns the index after the end of the fragment name starting at the
given index.
+ * Current implementation assumes that the fragment name is a Unicode
identifier.
+ */
+ static int endOfFragmentName(final String text, int upper) {
+ final int length = text.length();
+ while (upper < length) {
+ final int c = text.codePointAt(upper);
+ if (!Character.isUnicodeIdentifierPart(c)) break;
+ upper += Character.charCount(c);
+ }
+ return upper;
+ }
+
+ /**
* Parses a <cite>Well Know Text</cite> (WKT).
*
* @param text The text to be parsed.
@@ -238,7 +255,22 @@ abstract class AbstractParser implements
warnings = null;
ignoredElements.clear();
ArgumentChecks.ensureNonEmpty("text", text);
- final Element element = new Element("<root>", new Element(this, text,
position, null));
+ Element fragment;
+ int lower = CharSequences.skipLeadingWhitespaces(text,
position.getIndex(), text.length());
+ if (lower < text.length() && text.charAt(lower) ==
Symbols.FRAGMENT_VALUE) {
+ final int upper = endOfFragmentName(text, ++lower);
+ final String id = text.substring(lower, upper);
+ fragment = fragments.get(id);
+ if (fragment == null) {
+ position.setErrorIndex(lower);
+ throw new LocalizedParseException(errorLocale,
Errors.Keys.NoSuchValue_1, new Object[] {id}, lower);
+ }
+ position.setIndex(upper);
+ fragment = new Element(fragment);
+ } else {
+ fragment = new Element(this, text, position, null);
+ }
+ final Element element = new Element("<root>", fragment);
final Object object = parseObject(element);
element.close(ignoredElements);
return object;
Modified:
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java?rev=1737000&r1=1736999&r2=1737000&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-metadata/src/main/java/org/apache/sis/io/wkt/Element.java
[UTF-8] Tue Mar 29 11:24:13 2016
@@ -120,18 +120,18 @@ final class Element implements Serializa
keyword = name;
offset = singleton.offset;
locale = singleton.locale;
- list = new LinkedList<>(); // Needs to be a modifiable list.
+ list = new LinkedList<>(); // Needs to be
a modifiable list.
list.add(singleton);
}
/**
* Creates a modifiable copy of the given element.
*/
- private Element(final Element toCopy) {
+ Element(final Element toCopy) {
keyword = toCopy.keyword;
offset = toCopy.offset;
locale = toCopy.locale;
- list = new LinkedList<>(toCopy.list); // Needs to be a modifiable
list.
+ list = new LinkedList<>(toCopy.list); // Needs to be
a modifiable list.
final ListIterator<Object> it = list.listIterator();
while (it.hasNext()) {
final Object value = it.next();
@@ -226,12 +226,7 @@ final class Element implements Serializa
* to environment variables in Unix. If we find the "$"
character, get the identifier behind "$"
* and insert the corresponding WKT fragment here.
*/
- int upper = ++lower; // Increment of 1 is okay because
FRAGMENT_VALUE is a 'char'.
- while (upper < length) {
- final int c = text.codePointAt(upper);
- if (!Character.isUnicodeIdentifierPart(c)) break;
- upper += Character.charCount(c);
- }
+ final int upper = AbstractParser.endOfFragmentName(text,
++lower);
final String id = text.substring(lower, upper);
Element fragment = parser.fragments.get(id);
if (fragment == null) {
@@ -268,7 +263,7 @@ final class Element implements Serializa
* parsed text.
*/
final int n = Character.charCount(closingQuote);
- lower += Character.charCount(firstChar) - n; // This
will usually let 'lower' unchanged.
+ lower += Character.charCount(firstChar) - n; //
This will usually let 'lower' unchanged.
CharSequence content = null;
do {
final int upper = text.indexOf(closingQuote, lower +=
n);
@@ -276,7 +271,7 @@ final class Element implements Serializa
throw missingCharacter(closingQuote, lower,
position);
}
if (content == null) {
- content = text.substring(lower, upper); // First
text fragment, and usually the only one.
+ content = text.substring(lower, upper); //
First text fragment, and usually the only one.
} else {
/*
* We will enter in this block only if we found at
least one double quote.
@@ -309,7 +304,7 @@ final class Element implements Serializa
switch (valueType) {
case TEMPORAL: value = parser.parseDate (text,
position); break;
case NUMERIC: value = parser.parseNumber(text,
position); break;
- default: throw new AssertionError(valueType); //
Should never happen.
+ default: throw new AssertionError(valueType);
// Should never happen.
}
if (value == null) {
// Do not update the error index; it is already
updated by NumberFormat.
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java?rev=1737000&r1=1736999&r2=1737000&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/CoordinateOperationInference.java
[UTF-8] Tue Mar 29 11:24:13 2016
@@ -727,6 +727,9 @@ public class CoordinateOperationInferenc
}
}
properties.put(ReferencingServices.OPERATION_TYPE_KEY, type);
+ if (Conversion.class.isAssignableFrom(type)) {
+ properties.replace(IdentifiedObject.NAME_KEY, AXIS_CHANGES,
IDENTITY);
+ }
return factorySIS.createSingleOperation(properties, sourceCRS,
targetCRS, null, method, transform);
}
@@ -763,25 +766,26 @@ public class CoordinateOperationInferenc
final CoordinateOperation step2)
throws FactoryException
{
- if (isIdentity(step1)) return step2;
- if (isIdentity(step2)) return step1;
final MathTransform mt1 = step1.getMathTransform();
final MathTransform mt2 = step2.getMathTransform();
+ if (step1 instanceof Conversion && mt1.isIdentity()) return step2;
+ if (step2 instanceof Conversion && mt2.isIdentity()) return step1;
final CoordinateReferenceSystem sourceCRS = step1.getSourceCRS();
final CoordinateReferenceSystem targetCRS = step2.getTargetCRS();
- CoordinateOperation step = null;
- if (step1.getName() == AXIS_CHANGES && mt1.getSourceDimensions() ==
mt1.getTargetDimensions()) step = step2;
- if (step2.getName() == AXIS_CHANGES && mt2.getSourceDimensions() ==
mt2.getTargetDimensions()) step = step1;
- if (step instanceof SingleOperation) {
- /*
- * Applies only on operation in order to avoid merging with
PassThroughOperation.
- * Also applies only if the transform to hide has identical source
and target
- * dimensions in order to avoid mismatch with the method's
dimensions.
- */
- final MathTransformFactory mtFactory =
factorySIS.getMathTransformFactory();
- return createFromMathTransform(new
HashMap<>(IdentifiedObjects.getProperties(step)),
- sourceCRS, targetCRS,
mtFactory.createConcatenatedTransform(mt1, mt2),
- ((SingleOperation) step).getMethod(),
SingleOperation.class);
+ /*
+ * If one of the transform performs nothing more than a change of axis
order or units, do
+ * not expose that conversion in a ConcatenatedTransform. Instead,
merge that conversion
+ * with the "main" operation. The intend is to simplify the operation
chain by hidding
+ * trivial operations.
+ */
+ CoordinateOperation main = null;
+ if (step1.getName() == AXIS_CHANGES && mt1.getSourceDimensions() ==
mt1.getTargetDimensions()) main = step2;
+ if (step2.getName() == AXIS_CHANGES && mt2.getSourceDimensions() ==
mt2.getTargetDimensions()) main = step1;
+ if (main instanceof SingleOperation) {
+ final MathTransform mt =
factorySIS.getMathTransformFactory().createConcatenatedTransform(mt1, mt2);
+ return createFromMathTransform(new
HashMap<>(IdentifiedObjects.getProperties(main)),
+ sourceCRS, targetCRS, mt, ((SingleOperation)
main).getMethod(),
+ (main instanceof Transformation) ? Transformation.class :
SingleOperation.class);
}
return factory.createConcatenatedOperation(defaultName(sourceCRS,
targetCRS), step1, step2);
}
@@ -818,7 +822,7 @@ public class CoordinateOperationInferenc
* are usually datum shift and must be visible.
*/
private static boolean isIdentity(final CoordinateOperation operation) {
- return (operation == null) || ((operation instanceof Conversion) &&
operation.getMathTransform().isIdentity());
+ return (operation instanceof Conversion) &&
operation.getMathTransform().isIdentity();
}
/**
Modified:
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
URL:
http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java?rev=1737000&r1=1736999&r2=1737000&view=diff
==============================================================================
---
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
[UTF-8] (original)
+++
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/CoordinateOperationInferenceTest.java
[UTF-8] Tue Mar 29 11:24:13 2016
@@ -16,19 +16,29 @@
*/
package org.apache.sis.referencing.operation;
+import java.text.ParseException;
import org.opengis.util.FactoryException;
+import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.referencing.operation.Projection;
+import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.io.wkt.WKTFormat;
+
+import static org.apache.sis.internal.referencing.Formulas.LINEAR_TOLERANCE;
+import static org.apache.sis.internal.referencing.Formulas.ANGULAR_TOLERANCE;
// Test dependencies
import org.apache.sis.referencing.operation.transform.MathTransformTestCase;
+import org.apache.sis.test.DependsOnMethod;
import org.apache.sis.test.DependsOn;
import org.junit.BeforeClass;
import org.junit.AfterClass;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.opengis.test.Assert.*;
/**
@@ -47,17 +57,33 @@ import static org.junit.Assert.*;
})
public final strictfp class CoordinateOperationInferenceTest extends
MathTransformTestCase {
/**
+ * Tolerance threshold for strict comparisons of floating point numbers.
+ * This constant can be used like below, where {@code expected} and {@code
actual} are {@code double} values:
+ *
+ * {@preformat java
+ * assertEquals(expected, actual, STRICT);
+ * }
+ */
+ private static final double STRICT = 0;
+
+ /**
* The transformation factory to use for testing.
*/
private static DefaultCoordinateOperationFactory factory;
/**
+ * The parser to use for WKT strings used in this test.
+ */
+ private static WKTFormat parser;
+
+ /**
* Creates a new {@link DefaultCoordinateOperationFactory} to use for
testing purpose.
* The same factory will be used for all tests in this class.
*/
@BeforeClass
public static void createFactory() {
factory = new DefaultCoordinateOperationFactory();
+ parser = new WKTFormat(null, null);
}
/**
@@ -66,6 +92,14 @@ public final strictfp class CoordinateOp
@AfterClass
public static void disposeFactory() {
factory = null;
+ parser = null;
+ }
+
+ /**
+ * Returns the CRS for the given Well Known Text.
+ */
+ private static CoordinateReferenceSystem parse(final String wkt) throws
ParseException {
+ return (CoordinateReferenceSystem) parser.parseObject(wkt);
}
/**
@@ -92,4 +126,66 @@ public final strictfp class CoordinateOp
assertSame("targetCRS", crs, operation.getTargetCRS());
assertTrue("isIdentity", operation.getMathTransform().isIdentity());
}
+
+ /**
+ * Tests conversion from a geographic to a projected CRS without datum of
axis changes.
+ *
+ * @throws ParseException if the CRS used in this test can not be parsed.
+ * @throws FactoryException if the operation can not be created.
+ * @throws TransformException if an error occurred while converting the
test points.
+ */
+ @Test
+ @DependsOnMethod("testIdentityTransform")
+ public void testGeographicToProjected() throws ParseException,
FactoryException, TransformException {
+ /*
+ * The fist keyword in WKT below should be "GeodeticCRS" in WKT 2, but
we use the WKT 1 keyword ("GEOGCS")
+ * for allowing inclusion in ProjectedCRS. SIS is okay with mixed WKT
versions, but this is of course not
+ * something to recommend in production.
+ */
+ parser.addFragment("Sphere",
+ "GEOGCS[“Sphere”,\n" +
+ " Datum[“Sphere”, Ellipsoid[“Sphere”, 6370997, 0]],\n" +
+ " CS[ellipsoidal, 2],\n" +
+ " Axis[“Longitude (λ)”, EAST],\n" +
+ " Axis[“Latitude (φ)”, NORTH],\n" +
+ " Unit[“degree”, 0.017453292519943295]]");
+
+ final CoordinateReferenceSystem sourceCRS = parse("$Sphere");
+ final CoordinateReferenceSystem targetCRS = parse(
+ "ProjectedCRS[“UTM”,\n" +
+ " $Sphere,\n" +
+ " Conversion[“UTM”,\n" +
+ " Method[“Transverse Mercator”],\n" +
+ " Parameter[“Longitude of natural origin”, 170],\n" +
+ " Parameter[“Latitude of natural origin”, 50],\n" +
+ " Parameter[“Scale factor at natural origin”, 0.95]],\n" +
+ " CS[Cartesian, 2],\n" +
+ " Axis[“x”, EAST],\n" +
+ " Axis[“y”, NORTH],\n" +
+ " Unit[“US survey foot”, 0.304800609601219]]");
+
+ final CoordinateOperation operation =
factory.createOperation(sourceCRS, targetCRS);
+ assertSame("sourceCRS", sourceCRS, operation.getSourceCRS());
+ assertSame("targetCRS", targetCRS, operation.getTargetCRS());
+ assertInstanceOf("operation", Projection.class, operation);
+
+ final ParameterValueGroup param = ((SingleOperation)
operation).getParameterValues();
+ assertEquals("semi_major", 6370997, param.parameter("semi_major"
).doubleValue(), STRICT);
+ assertEquals("semi_minor", 6370997, param.parameter("semi_minor"
).doubleValue(), STRICT);
+ assertEquals("latitude_of_origin", 50,
param.parameter("latitude_of_origin").doubleValue(), STRICT);
+ assertEquals("central_meridian", 170,
param.parameter("central_meridian" ).doubleValue(), STRICT);
+ assertEquals("scale_factor", 0.95, param.parameter("scale_factor"
).doubleValue(), STRICT);
+ assertEquals("false_easting", 0,
param.parameter("false_easting" ).doubleValue(), STRICT);
+ assertEquals("false_northing", 0,
param.parameter("false_northing" ).doubleValue(), STRICT);
+
+ transform = operation.getMathTransform();
+ tolerance = ANGULAR_TOLERANCE;
+ verifyTransform(new double[] {170, 50}, new double[] {0, 0});
+ validate();
+
+ transform = transform.inverse();
+ tolerance = LINEAR_TOLERANCE;
+ verifyTransform(new double[] {0, 0}, new double[] {170, 50});
+ validate();
+ }
}