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 c946c02745 Preparation for a SIS 1.5 release: * Disable a test which
fails when the `SIS_DATA` environment variable is not set. * New layout for the
generated page of coordinate operation methods. * Remove the special cases
about datum names in the script that generate the list of CRS. * Make the code
that generate the list of supported CRS less dependent of test code
(geoapi-conformance).
c946c02745 is described below
commit c946c02745771fca48d233c3b3efd416cc5b35f0
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Fri Sep 26 17:39:11 2025 +0200
Preparation for a SIS 1.5 release:
* Disable a test which fails when the `SIS_DATA` environment variable is
not set.
* New layout for the generated page of coordinate operation methods.
* Remove the special cases about datum names in the script that generate
the list of CRS.
* Make the code that generate the list of supported CRS less dependent of
test code (geoapi-conformance).
---
.../report/CoordinateOperationMethods.java | 198 +++---
.../report/CoordinateReferenceSystems.java | 697 +++++++++------------
.../sis/referencing/report/HTMLGenerator.java | 24 +-
.../resources/embedded/EmbeddedResourcesTest.java | 29 +-
.../factory/sql/epsg/DataScriptUpdater.java | 10 +
5 files changed, 429 insertions(+), 529 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
index e0647eb452..205d55d710 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateOperationMethods.java
@@ -36,10 +36,7 @@ import org.apache.sis.util.Characters;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.Version;
import org.apache.sis.util.internal.shared.Constants;
-import org.apache.sis.util.internal.shared.URLs;
import org.apache.sis.referencing.CRS;
-import org.apache.sis.referencing.operation.DefaultOperationMethod;
-import org.apache.sis.referencing.operation.provider.Affine;
import org.apache.sis.referencing.operation.provider.AlbersEqualArea;
import org.apache.sis.referencing.operation.provider.LambertConformal2SP;
import org.apache.sis.referencing.operation.provider.ObliqueMercator;
@@ -60,8 +57,8 @@ import org.opengis.referencing.crs.DerivedCRS;
* Generates a list of projection parameters in a HTML page. This class is
used for updating the
* <a
href="https://sis.apache.org/tables/CoordinateOperationMethods.html">CoordinateOperationMethods.html</a>
page.
* The {@linkplain #main(String[])} method creates the "{@code
CoordinateOperationMethods.html}" file in the current
- * default directory if it does not already exists. Users is responsible for
moving the generated file to the Apache
- * SIS {@code "content/"} site directory.
+ * default directory if it does not already exists. Maintainers need to move
themselves the generated file to the
+ * Apache SIS {@code "content/"} site directory.
*
* <p><b>This class is designed for Apache SIS operation methods only</b> -
this is not a general purpose generator
* for arbitrary operation methods. The reason is that we make some
assumptions in various place (e.g. EPSG name is
@@ -78,35 +75,23 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
*/
public static void main(final String[] args) throws IOException {
final MathTransformFactory factory =
DefaultMathTransformFactory.provider();
- final List<OperationMethod> methods = new
ArrayList<>(factory.getAvailableMethods(SingleOperation.class));
+ final var methods = new
ArrayList<OperationMethod>(factory.getAvailableMethods(SingleOperation.class));
methods.removeIf((method) ->
method.getClass().getName().endsWith("Mock"));
Collections.sort(methods, (final OperationMethod o1, final
OperationMethod o2) -> {
- int c = category(o1) - category(o2);
- if (c == 0) { // If the two methods are in the same category,
sort by name.
- final String n1 = o1.getName().getCode().replace('(','
').replace(')',' ').replace('_',' ');
- final String n2 = o2.getName().getCode().replace('(','
').replace(')',' ').replace('_',' ');
- c = n1.compareTo(n2);
- }
- return c;
+ final String n1 = o1.getName().getCode().replace('(','
').replace(')',' ').replace('_',' ');
+ final String n2 = o2.getName().getCode().replace('(','
').replace(')',' ').replace('_',' ');
+ return n1.compareTo(n2);
});
- try (CoordinateOperationMethods writer = new
CoordinateOperationMethods()) {
- writer.writeIndex(methods);
- for (final OperationMethod method : methods) {
- writer.write(method);
- }
+ try (var writer = new CoordinateOperationMethods()) {
+ writer.write(methods);
}
}
- /**
- * Values returned by {@link #category(OperationMethod)}.
- */
- private static final int CONVERSION = 1, TRANSFORMATION = 3;
-
/**
* Parameters to default to the latitude of origin. We can hardly detect
those cases
* automatically, since the behavior for the default value is hard-coded
in Java.
*/
- private final GeneralParameterDescriptor defaultToLatitudeOfOrigin[] = {
+ private final GeneralParameterDescriptor[] defaultToLatitudeOfOrigin = {
AlbersEqualArea .STANDARD_PARALLEL_1,
LambertConformal2SP.STANDARD_PARALLEL_1
};
@@ -115,7 +100,7 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
* Parameters to default to the first standard parallel. We can hardly
detect those
* cases automatically, since the behavior for the default value is
hard-coded in Java.
*/
- private final GeneralParameterDescriptor defaultToStandardParallel1[] = {
+ private final GeneralParameterDescriptor[] defaultToStandardParallel1 = {
AlbersEqualArea .STANDARD_PARALLEL_2,
LambertConformal2SP.STANDARD_PARALLEL_2
};
@@ -124,7 +109,7 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
* Parameters to default to the azimuth. We can hardly detect those cases
automatically,
* since the behavior for the default value is hard-coded in Java.
*/
- private final GeneralParameterDescriptor defaultToAzimuth[] = {
+ private final GeneralParameterDescriptor[] defaultToAzimuth = {
ObliqueMercator .RECTIFIED_GRID_ANGLE,
// HotineObliqueMercator.PARAMETERS.descriptor("Angle from Rectified to
Skew Grid")
};
@@ -149,30 +134,40 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
* @throws IOException if an error occurred while writing to the file.
*/
public CoordinateOperationMethods() throws IOException {
- super("CoordinateOperationMethods.html", "Apache SIS Coordinate
Operation Methods", "authority-codes.css");
+ super("CoordinateOperationMethods.html",
+ "Coordinate Operation Methods supported by Apache SIS",
+ "authority-codes.css");
+
domainOfValidity = Map.of(); // TODO: not yet available.
rangeFormat = new RangeFormat(LOCALE);
+ }
+
+ /**
+ * Writes the table of content, the summary, and the tables of operation
methods.
+ *
+ * @param methods all methods to write.
+ * @throws IOException if an error occurred while writing to the file.
+ */
+ private void write(final List<OperationMethod> methods) throws IOException
{
+ writeIndex(methods);
+ final int div = openTag("div", "methods");
final int header = openTag("header");
- println("h1", "Apache <abbr title=\"Spatial Information
System\">SIS</abbr>™ Coordinate Operation Methods");
- int item = openTag("p");
+ println("h1", "Coordinate Operation Methods supported by Apache <abbr
title=\"Spatial Information System\">SIS</abbr>™");
+ final int item = openTag("p");
println("The following tables summarize the coordinate operation
methods known to Apache <abbr title=\"Spatial Information System\">SIS</abbr> "
+ Version.SIS);
- println("together with the recognized parameters. There are three
kinds of parameters:");
+ println("together with the recognized parameters. In each table, the
following parameters are handled in a special way:");
closeTags(item);
- openTag("ul", "verbose");
+ openTag("ul");
openTag("li");
- println("The <code>semi-major</code> and <code>semi-minor</code>
parameters are needed for all map projections,");
- println("but usually do not need to be specified explicitly since they
are inferred from the ellipsoid");
- println("(unless <a
href=\"https://sis.apache.org/apidocs/org/apache/sis/referencing/operation/transform/DefaultMathTransformFactory.html\">creating
parameterized transforms directly</a>).");
- println("For this reason, those parameters are usually not shown in <a
href=\"" + URLs.EPSG + "\"><abbr>EPSG</abbr> repository</a>");
- println("or <a href=\"https://www.ogc.org/standards/wkt-crs/\">Well
Known Text</a> (<abbr>WKT</abbr>) definitions.");
+ println("The <code>semi_major</code> and <code>semi_minor</code>
parameters usually do not need to be specified explicitly as they are inferred
from the ellipsoid.");
reopenTag("li");
- println("The <code>earth_radius</code> and
<code>inverse_flattening</code> parameters (not shown below) are implicitly
supported by all map projections.");
- println("They are other ways to specify the ellipsoid (actually rarely
used).");
- println("Read and write operations on those implicit parameters are
converted into equivalent operations on <code>semi-major</code> and
<code>semi-minor</code> parameters.");
- reopenTag("li");
- println("Unless otherwise noticed, all other parameters are
mandatory");
- println("(in the sense that they should always be shown in forms,
regardless of whether they have default value).");
+ println("Hidden <code>earth_radius</code> and
<code>inverse_flattening</code> parameters are");
+ println("alternatives to <code>semi_major</code> and
<code>semi_minor</code> for specifying the ellipsoid.");
closeTags(header);
+ for (final OperationMethod method : methods) {
+ write(method);
+ }
+ closeTags(div);
}
/**
@@ -183,22 +178,9 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
*/
public void writeIndex(final Iterable<? extends OperationMethod> methods)
throws IOException {
final int nav = openTag("nav");
- println("h2", "Table of content:");
- int innerUL = openTag("ul") + 1;
- int category = 0;
+ println("p", "Table of content");
+ openTag("ul");
for (final OperationMethod method : methods) {
- final int nc = category(method);
- if (nc != category) {
- closeTags(innerUL);
- reopenTag("li");
- switch (nc) {
- case CONVERSION: println("Conversions"); break;
- case TRANSFORMATION: println("Tranformations"); break;
- default: throw new AssertionError(category);
- }
- innerUL = openTag("ul");
- category = nc;
- }
println("li", "<a href=\"#" + getAnchor(method) + "\">" +
escape(method.getName().getCode()) + "</a>");
}
closeTags(nav);
@@ -234,10 +216,8 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
*/
private void writeIdentification(final OperationMethod method) throws
IOException {
final int table = openTag("table class=\"info\"");
- /*
- * ──────────────── EPSG IDENTIFIERS
────────────────────────────────────
- */
- final StringBuilder buffer = new StringBuilder();
+ // ──────────────── EPSG IDENTIFIERS
────────────────────────────────────
+ final var buffer = new StringBuilder();
for (final Identifier id : method.getIdentifiers()) {
if (Constants.EPSG.equalsIgnoreCase(id.getCodeSpace())) {
if (buffer.length() != 0) {
@@ -259,9 +239,7 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
println("td", buffer);
closeTags(tr);
}
- /*
- * ──────────────── ALIASES
─────────────────────────────────────────────
- */
+ // ──────────────── ALIASES
─────────────────────────────────────────────
buffer.setLength(0);
for (final GenericName alias : method.getAlias()) {
if (buffer.length() != 0) {
@@ -281,9 +259,7 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
println("td", buffer);
closeTags(tr);
}
- /*
- * ──────────────── DOMAIN OF VALIDITY
──────────────────────────────────
- */
+ // ──────────────── DOMAIN OF VALIDITY
──────────────────────────────────
buffer.setLength(0);
final DefaultGeographicBoundingBox domain =
getDomainOfValidity(method);
if (domain != null) {
@@ -297,6 +273,18 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
closeTags(table);
}
+ /**
+ * Returns {@code true} if at least one non-deprecated parameter has a
remark.
+ */
+ private static boolean hasRemarks(final ParameterDescriptorGroup group) {
+ for (final GeneralParameterDescriptor gp : group.descriptors()) {
+ if (!isDeprecated(gp) && ((ParameterDescriptor<?>)
gp).getRemarks().isPresent()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Writes the table of parameters.
* Table columns will be:
@@ -304,9 +292,9 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
* <ul>
* <li>First EPSG code</li>
* <li>Primary name</li>
- * <li>Reference to remarks, if any</li>
* <li>Domain of values</li>
* <li>Default values</li>
+ * <li>Reference to remarks, if any</li>
* </ul>
*/
private void writeParameters(final ParameterDescriptorGroup group) throws
IOException {
@@ -320,37 +308,21 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
}
println("th", "EPSG");
println("th class=\"sep\"", "Name");
- println("th class=\"sep\"", "Remarks");
println("th class=\"sep\" colspan=\"3\"", "Value domain");
println("th class=\"sep\"", "Default");
- final Map<String, Integer> footnotes = new LinkedHashMap<>();
+ final boolean hasRemarks = hasRemarks(group);
+ if (hasRemarks) {
+ println("th class=\"sep\"", "Remarks");
+ }
+ final var footnotes = new LinkedHashMap<String, Integer>();
for (final GeneralParameterDescriptor gp : group.descriptors()) {
if (isDeprecated(gp)) {
continue;
// Hide deprecated parameters.
}
- final ParameterDescriptor<?> param = (ParameterDescriptor<?>) gp;
+ final var param = (ParameterDescriptor<?>) gp;
reopenTag("tr");
println("td", escape(getFirstEpsgCode(param.getIdentifiers())));
writeName(param);
- String remarks =
toLocalizedString(param.getRemarks().orElse(null));
- if (remarks != null) {
- Integer index = footnotes.putIfAbsent(remarks,
footnotes.size() + 1);
- if (index == null) {
- index = footnotes.size();
- }
- if (param.getMinimumOccurs() == 0) {
- remarks = "Optional ";
- } else {
- final Comparable<?> min = param.getMinimumValue();
- if ((min instanceof Number n) && n.doubleValue() ==
((Number) param.getMaximumValue()).doubleValue()) {
- remarks = "Unmodifiable ";
- } else {
- remarks = "See note ";
- }
- }
- remarks += toSuperScript(index);
- }
- println("td class=\"sep\"", escape(remarks));
final String domain =
toLocalizedString(Parameters.getValueDomain(param));
final int s;
if (domain != null && ((s = domain.indexOf('…')) >= 0)) {
@@ -361,6 +333,27 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
println("td class=\"sep center\" colspan=\"3\"", domain);
}
println("td class=\"sep\"", escape(getDefaultValue(param,
getUnit(param))));
+ if (hasRemarks) {
+ String remarks =
toLocalizedString(param.getRemarks().orElse(null));
+ if (remarks != null) {
+ Integer index = footnotes.putIfAbsent(remarks,
footnotes.size() + 1);
+ if (index == null) {
+ index = footnotes.size();
+ }
+ if (param.getMinimumOccurs() == 0) {
+ remarks = "Optional ";
+ } else {
+ final Comparable<?> min = param.getMinimumValue();
+ if ((min instanceof Number n) && n.doubleValue() ==
((Number) param.getMaximumValue()).doubleValue()) {
+ remarks = "Unmodifiable ";
+ } else {
+ remarks = "See note ";
+ }
+ }
+ remarks += toSuperScript(index);
+ }
+ println("td class=\"sep\"", escape(remarks));
+ }
}
closeTags(table);
if (!footnotes.isEmpty()) {
@@ -413,7 +406,7 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
public static Map<String, DefaultGeographicBoundingBox>
computeUnionOfAllDomainOfValidity(
final CRSAuthorityFactory factory) throws FactoryException
{
- final Map<String, DefaultGeographicBoundingBox> domainOfValidity = new
HashMap<>();
+ final var domainOfValidity = new HashMap<String,
DefaultGeographicBoundingBox>();
for (final String code : factory.getAuthorityCodes(DerivedCRS.class)) {
final CoordinateReferenceSystem crs;
try {
@@ -503,29 +496,6 @@ public class CoordinateOperationMethods extends
HTMLGenerator {
return "";
}
- /**
- * Returns the operation type of the given method.
- */
- private static Class<?> getOperationType(final DefaultOperationMethod
method) {
- Class<?> type = method.getOperationType();
- if (type == SingleOperation.class) {
- if (method instanceof Affine) { // EPSG:9624 - Affine
parametric transformation
- type = Transformation.class;
- }
- }
- return type;
- }
-
- /**
- * Returns a code for sorting methods in categories.
- */
- private static int category(final OperationMethod method) {
- final Class<?> c = getOperationType((DefaultOperationMethod) method);
- if (Conversion .class.isAssignableFrom(c)) return CONVERSION;
- if (Transformation.class.isAssignableFrom(c)) return TRANSFORMATION;
- return 0;
- }
-
/**
* Returns the first EPSG code found in the given collection, or {@code
null} if none.
*/
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
index 4feac4cdcc..93059b4f41 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/CoordinateReferenceSystems.java
@@ -16,18 +16,18 @@
*/
package org.apache.sis.referencing.report;
+import java.util.List;
+import java.util.ArrayList;
import java.util.Locale;
import java.util.Set;
-import java.util.Map;
-import java.util.HashSet;
import java.util.TreeMap;
-import java.util.NavigableMap;
import java.util.Optional;
-import java.io.File;
+import java.util.Collections;
import java.io.IOException;
import org.opengis.metadata.Identifier;
import org.opengis.util.FactoryException;
import org.opengis.util.InternationalString;
+import org.opengis.util.NoSuchIdentifierException;
import org.opengis.referencing.IdentifiedObject;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.cs.SphericalCS;
@@ -41,6 +41,7 @@ import org.opengis.referencing.crs.DerivedCRS;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.CRSAuthorityFactory;
import org.opengis.referencing.datum.Datum;
+import org.opengis.referencing.datum.VerticalDatum;
import org.opengis.referencing.datum.RealizationMethod;
import org.opengis.referencing.operation.OperationMethod;
import org.apache.sis.metadata.iso.citation.Citations;
@@ -56,221 +57,38 @@ import org.apache.sis.util.Version;
import org.apache.sis.util.internal.shared.Constants;
import org.apache.sis.referencing.crs.AbstractCRS;
import org.apache.sis.referencing.cs.AxesConvention;
+import org.apache.sis.util.internal.shared.URLs;
import org.apache.sis.util.iso.DefaultNameSpace;
import org.apache.sis.util.logging.Logging;
-// Test dependencies
-import static org.junit.jupiter.api.Assertions.*;
-import org.opengis.test.report.AuthorityCodesReport;
-
/**
* Generates a list of supported Coordinate Reference Systems in the current
directory.
- * This class is for manual execution after the EPSG database has been updated,
- * or the projection implementations changed.
+ * This class is for manual execution after the <abbr>EPSG</abbr> database has
been updated,
+ * or after some implementations of operation methods changed.
*
- * <p><b>WARNING:</b>
+ * <h2>WARNING:</h2>
* this class implements heuristic rules for nicer sorting (e.g. of CRS having
numbers as Roman letters).
* Those heuristic rules were determined specifically for the EPSG dataset
expanded with WMS codes.
* This class is not likely to produce good results for any other authorities,
and many need to be updated
- * after any upgrade of the EPSG dataset.</p>
+ * after any upgrade of the <abbr>EPSG</abbr> dataset.
*
* @author Martin Desruisseaux (Geomatys)
*/
-public final class CoordinateReferenceSystems extends AuthorityCodesReport {
+public final class CoordinateReferenceSystems extends HTMLGenerator {
/**
- * The titles of some sections where to group CRS. By default CRS are
grouped by datum names.
- * But if a name is listed in this map, then that alternative name will be
used for grouping purpose.
- * Sometimes the change is only cosmetic (e.g. "Reseau Geodesique
Francais" → "Réseau Géodésique Français").
- * But sometimes the changes have the effect of merging different datum
under the same section.
- * For example, we merge the "Arc 1950" and "Arc 1960" sections into a
single "Arc" section,
- * since those sections were small and we do not want to scatter the HTML
page with too many sections.
- * However, we do not merge "NAD83" and "NAD83(HARN)" because those
sections are already quite large,
- * and merging them will result in a too large section.
+ * Generates the <abbr>HTML</abbr> report.
*
- * <p>The decision to merge or not is arbitrary. Generally, we try to
avoid small sections (less that 5 CRS)
- * but without merging together unrelated datum. Every CRS having a datum
whose name <em>starts</em> with a
- * value in the left column will be reported in the section given in the
right column.</p>
- */
- private static final NavigableMap<String,String> SECTION_TITLES = new
TreeMap<>();
- static {
- rd("American Samoa",
"American Samoa");
- rd("Arc", "Arc");
- rd("Ancienne Triangulation Francaise",
"Ancienne Triangulation Française");
- rd("Australian Geodetic Datum",
"Australian Geodetic Datum");
- rd("Australian Height Datum",
"Australian Height Datum");
- rd("Azores Central Islands", "Azores
Islands");
- rd("Azores Occidental Islands", "Azores
Islands");
- rd("Azores Oriental Islands", "Azores
Islands");
- rd("Baltic",
"Baltic");
- rd("Batavia",
"Batavia");
- rd("Bermuda",
"Bermuda");
- rd("Bogota 1975", "Bogota
1975");
- rd("Carthage",
"Carthage");
- rd("Bern 1938", "Bern /
CH1903");
- rd("Cais", "Cais");
- rd("Cayman Brac", "Cayman
Islands");
- rd("Cayman Islands", "Cayman
Islands");
- rd("CH1903", "Bern /
CH1903");
- rd("CH1903+", "Bern /
CH1903");
- rd("Canadian Geodetic Vertical Datum",
"Canadian Geodetic Vertical Datum");
- rd("Chatham Islands Datum", "Chatham
Islands Datum");
- rd("Corrego Alegre", "Corrego
Alegre");
- rd("Croatian Terrestrial Reference System",
"Croatian Reference System");
- rd("Croatian Vertical Reference Datum",
"Croatian Reference System");
- rd("Danger 1950", "Saint
Pierre et Miquelon 1950");
- rd("Dansk", "Dansk");
- rd("Dealul Piscului", "Dealul
Piscului");
- rd("Deutsches Haupthoehennetz",
"Deutsches Haupthoehennetz");
- rd("Douala",
"Douala");
- rd("Dunedin",
"Dunedin");
- rd("Dunedin-Bluff",
"Dunedin");
- rd("EGM2008 geoid", "EGM
geoid");
- rd("EGM84 geoid", "EGM
geoid");
- rd("EGM96 geoid", "EGM
geoid");
- rd("Egypt", "Egypt");
- rd("EPSG example", "EPSG
example");
- rd("Estonia",
"Estonia");
- rd("European Datum",
"European Datum");
- rd("European Terrestrial Reference Frame",
"European Terrestrial Reference Frame");
- rd("European Vertical Reference Frame",
"European Vertical Reference Frame");
- rd("Fahud", "Fahud");
- rd("Fao", "Fao");
- rd("Fehmarnbelt",
"Fehmarnbelt");
- rd("Faroe Datum", "Faroe
Islands");
- rd("Faroe Islands", "Faroe
Islands");
- rd("fk89", "Faroe
Islands");
- rd("Fiji", "Fiji");
- rd("Gan 1970",
"Gandajika");
- rd("Grand Cayman", "Grand
Cayman");
- rd("Greek", "Greek");
- rd("Greenland",
"Greenland");
- rd("Guadeloupe",
"Guadeloupe");
- rd("Guam", "Guam");
- rd("Gunung Segara", "Gunung
Segara");
- rd("Helsinki",
"Helsinki");
- rd("High Water", "High
Water");
- rd("Higher High Water", "High
Water");
- rd("Highest Astronomical Tide", "High
Water");
- rd("Hong Kong", "Hong
Kong");
- rd("Hungarian",
"Hungarian Datum");
- rd("IG05", "Israeli
Grid");
- rd("IGb", "IGb");
- rd("IGN", "IGN");
- rd("IGS", "IGS");
- rd("Indian",
"Indian");
- rd("International Great Lakes Datum",
"International Great Lakes Datum");
- rd("International Terrestrial Reference Frame",
"International Terrestrial Reference Frame");
- rd("Islands Net", "Islands
Net");
- rd("Israeli Geodetic Datum", "Israeli
Geodetic Datum");
- rd("Jamaica",
"Jamaica");
- rd("Japanese Geodetic Datum 2000",
"Japanese Geodetic Datum 2000");
- rd("Japanese Geodetic Datum 2011",
"Japanese Geodetic Datum 2011");
- rd("Japanese Standard Levelling Datum",
"Japanese Standard Levelling Datum");
- rd("Kalianpur",
"Kalianpur");
- rd("Kertau",
"Kertau");
- rd("KOC Construction Datum", "KOC
Construction Datum / Well Datum");
- rd("KOC Well Datum", "KOC
Construction Datum / Well Datum");
- rd("Korean Datum", "Korean
Datum");
- rd("Kuwait Oil Company", "Kuwait
Oil Company / Kuwait Utility");
- rd("Kuwait PWD", "Kuwait
Oil Company / Kuwait Utility");
- rd("Kuwait Utility", "Kuwait
Oil Company / Kuwait Utility");
- rd("Lao", "Lao");
- rd("Latvia",
"Latvia");
- rd("Lisbon",
"Lisbon");
- rd("Lower Low Water Large Tide", "Low
Water");
- rd("Lowest Astronomical Tide", "Low
Water");
- rd("Macao", "Macao");
- rd("Makassar",
"Makassar");
- rd("Manoca",
"Manoca");
- rd("Martinique",
"Martinique");
- rd("Maupiti",
"Maupiti");
- rd("Mean High Water", "Mean
Sea Level");
- rd("Mean Higher High Water", "Mean
Sea Level");
- rd("Mean Low Water", "Mean
Sea Level");
- rd("Mean Lower Low Water", "Mean
Sea Level");
- rd("Missao Hidrografico Angola y Sao Tome 1951", "Missao
Hidrografico Angola y Sao Tome");
- rd("Mhast (offshore)", "Missao
Hidrografico Angola y Sao Tome");
- rd("Mhast (onshore)", "Missao
Hidrografico Angola y Sao Tome");
- rd("Militar-Geographische Institut (Ferro)",
"Militar-Geographische Institut");
- rd("MOMRA", "MOMRA");
- rd("Monte Mario (Rome)", "Monte
Mario");
- rd("Moorea",
"Moorea");
- rd("Nahrwan",
"Nahrwan");
- rd("Naparima",
"Naparima");
- rd("Nivellement General de la Corse",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("Nivellement General de la France",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("Nivellement General de Nouvelle Caledonie",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("Nivellement General de Polynesie Francaise",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("Nivellement General du Luxembourg",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("Nivellement General Guyanais",
"Nivellement Général Corse / France / Nouvelle-Calédonie / Polynésie Française
/ Luxembourd / Guyanais");
- rd("NGO 1948", "NGO
1948");
- rd("Nouvelle Triangulation Francaise",
"Nouvelle Triangulation Française");
- rd("NAD83 Canadian Spatial Reference System", "North
American Datum 1983 — Canadian Spatial Reference System");
- rd("NAD83 (Continuously Operating Reference Station 1996)", "North
American Datum 1983 — Continuously Operating Reference Station 1996"); //
For better sort order.
- rd("NAD83 (Federal Base Network)", "North
American Datum 1983 — Federal Base Network");
- rd("NAD83 (High Accuracy Reference Network)", "North
American Datum 1983 — High Accuracy Reference Network");
- rd("NAD83 (High Accuracy Reference Network - Corrected)", "North
American Datum 1983 — High Accuracy Reference Network");
- rd("NAD83 (National Spatial Reference System 2007)", "North
American Datum 1983 — National Spatial Reference System 2007");
- rd("NAD83 (National Spatial Reference System 2011)", "North
American Datum 1983 — National Spatial Reference System 2011");
- rd("NAD83 (National Spatial Reference System MA11)", "North
American Datum 1983 — National Spatial Reference System MA11 / PA11");
- rd("NAD83 (National Spatial Reference System PA11)", "North
American Datum 1983 — National Spatial Reference System MA11 / PA11");
- rd("North American Datum of 1983 (CSRS)", "North
American Datum 1983 — CSRS");
- rd("North American Datum of 1983 (CSRS96)", "North
American Datum 1983 — CSRS");
- rd("New Zealand Vertical Datum", "New
Zealand Vertical Datum");
- rd("Norway Normal Null", "Norway
Normal Null");
- rd("Ordnance Datum Newlyn",
"Ordnance Datum Newlyn");
- rd("OSGB", "OSGB");
- rd("Parametry Zemli 1990",
"Parametry Zemli 1990");
- rd("PDO Height Datum 1993", "PDO
Survey / Height Datum 1993");
- rd("PDO Survey Datum 1993", "PDO
Survey / Height Datum 1993");
- rd("Pitcairn",
"Pitcairn");
- rd("Port Moresby", "Port
Moresby");
- rd("Porto Santo", "Porto
Santo");
- rd("Posiciones Geodésicas Argentinas",
"Posiciones Geodésicas Argentinas");
- rd("Puerto Rico", "Puerto
Rico");
- rd("Qatar", "Qatar");
- rd("Qornoq",
"Qornoq");
- rd("Reseau Geodesique de Nouvelle Caledonie", "Réseau
Géodésique de Nouvelle-Calédonie");
- rd("Reseau National Belge", "Réseau
National Belge");
- rd("Reunion",
"Réunion");
- rd("Rikets hojdsystem", "Rikets
hojdsystem");
- rd("Santa Cruz", "Santa
Cruz");
- rd("Serbian", "Serbian
Reference System / Network");
- rd("Sierra Leone", "Sierra
Leone");
- rd("SIRGAS",
"SIRGAS");
- rd("Slovenia",
"Slovenia");
- rd("Slovenian",
"Slovenia");
- rd("South American Datum", "South
American Datum");
- rd("Sri Lanka", "Sri
Lanka");
- rd("Stockholm 1938",
"Stockholm 1938");
- rd("St. Helena", "St.
Helena");
- rd("System of the Unified Trigonometrical Cadastral Network", "System
of the Unified Trigonometrical Cadastral Network");
- rd("Tahaa", "Tahaa");
- rd("Tahiti",
"Tahiti");
- rd("Taiwan",
"Taiwan");
- rd("Tananarive 1925",
"Tananarive 1925");
- rd("Tokyo", "Tokyo");
- rd("Viti Levu", "Viti
Levu");
- rd("Voirol",
"Voirol");
- rd("WGS 72 Transit Broadcast Ephemeris", "World
Geodetic System 1972 — Transit Broadcast Ephemeris");
- rd("World Geodetic System 1984", "World
Geodetic System 1984");
- rd("Yellow Sea", "Yellow
Sea");
- }
-
- /**
- * The datums from the above list which are deprecated, but that we do not
want to replace by the non-deprecated
- * datum. We disable some replacements when they allow better sorting of
deprecated CRS.
- */
- private static final Set<String> KEEP_DEPRECATED_DATUM = Set.of(
- "Dealul Piscului 1970"); // Datum does not exist but is an
alias for S-42 in Romania.
-
- /**
- * Shortcut for {@link #SECTION_TITLES} initialization.
- * {@code "rd"} stands for "rename datum".
+ * @param args ignored.
+ * @throws FactoryException if an error occurred while fetching the CRS.
+ * @throws IOException if an error occurred while writing the HTML file.
*/
- private static void rd(final String datum, final String display) {
- assertNull(datum, SECTION_TITLES.put(datum, display));
+ @SuppressWarnings("UseOfSystemOutOrSystemErr")
+ public static void main(final String[] args) throws FactoryException,
IOException {
+ Locale.setDefault(LOCALE); // We have to use this hack for now
because exceptions are formatted in the current locale.
+ try (var writer = new CoordinateReferenceSystems()) {
+ writer.write();
+ }
}
/**
@@ -289,7 +107,7 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
/**
* The keywords before which to cut the CRS names when sorting by
alphabetical order.
- * The main intent here is to preserve the "far west", "west", "central
west", "central",
+ * The intent is to preserve the "far west", "west", "central west",
"central",
* "central east", "east", "far east" order.
*/
private static final String[] CUT_BEFORE = {
@@ -326,8 +144,8 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
};
/**
- * The symbol to write in from of EPSG code of CRS having an axis order
different
- * then the (longitude, latitude) one.
+ * The symbol to write in front of <abbr>EPSG</abbr> code of
<abbr>CRS</abbr>
+ * having an axis order different than the (longitude, latitude) order.
*/
private static final char YX_ORDER = '\u21B7';
@@ -337,78 +155,86 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
private final CRSAuthorityFactory factory;
/**
- * The datum from the {@link #SECTION_TITLES} that we didn't found after
we processed all codes.
- * Used for verification purpose only.
+ * Version of the <abbr>EPSG</abbr> geodetic dataset.
*/
- private final Set<String> unusedDatumMapping;
+ private final String versionEPSG;
/**
* Creates a new instance.
*/
- private CoordinateReferenceSystems() throws FactoryException {
- super(null);
- unusedDatumMapping = new HashSet<>(SECTION_TITLES.keySet());
- properties.setProperty("TITLE", "Apache SIS™ Coordinate
Reference System (CRS) codes");
- properties.setProperty("PRODUCT.NAME", "Apache SIS™");
- properties.setProperty("PRODUCT.VERSION", getVersion());
- properties.setProperty("PRODUCT.URL", "https://sis.apache.org");
- properties.setProperty("JAVADOC.GEOAPI",
"https://www.geoapi.org/snapshot/javadoc");
- properties.setProperty("FACTORY.NAME", "EPSG");
- properties.setProperty("FACTORY.VERSION", "9.9.1");
- properties.setProperty("FACTORY.VERSION.SUFFIX", ", together with
other sources");
- properties.setProperty("PRODUCT.VERSION.SUFFIX", " (provided that <a
href=\"https://sis.apache.org/epsg.html\">a connection to an EPSG database
exists</a>)");
- properties.setProperty("DESCRIPTION", "<p><b>Notation:</b></p>\n" +
- "<ul>\n" +
- " <li>The " + YX_ORDER + " symbol in front of authority codes
(${PERCENT.ANNOTATED} of them) identifies" +
- " left-handed coordinate systems (for example with
<var>latitude</var> axis before <var>longitude</var>).</li>\n" +
- " <li>The <del>codes with a strike</del>
(${PERCENT.DEPRECATED} of them) identify deprecated CRS." +
- " In some cases, the remarks column indicates the
replacement.</li>\n" +
- "</ul>");
+ private CoordinateReferenceSystems() throws FactoryException, IOException {
+ super("CoordinateReferenceSystems.html",
+ "Coordinate Reference Systems recognized by Apache SIS™",
+ "crs-report.css");
factory = CRS.getAuthorityFactory(null);
- add(factory);
+ final GeographicCRS anyCRS = factory.createGeographicCRS("EPSG:4326");
+ versionEPSG = IdentifiedObjects.getIdentifier(anyCRS,
Citations.EPSG).getVersion();
}
/**
- * Generates the HTML report.
- *
- * @param args ignored.
- * @throws FactoryException if an error occurred while fetching the CRS.
- * @throws IOException if an error occurred while writing the HTML file.
+ * Writes the report after all rows have been collected.
*/
- @SuppressWarnings("UseOfSystemOutOrSystemErr")
- public static void main(final String[] args) throws FactoryException,
IOException {
- Locale.setDefault(Locale.US); // We have to use this hack for now
because exceptions are formatted in the current locale.
- final CoordinateReferenceSystems writer = new
CoordinateReferenceSystems();
- final File file = writer.write(new
File("CoordinateReferenceSystems.html"));
- System.out.println("Created " + file.getAbsolutePath());
- if (!writer.unusedDatumMapping.isEmpty()) {
- System.out.println();
- System.out.println("WARNING: the following datums were expected
but not found. Maybe their spelling changed?");
- for (final String name : writer.unusedDatumMapping) {
- System.out.print(" - ");
- System.out.println(name);
+ private void write() throws IOException, FactoryException {
+ int numSupportedCRS = 0;
+ int numDeprecatedCRS = 0;
+ int numAnnotatedCRS = 0;
+ final var rows = new ArrayList<Row>(10000);
+ for (final String code :
factory.getAuthorityCodes(CoordinateReferenceSystem.class)) {
+ final var row = new Row();
+ row.code = escape(code).toString();
+ try {
+ row.setValues(factory,
factory.createCoordinateReferenceSystem(code));
+ } catch (FactoryException exception) {
+ if (row.setValues(factory, exception, code)) {
+ continue;
+ }
}
+ rows.add(row);
+ if (!row.hasError) numSupportedCRS++;
+ if (row.annotation != 0) numAnnotatedCRS++;
+ if (row.isDeprecated) numDeprecatedCRS++;
}
- }
-
- /**
- * Returns the current Apache SIS version, with the {@code -SNAPSHOT}
trailing part omitted.
- *
- * @return the current Apache SIS version.
- */
- private static String getVersion() {
- String version = Version.SIS.toString();
- final int snapshot = version.lastIndexOf('-');
- if (snapshot >= 2) {
- version = version.substring(0, snapshot);
+ final int numCRS = rows.size();
+ sortRows(rows); // May add new rows as section separators.
+ println("h1", "Coordinate Reference Systems recognized by Apache <abbr
title=\"Spatial Information System\">SIS</abbr>™");
+ int item = openTag("p");
+ println("This list is generated from the <abbr>EPSG</abbr> geodetic
dataset version " + versionEPSG + ", together with other sources.");
+ println("Those Coordinate Reference Systems (<abbr>CRS</abbr>) are
supported by the Apache <abbr>SIS</abbr>™ library version " + Version.SIS);
+ println("(provided that a <a href=\"" + URLs.EPSG_INSTALL +
"\">connection to an <abbr>EPSG</abbr> database</a> exists),");
+ println("except those with a red text in the last column.");
+ println("There are " + numCRS + " codes, " + (100 * numSupportedCRS /
numCRS) + "% of them being supported.");
+ closeTags(item);
+ println("p", "<b>Notation:</b>");
+ item = openTag("ul");
+ openTag("li");
+ println("The " + YX_ORDER + " symbol in front of authority codes (" +
Math.round(100f * numAnnotatedCRS / numCRS) + "% of them) "
+ + "identifies left-handed coordinate systems (for example with
<var>latitude</var> axis before <var>longitude</var>).");
+ reopenTag("li");
+ println("The <del>codes with a strike</del> (" + Math.round(100f *
numDeprecatedCRS / numCRS) + "% of them) "
+ + "identify deprecated definitions. In some cases, the remarks
column indicates the replacement.");
+ reopenTag("li");
+ println("Coordinate Reference Systems are grouped by their reference
frame or datum.");
+ closeTags(item);
+ item = openTag("table");
+ printlnWithoutIndentation("<tr><th class=\"narrow\"></th><th
class=\"left-align\">Code</th><th class=\"left-align\">Name</th><th
class=\"left-align\">Remarks</th></tr>");
+ final var buffer = new StringBuilder();
+ int counterForHighlight = 0;
+ for (final Row row : rows) {
+ row.write(buffer, (counterForHighlight & 2) != 0);
+ printlnWithoutIndentation(buffer);
+ buffer.setLength(0);
+ counterForHighlight++;
+ if (row.isSectionHeader) {
+ counterForHighlight = 0;
+ }
}
- return version;
+ closeTags(item);
}
/**
* Creates the text to show in the "Remarks" column for the given CRS.
*/
- private String getRemark(final CoordinateReferenceSystem crs) {
+ private static String getRemark(final CoordinateReferenceSystem crs) {
if (crs instanceof GeographicCRS) {
return (crs.getCoordinateSystem().getDimension() == 3) ?
"Geographic 3D" : "Geographic";
}
@@ -430,13 +256,17 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
return "Geodetic";
}
if (crs instanceof VerticalCRS vertical) {
- final Optional<RealizationMethod> method =
vertical.getDatum().getRealizationMethod();
- if (method.isPresent()) {
- return
CharSequences.camelCaseToSentence(method.get().name().toLowerCase(getLocale()))
+ " realization method";
+ VerticalDatum datum = vertical.getDatum();
+ if (datum != null) {
+ Optional<RealizationMethod> method =
datum.getRealizationMethod();
+ if (method.isPresent()) {
+ String name = method.get().name().toLowerCase(LOCALE);
+ return CharSequences.camelCaseToSentence(name) + "
realization method";
+ }
}
}
if (crs instanceof CompoundCRS compound) {
- final StringBuilder buffer = new StringBuilder();
+ final var buffer = new StringBuilder();
for (final CoordinateReferenceSystem component :
compound.getComponents()) {
if (buffer.length() != 0) {
buffer.append(" + ");
@@ -471,7 +301,7 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
* If the first word of the CRS name seems to be an acronym of the datum
name,
* puts that acronym in a {@code <abbr title="datum name">...</abbr>}
element.
*/
- static String insertAbbreviationTitle(final String crsName, final String
datumName) {
+ private static String insertAbbreviationTitle(final String crsName, final
String datumName) {
int s = crsName.indexOf(' ');
if (s < 0) s = crsName.length();
int p = crsName.indexOf('(');
@@ -511,115 +341,28 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
return "<abbr title=\"" + datumName + "\">" + acronym + "</abbr>" +
crsName.substring(s);
}
- /**
- * Invoked when a CRS has been successfully created. This method modifies
the default
- * {@link org.opengis.test.report.AuthorityCodesReport.Row} attribute
values created
- * by GeoAPI.
- *
- * @param code the authority code of the created object.
- * @param object the object created from the given authority code.
- * @return the created row, or {@code null} if the row should be ignored.
- */
- @Override
- protected Row createRow(final String code, final IdentifiedObject object) {
- final Row row = super.createRow(code, object);
- final CoordinateReferenceSystem crs = (CoordinateReferenceSystem)
object;
- final CoordinateReferenceSystem crsXY =
AbstractCRS.castOrCopy(crs).forConvention(AxesConvention.RIGHT_HANDED);
- if (!Utilities.deepEquals(crs.getCoordinateSystem(),
crsXY.getCoordinateSystem(), ComparisonMode.IGNORE_METADATA)) {
- row.annotation = YX_ORDER;
- }
- CoordinateReferenceSystem replacement = crs;
- row.remark = getRemark(crs);
- /*
- * If the object is deprecated, find the replacement.
- * We do not take the whole comment because it may be pretty long.
- */
- if (object instanceof Deprecable dep) {
- row.isDeprecated = dep.isDeprecated();
- if (row.isDeprecated) {
- String replacedBy = null;
- InternationalString i18n = object.getRemarks().orElse(null);
- for (final Identifier id : object.getIdentifiers()) {
- if (id instanceof Deprecable did && did.isDeprecated()) {
- i18n = did.getRemarks().orElse(null);
- if (id instanceof DeprecatedCode dc) {
- replacedBy = dc.replacedBy;
- }
- break;
- }
- }
- if (i18n != null) {
- row.remark = i18n.toString(getLocale());
- }
- /*
- * If a replacement exists for a deprecated CRS, use the datum
of the replacement instead of
- * the datum of the deprecated CRS for determining in which
section to put the CRS. The reason
- * is that some CRS are deprecated because they were
associated to the wrong datum, in which
- * case the deprecated CRS would appear in the wrong section
if we do not apply this correction.
- */
- if
(!KEEP_DEPRECATED_DATUM.contains(CRS.getSingleComponents(crs).get(0).getDatum().getName().getCode()))
{
- if (replacedBy != null) try {
- replacement =
factory.createCoordinateReferenceSystem("EPSG:" + replacedBy);
- } catch (FactoryException e) {
- // Ignore - keep the datum of the deprecated object.
- }
- }
- }
- }
- ((ByName)
row).setup(CRS.getSingleComponents(replacement).get(0).getDatum(),
unusedDatumMapping);
- return row;
- }
-
- /**
- * Invoked when a CRS creation failed. This method modifies the default
- * {@link org.opengis.test.report.AuthorityCodesReport.Row} attribute
values
- * created by GeoAPI.
- *
- * @param code the authority code of the object to create.
- * @param exception the exception that occurred while creating the
identified object.
- * @return the created row, or {@code null} if the row should be ignored.
- */
- @Override
- protected Row createRow(final String code, final FactoryException
exception) {
- if (code.startsWith(Constants.PROJ4 +
DefaultNameSpace.DEFAULT_SEPARATOR)) {
- return null;
- }
- final Row row = super.createRow(code, exception);
- try {
- row.name =
factory.getDescriptionText(CoordinateReferenceSystem.class,
code).get().toString(getLocale());
- } catch (FactoryException e) {
- Logging.unexpectedException(null,
CoordinateReferenceSystems.class, "createRow", e);
- }
- if (code.startsWith("AUTO2:")) {
- // It is normal to be unable to instantiate an "AUTO" CRS,
- // because those authority codes need parameters.
- row.hasError = false;
- row.remark = "Projected";
- ((ByName) row).setup(CommonCRS.WGS84.datum(true),
unusedDatumMapping);
- } else {
- row.remark = exception.getLocalizedMessage();
- ((ByName) row).setup(null, unusedDatumMapping);
- }
- return row;
- }
-
- /**
- * Invoked by {@link AuthorityCodesReport} for creating a new row instance.
- *
- * @return the new row instance.
- */
- @Override
- protected Row newRow() {
- return new ByName();
- }
-
/**
* A row with a natural ordering that use the first part of the name
before to use the authority code.
+ * Every {@link String} fields in this class must be valid HTML. If some
text is expected to print
+ * {@code <} or {@code >} characters, then those characters need to be
escaped to their HTML entities.
+ *
+ * <p>Content of each {@code Row} instance is written in the following
order:</p>
+ * <ol>
+ * <li>{@link #annotation} if explicitly set (the default is none).</li>
+ * <li>{@link #code}</li>
+ * <li>{@link #name}</li>
+ * <li>{@link #remark}</li>
+ * </ol>
+ *
+ * <p>Other attributes ({@link #isSectionHeader}, {@link #isDeprecated}
and {@link #hasError})
+ * are not directly written in the table, but affect their styling.</p>
+ *
+ * <h2>Rules for sorting the rows</h2>
* We use only the part of the name prior some keywords (e.g. {@code
"zone"}).
- * For example if the following codes:
+ * For example in the following codes:
*
* <pre class="text">
* EPSG:32609 WGS 84 / UTM zone 9N
@@ -628,11 +371,12 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
* We compare only the "WGS 84 / UTM" string, then the code. This is a
reasonably easy way to keep a more
* natural ordering ("9" sorted before "10", "UTM North" projections kept
together and same for South).
*/
- private static final class ByName extends Row {
+ private static final class Row implements Comparable<Row> {
/**
- * A string derived from the {@link #name} to use for sorting.
+ * {@code true} if this row should actually be used as a section
header.
+ * We insert rows with this flag set to {@code true} for splitting the
large table is smaller sections.
*/
- private String reducedName;
+ boolean isSectionHeader;
/**
* The datum name, or {@code null} if unknown.
@@ -640,41 +384,144 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
*/
String section;
+ /**
+ * The authority code in HTML.
+ */
+ String code;
+
+ /**
+ * The object name in HTML, or {@code null} if none. By default, this
field is set to the value of
+ * <code>{@linkplain IdentifiedObject#getName()}.{@linkplain
Identifier#getCode() getCode()}</code>.
+ */
+ String name;
+
+ /**
+ * A remark in HTML to display after the name, or {@code null} if none.
+ */
+ private String remark;
+
+ /**
+ * A string derived from the {@link #name} to use for sorting.
+ */
+ private String reducedName;
+
+ /**
+ * A small symbol to put before the {@linkplain #code} and {@linkplain
#name}, or 0 (the default) if none.
+ * For example, it can indicate a <abbr>CRS</abbr> having unusual axes
order.
+ */
+ char annotation;
+
+ /**
+ * {@code true} if this authority code is deprecated, or {@code false}
otherwise.
+ */
+ boolean isDeprecated;
+
+ /**
+ * {@code true} if an exception occurred while creating the identified
object.
+ * If {@code true}, then the {@link #remark} field will contains the
exception localized message.
+ */
+ boolean hasError;
+
/**
* Creates a new row.
*/
- ByName() {
+ Row() {
}
/**
- * Computes the {@link #reducedName} field value.
+ * Invoked when the <abbr>CRS</abbr> cannot be constructed because of
the given error.
+ *
+ * @param factory the factory which created the <abbr>CRS</abbr>.
+ * @param cause the reason why the <abbr>CRS</abbr> cannot be
constructed.
+ * @param code the authority code without <abbr>HTML</abbr>
escapes.
+ * @return whether to ignore this row.
*/
- final void setup(final Datum datum, final Set<String>
unusedDatumMapping) {
- final String datumName;
- if (datum != null) {
- datumName = datum.getName().getCode();
+ final boolean setValues(final CRSAuthorityFactory factory, final
FactoryException cause, final String code) {
+ if (code.startsWith(Constants.PROJ4 +
DefaultNameSpace.DEFAULT_SEPARATOR)) {
+ return true;
+ }
+ String message = cause.getMessage();
+ if (message == null) {
+ message = cause.toString();
+ }
+ remark = escape(message).toString();
+ hasError = true;
+ try {
+ name =
toLocalizedString(factory.getDescriptionText(CoordinateReferenceSystem.class,
code).get());
+ } catch (FactoryException e) {
+ Logging.unexpectedException(null,
CoordinateReferenceSystems.class, "createRow", e);
+ }
+ if (code.startsWith("AUTO2:")) {
+ // It is normal to be unable to instantiate an "AUTO" CRS,
+ // because those authority codes need parameters.
+ hasError = false;
+ remark = "Projected";
+ setSection(CommonCRS.WGS84.datum(true));
} else {
- // Temporary patch (TODO: remove after we implemented the
missing methods in SIS)
- if (name.startsWith("NSIDC EASE-Grid")) {
- datumName = "Unspecified datum";
- } else if (code.equals("EPSG:2163")) {
- datumName = "Unspecified datum";
- } else if (code.equals("EPSG:5818")) {
- datumName = "Seismic bin grid datum";
+ if (cause instanceof NoSuchIdentifierException e) {
+ remark = '“' + e.getIdentifierCode() + "” operation method
is not yet supported.";
} else {
- datumName = null; // Keep ordering based on the name.
+ remark = cause.getLocalizedMessage();
}
+ setSection(null);
}
- if (datumName != null) {
- final String prefix;
- final Map.Entry<String,String> group =
SECTION_TITLES.floorEntry(datumName);
- if (group != null && datumName.startsWith(prefix =
group.getKey())) {
- unusedDatumMapping.remove(prefix);
- section = group.getValue();
- } else {
- section = datumName;
+ return false;
+ }
+
+ /**
+ * Invoked when a <abbr>CRS</abbr> has been successfully created.
+ *
+ * @param factory the factory which created the <abbr>CRS</abbr>.
+ * @param crs the object created from the authority code.
+ * @return the created row, or {@code null} if the row should be
ignored.
+ */
+ final void setValues(final CRSAuthorityFactory factory,
CoordinateReferenceSystem crs) {
+ name = escape(crs.getName().getCode()).toString();
+ final CoordinateReferenceSystem crsXY =
AbstractCRS.castOrCopy(crs).forConvention(AxesConvention.RIGHT_HANDED);
+ if (!Utilities.deepEquals(crs.getCoordinateSystem(),
crsXY.getCoordinateSystem(), ComparisonMode.IGNORE_METADATA)) {
+ annotation = YX_ORDER;
+ }
+ remark = getRemark(crs);
+ /*
+ * If the object is deprecated, find the replacement.
+ * We do not take the whole comment because it may be pretty long.
+ */
+ if (crs instanceof Deprecable dep) {
+ isDeprecated = dep.isDeprecated();
+ if (isDeprecated) {
+ String replacedBy = null;
+ InternationalString i18n = crs.getRemarks().orElse(null);
+ for (final Identifier id : crs.getIdentifiers()) {
+ if (id instanceof Deprecable did &&
did.isDeprecated()) {
+ i18n = did.getRemarks().orElse(null);
+ if (id instanceof DeprecatedCode dc) {
+ replacedBy = dc.replacedBy;
+ }
+ break;
+ }
+ }
+ remark = toLocalizedString(i18n);
+ /*
+ * If a replacement exists for a deprecated CRS, use the
datum of the replacement instead of
+ * the datum of the deprecated CRS for determining in
which section to put the CRS. The reason
+ * is that some CRS are deprecated because they were
associated to the wrong datum, in which
+ * case the deprecated CRS would appear in the wrong
section if we do not apply this correction.
+ */
+ if (replacedBy != null) try {
+ crs = factory.createCoordinateReferenceSystem("EPSG:"
+ replacedBy);
+ } catch (FactoryException e) {
+ // Ignore - keep the datum of the deprecated object.
+ }
}
}
+ setSection(CRS.getSingleComponents(crs).get(0).getDatum());
+ }
+
+ /**
+ * Computes the {@link #reducedName} field value.
+ * It determines the section where the <abbr>CRS</abbr> will be placed.
+ */
+ private void setSection(final Datum datum) {
/*
* Get a copy of the name in all lower case.
*/
@@ -703,8 +550,9 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
if (s != 0) b.setLength(s);
uniformizeZoneNumber(b);
reducedName = b.toString();
- if (datumName != null) {
- name = insertAbbreviationTitle(name, datumName);
+ if (datum != null) {
+ section = datum.getName().getCode().replace('_', ' ');
+ name = insertAbbreviationTitle(name, section);
}
}
@@ -753,13 +601,51 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
}
/**
- * Compares this row with the given row for ordering by name.
+ * Writes this row to the given stream.
+ *
+ * @param out where to write this row.
+ * @param highlight whether to highlight this row.
+ * @throws IOException if an error occurred while writing this row.
+ */
+ final void write(final StringBuilder out, final boolean highlight) {
+ if (isSectionHeader) {
+ out.append("<tr class=\"separator\"><td
colspan=\"4\">").append(name).append("</td></tr>");
+ return;
+ }
+ out.append("<tr");
+ if (highlight) out.append(" class=\"HL\"");
+ out.append("><td class=\"narrow\">");
+ if (annotation != 0) out.append(annotation);
+ out.append("</td><td>");
+ if (code != null) {
+ out.append("<code>");
+ if (isDeprecated) out.append("<del>");
+ out.append(code);
+ if (isDeprecated) out.append("</del>");
+ out.append("</code>");
+ }
+ out.append("</td><td>");
+ if (name != null) out.append(name);
+ out.append("</td><td");
+ if (hasError) out.append(" class=\"error\"");
+ else if (isDeprecated) out.append(" class=\"warning\"");
+ out.append('>');
+ if (remark != null) out.append(remark);
+ out.append("</td></tr>");
+ }
+
+ /**
+ * Compares this row with the given row for ordering by name and
authority code.
*/
@Override
public int compareTo(final Row o) {
- int n = reducedName.compareTo(((ByName) o).reducedName);
+ int n = reducedName.compareTo(o.reducedName);
if (n == 0) {
- n = super.compareTo(o);
+ try {
+ n = Integer.compare(Integer.parseInt(code),
Integer.parseInt(o.code));
+ } catch (NumberFormatException e) {
+ n = code.compareTo(o.code);
+ }
}
return n;
}
@@ -768,13 +654,12 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
/**
* Sorts the rows, then inserts sections between CRS instances that use
different datums.
*/
- @Override
- protected void sortRows() {
- super.sortRows();
+ private static void sortRows(final List<Row> rows) {
+ Collections.sort(rows);
@SuppressWarnings("SuspiciousToArrayCall")
- final ByName[] data = rows.toArray(ByName[]::new);
- final Map<String,String> sections = new TreeMap<>();
- for (final ByName row : data) {
+ final Row[] data = rows.toArray(Row[]::new);
+ final var sections = new TreeMap<String,String>();
+ for (final Row row : data) {
final String section = row.section;
if (section != null) {
sections.put(CharSequences.toASCII(section).toString().toLowerCase(), section);
@@ -793,7 +678,7 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
rows.add(separator);
boolean found = false;
for (int i=0; i<data.length; i++) {
- final ByName row = data[i];
+ final Row row = data[i];
if (row != null) {
if (row.section != null) {
found = section.equals(row.section);
@@ -807,7 +692,7 @@ public final class CoordinateReferenceSystems extends
AuthorityCodesReport {
}
}
boolean found = false;
- for (final ByName row : data) {
+ for (final Row row : data) {
if (row != null) {
if (!found) {
final Row separator = new Row();
diff --git
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/HTMLGenerator.java
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/HTMLGenerator.java
index ea8f216617..1a7ceb246d 100644
---
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/HTMLGenerator.java
+++
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/report/HTMLGenerator.java
@@ -35,8 +35,9 @@ import org.apache.sis.util.internal.shared.Constants;
* Base class of some classes used to generate HTML pages to be published on
* the <a href="https://sis.apache.org/">https://sis.apache.org/</a> web site.
*
- * <p>This class creates files in the current default directory. It is user's
responsibility
- * to move the files to the appropriate Apache SIS {@code "content/"} site
directory.</p>
+ * <p>This class creates files in the current default directory.
+ * Maintainers need to move the files to the Apache <abbr>SIS</abbr>
+ * {@code "content/"} site directory.</p>
*
* @author Martin Desruisseaux (Geomatys)
*/
@@ -87,7 +88,7 @@ abstract class HTMLGenerator implements Closeable {
* @throws IOException if the file cannot be created (e.g. because it
already exists).
*/
HTMLGenerator(final String filename, final String title, final String css)
throws IOException {
- final File file = new File(filename);
+ final var file = new File(filename);
if (file.exists()) {
throw new IOException("File " + file.getAbsolutePath() + " already
exists.");
}
@@ -243,7 +244,7 @@ abstract class HTMLGenerator implements Closeable {
}
/**
- * Writes the given text on its own line, then write EOL sequence.
+ * Writes the given text on its own line, then write <abbr>EOL</abbr>
sequence.
* The {@code &}, {@code <} and {@code >} characters are
<strong>not</strong> escaped.
* For escaping those characters, invoke <code>println({@linkplain
#escape(CharSequence) escape}(value))</code>.
*
@@ -258,6 +259,21 @@ abstract class HTMLGenerator implements Closeable {
}
}
+ /**
+ * Writes the given text on its own line without indentation.
+ * This is the same as {@link #println(CharSequence)} but more compact.
+ * Should be used only for very large tables.
+ *
+ * @param value the text to write, or {@code null} if none.
+ * @throws IOException if an error occurred while writing to the file.
+ */
+ final void printlnWithoutIndentation(final CharSequence value) throws
IOException {
+ if (value != null) {
+ out.write(value.toString());
+ out.newLine();
+ }
+ }
+
/**
* Closes the HTML generator.
*
diff --git
a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
index 131f5dcbb5..49df55c576 100644
---
a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
+++
b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
+import java.util.Map;
import javax.sql.DataSource;
import java.util.ServiceLoader;
import org.opengis.util.FactoryException;
@@ -27,6 +28,7 @@ import org.apache.sis.setup.InstallationResources;
import org.apache.sis.metadata.sql.internal.shared.Initializer;
import org.apache.sis.system.DataDirectory;
import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.factory.sql.EPSGFactory;
// Test dependencies
import org.junit.jupiter.api.Test;
@@ -100,6 +102,9 @@ public final strictfp class EmbeddedResourcesTest {
* Tests connecting to the database.
*
* @throws Exception if an error occurred while fetching the data source,
or connecting to the database.
+ *
+ * @todo The test is currently not executed because {@code SIS_DATA} is
set by the build script.
+ * We need a way to ignore it.
*/
@Test
public void testConnection() throws Exception {
@@ -118,19 +123,33 @@ public final strictfp class EmbeddedResourcesTest {
}
}
}
+ try (EPSGFactory factory = new EPSGFactory(Map.of("dataSource",
Initializer.getDataSource()))) {
+
verifyEPSG_6676(factory.createCoordinateReferenceSystem("EPSG:6676"));
+ }
}
/**
- * Tests {@link CRS#forCode(String)} with the embedded database. This test
asks for a CRS for which
- * no hard-coded fallback exists in {@link
org.apache.sis.referencing.CommonCRS}. Consequently this
- * test should fail if we do not have a connection to a complete EPSG
database.
+ * Tests the use of the embedded database. This test asks for a CRS for
which no hard-coded fallback exists
+ * in {@link org.apache.sis.referencing.CommonCRS}. Consequently, this
test should fail if we do not have a
+ * connection to a complete <abbr>EPSG</abbr> database.
+ *
+ * @throws FactoryException if an error occurred while creating the
<abbr>CRS</abbr>.
*
- * @throws FactoryException if an error occurred while creating the CRS.
+ * @todo The test is currently not executed because {@code SIS_DATA} is
set by the build script.
+ * We need a way to ignore it.
*/
@Test
public void testCrsforCode() throws FactoryException {
assumeContainsEPSG();
- CoordinateReferenceSystem crs = CRS.forCode("EPSG:6676");
+ final String dir = DataDirectory.getenv();
+ assumeTrue((dir == null) || dir.isEmpty(), "The SIS_DATA environment
variable must be unset for enabling this test.");
+ verifyEPSG_6676(CRS.forCode("EPSG:6676"));
+ }
+
+ /**
+ * Verifies the <abbr>CRS</abbr> created from code EPSG:6676
+ */
+ private static void verifyEPSG_6676(final CoordinateReferenceSystem crs) {
String area =
TestUtilities.getSingleton(crs.getDomains()).getDomainOfValidity().getDescription().toString();
assertTrue(area.contains("Japan"), area);
}
diff --git
a/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptUpdater.java
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptUpdater.java
index 82fa5b4dc9..551bf54de0 100644
---
a/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptUpdater.java
+++
b/optional/src/org.apache.sis.referencing.epsg/test/org/apache/sis/referencing/factory/sql/epsg/DataScriptUpdater.java
@@ -83,7 +83,17 @@ public final class DataScriptUpdater {
formatter.addAccentedCharacters("Nivellement Général de Nouvelle
Calédonie");
formatter.addAccentedCharacters("Nivellement Général de Polynésie
Française");
formatter.addAccentedCharacters("Nivellement Général Guyanais");
+ formatter.addAccentedCharacters("Réseau de Référence des Antilles
Françaises");
+ formatter.addAccentedCharacters("Réseau Géodesique Français");
+ formatter.addAccentedCharacters("Réseau Géodésique de la Polynésie
Française");
+ formatter.addAccentedCharacters("Réseau Géodésique de la RDC");
+ formatter.addAccentedCharacters("Réseau Géodésique de la Réunion");
+ formatter.addAccentedCharacters("Réseau Géodésique de Mayotte");
formatter.addAccentedCharacters("Réseau Géodésique de Nouvelle
Calédonie");
+ formatter.addAccentedCharacters("Réseau Géodésique de Saint Pierre
et Miquelon");
+ formatter.addAccentedCharacters("Réseau Géodésique de Wallis et
Futuna");
+ formatter.addAccentedCharacters("Réseau Géodésique des Antilles
Françaises");
+ formatter.addAccentedCharacters("Réseau Géodésique des Terres
Australes et Antarctiques Françaises");
formatter.addAccentedCharacters("Réseau National Belge");
formatter.addAccentedCharacters("Posiciones Geodésicas
Argentinas");
formatter.run(Path.of(arguments[0]), Path.of(arguments[1]));