You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2026/05/06 15:12:33 UTC
(sis) 01/01: Merge branch 'geoapi-3.1'. This is mostly consolidation with bug fixes, in particular in the GUI application.
This is an automated email from the ASF dual-hosted git repository.
asf-gitbox-commits pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 8eb0911e58c0cbb7cc65d9819cf7b58c8ec5d424
Merge: eb71d2424c 60f07fc246
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed May 6 17:11:33 2026 +0200
Merge branch 'geoapi-3.1'.
This is mostly consolidation with bug fixes, in particular in the GUI application.
.../resources/IndexedResourceCompiler.java | 44 +-
.../org/apache/sis/cloud/aws/s3/Resources.java | 14 +
.../sis/coverage/grid/BufferedGridCoverage.java | 5 +-
.../apache/sis/coverage/grid/DefaultEvaluator.java | 3 +-
.../apache/sis/coverage/grid/GridCRSBuilder.java | 540 +++++++++++++++
.../org/apache/sis/coverage/grid/GridExtent.java | 64 +-
.../apache/sis/coverage/grid/GridExtentCRS.java | 410 ------------
.../org/apache/sis/coverage/grid/GridGeometry.java | 59 +-
.../sis/coverage/grid/ResampledGridCoverage.java | 4 +-
.../apache/sis/coverage/grid/SliceGeometry.java | 3 +-
.../main/org/apache/sis/feature/Validator.java | 3 +-
.../org/apache/sis/feature/internal/Resources.java | 14 +
.../geometry/wrapper/SpatialOperationContext.java | 5 +-
.../apache/sis/image/BandedSampleConverter.java | 6 +-
.../main/org/apache/sis/image/DataType.java | 2 +-
.../org/apache/sis/image/SourceAlignedImage.java | 2 +-
.../image/internal/shared/BatchComputedImage.java | 7 +-
.../image/internal/shared/ColorModelFactory.java | 121 +++-
.../apache/sis/coverage/grid/GridGeometryTest.java | 78 ++-
.../builder/AssociationRoleBuilderTest.java | 7 +-
.../apache/sis/metadata/internal/Resources.java | 14 +
.../metadata/internal/shared/NameToIdentifier.java | 10 +-
.../org/apache/sis/metadata/sql/Dispatcher.java | 33 +-
.../org/apache/sis/metadata/sql/MetadataProxy.java | 13 +-
.../apache/sis/metadata/sql/MetadataSource.java | 22 +-
.../org/apache/sis/metadata/sql/package-info.java | 2 +-
.../org/apache/sis/map/internal/Resources.java | 14 +
.../gazetteer/MilitaryGridReferenceSystem.java | 4 +-
.../referencing/gazetteer/internal/Resources.java | 14 +
.../main/module-info.java | 3 +
.../sis/geometry/AbstractDirectPosition.java | 2 +
.../main/org/apache/sis/geometry/Envelopes.java | 2 +-
.../main/org/apache/sis/io/wkt/Formatter.java | 5 +-
.../main/org/apache/sis/io/wkt/WKTFormat.java | 6 +-
.../main/org/apache/sis/io/wkt/Warnings.java | 145 ++--
.../main/org/apache/sis/io/wkt/package-info.java | 2 +-
.../sis/referencing/AbstractIdentifiedObject.java | 16 +-
.../main/org/apache/sis/referencing/Builder.java | 14 +-
.../main/org/apache/sis/referencing/CRS.java | 8 +-
.../main/org/apache/sis/referencing/CommonCRS.java | 27 +-
.../referencing/EllipsoidalHeightSeparator.java | 8 +-
.../apache/sis/referencing/IdentifiedObjects.java | 10 +-
.../apache/sis/referencing/NamedIdentifier.java | 44 +-
.../sis/referencing/StandardDefinitions.java | 4 +-
.../sis/referencing/crs/DefaultDerivedCRS.java | 3 +
.../org/apache/sis/referencing/cs/AbstractCS.java | 2 +-
.../apache/sis/referencing/cs/DefaultAffineCS.java | 2 +-
.../sis/referencing/cs/DefaultCylindricalCS.java | 2 +-
.../apache/sis/referencing/cs/DefaultLinearCS.java | 2 +-
.../apache/sis/referencing/cs/DefaultPolarCS.java | 2 +-
.../sis/referencing/cs/DefaultSphericalCS.java | 2 +-
.../apache/sis/referencing/cs/DefaultTimeCS.java | 10 +-
.../sis/referencing/cs/DefaultVerticalCS.java | 2 +-
.../apache/sis/referencing/cs/package-info.java | 2 +-
.../sis/referencing/datum/AbstractDatum.java | 14 +-
.../referencing/datum/DefaultDatumEnsemble.java | 4 +-
.../referencing/datum/DefaultGeodeticDatum.java | 10 +-
.../referencing/factory/GeodeticObjectFactory.java | 35 +
.../referencing/factory/IdentifiedObjectSet.java | 2 +-
.../apache/sis/referencing/internal/Resources.java | 14 +
.../internal/shared/ReferencingUtilities.java | 8 +-
.../operation/AbstractCoordinateOperation.java | 60 +-
.../operation/AbstractSingleOperation.java | 3 +-
.../operation/CoordinateOperationFinder.java | 2 +-
.../referencing/operation/DefaultConversion.java | 123 ++--
.../DefaultCoordinateOperationFactory.java | 2 +-
.../operation/DefaultPassThroughOperation.java | 2 -
.../referencing/operation/DefaultProjection.java | 3 +-
.../referencing/operation/DefiningConversion.java | 163 +++++
.../apache/sis/parameter/ParameterFormatTest.java | 3 +-
.../test/org/apache/sis/referencing/CRSTest.java | 32 +
.../internal/shared/DefinitionVerifierTest.java | 3 +-
.../operation/DefaultConversionTest.java | 2 +-
.../operation/HardCodedConversions.java | 2 +-
.../transform/DefaultMathTransformFactoryTest.java | 4 +-
.../apache/sis/storage/geotiff/base/Resources.java | 14 +
.../sis/storage/geotiff/GeoTiffStoreTest.java | 4 +-
.../test/org/apache/sis/storage/geotiff/tiled.tiff | Bin 3882 -> 2334 bytes
.../org/apache/sis/storage/geotiff/untiled.tiff | Bin 2602 -> 1054 bytes
.../org/apache/sis/storage/netcdf/base/Axis.java | 7 +-
.../sis/storage/netcdf/internal/Resources.java | 14 +
.../apache/sis/storage/sql/feature/Resources.java | 14 +
.../main/org/apache/sis/storage/DataStore.java | 10 +-
.../apache/sis/storage/base/MetadataBuilder.java | 6 +-
.../org/apache/sis/storage/internal/Resources.java | 14 +
.../main/org/apache/sis/util/resources/Errors.java | 14 +
.../apache/sis/util/resources/KeyConstants.java | 38 +-
.../org/apache/sis/util/resources/Messages.java | 14 +
.../resources/ResourceInternationalString.java | 10 +-
.../org/apache/sis/util/resources/Vocabulary.java | 24 +
.../sis/util/resources/Vocabulary.properties | 6 +-
.../sis/util/resources/Vocabulary_fr.properties | 6 +-
.../sis/storage/shapefile/ShapefileStore.java | 64 +-
.../apache/sis/gui/coverage/CoverageCanvas.java | 85 ++-
.../apache/sis/gui/coverage/CoverageControls.java | 7 +-
.../apache/sis/gui/coverage/CoverageExplorer.java | 27 +-
.../org/apache/sis/gui/coverage/GridControls.java | 6 +-
.../apache/sis/gui/coverage/GridSliceSelector.java | 2 +-
.../main/org/apache/sis/gui/coverage/GridView.java | 27 +-
.../org/apache/sis/gui/coverage/ImageRequest.java | 47 +-
.../sis/gui/coverage/StyledRenderingData.java | 10 +
.../apache/sis/gui/coverage/ViewAndControls.java | 3 +-
.../org/apache/sis/gui/internal/Resources.java | 16 +-
.../apache/sis/gui/internal/Resources.properties | 2 +-
.../sis/gui/internal/Resources_fr.properties | 2 +-
.../main/org/apache/sis/gui/internal/Styles.java | 2 +-
.../main/org/apache/sis/gui/map/MapCanvas.java | 47 +-
.../main/org/apache/sis/gui/map/MapMenu.java | 1 +
.../main/org/apache/sis/gui/map/MultiCanvas.java | 45 +-
.../sis/gui/map/RenderingCompletedEvent.java | 86 +++
.../main/org/apache/sis/gui/map/RenderingTask.java | 16 +-
.../main/org/apache/sis/gui/map/StatusBar.java | 159 +++--
.../org/apache/sis/gui/map/ValuesUnderCursor.java | 1 +
.../apache/sis/gui/referencing/AuthorityCodes.java | 32 +-
.../org/apache/sis/gui/referencing/CRSChooser.java | 40 +-
.../apache/sis/gui/referencing/FilterByDatum.java | 185 +++++
.../org/apache/sis/gui/referencing/MenuSync.java | 47 +-
.../gui/referencing/RecentReferenceSystems.java | 743 ++++++++++-----------
.../org/apache/sis/gui/referencing/Unverified.java | 60 ++
.../org/apache/sis/storage/panama/Resources.java | 14 +
120 files changed, 2896 insertions(+), 1408 deletions(-)
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
index 0000000000,1f96d230e7..7a1df0e19d
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java
@@@ -1,0 -1,540 +1,540 @@@
+ /*
+ * 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.coverage.grid;
+
+ import java.util.Arrays;
+ import java.util.Collections;
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.Locale;
+ import java.util.Optional;
+ import org.opengis.util.FactoryException;
+ import org.opengis.util.InternationalString;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.spatial.DimensionNameType;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.parameter.ParameterDescriptor;
+ import org.opengis.parameter.ParameterDescriptorGroup;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.cs.CSFactory;
+ import org.opengis.referencing.cs.AxisDirection;
+ import org.opengis.referencing.cs.CoordinateSystem;
+ import org.opengis.referencing.cs.CoordinateSystemAxis;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.opengis.referencing.crs.CompoundCRS;
+ import org.opengis.referencing.crs.DerivedCRS;
+ import org.opengis.referencing.crs.EngineeringCRS;
+ import org.opengis.referencing.datum.EngineeringDatum;
+ import org.opengis.referencing.operation.Matrix;
+ import org.opengis.referencing.operation.OperationMethod;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.TransformException;
+ import org.opengis.referencing.operation.NoninvertibleTransformException;
+ import org.apache.sis.metadata.iso.extent.DefaultExtent;
+ import org.apache.sis.parameter.ParameterBuilder;
+ import org.apache.sis.referencing.CommonCRS;
+ import org.apache.sis.referencing.IdentifiedObjects;
+ import org.apache.sis.referencing.cs.AbstractCS;
+ import org.apache.sis.referencing.cs.CoordinateSystems;
+ import org.apache.sis.referencing.operation.DefiningConversion;
+ import org.apache.sis.referencing.operation.DefaultOperationMethod;
+ import org.apache.sis.referencing.operation.transform.TransformSeparator;
+ import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
+ import org.apache.sis.referencing.internal.shared.AxisDirections;
+ import org.apache.sis.referencing.internal.shared.DirectPositionView;
+ import org.apache.sis.referencing.internal.shared.ReferencingFactoryContainer;
+ import org.apache.sis.feature.internal.Resources;
+ import org.apache.sis.util.ArraysExt;
+ import org.apache.sis.util.Characters;
+ import org.apache.sis.util.logging.Logging;
+ import org.apache.sis.util.resources.Vocabulary;
+ import org.apache.sis.util.iso.Types;
+ import org.apache.sis.measure.Units;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.ObjectDomain;
++// Specific to the main branch:
++import org.opengis.referencing.datum.Datum;
+
+
+ /**
+ * Builder for coordinate reference system which is derived from the coverage <abbr>CRS</abbr>
+ * by the inverse of the "grid to <abbr>CRS</abbr>" transform. Those <abbr>CRS</abbr> describe
+ * coordinates associated to the grid extent. This class provides two factory methods:
+ *
+ * <ul>
+ * <li>{@link #forCoverage()}</li>
+ * <li>{@link #forExtentAlone(Matrix, DimensionNameType[])}</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ */
+ final class GridCRSBuilder extends ReferencingFactoryContainer {
+ /**
+ * Name of the parameter specifying which part (center or corner)
+ * of the call is associated with the coverage data attributes.
+ */
+ private static final String ANCHOR_PARAM = "Pixel in cell";
+
+ /**
+ * Description of the "<abbr>CRS</abbr> to grid indices" operation method.
+ */
+ private static final OperationMethod METHOD;
+ static {
+ final ParameterBuilder b = new ParameterBuilder().setRequired(true);
+ final ParameterDescriptor<?> anchor = b.addName(ANCHOR_PARAM).create(PixelInCell.class, PixelInCell.CELL_CENTER);
+ final ParameterDescriptorGroup params = b.addName("CRS to grid indices").createGroup(anchor);
+ METHOD = new DefaultOperationMethod(Map.of(IdentifiedObject.NAME_KEY, params.getName()), params);
+ }
+
+ /**
+ * Scope of usage of the derived or engineering <abbr>CRS</abbr> created by this class.
+ * This is a localized text saying "Conversions from coverage <abbr>CRS</abbr> to grid cell indices."
+ */
+ private static final InternationalString SCOPE = Resources.formatInternational(Resources.Keys.CrsToGridConversion);
+
+ /**
+ * The extent of the grid geometry, or {@code null} if none.
+ */
+ private GridExtent extent;
+
+ /**
+ * The cell part (center or corner) to map.
+ */
+ private final PixelInCell anchor;
+
+ /**
+ * A helper tool for separating the "<abbr>CRS</abbr> to grid" transform for each component.
+ * This is {@code null} if the grid geometry has no <abbr>CRS</abbr> or no transform.
+ */
+ private TransformSeparator separator;
+
+ /**
+ * Properties to pass to the constructors of <abbr>CRS</abbr> components. Populated with metadata
+ * of potential interest except {@value IdentifiedObject#NAME_KEY}, which must be added before usage.
+ *
+ * @see #properties(Object)
+ */
+ private final Map<String, Object> properties;
+
+ /**
+ * Locale to use for axis names and error messages, or {@code null} for default.
+ * This is static and final for now because we do not yet provide a public API
+ * for this option, but this policy may change in the future.
+ */
+ private static final Locale LOCALE = null;
+
+ /**
+ * Creates a new helper class for building a grid coordinate reference system.
+ *
+ * @param anchor the cell part to map (center or corner).
+ */
+ GridCRSBuilder(final PixelInCell anchor) {
+ this.anchor = anchor;
+ properties = new HashMap<>(8);
+ if (LOCALE != null) {
+ properties.put(DefiningConversion.LOCALE_KEY, LOCALE);
+ }
+ }
+
+ /**
+ * Returns an error message for an illegal "grid to <abbr>CRS</abbr>" transform.
+ */
+ private static String illegalGridToCRS() {
+ return Resources.forLocale(LOCALE).getString(Resources.Keys.IllegalGridGeometryComponent_1, "gridToCRS");
+ }
+
+ /**
+ * Creates a derived or engineering <abbr>CRS</abbr> for the grid extent of a grid coverage.
+ * Derived <abbr>CRS</abbr> are preferred as they allow conversions to geospatial <abbr>CRS</abbr>.
+ * May return a compound <abbr>CRS</abbr> if the grid geometry has, for example, a temporal component.
+ *
+ * @param grid grid geometry of the coverage.
+ * @param derived whether to force {@link DerivedCRS} instances.
+ * @param name name of the derived or engineering <abbr>CRS</abbr> to create.
+ * @return a derived, engineering or compound <abbr>CRS</abbr> for cell indices associated to the grid extent.
+ * @throws InvalidGeodeticParameterException if characteristics of the grid geometry disallow this operation.
+ * @throws FactoryException if another error occurred during the use of a referencing factory.
+ */
+ final CoordinateReferenceSystem forCoverage(final GridGeometry grid, final boolean derived, final Identifier name)
+ throws FactoryException
+ {
+ properties.put(DefiningConversion.NORMALIZED_KEY, Boolean.FALSE);
- properties.put(ObjectDomain.SCOPE_KEY, SCOPE);
++ properties.put(Datum.SCOPE_KEY, SCOPE);
+ grid.getGeographicExtent().ifPresent((domain) -> {
- properties.put(ObjectDomain.DOMAIN_OF_VALIDITY_KEY, new DefaultExtent(null, domain, null, null));
++ properties.put(Datum.DOMAIN_OF_VALIDITY_KEY, new DefaultExtent(null, domain, null, null));
+ });
+ if (grid.isDefined(GridGeometry.EXTENT)) {
+ extent = grid.getExtent();
+ }
+ if (derived || grid.isDefined(GridGeometry.CRS | GridGeometry.GRID_TO_CRS)) try {
+ separator = new TransformSeparator(grid.getGridToCRS(anchor).inverse());
+ return forComponent(name, grid.getCoordinateReferenceSystem(), 0, 0);
+ } catch (NoninvertibleTransformException e) {
+ throw new InvalidGeodeticParameterException(illegalGridToCRS(), e);
+ }
+ /*
+ * Case where the grid geometry has no CRS or no "grid to CRS" transform.
+ * We cannot create a derived CRS. Fallback on an engineering CRS with no
+ * relationship to any other CRS.
+ */
+ final int dimension = grid.getDimension();
+ final DimensionNameType[] dimensionNames;
+ if (extent != null) {
+ dimensionNames = Arrays.copyOf(extent.getAxisTypes(), dimension);
+ } else {
+ dimensionNames = new DimensionNameType[dimension];
+ }
+ final CoordinateSystem cs = createCS(dimension, dimensionNames, directions(dimensionNames), 1, true);
+ final EngineeringDatum datum = getDatumFactory().createEngineeringDatum(properties(name));
+ return getCRSFactory().createEngineeringCRS(properties(datum.getName()), datum, cs);
+ }
+
+ /**
+ * Creates a derived <abbr>CRS</abbr> with a conversion from the real world <abbr>CRS</abbr> to grid indices.
+ * This method may invoke itself recursively for separating a compound <abbr>CRS</abbr> into its components.
+ *
+ * <p>After return, {@link #separator} contains information about the transform for this component.
+ * Caller can get the dimensions that have been used. Caller shall invoke {@code transform.clear()}
+ * before to invoke this method again.</p>
+ *
+ * @param name name of the <abbr>CRS</abbr> to create.
+ * @param baseCRS real world <abbr>CRS</abbr> or component of that <abbr>CRS</abbr>.
+ * @param srcDim dimension of the first axis of {@code baseCRS} relatively to the full real world <abbr>CRS</abbr>.
+ * @param tgtDim dimension of the first axis of the return value relatively to the full derived <abbr>CRS</abbr>.
+ * @return grid extent <abbr>CRS</abbr> derived from the given {@code baseCRS}.
+ * @throws FactoryException if an error occurred during the use of a referencing factory.
+ */
+ private CoordinateReferenceSystem forComponent(final Object name, final CoordinateReferenceSystem baseCRS, int srcDim, int tgtDim)
+ throws FactoryException
+ {
+ final int dimension = baseCRS.getCoordinateSystem().getDimension();
+ /*
+ * If the given CRS is a compound CRS (e.g. horizontal + vertical + temporal),
+ * invoke this method recursively for each component and assemble the result.
+ * We must keep in mind that the resulting CRS components are not necessarily
+ * in same order as the components of the real world CRS.
+ */
+ if (baseCRS instanceof CompoundCRS) {
+ // At first, elements are duplicated in the `components` array for each axis.
+ final var components = new CoordinateReferenceSystem[dimension];
+ for (final CoordinateReferenceSystem crs : ((CompoundCRS) baseCRS).getComponents()) {
+ final CoordinateReferenceSystem derived = forComponent(name(crs), crs, srcDim, tgtDim);
+ for (int i : separator.getTargetDimensions()) {
+ components[i] = derived;
+ }
+ separator.clear();
+ srcDim += crs.getCoordinateSystem().getDimension();
+ tgtDim += derived.getCoordinateSystem().getDimension();
+ }
+ // Deduplicate components with the restriction that same components must be consecutive.
+ int count = 1;
+ for (int i=1; i<dimension; i++) {
+ final CoordinateReferenceSystem crs = components[i];
+ if (crs != components[i-1]) {
+ for (int j = count; --j >= 0;) {
+ if (components[j] == crs) {
+ throw new InvalidGeodeticParameterException(illegalGridToCRS());
+ }
+ }
+ components[count++] = crs;
+ }
+ }
+ return getCRSFactory().createCompoundCRS(properties(name), ArraysExt.resize(components, count));
+ }
+ /*
+ * Case of a single (non-compound) CRS. The separator contains the "CRS to grid" transform.
+ * Therefore, the source dimensions are in the base CRS (the real world CRS) and the target
+ * dimensions are those of the derived CRS to create.
+ */
+ separator.addSourceDimensionRange(srcDim, srcDim + dimension);
+ final MathTransform crsToGrid = separator.separate();
+ final int[] dispatch = separator.getTargetDimensions();
+ final var dimensionNames = new DimensionNameType[dispatch.length];
+ if (extent != null) {
+ Arrays.setAll(dimensionNames, (i) -> extent.getAxisType(dispatch[i]).orElse(null));
+ }
+ /*
+ * Get the directions of the axes of the coverage coordinate system, but in the order of grid dimensions.
+ * The direction array may contain null elements if directions could not be inferred for some dimensions.
+ * Design note: we perform this check for each single component instead of computing the derivative once
+ * in the `forCoverage(…)` method because we need an affine transform. When the transform for the whole
+ * grid is not affine, very often the transform for some single components is still affine.
+ */
+ AxisDirection[] directions;
+ toGrid: try {
+ final Matrix derivative;
+ if (extent != null) {
+ derivative = crsToGrid.derivative(new DirectPositionView.Double(extent.getPointOfInterest(anchor), srcDim, dimension));
+ } else try {
+ derivative = crsToGrid.derivative(null);
+ } catch (NullPointerException e) {
+ Logging.ignorableException(GridExtent.LOGGER, GridGeometry.class, "createGridCRS", e);
+ directions = directions(dimensionNames);
+ break toGrid;
+ }
+ // Last arguments are `null` because `dimensionNames` is already in the desired order.
+ directions = CoordinateSystems.getSimpleAxisDirections(baseCRS.getCoordinateSystem());
+ directions = reorder(directions, derivative, null, null);
+ for (int i=0; i < dimensionNames.length; i++) {
+ if (dimensionNames[i] == null && directions[i] != null) {
+ final DimensionNameType type = GridExtent.DIMENSION_NAMES.get(directions[i]);
+ if (type != null && !ArraysExt.contains(dimensionNames, type)) {
+ dimensionNames[i] = type;
+ }
+ }
+ }
+ } catch (TransformException e) {
+ // `GridGeometry.createGridCRS(…)` is the public API that invoked this method.
+ Logging.recoverableException(GridExtent.LOGGER, GridGeometry.class, "createGridCRS", e);
+ directions = directions(dimensionNames);
+ }
+ /*
+ * Creates the coordinate system, then the conversion, and finally the derived CRS.
+ */
+ final CoordinateSystem cs = createCS(dispatch.length, dimensionNames, directions, tgtDim + 1, true);
+ final ParameterValueGroup params = METHOD.getParameters().createValue();
+ params.parameter(ANCHOR_PARAM).setValue(anchor);
+ final var conversion = new DefiningConversion(properties(METHOD.getName()), METHOD, crsToGrid, params);
+ return getCRSFactory().createDerivedCRS(properties(name), baseCRS, conversion, cs);
+ }
+
+ /**
+ * Returns the properties map for the construction of a <abbr>CRS</abbr> or operation of the given name.
+ */
+ private Map<String,?> properties(final Object name) {
+ properties.put(IdentifiedObject.NAME_KEY, name);
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Returns a default name for the component of a grid <abbr>CRS</abbr>.
+ *
+ * @param baseCRS the real world <abbr>CRS</abbr> from which to derive a grid <abbr>CRS</abbr>.
+ * @return default name for a grid <abbr>CRS</abbr> derived from the given real world <abbr>CRS</abbr>.
+ */
+ private static String name(final CoordinateReferenceSystem baseCRS) {
+ String name = IdentifiedObjects.getSimpleNameOrIdentifier(baseCRS);
+ if (name == null) {
+ name = IdentifiedObjects.getDisplayName(baseCRS, LOCALE);
+ }
+ return "Grid based on " + name;
+ }
+
+ /**
+ * Returns the coordinate reference system that we use as a template for object names.
+ * This template uses generic terms such as "Cell indices" for the <abbr>CRS</abbr> name
+ * and "Unknown grid" for the datum.
+ */
+ private static EngineeringCRS template() {
+ return CommonCRS.Engineering.GRID.crs();
+ }
+
+ /**
+ * Creates the coordinate system for the derived or engineering <abbr>CRS</abbr> of a grid.
+ *
+ * @param dimension number of dimensions of the coordinate system to create.
+ * @param dimensionNames names of grid dimension. Shall not be null but may contain null elements.
+ * @param directions directions of the axes of the coordinate system to create. May contain null elements.
+ * @param labelOffset offset to add to the dimension for producing a default axis name or abbreviation.
+ * @return coordinate system for the grid extent, or {@code null} if it cannot be inferred.
+ * @throws FactoryException if an error occurred during the use of {@link CSFactory}.
+ */
+ private CoordinateSystem createCS(final int dimension, final DimensionNameType[] dimensionNames,
+ final AxisDirection[] directions, final int labelOffset, final boolean cartesian)
+ throws FactoryException
+ {
+ final CSFactory csFactory = getCSFactory();
+ boolean hasVertical = false;
+ boolean hasTime = false;
+ boolean hasOther = false;
+ final var axes = new CoordinateSystemAxis[dimension];
+ for (int j=0; j<dimension; j++) {
+ String abbreviation = null;
+ final DimensionNameType type = dimensionNames[j];
+ if (type != null) {
+ if (type == DimensionNameType.COLUMN || type == DimensionNameType.SAMPLE) {
+ abbreviation = "x";
+ } else if (type == DimensionNameType.ROW || type == DimensionNameType.LINE) {
+ abbreviation = "y";
+ } else if (type == DimensionNameType.VERTICAL) {
+ abbreviation = "z"; hasVertical = true;
+ } else if (type == DimensionNameType.TIME) {
+ abbreviation = "t"; hasTime = true;
+ } else {
+ hasOther = true;
+ }
+ }
+ if (abbreviation != null) {
+ for (int i = j; --i >= 0;) {
+ final CoordinateSystemAxis previous = axes[i];
+ if (abbreviation.equals(previous.getAbbreviation())) {
+ abbreviation = null;
+ break;
+ }
+ }
+ }
+ if (abbreviation == null) {
+ final var b = new StringBuilder(4).append('x').append(labelOffset + j);
+ for (int i = b.length(); --i >= 1;) {
+ b.setCharAt(i, Characters.toSubScript(b.charAt(i)));
+ }
+ abbreviation = b.toString();
+ }
+ /*
+ * Try to infer the axis name from the grid dimension name type, otherwise create
+ * a default name in a way similar to the abbreviation (with indices in subscripts).
+ */
+ String name = Types.toString(Types.getCodeTitle(type), LOCALE);
+ if (name == null) {
+ name = Vocabulary.forLocale(LOCALE).getString(Vocabulary.Keys.Dimension_1, labelOffset + j);
+ }
+ AxisDirection direction = directions[j];
+ if (direction == null) {
- direction = AxisDirection.UNSPECIFIED;
++ direction = AxisDirections.UNSPECIFIED;
+ }
+ axes[j] = csFactory.createCoordinateSystemAxis(properties(name), abbreviation, direction, Units.UNITY);
+ }
+ /*
+ * Create a coordinate system of affine type if all axes seem spatial.
+ * If no specialized type seems to fit, use an unspecified ("abstract")
+ * coordinate system type in last resort.
+ */
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
+ final Map<String,?> properties = properties(template().getCoordinateSystem().getName());
+ final CoordinateSystemAxis axis = axes[0];
+ switch (dimension) {
+ case 1: {
+ if (hasVertical) {
+ return csFactory.createVerticalCS(properties, axis);
+ } else if (hasTime) {
+ return csFactory.createTimeCS(properties, axis);
+ } else if (hasOther) {
+ break;
+ } else {
+ return csFactory.createLinearCS(properties, axis);
+ }
+ }
+ case 2: {
+ if (hasVertical | hasTime | hasOther) break;
+ return cartesian
+ ? csFactory.createCartesianCS(properties, axis, axes[1])
+ : csFactory.createAffineCS (properties, axis, axes[1]);
+ }
+ case 3: {
+ if (hasVertical | hasTime | hasOther) break;
+ return cartesian
+ ? csFactory.createCartesianCS(properties, axis, axes[1], axes[2])
+ : csFactory.createAffineCS (properties, axis, axes[1], axes[2]);
+ }
+ }
+ return new AbstractCS(properties, axes);
+ }
+
+ /**
+ * Returns the default axis directions for grid dimensions of the given name.
+ *
+ * @param types grid dimension names.
+ * @return default axis directions. May contain null elements.
+ */
+ private static AxisDirection[] directions(final DimensionNameType[] types) {
+ final var directions = new AxisDirection[types.length];
+ for (int i=0; i<types.length; i++) {
+ final DimensionNameType type = types[i];
+ if (type != null) {
+ final AxisDirection direction = GridExtent.AXIS_DIRECTIONS.get(type);
+ if (!ArraysExt.contains(directions, direction)) {
+ directions[i] = direction;
+ }
+ }
+ }
+ return directions;
+ }
+
+ /**
+ * Adjusts the order of code list values for any change of order applied by the given transform.
+ * For example, if {@code directions} contains the axis directions in the coverage <abbr>CRS</abbr>
+ * and if {@code derivative} is the derivative of the <abbr>CRS</abbr> to grid transform, then this
+ * method returns the axis directions of the grid. Values that cannot be mapped are set to null.
+ *
+ * @param directions the directions to reorder. Shall not be null but may contain null elements.
+ * @param derivative derivative of the transform from source to target <abbr>CRS</abbr>.
+ * @param source an optional array to reorder together with {@code directions}.
+ * @param target where to store the result of {@code source} reordering, or {@code null}.
+ * @return the reordered axis directions. May contain {@code null} elements.
+ */
+ private static AxisDirection[] reorder(final AxisDirection[] directions, final Matrix derivative,
+ final DimensionNameType[] source, final DimensionNameType[] target)
+ {
+ final var ordered = new AxisDirection[derivative.getNumRow()];
+ for (int j=0; j<ordered.length; j++) {
+ boolean found = false;
+ for (int i=0; i<directions.length; i++) {
+ final double m = derivative.getElement(j, i);
+ if (m != 0) {
+ if (found) {
+ ordered[j] = null;
+ if (target != null) {
+ target[j] = null;
+ }
+ break;
+ }
+ found = true;
+ AxisDirection selected = directions[i];
+ if (selected != null && m < 0) {
+ selected = AxisDirections.opposite(selected);
+ }
+ ordered[j] = selected;
+ if (target != null && i < source.length) {
+ target[j] = source[i];
+ }
+ }
+ }
+ }
+ return ordered;
+ }
+
+ /**
+ * Builds the coordinate reference system of the result of transforming a {@link GridExtent}.
+ * This is used only in the rare cases where we need to represent an extent as an envelope.
+ * This class converts {@link DimensionNameType} codes into axis names, abbreviations and directions.
+ * It is the converse of {@link GridExtent#typeFromAxes(CoordinateReferenceSystem, int)}.
+ *
+ * <p>The <abbr>CRS</abbr> type is always engineering. In particular, the <abbr>CRS</abbr> cannot be temporal
+ * because we do not know the temporal datum origin and because index unit is not a temporal unit.</p>
+ *
+ * @param derivative derivative of the transform converting grid cell indices to envelope coordinates.
+ * @param types the value of {@link GridExtent#types} or a default value (shall not be {@code null}).
+ * @return <abbr>CRS</abbr> for the grid, or empty if it cannot be built.
+ * @throws FactoryException if an error occurred during the use of a referencing factory.
+ *
+ * @see GridExtent#toEnvelope(MathTransform)
+ * @see GridExtent#typeFromAxes(CoordinateReferenceSystem, int)
+ */
+ final Optional<EngineeringCRS> forExtentAlone(final Matrix derivative, final DimensionNameType[] types)
+ throws FactoryException
+ {
+ final int dimension = derivative.getNumRow();
+ final var dimensionNames = new DimensionNameType[dimension];
+ AxisDirection[] directions = directions(ArraysExt.resize(types, dimension));
+ directions = reorder(directions, derivative, types, dimensionNames);
+ final CoordinateSystem cs = createCS(dimension, dimensionNames, directions, 1, false);
+ if (cs == null) {
+ return Optional.empty();
+ }
+ final EngineeringCRS template = template();
+ return Optional.of(getCRSFactory().createEngineeringCRS(properties(template.getName()), template.getDatum(), cs));
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
index 62112ecfca,80d8eaca8b..0730dd5952
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridGeometry.java
@@@ -37,9 -37,8 +37,9 @@@ import org.opengis.referencing.operatio
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.operation.CoordinateOperation;
- import org.opengis.referencing.operation.NoninvertibleTransformException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.DerivedCRS;
++import org.opengis.referencing.crs.EngineeringCRS;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.apache.sis.math.MathFunctions;
diff --cc endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
index 433e41bdfa,4c03cc09fe..bccc0a700e
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/GridGeometryTest.java
@@@ -17,11 -17,18 +17,17 @@@
package org.apache.sis.coverage.grid;
import java.util.Set;
+ import java.util.List;
import org.opengis.util.FactoryException;
import org.opengis.geometry.Envelope;
+ import org.opengis.metadata.Identifier;
import org.opengis.metadata.spatial.DimensionNameType;
+ import org.opengis.metadata.extent.GeographicBoundingBox;
-import org.opengis.referencing.ObjectDomain;
import org.opengis.referencing.cs.AxisDirection;
+ import org.opengis.referencing.cs.CoordinateSystem;
+ import org.opengis.referencing.crs.SingleCRS;
import org.opengis.referencing.crs.DerivedCRS;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
@@@ -50,6 -61,6 +59,9 @@@ import static org.apache.sis.referencin
import static org.apache.sis.feature.Assertions.assertGridToCenterEquals;
import static org.apache.sis.feature.Assertions.assertGridToCornerEquals;
++// Specific to the main branch:
++import static org.apache.sis.test.GeoapiAssert.assertAxisDirectionsEqual;
++
/**
* Tests the {@link GridGeometry} implementation.
@@@ -808,29 -819,68 +820,67 @@@ public final class GridGeometryTest ext
}
/**
- * Tests {@link GridGeometry#createImageCRS(String, PixelInCell)}.
+ * Tests {@link GridGeometry#createGridCRS(Identifier, PixelInCell)}.
+ *
+ * @throws FactoryException if the <abbr>CRS</abbr> cannot be built.
*/
@Test
- public void testCreateImageCRS() {
+ public void testCreateGridCRS() throws FactoryException {
final var gg = new GridGeometry(
new GridExtent(null, null, new long[] {17, 10, 4}, true),
- PixelInCell.CELL_CENTER,
+ PixelInCell.CELL_CORNER,
MathTransforms.linear(new Matrix4(
- 1, 0, 0, -7,
- 0, -1, 0, 50,
- 0, 0, 8, 20,
+ 0, -1, 0, 50, // Longitude
+ 1, 0, 0, -7, // Latitude
+ 0, 0, 8, 20, // Time
0, 0, 0, 1)),
HardCodedCRS.WGS84_WITH_TIME);
-
- final DerivedCRS crs = gg.createImageCRS("Horizontal part", PixelInCell.CELL_CENTER);
- assertEquals("Horizontal part", crs.getName().getCode());
- assertSame(HardCodedCRS.WGS84, crs.getBaseCRS());
+ /*
+ * Metadata about the CRS as a whole (a CompoundCRS).
+ * Axes are like "Dimension 1 (x₁)" because we did not
+ * specified some `DimentionNameType` parameter values.
+ */
+ final Identifier name = new ImmutableIdentifier(null, null, "Tested grid CRS");
+ final CoordinateReferenceSystem crs = gg.createGridCRS(name, PixelInCell.CELL_CORNER);
- final ObjectDomain domain = assertSingleton(crs.getDomains());
+ final GeographicBoundingBox bbox = assertInstanceOf(GeographicBoundingBox.class,
- assertSingleton(domain.getDomainOfValidity().getGeographicElements()));
++ assertSingleton(crs.getDomainOfValidity().getGeographicElements()));
+ assertEquals(39, bbox.getWestBoundLongitude());
+ assertEquals(50, bbox.getEastBoundLongitude());
+ assertEquals(-7, bbox.getSouthBoundLatitude());
+ assertEquals(11, bbox.getNorthBoundLatitude());
- assertNotNull(domain.getScope()); // Text depends on the locale.
++ assertNotNull(crs.getScope()); // Text depends on the locale.
+ assertSame(name, crs.getName());
+ /*
+ * Check the horizontal and temporal components.
+ */
+ final List<SingleCRS> components = CRS.getSingleComponents(crs);
+ assertEquals(2, components.size());
+ final var horizontal = assertInstanceOf(DerivedCRS.class, components.get(0));
+ final var temporal = assertInstanceOf(DerivedCRS.class, components.get(1));
+ assertSame(HardCodedCRS.WGS84, horizontal.getBaseCRS());
+ assertSame(HardCodedCRS.TIME, temporal .getBaseCRS());
+ assertAxisDirectionsEqual(horizontal.getCoordinateSystem(), AxisDirection.NORTH, AxisDirection.WEST);
+ assertAxisDirectionsEqual(temporal .getCoordinateSystem(), AxisDirection.FUTURE);
assertMatrixEquals(
- new Matrix3(1, 0, 7, // Opposite sign because this is the inverse transform.
- 0, -1, 50, // Opposite sign cancelled by -1 scale factor.
- 0, 0, 1),
- crs.getConversionFromBase().getMathTransform(),
+ new Matrix3(0, 1, 7, // Opposite sign of translation term because this is the inverse transform.
+ -1, 0, 50, // Reminder: sign of translation term is inversed by the scale factor -1.
+ 0, 0, 1),
+ horizontal.getConversionFromBase().getMathTransform(),
"CRS to grid");
+ assertMatrixEquals(
+ new Matrix2(0.125, -2.5, 0, 1),
+ temporal.getConversionFromBase().getMathTransform(),
+ "CRS to grid");
+ /*
+ * Check axis names. The grid dimension which is mapped to longitudes has its direction
+ * reversed because of the -1 sign in the scale factor.
+ */
+ final CoordinateSystem cs = crs.getCoordinateSystem();
+ assertEquals(3, cs.getDimension());
+ assertEquals("x₁", cs.getAxis(0).getAbbreviation());
+ assertEquals("x₂", cs.getAxis(1).getAbbreviation());
+ assertEquals("t", cs.getAxis(2).getAbbreviation());
+ assertAxisDirectionsEqual(cs, AxisDirection.NORTH, AxisDirection.WEST, AxisDirection.FUTURE);
}
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/shared/NameToIdentifier.java
index 43d3262342,b7624b10ff..876bbd9d08
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/shared/NameToIdentifier.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/shared/NameToIdentifier.java
@@@ -121,16 -125,10 +125,18 @@@ public final class NameToIdentifier imp
return name.tip().toString();
}
+ /**
+ * Returns {@code null} since names are not versioned.
+ */
+ @Override
+ public String getVersion() {
+ return null;
+ }
+
/**
* Returns a hash code value for this object.
+ *
+ * @return hash code for this object.
*/
@Override
public int hashCode() {
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
index 0b11ac9e8a,c9bb1dd381..30fef315df
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/Dispatcher.java
@@@ -336,6 -337,27 +342,27 @@@ final class Dispatcher implements Invoc
return value;
}
+ /**
+ * Copies the content to an ordinary implementation class (plain old object).
+ * The returned object should be serializable if allowed by the implementation.
+ *
+ * @param proxy the object on which the method is invoked.
+ * @return a copy of this metadata object.
+ */
+ private Object writeReplace(final Object proxy) throws NotSerializableException {
+ ReflectiveOperationException cause = null;
+ final Class<?> type = proxy.getClass().getInterfaces()[0];
+ final Class<?> impl = source.standard.getImplementation(type);
+ if (impl != null) try {
- return impl.getDeclaredConstructor(argument(type)).newInstance(proxy);
++ return impl.getDeclaredConstructor(type).newInstance(proxy);
+ } catch (ReflectiveOperationException e) {
+ cause = e;
+ }
+ final var e = new NotSerializableException(type.getCanonicalName());
+ e.initCause(cause);
+ throw e;
+ }
+
/**
* Returns the error message for a failure to query the database for the property identified by the given method.
*/
diff --cc endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
index 8f7355e473,6917ee93f9..9e82cf5dd7
--- a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
+++ b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/MilitaryGridReferenceSystem.java
@@@ -289,8 -294,15 +289,8 @@@ public class MilitaryGridReferenceSyste
*/
@Workaround(library="JDK", version="8", fixed="25")
private static Map<String,?> properties() {
- AbstractParty party = new AbstractParty("North Atlantic Treaty Organization", null);
- Party party;
- try {
- party = MetadataSource.getProvided().lookup(Party.class, "{org}NATO");
- } catch (MetadataStoreException e) {
- party = null;
- Logging.unexpectedException(LOGGER, MilitaryGridReferenceSystem.class, "<init>", e);
- }
- var name = new NamedIdentifier(null, "NATO", Resources.formatInternational(Resources.Keys.MGRS), null, null);
- return properties(name, IDENTIFIER, party);
++ final var party = new AbstractParty("North Atlantic Treaty Organization", null);
+ return properties(new NamedIdentifier(null, "NATO", Resources.formatInternational(Resources.Keys.MGRS), null, null), IDENTIFIER, party);
}
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/module-info.java
index b5620ddf6d,bacffd1670..6b1c1f3ee8
--- a/endorsed/src/org.apache.sis.referencing/main/module-info.java
+++ b/endorsed/src/org.apache.sis.referencing/main/module-info.java
@@@ -200,10 -204,6 +200,13 @@@ module org.apache.sis.referencing
org.glassfish.jaxb.core, // For access to various classes.
jakarta.xml.bind; // Seems ignored.
+ exports org.apache.sis.referencing.internal to // On main branch only, for transition from GeoAPI 3.0.
+ org.apache.sis.console,
+ org.apache.sis.openoffice;
+
++ exports org.apache.sis.pending.geoapi.referencing to
++ org.apache.sis.gui; // In the "optional" sub-project.
++
/*
* Allow JAXB to use reflection for marshalling and
* unmarshalling Apache SIS objects in XML documents.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/CRS.java
index c3e7f38698,48f2dee3bc..65524500e0
--- 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
@@@ -84,9 -84,10 +84,9 @@@ import org.apache.sis.referencing.datum
import org.apache.sis.referencing.operation.AbstractCoordinateOperation;
import org.apache.sis.referencing.operation.CoordinateOperationContext;
import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
- import org.apache.sis.referencing.operation.DefaultConversion;
+ import org.apache.sis.referencing.operation.DefiningConversion;
import org.apache.sis.referencing.factory.GeodeticObjectFactory;
import org.apache.sis.referencing.factory.UnavailableFactoryException;
-import org.apache.sis.coordinate.DefaultCoordinateMetadata;
import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
import org.apache.sis.metadata.iso.extent.Extents;
import org.apache.sis.util.ArgumentChecks;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
index 964472c9fc,de463c2b1f..8b4878a52c
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/NamedIdentifier.java
@@@ -122,14 -122,18 +122,18 @@@ public class NamedIdentifier extends Im
*
* @param identifier the identifier to copy.
*
- * @see #castOrCopy(Identifier)
+ * @see #castOrCopy(ReferenceIdentifier)
*/
- public NamedIdentifier(final Identifier identifier) {
+ public NamedIdentifier(final ReferenceIdentifier identifier) {
super(identifier);
- if (identifier instanceof GenericName) {
+ if (identifier instanceof NameToIdentifier) {
+ name = ((NameToIdentifier) identifier).name;
+ } else if (identifier instanceof GenericName) {
name = (GenericName) identifier;
- isNameSupplied = true;
+ } else {
+ return;
}
+ isNameSupplied = true;
}
/**
@@@ -342,9 -346,11 +346,11 @@@
* @return a SIS implementation containing the values of the given object (may be the
* given object itself), or {@code null} if the argument was null.
*
+ * @see #toGenericName(Identifier)
+ *
* @since 1.0
*/
- public static NamedIdentifier castOrCopy(final Identifier object) {
+ public static NamedIdentifier castOrCopy(final ReferenceIdentifier object) {
if (object == null || object instanceof NamedIdentifier) {
return (NamedIdentifier) object;
}
@@@ -378,6 -386,36 +386,36 @@@
return new NamedIdentifier(object);
}
+ /**
+ * Returns the given identifier as a name. This method is similar to {@link #castOrCopy(Identifier)}
+ * except that it does not require the given {@code object} to be a {@code NamedIdentifier} instance.
+ *
+ * @param object the object to get as a name, or {@code nulk}.
+ * @return the given object as a name, or {@code null} if the argument was null.
+ * @since 1.7
+ */
- public static GenericName toGenericName(final Identifier object) {
++ public static GenericName toGenericName(final ReferenceIdentifier object) {
+ if (object == null || object instanceof GenericName) {
+ return (GenericName) object;
+ }
+ return new NamedIdentifier(object);
+ }
+
+ /**
+ * Returns the given name as an identifier. This method is similar to {@link #castOrCopy(GenericName)}
+ * except that it does not require the given {@code object} to be a {@code NamedIdentifier} instance.
+ *
+ * @param object the object to get as an identifier, or {@code nulk}.
+ * @return the given object as an identifier, or {@code null} if the argument was null.
+ * @since 1.7
+ */
+ public static ReferenceIdentifier toIdentifier(final GenericName object) {
+ if (object == null || object instanceof ReferenceIdentifier) {
+ return (ReferenceIdentifier) object;
+ }
+ return new NamedIdentifier(object);
+ }
+
/**
* The last element in the sequence of {@linkplain #getParsedNames() parsed names}.
* By default, this is the same value as the {@linkplain #getCode() code} provided as a local name.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
index 4bcc651c5b,697396e01c..f3277fb785
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java
@@@ -355,9 -348,10 +355,10 @@@ public class GeodeticObjectFactory exte
* @param ensemble collection of reference frames which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the three-dimensional Cartesian coordinate system for the created CRS.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, DatumEnsemble, CartesianCS)
+ * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, DefaultDatumEnsemble, CartesianCS)
* @see GeodeticAuthorityFactory#createGeodeticCRS(String)
*
* @since 1.5
@@@ -470,9 -463,10 +472,10 @@@
* @param ensemble collection of reference frames which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the spherical coordinate system for the created CRS.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, DatumEnsemble, SphericalCS)
+ * @see DefaultGeocentricCRS#DefaultGeocentricCRS(Map, GeodeticDatum, DefaultDatumEnsemble, SphericalCS)
* @see GeodeticAuthorityFactory#createGeodeticCRS(String)
*
* @since 1.5
@@@ -618,9 -616,10 +623,10 @@@
* @param ensemble collection of reference frames which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the two- or three-dimensional ellipsoidal coordinate system for the created <abbr>CRS</abbr>.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultGeographicCRS#DefaultGeographicCRS(Map, GeodeticDatum, DatumEnsemble, EllipsoidalCS)
+ * @see DefaultGeographicCRS#DefaultGeographicCRS(Map, GeodeticDatum, DefaultDatumEnsemble, EllipsoidalCS)
* @see GeodeticAuthorityFactory#createGeographicCRS(String)
*
* @since 1.5
@@@ -1086,9 -1095,10 +1102,10 @@@
* @param ensemble collection of reference frames which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the vertical coordinate system for the created <abbr>CRS</abbr>.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultVerticalCRS#DefaultVerticalCRS(Map, VerticalDatum, DatumEnsemble, VerticalCS)
+ * @see DefaultVerticalCRS#DefaultVerticalCRS(Map, VerticalDatum, DefaultDatumEnsemble, VerticalCS)
* @see GeodeticAuthorityFactory#createVerticalCRS(String)
*
* @since 1.5
@@@ -1235,9 -1313,10 +1253,10 @@@
* @param ensemble collection of datum which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the temporal coordinate system for the created <abbr>CRS</abbr>.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultTemporalCRS#DefaultTemporalCRS(Map, TemporalDatum, DatumEnsemble, TimeCS)
+ * @see DefaultTemporalCRS#DefaultTemporalCRS(Map, TemporalDatum, DefaultDatumEnsemble, TimeCS)
* @see GeodeticAuthorityFactory#createTemporalCRS(String)
*
* @since 1.5
@@@ -1362,9 -1439,10 +1383,10 @@@
* @param ensemble collection of datum which for low accuracy requirements may be considered to be
* insignificantly different from each other, or {@code null} if there is no such ensemble.
* @param cs the parametric coordinate system for the created <abbr>CRS</abbr>.
+ * @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*
- * @see DefaultParametricCRS#DefaultParametricCRS(Map, ParametricDatum, DatumEnsemble, ParametricCS)
+ * @see DefaultParametricCRS#DefaultParametricCRS(Map, DefaultParametricDatum, DefaultDatumEnsemble, ParametricCS)
* @see GeodeticAuthorityFactory#createParametricCRS(String)
*
* @since 1.5
@@@ -1407,10 -1488,8 +1429,11 @@@
* Creates a parametric datum.
* The default implementation creates a {@link DefaultParametricDatum} instance.
*
+ * <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
+ * to {@code org.opengis.referencing.datum.ParametricDatum}. This change is pending GeoAPI revision.</div>
+ *
* @param properties name and other properties to give to the new object.
+ * @return the datum for the given properties.
* @throws FactoryException if the object creation failed.
*
* @see DefaultParametricDatum#DefaultParametricDatum(Map)
@@@ -1440,11 -1520,9 +1463,12 @@@
*
* The default implementation creates a {@link DefaultParametricCS} instance.
*
+ * <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
+ * to {@code org.opengis.referencing.cs.ParametricCS}. This change is pending GeoAPI revision.</div>
+ *
* @param properties name and other properties to give to the new object.
* @param axis the single axis.
+ * @return the coordinate system for the given properties.
* @throws FactoryException if the object creation failed.
*
* @see DefaultParametricCS#DefaultParametricCS(Map, CoordinateSystemAxis)
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
index 9e7c3209de,500fe6329d..425109e109
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultConversion.java
@@@ -50,13 -50,10 +50,13 @@@ import org.opengis.referencing.crs.Gene
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
+// Specific to the main branch:
+import static org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
+
/**
- * A parameterized mathematical operation that converts coordinates to another CRS without any change of
- * {@linkplain org.apache.sis.referencing.datum.AbstractDatum datum}.
+ * A parameterized mathematical operation that converts coordinates
+ * to another <abbr>CRS</abbr> without any change of datum.
* The best-known example of a coordinate conversion is a map projection.
* The parameters describing coordinate conversions are defined rather than empirically derived.
*
@@@ -220,16 -181,40 +184,39 @@@ public class DefaultConversion extends
}
/**
- * Constructs a new conversion with the same values as the specified one, together with the
- * specified source and target CRS. While the source conversion can be an arbitrary one,
- * it is typically a defining conversion.
+ * Creates a new coordinate operation initialized from the given properties.
+ * It is caller's responsibility to set the following fields:
+ *
+ * <ul>
+ * <li>{@link #sourceCRS}</li>
+ * <li>{@link #targetCRS}</li>
+ * <li>{@link #transform}</li>
+ * <li>{@link #parameters}</li>
+ * </ul>
+ */
+ DefaultConversion(final Map<String,?> properties, final OperationMethod method) {
+ super(properties, method);
+ }
+
+ /**
+ * Constructs a new conversion with the same values as the specified one,
+ * together with the specified source and target <abbr>CRS</abbr>.
+ * While the source conversion can be an arbitrary one, it is typically a defining conversion.
+ *
+ * <p>The {@code normalized} argument is {@code true} if the defining conversion provides a normalized transform.
+ * In such case, an adjustment for axis directions and units of measurement will be added for matching the given
+ * source and target <abbr>CRS</abbr>s. If {@code normalized} is {@code false}, then the defining conversion shall
+ * provide the complete transform and no adjustments is added. This argument is ignored if the defining conversion
+ * already provides source and target <abbr>CRS</abbr>s.</p>
*
* @param definition the defining conversion.
- * @param source the new source CRS.
- * @param target the new target CRS.
+ * @param normalized whether the transform provided by the defining conversion is normalized.
+ * @param source the new source <abbr>CRS</abbr>.
+ * @param target the new target <abbr>CRS</abbr>.
* @param factory the factory to use for creating a transform from the parameters or for performing axis changes.
*/
- @SuppressWarnings("deprecation")
DefaultConversion(final Conversion definition,
+ final boolean normalized,
final CoordinateReferenceSystem source,
final CoordinateReferenceSystem target,
final MathTransformFactory factory) throws FactoryException
diff --cc endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java
index a81d3e9ab3,ac6cda0794..56c8eda0be
--- a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java
+++ b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/CRSTest.java
@@@ -47,7 -53,11 +53,8 @@@ import org.apache.sis.referencing.crs.H
import org.apache.sis.referencing.operation.HardCodedConversions;
import static org.apache.sis.test.Assertions.assertEqualsIgnoreMetadata;
import static org.apache.sis.test.Assertions.assertMessageContains;
+ import static org.apache.sis.test.Assertions.assertMultilinesEquals;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.ObjectDomain;
-
/**
* Tests the {@link CRS} class.
diff --cc optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/CRSChooser.java
index 66562ab1c5,415fd9f432..fc2218c89a
--- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/CRSChooser.java
+++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/CRSChooser.java
@@@ -71,9 -71,12 +71,9 @@@ import org.apache.sis.util.resources.Vo
// Specific to the main and geoapi-3.1 branches:
import org.opengis.referencing.crs.GeneralDerivedCRS;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.ObjectDomain;
-
/**
- * A list of Coordinate Reference Systems (CRS) from which the user can select.
+ * A list of Coordinate Reference Systems (<abbr>CRS</abbr>) from which the user can select.
* The CRS choices is built in a background thread from a specified {@link CRSAuthorityFactory}.
*
* @author Johann Sorel (Geomatys)
diff --cc optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/FilterByDatum.java
index 0000000000,dfec44608c..66377022fe
mode 000000,100644..100644
--- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/FilterByDatum.java
+++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/referencing/FilterByDatum.java
@@@ -1,0 -1,182 +1,185 @@@
+ /*
+ * 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.gui.referencing;
+
+ import java.util.Set;
+ import java.util.List;
+ import java.util.HashSet;
+ import java.util.ArrayList;
+ import java.util.function.Predicate;
+ import org.opengis.referencing.ReferenceSystem;
+ import org.opengis.referencing.datum.Datum;
+ import org.opengis.referencing.datum.GeodeticDatum;
+ import org.opengis.referencing.datum.TemporalDatum;
+ import org.opengis.referencing.datum.VerticalDatum;
+ import org.opengis.referencing.datum.EngineeringDatum;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.opengis.referencing.crs.SingleCRS;
+ import org.opengis.referencing.crs.TemporalCRS;
+ import org.opengis.referencing.crs.VerticalCRS;
+ import org.opengis.referencing.crs.EngineeringCRS;
+ import org.apache.sis.util.Classes;
+ import org.apache.sis.referencing.CRS;
+ import org.apache.sis.referencing.internal.shared.ReferencingUtilities;
+ import org.apache.sis.util.Utilities;
+
++// Specific to the main branch:
++import static org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble;
++
+
+ /**
+ * Filter of reference systems that are compatible with the data to render.
+ * Instances of this class are immutable and thread-safe.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ final class FilterByDatum implements Predicate<ReferenceSystem> {
+ /**
+ * The types of datum allowed by the coordinate reference systems shown in the menus of combo boxes.
+ * If a <abbr>CRS</abbr> has a datum of a type which is not in this array, then it will not be shown.
+ * A {@code null} array means that no filtering is applied.
+ *
+ * <p>The current version of this class filters only by datum type because this is quick.
+ * But a more advanced version should check if a path exists between <abbr>CRS</abbr>s.</p>
+ */
+ private final Class<? extends Datum>[] allowedDatumTypes;
+
+ /**
+ * Datum instances for which an exact match is required.
+ * We handle engineering datum in a special way because they are the kind of datum created
+ * when we cannot map the <abbr>CRS</abbr> of a grid coverage to a geodetic <abbr>CRS</abbr>.
+ * We no such pivot, we cannot transform from one engineering <abbr>CRS</abbr> to another.
+ */
+ private final EngineeringDatum[] instances;
+
+ /**
+ * Creates a new filter for coordinate reference systems to show in the menu or combo box.
+ * If the <abbr>CRS</abbr> of a menu item has a datum of a type which is different than the
+ * datum types of all elements in the {@code refsys} array, then that menu item is hidden.
+ *
+ * @param refsys <abbr>CRS</abbr>s from which to get the datum types. Null elements are ignored.
+ * @return filter, or {@code null} if none.
+ */
+ static FilterByDatum create(final CoordinateReferenceSystem[] refsys) {
+ final var types = new HashSet<Class<? extends Datum>>();
+ final var instances = new ArrayList<EngineeringDatum>();
+ for (final CoordinateReferenceSystem crs : refsys) {
+ for (final SingleCRS component : CRS.getSingleComponents(crs)) {
+ add(component.getDatum(), types, instances);
- final var ensemble = component.getDatumEnsemble();
++ final var ensemble = getDatumEnsemble(component);
+ if (ensemble != null) {
+ for (final Datum datum : ensemble.getMembers()) {
+ add(datum, types, instances);
+ }
+ }
+ }
+ }
+ types.remove(null);
+ if (instances.isEmpty() && types.equals(Set.of(Datum.class))) {
+ return null;
+ }
+ return new FilterByDatum(types.toArray(Class[]::new), instances.toArray(EngineeringDatum[]::new));
+ }
+
+ /**
+ * Adds the given datum in the given collections.
+ *
+ * @param datum the datum to add.
+ * @param types where to add the datum type.
+ * @param instances where to add the datum instance if not a duplicate.
+ */
+ private static void add(final Datum datum, final Set<Class<? extends Datum>> types, final List<EngineeringDatum> instances) {
+ types.add(ReferencingUtilities.getInterface(Datum.class, datum));
+ if (datum instanceof EngineeringDatum) {
+ for (int i = instances.size(); --i >= 0;) {
+ if (Utilities.equalsIgnoreMetadata(datum, instances.get(i))) {
+ return;
+ }
+ }
+ instances.add((EngineeringDatum) datum);
+ }
+ }
+
+ /**
+ * Creates a new filter for the given datum types.
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ private FilterByDatum(final Class[] types, final EngineeringDatum[] instances) {
+ allowedDatumTypes = types;
+ this.instances = instances;
+ }
+
+ /**
+ * Returns whether the given reference system has valid datum types.
+ *
+ * @param system the reference system to test.
+ * @return whether the given reference system can be accepted.
+ */
+ @Override
+ public boolean test(final ReferenceSystem system) {
+ if (system instanceof CoordinateReferenceSystem) {
+ next: for (final SingleCRS component : CRS.getSingleComponents((CoordinateReferenceSystem) system)) {
+ if (!accept(component.getDatum())) {
- final var ensemble = component.getDatumEnsemble();
++ final var ensemble = getDatumEnsemble(component);
+ if (ensemble != null) {
+ for (final Datum datum : ensemble.getMembers()) {
+ if (accept(datum)) {
+ continue next;
+ }
+ }
+ }
+ return false;
+ }
+ }
+ return true;
+ } else {
+ // Assume that referencing by identifiers depend on geodetic datum.
+ return Classes.isAssignableToAny(GeodeticDatum.class, allowedDatumTypes);
+ }
+ }
+
+ /**
+ * Tests whether the given datum is accepted.
+ */
+ private boolean accept(final Datum datum) {
+ if (datum != null && Classes.isAssignableToAny(datum.getClass(), allowedDatumTypes)) {
+ if (!(datum instanceof EngineeringDatum)) {
+ return true;
+ }
+ for (final EngineeringDatum instance : instances) {
+ if (Utilities.equalsIgnoreMetadata(datum, instance)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the base type of coordinate reference system for the datum accepted by this filter.
+ */
+ final Class<? extends CoordinateReferenceSystem> baseType() {
+ if (allowedDatumTypes.length == 1) {
+ final Class<? extends Datum> type = allowedDatumTypes[0];
+ if (VerticalDatum.class.isAssignableFrom(type)) return VerticalCRS.class;
+ if (TemporalDatum.class.isAssignableFrom(type)) return TemporalCRS.class;
+ if (EngineeringDatum.class.isAssignableFrom(type)) return EngineeringCRS.class;
+ return SingleCRS.class;
+ }
+ return CoordinateReferenceSystem.class;
+ }
+ }