This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit c9dfe193eca6928f27ceb071973d17f001bd0546 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu Jul 24 17:10:12 2025 +0200 Support the `CONVENTIONAL_RS_CODE` column of the "Datum" table of EPSG version 10+. --- .../referencing/factory/AuthorityFactoryProxy.java | 11 +++ .../factory/ConcurrentAuthorityFactory.java | 28 +++++++- .../factory/GeodeticAuthorityFactory.java | 79 +++++++++++----------- .../referencing/factory/sql/EPSGDataAccess.java | 79 ++++++++++++++++++---- .../sis/referencing/factory/sql/SQLTranslator.java | 1 + 5 files changed, 144 insertions(+), 54 deletions(-) diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java index df276fe9ca..96018ea2c8 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/AuthorityFactoryProxy.java @@ -202,6 +202,17 @@ abstract class AuthorityFactoryProxy<T> { } }; + @SuppressWarnings("unchecked") + static final AuthorityFactoryProxy<DatumEnsemble<?>> ENSEMBLE = + new AuthorityFactoryProxy<DatumEnsemble<?>>((Class) DatumEnsemble.class, AuthorityFactoryIdentifier.Type.DATUM) { + @Override DatumEnsemble<?> create(GeodeticAuthorityFactory factory, String code) throws FactoryException { + return factory.createDatumEnsemble(code); + } + @Override DatumEnsemble<?> createFromAPI(AuthorityFactory factory, String code) throws FactoryException { + return datumFactory(factory).createDatumEnsemble(code); + } + }; + static final AuthorityFactoryProxy<Datum> DATUM = new AuthorityFactoryProxy<Datum>(Datum.class, AuthorityFactoryIdentifier.Type.DATUM) { @Override Datum create(GeodeticAuthorityFactory factory, String code) throws FactoryException { diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java index 7dda302217..5f8fd800ea 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java @@ -1093,7 +1093,33 @@ public abstract class ConcurrentAuthorityFactory<DAO extends GeodeticAuthorityFa } /** - * Returns an arbitrary datum from a code. The returned object will typically be an + * Returns an arbitrary datum ensemble from a code. + * The default implementation performs the following steps: + * <ul> + * <li>Return the cached instance for the given code if such instance already exists.</li> + * <li>Otherwise if the Data Access Object (DAO) overrides the {@code createDatumEnsemble(String)} + * method, invoke that method and cache the result for future use.</li> + * <li>Otherwise delegate to the {@link GeodeticAuthorityFactory#createDatumEnsemble(String)} + * method in the parent class. This allows to check if the more generic + * {@link #createObject(String)} method cached a value before to try that method.</li> + * </ul> + * + * @return the datum for the given code. + * @throws FactoryException if the object creation failed. + * + * @since 1.5 + */ + @Override + public DatumEnsemble<?> createDatumEnsemble(final String code) throws FactoryException { + if (isDefault(DatumEnsemble.class)) { + return super.createDatumEnsemble(code); + } + return create(AuthorityFactoryProxy.ENSEMBLE, code); + } + + /** + * Returns an arbitrary datum from a code. The returned object will typically be a + * {@link GeodeticDatum}, {@link VerticalDatum}, {@link TemporalDatum} or {@link EngineeringDatum}. * The default implementation performs the following steps: * <ul> * <li>Return the cached instance for the given code if such instance already exists.</li> diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java index 8016fd352c..30ebddb624 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticAuthorityFactory.java @@ -524,6 +524,46 @@ public abstract class GeodeticAuthorityFactory extends AbstractFactory implement return cast(EngineeringCRS.class, createCoordinateReferenceSystem(code), code); } + /** + * Creates an arbitrary datum ensemble from a code. + * A datum ensemble is a collection of datums which for low accuracy requirements + * may be considered to be insignificantly different from each other. + * + * <h4>Examples</h4> + * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining + * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use. + * A frequently used authority is "EPSG", which includes the following codes: + * + * <table class="sis"> + * <caption>Authority codes examples</caption> + * <tr><th>Code</th> <th>Description</th></tr> + * <tr><td>EPSG:6326</td> <td>World Geodetic System 1984</td></tr> + * <tr><td>EPSG:6258</td> <td>European Terrestrial Reference System 1989</td></tr> + * </table> + * + * <h4>Default implementation</h4> + * The default implementation delegates to {@link #createDatum(String)} and casts the result. + * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown. + * This approach assumes that the datum ensemble implements also the {@link Datum} interface. + * It is the case of the {@link org.apache.sis.referencing.datum.DefaultDatumEnsemble} class. + * + * <p>This default implementation is unusual, but is convenient for the implementation strategy + * of Apache <abbr>SIS</abbr> and for the structure of the <abbr>EPSG</abbr> geodetic dataset, + * which uses the same {@code "Datum"} table for storing the properties of the two kinds of object.</p> + * + * @param code value allocated by authority. + * @return the datum ensemble for the given code. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the object creation failed for some other reason. + * + * @see org.apache.sis.referencing.datum.DefaultDatumEnsemble + * + * @since 1.5 + */ + public DatumEnsemble<?> createDatumEnsemble(final String code) throws NoSuchAuthorityCodeException, FactoryException { + return cast(DatumEnsemble.class, createDatum(code), code); + } + /** * Creates an arbitrary datum from a code. The returned object will typically be an * instance of {@link GeodeticDatum}, {@link VerticalDatum} or {@link TemporalDatum}. @@ -708,45 +748,6 @@ public abstract class GeodeticAuthorityFactory extends AbstractFactory implement return cast(EngineeringDatum.class, createDatum(code), code); } - /** - * Creates an arbitrary datum ensemble from a code. - * A datum ensemble is a collection of datums which for low accuracy requirements - * may be considered to be insignificantly different from each other. - * - * <h4>Examples</h4> - * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining - * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use. - * A frequently used authority is "EPSG", which includes the following codes: - * - * <table class="sis"> - * <caption>Authority codes examples</caption> - * <tr><th>Code</th> <th>Description</th></tr> - * <tr><td>EPSG:6326</td> <td>World Geodetic System 1984</td></tr> - * <tr><td>EPSG:6258</td> <td>European Terrestrial Reference System 1989</td></tr> - * </table> - * - * <h4>Default implementation</h4> - * The default implementation delegates to {@link #createDatum(String)} and casts the result. - * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown. - * Note that the delegation to {@code createDatum(…)} works only if the implementation - * stores datum and datum ensembles together. This is the case of the <abbr>EPSG</abbr> - * geodetic dataset for instance. In such case, the datum ensemble can be returned as an - * object that implements both the {@link Datum} and {@link DatumEnsemble} interfaces, - * such as the {@link org.apache.sis.referencing.datum.DefaultDatumEnsemble} class. - * - * @param code value allocated by authority. - * @return the datum for the given code. - * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. - * @throws FactoryException if the object creation failed for some other reason. - * - * @see org.apache.sis.referencing.datum.DefaultDatumEnsemble - * - * @since 1.5 - */ - public DatumEnsemble<?> createDatumEnsemble(final String code) throws NoSuchAuthorityCodeException, FactoryException { - return cast(DatumEnsemble.class, createDatum(code), code); - } - /** * Creates a geometric figure that can be used to describe the approximate shape of the earth. * In mathematical terms, it is a surface formed by the rotation of an ellipse about its minor axis. diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java index 71746b6337..1a43f3d334 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java @@ -258,6 +258,17 @@ public class EPSGDataAccess extends GeodeticAuthorityFactory implements CRSAutho */ private final Map<Integer, AxisName> axisNames = new HashMap<>(); + /** + * Cache for conventional reference systems, which are instances of the generic {@link IdentifiedObject} type. + * This is not cached by {@code ConcurrentAuthorityFactory} because there is not yet a specific type for that. + * Since we are not using the shared cache, there is a possibility that many objects are created for the same + * conventional <abbr>RS</abbr> code. However, this duplication should not happen if each instance appears in + * only one datum ensemble created by {@link #createDatumEnsemble(Integer, Map)}. + * + * @see #createConventionalRS(Integer) + */ + private final Map<Integer, IdentifiedObject> conventionalRS = new HashMap<>(); + /** * Cache for realization methods. This service is not provided by {@code ConcurrentAuthorityFactory} * because the realization method table is specific to the <abbr>EPSG</abbr> authority factory. @@ -1690,20 +1701,21 @@ codes: for (int i=0; i<codes.length; i++) { ArgumentChecks.ensureNonNull("code", code); Datum returnValue = null; try (ResultSet result = executeQuery("Datum", "DATUM_CODE", "DATUM_NAME", - "SELECT DATUM_CODE," + // [ 1] - " DATUM_NAME," + // [ 2] - " DATUM_TYPE," + // [ 3] - " ORIGIN_DESCRIPTION," + // [ 4] - " ANCHOR_EPOCH," + // [ 5] - " FRAME_REFERENCE_EPOCH," + // [ 6] — NULL for static datum, non-null if dynamic. - " PUBLICATION_DATE," + // [ 7] — Was REALIZATION_EPOCH in EPSG version 9. - " AREA_OF_USE_CODE," + // [ 8] — Deprecated since EPSG version 10 (always null) - " DATUM_SCOPE," + // [ 9] - " REMARKS," + // [10] - " DEPRECATED," + // [11] - " ELLIPSOID_CODE," + // [12] — Only for geodetic type - " PRIME_MERIDIAN_CODE," + // [13] — Only for geodetic type - " REALIZATION_METHOD_CODE" + // [14] — Only for vertical type + "SELECT DATUM_CODE," + // [ 1] + " DATUM_NAME," + // [ 2] + " DATUM_TYPE," + // [ 3] + " ORIGIN_DESCRIPTION," + // [ 4] + " ANCHOR_EPOCH," + // [ 5] + " FRAME_REFERENCE_EPOCH," + // [ 6] — NULL for static datum, non-null if dynamic. + " PUBLICATION_DATE," + // [ 7] — Was REALIZATION_EPOCH in EPSG version 9. + " AREA_OF_USE_CODE," + // [ 8] — Deprecated since EPSG version 10 (always null) + " DATUM_SCOPE," + // [ 9] + " REMARKS," + // [10] + " DEPRECATED," + // [11] + " ELLIPSOID_CODE," + // [12] — Only for geodetic type + " PRIME_MERIDIAN_CODE," + // [13] — Only for geodetic type + " REALIZATION_METHOD_CODE," + // [14] — Only for vertical type + " CONVENTIONAL_RS_CODE" + // [15] — Only for members of an ensemble " FROM \"Datum\"" + " WHERE DATUM_CODE = ?", code)) { @@ -1725,6 +1737,7 @@ codes: for (int i=0; i<codes.length; i++) { properties.put(Datum.ANCHOR_DEFINITION_KEY, anchor); properties.put(Datum.ANCHOR_EPOCH_KEY, epoch); properties.put(Datum.PUBLICATION_DATE_KEY, publish); + properties.put(Datum.CONVENTIONAL_RS_KEY, createConventionalRS(getOptionalInteger(result, 15))); /* * The following switch statement should have a case for all "epsg_datum_kind" values enumerated * in the "EPSG_Prepare.sql" file, except that the values in this Java code are in lower cases. @@ -1819,6 +1832,9 @@ codes: for (int i=0; i<codes.length; i++) { * @param code value allocated by EPSG. * @param properties properties to assign to the datum ensemble. * @return the datum ensemble for the given code, or {@code null} if not found. + * + * @see #createDatum(String) + * @see #createDatumEnsemble(String) */ private DatumEnsemble<?> createDatumEnsemble(final Integer code, final Map<String,Object> properties) throws SQLException, FactoryException @@ -1851,6 +1867,41 @@ codes: for (int i=0; i<codes.length; i++) { return owner.datumFactory.createDatumEnsemble(properties, members, PositionalAccuracyConstant.ensemble(accuracy)); } + /** + * Creates a conventional reference system from a code. + * All members of a datum ensemble shall have the same conventional reference system. + * + * @param code value allocated by EPSG, or {@code null} if none. + * @return the datum for the given code, or {@code null} if not found. + */ + private IdentifiedObject createConventionalRS(final Integer code) throws SQLException, FactoryException { + assert Thread.holdsLock(this); + IdentifiedObject returnValue = conventionalRS.get(code); + if (returnValue == null && code != null) { + try (ResultSet result = executeQuery("ConventionalRS", + "SELECT CONVENTIONAL_RS_CODE," + + " CONVENTIONAL_RS_NAME," + + " REMARKS," + + " DEPRECATED" + + " FROM \"ConventionalRS\"" + + " WHERE CONVENTIONAL_RS_CODE = ?", code)) + { + while (result.next()) { + final Integer epsg = getInteger (code, result, 1); + final String name = getString (code, result, 2); + final String remarks = getOptionalString (result, 3); + final boolean deprecated = getOptionalBoolean (result, 4); + @SuppressWarnings("LocalVariableHidesMemberVariable") + Map<String,Object> properties = createProperties("ConventionalRS", + epsg, name, null, null, null, remarks, deprecated); + returnValue = ensureSingleton(new AbstractIdentifiedObject(properties), returnValue, code); + } + } + conventionalRS.put(code, returnValue); + } + return returnValue; + } + /** * Returns Bursa-Wolf parameters for a geodetic reference frame. * If the specified datum has no conversion information, then this method returns {@code 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 cd160a4feb..8e89ab2ca9 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 @@ -388,6 +388,7 @@ skip: try (ResultSet result = md.getColumns(catalog, schemaPattern, toActualTa addMissingColumn ("ANCHOR_EPOCH", "DOUBLE PRECISION"); // In table "Datum". addMissingColumn ("FRAME_REFERENCE_EPOCH", "DOUBLE PRECISION"); // In table "Datum". addMissingColumn ("REALIZATION_METHOD_CODE", "INTEGER"); // In table "Datum". + addMissingColumn ("CONVENTIONAL_RS_CODE", "INTEGER"); // In table "Datum". columnRenaming = Map.copyOf(columnRenaming); } /*
