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 08239e9570 Fix the extensive tests with EPSG 9.9.1. This commit does
not include the fixes for EPSG 12.
08239e9570 is described below
commit 08239e9570e059bced5c2c85ab26dbc435e2a252
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Aug 21 18:31:39 2025 +0200
Fix the extensive tests with EPSG 9.9.1.
This commit does not include the fixes for EPSG 12.
---
.../main/org/apache/sis/io/wkt/Formatter.java | 2 +-
.../apache/sis/io/wkt/GeodeticObjectParser.java | 125 ++++++++++-----------
.../sis/referencing/crs/DefaultGeodeticCRS.java | 2 +-
.../referencing/datum/DefaultVerticalDatum.java | 22 +++-
.../sis/referencing/factory/sql/SQLTranslator.java | 4 +-
.../referencing/internal/VerticalDatumTypes.java | 11 +-
.../operation/AbstractCoordinateOperation.java | 4 +-
.../sis/referencing/privy/AxisDirections.java | 4 +-
.../factory/GeodeticObjectFactoryTest.java | 3 +-
.../sis/test/integration/ConsistencyTest.java | 11 +-
.../main/org/apache/sis/util/CharSequences.java | 66 +++++++----
.../org/apache/sis/util/CharSequencesTest.java | 5 +-
12 files changed, 149 insertions(+), 110 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 513ac1491a..345354868a 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -1714,7 +1714,7 @@ public class Formatter implements Localized {
}
/**
- * Returns the enclosing WKT element, or {@code null} if element being
formatted is the root.
+ * Returns the enclosing <abbr>WKT</abbr> element, or {@code null} if the
element being formatted is the root.
* This method can be invoked by child elements having some aspects that
depend on the enclosing element.
*
* @param depth 1 for the immediate parent, 2 for the parent of the
parent, <i>etc.</i>
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
index 796b7abe65..da10560a59 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/GeodeticObjectParser.java
@@ -683,13 +683,16 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
* @param dimension the minimal number of dimensions. Can be 1 if
unknown.
* @param isWKT1 {@code true} if the parent element is an element
from the WKT 1 standard.
* @param defaultUnit the contextual unit (usually {@code Units.METRE}
or {@code Units.RADIAN}), or {@code null} if unknown.
- * @param datum the datum of the enclosing CRS, or {@code null} if
unknown.
+ * @param geodetic whether the datum of the enclosing
<abbr>CRS</abbr> is geodetic.
+ * @param vertical the realization method (formally known as vertical
datum type), or {@code null} if unknown or not applicable.
* @return the {@code "CS"}, {@code "UNIT"} and/or {@code "AXIS"} elements
as a Coordinate System, or {@code null}.
* @throws ParseException if an element cannot be parsed.
* @throws FactoryException if the factory cannot create the coordinate
system.
*/
private CoordinateSystem parseCoordinateSystem(final Element parent,
String type, int dimension,
- final boolean isWKT1, final Unit<?> defaultUnit, final Datum
datum) throws ParseException, FactoryException
+ final boolean isWKT1, final
Unit<?> defaultUnit,
+ final boolean geodetic,
final RealizationMethod vertical)
+ throws ParseException, FactoryException
{
axisOrder.clear();
final boolean is3D = (dimension >= 3);
@@ -774,7 +777,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
* are for two- or three-dimensional Projected or
three-dimensional Geocentric CRS.
*/
case WKTKeywords.Cartesian: {
- if (datum != null && !(datum instanceof GeodeticDatum)) {
+ if (!geodetic) {
throw parent.missingComponent(WKTKeywords.Axis);
}
if (defaultUnit == null) {
@@ -829,20 +832,17 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
z = "h";
nz = "Height";
direction = AxisDirection.UP;
- if (datum instanceof VerticalDatum) {
- final RealizationMethod vt = ((VerticalDatum)
datum).getRealizationMethod().orElse(null);
- if (vt == RealizationMethod.GEOID) {
- nz = AxisNames.GRAVITY_RELATED_HEIGHT;
- z = "H";
- } else if (vt == RealizationMethod.TIDAL) {
- direction = AxisDirection.DOWN;
- nz = AxisNames.DEPTH;
- z = "D";
- } else if (VerticalDatumTypes.ellipsoidal(vt)) {
- // Not allowed by ISO 19111 as a standalone axis,
but SIS is
- // tolerant to this case since it is sometimes
hard to avoid.
- nz = AxisNames.ELLIPSOIDAL_HEIGHT;
- }
+ if (vertical == RealizationMethod.GEOID) {
+ nz = AxisNames.GRAVITY_RELATED_HEIGHT;
+ z = "H";
+ } else if (vertical == RealizationMethod.TIDAL) {
+ direction = AxisDirection.DOWN;
+ nz = AxisNames.DEPTH;
+ z = "D";
+ } else if (VerticalDatumTypes.ellipsoidal(vertical)) {
+ // Not allowed by ISO 19111 as a standalone axis, but
SIS is
+ // tolerant to this case since it is sometimes hard to
avoid.
+ nz = AxisNames.ELLIPSOIDAL_HEIGHT;
}
break;
}
@@ -894,7 +894,8 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
if (type != null && !type.isEmpty()) {
final int c = type.codePointAt(0);
buffer.appendCodePoint(Character.toUpperCase(c))
- .append(type, Character.charCount(c),
type.length()).append(' ');
+ .append(type, Character.charCount(c), type.length())
+ .append(' ');
}
name = AxisDirections.appendTo(buffer.append("CS"), axes);
}
@@ -1643,7 +1644,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final CRSFactory crsFactory = factories.getCRSFactory();
try {
- final CoordinateSystem cs = parseCoordinateSystem(element, null,
1, isWKT1, unit, datum);
+ final CoordinateSystem cs = parseCoordinateSystem(element, null,
1, isWKT1, unit, false, null);
final Map<String,Object> properties =
parseMetadataAndClose(element, name, datum);
if (baseCRS != null) {
properties.put(Legacy.DERIVED_TYPE_KEY, EngineeringCRS.class);
@@ -1675,7 +1676,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
final Unit<?> unit = parseUnit(element);
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 2,
false, unit, datum);
+ cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 2,
false, unit, false, null);
final Map<String,?> properties = parseMetadataAndClose(element,
name, datum);
if (cs instanceof AffineCS) {
return new DefaultImageCRS(properties, datum, (AffineCS) cs);
@@ -1824,7 +1825,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
final CRSFactory crsFactory = factories.getCRSFactory();
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, csType, dimension, isWKT1,
csUnit, null);
+ cs = parseCoordinateSystem(element, csType, dimension, isWKT1,
csUnit, true, null);
if (baseCRS != null) {
final Map<String,?> properties =
parseMetadataAndClose(element, name, null);
return crsFactory.createDerivedCRS(properties, baseCRS,
fromBase, cs);
@@ -1924,7 +1925,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1,
isWKT1, unit, datum);
+ cs = parseCoordinateSystem(element, WKTKeywords.vertical, 1,
isWKT1, unit, false, datum.getRealizationMethod().orElse(null));
final Map<String,?> properties = parseMetadataAndClose(element,
name, datum);
if (cs instanceof VerticalCS) {
final CRSFactory crsFactory = factories.getCRSFactory();
@@ -2005,7 +2006,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.temporal, 1,
false, unit, datum);
+ cs = parseCoordinateSystem(element, WKTKeywords.temporal, 1,
false, unit, false, null);
final Map<String,?> properties = parseMetadataAndClose(element,
name, datum);
if (cs instanceof TimeCS) {
final CRSFactory crsFactory = factories.getCRSFactory();
@@ -2067,7 +2068,7 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.parametric, 1,
false, unit, datum);
+ cs = parseCoordinateSystem(element, WKTKeywords.parametric, 1,
false, unit, false, null);
final Map<String,?> properties = parseMetadataAndClose(element,
name, datum);
if (cs instanceof ParametricCS) {
final CRSFactory crsFactory = factories.getCRSFactory();
@@ -2111,55 +2112,49 @@ class GeodeticObjectParser extends MathTransformParser
implements Comparator<Coo
if (element == null) {
return null;
}
- final boolean isWKT1 = element.getKeywordIndex() == 2;
// Index of "ProjCS" above.
- final String name = element.pullString("name");
- final SingleCRS geoCRS = parseGeodeticCRS(MANDATORY, element, 2,
WKTKeywords.ellipsoidal);
- if (!(geoCRS instanceof GeodeticCRS)) {
- throw new UnparsableObjectException(errorLocale,
Errors.Keys.IllegalCRSType_1,
- new Object[] {geoCRS.getClass()}, element.offset);
- }
- /*
- * Parse the projection parameters. If a default linear unit is
specified, it will apply to
- * all parameters that do not specify explicitly a LengthUnit. If no
such crs-wide unit was
- * specified, then the default will be degrees.
- *
- * More specifically §9.3.4 in the specification said about the
default units:
- *
- * - lengths shall be given in the unit for the projected CRS axes.
- * - angles shall be given in the unit for the base geographic CRS
of the projected CRS.
- */
- Unit<Length> csUnit = parseScaledUnit(element, WKTKeywords.LengthUnit,
Units.METRE);
- final Unit<Length> linearUnit;
- final Unit<Angle> angularUnit;
- if (isWKT1 && usesCommonUnits) {
- linearUnit = Units.METRE;
- angularUnit = Units.DEGREE;
- } else {
- linearUnit = csUnit;
- angularUnit =
AxisDirections.getAngularUnit(geoCRS.getCoordinateSystem(), Units.DEGREE);
- }
- final Conversion conversion = parseDerivingConversion(MANDATORY,
element,
- isWKT1 ? null : WKTKeywords.Conversion, linearUnit,
angularUnit);
- /*
- * Parse the coordinate system. The linear unit must be specified
somewhere, either explicitly in each axis
- * or for the whole CRS with the above `csUnit` value. If `csUnit` is
null, then an exception will be thrown
- * with a message like "A LengthUnit component is missing in
ProjectedCRS".
- *
- * However, we make an exception if we are parsing a BaseProjCRS,
since the coordinate system is unspecified
- * in the WKT of base CRS. In this case only, we will default to metre.
- */
+ final boolean isWKT1 = element.getKeywordIndex() == 2;
// Index of "ProjCS" above.
+ final String name = element.pullString("name");
+ Unit<Length> csUnit = parseScaledUnit(element,
WKTKeywords.LengthUnit, Units.METRE);
if (csUnit == null && isBaseCRS) {
csUnit = Units.METRE;
+ /*
+ * Except when parsing a BaseProjCRS, the linear unit must be
specified somewhere,
+ * either explicitly in each axis or for the whole CRS with the
`csUnit` value.
+ * If `csUnit` is null, an exception will be thrown by
`parseCoordinateSystem(…)`
+ * with a message like "A LengthUnit component is missing in
ProjectedCRS".
+ */
}
final CoordinateSystem cs;
try {
- cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 2,
isWKT1, csUnit, geoCRS.getDatum());
- final Map<String,?> properties = parseMetadataAndClose(element,
name, conversion);
+ cs = parseCoordinateSystem(element, WKTKeywords.Cartesian, 2,
isWKT1, csUnit, true, null);
if (cs instanceof CartesianCS) {
+ final SingleCRS geoCRS = parseGeodeticCRS(MANDATORY, element,
cs.getDimension(), WKTKeywords.ellipsoidal);
+ if (!(geoCRS instanceof GeodeticCRS)) {
+ throw new UnparsableObjectException(errorLocale,
Errors.Keys.IllegalCRSType_1,
+ new Object[] {geoCRS.getClass()}, element.offset);
+ }
/*
- * TODO: if the CartesianCS is three-dimensional, we need to
ensure that the base CRS is also
- * three-dimensional. We could do that by parsing the CS
before to invoke `parseGeodeticCRS`.
+ * Parse the projection parameters. If a default linear unit
is specified, it will apply to
+ * all parameters that do not specify explicitly a LengthUnit.
If no such crs-wide unit was
+ * specified, then the default will be degrees.
+ *
+ * More specifically §9.3.4 in the specification said about
the default units:
+ *
+ * - lengths shall be given in the unit for the projected
CRS axes.
+ * - angles shall be given in the unit for the base
geographic CRS of the projected CRS.
*/
+ final Unit<Length> linearUnit;
+ final Unit<Angle> angularUnit;
+ if (isWKT1 && usesCommonUnits) {
+ linearUnit = Units.METRE;
+ angularUnit = Units.DEGREE;
+ } else {
+ linearUnit = csUnit;
+ angularUnit =
AxisDirections.getAngularUnit(geoCRS.getCoordinateSystem(), Units.DEGREE);
+ }
+ final Conversion conversion =
parseDerivingConversion(MANDATORY, element,
+ isWKT1 ? null : WKTKeywords.Conversion, linearUnit,
angularUnit);
+ final Map<String,?> properties =
parseMetadataAndClose(element, name, conversion);
final CRSFactory crsFactory = factories.getCRSFactory();
return crsFactory.createProjectedCRS(properties, (GeodeticCRS)
geoCRS, conversion, (CartesianCS) cs);
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
index e1ad9ffe9b..7e695e2db6 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeodeticCRS.java
@@ -253,7 +253,7 @@ class DefaultGeodeticCRS extends
AbstractSingleCRS<GeodeticDatum> implements Geo
/*
* Format the coordinate system, except if this CRS is the base CRS of
an AbstractDerivedCRS in WKT 2 format.
* This is because ISO 19162 omits the coordinate system definition of
enclosed base CRS in order to simplify
- * the WKT. The 'formatCS(…)' method may write axis unit before or
after the axes depending on whether we are
+ * the WKT. The `formatCS(…)` method may write axis unit before or
after the axes depending on whether we are
* formatting WKT version 1 or 2 respectively.
*
* Note that even if we do not format the CS, we may still write the
units if we are formatting in "simplified"
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
index 0d27307af2..58fa153b79 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
@@ -25,7 +25,10 @@ import jakarta.xml.bind.annotation.XmlRootElement;
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
import org.opengis.referencing.datum.VerticalDatum;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.io.wkt.Formatter;
+import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.xml.bind.Context;
import org.apache.sis.xml.privy.LegacyNamespaces;
@@ -215,6 +218,21 @@ public class DefaultVerticalDatum extends AbstractDatum
implements VerticalDatum
return Optional.ofNullable(method);
}
+ /**
+ * Returns the realization method if it was explicitly specified, or
otherwise tries to guess it.
+ * This method may return {@code null} if it cannot guess the method. This
is used for compatibility
+ * with legacy formats such as WKT 1 or GML 3.1, before realization method
became a formal property.
+ */
+ private RealizationMethod getOrGuessMethod(final FormattableObject parent)
{
+ return getRealizationMethod().orElseGet(() -> {
+ CoordinateSystemAxis axis = null;
+ if (parent instanceof CoordinateReferenceSystem) {
+ axis = ((CoordinateReferenceSystem)
parent).getCoordinateSystem().getAxis(0);
+ }
+ return VerticalDatumTypes.fromDatum(getName().getCode(),
getAlias(), axis);
+ });
+ }
+
/**
* A vertical reference frame in which some of the defining parameters
have time dependency.
* The parameter values are valid at the time given by the
@@ -368,7 +386,7 @@ public class DefaultVerticalDatum extends AbstractDatum
implements VerticalDatum
protected String formatTo(final Formatter formatter) {
super.formatTo(formatter);
if (formatter.getConvention().majorVersion() == 1) {
- formatter.append(VerticalDatumTypes.toLegacyCode(method));
+
formatter.append(VerticalDatumTypes.toLegacyCode(getOrGuessMethod(formatter.getEnclosingElement(1))));
return WKTKeywords.Vert_Datum;
}
return formatter.shortOrLong(WKTKeywords.VDatum,
WKTKeywords.VerticalDatum);
@@ -410,7 +428,7 @@ public class DefaultVerticalDatum extends AbstractDatum
implements VerticalDatum
if (Context.isGMLVersion(Context.current(),
LegacyNamespaces.VERSION_3_2)) {
return null;
}
- return VerticalDatumTypes.toLegacyName(method);
+ return VerticalDatumTypes.toLegacyName(getOrGuessMethod(null));
}
/**
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
index 9228a36e80..1ec4515c09 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/SQLTranslator.java
@@ -475,10 +475,12 @@ check: for (;;) {
/*
* Detect if the tables use enumeration (on PostgreSQL
database) instead of VARCHAR.
* Enumerations appear in various tables, including in a
WHERE clause for the Alias table.
+ * Note: we cannot rely on
`result.getInt(Reflection.DATA_TYPE)` because the declared type
+ * of enumeration is `Types.VARCHAR` (at least on
PostgresSQL)`.
*/
if (ENUMERATION_COLUMN.equals(column)) {
String type = result.getString(Reflection.TYPE_NAME);
- if (!CharSequences.startsWith(type, "VARCHAR", true)) {
+ if (!(CharSequences.startsWith(type, "VARCHAR", true)
|| CharSequences.startsWith(type, "CHARACTER", true))) {
if (!type.contains(identifierQuote)) {
type = identifierQuote + type +
identifierQuote;
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
index 6f7c9b7c7b..91a2b9111b 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/VerticalDatumTypes.java
@@ -246,6 +246,7 @@ public final class VerticalDatumTypes {
switch (abbreviation.charAt(0)) {
case 'h': method = ellipsoidal(); break;
case 'H': method = RealizationMethod.GEOID; break;
+ case 'd': // Fall through
case 'D': method = RealizationMethod.TIDAL; dir =
AxisDirection.DOWN; break;
default: return null;
}
@@ -272,9 +273,13 @@ public final class VerticalDatumTypes {
if (CharSequences.equalsFiltered("Mean Sea Level", name,
Characters.Filter.LETTERS_AND_DIGITS, true)) {
return RealizationMethod.TIDAL;
}
- if (name.regionMatches(true, 0, "geoid", 0, 5)) {
- return RealizationMethod.GEOID;
- }
+ int i = 0;
+ do {
+ if (name.regionMatches(true, i, "geoid", 0, 5)) {
+ return RealizationMethod.GEOID;
+ }
+ i = name.indexOf(' ', i) + 1;
+ } while (i > 0);
}
return null;
}
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 463ab03524..3619628b83 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@ -830,7 +830,7 @@ check: for (int isTarget=0; ; isTarget++) { //
0 == source check; 1
public boolean equals(final Object object, ComparisonMode mode) {
if (super.equals(object, mode)) {
if (mode == ComparisonMode.STRICT) {
- final AbstractCoordinateOperation that =
(AbstractCoordinateOperation) object;
+ final var that = (AbstractCoordinateOperation) object;
if (Objects.equals(sourceCRS,
that.sourceCRS) &&
Objects.equals(interpolationCRS,
that.interpolationCRS) &&
Objects.equals(transform,
that.transform) &&
@@ -854,7 +854,7 @@ check: for (int isTarget=0; ; isTarget++) { //
0 == source check; 1
* - Scope, domain and accuracy properties only if NOT in
"ignore metadata" mode.
* - Interpolation CRS in all cases (regardless if ignoring
metadata or not).
*/
- final CoordinateOperation that = (CoordinateOperation) object;
+ final var that = (CoordinateOperation) object;
if ((mode.isIgnoringMetadata() ||
(deepEquals(getCoordinateOperationAccuracy(),
that.getCoordinateOperationAccuracy(), mode))) &&
deepEquals(getInterpolationCRS(),
that.getInterpolationCRS(), mode))
diff --git
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
index 178de792bb..d25a371428 100644
---
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
+++
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
@@ -567,9 +567,7 @@ next: for (int i=0; i <= limit; i++) {
public static AxisDirection find(final String name, final AxisDirection[]
directions) {
for (final AxisDirection candidate : directions) {
final String identifier = candidate.name();
- if (equalsFiltered(name, identifier,
Characters.Filter.LETTERS_AND_DIGITS, true)
- || isAcronymForWords(name, identifier))
- {
+ if (equalsFiltered(name, identifier,
Characters.Filter.LETTERS_AND_DIGITS, true) || isAcronymForWords(name,
identifier)) {
return candidate;
}
}
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
index 762cad6ece..6209578b96 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/factory/GeodeticObjectFactoryTest.java
@@ -113,7 +113,8 @@ public final class GeodeticObjectFactoryTest extends
ObjectFactoryTest {
" PARAMETER[“Central parallel”, 41.75]],\n" + //
Wrong parameter.
" CS[Cartesian, 2],\n" +
" AXIS[“(Y)”, north],\n" +
- " AXIS[“(X)”, east]]"),
+ " AXIS[“(X)”, east],\n" +
+ " UNIT[“metre”, 1]]"),
"Should not have parsed a WKT with wrong projection
parameter.");
assertMessageContains(e, "Central parallel");
}
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
index 714b681bb9..64d3f4ff07 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/test/integration/ConsistencyTest.java
@@ -65,6 +65,7 @@ public final class ConsistencyTest extends TestCase {
private static final Set<String> EXCLUDES = Set.of(
"CRS:1", // Computer display: WKT parser alters the (i,j)
axis names.
"EPSG:5819", // EPSG topocentric example A: DerivedCRS wrongly
handled as a ProjectedCRS. See SIS-518.
+ "EPSG:5820", // EPSG topocentric example B.
"AUTO2:42001", // This projection requires parameters, but we
provide none.
"AUTO2:42002", // This projection requires parameters, but we
provide none.
"AUTO2:42003", // This projection requires parameters, but we
provide none.
@@ -126,6 +127,9 @@ public final class ConsistencyTest extends TestCase {
} catch (UnavailableFactoryException |
NoSuchIdentifierException | FactoryDataException e) {
print(code, "WARNING", e.getLocalizedMessage());
continue;
+ } catch (FactoryException e) {
+ fail("Cannot create CRS for \"" + code + "\".", e);
+ continue;
}
lookup(parseAndFormat(v2, code, crs), crs);
lookup(parseAndFormat(v2s, code, crs), crs);
@@ -170,9 +174,7 @@ public final class ConsistencyTest extends TestCase {
* @param crs the CRS to test.
* @return the parsed CRS.
*/
- private CoordinateReferenceSystem parseAndFormat(final WKTFormat f,
- final String code, final CoordinateReferenceSystem crs)
- {
+ private CoordinateReferenceSystem parseAndFormat(final WKTFormat f, final
String code, final CoordinateReferenceSystem crs) {
String wkt = f.format(crs);
final Warnings warnings = f.getWarnings();
if (warnings != null && !warnings.getExceptions().isEmpty()) {
@@ -185,8 +187,7 @@ public final class ConsistencyTest extends TestCase {
print(code, "ERROR", "Cannot parse the WKT below.");
out.println(wkt);
out.println();
- e.printStackTrace(out);
- fail(e.getLocalizedMessage());
+ fail(e);
return null;
}
final String again = f.format(parsed);
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/CharSequences.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/CharSequences.java
index c68e32c10d..61e12993ec 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/CharSequences.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/CharSequences.java
@@ -643,7 +643,7 @@ search: for (; fromIndex <= toIndex; fromIndex++) {
}
return split;
}
- // 'excludeEmpty' must use the same criterion as trimWhitespaces(…).
+ // `excludeEmpty` must use the same criterion as `trimWhitespaces(…)`.
final boolean excludeEmpty = isWhitespace(separator);
CharSequence[] split = createSplitArray(text);
final int length = text.length();
@@ -1130,8 +1130,8 @@ search: for (; fromIndex <= toIndex; fromIndex++) {
if (toRemove > 0) {
toRemove += 5; // Space needed for the " (…) " string.
/*
- * We will remove characters from 'lower' to 'upper' both
exclusive. We try to
- * adjust 'lower' and 'upper' in such a way that the first and
last characters
+ * We will remove characters from `lower` to `upper` both
exclusive. We try to
+ * adjust `lower` and `upper` in such a way that the first and
last characters
* to be removed will be spaces or punctuation characters.
*/
int lower = length >>> 1;
@@ -1316,7 +1316,7 @@ searchWordBreak: while (true) {
return null;
}
/*
- * Implementation note: the 'camelCaseToSentence' method needs
+ * Implementation note: the `camelCaseToSentence` method needs
* this method to unconditionally returns a new StringBuilder.
*/
final int length = identifier.length();
@@ -1424,11 +1424,15 @@ searchWordBreak: while (true) {
/**
* Returns {@code true} if the first string is likely to be an acronym of
the second string.
* An acronym is a sequence of {@linkplain Character#isLetterOrDigit(int)
letters or digits}
- * built from at least one character of each word in the {@code words}
string. More than
- * one character from the same word may appear in the acronym, but they
must always
- * be the first consecutive characters. The comparison is case-insensitive.
+ * built from at least one character of each word in the {@code words}
string.
+ * More than one character from the same word may appear in the acronym,
+ * but they must always be the first consecutive characters.
+ * The comparison is case-insensitive.
* If any of the given arguments is {@code null}, this method returns
{@code false}.
*
+ * <p>If a word contains digits, than the digits shall either be all
absent or all present in the acronym.
+ * An acronym with only the first digits is not considered as a match
because it changes the numerical value.</p>
+ *
* <h4>Example</h4>
* Given the {@code "Open Geospatial Consortium"} words, the following
strings are recognized as acronyms:
* {@code "OGC"}, {@code "ogc"}, {@code "O.G.C."}, {@code "OpGeoCon"}.
@@ -1459,9 +1463,10 @@ searchWordBreak: while (true) {
}
cmp: while (ia < lga) {
if (ic >= lgc) {
- // There is more letters in the acronym than in the complete
name.
+ // There is more letters in the acronym than in the remaining
part of the complete name.
return false;
}
+ final int last = ca;
ca = codePointAt(acronym, ia); ia += charCount(ca);
cc = codePointAt(words, ic); ic += charCount(cc);
if (isLetterOrDigit(ca)) {
@@ -1470,14 +1475,20 @@ cmp: while (ia < lga) {
// Continue the comparison with next letter of both
strings.
continue;
}
- // Will search for the next word after the 'else' block.
- } else do {
- if (ia >= lga) break cmp;
- ca = codePointAt(acronym, ia);
- ia += charCount(ca);
- } while (!isLetterOrDigit(ca));
+ // Will search for the next word after the `else` block.
+ } else {
+ if (isDigit(cc) && isDigit(last)) {
+ // Acronym contains a truncated number.
+ return false;
+ }
+ do {
+ if (ia >= lga) break cmp;
+ ca = codePointAt(acronym, ia);
+ ia += charCount(ca);
+ } while (!isLetterOrDigit(ca));
+ }
/*
- * At this point, 'ca' is the next acronym letter to compare and we
+ * At this point, `ca` is the next acronym letter to compare and we
* need to search for the next word in the complete name. We first
* skip remaining letters, then we skip non-letter characters.
*/
@@ -1499,15 +1510,22 @@ cmp: while (ia < lga) {
* any additional word. We can only finish the current word and skip
trailing non-
* letter characters.
*/
+ boolean disallowDigit = isDigit(ca);
boolean skipLetters = true;
- do {
- do {
- if (ic >= lgc) return true;
- cc = codePointAt(words, ic);
- ic += charCount(cc);
- } while (isLetterOrDigit(cc) == skipLetters);
- } while ((skipLetters = !skipLetters) == false);
- return false;
+ while (ic < lgc) {
+ cc = codePointAt(words, ic);
+ ic += charCount(cc);
+ if (skipLetters) {
+ if (disallowDigit) {
+ if (isDigit(cc)) return false;
+ disallowDigit = false;
+ }
+ skipLetters = isLetterOrDigit(cc);
+ } else if (isLetterOrDigit(cc)) {
+ return false;
+ }
+ }
+ return true;
}
/**
@@ -2166,7 +2184,7 @@ cmp: while (ia < lga) {
int upper = fromIndex;
/*
* Skip whitespaces. At the end of this loop,
- * 'c' will be the first non-blank character.
+ * `c` will be the first non-blank character.
*/
int c;
do {
diff --git
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/CharSequencesTest.java
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/CharSequencesTest.java
index 6fb5cd1ad8..1dd201c5eb 100644
---
a/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/CharSequencesTest.java
+++
b/endorsed/src/org.apache.sis.util/test/org/apache/sis/util/CharSequencesTest.java
@@ -324,7 +324,7 @@ public final class CharSequencesTest extends TestCase {
@Test
public void testIsAcronymForWords() {
/*
- * Following shall be accepted as acronyms...
+ * Following shall be accepted as acronyms.
*/
assertTrue(isAcronymForWords("OGC", "Open
Geospatial Consortium"));
assertTrue(isAcronymForWords("O.G.C.", "Open
Geospatial Consortium"));
@@ -334,7 +334,7 @@ public final class CharSequencesTest extends TestCase {
assertTrue(isAcronymForWords("E", "EAST"));
assertTrue(isAcronymForWords("ENE",
"EAST_NORTH_EAST"));
/*
- * Following shall be rejected...
+ * Following shall be rejected.
*/
assertFalse(isAcronymForWords("ORC", "Open Geospatial Consortium"));
assertFalse(isAcronymForWords("O.C.G.", "Open Geospatial Consortium"));
@@ -382,6 +382,7 @@ public final class CharSequencesTest extends TestCase {
* otherwise it leads to a confusion in `EPSGDataAccess`.
*/
assertFalse(isAcronymForWords("coordoperation", "Coordinate_Operation
Method"));
+ assertFalse(isAcronymForWords("North along 15°W", "North along
150°W"));
}
/**