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 02e6ffe626 Add an implementation of `RegisterOperations` (a new interface added in ISO 19111:2019). 02e6ffe626 is described below commit 02e6ffe626b5eaae5747b80a54567bb01982e9e3 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Jun 28 20:09:55 2024 +0200 Add an implementation of `RegisterOperations` (a new interface added in ISO 19111:2019). --- .../geometry/wrapper/SpatialOperationContext.java | 4 +- .../org/apache/sis/util/iso/AbstractFactory.java | 22 +- .../main/module-info.java | 3 + .../main/org/apache/sis/referencing/CRS.java | 3 +- .../sis/referencing/MultiRegisterOperations.java | 445 +++++++++++++++++++++ .../factory/MultiAuthoritiesFactory.java | 22 +- .../operation/CoordinateOperationRegistry.java | 5 +- .../DefaultCoordinateOperationFactory.java | 4 +- .../factory/GeodeticObjectFactoryTest.java | 18 +- .../referencing/geoapi/AuthorityFactoryTest.java | 3 +- geoapi/snapshot | 2 +- 11 files changed, 513 insertions(+), 18 deletions(-) diff --git a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java index 208b00e466..7fa87083ee 100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java @@ -357,8 +357,8 @@ select: if (commonCRS == null) { /** Creates the {@link #INSTANCE} singleton. */ private Projector() throws FactoryException { - final ReferencingFactoryContainer f = new ReferencingFactoryContainer(); - method = f.getCoordinateOperationFactory().getOperationMethod("Mercator_2SP"); + final var f = new ReferencingFactoryContainer(); + method = f.findOperationMethod("Mercator_2SP"); cartCS = f.getStandardProjectedCS(); name = Map.of(DefaultConversion.NAME_KEY, new ImmutableIdentifier(Citations.SIS, "SIS", "Mercator for geometry")); diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/AbstractFactory.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/AbstractFactory.java index 26b8a18ef3..11a32493d4 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/AbstractFactory.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/AbstractFactory.java @@ -19,13 +19,14 @@ package org.apache.sis.util.iso; import org.opengis.util.Factory; import org.opengis.metadata.citation.Citation; import org.apache.sis.metadata.simple.SimpleCitation; +import org.apache.sis.util.privy.Strings; /** * Base class of factories provided in the Apache SIS library. * * @author Martin Desruisseaux (Geomatys) - * @version 0.3 + * @version 1.5 * @since 0.3 */ public abstract class AbstractFactory implements Factory { @@ -55,4 +56,23 @@ public abstract class AbstractFactory implements Factory { } return null; } + + /** + * Returns a string representation of this factory for debugging purposes. + * This string representation may change in any future version of Apache SIS. + * + * @return a string representation of this factory. + * + * @since 1.5 + */ + @Override + public String toString() { + final var args = new Object[2]; + Citation c = getVendor(); + if (c != null) { + args[0] = "vendor"; + args[1] = c.getTitle(); + } + return Strings.toString(getClass(), args); + } } diff --git a/endorsed/src/org.apache.sis.referencing/main/module-info.java b/endorsed/src/org.apache.sis.referencing/main/module-info.java index c19d16bee7..ba29bb0806 100644 --- a/endorsed/src/org.apache.sis.referencing/main/module-info.java +++ b/endorsed/src/org.apache.sis.referencing/main/module-info.java @@ -33,6 +33,9 @@ module org.apache.sis.referencing { provides org.apache.sis.metadata.sql.privy.Initializer with org.apache.sis.referencing.internal.DatabaseListener; + provides org.opengis.referencing.RegisterOperations + with org.apache.sis.referencing.MultiRegisterOperations; + provides org.opengis.referencing.crs.CRSFactory with org.apache.sis.referencing.factory.GeodeticObjectFactory; diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java index 8003fd784a..7cb26286f0 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java @@ -1576,8 +1576,7 @@ check: while (lower != 0 || upper != dimension) { * @return the system-wide authority factory used by SIS for the given authority. * @throws FactoryException if no factory can be returned for the given authority. * - * @see #forCode(String) - * @see org.apache.sis.referencing.factory.MultiAuthoritiesFactory + * @see MultiRegisterOperations * * @since 0.7 */ diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java new file mode 100644 index 0000000000..6bf7628dbd --- /dev/null +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/MultiRegisterOperations.java @@ -0,0 +1,445 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.referencing; + +import java.util.Map; +import java.util.Set; +import java.util.List; +import java.util.Iterator; +import java.util.AbstractSet; +import java.util.Objects; +import java.util.Optional; +import org.opengis.util.Factory; +import org.opengis.util.FactoryException; +import org.opengis.util.InternationalString; +import org.opengis.metadata.citation.Citation; +import org.opengis.metadata.extent.GeographicBoundingBox; +import org.opengis.referencing.IdentifiedObject; +import org.opengis.referencing.AuthorityFactory; +import org.opengis.referencing.RegisterOperations; +import org.opengis.referencing.crs.SingleCRS; +import org.opengis.referencing.crs.CRSFactory; +import org.opengis.referencing.crs.CRSAuthorityFactory; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.opengis.referencing.cs.CSFactory; +import org.opengis.referencing.cs.CSAuthorityFactory; +import org.opengis.referencing.datum.DatumFactory; +import org.opengis.referencing.datum.DatumAuthorityFactory; +import org.opengis.referencing.operation.CoordinateOperation; +import org.opengis.referencing.operation.CoordinateOperationFactory; +import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory; +import org.opengis.referencing.operation.MathTransformFactory; +import org.apache.sis.referencing.factory.GeodeticObjectFactory; +import org.apache.sis.referencing.factory.MultiAuthoritiesFactory; +import org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException; +import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory; +import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; +import org.apache.sis.util.Utilities; +import org.apache.sis.util.logging.Logging; +import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.iso.AbstractFactory; + + +/** + * Finds <abbr>CRS</abbr>s or coordinate operations in one or many geodetic registries. + * Each {@code MultiRegisterOperations} instance can narrow the search to a single registry, + * a specific version of that registry, or to a domain of validity. + * Each instance is immutable and thread-safe. + * + * <p>This class delegates its work to {@linkplain CRS#forCode(String) static methods} or to + * {@link MultiAuthoritiesFactory}. It does not provide new services compared to the above, + * but provides a more high-level <abbr>API</abbr> with the most important registry-based + * services in a single place. {@link RegisterOperations} can also be used as en entry point, + * with accesses to the low-level <abbr>API</abbr> granted by {@link #getFactory(Class)}.</p> + * + * <h2>User-defined geodetic registries</h2> + * User-defined authorities can be added to the SIS environment by creating {@link CRSAuthorityFactory} + * implementations with a public no-argument constructor or a public static {@code provider()} method, + * and declaring the name of those classes in the {@code module-info.java} file as a provider of the + * {@code org.opengis.referencing.crs.CRSAuthorityFactory} service. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * @since 1.5 + */ +public class MultiRegisterOperations extends AbstractFactory implements RegisterOperations { + /** + * Types of factories supported by this implementation. + * A value of {@code true} means that the factory is an authority factory. + * A value of {@code false} means that the factory is an object factory. + * + * @see #getFactory(Class) + */ + private static final Map<Class<?>, Boolean> FACTORY_TYPES = Map.of( + CoordinateOperationAuthorityFactory.class, Boolean.TRUE, + DatumAuthorityFactory.class, Boolean.TRUE, + CRSAuthorityFactory.class, Boolean.TRUE, + CSAuthorityFactory.class, Boolean.TRUE, + DatumFactory.class, Boolean.FALSE, + CRSFactory.class, Boolean.FALSE, + CSFactory.class, Boolean.FALSE); + + /** + * The authority of the <abbr>CRS</abbr> and coordinate operations to search, + * or {@code null} for all authorities. In the latter case, the authority must + * be specified in the code, for example {@code "EPSG:4326"} instead of "4326". + * + * @see #withAuthority(String) + */ + private final String authority; + + /** + * Version of the registry to use, or {@code null} for the default version. + * Can be non-null only if {@link #authority} is also non-null. + * If null, the default version is usually the latest one. + * + * @see #withVersion(String) + */ + private final String version; + + /** + * The area of interest for coordinate operations, or {@code null} for the whole world. + * + * @see #withAreaOfInterest(GeographicBoundingBox) + */ + private final GeographicBoundingBox areaOfInterest; + + /** + * The authority factory to use for extracting <abbr>CRS</abbr> instances, or {@code null} if no + * authority has been specified. In the latter case, {@link CRS} static methods should be used. + * In Apache SIS implementation, this is also an {@link CoordinateOperationAuthorityFactory}. + * + * @see #findCoordinateReferenceSystem(String) + * @see #findCoordinateOperation(String) + */ + private final CRSAuthorityFactory crsFactory; + + /** + * The singleton instance for all authorities in their default versions, with no <abbr>AOI</abbr>. + * + * @see #provider() + */ + private static final MultiRegisterOperations DEFAULT = new MultiRegisterOperations(); + + /** + * Returns an instance which will search <abbr>CRS</abbr> definitions in all registries that are known to SIS. + * Because this instance is not for a specific registry, the authority will need to be part of the {@code code} + * argument given to {@code create(String)} methods. For example, {@code "EPSG:4326"} instead of {@code "4326"}. + * The registry can be made implicit by a call to {@link #withAuthority(String)}. + * + * @return the default instance for all registries known to SIS. + */ + public static MultiRegisterOperations provider() { + return DEFAULT; + } + + /** + * Creates an instance which will search <abbr>CRS</abbr> definitions in all registries that are known to SIS. + * + * @see #provider() + */ + private MultiRegisterOperations() { + authority = null; + version = null; + crsFactory = null; + areaOfInterest = null; + } + + /** + * Creates an instance with the same register than the given instance, but a different <abbr>AOI</abbr>. + * + * @param source the register from which to copy the authority and version. + * @param areaOfInterest the new area of interest (<abbr>AOI</abbr>), or {@code null} if none. + * + * @see #withAreaOfInterest(GeographicBoundingBox) + */ + protected MultiRegisterOperations(final MultiRegisterOperations source, final GeographicBoundingBox areaOfInterest) { + authority = source.authority; + version = source.version; + crsFactory = source.crsFactory; + this.areaOfInterest = areaOfInterest; + } + + /** + * Creates an instance which will use the registry of the specified authority, optionally at a specified version. + * + * @param source the register from which to copy the area of interest. + * @param authority identification of the registry to use (e.g., "EPSG"). + * @param version the registry version to use, or {@code null} for the default version. + * @throws NoSuchAuthorityFactoryException if the specified registry has not been found. + * + * @see #withAuthority(String) + * @see #withVersion(String) + */ + protected MultiRegisterOperations(final MultiRegisterOperations source, final String authority, final String version) + throws NoSuchAuthorityFactoryException + { + this.authority = Objects.requireNonNull(authority); + this.version = version; + this.areaOfInterest = source.areaOfInterest; + crsFactory = AuthorityFactories.ALL.getAuthorityFactory(CRSAuthorityFactory.class, authority, version); + } + + /** + * Returns the <abbr>CRS</abbr> authority factory. + */ + private CRSAuthorityFactory crsFactory() { + return (crsFactory != null) ? crsFactory : AuthorityFactories.ALL; + } + + /** + * Returns the organization or party responsible for definition and maintenance of the register. + * If an authority has been specified by a call to {@link #withAuthority(String)}, then this method + * returns that authority. Otherwise, this method returns {@code null}. + * + * @return the organization responsible for definitions in the registry, or {@code null} if none or many. + * + * @see MultiAuthoritiesFactory#getAuthority() + */ + @Override + public Citation getAuthority() { + return crsFactory().getAuthority(); + } + + /** + * Returns an instance for a geodetic registry of the specified authority, such as "EPSG". + * If a {@linkplain #withVersion(String) version number was specified} previously, that version is cleared. + * If an area of interest was specified, the same area of interest is reused. + * + * <h2>User-defined geodetic registries</h2> + * A user-defined authority can be specified if the implementation is declared in a {@code module-info} + * file as a {@link CRSAuthorityFactory} service. See class javadoc for more information. + * + * @param newValue the desired authority, or {@code null} for all of them. + * @return register operations for the specified authority. + * @throws NoSuchAuthorityFactoryException if the given authority is unknown to SIS. + * + * @see CRS#getAuthorityFactory(String) + */ + public MultiRegisterOperations withAuthority(final String newValue) throws NoSuchAuthorityFactoryException { + if (version == null && Objects.equals(authority, newValue)) { + return this; + } else if (newValue == null) { + return DEFAULT.withAreaOfInterest(areaOfInterest); + } else { + return new MultiRegisterOperations(this, newValue, null); + } + } + + /** + * Returns an instance for the specified version of the geodetic registry. + * A non-null authority must have been {@linkplain #withAuthority(String) specified} before to invoke this method. + * If an area of interest was specified, the same area of interest is reused. + * + * @param newValue the desired version, or {@code null} for the default version. + * @return register operations for the specified version of the geodetic registry. + * @throws IllegalStateException if the version is non-null and no authority has been specified previously. + * @throws NoSuchAuthorityFactoryException if the given version is unknown to SIS. + */ + public MultiRegisterOperations withVersion(final String newValue) throws NoSuchAuthorityFactoryException { + if (Objects.equals(version, newValue)) { + return this; + } else if (newValue == null && authority == null) { + return DEFAULT.withAreaOfInterest(areaOfInterest); + } else if (authority != null) { + return new MultiRegisterOperations(this, authority, newValue); + } else { + throw new IllegalStateException(Errors.format(Errors.Keys.MissingValueForProperty_1, "authority")); + } + } + + /** + * Returns an instance for the specified area of interest (<abbr>AOI</abbr>). + * The area of interest is used for filtering coordinate operations between + * a {@linkplain #findCoordinateOperations between a pair of CRSs}. + * + * @param newValue the desired area of interest, or {@code null} for the world. + * @return register operations for the specified area of interest. + */ + public MultiRegisterOperations withAreaOfInterest(final GeographicBoundingBox newValue) { + if (Objects.equals(areaOfInterest, newValue)) { + return this; + } else if (newValue == null && authority == null && version == null) { + return DEFAULT; + } else { + return new MultiRegisterOperations(this, newValue); + } + } + + /** + * Returns the set of authority codes for objects of the given type. + * The {@code type} argument specifies the base type of identified objects. + * For example, {@code CoordinateReferenceSystem.class} is for requesting the <abbr>CRS</abbr> codes. + * + * <h4>Limitations</h4> + * In the current implementation, codes are filtered by authority and registry version, + * but not for the area of interest. + * + * @param type the type of referencing object for which to get authority codes. + * @return the set of authority codes for referencing objects of the given type. + * @throws FactoryException if access to the underlying database failed. + */ + @Override + public Set<String> getAuthorityCodes(Class<? extends IdentifiedObject> type) throws FactoryException { + return crsFactory().getAuthorityCodes(type); + } + + /** + * Returns a textual description of the object corresponding to a code. + * The description may be used in graphical user interfaces. + * + * @param type the type of object for which to get a description. + * @param code value allocated by the authority for an object of the given type. + * @return a description of the object, or empty if the object has no description. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the query failed for some other reason. + */ + @Override + public Optional<InternationalString> getDescriptionText(Class<? extends IdentifiedObject> type, String code) + throws FactoryException + { + return crsFactory().getDescriptionText(type, code); + } + + /** + * Extracts <abbr>CRS</abbr> details from the registry. If this {@code RegisterOperations} has not + * been restricted to a specific authority by a call to {@link #withAuthority(String)}, then the + * given code must contain the authority (e.g., {@code "EPSG:4326"} instead of {@code "4326"}. + * Otherwise, this method delegates to {@link CRS#forCode(jString)}. + * + * <p>By default, this method recognizes the {@code "EPSG"} and {@code "OGC"} authorities. + * In the {@code "EPSG"} case, whether the full set of EPSG codes is supported or not depends + * on whether a {@linkplain org.apache.sis.referencing.factory.sql connection to the database} + * can be established. If no connection can be established, then this method uses a small embedded + * EPSG factory containing at least the CRS defined in the {@link #forCode(String)} method javadoc.</p> + * + * @param code <abbr>CRS</abbr> identifier allocated by the authority. + * @return the <abbr>CRS</abbr> for the given authority code. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the search failed for some other reason. + * + * @see CRS#forCode(String) + */ + @Override + public CoordinateReferenceSystem findCoordinateReferenceSystem(final String code) throws FactoryException { + if (crsFactory != null) { + return crsFactory.createCoordinateReferenceSystem(code); + } + return CRS.forCode(code); + } + + /** + * Extracts coordinate operation details from the registry. If this {@code RegisterOperations} + * has not been restricted to a specific authority by a call to {@link #withAuthority(String)}, + * then the given code must contain the authority. + * + * @param code operation identifier allocated by the authority. + * @return the operation for the given authority code. + * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found. + * @throws FactoryException if the search failed for some other reason. + */ + @Override + public CoordinateOperation findCoordinateOperation(String code) throws FactoryException { + if (crsFactory instanceof CoordinateOperationAuthorityFactory) { + ((CoordinateOperationAuthorityFactory) crsFactory).createCoordinateOperation(code); + } + return AuthorityFactories.ALL.createCoordinateOperation(code); + } + + /** + * Finds or infers any coordinate operations for which the given <abbr>CRS</abbr>s are the source and target, + * in that order. This method searches for operation paths defined in the registry. + * If none are found, this method tries to infer a path itself. + * + * @param source the source <abbr>CRS</abbr>. + * @param target the target <abbr>CRS</abbr>. + * @return coordinate operations found or inferred between the given pair <abbr>CRS</abbr>s. May be an empty set. + * @throws FactoryException if an error occurred while searching for coordinate operations. + */ + @Override + public Set<CoordinateOperation> findCoordinateOperations(CoordinateReferenceSystem source, CoordinateReferenceSystem target) + throws FactoryException + { + final List<CoordinateOperation> operations = CRS.findOperations(source, target, areaOfInterest); + return new AbstractSet<>() { // Assuming that the list does not contain duplicated elements. + @Override public Iterator<CoordinateOperation> iterator() {return operations.iterator();} + @Override public boolean isEmpty() {return operations.isEmpty();} + @Override public int size() {return operations.size();} + }; + } + + /** + * Determines whether two <abbr>CRS</abbr>s are members of one ensemble. + * If this method returns {@code true}, then for low accuracy purposes coordinate sets referenced + * to these <abbr>CRS</abbr>s may be merged without coordinate transformation. + * The attribute {@link DatumEnsemble#getEnsembleAccuracy()} gives some indication + * of the inaccuracy introduced through such merger. + * + * @param source the source <abbr>CRS</abbr>. + * @param target the target <abbr>CRS</abbr>. + * @return whether the two <abbr>CRS</abbr>s are members of one ensemble. + * @throws FactoryException if an error occurred while searching for ensemble information in the registry. + */ + @Override + public boolean areMembersOfSameEnsemble(CoordinateReferenceSystem source, CoordinateReferenceSystem target) + throws FactoryException + { + return (source instanceof SingleCRS) && (target instanceof SingleCRS) + && Utilities.equalsIgnoreMetadata( + ((SingleCRS) source).getDatumEnsemble(), + ((SingleCRS) target).getDatumEnsemble()); + } + + /** + * Returns a factory used for building components of <abbr>CRS</abbr> or coordinate operations. + * The factories returned by this method provide accesses to the low-level services used by this + * {@code RegisterOperations} instance for implementing its high-level services. + * + * @param <T> compile-time value of the {@code type} argument. + * @param type the desired type of factory. + * @return factory of the specified type. + * @throws NullPointerException if the specified type is null. + * @throws IllegalArgumentException if the specified type is not one of the above-cited values. + */ + @Override + public <T extends Factory> Optional<T> getFactory(final Class<T> type) { + final Factory factory; + final Boolean b = FACTORY_TYPES.get(type); + if (b != null) { + if (b) { + final MultiAuthoritiesFactory mf = AuthorityFactories.ALL; + if (authority == null) { + factory = mf; + } else try { + factory = mf.getAuthorityFactory(type.asSubclass(AuthorityFactory.class), authority, version); + } catch (NoSuchAuthorityFactoryException e) { + Logging.recoverableException(AuthorityFactories.LOGGER, MultiRegisterOperations.class, "getFactory", e); + return Optional.empty(); + } + } else { + factory = GeodeticObjectFactory.provider(); + } + } else if (type == CoordinateOperationFactory.class) { + factory = DefaultCoordinateOperationFactory.provider(); + } else if (type == MathTransformFactory.class) { + factory = DefaultMathTransformFactory.provider(); + } else { + throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "type", type)); + } + return Optional.of(type.cast(factory)); + } +} diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java index 3582606d22..b3a3d07594 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/MultiAuthoritiesFactory.java @@ -46,6 +46,7 @@ import org.opengis.metadata.extent.Extent; import org.opengis.parameter.ParameterDescriptor; import org.opengis.util.FactoryException; import org.opengis.util.InternationalString; +import org.opengis.util.NoSuchIdentifierException; import org.apache.sis.util.ArraysExt; import org.apache.sis.util.CharSequences; import org.apache.sis.util.ArgumentChecks; @@ -61,6 +62,7 @@ import org.apache.sis.referencing.privy.LazySet; import org.apache.sis.referencing.internal.Resources; import org.apache.sis.metadata.iso.citation.Citations; import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory; +import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.collection.BackingStoreException; @@ -289,8 +291,10 @@ public class MultiAuthoritiesFactory extends GeodeticAuthorityFactory implements /** * Returns the database or specification that defines the codes recognized by this factory. - * The default implementation returns {@code null} since {@code MultiAuthoritiesFactory} is not - * about a particular authority. + * The default implementation returns {@code null} because {@code MultiAuthoritiesFactory} + * is not about a particular authority. + * + * @return the organization responsible for definitions in the registry, or {@code null} if none or many. */ @Override public Citation getAuthority() { @@ -1430,12 +1434,24 @@ public class MultiAuthoritiesFactory extends GeodeticAuthorityFactory implements * <li><code>http://www.opengis.net/def/<b>method</b>/</code><var>authority</var>{@code /}<var>version</var>{@code /}<var>code</var></li> * </ul> * + * If the given code is not found in the geodetic registry, then this method searches also among + * the {@linkplain DefaultMathTransformFactory#getOperationMethod(String) build-in methods}. + * * @return the operation method for the given code. * @throws FactoryException if the object creation failed. */ @Override public OperationMethod createOperationMethod(final String code) throws FactoryException { - return create(AuthorityFactoryProxy.METHOD, code); + try { + return create(AuthorityFactoryProxy.METHOD, code); + } catch (NoSuchAuthorityCodeException e) { + try { + return DefaultMathTransformFactory.provider().getOperationMethod(code); + } catch (NoSuchIdentifierException s) { + e.addSuppressed(s); + throw e; + } + } } /** diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java index 33c46f35e5..6bbb7f6814 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/CoordinateOperationRegistry.java @@ -63,6 +63,7 @@ import org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException; import org.apache.sis.referencing.privy.CoordinateOperations; import org.apache.sis.referencing.privy.EllipsoidalHeightCombiner; import org.apache.sis.referencing.privy.PositionalAccuracyConstant; +import org.apache.sis.referencing.privy.ReferencingFactoryContainer; import org.apache.sis.referencing.privy.ReferencingUtilities; import org.apache.sis.referencing.internal.DeferredCoordinateOperation; import org.apache.sis.referencing.internal.Resources; @@ -1337,7 +1338,9 @@ class CoordinateOperationRegistry { if (descriptor != null) { final Identifier name = descriptor.getName(); if (name != null) { - method = factory.getOperationMethod(name.getCode()); + final MathTransformFactory mtFactory = factorySIS.getMathTransformFactory(); + var c = new ReferencingFactoryContainer(null, null, null, null, factory, mtFactory); + method = c.findOperationMethod(name.getCode()); } if (method == null) { method = factory.createOperationMethod(properties, descriptor); diff --git a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java index 7f72c76705..80e25bccd7 100644 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultCoordinateOperationFactory.java @@ -273,8 +273,10 @@ public class DefaultCoordinateOperationFactory extends AbstractFactory implement * @throws FactoryException if the requested operation method cannot be fetched. * * @see DefaultMathTransformFactory#getOperationMethod(String) + * + * @deprecated Use {@link DefaultMathTransformFactory} instead. */ - @Override + @Deprecated(since="1.5", forRemoval=true) public OperationMethod getOperationMethod(String name) throws FactoryException { return new ReferencingFactoryContainer(null, null, null, null, null, mtFactory).findOperationMethod(name); } 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 d96503c7c5..762cad6ece 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 @@ -22,13 +22,16 @@ import javax.measure.quantity.Angle; import javax.measure.quantity.Length; import org.opengis.util.FactoryException; import org.opengis.referencing.IdentifiedObject; +import org.opengis.referencing.cs.CSFactory; import org.opengis.referencing.cs.CartesianCS; import org.opengis.referencing.cs.EllipsoidalCS; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystemAxis; +import org.opengis.referencing.crs.CRSFactory; import org.opengis.referencing.crs.GeodeticCRS; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ProjectedCRS; +import org.opengis.referencing.datum.DatumFactory; import org.opengis.referencing.datum.Ellipsoid; import org.opengis.referencing.datum.PrimeMeridian; import org.opengis.referencing.datum.GeodeticDatum; @@ -36,8 +39,9 @@ import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.Conversion; import org.opengis.parameter.ParameterValueGroup; import org.apache.sis.referencing.CommonCRS; +import org.apache.sis.referencing.MultiRegisterOperations; import org.apache.sis.referencing.operation.DefaultConversion; -import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory; +import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory; import org.apache.sis.io.wkt.Convention; import org.apache.sis.measure.Units; @@ -63,10 +67,7 @@ public final class GeodeticObjectFactoryTest extends ObjectFactoryTest { * Creates a new test suite using the singleton factory instance. */ public GeodeticObjectFactoryTest() { - super(GeodeticObjectFactory.provider(), - GeodeticObjectFactory.provider(), - GeodeticObjectFactory.provider(), - DefaultCoordinateOperationFactory.provider()); + super(MultiRegisterOperations.provider()); } /** @@ -78,6 +79,7 @@ public final class GeodeticObjectFactoryTest extends ObjectFactoryTest { */ @Test public void testCreateFromWKT() throws FactoryException { + final CRSFactory crsFactory = factories.getFactory(CRSFactory.class).orElseThrow(); final GeodeticCRS crs = (GeodeticCRS) crsFactory.createFromWKT( "GEOGCS[“WGS 84”,\n" + " DATUM[“World Geodetic System 1984”,\n" + @@ -97,6 +99,7 @@ public final class GeodeticObjectFactoryTest extends ObjectFactoryTest { */ @Test public void testInvalidParameterInWKT() throws FactoryException { + final CRSFactory crsFactory = factories.getFactory(CRSFactory.class).orElseThrow(); var e = assertThrows(InvalidGeodeticParameterException.class, () -> crsFactory.createFromWKT( "PROJCRS[“Custom”,\n" + @@ -134,6 +137,9 @@ public final class GeodeticObjectFactoryTest extends ObjectFactoryTest { */ @Test public void testStepByStepCreation() throws FactoryException { + final CRSFactory crsFactory = factories.getFactory(CRSFactory.class).orElseThrow(); + final CSFactory csFactory = factories.getFactory(CSFactory.class).orElseThrow(); + final DatumFactory datumFactory = factories.getFactory(DatumFactory.class).orElseThrow(); /* * List of all objects to be created in this test. */ @@ -189,7 +195,7 @@ public final class GeodeticObjectFactoryTest extends ObjectFactoryTest { /* * Defining conversion */ - method = copFactory.getOperationMethod("Transverse_Mercator"); + method = DefaultMathTransformFactory.provider().getOperationMethod("Transverse_Mercator"); parameters = method.getParameters().createValue(); parameters.parameter("semi_major") .setValue(ellipsoid.getSemiMajorAxis()); parameters.parameter("semi_minor") .setValue(ellipsoid.getSemiMinorAxis()); diff --git a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/geoapi/AuthorityFactoryTest.java b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/geoapi/AuthorityFactoryTest.java index 4942e3c371..9912eeb342 100644 --- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/geoapi/AuthorityFactoryTest.java +++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/geoapi/AuthorityFactoryTest.java @@ -18,6 +18,7 @@ package org.apache.sis.referencing.geoapi; import org.opengis.util.FactoryException; import org.apache.sis.referencing.CRS; +import org.apache.sis.referencing.MultiRegisterOperations; // Test dependencies import org.junit.jupiter.api.Disabled; @@ -42,7 +43,7 @@ public final class AuthorityFactoryTest extends org.opengis.test.referencing.Aut * @throws FactoryException if no factory can be returned for the given authority. */ public AuthorityFactoryTest() throws FactoryException { - super(CRS.getAuthorityFactory(null), null, null); + super(MultiRegisterOperations.provider()); } /** diff --git a/geoapi/snapshot b/geoapi/snapshot index a07e965123..a0ae30dd67 160000 --- a/geoapi/snapshot +++ b/geoapi/snapshot @@ -1 +1 @@ -Subproject commit a07e965123706be9092d0dc615592c23c8401450 +Subproject commit a0ae30dd67b67a718b74bd8a90140ea0a559e8b6