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 2024/03/07 18:11:54 UTC
(sis) 01/01: Merge branch 'geoapi-3.1'. https://issues.apache.org/jira/browse/SIS-596
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/sis.git
commit 2b40ccfdc4a3072628ae881bf55b4826dc140ab3
Merge: 4150341710 9048ae55d3
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Mar 7 19:09:19 2024 +0100
Merge branch 'geoapi-3.1'.
https://issues.apache.org/jira/browse/SIS-596
endorsed/build.gradle.kts | 6 +-
.../apache/sis/cloud/aws/s3/ClientFileSystem.java | 2 +-
.../main/org/apache/sis/console/AboutCommand.java | 4 +-
.../main/org/apache/sis/console/Command.java | 2 +-
.../main/org/apache/sis/console/CommandRunner.java | 2 +-
.../apache/sis/console/FormattedOutputCommand.java | 2 +-
.../org/apache/sis/console/IdentifierCommand.java | 2 +-
.../apache/sis/console/ResourcesDownloader.java | 2 +-
.../org/apache/sis/console/TransformCommand.java | 8 +-
.../org.apache.sis.feature/main/module-info.java | 13 +-
.../org/apache/sis/coverage/CoverageCombiner.java | 6 +-
.../org/apache/sis/coverage/SampleDimension.java | 2 +-
.../org/apache/sis/coverage/SampleRangeFormat.java | 2 +-
.../coverage/grid/BandAggregateGridCoverage.java | 4 +-
.../coverage/grid/CoordinateOperationFinder.java | 6 +-
.../apache/sis/coverage/grid/DefaultEvaluator.java | 8 +-
.../sis/coverage/grid/DimensionAppender.java | 2 +-
.../apache/sis/coverage/grid/DimensionReducer.java | 2 +-
.../sis/coverage/grid/DimensionalityReduction.java | 2 +-
.../coverage/grid/FractionalGridCoordinates.java | 2 +-
.../org/apache/sis/coverage/grid/GridCoverage.java | 4 +-
.../apache/sis/coverage/grid/GridCoverage2D.java | 4 +-
.../sis/coverage/grid/GridCoverageBuilder.java | 10 +-
.../sis/coverage/grid/GridCoverageProcessor.java | 8 +-
.../apache/sis/coverage/grid/GridDerivation.java | 2 +-
.../org/apache/sis/coverage/grid/GridExtent.java | 10 +-
.../apache/sis/coverage/grid/GridExtentCRS.java | 2 +-
.../org/apache/sis/coverage/grid/GridGeometry.java | 18 +-
.../apache/sis/coverage/grid/ImageRenderer.java | 12 +-
.../sis/coverage/grid/ReducedGridCoverage.java | 2 +-
.../sis/coverage/grid/ResampledGridCoverage.java | 6 +-
.../apache/sis/coverage/grid/SliceGeometry.java | 8 +-
.../sis/coverage/grid/j2d/BatchComputedImage.java | 213 ----
.../sis/coverage/grid/j2d/ColorModelBuilder.java | 701 --------------
.../sis/coverage/grid/j2d/ColorModelFactory.java | 864 -----------------
.../sis/coverage/grid/j2d/ColorModelType.java | 106 --
.../sis/coverage/grid/j2d/ColorsForRange.java | 291 ------
.../sis/coverage/grid/j2d/DeferredProperty.java | 124 ---
.../apache/sis/coverage/grid/j2d/FillValues.java | 208 ----
.../apache/sis/coverage/grid/j2d/ImageLayout.java | 438 ---------
.../sis/coverage/grid/j2d/ImageUtilities.java | 673 -------------
.../grid/j2d/MultiBandsIndexColorModel.java | 314 ------
.../sis/coverage/grid/j2d/ObservableImage.java | 288 ------
.../sis/coverage/grid/j2d/RasterFactory.java | 314 ------
.../sis/coverage/grid/j2d/SampleModelFactory.java | 291 ------
.../sis/coverage/grid/j2d/ScaledColorModel.java | 162 ----
.../sis/coverage/grid/j2d/ScaledColorSpace.java | 223 -----
.../sis/coverage/grid/j2d/TileErrorHandler.java | 120 ---
.../sis/coverage/grid/j2d/TileOpExecutor.java | 1019 --------------------
.../sis/coverage/grid/j2d/TilePlaceholder.java | 404 --------
.../apache/sis/coverage/grid/j2d/TiledImage.java | 274 ------
.../sis/coverage/grid/j2d/WritableTiledImage.java | 217 -----
.../apache/sis/coverage/grid/j2d/package-info.java | 31 -
.../sis/coverage/internal/CommonDomainFinder.java | 383 --------
.../sis/coverage/internal/MultiSourceArgument.java | 619 ------------
.../sis/coverage/internal/RangeArgument.java | 395 --------
.../sis/coverage/internal/SampleDimensions.java | 179 ----
.../apache/sis/coverage/internal/package-info.java | 2 +-
.../sis/coverage/privy/BatchComputedImage.java | 213 ++++
.../sis/coverage/privy/ColorModelBuilder.java | 701 ++++++++++++++
.../sis/coverage/privy/ColorModelFactory.java | 864 +++++++++++++++++
.../apache/sis/coverage/privy/ColorModelType.java | 106 ++
.../apache/sis/coverage/privy/ColorsForRange.java | 291 ++++++
.../sis/coverage/privy/CommonDomainFinder.java | 383 ++++++++
.../sis/coverage/privy/DeferredProperty.java | 124 +++
.../org/apache/sis/coverage/privy/FillValues.java | 208 ++++
.../org/apache/sis/coverage/privy/ImageLayout.java | 438 +++++++++
.../apache/sis/coverage/privy/ImageUtilities.java | 673 +++++++++++++
.../coverage/privy/MultiBandsIndexColorModel.java | 314 ++++++
.../sis/coverage/privy/MultiSourceArgument.java | 619 ++++++++++++
.../apache/sis/coverage/privy/ObservableImage.java | 288 ++++++
.../apache/sis/coverage/privy/RangeArgument.java | 395 ++++++++
.../apache/sis/coverage/privy/RasterFactory.java | 314 ++++++
.../sis/coverage/privy/SampleDimensions.java | 179 ++++
.../sis/coverage/privy/SampleModelFactory.java | 291 ++++++
.../sis/coverage/privy/ScaledColorModel.java | 162 ++++
.../sis/coverage/privy/ScaledColorSpace.java | 223 +++++
.../sis/coverage/privy/TileErrorHandler.java | 120 +++
.../apache/sis/coverage/privy/TileOpExecutor.java | 1019 ++++++++++++++++++++
.../apache/sis/coverage/privy/TilePlaceholder.java | 404 ++++++++
.../org/apache/sis/coverage/privy/TiledImage.java | 274 ++++++
.../sis/coverage/privy/WritableTiledImage.java | 217 +++++
.../apache/sis/coverage/privy/package-info.java | 30 +
.../org/apache/sis/feature/AbstractFeature.java | 2 +-
.../org/apache/sis/feature/CharacteristicMap.java | 8 +-
.../apache/sis/feature/CharacteristicTypeMap.java | 4 +-
.../apache/sis/feature/DefaultAssociationRole.java | 2 +-
.../org/apache/sis/feature/DefaultFeatureType.java | 4 +-
.../main/org/apache/sis/feature/DenseFeature.java | 4 +-
.../org/apache/sis/feature/EnvelopeOperation.java | 6 +-
.../apache/sis/feature/ExpressionOperation.java | 6 +-
.../main/org/apache/sis/feature/FeatureFormat.java | 4 +-
.../org/apache/sis/feature/FeatureOperations.java | 2 +-
.../sis/feature/GroupAsPolylineOperation.java | 4 +-
.../main/org/apache/sis/feature/LinkOperation.java | 2 +-
.../apache/sis/feature/MultiValuedAssociation.java | 2 +-
.../apache/sis/feature/MultiValuedAttribute.java | 4 +-
.../org/apache/sis/feature/NamedFeatureType.java | 2 +-
.../org/apache/sis/feature/SingletonAttribute.java | 2 +-
.../main/org/apache/sis/feature/SparseFeature.java | 4 +-
.../apache/sis/feature/StringJoinOperation.java | 6 +-
.../sis/feature/builder/AttributeTypeBuilder.java | 8 +-
.../sis/feature/builder/FeatureTypeBuilder.java | 2 +-
.../sis/feature/internal/AttributeConvention.java | 440 ---------
.../sis/feature/internal/FeatureExpression.java | 127 ---
.../sis/feature/internal/FeatureUtilities.java | 92 --
.../apache/sis/feature/internal/InstantList.java | 80 --
.../sis/feature/internal/MovingFeatures.java | 129 ---
.../apache/sis/feature/internal/package-info.java | 4 +-
.../sis/feature/privy/AttributeConvention.java | 440 +++++++++
.../sis/feature/privy/FeatureExpression.java | 127 +++
.../apache/sis/feature/privy/FeatureUtilities.java | 92 ++
.../org/apache/sis/feature/privy/InstantList.java | 80 ++
.../apache/sis/feature/privy/MovingFeatures.java | 129 +++
.../org/apache/sis/feature/privy/package-info.java | 32 +
.../org/apache/sis/filter/ArithmeticFunction.java | 4 +-
.../apache/sis/filter/BinaryGeometryFilter.java | 2 +-
.../org/apache/sis/filter/ConvertFunction.java | 4 +-
.../apache/sis/filter/DefaultFilterFactory.java | 1 -
.../org/apache/sis/filter/FunctionRegister.java | 63 ++
.../org/apache/sis/filter/IdentifierFilter.java | 2 +-
.../main/org/apache/sis/filter/LeafExpression.java | 2 +-
.../main/org/apache/sis/filter/LogicalFilter.java | 4 +-
.../main/org/apache/sis/filter/Optimization.java | 2 +-
.../main/org/apache/sis/filter/PropertyValue.java | 2 +-
.../apache/sis/filter/internal/FunctionNames.java | 89 --
.../sis/filter/internal/FunctionRegister.java | 65 --
.../main/org/apache/sis/filter/internal/Node.java | 2 +-
.../sis/filter/internal/SortByComparator.java | 203 ----
.../org/apache/sis/filter/internal/Visitor.java | 328 -------
.../main/org/apache/sis/filter/internal/XPath.java | 251 -----
.../org/apache/sis/filter/privy/FunctionNames.java | 89 ++
.../apache/sis/filter/privy/SortByComparator.java | 203 ++++
.../main/org/apache/sis/filter/privy/Visitor.java | 328 +++++++
.../main/org/apache/sis/filter/privy/XPath.java | 251 +++++
.../org/apache/sis/filter/privy/package-info.java | 31 +
.../apache/sis/filter/sqlmm/FunctionWithSRID.java | 2 +-
.../main/org/apache/sis/filter/sqlmm/Registry.java | 2 +-
.../main/org/apache/sis/filter/sqlmm/ST_Point.java | 2 +-
.../apache/sis/filter/sqlmm/SpatialFunction.java | 2 +-
.../org/apache/sis/filter/sqlmm/TwoGeometries.java | 2 +-
.../apache/sis/geometry/wrapper/Geometries.java | 2 +-
.../apache/sis/geometry/wrapper/GeometryType.java | 2 +-
.../sis/geometry/wrapper/GeometryWrapper.java | 2 +-
.../geometry/wrapper/SpatialOperationContext.java | 6 +-
.../apache/sis/geometry/wrapper/esri/Factory.java | 2 +-
.../sis/geometry/wrapper/j2d/EmptyShape.java | 2 +-
.../apache/sis/geometry/wrapper/j2d/Factory.java | 6 +-
.../apache/sis/geometry/wrapper/j2d/FlatShape.java | 2 +-
.../sis/geometry/wrapper/j2d/MultiPolylines.java | 2 +-
.../sis/geometry/wrapper/j2d/ShapeProperties.java | 2 +-
.../sis/geometry/wrapper/j2d/ShapeWrapper.java | 2 +-
.../apache/sis/geometry/wrapper/j2d/Wrapper.java | 4 +-
.../apache/sis/geometry/wrapper/jts/Factory.java | 2 +-
.../org/apache/sis/geometry/wrapper/jts/JTS.java | 2 +-
.../sis/geometry/wrapper/jts/ShapeAdapter.java | 4 +-
.../sis/geometry/wrapper/jts/ShapeConverter.java | 2 +-
.../apache/sis/geometry/wrapper/jts/Wrapper.java | 2 +-
.../main/org/apache/sis/image/AnnotatedImage.java | 6 +-
.../org/apache/sis/image/BandAggregateImage.java | 4 +-
.../main/org/apache/sis/image/BandSelectImage.java | 8 +-
.../main/org/apache/sis/image/BandSharing.java | 2 +-
.../apache/sis/image/BandedSampleConverter.java | 14 +-
.../main/org/apache/sis/image/Colorizer.java | 4 +-
.../main/org/apache/sis/image/ComputedImage.java | 4 +-
.../main/org/apache/sis/image/DataType.java | 4 +-
.../main/org/apache/sis/image/ImageCombiner.java | 6 +-
.../main/org/apache/sis/image/ImageProcessor.java | 6 +-
.../main/org/apache/sis/image/MaskImage.java | 6 +-
.../main/org/apache/sis/image/MaskedImage.java | 10 +-
.../org/apache/sis/image/MultiSourceImage.java | 2 +-
.../org/apache/sis/image/MultiSourceLayout.java | 10 +-
.../org/apache/sis/image/MultiSourcePrefetch.java | 2 +-
.../main/org/apache/sis/image/PixelIterator.java | 4 +-
.../main/org/apache/sis/image/PlanarImage.java | 6 +-
.../sis/image/PositionalConsistencyImage.java | 2 +-
.../main/org/apache/sis/image/PrefetchedImage.java | 8 +-
.../main/org/apache/sis/image/RecoloredImage.java | 6 +-
.../main/org/apache/sis/image/ResampledImage.java | 8 +-
.../main/org/apache/sis/image/ResamplingGrid.java | 2 +-
.../org/apache/sis/image/StatisticsCalculator.java | 2 +-
.../main/org/apache/sis/image/TileCache.java | 2 +-
.../main/org/apache/sis/image/Transferer.java | 6 +-
.../main/org/apache/sis/image/Visualization.java | 8 +-
.../apache/sis/image/WritableComputedImage.java | 2 +-
.../sis/image/processing/CompoundFuture.java | 2 +-
.../sis/image/processing/isoline/Fragments.java | 2 +-
.../org/apache/sis/index/tree/NodeIterator.java | 2 +-
.../main/org/apache/sis/index/tree/PointTree.java | 2 +-
.../org/apache/sis/pending/geoapi/filter/Name.java | 2 +-
.../coverage/grid/ConvertedGridCoverageTest.java | 2 +-
.../coverage/grid/DimensionalityReductionTest.java | 2 +-
.../sis/coverage/grid/GridCoverage2DTest.java | 2 +-
.../sis/coverage/grid/GridCoverageBuilderTest.java | 2 +-
.../sis/coverage/grid/GridDerivationTest.java | 4 +-
.../apache/sis/coverage/grid/GridGeometryTest.java | 2 +-
.../coverage/grid/ResampledGridCoverageTest.java | 6 +-
.../coverage/grid/TranslatedGridCoverageTest.java | 2 +-
.../coverage/grid/j2d/ColorModelBuilderTest.java | 151 ---
.../sis/coverage/grid/j2d/ImageLayoutTest.java | 48 -
.../sis/coverage/grid/j2d/ImageUtilitiesTest.java | 235 -----
.../coverage/grid/j2d/SampleModelFactoryTest.java | 151 ---
.../coverage/grid/j2d/ScaledColorSpaceTest.java | 102 --
.../sis/coverage/internal/RangeArgumentTest.java | 94 --
.../sis/coverage/privy/ColorModelBuilderTest.java | 151 +++
.../apache/sis/coverage/privy/ImageLayoutTest.java | 48 +
.../sis/coverage/privy/ImageUtilitiesTest.java | 235 +++++
.../sis/coverage/privy/RangeArgumentTest.java | 94 ++
.../sis/coverage/privy/SampleModelFactoryTest.java | 151 +++
.../sis/coverage/privy/ScaledColorSpaceTest.java | 102 ++
.../apache/sis/feature/EnvelopeOperationTest.java | 2 +-
.../apache/sis/feature/FeatureMemoryBenchmark.java | 2 +-
.../apache/sis/feature/FeatureOperationsTest.java | 2 +-
.../sis/feature/StringJoinOperationTest.java | 2 +-
.../feature/builder/AttributeTypeBuilderTest.java | 2 +-
.../feature/builder/FeatureTypeBuilderTest.java | 2 +-
.../feature/internal/AttributeConventionTest.java | 122 ---
.../sis/feature/privy/AttributeConventionTest.java | 122 +++
.../apache/sis/feature/test/FeatureComparator.java | 2 +-
.../org/apache/sis/filter/TemporalFilterTest.java | 2 +-
.../test/org/apache/sis/filter/XPathTest.java | 2 +-
.../sis/filter/internal/FunctionNamesTest.java | 67 --
.../apache/sis/filter/privy/FunctionNamesTest.java | 67 ++
.../apache/sis/geometry/wrapper/jts/JTSTest.java | 2 +-
.../apache/sis/image/BandAggregateImageTest.java | 4 +-
.../org/apache/sis/image/BandSelectImageTest.java | 4 +-
.../sis/image/BandedSampleConverterTest.java | 2 +-
.../org/apache/sis/image/ImageCombinerTest.java | 2 +-
.../org/apache/sis/image/InterpolationTest.java | 4 +-
.../test/org/apache/sis/image/MaskedImageTest.java | 2 +-
.../test/org/apache/sis/image/PlanarImageTest.java | 2 +-
.../org/apache/sis/image/ResampledImageTest.java | 6 +-
.../org/apache/sis/image/ResamplingGridTest.java | 4 +-
.../apache/sis/image/StatisticsCalculatorTest.java | 2 +-
.../test/org/apache/sis/image/TiledImageMock.java | 4 +-
.../sis/image/processing/isoline/IsolinesTest.java | 2 +-
.../sis/image/processing/isoline/StepsViewer.java | 2 +-
... => org.apache.sis.util.privy.MetadataServices} | 0
.../org.apache.sis.metadata/main/module-info.java | 12 +-
.../org/apache/sis/metadata/MetadataStandard.java | 2 +-
.../org/apache/sis/metadata/MetadataVisitor.java | 2 +-
.../sis/metadata/MetadataVisitorException.java | 2 +-
.../apache/sis/metadata/ModifiableMetadata.java | 8 +-
.../org/apache/sis/metadata/PropertyAccessor.java | 10 +-
.../main/org/apache/sis/metadata/PropertyMap.java | 2 +-
.../main/org/apache/sis/metadata/Pruner.java | 2 +-
.../main/org/apache/sis/metadata/SpecialCases.java | 4 +-
.../main/org/apache/sis/metadata/StateChanger.java | 6 +-
.../main/org/apache/sis/metadata/TreeNode.java | 4 +-
.../org/apache/sis/metadata/TreeTableView.java | 4 +-
.../apache/sis/metadata/internal/AxisNames.java | 221 -----
.../sis/metadata/internal/CitationConstant.java | 238 +++++
.../apache/sis/metadata/internal/Identifiers.java | 306 ------
.../metadata/internal/ImplementationHelper.java | 304 ------
.../org/apache/sis/metadata/internal/Merger.java | 425 --------
.../apache/sis/metadata/internal/NameMeaning.java | 238 -----
.../sis/metadata/internal/NameToIdentifier.java | 321 ------
.../sis/metadata/internal/RecordSchemaSIS.java | 2 +-
.../sis/metadata/internal/ReferencingServices.java | 314 ------
.../apache/sis/metadata/internal/Resources.java | 7 +-
.../sis/metadata/internal/Resources.properties | 1 -
.../sis/metadata/internal/Resources_fr.properties | 1 -
.../sis/metadata/internal/ServicesForUtility.java | 14 +-
.../sis/metadata/internal/TemporalUtilities.java | 144 ---
.../metadata/internal/TransformationAccuracy.java | 89 --
.../apache/sis/metadata/internal/package-info.java | 2 +-
.../iso/DefaultExtendedElementInformation.java | 6 +-
.../apache/sis/metadata/iso/DefaultMetadata.java | 6 +-
.../org/apache/sis/metadata/iso/ISOMetadata.java | 6 +-
.../acquisition/DefaultEnvironmentalRecord.java | 2 +-
.../sis/metadata/iso/acquisition/DefaultEvent.java | 4 +-
.../iso/acquisition/DefaultRequestedDate.java | 4 +-
.../iso/acquisition/DefaultRequirement.java | 4 +-
.../sis/metadata/iso/citation/Citations.java | 8 +-
.../sis/metadata/iso/citation/DefaultCitation.java | 6 +-
.../metadata/iso/citation/DefaultCitationDate.java | 4 +-
.../sis/metadata/iso/citation/DefaultContact.java | 4 +-
.../iso/citation/DefaultResponsibleParty.java | 4 +-
.../metadata/iso/citation/DefaultTelephone.java | 4 +-
.../sis/metadata/iso/citation/package-info.java | 2 +-
.../sis/metadata/iso/content/DefaultBand.java | 2 +-
.../iso/content/DefaultCoverageDescription.java | 6 +-
.../DefaultFeatureCatalogueDescription.java | 4 +-
.../iso/content/DefaultFeatureTypeInfo.java | 2 +-
.../iso/content/DefaultImageDescription.java | 6 +-
.../iso/content/DefaultRangeDimension.java | 2 +-
.../iso/content/DefaultSampleDimension.java | 2 +-
.../sis/metadata/iso/content/package-info.java | 2 +-
.../metadata/iso/distribution/DefaultDataFile.java | 2 +-
.../DefaultDigitalTransferOptions.java | 4 +-
.../metadata/iso/distribution/DefaultFormat.java | 4 +-
.../metadata/iso/distribution/DefaultMedium.java | 10 +-
.../distribution/DefaultStandardOrderProcess.java | 4 +-
.../metadata/iso/distribution/package-info.java | 2 +-
.../sis/metadata/iso/extent/DefaultExtent.java | 2 +-
.../iso/extent/DefaultGeographicBoundingBox.java | 2 +-
.../iso/extent/DefaultSpatialTemporalExtent.java | 4 +-
.../metadata/iso/extent/DefaultTemporalExtent.java | 4 +-
.../metadata/iso/extent/DefaultVerticalExtent.java | 2 +-
.../apache/sis/metadata/iso/extent/Extents.java | 6 +-
.../iso/identification/AbstractIdentification.java | 2 +-
.../DefaultAggregateInformation.java | 2 +-
.../iso/identification/DefaultCoupledResource.java | 4 +-
.../identification/DefaultDataIdentification.java | 2 +-
.../DefaultRepresentativeFraction.java | 6 +-
.../iso/identification/DefaultResolution.java | 2 +-
.../metadata/iso/identification/DefaultUsage.java | 4 +-
.../metadata/iso/identification/OperationName.java | 2 +-
.../metadata/iso/identification/package-info.java | 2 +-
.../metadata/iso/legacy/LegacyPropertyAdapter.java | 2 +-
.../iso/lineage/DefaultNominalResolution.java | 2 +-
.../metadata/iso/lineage/DefaultProcessStep.java | 4 +-
.../sis/metadata/iso/lineage/DefaultSource.java | 2 +-
.../sis/metadata/iso/lineage/package-info.java | 2 +-
.../maintenance/DefaultMaintenanceInformation.java | 6 +-
.../sis/metadata/iso/maintenance/DefaultScope.java | 2 +-
.../sis/metadata/iso/maintenance/package-info.java | 2 +-
.../org/apache/sis/metadata/iso/package-info.java | 2 +-
.../sis/metadata/iso/quality/AbstractElement.java | 2 +-
.../iso/quality/DefaultCoverageResult.java | 2 +-
.../metadata/iso/quality/DefaultDataQuality.java | 2 +-
.../iso/quality/DefaultEvaluationMethod.java | 4 +-
.../iso/quality/DefaultMeasureReference.java | 2 +-
.../DefaultNonQuantitativeAttributeAccuracy.java | 2 +-
.../iso/quality/DefaultQuantitativeResult.java | 2 +-
.../sis/metadata/iso/quality/DefaultUsability.java | 2 +-
.../sis/metadata/iso/quality/package-info.java | 2 +-
.../sis/metadata/iso/spatial/DefaultDimension.java | 2 +-
.../iso/spatial/DefaultGeometricObjects.java | 2 +-
.../spatial/DefaultGridSpatialRepresentation.java | 2 +-
.../org/apache/sis/metadata/privy/AxisNames.java | 221 +++++
.../org/apache/sis/metadata/privy/Identifiers.java | 306 ++++++
.../sis/metadata/privy/ImplementationHelper.java | 304 ++++++
.../main/org/apache/sis/metadata/privy/Merger.java | 425 ++++++++
.../org/apache/sis/metadata/privy/NameMeaning.java | 238 +++++
.../sis/metadata/privy/NameToIdentifier.java | 321 ++++++
.../sis/metadata/privy/ReferencingServices.java | 314 ++++++
.../sis/metadata/privy/TemporalUtilities.java | 144 +++
.../sis/metadata/privy/TransformationAccuracy.java | 90 ++
.../apache/sis/metadata/privy/package-info.java | 30 +
.../sis/metadata/simple/CitationConstant.java | 237 -----
.../apache/sis/metadata/simple/SimpleCitation.java | 2 +-
.../apache/sis/metadata/simple/SimpleDuration.java | 2 +-
.../apache/sis/metadata/simple/SimpleExtent.java | 2 +-
.../apache/sis/metadata/simple/SimpleFormat.java | 2 +-
.../metadata/simple/SimpleIdentifiedObject.java | 4 +-
.../sis/metadata/simple/SimpleIdentifier.java | 2 +-
.../org/apache/sis/metadata/sql/Dispatcher.java | 4 +-
.../sis/metadata/sql/IdentifierGenerator.java | 2 +-
.../org/apache/sis/metadata/sql/Installer.java | 2 +-
.../apache/sis/metadata/sql/MetadataFallback.java | 2 +-
.../apache/sis/metadata/sql/MetadataSource.java | 14 +-
.../apache/sis/metadata/sql/MetadataWriter.java | 8 +-
.../org/apache/sis/metadata/sql/privy/Dialect.java | 149 +++
.../apache/sis/metadata/sql/privy/Initializer.java | 457 +++++++++
.../sis/metadata/sql/privy/LocalDataSource.java | 455 +++++++++
.../apache/sis/metadata/sql/privy/Reflection.java | 213 ++++
.../apache/sis/metadata/sql/privy/SQLBuilder.java | 367 +++++++
.../sis/metadata/sql/privy/SQLUtilities.java | 199 ++++
.../sis/metadata/sql/privy/ScriptRunner.java | 836 ++++++++++++++++
.../org/apache/sis/metadata/sql/privy/Syntax.java | 81 ++
.../apache/sis/metadata/sql/privy/TypeMapper.java | 105 ++
.../sis/metadata/sql/privy/package-info.java | 77 ++
.../org/apache/sis/metadata/sql/util/Dialect.java | 149 ---
.../apache/sis/metadata/sql/util/Initializer.java | 457 ---------
.../sis/metadata/sql/util/LocalDataSource.java | 455 ---------
.../apache/sis/metadata/sql/util/Reflection.java | 213 ----
.../apache/sis/metadata/sql/util/SQLBuilder.java | 367 -------
.../apache/sis/metadata/sql/util/SQLUtilities.java | 199 ----
.../apache/sis/metadata/sql/util/ScriptRunner.java | 836 ----------------
.../org/apache/sis/metadata/sql/util/Syntax.java | 81 --
.../apache/sis/metadata/sql/util/TypeMapper.java | 105 --
.../apache/sis/metadata/sql/util/package-info.java | 77 --
.../apache/sis/util/iso/DefaultNameFactory.java | 2 +-
.../org/apache/sis/util/iso/DefaultNameSpace.java | 4 +-
.../org/apache/sis/util/iso/DefaultRecord.java | 4 +-
.../apache/sis/util/iso/DefaultRecordSchema.java | 2 +-
.../org/apache/sis/util/iso/DefaultScopedName.java | 2 +-
.../org/apache/sis/util/iso/RecordDefinition.java | 2 +-
.../main/org/apache/sis/util/iso/TypeNames.java | 2 +-
.../main/org/apache/sis/util/iso/Types.java | 2 +-
.../main/org/apache/sis/xml/MarshallerPool.java | 2 +-
.../main/org/apache/sis/xml/Namespaces.java | 4 +-
.../main/org/apache/sis/xml/NilObjectHandler.java | 2 +-
.../main/org/apache/sis/xml/OutputFactory.java | 2 +-
.../main/org/apache/sis/xml/Pooled.java | 6 +-
.../main/org/apache/sis/xml/PooledMarshaller.java | 2 +-
.../org/apache/sis/xml/PooledUnmarshaller.java | 2 +-
.../main/org/apache/sis/xml/ReferenceResolver.java | 8 +-
.../main/org/apache/sis/xml/TransformVersion.java | 2 +-
.../org/apache/sis/xml/TransformingReader.java | 14 +-
.../org/apache/sis/xml/TransformingWriter.java | 4 +-
.../main/org/apache/sis/xml/ValueConverter.java | 2 +-
.../main/org/apache/sis/xml/XML.java | 2 +-
.../main/org/apache/sis/xml/XPointer.java | 2 +-
.../main/org/apache/sis/xml/bind/Context.java | 6 +-
.../apache/sis/xml/bind/IdentifierMapAdapter.java | 4 +-
.../sis/xml/bind/NonMarshalledAuthority.java | 6 +-
.../apache/sis/xml/bind/SpecializedIdentifier.java | 2 +-
.../org/apache/sis/xml/bind/cat/CodeListUID.java | 4 +-
.../sis/xml/bind/gco/CharSequenceAdapter.java | 2 +-
.../sis/xml/bind/gco/GO_CharacterString.java | 2 +-
.../org/apache/sis/xml/bind/gco/GO_DateTime.java | 2 +-
.../org/apache/sis/xml/bind/gco/GO_Temporal.java | 2 +-
.../org/apache/sis/xml/bind/gco/PropertyType.java | 2 +-
.../org/apache/sis/xml/bind/gmi/MI_Metadata.java | 2 +-
.../org/apache/sis/xml/bind/gmi/package-info.java | 2 +-
.../org/apache/sis/xml/bind/gml/DateAdapter.java | 2 +-
.../main/org/apache/sis/xml/bind/gml/Measure.java | 4 +-
.../org/apache/sis/xml/bind/gml/TM_Primitive.java | 4 +-
.../org/apache/sis/xml/bind/gml/TimeInstant.java | 4 +-
.../org/apache/sis/xml/bind/gml/TimePeriod.java | 4 +-
.../sis/xml/bind/gml/UniversalTimeAdapter.java | 4 +-
.../apache/sis/xml/bind/gts/TM_PeriodDuration.java | 4 +-
.../org/apache/sis/xml/bind/gts/package-info.java | 2 +-
.../main/org/apache/sis/xml/bind/lan/Country.java | 4 +-
.../apache/sis/xml/bind/lan/LocaleAndCharset.java | 6 +-
.../org/apache/sis/xml/bind/lan/PT_Locale.java | 4 +-
.../xml/bind/metadata/code/MD_MediumNameCode.java | 2 +-
.../sis/xml/bind/metadata/code/package-info.java | 2 +-
.../sis/xml/bind/metadata/replace/Parameter.java | 2 +-
.../bind/metadata/replace/QualityParameter.java | 2 +-
.../xml/bind/metadata/replace/RS_Identifier.java | 2 +-
.../bind/metadata/replace/ServiceParameter.java | 8 +-
.../xml/bind/metadata/replace/package-info.java | 2 +-
.../apache/sis/xml/privy/ExceptionSimplifier.java | 150 +++
.../apache/sis/xml/privy/ExternalLinkHandler.java | 317 ++++++
.../org/apache/sis/xml/privy/LegacyNamespaces.java | 121 +++
.../apache/sis/xml/privy/StreamWriterDelegate.java | 243 +++++
.../main/org/apache/sis/xml/privy/URISource.java | 125 +++
.../org/apache/sis/xml/privy/XmlUtilities.java | 374 +++++++
.../org/apache/sis/xml/privy/package-info.java | 34 +
.../apache/sis/xml/util/ExceptionSimplifier.java | 150 ---
.../apache/sis/xml/util/ExternalLinkHandler.java | 317 ------
.../org/apache/sis/xml/util/LegacyNamespaces.java | 121 ---
.../apache/sis/xml/util/StreamWriterDelegate.java | 243 -----
.../main/org/apache/sis/xml/util/URISource.java | 125 ---
.../main/org/apache/sis/xml/util/XmlUtilities.java | 374 -------
.../main/org/apache/sis/xml/util/package-info.java | 34 -
.../test/org/apache/sis/metadata/Assertions.java | 2 +-
.../apache/sis/metadata/MetadataCopierTest.java | 2 +-
.../sis/metadata/PropertyConsistencyCheck.java | 2 +-
.../sis/metadata/internal/AxisNamesTest.java | 63 --
.../sis/metadata/internal/IdentifiersTest.java | 121 ---
.../internal/ImplementationHelperTest.java | 85 --
.../apache/sis/metadata/internal/MergerTest.java | 131 ---
.../sis/metadata/internal/NameMeaningTest.java | 80 --
.../sis/metadata/iso/citation/CitationsTest.java | 4 +-
.../metadata/iso/citation/DefaultCitationTest.java | 2 +-
.../iso/citation/DefaultResponsibilityTest.java | 2 +-
.../metadata/iso/citation/HardCodedCitations.java | 4 +-
.../constraint/DefaultLegalConstraintsTest.java | 4 +-
.../sis/metadata/iso/content/DefaultBandTest.java | 2 +-
.../sis/metadata/iso/extent/ExtentsTest.java | 2 +-
.../iso/identification/DefaultResolutionTest.java | 2 +-
.../metadata/iso/lineage/DefaultLineageTest.java | 2 +-
.../iso/quality/DefaultQuantitativeResultTest.java | 2 +-
.../{internal => privy}/AxisDirectionsTest.md | 0
.../apache/sis/metadata/privy/AxisNamesTest.java | 63 ++
.../apache/sis/metadata/privy/IdentifiersTest.java | 121 +++
.../metadata/privy/ImplementationHelperTest.java | 85 ++
.../org/apache/sis/metadata/privy/MergerTest.java | 131 +++
.../apache/sis/metadata/privy/NameMeaningTest.java | 80 ++
.../sis/metadata/sql/IdentifierGeneratorTest.java | 2 +-
.../sis/metadata/sql/MetadataFallbackVerifier.java | 4 +-
.../org/apache/sis/metadata/sql/TestDatabase.java | 6 +-
.../sis/metadata/sql/privy/SQLUtilitiesTest.java | 58 ++
.../sis/metadata/sql/privy/ScriptRunnerTest.java | 89 ++
.../sis/metadata/sql/privy/TypeMapperTest.java | 48 +
.../sis/metadata/sql/util/SQLUtilitiesTest.java | 58 --
.../sis/metadata/sql/util/ScriptRunnerTest.java | 89 --
.../sis/metadata/sql/util/TypeMapperTest.java | 48 -
.../apache/sis/test/mock/FeatureAttributeMock.java | 2 +-
.../apache/sis/test/mock/IdentifiedObjectMock.java | 4 +-
.../org/apache/sis/test/mock/MetadataMock.java | 2 +-
.../org/apache/sis/test/mock/package-info.java | 2 +-
.../apache/sis/util/iso/DefaultLocalNameTest.java | 2 +-
.../apache/sis/util/iso/NameMarshallingTest.java | 2 +-
.../org/apache/sis/util/iso/TypeNamesTest.java | 2 +-
.../sis/xml/CharSequenceSubstitutionTest.java | 4 +-
.../test/org/apache/sis/xml/NamespacesTest.java | 2 +-
.../test/org/apache/sis/xml/TransformerTest.java | 2 +-
.../apache/sis/xml/TransformingNamespacesTest.java | 2 +-
.../sis/xml/bind/cat/CodeListMarshallingTest.java | 2 +-
.../apache/sis/xml/bind/gml/TimePeriodTest.java | 2 +-
.../sis/xml/bind/lan/FreeTextMarshallingTest.java | 2 +-
.../apache/sis/xml/bind/lan/LanguageCodeTest.java | 4 +-
.../sis/xml/privy/DocumentComparatorTest.java | 137 +++
.../org/apache/sis/xml/privy/XmlUtilitiesTest.java | 120 +++
.../sis/xml/test/AnnotationConsistencyCheck.java | 2 +-
.../apache/sis/xml/test/DocumentComparator.java | 4 +-
.../test/org/apache/sis/xml/test/TestCase.java | 2 +-
.../sis/xml/util/DocumentComparatorTest.java | 137 ---
.../org/apache/sis/xml/util/XmlUtilitiesTest.java | 120 ---
.../org/apache/sis/openoffice/Transformer.java | 2 +-
.../sis/openoffice/ReferencingFunctionsTest.java | 4 +-
.../org/apache/sis/map/coverage/RenderingData.java | 10 +-
.../sis/map/coverage/RenderingWorkaround.java | 2 +-
.../main/org/apache/sis/portrayal/Canvas.java | 8 +-
.../org/apache/sis/portrayal/CanvasContext.java | 2 +-
.../org/apache/sis/portrayal/CanvasExtent.java | 4 +-
.../org/apache/sis/portrayal/CanvasFollower.java | 2 +-
.../org/apache/sis/portrayal/PlanarCanvas.java | 2 +-
.../org/apache/sis/style/se1/StyleFactory.java | 2 +-
.../referencing/gazetteer/FinalLocationType.java | 2 +-
.../referencing/gazetteer/GazetteerFactory.java | 2 +-
.../gazetteer/GeohashReferenceSystem.java | 2 +-
.../sis/referencing/gazetteer/LocationFormat.java | 2 +-
.../gazetteer/MilitaryGridReferenceSystem.java | 6 +-
.../gazetteer/ModifiableLocationTypeAdapter.java | 2 +-
.../gazetteer/ReferencingByIdentifiers.java | 4 +-
.../sis/referencing/gazetteer/LocationViewer.java | 2 +-
.../gazetteer/MilitaryGridReferenceSystemTest.java | 2 +-
....apache.sis.metadata.privy.ReferencingServices} | 0
... org.apache.sis.metadata.sql.privy.Initializer} | 0
.../main/module-info.java | 15 +-
.../sis/geometry/AbstractDirectPosition.java | 6 +-
.../org/apache/sis/geometry/AbstractEnvelope.java | 8 +-
.../org/apache/sis/geometry/ArrayEnvelope.java | 2 +-
.../org/apache/sis/geometry/CoordinateFormat.java | 10 +-
.../main/org/apache/sis/geometry/Envelope2D.java | 2 +-
.../org/apache/sis/geometry/EnvelopeReducer.java | 2 +-
.../main/org/apache/sis/geometry/Envelopes.java | 10 +-
.../org/apache/sis/geometry/GeneralEnvelope.java | 4 +-
.../main/org/apache/sis/geometry/Shapes2D.java | 6 +-
.../apache/sis/geometry/WraparoundAdjustment.java | 6 +-
.../main/org/apache/sis/io/wkt/AbstractParser.java | 2 +-
.../main/org/apache/sis/io/wkt/Colors.java | 2 +-
.../main/org/apache/sis/io/wkt/Element.java | 4 +-
.../org/apache/sis/io/wkt/FormattableObject.java | 4 +-
.../main/org/apache/sis/io/wkt/Formatter.java | 12 +-
.../apache/sis/io/wkt/GeodeticObjectParser.java | 22 +-
.../org/apache/sis/io/wkt/MathTransformParser.java | 8 +-
.../main/org/apache/sis/io/wkt/StoredTree.java | 2 +-
.../main/org/apache/sis/io/wkt/Transliterator.java | 6 +-
.../main/org/apache/sis/io/wkt/VerticalInfo.java | 2 +-
.../main/org/apache/sis/io/wkt/WKTDictionary.java | 10 +-
.../main/org/apache/sis/io/wkt/WKTFormat.java | 6 +-
.../sis/parameter/AbstractParameterDescriptor.java | 4 +-
.../sis/parameter/DefaultParameterDescriptor.java | 2 +-
.../parameter/DefaultParameterDescriptorGroup.java | 4 +-
.../sis/parameter/DefaultParameterValue.java | 10 +-
.../sis/parameter/DefaultParameterValueGroup.java | 2 +-
.../apache/sis/parameter/FilteredParameters.java | 2 +-
.../sis/parameter/MapProjectionDescriptor.java | 2 +-
.../sis/parameter/MapProjectionParameters.java | 4 +-
.../org/apache/sis/parameter/MatrixParameters.java | 2 +-
.../sis/parameter/MatrixParametersAlphaNum.java | 4 +-
.../org/apache/sis/parameter/ParameterFormat.java | 6 +-
.../apache/sis/parameter/ParameterTableRow.java | 8 +-
.../org/apache/sis/parameter/TensorParameters.java | 2 +-
.../org/apache/sis/parameter/TensorValues.java | 10 +-
.../sis/parameter/UninitializedParameter.java | 2 +-
.../sis/parameter/UnmodifiableParameterValue.java | 2 +-
.../parameter/UnmodifiableParameterValueGroup.java | 2 +-
.../main/org/apache/sis/parameter/Verifier.java | 2 +-
.../sis/referencing/AbstractIdentifiedObject.java | 20 +-
.../apache/sis/referencing/AuthorityFactories.java | 2 +-
.../main/org/apache/sis/referencing/CRS.java | 12 +-
.../main/org/apache/sis/referencing/CommonCRS.java | 6 +-
.../sis/referencing/DefaultObjectDomain.java | 2 +-
.../sis/referencing/EPSGFactoryFallback.java | 6 +-
.../referencing/EllipsoidalHeightSeparator.java | 10 +-
.../sis/referencing/GeodesicsOnEllipsoid.java | 2 +-
.../apache/sis/referencing/GeodeticCalculator.java | 14 +-
.../apache/sis/referencing/IdentifiedObjects.java | 14 +-
.../sis/referencing/ImmutableIdentifier.java | 8 +-
.../org/apache/sis/referencing/NameIterator.java | 8 +-
.../apache/sis/referencing/NamedIdentifier.java | 2 +-
.../org/apache/sis/referencing/Properties.java | 4 +-
.../sis/referencing/StandardDefinitions.java | 8 +-
.../apache/sis/referencing/crs/AbstractCRS.java | 8 +-
.../sis/referencing/crs/AbstractDerivedCRS.java | 6 +-
.../sis/referencing/crs/DefaultCompoundCRS.java | 8 +-
.../sis/referencing/crs/DefaultDerivedCRS.java | 6 +-
.../sis/referencing/crs/DefaultEngineeringCRS.java | 4 +-
.../sis/referencing/crs/DefaultGeodeticCRS.java | 10 +-
.../sis/referencing/crs/DefaultGeographicCRS.java | 10 +-
.../sis/referencing/crs/DefaultImageCRS.java | 4 +-
.../sis/referencing/crs/DefaultParametricCRS.java | 4 +-
.../sis/referencing/crs/DefaultProjectedCRS.java | 8 +-
.../sis/referencing/crs/DefaultTemporalCRS.java | 8 +-
.../sis/referencing/crs/DefaultVerticalCRS.java | 4 +-
.../sis/referencing/crs/ExplicitParameters.java | 6 +-
.../org/apache/sis/referencing/cs/AbstractCS.java | 12 +-
.../apache/sis/referencing/cs/AxesConvention.java | 2 +-
.../main/org/apache/sis/referencing/cs/Codes.java | 4 +-
.../sis/referencing/cs/CoordinateSystems.java | 6 +-
.../apache/sis/referencing/cs/DefaultAffineCS.java | 2 +-
.../sis/referencing/cs/DefaultCompoundCS.java | 4 +-
.../cs/DefaultCoordinateSystemAxis.java | 12 +-
.../sis/referencing/cs/DefaultCylindricalCS.java | 2 +-
.../sis/referencing/cs/DefaultEllipsoidalCS.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 | 2 +-
.../sis/referencing/cs/DefaultVerticalCS.java | 2 +-
.../sis/referencing/cs/DirectionAlongMeridian.java | 6 +-
.../org/apache/sis/referencing/cs/Normalizer.java | 6 +-
.../org/apache/sis/referencing/cs/SubTypes.java | 2 +-
.../sis/referencing/datum/AbstractDatum.java | 8 +-
.../sis/referencing/datum/BursaWolfParameters.java | 4 +-
.../sis/referencing/datum/DatumShiftGrid.java | 4 +-
.../sis/referencing/datum/DefaultEllipsoid.java | 12 +-
.../referencing/datum/DefaultEngineeringDatum.java | 2 +-
.../referencing/datum/DefaultGeodeticDatum.java | 14 +-
.../sis/referencing/datum/DefaultImageDatum.java | 4 +-
.../referencing/datum/DefaultParametricDatum.java | 2 +-
.../referencing/datum/DefaultPrimeMeridian.java | 14 +-
.../referencing/datum/DefaultTemporalDatum.java | 4 +-
.../referencing/datum/DefaultVerticalDatum.java | 6 +-
.../sis/referencing/datum/TimeDependentBWP.java | 4 +-
.../apache/sis/referencing/datum/package-info.java | 2 +-
.../factory/AuthorityFactoryIdentifier.java | 10 +-
.../referencing/factory/AuthorityFactoryProxy.java | 2 +-
.../referencing/factory/CommonAuthorityCode.java | 4 +-
.../factory/CommonAuthorityFactory.java | 4 +-
.../factory/ConcurrentAuthorityFactory.java | 4 +-
.../sis/referencing/factory/FilteredCodes.java | 2 +-
.../factory/GeodeticAuthorityFactory.java | 4 +-
.../referencing/factory/GeodeticObjectFactory.java | 2 +-
.../factory/IdentifiedObjectFinder.java | 2 +-
.../factory/LazySynchronizedIterator.java | 2 +-
.../factory/MultiAuthoritiesFactory.java | 14 +-
.../sis/referencing/factory/ReferenceKeeper.java | 2 +-
.../referencing/factory/sql/AuthorityCodes.java | 2 +-
.../sis/referencing/factory/sql/BursaWolfInfo.java | 2 +-
.../referencing/factory/sql/EPSGCodeFinder.java | 10 +-
.../referencing/factory/sql/EPSGDataAccess.java | 26 +-
.../sis/referencing/factory/sql/EPSGFactory.java | 6 +-
.../sis/referencing/factory/sql/EPSGInstaller.java | 8 +-
.../factory/sql/InstallationScriptProvider.java | 4 +-
.../sis/referencing/factory/sql/SQLTranslator.java | 8 +-
.../sis/referencing/factory/sql/TableInfo.java | 2 +-
.../sis/referencing/internal/AnnotatedMatrix.java | 4 +-
.../sis/referencing/internal/Arithmetic.java | 6 +-
.../apache/sis/referencing/internal/Bezier.java | 532 ++++++++++
.../sis/referencing/internal/DatabaseListener.java | 4 +-
.../referencing/internal/EPSGParameterDomain.java | 2 +-
.../internal/ImmutableAffineTransform.java | 314 ++++++
.../apache/sis/referencing/internal/Legacy.java | 2 +-
.../referencing/internal/LinearTransform2D.java | 39 +
.../sis/referencing/internal/MergedProperties.java | 2 +-
.../referencing/internal/ParameterizedAffine.java | 172 ++++
.../referencing/internal/ServicesForMetadata.java | 12 +-
.../operation/AbstractCoordinateOperation.java | 20 +-
.../operation/AbstractSingleOperation.java | 8 +-
.../apache/sis/referencing/operation/CRSPair.java | 4 +-
.../operation/CoordinateOperationContext.java | 4 +-
.../operation/CoordinateOperationFinder.java | 12 +-
.../operation/CoordinateOperationRegistry.java | 12 +-
.../operation/DefaultConcatenatedOperation.java | 8 +-
.../referencing/operation/DefaultConversion.java | 2 +-
.../DefaultCoordinateOperationFactory.java | 10 +-
.../sis/referencing/operation/DefaultFormula.java | 2 +-
.../operation/DefaultOperationMethod.java | 10 +-
.../operation/DefaultPassThroughOperation.java | 4 +-
.../operation/InverseOperationMethod.java | 2 +-
.../operation/MathTransformContext.java | 2 +-
.../operation/builder/LinearTransformBuilder.java | 10 +-
.../operation/builder/LocalizationGridBuilder.java | 4 +-
.../operation/builder/ResidualGrid.java | 6 +-
.../operation/gridded/CompressedGrid.java | 2 +-
.../referencing/operation/gridded/GridGroup.java | 8 +-
.../referencing/operation/gridded/LoadedGrid.java | 2 +-
.../sis/referencing/operation/gridded/Tile.java | 679 +++++++++++++
.../operation/gridded/TileOrganizer.java | 567 +++++++++++
.../operation/matrix/AffineTransforms2D.java | 4 +-
.../operation/matrix/GeneralMatrix.java | 4 +-
.../sis/referencing/operation/matrix/Matrices.java | 8 +-
.../sis/referencing/operation/matrix/Matrix1.java | 2 +-
.../sis/referencing/operation/matrix/Matrix2.java | 2 +-
.../referencing/operation/matrix/MatrixSIS.java | 4 +-
.../sis/referencing/operation/matrix/Solver.java | 2 +-
.../operation/matrix/UnmodifiableMatrix.java | 2 +-
.../operation/projection/AlbersEqualArea.java | 4 +-
.../operation/projection/AuthalicConversion.java | 2 +-
.../operation/projection/ConformalProjection.java | 2 +-
.../operation/projection/CylindricalEqualArea.java | 2 +-
.../operation/projection/Initializer.java | 6 +-
.../projection/LambertAzimuthalEqualArea.java | 4 +-
.../projection/LambertConicConformal.java | 6 +-
.../operation/projection/LongitudeWraparound.java | 4 +-
.../referencing/operation/projection/Mercator.java | 2 +-
.../operation/projection/MeridianArcBased.java | 2 +-
.../operation/projection/NormalizedProjection.java | 8 +-
.../operation/projection/ObliqueMercator.java | 2 +-
.../operation/projection/ObliqueStereographic.java | 4 +-
.../operation/projection/PolarStereographic.java | 6 +-
.../operation/projection/SatelliteTracking.java | 2 +-
.../operation/projection/TransverseMercator.java | 2 +-
.../operation/projection/ZonedGridSystem.java | 2 +-
.../operation/provider/AbstractProvider.java | 2 +-
.../sis/referencing/operation/provider/Affine.java | 4 +-
.../referencing/operation/provider/EPSGName.java | 2 +-
.../operation/provider/Equirectangular.java | 6 +-
.../provider/FranceGeocentricInterpolation.java | 4 +-
.../operation/provider/GeocentricAffine.java | 6 +-
.../GeocentricAffineBetweenGeographic.java | 2 +-
.../provider/GeocentricToTopocentric.java | 2 +-
.../operation/provider/Geographic2Dto3D.java | 2 +-
.../operation/provider/Geographic3Dto2D.java | 4 +-
.../operation/provider/GeographicOffsets2D.java | 2 +-
.../operation/provider/GeographicRedimension.java | 2 +-
.../operation/provider/GeographicToGeocentric.java | 2 +-
.../operation/provider/Interpolation1D.java | 2 +-
.../operation/provider/LambertConformal2SP.java | 2 +-
.../operation/provider/LongitudeRotation.java | 2 +-
.../operation/provider/MapProjection.java | 2 +-
.../operation/provider/Mercator1SP.java | 2 +-
.../operation/provider/MillerCylindrical.java | 2 +-
.../referencing/operation/provider/Molodensky.java | 6 +-
.../sis/referencing/operation/provider/NTv2.java | 4 +-
.../operation/provider/PolarStereographicA.java | 6 +-
.../operation/provider/SatelliteTracking.java | 2 +-
.../referencing/operation/provider/Sinusoidal.java | 2 +-
.../operation/provider/TransverseMercator.java | 6 +-
.../transform/AbstractLinearTransform.java | 4 +-
.../operation/transform/AbstractMathTransform.java | 6 +-
.../transform/AbstractMathTransform2D.java | 2 +-
.../operation/transform/CartesianToPolar.java | 2 +-
.../operation/transform/ConcatenatedTransform.java | 6 +-
.../operation/transform/ContextualParameters.java | 14 +-
.../transform/CoordinateSystemTransform.java | 8 +-
.../transform/DefaultMathTransformFactory.java | 14 +-
.../transform/EllipsoidToCentricTransform.java | 12 +-
.../transform/ExponentialTransform1D.java | 2 +-
.../operation/transform/IdentityTransform.java | 2 +-
.../transform/InterpolatedGeocentricTransform.java | 2 +-
.../operation/transform/InterpolatedTransform.java | 4 +-
.../operation/transform/LinearInterpolator1D.java | 2 +-
.../operation/transform/LinearTransform1D.java | 6 +-
.../transform/LogarithmicTransform1D.java | 2 +-
.../operation/transform/MathTransforms.java | 6 +-
.../operation/transform/MolodenskyTransform.java | 2 +-
.../operation/transform/OperationMethodSet.java | 2 +-
.../operation/transform/PassThroughTransform.java | 6 +-
.../operation/transform/PolarToCartesian.java | 2 +-
.../operation/transform/PoleRotation.java | 6 +-
.../operation/transform/PowerTransform1D.java | 2 +-
.../operation/transform/ProjectiveTransform.java | 8 +-
.../operation/transform/ProjectiveTransform2D.java | 4 +-
.../operation/transform/ScaleTransform.java | 4 +-
.../transform/SpecializableTransform.java | 2 +-
.../operation/transform/TransformSeparator.java | 4 +-
.../operation/transform/TranslationTransform.java | 4 +-
.../operation/transform/WraparoundTransform.java | 2 +-
.../org/apache/sis/referencing/package-info.java | 2 +-
.../sis/referencing/privy/AbstractShape.java | 79 ++
.../apache/sis/referencing/privy/AffineMatrix.java | 242 +++++
.../sis/referencing/privy/AffineTransform2D.java | 491 ++++++++++
.../apache/sis/referencing/privy/AxesMapper.java | 156 +++
.../sis/referencing/privy/AxisDirections.java | 831 ++++++++++++++++
.../referencing/privy/CoordinateOperations.java | 361 +++++++
.../sis/referencing/privy/DefinitionVerifier.java | 359 +++++++
.../sis/referencing/privy/DirectPositionView.java | 153 +++
.../privy/EllipsoidalHeightCombiner.java | 227 +++++
.../referencing/privy/ExtendedPrecisionMatrix.java | 150 +++
.../sis/referencing/privy/ExtentSelector.java | 558 +++++++++++
.../org/apache/sis/referencing/privy/Formulas.java | 292 ++++++
.../referencing/privy/GeodeticObjectBuilder.java | 674 +++++++++++++
.../sis/referencing/privy/IntervalRectangle.java | 573 +++++++++++
.../org/apache/sis/referencing/privy/LazySet.java | 247 +++++
.../referencing/privy/NilReferencingObject.java | 99 ++
.../privy/PositionalAccuracyConstant.java | 246 +++++
.../privy/ReferencingFactoryContainer.java | 403 ++++++++
.../referencing/privy/ReferencingUtilities.java | 687 +++++++++++++
.../sis/referencing/privy/ShapeUtilities.java | 426 ++++++++
.../sis/referencing/privy/TemporalAccessor.java | 138 +++
.../apache/sis/referencing/privy/WKTKeywords.java | 299 ++++++
.../apache/sis/referencing/privy/WKTUtilities.java | 548 +++++++++++
.../referencing/privy/WraparoundApplicator.java | 222 +++++
.../referencing/privy/WraparoundAxesFinder.java | 83 ++
.../apache/sis/referencing/privy/package-info.java | 30 +
.../apache/sis/referencing/util/AxesMapper.java | 156 ---
.../sis/referencing/util/AxisDirections.java | 831 ----------------
.../sis/referencing/util/CoordinateOperations.java | 361 -------
.../sis/referencing/util/DefinitionVerifier.java | 359 -------
.../sis/referencing/util/DirectPositionView.java | 153 ---
.../util/EllipsoidalHeightCombiner.java | 227 -----
.../referencing/util/ExtendedPrecisionMatrix.java | 150 ---
.../sis/referencing/util/ExtentSelector.java | 558 -----------
.../org/apache/sis/referencing/util/Formulas.java | 292 ------
.../referencing/util/GeodeticObjectBuilder.java | 674 -------------
.../org/apache/sis/referencing/util/LazySet.java | 247 -----
.../sis/referencing/util/NilReferencingObject.java | 99 --
.../util/PositionalAccuracyConstant.java | 246 -----
.../util/ReferencingFactoryContainer.java | 403 --------
.../sis/referencing/util/ReferencingUtilities.java | 687 -------------
.../sis/referencing/util/TemporalAccessor.java | 138 ---
.../apache/sis/referencing/util/WKTKeywords.java | 299 ------
.../apache/sis/referencing/util/WKTUtilities.java | 548 -----------
.../sis/referencing/util/WraparoundApplicator.java | 222 -----
.../sis/referencing/util/WraparoundAxesFinder.java | 83 --
.../sis/referencing/util/j2d/AbstractShape.java | 79 --
.../sis/referencing/util/j2d/AffineMatrix.java | 243 -----
.../referencing/util/j2d/AffineTransform2D.java | 490 ----------
.../apache/sis/referencing/util/j2d/Bezier.java | 532 ----------
.../util/j2d/ImmutableAffineTransform.java | 314 ------
.../referencing/util/j2d/IntervalRectangle.java | 573 -----------
.../referencing/util/j2d/LinearTransform2D.java | 39 -
.../referencing/util/j2d/ParameterizedAffine.java | 171 ----
.../sis/referencing/util/j2d/ShapeUtilities.java | 426 --------
.../org/apache/sis/referencing/util/j2d/Tile.java | 678 -------------
.../sis/referencing/util/j2d/TileOrganizer.java | 565 -----------
.../sis/referencing/util/j2d/package-info.java | 30 -
.../apache/sis/referencing/util/package-info.java | 30 -
.../bind/referencing/CC_CoordinateOperation.java | 2 +-
.../referencing/CC_GeneralOperationParameter.java | 2 +-
.../sis/xml/bind/referencing/CS_AxisDirection.java | 2 +-
.../sis/xml/bind/referencing/CS_RangeMeaning.java | 2 +-
.../org/apache/sis/xml/bind/referencing/Code.java | 8 +-
.../org/apache/sis/geometry/EnvelopesTest.java | 2 +-
.../apache/sis/geometry/GeneralEnvelopeTest.java | 2 +-
.../org/apache/sis/geometry/TransformTestCase.java | 4 +-
.../sis/geometry/WraparoundAdjustmentTest.java | 2 +-
.../sis/io/wkt/GeodeticObjectParserTest.java | 6 +-
.../org/apache/sis/io/wkt/TransliteratorTest.java | 2 +-
.../DefaultParameterDescriptorGroupTest.java | 2 +-
.../parameter/DefaultParameterDescriptorTest.java | 2 +-
.../sis/parameter/DefaultParameterValueTest.java | 2 +-
.../sis/parameter/MapProjectionParametersTest.java | 18 +-
.../apache/sis/parameter/TensorParametersTest.java | 4 +-
.../org/apache/sis/parameter/TensorValuesTest.java | 6 +-
.../org/apache/sis/referencing/Assertions.java | 2 +-
.../sis/referencing/AuthorityFactoriesTest.java | 2 +-
.../org/apache/sis/referencing/CommonCRSTest.java | 4 +-
.../sis/referencing/GeodesicsOnEllipsoidTest.java | 6 +-
.../sis/referencing/GeodeticCalculatorTest.java | 6 +-
.../sis/referencing/GeodeticObjectVerifier.java | 2 +-
.../sis/referencing/ImmutableIdentifierTest.java | 4 +-
.../sis/referencing/StandardDefinitionsTest.java | 2 +-
.../sis/referencing/crs/DefaultDerivedCRSTest.java | 2 +-
.../referencing/crs/DefaultProjectedCRSTest.java | 4 +-
.../referencing/crs/DefaultTemporalCRSTest.java | 4 +-
.../referencing/cs/DefaultCylindricalCSTest.java | 2 +-
.../sis/referencing/cs/DefaultPolarCSTest.java | 2 +-
.../sis/referencing/cs/DefaultSphericalCSTest.java | 2 +-
.../apache/sis/referencing/cs/HardCodedAxes.java | 4 +-
.../datum/DefaultGeodeticDatumTest.java | 2 +-
.../datum/DefaultPrimeMeridianTest.java | 2 +-
.../datum/DefaultTemporalDatumTest.java | 2 +-
.../datum/DefaultVerticalDatumTest.java | 2 +-
.../sis/referencing/datum/GeodeticDatumMock.java | 2 +-
.../sis/referencing/datum/HardCodedDatum.java | 2 +-
.../referencing/datum/TimeDependentBWPTest.java | 2 +-
.../factory/CommonAuthorityFactoryTest.java | 2 +-
.../factory/ConcurrentAuthorityFactoryTest.java | 2 +-
.../sis/referencing/factory/TestFactorySource.java | 4 +-
.../referencing/factory/sql/EPSGInstallerTest.java | 4 +-
.../factory/sql/epsg/DataScriptFormatter.java | 4 +-
.../operation/CoordinateOperationFinderTest.java | 8 +-
.../operation/CoordinateOperationRegistryTest.java | 2 +-
.../operation/DefaultConversionTest.java | 4 +-
.../DefaultCoordinateOperationFactoryTest.java | 6 +-
.../operation/DefaultTransformationTest.java | 2 +-
.../operation/matrix/GeneralMatrixTest.java | 2 +-
.../referencing/operation/matrix/MatricesTest.java | 2 +-
.../operation/matrix/MatrixTestCase.java | 4 +-
.../operation/projection/AlbersEqualAreaTest.java | 2 +-
.../projection/AuthalicConversionTest.java | 2 +-
.../projection/AzimuthalEquidistantTest.java | 2 +-
.../operation/projection/CassiniSoldnerTest.java | 2 +-
.../projection/ConformalProjectionTest.java | 2 +-
.../projection/CylindricalEqualAreaTest.java | 2 +-
.../operation/projection/EquirectangularTest.java | 4 +-
.../operation/projection/InitializerTest.java | 2 +-
.../projection/LambertAzimuthalEqualAreaTest.java | 6 +-
.../projection/LambertConicConformalTest.java | 2 +-
.../projection/MapProjectionTestCase.java | 4 +-
.../projection/MercatorMethodComparison.java | 4 +-
.../operation/projection/MercatorTest.java | 4 +-
.../operation/projection/MollweideTest.java | 2 +-
.../sis/referencing/operation/projection/NoOp.java | 2 +-
.../projection/NormalizedProjectionTest.java | 2 +-
.../projection/ObliqueStereographicTest.java | 2 +-
.../projection/PolarStereographicTest.java | 2 +-
.../operation/projection/PolyconicTest.java | 2 +-
.../operation/projection/ProjectionBenchmark.java | 4 +-
.../projection/ProjectionResultComparator.java | 2 +-
.../projection/SatelliteTrackingTest.java | 2 +-
.../operation/projection/SinusoidalTest.java | 2 +-
.../projection/TransverseMercatorTest.java | 2 +-
.../operation/projection/ZonedGridSystemTest.java | 4 +-
.../provider/CoordinateFrameRotationTest.java | 2 +-
.../provider/GeocentricTranslationTest.java | 2 +-
.../operation/provider/GeographicOffsetsTest.java | 2 +-
.../operation/provider/MapProjectionTest.java | 2 +-
.../referencing/operation/provider/NTv2Test.java | 2 +-
.../provider/PositionVector7ParamTest.java | 2 +-
.../operation/provider/TransverseMercatorTest.java | 2 +-
.../transform/AbridgedMolodenskyTransformTest.java | 2 +-
.../transform/ConcatenatedTransformTest.java | 2 +-
.../operation/transform/CoordinateDomain.java | 2 +-
.../transform/DefaultMathTransformFactoryTest.java | 2 +-
.../operation/transform/DomainDefinitionTest.java | 2 +-
.../transform/EllipsoidToCentricTransformTest.java | 2 +-
.../transform/InterpolatedTransformTest.java | 2 +-
.../operation/transform/LinearTransformTest.java | 2 +-
.../transform/MolodenskyTransformTest.java | 2 +-
.../transform/OperationMethodSetTest.java | 2 +-
.../operation/transform/PoleRotationTest.java | 2 +-
.../transform/ProjectiveTransformTest.java | 6 +-
.../operation/transform/ScaleTransformTest.java | 2 +-
.../transform/SpecializableTransformTest.java | 2 +-
.../transform/TranslationTransformTest.java | 4 +-
.../transform/WraparoundTransformTest.java | 2 +-
.../sis/referencing/privy/AbstractShapeTest.java | 50 +
.../sis/referencing/privy/AxesMapperTest.java | 101 ++
.../sis/referencing/privy/AxisDirectionsTest.java | 545 +++++++++++
.../privy/CoordinateOperationsTest.java | 80 ++
.../referencing/privy/DefinitionVerifierTest.java | 103 ++
.../privy/EllipsoidalHeightCombinerTest.java | 171 ++++
.../sis/referencing/privy/ExtentSelectorTest.java | 210 ++++
.../apache/sis/referencing/privy/FormulasTest.java | 142 +++
.../privy/GeodeticObjectBuilderTest.java | 66 ++
.../apache/sis/referencing/privy/LazySetTest.java | 118 +++
.../privy/PositionalAccuracyConstantTest.java | 68 ++
.../privy/ReferencingUtilitiesTest.java | 128 +++
.../sis/referencing/privy/ShapeUtilitiesExt.java | 147 +++
.../sis/referencing/privy/ShapeUtilitiesTest.java | 248 +++++
.../referencing/privy/ShapeUtilitiesViewer.java | 266 +++++
.../sis/referencing/privy/WKTKeywordsTest.java | 99 ++
.../sis/referencing/privy/WKTUtilitiesTest.java | 102 ++
.../privy/WraparoundApplicatorTest.java | 47 +
.../report/CoordinateOperationMethods.java | 4 +-
.../sis/referencing/report/HTMLGenerator.java | 2 +-
.../sis/referencing/util/AxesMapperTest.java | 101 --
.../sis/referencing/util/AxisDirectionsTest.java | 545 -----------
.../referencing/util/CoordinateOperationsTest.java | 80 --
.../referencing/util/DefinitionVerifierTest.java | 103 --
.../util/EllipsoidalHeightCombinerTest.java | 171 ----
.../sis/referencing/util/ExtentSelectorTest.java | 210 ----
.../apache/sis/referencing/util/FormulasTest.java | 142 ---
.../util/GeodeticObjectBuilderTest.java | 66 --
.../apache/sis/referencing/util/LazySetTest.java | 118 ---
.../util/PositionalAccuracyConstantTest.java | 68 --
.../referencing/util/ReferencingUtilitiesTest.java | 128 ---
.../sis/referencing/util/WKTKeywordsTest.java | 99 --
.../sis/referencing/util/WKTUtilitiesTest.java | 102 --
.../referencing/util/WraparoundApplicatorTest.java | 47 -
.../referencing/util/j2d/AbstractShapeTest.java | 50 -
.../referencing/util/j2d/ShapeUtilitiesExt.java | 144 ---
.../referencing/util/j2d/ShapeUtilitiesTest.java | 248 -----
.../referencing/util/j2d/ShapeUtilitiesViewer.java | 266 -----
.../sis/test/integration/ConsistencyTest.java | 2 +-
.../apache/sis/test/integration/MetadataTest.java | 4 +-
.../org/apache/sis/test/widget/package-info.java | 2 +-
.../CC_OperationParameterGroupTest.java | 2 +-
.../apache/sis/xml/bind/referencing/CodeTest.java | 2 +-
.../main/org/apache/sis/storage/landsat/Band.java | 2 +-
.../org/apache/sis/storage/landsat/BandGroup.java | 2 +-
.../apache/sis/storage/landsat/LandsatStore.java | 2 +-
.../apache/sis/storage/landsat/MetadataReader.java | 12 +-
.../sis/storage/geotiff/CompressedSubset.java | 2 +-
.../apache/sis/storage/geotiff/Compression.java | 2 +-
.../org/apache/sis/storage/geotiff/DataSubset.java | 8 +-
.../apache/sis/storage/geotiff/GeoTiffStore.java | 4 +-
.../sis/storage/geotiff/GeoTiffStoreProvider.java | 2 +-
.../sis/storage/geotiff/ImageFileDirectory.java | 10 +-
.../sis/storage/geotiff/MultiResolutionImage.java | 2 +-
.../org/apache/sis/storage/geotiff/Writer.java | 4 +-
.../sis/storage/geotiff/reader/CRSBuilder.java | 14 +-
.../geotiff/reader/GridGeometryBuilder.java | 2 +-
.../apache/sis/storage/geotiff/reader/Type.java | 2 +-
.../sis/storage/geotiff/reader/XMLMetadata.java | 4 +-
.../sis/storage/geotiff/spi/SchemaModifier.java | 2 +-
.../sis/storage/geotiff/writer/GeoEncoder.java | 8 +-
.../storage/geotiff/writer/ReformattedImage.java | 2 +-
.../org/apache/sis/storage/geotiff/WriterTest.java | 2 +-
.../storage/geotiff/reader/XMLMetadataTest.java | 2 +-
.../apache/sis/storage/netcdf/MetadataReader.java | 6 +-
.../org/apache/sis/storage/netcdf/NetcdfStore.java | 4 +-
.../org/apache/sis/storage/netcdf/base/Axis.java | 2 +-
.../apache/sis/storage/netcdf/base/AxisType.java | 2 +-
.../apache/sis/storage/netcdf/base/CRSBuilder.java | 4 +-
.../apache/sis/storage/netcdf/base/CRSMerger.java | 4 +-
.../apache/sis/storage/netcdf/base/Convention.java | 2 +-
.../apache/sis/storage/netcdf/base/Decoder.java | 4 +-
.../apache/sis/storage/netcdf/base/FeatureSet.java | 4 +-
.../org/apache/sis/storage/netcdf/base/Grid.java | 2 +-
.../sis/storage/netcdf/base/GridCacheKey.java | 2 +-
.../sis/storage/netcdf/base/GridMapping.java | 6 +-
.../org/apache/sis/storage/netcdf/base/HYCOM.java | 2 +-
.../apache/sis/storage/netcdf/base/Linearizer.java | 6 +-
.../sis/storage/netcdf/base/NamedElement.java | 2 +-
.../org/apache/sis/storage/netcdf/base/Node.java | 2 +-
.../sis/storage/netcdf/base/RasterResource.java | 8 +-
.../apache/sis/storage/netcdf/base/Variable.java | 6 +-
.../sis/storage/netcdf/classic/ChannelDecoder.java | 6 +-
.../sis/storage/netcdf/classic/GridInfo.java | 2 +-
.../sis/storage/netcdf/classic/VariableInfo.java | 4 +-
.../sis/storage/netcdf/ucar/DimensionWrapper.java | 2 +-
.../sis/storage/netcdf/ucar/GridWrapper.java | 2 +-
.../sis/storage/netcdf/ucar/VariableWrapper.java | 2 +-
.../main/org/apache/sis/storage/sql/SQLStore.java | 2 +-
.../org/apache/sis/storage/sql/feature/Column.java | 6 +-
.../apache/sis/storage/sql/feature/Database.java | 10 +-
.../sis/storage/sql/feature/FeatureAdapter.java | 2 +-
.../sis/storage/sql/feature/FeatureAnalyzer.java | 2 +-
.../sis/storage/sql/feature/FeatureIterator.java | 2 +-
.../sis/storage/sql/feature/FeatureStream.java | 6 +-
.../sis/storage/sql/feature/InfoStatements.java | 8 +-
.../apache/sis/storage/sql/feature/PrimaryKey.java | 4 +-
.../sis/storage/sql/feature/QueryAnalyzer.java | 4 +-
.../apache/sis/storage/sql/feature/Relation.java | 4 +-
.../sis/storage/sql/feature/SelectionClause.java | 2 +-
.../storage/sql/feature/SelectionClauseWriter.java | 4 +-
.../org/apache/sis/storage/sql/feature/Table.java | 4 +-
.../sis/storage/sql/feature/TableAnalyzer.java | 6 +-
.../sis/storage/sql/feature/TableReference.java | 2 +-
.../sis/storage/sql/feature/ValueGetter.java | 4 +-
.../sis/storage/sql/postgis/ExtentEstimator.java | 2 +-
.../apache/sis/storage/sql/postgis/Postgres.java | 2 +-
.../sis/storage/sql/postgis/RasterReader.java | 8 +-
.../org/apache/sis/storage/sql/SQLStoreTest.java | 2 +-
.../sql/feature/TemporalValueGetterTest.java | 2 +-
.../apache/sis/storage/sql/postgis/TestRaster.java | 2 +-
.../main/org/apache/sis/storage/gpx/Metadata.java | 2 +-
.../main/org/apache/sis/storage/gpx/Reader.java | 2 +-
.../main/org/apache/sis/storage/gpx/Types.java | 2 +-
.../main/org/apache/sis/storage/gpx/Writer.java | 2 +-
.../sis/storage/xml/stream/FormattedWriter.java | 2 +-
.../sis/storage/xml/stream/NamespaceEraser.java | 2 +-
.../sis/storage/xml/stream/StaxDataStore.java | 6 +-
.../sis/storage/xml/stream/StaxStreamReader.java | 4 +-
.../test/org/apache/sis/storage/gpx/TypesTest.java | 2 +-
.../apache/sis/io/stream/ChannelDataOutput.java | 2 +-
.../apache/sis/io/stream/FileCacheByteChannel.java | 2 +-
.../main/org/apache/sis/io/stream/IOUtilities.java | 2 +-
.../sis/storage/AbstractGridCoverageResource.java | 2 +-
.../org/apache/sis/storage/CoverageSubset.java | 6 +-
.../main/org/apache/sis/storage/DataStore.java | 2 +-
.../org/apache/sis/storage/DataStoreRegistry.java | 4 +-
.../main/org/apache/sis/storage/FeatureNaming.java | 2 +-
.../main/org/apache/sis/storage/FeatureQuery.java | 8 +-
.../main/org/apache/sis/storage/FeatureSubset.java | 2 +-
.../main/org/apache/sis/storage/ProbeResult.java | 2 +-
.../org/apache/sis/storage/StorageConnector.java | 2 +-
.../aggregate/BandAggregateGridResource.java | 6 +-
.../storage/aggregate/ConcatenatedFeatureSet.java | 4 +-
.../aggregate/ConcatenatedGridCoverage.java | 4 +-
.../aggregate/ConcatenatedGridResource.java | 6 +-
.../sis/storage/aggregate/CoverageAggregator.java | 2 +-
.../sis/storage/aggregate/DimensionSelector.java | 2 +-
.../apache/sis/storage/aggregate/GridSlice.java | 4 +-
.../org/apache/sis/storage/aggregate/Group.java | 2 +-
.../sis/storage/aggregate/GroupAggregate.java | 4 +-
.../sis/storage/aggregate/JoinFeatureSet.java | 2 +-
.../sis/storage/aggregate/MergeStrategy.java | 4 +-
.../main/org/apache/sis/storage/base/CodeType.java | 4 +-
.../org/apache/sis/storage/base/LegalSymbols.java | 2 +-
.../apache/sis/storage/base/MetadataBuilder.java | 6 +-
.../org/apache/sis/storage/base/PRJDataStore.java | 2 +-
.../apache/sis/storage/base/ResourceLineage.java | 4 +-
.../apache/sis/storage/base/SimpleAggregate.java | 2 +-
.../apache/sis/storage/base/StoreUtilities.java | 2 +-
.../sis/storage/base/TiledDeferredImage.java | 2 +-
.../apache/sis/storage/base/TiledGridCoverage.java | 4 +-
.../apache/sis/storage/base/TiledGridResource.java | 2 +-
.../org/apache/sis/storage/base/URIDataStore.java | 4 +-
.../sis/storage/csv/MovingFeatureBuilder.java | 4 +-
.../main/org/apache/sis/storage/csv/Store.java | 8 +-
.../org/apache/sis/storage/csv/TimeEncoding.java | 2 +-
.../apache/sis/storage/esri/AsciiGridStore.java | 2 +-
.../org/apache/sis/storage/esri/RasterStore.java | 12 +-
.../apache/sis/storage/esri/RawRasterReader.java | 8 +-
.../apache/sis/storage/esri/RawRasterStore.java | 6 +-
.../apache/sis/storage/event/StoreListeners.java | 2 +-
.../main/org/apache/sis/storage/folder/Store.java | 2 +-
.../sis/storage/image/WorldFileResource.java | 6 +-
.../apache/sis/storage/image/WorldFileStore.java | 4 +-
.../org/apache/sis/storage/wkt/StoreFormat.java | 2 +-
.../org/apache/sis/storage/wkt/StoreProvider.java | 2 +-
.../main/org/apache/sis/storage/xml/Store.java | 4 +-
.../org/apache/sis/storage/xml/StoreProvider.java | 2 +-
.../org/apache/sis/storage/CoverageQueryTest.java | 2 +-
.../org/apache/sis/storage/FeatureQueryTest.java | 2 +-
.../sis/storage/aggregate/JoinFeatureSetTest.java | 2 +-
.../sis/storage/base/MemoryGridResourceTest.java | 2 +-
.../sis/storage/test/CoverageReadConsistency.java | 4 +-
.../apache/sis/storage/test/SubsampledImage.java | 2 +-
.../sis/storage/xml/MimeTypeDetectorTest.java | 2 +-
.../test/org/apache/sis/storage/xml/StoreTest.java | 2 +-
.../src/org.apache.sis.util/main/module-info.java | 4 +-
.../org/apache/sis/converter/StringConverter.java | 4 +-
.../main/org/apache/sis/io/CompoundFormat.java | 6 +-
.../main/org/apache/sis/io/DefaultFormat.java | 2 +-
.../org/apache/sis/io/IdentifiedObjectFormat.java | 6 +-
.../main/org/apache/sis/io/LineAppender.java | 2 +-
.../main/org/apache/sis/io/TableAppender.java | 2 +-
.../main/org/apache/sis/math/ArrayVector.java | 2 +-
.../main/org/apache/sis/math/DecimalFunctions.java | 2 +-
.../main/org/apache/sis/math/Fraction.java | 2 +-
.../main/org/apache/sis/math/Line.java | 6 +-
.../org/apache/sis/math/LinearlyDerivedVector.java | 2 +-
.../main/org/apache/sis/math/MathFunctions.java | 6 +-
.../main/org/apache/sis/math/Plane.java | 6 +-
.../main/org/apache/sis/math/Statistics.java | 4 +-
.../main/org/apache/sis/math/StatisticsFormat.java | 2 +-
.../main/org/apache/sis/math/Vector.java | 2 +-
.../main/org/apache/sis/measure/AbstractUnit.java | 2 +-
.../main/org/apache/sis/measure/Angle.java | 2 +-
.../main/org/apache/sis/measure/AngleFormat.java | 4 +-
.../org/apache/sis/measure/LinearConverter.java | 4 +-
.../main/org/apache/sis/measure/NumberRange.java | 2 +-
.../main/org/apache/sis/measure/Prefixes.java | 2 +-
.../main/org/apache/sis/measure/Range.java | 2 +-
.../main/org/apache/sis/measure/RangeFormat.java | 6 +-
.../main/org/apache/sis/measure/Scalar.java | 2 +-
.../apache/sis/measure/SexagesimalConverter.java | 2 +-
.../main/org/apache/sis/measure/UnitDimension.java | 2 +-
.../main/org/apache/sis/measure/UnitFormat.java | 6 +-
.../main/org/apache/sis/measure/UnitRegistry.java | 2 +-
.../main/org/apache/sis/measure/UnitServices.java | 2 +-
.../main/org/apache/sis/measure/Units.java | 2 +-
.../main/org/apache/sis/setup/About.java | 6 +-
.../main/org/apache/sis/setup/Configuration.java | 2 +-
.../apache/sis/setup/InstallationResources.java | 6 +-
.../apache/sis/setup/OptionalInstallations.java | 2 +-
.../main/org/apache/sis/system/DataDirectory.java | 2 +-
.../sis/util/AbstractInternationalString.java | 2 +-
.../main/org/apache/sis/util/ArgumentChecks.java | 2 +-
.../main/org/apache/sis/util/Characters.java | 4 +-
.../main/org/apache/sis/util/Numbers.java | 4 +-
.../apache/sis/util/collection/CodeListSet.java | 4 +-
.../org/apache/sis/util/collection/Containers.java | 2 +-
.../sis/util/collection/DefaultTreeTable.java | 4 +-
.../org/apache/sis/util/collection/DerivedSet.java | 2 +-
.../apache/sis/util/collection/IntegerList.java | 2 +-
.../sis/util/collection/TreeTableFormat.java | 6 +-
.../apache/sis/util/internal/AbstractIterator.java | 63 --
.../org/apache/sis/util/internal/AbstractMap.java | 709 --------------
.../apache/sis/util/internal/AbstractMapEntry.java | 100 --
.../sis/util/internal/AutoMessageFormat.java | 1 +
.../main/org/apache/sis/util/internal/Bag.java | 99 --
.../apache/sis/util/internal/CheckedArrayList.java | 324 -------
.../apache/sis/util/internal/CheckedHashSet.java | 115 ---
.../org/apache/sis/util/internal/CloneAccess.java | 46 -
.../main/org/apache/sis/util/internal/Cloner.java | 267 -----
.../org/apache/sis/util/internal/CodeLists.java | 169 ----
.../apache/sis/util/internal/CollectionsExt.java | 880 -----------------
.../org/apache/sis/util/internal/Constants.java | 266 -----
.../apache/sis/util/internal/DefinitionURI.java | 749 --------------
.../org/apache/sis/util/internal/DoubleDouble.java | 918 ------------------
.../org/apache/sis/util/internal/EmptyQueue.java | 68 --
.../sis/util/internal/ListOfUnknownSize.java | 259 -----
.../sis/util/internal/LocalizedParseException.java | 195 ----
.../apache/sis/util/internal/MetadataServices.java | 186 ----
.../org/apache/sis/util/internal/Numerics.java | 648 -------------
.../apache/sis/util/internal/PropertyFormat.java | 238 -----
.../apache/sis/util/internal/SetOfUnknownSize.java | 199 ----
.../sis/util/internal/StandardDateFormat.java | 513 ----------
.../main/org/apache/sis/util/internal/Strings.java | 420 --------
.../sis/util/internal/TreeFormatCustomization.java | 51 -
.../main/org/apache/sis/util/internal/URLs.java | 64 --
.../sis/util/internal/UnmodifiableArrayList.java | 439 ---------
.../main/org/apache/sis/util/internal/Unsafe.java | 106 --
.../main/org/apache/sis/util/internal/X364.java | 303 ------
.../org/apache/sis/util/internal/package-info.java | 8 +-
.../apache/sis/util/logging/MonolineFormatter.java | 6 +-
.../apache/sis/util/privy/AbstractIterator.java | 63 ++
.../org/apache/sis/util/privy/AbstractMap.java | 709 ++++++++++++++
.../apache/sis/util/privy/AbstractMapEntry.java | 100 ++
.../main/org/apache/sis/util/privy/Bag.java | 99 ++
.../apache/sis/util/privy/CheckedArrayList.java | 324 +++++++
.../org/apache/sis/util/privy/CheckedHashSet.java | 115 +++
.../org/apache/sis/util/privy/CloneAccess.java | 46 +
.../main/org/apache/sis/util/privy/Cloner.java | 267 +++++
.../main/org/apache/sis/util/privy/CodeLists.java | 169 ++++
.../org/apache/sis/util/privy/CollectionsExt.java | 880 +++++++++++++++++
.../main/org/apache/sis/util/privy/Constants.java | 266 +++++
.../org/apache/sis/util/privy/DefinitionURI.java | 749 ++++++++++++++
.../org/apache/sis/util/privy/DoubleDouble.java | 918 ++++++++++++++++++
.../main/org/apache/sis/util/privy/EmptyQueue.java | 68 ++
.../apache/sis/util/privy/ListOfUnknownSize.java | 259 +++++
.../sis/util/privy/LocalizedParseException.java | 195 ++++
.../apache/sis/util/privy/MetadataServices.java | 186 ++++
.../main/org/apache/sis/util/privy/Numerics.java | 648 +++++++++++++
.../org/apache/sis/util/privy/PropertyFormat.java | 238 +++++
.../apache/sis/util/privy/SetOfUnknownSize.java | 199 ++++
.../apache/sis/util/privy/StandardDateFormat.java | 513 ++++++++++
.../main/org/apache/sis/util/privy/Strings.java | 420 ++++++++
.../sis/util/privy/TreeFormatCustomization.java | 51 +
.../main/org/apache/sis/util/privy/URLs.java | 64 ++
.../sis/util/privy/UnmodifiableArrayList.java | 439 +++++++++
.../main/org/apache/sis/util/privy/Unsafe.java | 106 ++
.../main/org/apache/sis/util/privy/X364.java | 303 ++++++
.../org/apache/sis/util/privy/package-info.java | 36 +
.../sis/util/resources/IndexedResourceBundle.java | 4 +-
.../org/apache/sis/util/resources/Messages.java | 5 +
.../apache/sis/util/resources/Messages.properties | 1 +
.../sis/util/resources/Messages_fr.properties | 1 +
.../apache/sis/util/resources/package-info.java | 4 +-
.../test/org/apache/sis/io/WordWrapTest.java | 2 +-
.../org/apache/sis/measure/RangeFormatTest.java | 2 +-
.../test/org/apache/sis/test/TestCase.java | 2 +-
.../test/org/apache/sis/test/TestUtilities.java | 4 +-
.../org/apache/sis/util/collection/CacheTest.java | 2 +-
.../apache/sis/util/collection/RangeSetTest.java | 4 +-
.../apache/sis/util/internal/AbstractMapTest.java | 191 ----
.../test/org/apache/sis/util/internal/BagTest.java | 84 --
.../sis/util/internal/CheckedArrayListTest.java | 146 ---
.../sis/util/internal/CollectionsExtTest.java | 175 ----
.../sis/util/internal/DefinitionURITest.java | 270 ------
.../apache/sis/util/internal/DoubleDoubleTest.java | 410 --------
.../sis/util/internal/ListOfUnknownSizeTest.java | 64 --
.../util/internal/LocalizedParseExceptionTest.java | 76 --
.../org/apache/sis/util/internal/NumericsTest.java | 242 -----
.../sis/util/internal/StandardDateFormatTest.java | 148 ---
.../org/apache/sis/util/internal/StringsTest.java | 57 --
.../org/apache/sis/util/internal/X364Test.java | 69 --
.../sis/util/logging/MonolineFormatterTest.java | 2 +-
.../org/apache/sis/util/privy/AbstractMapTest.java | 191 ++++
.../test/org/apache/sis/util/privy/BagTest.java | 84 ++
.../sis/util/privy/CheckedArrayListTest.java | 146 +++
.../apache/sis/util/privy/CollectionsExtTest.java | 175 ++++
.../apache/sis/util/privy/DefinitionURITest.java | 270 ++++++
.../apache/sis/util/privy/DoubleDoubleTest.java | 410 ++++++++
.../sis/util/privy/ListOfUnknownSizeTest.java | 64 ++
.../util/privy/LocalizedParseExceptionTest.java | 76 ++
.../org/apache/sis/util/privy/NumericsTest.java | 242 +++++
.../sis/util/privy/StandardDateFormatTest.java | 148 +++
.../org/apache/sis/util/privy/StringsTest.java | 57 ++
.../test/org/apache/sis/util/privy/X364Test.java | 69 ++
.../storage/shapefile/ListingPropertyVisitor.java | 4 +-
.../sis/storage/shapefile/ShapefileStore.java | 4 +-
.../sis/storage/shapefile/ShapefileStoreTest.java | 2 +-
netbeans-project/nbproject/project.properties | 6 +-
.../apache/sis/gui/controls/SyncWindowList.java | 2 +-
.../apache/sis/gui/controls/ValueColorMapper.java | 2 +-
.../apache/sis/gui/coverage/CoverageCanvas.java | 2 +-
.../org/apache/sis/gui/coverage/GridTileCache.java | 2 +-
.../main/org/apache/sis/gui/coverage/GridView.java | 2 +-
.../apache/sis/gui/coverage/IsolineRenderer.java | 2 +-
.../org/apache/sis/gui/dataset/ExpandableList.java | 2 +-
.../org/apache/sis/gui/dataset/FeatureList.java | 2 +-
.../org/apache/sis/gui/dataset/FeatureTable.java | 4 +-
.../org/apache/sis/gui/dataset/ResourceCell.java | 2 +-
.../apache/sis/gui/internal/DataStoreOpener.java | 2 +-
.../org/apache/sis/gui/internal/GUIUtilities.java | 2 +-
.../apache/sis/gui/internal/ImageConverter.java | 6 +-
.../sis/gui/internal/PropertyValueFormats.java | 2 +-
.../sis/gui/internal/PropertyValueFormatter.java | 2 +-
.../apache/sis/gui/internal/io/FileAccessItem.java | 2 +-
.../main/org/apache/sis/gui/map/MapCanvas.java | 8 +-
.../main/org/apache/sis/gui/map/MapCanvasAWT.java | 2 +-
.../main/org/apache/sis/gui/map/StatusBar.java | 2 +-
.../org/apache/sis/gui/map/ValuesFormatter.java | 2 +-
.../sis/gui/metadata/IdentificationInfo.java | 4 +-
.../apache/sis/gui/metadata/MetadataSummary.java | 2 +-
.../sis/gui/metadata/RepresentationInfo.java | 2 +-
.../sis/gui/metadata/StandardMetadataTree.java | 2 +-
.../apache/sis/gui/referencing/AuthorityCodes.java | 4 +-
.../org/apache/sis/gui/referencing/CRSChooser.java | 2 +-
.../org/apache/sis/gui/referencing/CodeFilter.java | 2 +-
.../org/apache/sis/gui/referencing/MenuSync.java | 2 +-
.../gui/referencing/PositionableProjection.java | 4 +-
.../gui/referencing/RecentReferenceSystems.java | 4 +-
.../main/org/apache/sis/gui/referencing/Utils.java | 2 +-
1261 files changed, 45217 insertions(+), 45134 deletions(-)
diff --cc endorsed/src/org.apache.sis.feature/main/module-info.java
index 519d3be0f3,6daf162d76..80ebe49fda
--- a/endorsed/src/org.apache.sis.feature/main/module-info.java
+++ b/endorsed/src/org.apache.sis.feature/main/module-info.java
@@@ -45,14 -45,10 +45,14 @@@ module org.apache.sis.feature
org.apache.sis.storage,
org.apache.sis.storage.sql,
org.apache.sis.storage.shapefile, // In the "incubator" sub-project.
- org.apache.sis.cql, // In the "incubator" sub-project.
- org.apache.sis.portrayal.map; // In the "incubator" sub-project.
+ org.apache.sis.portrayal;
+
- exports org.apache.sis.filter.internal to
++ exports org.apache.sis.filter.privy to
+ org.apache.sis.storage,
+ org.apache.sis.storage.sql,
+ org.apache.sis.storage.shapefile; // In the "incubator" sub-project.
- exports org.apache.sis.feature.internal to
+ exports org.apache.sis.feature.privy to
org.apache.sis.storage,
org.apache.sis.storage.xml,
org.apache.sis.storage.netcdf,
@@@ -70,12 -69,11 +70,7 @@@
exports org.apache.sis.geometry.wrapper.j2d to
org.apache.sis.gui; // In the "optional" sub-project.
- exports org.apache.sis.coverage.internal to
- org.apache.sis.storage,
- org.apache.sis.storage.netcdf,
- org.apache.sis.portrayal;
- exports org.apache.sis.geometry.wrapper.jts to
- org.apache.sis.portrayal.map, // In the "incubator" sub-project.
- org.apache.sis.cql; // In the "incubator" sub-project.
--
- exports org.apache.sis.coverage.grid.j2d to
+ exports org.apache.sis.coverage.privy to
org.apache.sis.storage,
org.apache.sis.storage.sql,
org.apache.sis.storage.netcdf,
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/CoverageCombiner.java
index 4cde54a13b,1834312069..bc3974f7b4
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/CoverageCombiner.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/CoverageCombiner.java
@@@ -39,10 -39,13 +39,10 @@@ import org.apache.sis.image.Interpolati
import org.apache.sis.image.PlanarImage;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.measure.NumberRange;
- import org.apache.sis.coverage.internal.SampleDimensions;
- import static org.apache.sis.util.internal.Numerics.saturatingAdd;
- import static org.apache.sis.util.internal.Numerics.saturatingSubtract;
+ import org.apache.sis.coverage.privy.SampleDimensions;
+ import static org.apache.sis.util.privy.Numerics.saturatingAdd;
+ import static org.apache.sis.util.privy.Numerics.saturatingSubtract;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.coverage.CannotEvaluateException;
-
/**
* Combines an arbitrary number of coverages into a single one.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
index 46ca35a33b,657f765cdc..e744a78197
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/FractionalGridCoordinates.java
@@@ -26,11 -26,12 +26,11 @@@ import org.opengis.referencing.crs.Coor
import org.opengis.referencing.datum.PixelInCell;
import org.apache.sis.feature.internal.Resources;
import org.apache.sis.util.StringBuilders;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.coverage.PointOutsideCoverageException;
-import org.opengis.coverage.grid.GridCoordinates;
+// Specific to the main branch:
+import org.apache.sis.coverage.PointOutsideCoverageException;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/CommonDomainFinder.java
index 0000000000,6d2d581f8c..bc7e23ebe4
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/CommonDomainFinder.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/privy/CommonDomainFinder.java
@@@ -1,0 -1,374 +1,383 @@@
+ /*
+ * 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.privy;
+
+ import java.util.Map;
+ import java.util.LinkedHashMap;
+ import java.util.NoSuchElementException;
+ import org.opengis.util.FactoryException;
+ import org.opengis.geometry.MismatchedDimensionException;
+ import org.opengis.referencing.datum.PixelInCell;
+ import org.opengis.referencing.operation.Matrix;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.TransformException;
+ import org.opengis.referencing.operation.NoninvertibleTransformException;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.apache.sis.referencing.CRS;
+ import org.apache.sis.referencing.operation.transform.MathTransforms;
+ import org.apache.sis.coverage.grid.GridExtent;
+ import org.apache.sis.coverage.grid.GridGeometry;
+ import org.apache.sis.coverage.grid.IllegalGridGeometryException;
+ import org.apache.sis.referencing.privy.ExtendedPrecisionMatrix;
+ import org.apache.sis.feature.internal.Resources;
+ import org.apache.sis.util.Numbers;
+ import org.apache.sis.util.privy.Numerics;
+
+
+ /**
+ * Helper class for building a combined domain from a list of grid geometries.
+ * After construction, one of the following methods shall be invoked exactly once.
+ *
+ * <ul>
+ * <li>{@link #setFromGridAligned(GridGeometry...)}</li>
+ * </ul>
+ *
+ * Then, the result can be obtained by the given methods:
+ *
+ * <ul>
+ * <li>{@link #result()}</li>
+ * <li>{@link #gridTranslations()}</li>
+ * <li>{@link #sourceOfGridToCRS()}</li>
+ * </ul>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class CommonDomainFinder {
+ /**
+ * Whether to compute intersection (true) or union (false) of all grid coverages.
+ * This is not yet configurable in current version.
+ *
+ * <p>We use this constant for tracking the code to update when we will want to provide an option for using
+ * union or strict equality instead (the latter would be a mode that fails if all extents are not identical).
+ * Note that in the case of unions, it would be possible to specify coverages with no intersection.
+ * Whether we should accept that or raise an exception is still an open question.</p>
+ */
+ public static final boolean INTERSECTION = true;
+
+ /**
+ * The grid geometry taken as the reference for computing the translations of all grid geometry items.
+ * Values of {@link #gridTranslations} and {@link #itemTranslations} are relative to that reference.
+ * At first this is an arbitrary grid geometry, for example the first encountered one.
+ * After {@code CommonDomainFinder} completed its task, this is updated to the final result.
+ *
+ * @see #result()
+ */
+ private GridGeometry reference;
+
+ /**
+ * Coordinate reference system of the reference, or {@code null} if not yet known.
+ * It will be set to the CRS of the first grid geometry where the CRS is defined.
+ */
+ private CoordinateReferenceSystem crs;
+
+ /**
+ * The inverse of the "grid to CRS" transform of the grid geometry taken as a reference.
+ */
+ private MathTransform crsToGrid;
+
+ /**
+ * The convention to use for fetching the "grid to CRS" transforms.
+ */
+ private final PixelInCell anchor;
+
+ /**
+ * The combined extent, as the union or intersection of all grid extents.
+ */
+ private GridExtent extent;
+
+ /**
+ * The translation in units of grid cells from the {@linkplain #reference} grid geometry
+ * to the grid geometry in the key.
+ */
+ private final Map<GridGeometry,long[]> gridTranslations;
+
+ /**
+ * Translations in units of grid cells from the {@linkplain #reference} grid geometry to each item.
+ * For each index <var>i</var>, {@code itemTranslations[i]} is a value from {@link #gridTranslations}
+ * map and may be reused at more than one index <var>i</var>.
+ *
+ * @see #gridTranslations()
+ */
+ private long[][] itemTranslations;
+
+ /**
+ * If one of the grid geometries has the same "grid to CRS" than the common grid geometry, the index.
+ * Otherwise -1.
+ */
+ private int sourceOfGridToCRS;
+
+ /**
+ * Creates a new common domain finder.
+ *
+ * @param anchor the convention to use for fetching the "grid to CRS" transforms.
+ */
+ CommonDomainFinder(final PixelInCell anchor) {
+ this.anchor = anchor;
+ gridTranslations = new LinkedHashMap<>();
+ }
+
+ /**
+ * Computes a common grid geometry from the given items.
+ * All items shall share be aligned on the same grid.
+ * Items may be translated relative to each other,
+ * but the translations shall be an integer number of grid cells.
+ *
+ * <h4>Coordinate reference system</h4>
+ * If the CRS of a grid geometry is undefined, it is assumed the same as other grid geometries.
+ *
+ * @param items the grid geometries for which to compute a common grid geometry.
+ * @throws IllegalGridGeometryException if the specified item is not compatible with the reference grid geometry.
+ */
+ final void setFromGridAligned(final GridGeometry... items) {
+ itemTranslations = new long[items.length][];
+ for (int i=0; i<items.length; i++) {
+ itemTranslations[i] = gridTranslations.computeIfAbsent(items[i], this::itemToCommon);
+ }
+ /*
+ * Change the reference grid geometry for matching more closely the desired grid extent.
+ * If one item has exactly the desired grid extent, use it. Otherwise search for an item
+ * having the same origin. This criterion is arbitrary and may change in future version.
+ */
+ GridGeometry fallback = null;
+ GridExtent location = null;
+ for (final Map.Entry<GridGeometry,long[]> entry : gridTranslations.entrySet()) {
+ final GridGeometry item = entry.getKey();
+ final GridExtent actual = item.getExtent();
+ final GridExtent expected = extent.translate(entry.getValue());
+ if (actual.equals(expected)) {
+ setGridToCRS(items, item); // Must be before `reference` assignation.
+ reference = item;
+ return;
+ }
+ // Arbitrary criterion (may be revisited in any future version).
- if (fallback == null && expected.getLow().equals(actual.getLow())) {
++ if (fallback == null && sameLow(expected, actual)) {
+ location = expected;
+ fallback = item;
+ }
+ }
+ if (fallback == null) {
+ fallback = reference;
+ location = extent;
+ }
+ setGridToCRS(items, fallback);
+ try {
+ reference = fallback.relocate(location);
+ } catch (TransformException e) {
+ throw new IllegalGridGeometryException(Resources.format(Resources.Keys.IncompatibleGridGeometries), e);
+ }
+ }
+
++ private static boolean sameLow(final GridExtent e1, final GridExtent e2) {
++ final int dim = e1.getDimension();
++ if (dim != e2.getDimension()) return false;
++ for (int i=0; i<dim; i++) {
++ if (e1.getLow(i) != e2.getLow(i)) return false;
++ }
++ return true;
++ }
++
+ /**
+ * Given a grid geometry with the desired "grid to CRS", saves its index in {@link #sourceOfGridToCRS}.
+ * This method updates all previously computed translations for making them relative to the new reference.
+ *
+ * Note: updating values of the {@link #gridTranslations} map indirectly update all
+ * {@link #itemTranslations} array elements.
+ */
+ private void setGridToCRS(final GridGeometry[] items, final GridGeometry item) {
+ sourceOfGridToCRS = indexOf(items, item);
+ if (item == reference) { // Quick check for a common case.
+ return;
+ }
+ final long[] oldReference = itemTranslations[indexOf(items, reference)];
+ final long[] newReference = itemTranslations[sourceOfGridToCRS];
+ final long[] change = new long[newReference.length];
+ for (int i=0; i < newReference.length; i++) {
+ change[i] = Math.subtractExact(newReference[i], oldReference[i]);
+ }
+ for (final long[] offset : gridTranslations.values()) {
+ for (int i=0; i < offset.length; i++) {
+ offset[i] = Math.subtractExact(offset[i], change[i]);
+ }
+ }
+ }
+
+ /**
+ * Returns the index of the given grid geometry.
+ * This method is invoked when the instance should always exist in the array.
+ */
+ private static int indexOf(final GridGeometry[] items, final GridGeometry item) {
+ for (int i=0; i<items.length; i++) {
+ if (items[i] == item) {
+ return i;
+ }
+ }
+ throw new NoSuchElementException(); // Should never happen.
+ }
+
+ /**
+ * Returns the common grid geometry computed from all specified items.
+ *
+ * @return a grid geometry which is the union or intersection of all specified items.
+ */
+ final GridGeometry result() {
+ if (crs != null && !reference.isDefined(GridGeometry.CRS)) {
+ reference = new GridGeometry(extent, anchor, reference.getGridToCRS(anchor), crs);
+ }
+ return reference;
+ }
+
+ /**
+ * Returns the translations (in units of grid cells) from the common grid geometry to all items.
+ * The items are the arguments given to {@link #setFromGridAligned(GridGeometry...)}, in order.
+ * The common grid geometry is the value returned by {@link #result()}.
+ *
+ * <p>The returned array should not be modified because it is not cloned.</p>
+ *
+ * @return translation from the common grid geometry to all items. This array is not cloned.
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ final long[][] gridTranslations() {
+ return itemTranslations;
+ }
+
+ /**
+ * If one items has the same "grid to CRS" than the common grid geometry, returns its index.
+ *
+ * @return index of an items having the desired "grid to CRS", or -1 if none.
+ */
+ final int sourceOfGridToCRS() {
+ return sourceOfGridToCRS;
+ }
+
+ /**
+ * Computes the translation in units of grid cells from the common grid geometry to the given item.
+ * This method also opportunistically computes the union or intersection of all grid extents.
+ *
+ * @param item the grid geometry for which to get a translation from the common grid geometry.
+ * @return translation in unis of grid cells. Note that the caller may reuse this array for many grid geometries.
+ * @throws IllegalGridGeometryException if the specified item is not compatible with the reference grid geometry.
+ */
+ private long[] itemToCommon(final GridGeometry item) {
+ /*
+ * Compute the change ourselves instead of invoking `GridGeometry.createTransformTo(…)`
+ * because we do not want wraparound handling when we search for a simple translation.
+ */
+ MathTransform change = item.getGridToCRS(anchor);
+ try {
+ if (crsToGrid == null) {
+ crsToGrid = change.inverse();
+ reference = item;
+ }
+ if (item.isDefined(GridGeometry.CRS)) {
+ final CoordinateReferenceSystem src = item.getCoordinateReferenceSystem();
+ if (crs == null) {
+ crs = src;
+ } else {
+ /*
+ * Ask for a change of CRS without specifying an area of interest (AOI) on the assumption
+ * that if the transformation is only a translation, the AOI would not make a difference.
+ * It save not only the AOI computation cost, but also make easier for `findOperation(…)`
+ * to use its cache.
+ */
+ change = MathTransforms.concatenate(change, CRS.findOperation(src, crs, null).getMathTransform());
+ }
+ }
+ change = MathTransforms.concatenate(change, crsToGrid);
+ } catch (FactoryException | NoninvertibleTransformException | MismatchedDimensionException e) {
+ throw new IllegalGridGeometryException(Resources.format(Resources.Keys.IncompatibleGridGeometries), e);
+ }
+ final long[] offset = integerTranslation(MathTransforms.getMatrix(change), null);
+ if (offset == null) {
+ throw new IllegalGridGeometryException(Resources.format(Resources.Keys.IncompatibleGridGeometries));
+ }
+ /*
+ * The grid geometry has been accepted as valid. Now compute the combined extent,
+ * taking offset in account. At this point this is an offset TO the common grid geometry.
+ * It will be converted to an offset FROM the common grid geometry after the extent update.
+ */
+ final GridExtent e = item.getExtent().translate(offset);
+ if (extent == null) {
+ extent = e;
+ } else if (INTERSECTION) {
+ extent = extent.intersect(e);
+ } else if (!extent.equals(e)) {
+ throw new IllegalGridGeometryException();
+ }
+ for (int i=0; i<offset.length; i++) {
+ offset[i] = Math.negateExact(offset[i]);
+ }
+ return offset;
+ }
+
+ /**
+ * If the given matrix is the identity matrix except for translation terms, returns the translation.
+ * Translation terms must be integer values and will be stored in the given {@code offset} array.
+ * If the matrix is not an integer translation, this method return {@code null} without modifying
+ * the given {@code offset} array.
+ *
+ * @param change conversion between two grid geometries, or {@code null}.
+ * @param offset where to store translation terms if the change is an integer translation, or {@code null}.
+ * @return the translation terms, or {@code null} if the given matrix does not met the conditions.
+ *
+ * @see org.apache.sis.referencing.operation.matrix.Matrices#isTranslation(Matrix)
+ */
+ public static long[] integerTranslation(final Matrix change, long[] offset) {
+ if (change == null) {
+ return null;
+ }
+ final int numRows = change.getNumRow();
+ final int numCols = change.getNumCol();
+ for (int j=0; j<numRows; j++) {
+ for (int i=0; i<numCols; i++) {
+ double tolerance = Numerics.COMPARISON_THRESHOLD;
+ double e = change.getElement(j, i);
+ if (i == j) {
+ e--;
+ } else if (i == numCols - 1) {
+ final double a = Math.abs(e);
+ if (a > 1) {
+ tolerance = Math.min(tolerance*a, 0.125);
+ }
+ e -= Math.rint(e);
+ }
+ if (!(Math.abs(e) <= tolerance)) { // Use `!` for catching NaN.
+ return null;
+ }
+ }
+ }
+ /*
+ * Store the translation terms after we have determined that they are integers.
+ * It must be an "all or nothing" operation (unless an exception is thrown).
+ */
+ if (offset == null) {
+ offset = new long[numRows - 1];
+ }
+ final int i = numCols - 1;
+ if (change instanceof ExtendedPrecisionMatrix) {
+ final var epm = (ExtendedPrecisionMatrix) change;
+ for (int j=0; j<offset.length; j++) {
+ final Number e = epm.getElementOrNull(j, i);
+ offset[j] = (e != null) ? Numbers.round(e) : 0;
+ }
+ } else {
+ for (int j=0; j<offset.length; j++) {
+ offset[j] = Math.round(change.getElement(j, i));
+ }
+ }
+ return offset;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
index f3c6fbf2bd,c073f80a18..14f769111d
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java
@@@ -30,9 -30,23 +30,9 @@@ import org.apache.sis.util.ArgumentChec
import org.apache.sis.util.CorruptedObjectException;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.collection.Containers;
- import org.apache.sis.util.internal.CheckedArrayList;
+ import org.apache.sis.util.privy.CheckedArrayList;
import org.apache.sis.feature.internal.Resources;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Property;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.PropertyNotFoundException;
-import org.opengis.feature.InvalidPropertyValueException;
-import org.opengis.feature.Attribute;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.Operation;
-
/**
* An instance of a {@linkplain DefaultFeatureType feature type} containing values for a real-world phenomena.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/CharacteristicMap.java
index 1008b52f1d,8c32f92b3a..8b579e0324
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/CharacteristicMap.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/CharacteristicMap.java
@@@ -18,12 -18,18 +18,12 @@@ package org.apache.sis.feature
import java.util.Map;
import org.opengis.util.GenericName;
- import org.apache.sis.util.internal.Cloner;
- import org.apache.sis.util.internal.CloneAccess;
- import org.apache.sis.util.internal.AbstractMap;
- import org.apache.sis.util.internal.AbstractMapEntry;
+ import org.apache.sis.util.privy.Cloner;
+ import org.apache.sis.util.privy.CloneAccess;
+ import org.apache.sis.util.privy.AbstractMap;
+ import org.apache.sis.util.privy.AbstractMapEntry;
import org.apache.sis.feature.internal.Resources;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Attribute;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.InvalidPropertyValueException;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* Implementation of {@link AbstractAttribute#characteristics()} map.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultAssociationRole.java
index a2ace425b1,ab1fbb4b57..fd126ca70b
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultAssociationRole.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultAssociationRole.java
@@@ -26,8 -26,16 +26,8 @@@ import org.opengis.util.GenericName
import org.opengis.util.InternationalString;
import org.opengis.metadata.Identifier;
import org.apache.sis.feature.internal.Resources;
- import org.apache.sis.feature.internal.AttributeConvention;
+ import org.apache.sis.feature.privy.AttributeConvention;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* Indicates the role played by the association between two features.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultFeatureType.java
index 7a942b459e,772e341d96..d8fe6432f3
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultFeatureType.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DefaultFeatureType.java
@@@ -36,10 -36,21 +36,10 @@@ import org.opengis.util.InternationalSt
import org.opengis.parameter.ParameterDescriptorGroup;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.collection.Containers;
- import org.apache.sis.util.internal.CollectionsExt;
- import org.apache.sis.util.internal.UnmodifiableArrayList;
+ import org.apache.sis.util.privy.CollectionsExt;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
import org.apache.sis.feature.internal.Resources;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.feature.Operation;
-import org.opengis.feature.FeatureInstantiationException;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* Abstraction of a real-world phenomena. A {@code FeatureType} instance describes the class of all
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
index 70ef332a83,6cdfb9c7e7..bbdfb52a4a
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/DenseFeature.java
@@@ -21,9 -21,15 +21,9 @@@ import java.util.Arrays
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.metadata.quality.DataQuality;
import org.apache.sis.util.ArgumentChecks;
- import org.apache.sis.util.internal.CloneAccess;
- import org.apache.sis.util.internal.Cloner;
+ import org.apache.sis.util.privy.CloneAccess;
+ import org.apache.sis.util.privy.Cloner;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Property;
-import org.opengis.feature.Attribute;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* A feature in which most properties are expected to be provided. This implementation uses a plain array for
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/ExpressionOperation.java
index 329dc3ed22,3518b6b10d..9d56804430
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/ExpressionOperation.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/ExpressionOperation.java
@@@ -23,15 -23,20 +23,15 @@@ import java.util.Collection
import java.util.function.Function;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
- import org.apache.sis.feature.internal.FeatureUtilities;
- import org.apache.sis.filter.internal.FunctionNames;
- import org.apache.sis.filter.internal.Visitor;
+ import org.apache.sis.feature.privy.FeatureUtilities;
+ import org.apache.sis.filter.privy.FunctionNames;
+ import org.apache.sis.filter.privy.Visitor;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.CodeList;
-import org.opengis.feature.Feature;
-import org.opengis.feature.Property;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.filter.Filter;
-import org.opengis.filter.Expression;
-import org.opengis.filter.LogicalOperator;
-import org.opengis.filter.ValueReference;
+// Specific to the main branch:
+import org.apache.sis.filter.Filter;
+import org.apache.sis.filter.Expression;
+import org.apache.sis.pending.geoapi.filter.LogicalOperator;
+import org.apache.sis.pending.geoapi.filter.ValueReference;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
index 0daba2f8aa,f88ee704ac..ef10b003fa
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/FeatureOperations.java
@@@ -27,11 -27,16 +27,11 @@@ import org.apache.sis.util.Static
import org.apache.sis.util.UnconvertibleObjectException;
import org.apache.sis.util.collection.WeakHashSet;
import org.apache.sis.util.resources.Errors;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
import org.apache.sis.setup.GeometryLibrary;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.feature.Operation;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.filter.Expression;
+// Specific to the main branch:
+import org.apache.sis.filter.Expression;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/LinkOperation.java
index 79f4648ec3,6b99151a2c..f5efb4ae70
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/LinkOperation.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/LinkOperation.java
@@@ -21,9 -21,15 +21,9 @@@ import java.util.Map
import java.io.IOException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
- import org.apache.sis.feature.internal.FeatureUtilities;
+ import org.apache.sis.feature.privy.FeatureUtilities;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.Property;
-import org.opengis.feature.PropertyType;
-
/**
* A link operation, which is like a redirection or an alias.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/MultiValuedAssociation.java
index d06569c32a,1f9b497dc6..ecca415815
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/MultiValuedAssociation.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/MultiValuedAssociation.java
@@@ -18,9 -18,15 +18,9 @@@ package org.apache.sis.feature
import java.util.Collection;
import org.apache.sis.util.ArgumentChecks;
- import org.apache.sis.util.internal.CheckedArrayList;
+ import org.apache.sis.util.privy.CheckedArrayList;
import org.apache.sis.feature.internal.Resources;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.FeatureAssociationRole;
-import org.opengis.feature.MultiValuedPropertyException;
-
/**
* An instance of an {@linkplain DefaultAssociationRole association role} containing an arbitrary number of values.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/NamedFeatureType.java
index c6e8e439aa,abc94cb28d..540e63e97b
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/NamedFeatureType.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/NamedFeatureType.java
@@@ -20,8 -20,18 +20,8 @@@ import java.util.Collection
import java.util.Collections;
import java.io.Serializable;
import org.opengis.util.GenericName;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.Set;
-import org.opengis.util.InternationalString;
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.PropertyNotFoundException;
-import org.opengis.feature.FeatureInstantiationException;
-import org.apache.sis.feature.internal.Resources;
-
/**
* A feature type identified only by its name. Instances of {@code NamedFeatureType} shall be used only as placeholder
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SingletonAttribute.java
index dbe9774856,8175cd608a..2ef54b20b7
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SingletonAttribute.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SingletonAttribute.java
@@@ -17,8 -17,11 +17,8 @@@
package org.apache.sis.feature;
import java.util.Objects;
- import org.apache.sis.util.internal.CloneAccess;
+ import org.apache.sis.util.privy.CloneAccess;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.AttributeType;
-
/**
* An instance of an {@linkplain DefaultAttributeType attribute type} containing at most one value.
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
index 6eba9707f0,8fdb173a5f..21ce42eade
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/SparseFeature.java
@@@ -24,9 -24,15 +24,9 @@@ import org.opengis.metadata.maintenance
import org.opengis.metadata.quality.DataQuality;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.CorruptedObjectException;
- import org.apache.sis.util.internal.CloneAccess;
- import org.apache.sis.util.internal.Cloner;
+ import org.apache.sis.util.privy.CloneAccess;
+ import org.apache.sis.util.privy.Cloner;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Property;
-import org.opengis.feature.Attribute;
-import org.opengis.feature.FeatureAssociation;
-import org.opengis.feature.PropertyNotFoundException;
-
/**
* A feature in which only a small fraction of properties are expected to be provided. This implementation uses
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
index 0000000000,1e2c9030dc..150d5afad5
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/AttributeConvention.java
@@@ -1,0 -1,444 +1,440 @@@
+ /*
+ * 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.feature.privy;
+
+ import java.util.Optional;
+ import org.opengis.util.LocalName;
+ import org.opengis.util.ScopedName;
+ import org.opengis.util.GenericName;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.util.iso.Names;
+ import org.apache.sis.feature.Features;
+ import org.apache.sis.geometry.wrapper.Geometries;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.feature.Attribute;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.Operation;
-import org.opengis.feature.Property;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyNotFoundException;
++// Specific to the main branch:
++import org.apache.sis.feature.AbstractFeature;
++import org.apache.sis.feature.AbstractAttribute;
++import org.apache.sis.feature.AbstractIdentifiedType;
++import org.apache.sis.feature.DefaultAttributeType;
++import org.apache.sis.feature.DefaultFeatureType;
+
+
+ /**
+ * Defines the names of some properties or characteristics for which we assign a conventional usage.
+ * Properties with the names defined in this {@code AttributeConvention} class are often aliases generated
+ * by the SIS implementation of various file readers. Those synthetic properties redirect to the most
+ * appropriate "real" property in the feature.
+ *
+ * <div class="note"><b>Example:</b>
+ * one of the most frequently used synthetic property is {@code "sis:identifier"}, which contains a unique
+ * identifier (or primary key) for the feature. This property is usually (but not necessarily)
+ * a {@linkplain org.apache.sis.feature.FeatureOperations#link link to an existing attribute}.
+ * By using the {@code "sis:identifier"} alias, users do not need to know the name of the "real" attribute.
+ * </div>
+ *
+ * This class defines names for two kinds of usage:
+ * <ul>
+ * <li>Names ending with {@code "_PROPERTY"} are used for attributes or operations that are members of the
+ * collection returned by {@link org.apache.sis.feature.DefaultFeatureType#getProperties(boolean)}.</li>
+ * <li>Names ending with {@code "_CHARACTERISTIC"} are used for characteristics that are entries of the
+ * map returned by {@link org.apache.sis.feature.DefaultAttributeType#characteristics()}.</li>
+ * </ul>
+ *
+ * <h2>Mixing with other conventions</h2>
+ * The conventions defined in this class are specific to Apache SIS.
+ * Current implementation does not support any other convention than the SIS one,
+ * but we may refactor this class in future SIS versions if there is a need to support different conventions.
+ *
+ * <p>In order to reduce the risk of name collision with properties in user-defined features
+ * (e.g. the user may already have an attribute named {@code "identifier"} for his own purpose),
+ * all names defined in this class begin with the {@code "@"} character.</p>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class AttributeConvention extends Static {
+ /**
+ * Scope of all names defined by SIS convention.
+ */
+ private static final LocalName SCOPE = Names.createLocalName("Apache", null, "sis");
+
+ /**
+ * Conventional name for a property used as a unique identifier.
+ * The identifier should be unique in the {@link org.apache.sis.storage.DataStore} instance containing the feature
+ * (for example a {@code DataStore} opened for a XML file), but does not need to be unique between two independent
+ * {@code DataStore} instances.
+ *
+ * <p>Properties of this name are usually
+ * {@linkplain org.apache.sis.feature.FeatureOperations#link aliases for existing attributes}, or
+ * {@linkplain org.apache.sis.feature.FeatureOperations#compound compound keys} made by concatenation
+ * of two or more other attributes.</p>
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} is usually
+ * {@link String}, {@link Integer}, {@link java.util.UUID} or other types commonly used as identifiers.</p>
+ */
+ public static final ScopedName IDENTIFIER_PROPERTY = Names.createScopedName(SCOPE, null, "identifier");
+
+ /**
+ * Conventional name for a property containing the geometric object to use by default.
+ * Some features may contain more than one geometric object; this property tells which
+ * geometry to render on a map for example.
+ *
+ * <p>Properties of this name are usually {@linkplain org.apache.sis.feature.FeatureOperations#link
+ * operations acting as a redirection to another attribute}.</p>
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} can be
+ * the {@link com.esri.core.geometry.Geometry} class from ESRI's API, or the {@code Geometry} class from
+ * <cite>Java Topology Suite</cite> (JTS) library, or any other class defined in future SIS versions.
- * See {@link #isGeometryAttribute(IdentifiedType)} for testing whether the value is a supported type.</p>
++ * See {@code isGeometryAttribute(IdentifiedType)} for testing whether the value is a supported type.</p>
+ *
- * @see #isGeometryAttribute(IdentifiedType)
++ * @see #isGeometryAttribute(AbstractIdentifiedType)
+ */
+ public static final ScopedName GEOMETRY_PROPERTY = Names.createScopedName(SCOPE, null, "geometry");
+
+ /**
+ * Conventional name for fetching the envelope encompassing all geometries in a feature.
+ * Most {@code FeatureType}s have at most one geometry, which is also the {@link #GEOMETRY_PROPERTY default geometry}.
+ * But if several geometries exist, then the value for this synthetic property is the union of all geometries.
+ *
+ * <p>Properties of this name are usually
+ * {@linkplain org.apache.sis.feature.FeatureOperations#envelope operations}.</p>
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should be
+ * {@link org.opengis.geometry.Envelope}.</p>
+ */
+ public static final ScopedName ENVELOPE_PROPERTY = Names.createScopedName(SCOPE, null, "envelope");
+
+ /**
+ * Conventional name for fetching the Coordinate Reference System (CRS) of a geometry or a coverage.
+ * This characteristic is typically an entry in the map returned by a call to the
+ * {@link org.apache.sis.feature.DefaultAttributeType#characteristics()} method
+ * on the attribute referenced by {@link #GEOMETRY_PROPERTY}.
+ *
+ * <p>While it is technically possible to have different CRS for different feature instances,
+ * in most cases the CRS is the same for all geometries found in {@code GEOMETRY_PROPERTY}.
+ * In such cases, the CRS can be specified only once as the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#getDefaultValue() default value}
+ * of this {@code CRS_CHARACTERISTIC}.</p>
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should be
+ * {@link org.opengis.referencing.crs.CoordinateReferenceSystem}.</p>
+ *
- * @see #getCRSCharacteristic(Property)
++ * @see #getCRSCharacteristic(Object)
+ */
+ public static final ScopedName CRS_CHARACTERISTIC = Names.createScopedName(SCOPE, null, "crs");
+
+ /**
+ * Conventional name for fetching the unit of measurement of a property.
+ * This characteristic is typically an entry in the map returned by a call to the
+ * {@link org.apache.sis.feature.DefaultAttributeType#characteristics()} method
+ * on any attribute of numeric type.
+ *
+ * <p>While it is technically possible to have different units of measurement for the same property
+ * on different feature instances, in most cases the unit is the same for all feature instances.
+ * In such cases, the unit can be specified only once as the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#getDefaultValue() default value}.</p>
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should be
+ * {@link javax.measure.Unit}.</p>
+ */
+ public static final ScopedName UNIT_CHARACTERISTIC = Names.createScopedName(SCOPE, null, "unit");
+
+ /**
+ * Conventional name for fetching the maximal length of string values.
+ * The maximal length is stored as the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#getDefaultValue() default value} of the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#characteristics() characteristic} associated
+ * to the attribute on which the maximal length applies.
+ *
+ * <p>The {@linkplain org.apache.sis.feature.DefaultAttributeType#getValueClass() value class} should be
+ * {@link Integer}.</p>
+ *
- * @see #getMaximalLengthCharacteristic(Property)
++ * @see #getMaximalLengthCharacteristic(Object)
+ */
+ public static final ScopedName MAXIMAL_LENGTH_CHARACTERISTIC = Names.createScopedName(SCOPE, null, "maximalLength");
+
+ /**
+ * Conventional name for fetching the enumeration of valid values.
+ * The set of valid values is stored stored as the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#getDefaultValue() default value} of the
+ * {@linkplain org.apache.sis.feature.DefaultAttributeType#characteristics() characteristic} associated
+ * to the attribute on which the restriction applies.
+ */
+ public static final GenericName VALID_VALUES_CHARACTERISTIC = Names.createScopedName(SCOPE, null, "validValues");
+
+ /**
+ * String representation of the {@link #IDENTIFIER_PROPERTY} name.
- * This can be used in calls to {@link Feature#getPropertyValue(String)}.
++ * This can be used in calls to {@link AbstractFeature#getPropertyValue(String)}.
+ */
+ public static final String IDENTIFIER = "sis:identifier";
+
+ /**
+ * String representation of the {@link #GEOMETRY_PROPERTY} name.
- * This can be used in calls to {@link Feature#getPropertyValue(String)}.
++ * This can be used in calls to {@link AbstractFeature#getPropertyValue(String)}.
+ */
+ public static final String GEOMETRY = "sis:geometry";
+
+ /**
+ * String representation of the {@link #ENVELOPE_PROPERTY} name.
+ * This can be used in calls to {@link Feature#getPropertyValue(String)}.
+ */
+ public static final String ENVELOPE = "sis:envelope";
+
+ /**
+ * String representation of the {@link #CRS_CHARACTERISTIC} name.
+ */
+ public static final String CRS = "sis:crs";
+
+ /**
+ * String representation of the {@link #UNIT_CHARACTERISTIC} name.
+ */
+ public static final String UNIT = "sis:unit";
+
+ /**
+ * String representation of the {@link #MAXIMAL_LENGTH_CHARACTERISTIC} name.
+ */
+ public static final String MAXIMAL_LENGTH = "sis:maximalLength";
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private AttributeConvention() {
+ }
+
+ /**
+ * Returns {@code true} if the given name stands for one of the synthetic properties defined by convention.
+ * Conventional properties are properties added by the {@code DataStore} to the {@code FeatureType} in order
+ * to provide a uniform way to access commonly used information.
+ *
+ * <p>Synthetic properties should generally not be written by the user.
+ * Those properties are calculated most of the time and have only a meaning within SIS.</p>
+ *
+ * <p>Current implementation returns {@code true} if the given name is in the SIS namespace.</p>
+ *
+ * @param name the name of the property or characteristic to test, or {@code null}.
+ * @return {@code true} if the given name is non-null and in the SIS namespace.
+ */
+ public static boolean contains(GenericName name) {
+ while (name instanceof ScopedName) {
+ if (SCOPE.equals(((ScopedName) name).path())) {
+ return true;
+ }
+ name = ((ScopedName) name).tail();
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the given feature type is non-null and has a {@value #IDENTIFIER} property.
+ *
+ * @param feature the feature type to test, or {@code null}.
+ * @return whether the given feature type is non-null and has a {@value #IDENTIFIER} property.
+ */
- public static boolean hasIdentifier(final FeatureType feature) {
++ public static boolean hasIdentifier(final DefaultFeatureType feature) {
+ if (feature != null) try {
+ return feature.getProperty(IDENTIFIER) != null;
- } catch (PropertyNotFoundException e) {
++ } catch (IllegalArgumentException e) {
+ // Ignore
+ }
+ return false;
+ }
+
+ /**
- * Returns {@code true} if the given type is an {@link AttributeType} or an {@link Operation} computing
++ * Returns {@code true} if the given type is an {@code AttributeType} or an {@code Operation} computing
+ * an attribute, and the attribute value is one of the geometry types recognized by SIS.
+ * The types currently recognized by SIS are:
+ *
+ * <ul>
+ * <li>{@link com.esri.core.geometry.Geometry} of the ESRI's API.</li>
+ * </ul>
+ *
+ * The above list may be expanded in any future SIS version.
+ *
+ * @param type the type to test, or {@code null}.
+ * @return {@code true} if the given type is (directly or indirectly) an attribute type
+ * for one of the recognized geometry types.
+ *
+ * @see #GEOMETRY_PROPERTY
+ */
- public static boolean isGeometryAttribute(final IdentifiedType type) {
- final Optional<AttributeType<?>> at = Features.toAttribute(type);
++ public static boolean isGeometryAttribute(final AbstractIdentifiedType type) {
++ final Optional<DefaultAttributeType<?>> at = Features.toAttribute(type);
+ return at.isPresent() && Geometries.isKnownType(at.get().getValueClass());
+ }
+
+ /**
+ * Returns whether the given operation or attribute type is characterized by a coordinate reference system.
+ * This method verifies whether a characteristic named {@link #CRS_CHARACTERISTIC} with values assignable to
+ * {@link CoordinateReferenceSystem} exists (directly or indirectly) for the given type.
+ *
+ * @param type the operation or attribute type for which to get the CRS, or {@code null}.
+ * @return {@code true} if a characteristic for Coordinate Reference System has been found.
+ */
- public static boolean characterizedByCRS(final IdentifiedType type) {
++ public static boolean characterizedByCRS(final AbstractIdentifiedType type) {
+ return hasCharacteristic(type, CRS, CoordinateReferenceSystem.class);
+ }
+
+ /**
+ * Returns the Coordinate Reference Systems characteristic for the given attribute, or {@code null} if none.
+ * This method gets the value or default value from the characteristic named {@link #CRS_CHARACTERISTIC}.
+ *
+ * @param attribute the attribute for which to get the CRS, or {@code null}.
+ * @return the Coordinate Reference System characteristic of the given attribute, or {@code null} if none.
+ * @throws ClassCastException if {@link #CRS_CHARACTERISTIC} has been found but is associated
+ * to an object which is not a {@link CoordinateReferenceSystem} instance.
+ *
+ * @see org.apache.sis.feature.builder.AttributeTypeBuilder#setCRS(CoordinateReferenceSystem)
+ */
- public static CoordinateReferenceSystem getCRSCharacteristic(final Property attribute) {
++ public static CoordinateReferenceSystem getCRSCharacteristic(final Object attribute) {
+ return (CoordinateReferenceSystem) getCharacteristic(attribute, CRS);
+ }
+
+ /**
+ * Returns the Coordinate Reference Systems characteristic for the given property type, or {@code null} if none.
+ * This method gets the default value from the characteristic named {@link #CRS_CHARACTERISTIC}.
+ * If the given property is a link, then this method follows the link in the given feature type (if non-null).
+ *
+ * <p>This method should be used only when the actual property instance is unknown.
- * Otherwise, {@link #getCRSCharacteristic(Property)} should be used because the CRS
++ * Otherwise, {@code getCRSCharacteristic(Property)} should be used because the CRS
+ * may vary for each property instance.</p>
+ *
+ * @param feature the feature type in which to follow links, or {@code null} if none.
+ * @param attribute the attribute type for which to get the CRS, or {@code null}.
+ * @return the Coordinate Reference System characteristic of the given property type, or {@code null} if none.
+ * @throws ClassCastException if {@link #CRS_CHARACTERISTIC} has been found but is associated
+ * to an object which is not a {@link CoordinateReferenceSystem} instance.
+ */
- public static CoordinateReferenceSystem getCRSCharacteristic(final FeatureType feature, final PropertyType attribute) {
++ public static CoordinateReferenceSystem getCRSCharacteristic(final DefaultFeatureType feature, final AbstractIdentifiedType attribute) {
+ return (CoordinateReferenceSystem) getCharacteristic(feature, attribute, CRS);
+ }
+
+ /**
+ * Returns whether the given operation or attribute type is characterized by a maximal length.
+ * This method verifies whether a characteristic named {@link #MAXIMAL_LENGTH_CHARACTERISTIC}
+ * with values of class {@link Integer} exists (directly or indirectly) for the given type.
+ *
+ * @param type the operation or attribute type for which to get the maximal length, or {@code null}.
+ * @return {@code true} if a characteristic for maximal length has been found.
+ */
- public static boolean characterizedByMaximalLength(final IdentifiedType type) {
++ public static boolean characterizedByMaximalLength(final AbstractIdentifiedType type) {
+ return hasCharacteristic(type, MAXIMAL_LENGTH, Integer.class);
+ }
+
+ /**
+ * Returns the maximal length characteristic for the given attribute, or {@code null} if none.
+ * This method gets the value or default value from the characteristic named {@link #MAXIMAL_LENGTH_CHARACTERISTIC}.
+ *
+ * @param attribute the attribute for which to get the maximal length, or {@code null}.
+ * @return the maximal length characteristic of the given attribute, or {@code null} if none.
+ * @throws ClassCastException if {@link #MAXIMAL_LENGTH_CHARACTERISTIC} has been found but is associated
+ * to an object which is not an {@link Integer} instance.
+ *
+ * @see org.apache.sis.feature.builder.AttributeTypeBuilder#setMaximalLength(Integer)
+ */
- public static Integer getMaximalLengthCharacteristic(final Property attribute) {
++ public static Integer getMaximalLengthCharacteristic(final Object attribute) {
+ return (Integer) getCharacteristic(attribute, MAXIMAL_LENGTH);
+ }
+
+ /**
+ * Returns the maximal length characteristic for the given property type, or {@code null} if none.
+ * This method gets the default value from the characteristic named {@link #MAXIMAL_LENGTH_CHARACTERISTIC}.
+ * If the given property is a link, then this method follows the link in the given feature type (if non-null).
+ *
+ * <p>This method should be used only when the actual property instance is unknown.
- * Otherwise, {@link #getMaximalLengthCharacteristic(Property)} should be used because
++ * Otherwise, {@code getMaximalLengthCharacteristic(Property)} should be used because
+ * the maximal length may vary for each property instance.</p>
+ *
+ * @param feature the feature type in which to follow links, or {@code null} if none.
+ * @param attribute the attribute type for which to get the maximal length, or {@code null}.
+ * @return the maximal length characteristic of the given property type, or {@code null} if none.
+ * @throws ClassCastException if {@link #MAXIMAL_LENGTH_CHARACTERISTIC} has been found but is associated
+ * to an object which is not a {@link CoordinateReferenceSystem} instance.
+ */
- public static Integer getMaximalLengthCharacteristic(final FeatureType feature, final PropertyType attribute) {
++ public static Integer getMaximalLengthCharacteristic(final DefaultFeatureType feature, final AbstractIdentifiedType attribute) {
+ return (Integer) getCharacteristic(feature, attribute, MAXIMAL_LENGTH);
+ }
+
+ /**
+ * Returns {@code true} if the given operation or attribute type has a characteristic of the given name,
+ * and the values of that characteristic are assignable to the given {@code valueClass}.
+ *
+ * @param type the operation or attribute type for which to test the existence of a characteristic.
+ * @param name the name of the characteristic to test.
+ * @param valueClass the expected characteristic values.
+ * @return {@code true} if a characteristic of the given name exists and has values assignable to the given class.
+ */
- private static boolean hasCharacteristic(IdentifiedType type, final String name, final Class<?> valueClass) {
- final Optional<AttributeType<?>> at = Features.toAttribute(type);
++ private static boolean hasCharacteristic(AbstractIdentifiedType type, final String name, final Class<?> valueClass) {
++ final Optional<DefaultAttributeType<?>> at = Features.toAttribute(type);
+ if (at.isPresent()) {
- final AttributeType<?> ct = at.get().characteristics().get(name);
++ final DefaultAttributeType<?> ct = at.get().characteristics().get(name);
+ if (ct != null) {
+ return valueClass.isAssignableFrom(ct.getValueClass());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Fetches from the given property the value or default value of the named characteristic.
+ * If the given property is null, or is not an attribute, or does not have characteristics
+ * of the given name, then this method returns {@code null}.
+ *
+ * @param attribute the attribute from which to get the characteristic value or default value, or {@code null}.
+ * @param name name of the characteristic to get.
+ * @return the value or default value of the given characteristic in the given property, or {@code null} if none.
+ */
- private static Object getCharacteristic(final Property attribute, final String name) {
- if (attribute instanceof Attribute<?>) {
- final Attribute<?> at = ((Attribute<?>) attribute).characteristics().get(name);
++ private static Object getCharacteristic(final Object attribute, final String name) {
++ if (attribute instanceof AbstractAttribute<?>) {
++ final AbstractAttribute<?> at = ((AbstractAttribute<?>) attribute).characteristics().get(name);
+ if (at != null) {
+ final Object value = at.getValue();
+ if (value != null) {
+ return value;
+ }
+ }
- final AttributeType<?> type = ((Attribute<?>) attribute).getType().characteristics().get(name);
++ final DefaultAttributeType<?> type = ((AbstractAttribute<?>) attribute).getType().characteristics().get(name);
+ if (type != null) {
+ return type.getDefaultValue();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Fetches from the given property the default value of the characteristic of the given name.
+ * If the given property is a link, then this method follows the link in the given feature type
+ * (unless that feature type is null).
+ *
+ * @param feature the feature type in which to follow links, or {@code null} if none.
+ * @param property the property from which to get the characteristic value, or {@code null}.
+ * @param characteristic name of the characteristic from which to get the default value.
+ * @return the default value of the named characteristic in the given property, or {@code null} if none.
+ */
- private static Object getCharacteristic(final FeatureType feature, PropertyType property, final String characteristic) {
++ private static Object getCharacteristic(final DefaultFeatureType feature, AbstractIdentifiedType property, final String characteristic) {
+ final Optional<String> referent = Features.getLinkTarget(property);
+ if (referent.isPresent() && feature != null) {
+ property = feature.getProperty(referent.get());
+ }
- if (property instanceof AttributeType<?>) {
- final AttributeType<?> type = ((AttributeType<?>) property).characteristics().get(characteristic);
++ if (property instanceof DefaultAttributeType<?>) {
++ final DefaultAttributeType<?> type = ((DefaultAttributeType<?>) property).characteristics().get(characteristic);
+ if (type != null) {
+ return type.getDefaultValue();
+ }
+ }
+ return null;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureExpression.java
index 0000000000,b60a74b5e9..9c1b95725a
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureExpression.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureExpression.java
@@@ -1,0 -1,128 +1,127 @@@
+ /*
+ * 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.feature.privy;
+
+ import java.util.Set;
+ import org.apache.sis.math.FunctionProperty;
+ import org.apache.sis.filter.Optimization;
+ import org.apache.sis.filter.DefaultFilterFactory;
+ import org.apache.sis.feature.builder.FeatureTypeBuilder;
+ import org.apache.sis.feature.builder.PropertyTypeBuilder;
+ import org.apache.sis.feature.builder.AttributeTypeBuilder;
+ import org.apache.sis.filter.internal.Node;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.AttributeType;
-import org.opengis.filter.Literal;
-import org.opengis.filter.Expression;
-import org.opengis.filter.ValueReference;
++// Specific to the main branch:
++import org.apache.sis.filter.Expression;
++import org.apache.sis.feature.DefaultFeatureType;
++import org.apache.sis.pending.geoapi.filter.Literal;
++import org.apache.sis.pending.geoapi.filter.ValueReference;
+
+
+ /**
+ * OGC expressions or other functions operating on feature instances.
+ * This interface adds an additional method, {@link #expectedType(FeatureType, FeatureTypeBuilder)},
+ * for fetching in advance the expected type of expression results.
+ *
+ * <p>This is an experimental interface which may be removed in any future version.</p>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ *
- * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs.
++ * @param <R> the type of resources (e.g. {@code Feature}) used as inputs.
+ * @param <V> the type of values computed by the expression.
+ */
+ public interface FeatureExpression<R,V> extends Expression<R,V> {
+ /**
+ * Returns the manner in which values are computed from given resources.
+ * The default implementation combines the properties of all parameters.
+ *
+ * @return the manners in which values are computed from resources.
+ */
+ default Set<FunctionProperty> properties() {
+ return Node.transitiveProperties(getParameters());
+ }
+
+ /**
+ * Returns the type of values computed by this expression, or {@code Object.class} if unknown.
+ *
+ * <h4>Note on type safety</h4>
+ * The parameterized type should be {@code <? extends V>} because some implementations get this
+ * information by a call to {@code value.getClass()}. But it should also be {@code <? super V>}
+ * for supporting the {@code Object.class} return value. Those contradictory requirements force
+ * us to use {@code <?>}.
+ *
+ * @return the type of values computed by this expression.
+ */
+ default Class<?> getValueClass() {
+ return Object.class;
+ }
+
+ /**
+ * Provides the expected type of values produced by this expression when a feature of the given
- * type is evaluated. The resulting type shall describe a "static" property, i.e. it can be an
- * {@link AttributeType} or a {@link org.opengis.feature.FeatureAssociationRole}
- * but not an {@link org.opengis.feature.Operation}.
++ * type is evaluated. The resulting type shall describe a "static" property, i.e. it can be a
++ * {@link org.apache.sis.feature.DefaultAttributeType} or a {@link org.apache.sis.feature.DefaultAssociationRole}
++ * but not an {@link org.apache.sis.feature.AbstractOperation}.
+ *
+ * <p>If this method returns an instance of {@link AttributeTypeBuilder}, then its parameterized
+ * type should be the same {@code <V>} than this {@code FeatureExpression}.</p>
+ *
+ * @param valueType the type of features to be evaluated by the given expression.
+ * @param addTo where to add the type of properties evaluated by this expression.
+ * @return builder of the added property, or {@code null} if this method cannot add a property.
+ * @throws IllegalArgumentException if this method can operate only on some feature types
+ * and the given type is not one of them.
+ */
- PropertyTypeBuilder expectedType(FeatureType valueType, FeatureTypeBuilder addTo);
++ PropertyTypeBuilder expectedType(DefaultFeatureType valueType, FeatureTypeBuilder addTo);
+
+ /**
+ * Tries to cast or convert the given expression to a {@link FeatureExpression}.
+ * If the given expression cannot be casted, then this method creates a copy
+ * provided that the expression is one of the following type:
+ *
+ * <ol>
+ * <li>{@link Literal}.</li>
+ * <li>{@link ValueReference}, assuming that the expression expects feature instances.</li>
+ * </ol>
+ *
+ * Otherwise this method returns {@code null}.
+ * It is caller's responsibility to verify if this method returns {@code null} and to throw an exception in such case.
+ * We leave that responsibility to the caller because (s)he may be able to provide better error messages.
+ *
+ * <h4>Note on type safety</h4>
+ * This method does not use parameterized types because of the assumption on {@link ValueReference}.
+ * As of Apache SIS 1.3, we have no way to check if {@code <R>} is for feature instances.
+ *
+ * @param candidate the expression to cast or copy. Can be null.
+ * @return the given expression as a feature expression, or {@code null} if it cannot be casted or converted.
+ */
+ public static FeatureExpression<?,?> castOrCopy(final Expression<?,?> candidate) {
+ if (candidate instanceof FeatureExpression<?,?>) {
+ return (FeatureExpression<?,?>) candidate;
+ }
+ final Expression<?,?> copy;
+ if (candidate instanceof Literal<?,?>) {
+ copy = Optimization.literal(((Literal<?,?>) candidate).getValue());
+ } else if (candidate instanceof ValueReference<?,?>) {
+ final String xpath = ((ValueReference<?,?>) candidate).getXPath();
+ copy = DefaultFilterFactory.forFeatures().property(xpath);
+ } else {
+ return null;
+ }
+ return (FeatureExpression<?,?>) copy;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureUtilities.java
index 0000000000,b089754d1a..50aca20234
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureUtilities.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/FeatureUtilities.java
@@@ -1,0 -1,92 +1,92 @@@
+ /*
+ * 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.feature.privy;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.Iterator;
+ import java.util.Collection;
+ import java.util.ConcurrentModificationException;
+ import org.opengis.util.GenericName;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.parameter.ParameterDescriptor;
+ import org.opengis.parameter.ParameterDescriptorGroup;
+ import org.apache.sis.parameter.DefaultParameterDescriptorGroup;
+ import org.apache.sis.metadata.iso.citation.Citations;
+ import org.apache.sis.util.Static;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.PropertyType;
++// Specific to the main branch:
++import org.apache.sis.feature.AbstractIdentifiedType;
+
+
+ /**
+ * Non-public utility methods for Apache SIS internal usage.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class FeatureUtilities extends Static {
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private FeatureUtilities() {
+ }
+
+ /**
+ * Creates a parameter descriptor in the Apache SIS namespace. This convenience method shall
+ * not be in public API, because users should define operations in their own namespace.
+ *
+ * @param name the parameter group name, typically the same as operation name.
+ * @param parameters the parameters, or an empty array if none.
+ * @return description of the parameters group.
+ */
+ public static ParameterDescriptorGroup parameters(final String name, final ParameterDescriptor<?>... parameters) {
+ final Map<String,Object> properties = new HashMap<>(4);
+ properties.put(ParameterDescriptorGroup.NAME_KEY, name);
+ properties.put(Identifier.AUTHORITY_KEY, Citations.SIS);
+ return new DefaultParameterDescriptorGroup(properties, 1, 1);
+ }
+
+ /**
+ * Gets the name of all given properties. If any property is null or has a null name,
+ * then the corresponding entry in the returned array will be null.
+ *
+ * @param properties the properties for which to get the names, or {@code null}.
+ * @return the name of all given properties, or {@code null} if the given list was null.
+ */
- public static String[] getNames(final Collection<? extends PropertyType> properties) {
++ public static String[] getNames(final Collection<? extends AbstractIdentifiedType> properties) {
+ if (properties == null) {
+ return null;
+ }
+ final String[] names = new String[properties.size()];
- final Iterator<? extends PropertyType> it = properties.iterator();
++ final Iterator<? extends AbstractIdentifiedType> it = properties.iterator();
+ for (int i=0; i < names.length; i++) {
- final PropertyType property = it.next();
++ final AbstractIdentifiedType property = it.next();
+ if (property != null) {
+ final GenericName name = property.getName();
+ if (name != null) {
+ names[i] = name.toString();
+ }
+ }
+ }
+ // Should not have any element left, unless collection size changed during iteration.
+ if (it.hasNext()) {
+ throw new ConcurrentModificationException();
+ }
+ return names;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/MovingFeatures.java
index 0000000000,c468b4d0dd..5a78fafad0
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/MovingFeatures.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/privy/MovingFeatures.java
@@@ -1,0 -1,130 +1,129 @@@
+ /*
+ * 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.feature.privy;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.time.Instant;
+ import org.apache.sis.util.iso.Names;
+ import org.apache.sis.math.Vector;
+ import org.apache.sis.feature.DefaultAttributeType;
+ import org.apache.sis.referencing.crs.DefaultTemporalCRS;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Attribute;
-import org.opengis.feature.AttributeType;
++// Specific to the main branch:
++import org.apache.sis.feature.AbstractAttribute;
+
+
+ /**
+ * Utility methods for instantiating features where the geometry is a trajectory
+ * and some property values may change with time.
+ *
+ * This class is <strong>not</strong> thread-safe.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public class MovingFeatures {
+ /**
+ * Definition of characteristics containing a list of instants, without duplicates.
+ * Should be in chronological order, but this is not verified.
+ */
- public static final AttributeType<Instant> TIME_AS_INSTANTS;
++ public static final DefaultAttributeType<Instant> TIME_AS_INSTANTS;
+
+ /**
+ * An alternative to {@link #TIME_AS_INSTANTS} used when times cannot be mapped to calendar dates.
+ * This characteristic uses the same name as {@code TIME_AS_INSTANTS}. Consequently, at most one
+ * of {@code TIME_AS_INSTANTS} and {@code TIME_AS_NUMBERS} can be used on the same property.
+ */
- private static final AttributeType<Number> TIME_AS_NUMBERS;
++ private static final DefaultAttributeType<Number> TIME_AS_NUMBERS;
+ static {
+ final var scope = Names.createLocalName("OGC", null, "MF");
+ final var properties = Map.of(DefaultAttributeType.NAME_KEY, Names.createScopedName(scope, null, "datetimes"));
+ TIME_AS_INSTANTS = new DefaultAttributeType<>(properties, Instant.class, 0, Integer.MAX_VALUE, null);
+ TIME_AS_NUMBERS = new DefaultAttributeType<>(properties, Number.class, 0, Integer.MAX_VALUE, null);
+ }
+
+ /**
+ * Returns the "datetimes" characteristic to add on an attribute type.
+ * The characteristic will expect either {@link Instant} or {@link Number} values,
+ * depending on whether a temporal CRS is available or not.
+ *
+ * @param hasCRS whether a temporal CRS is available.
+ * @return the "datetimes" characteristic.
+ */
- public static AttributeType<?> characteristic(final boolean hasCRS) {
++ public static DefaultAttributeType<?> characteristic(final boolean hasCRS) {
+ return hasCRS ? TIME_AS_INSTANTS : TIME_AS_NUMBERS;
+ }
+
+ /**
+ * Caches of list of times or instants, used for sharing existing instances.
+ * We do this sharing because it is common to have many properties having the same time characteristics.
+ */
+ private final Map<Vector,InstantList> cache;
+
+ /**
+ * Creates a new builder.
+ *
+ * @param share other builder that may share time vectors, or {@code null} if none.
+ */
+ public MovingFeatures(final MovingFeatures share) {
+ cache = (share != null) ? share.cache : new HashMap<>();
+ }
+
+ /**
+ * Sets the "datetimes" characteristic on the given attribute as a list of {@link Instant} instances.
+ * Should be in chronological order, but this is not verified.
+ *
+ * @param dest the attribute on which to set time characteristic.
+ * @param millis times in milliseconds since the epoch.
+ */
- public final void setInstants(final Attribute<?> dest, final long[] millis) {
- final Attribute<Instant> c = TIME_AS_INSTANTS.newInstance();
++ public final void setInstants(final AbstractAttribute<?> dest, final long[] millis) {
++ final AbstractAttribute<Instant> c = TIME_AS_INSTANTS.newInstance();
+ c.setValues(cache.computeIfAbsent(InstantList.vectorize(millis), InstantList::new));
+ dest.characteristics().values().add(c);
+ }
+
+ /**
+ * Sets the "datetimes" characteristic on the given attribute.
+ * If the {@code converter} is non-null, it will be used for converting values to {@link Instant} instances.
+ * Otherwise values are stored as-is as time elapsed in arbitrary units since an arbitrary epoch.
+ *
+ * <p>Values should be in chronological order, but this is not verified.
+ * Current implementation does not cache the values, but this policy may be revisited in a future version.</p>
+ *
+ * @param dest the attribute on which to set time characteristic.
+ * @param values times in arbitrary units since an arbitrary epoch.
+ * @param converter the CRS to use for converting values to {@link Instant} instances, or {@code null}.
+ */
- public static void setTimes(final Attribute<?> dest, final Vector values, final DefaultTemporalCRS converter) {
- final Attribute<?> ct;
++ public static void setTimes(final AbstractAttribute<?> dest, final Vector values, final DefaultTemporalCRS converter) {
++ final AbstractAttribute<?> ct;
+ if (converter != null) {
+ final Instant[] instants = new Instant[values.size()];
+ for (int i=0; i<instants.length; i++) {
+ instants[i] = converter.toInstant(values.doubleValue(i));
+ }
- final Attribute<Instant> c = TIME_AS_INSTANTS.newInstance();
++ final AbstractAttribute<Instant> c = TIME_AS_INSTANTS.newInstance();
+ c.setValues(UnmodifiableArrayList.wrap(instants));
+ ct = c;
+ } else {
- final Attribute<Number> c = TIME_AS_NUMBERS.newInstance();
++ final AbstractAttribute<Number> c = TIME_AS_NUMBERS.newInstance();
+ c.setValues(values);
+ ct = c;
+ }
+ dest.characteristics().values().add(ct);
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
index 15620e9101,f35237d4d7..42def4b9b3
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/BinaryGeometryFilter.java
@@@ -25,13 -25,19 +25,13 @@@ import org.opengis.referencing.operatio
import org.apache.sis.geometry.wrapper.Geometries;
import org.apache.sis.geometry.wrapper.GeometryWrapper;
import org.apache.sis.geometry.wrapper.SpatialOperationContext;
- import org.apache.sis.feature.internal.AttributeConvention;
+ import org.apache.sis.feature.privy.AttributeConvention;
import org.apache.sis.filter.internal.Node;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Filter;
-import org.opengis.filter.Literal;
-import org.opengis.filter.Expression;
-import org.opengis.filter.ValueReference;
-import org.opengis.filter.SpatialOperator;
-import org.opengis.filter.BinarySpatialOperator;
-import org.opengis.filter.InvalidFilterValueException;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyNotFoundException;
+// Specific to the main branch:
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.pending.geoapi.filter.Literal;
+import org.apache.sis.pending.geoapi.filter.ValueReference;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
index 0000000000,1789b46fb0..810ede286f
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/FunctionRegister.java
@@@ -1,0 -1,78 +1,63 @@@
+ /*
+ * 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.filter;
+
+ import java.util.Collection;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Expression;
-import org.opengis.filter.capability.AvailableFunction;
-
+
+ /**
+ * A factory of {@code org.opengis.filter} functions identified by their names.
+ * Each factory can provide an arbitrary number of functions, enumerated by {@link #getNames()}.
+ * The {@link org.apache.sis.filter.DefaultFilterFactory#function(String, Expression...)} method
+ * delegates to this interface for creating the function implementation for a given name.
+ *
+ * <p><b>Warning:</b> there is currently no mechanism for avoiding name collision.
+ * It is implementer responsibility to keep trace of the whole universe of functions and avoid collision.
+ * Checks against name collisions may be added in a future version.</p>
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.5
+ * @since 1.5
- *
- * @see org.opengis.filter.FilterFactory#function(String, Expression...)
+ */
+ public interface FunctionRegister {
+ /**
+ * Returns the name of the standard or authority defining the functions.
+ *
+ * @return provider of function definitions.
+ */
+ String getAuthority();
+
+ /**
+ * Returns the names of all functions that this factory can create.
+ * It is currently implementer responsibility to ensure that there are no name collisions with
+ * functions provided by other factories (this problem may be improved in future SIS release).
+ *
+ * @return set of supported function names.
+ */
+ Collection<String> getNames();
+
- /**
- * Describes the parameters of a function.
- *
- * @param name name of the function to describe (not null).
- * @return description of the function parameters.
- * @throws IllegalArgumentException if function name is unknown..
- */
- AvailableFunction describe(String name) throws IllegalArgumentException;
-
+ /**
+ * Creates a new function of the given name with given parameters.
+ *
- * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs.
++ * @param <R> the type of resources (e.g. {@code Feature}) used as inputs.
+ * @param name name of the function to create (not null).
+ * @param parameters function parameters.
+ * @return function for the given name and parameters.
+ * @throws IllegalArgumentException if function name is unknown or some parameters are illegal.
+ */
+ <R> Expression<R,?> create(String name, Expression<R,?>[] parameters) throws IllegalArgumentException;
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/IdentifierFilter.java
index 348936ea2e,54bf782cf1..4d3919e7a1
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/IdentifierFilter.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/IdentifierFilter.java
@@@ -20,10 -20,13 +20,10 @@@ import java.util.List
import java.util.Collection;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.filter.internal.Node;
- import org.apache.sis.feature.internal.AttributeConvention;
+ import org.apache.sis.feature.privy.AttributeConvention;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.filter.Expression;
-import org.opengis.filter.ResourceId;
-import org.opengis.filter.Filter;
+// Specific to the main branch:
+import org.apache.sis.feature.AbstractFeature;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
index dde264f0d5,42ef29a5cb..6127180fbf
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/LogicalFilter.java
@@@ -22,12 -22,13 +22,12 @@@ import java.util.LinkedHashSet
import java.util.Objects;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.filter.internal.Node;
- import org.apache.sis.util.internal.CollectionsExt;
- import org.apache.sis.util.internal.UnmodifiableArrayList;
+ import org.apache.sis.util.privy.CollectionsExt;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Filter;
-import org.opengis.filter.LogicalOperator;
-import org.opengis.filter.LogicalOperatorName;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.filter.LogicalOperator;
+import org.apache.sis.pending.geoapi.filter.LogicalOperatorName;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
index 1392b878e3,5a396dc0b4..40c624b6a4
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/Optimization.java
@@@ -27,13 -27,15 +27,13 @@@ import org.opengis.util.CodeList
import org.apache.sis.math.FunctionProperty;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.filter.internal.Node;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.util.privy.CollectionsExt;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Filter;
-import org.opengis.filter.Literal;
-import org.opengis.filter.Expression;
-import org.opengis.filter.LogicalOperator;
-import org.opengis.filter.LogicalOperatorName;
-import org.opengis.feature.FeatureType;
+// Specific to the main branch:
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.pending.geoapi.filter.Literal;
+import org.apache.sis.pending.geoapi.filter.LogicalOperator;
+import org.apache.sis.pending.geoapi.filter.LogicalOperatorName;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
index 1b8a87fd08,bb62d7559c..9fc8e48551
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java
@@@ -26,18 -26,18 +26,18 @@@ import org.apache.sis.util.Unconvertibl
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.apache.sis.feature.builder.PropertyTypeBuilder;
import org.apache.sis.feature.builder.AttributeTypeBuilder;
- import org.apache.sis.filter.internal.XPath;
+ import org.apache.sis.filter.privy.XPath;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.Feature;
-import org.opengis.feature.FeatureType;
-import org.opengis.feature.PropertyType;
-import org.opengis.feature.AttributeType;
-import org.opengis.feature.IdentifiedType;
-import org.opengis.feature.Operation;
-import org.opengis.feature.PropertyNotFoundException;
-import org.opengis.filter.ValueReference;
+// Specific to the main branch:
+import org.opengis.util.ScopedName;
+import org.apache.sis.feature.AbstractFeature;
+import org.apache.sis.feature.AbstractIdentifiedType;
+import org.apache.sis.feature.AbstractOperation;
+import org.apache.sis.feature.DefaultAttributeType;
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.pending.geoapi.filter.Name;
+import org.apache.sis.pending.geoapi.filter.ValueReference;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/FunctionNames.java
index 0000000000,06be9b3463..ad709c9195
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/FunctionNames.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/FunctionNames.java
@@@ -1,0 -1,89 +1,89 @@@
+ /*
+ * 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.filter.privy;
+
+ import org.apache.sis.util.Static;
+ import org.apache.sis.filter.sqlmm.SQLMM;
+
+
+ /**
+ * Names of some expressions used in Apache SIS.
+ * This class defines only the names that need to be referenced from at least two different classes.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class FunctionNames extends Static {
- /** Value of {@link org.opengis.filter.NullOperator#getOperatorType()}. */
++ /** Value of {@code NullOperator.getOperatorType()}. */
+ public static final String PROPERTY_IS_NULL = "PROPERTY_IS_NULL";
+
- /** Value of {@link org.opengis.filter.NilOperator#getOperatorType()}. */
++ /** Value of {@code NilOperator.getOperatorType()}. */
+ public static final String PROPERTY_IS_NIL = "PROPERTY_IS_NIL";
+
- /** Value of {@link org.opengis.filter.LikeOperator#getOperatorType()}. */
++ /** Value of {@code LikeOperator.getOperatorType()}. */
+ public static final String PROPERTY_IS_LIKE = "PROPERTY_IS_LIKE";
+
+ /** Value of {@link org.opengis.filter.BetweenComparisonOperator#getOperatorType()}. */
+ public static final String PROPERTY_IS_BETWEEN = "PROPERTY_IS_BETWEEN";
+
- /** Value of {@link org.opengis.filter.Literal#getFunctionName()}. */
++ /** Value of {@code Literal.getFunctionName()}. */
+ public static final String Literal = "Literal";
+
- /** Value of {@link org.opengis.filter.ValueReference#getFunctionName()}. */
++ /** Value of {@code ValueReference.getFunctionName()}. */
+ public static final String ValueReference = "ValueReference";
+
+ /** The "Add" (+) arithmetic expression. */
+ public static final String Add = "Add";
+
+ /** The "Subtract" (−) arithmetic expression. */
+ public static final String Subtract = "Subtract";
+
+ /** The "Multiply" (×) arithmetic expression. */
+ public static final String Multiply = "Multiply";
+
+ /** The "Divide" (÷) arithmetic expression. */
+ public static final String Divide = "Divide";
+
+ /** Name of {@link SQLMM#ST_Contains}. */
+ public static final String ST_Contains = "ST_Contains";
+
+ /** Name of {@link SQLMM#ST_Crosses}. */
+ public static final String ST_Crosses = "ST_Crosses";
+
+ /** Name of {@link SQLMM#ST_Disjoint}. */
+ public static final String ST_Disjoint = "ST_Disjoint";
+
+ /** Name of {@link SQLMM#ST_Equals}. */
+ public static final String ST_Equals = "ST_Equals";
+
+ /** Name of {@link SQLMM#ST_Intersects}. */
+ public static final String ST_Intersects = "ST_Intersects";
+
+ /** Name of {@link SQLMM#ST_Overlaps}. */
+ public static final String ST_Overlaps = "ST_Overlaps";
+
+ /** Name of {@link SQLMM#ST_Touches}. */
+ public static final String ST_Touches = "ST_Touches";
+
+ /** Name of {@link SQLMM#ST_Within}. */
+ public static final String ST_Within = "ST_Within";
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private FunctionNames() {
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/SortByComparator.java
index 0000000000,624e60d7c2..9c1d3a5db4
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/SortByComparator.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/SortByComparator.java
@@@ -1,0 -1,203 +1,203 @@@
+ /*
+ * 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.filter.privy;
+
+ import java.util.Arrays;
+ import java.util.List;
+ import java.util.Map;
+ import java.util.LinkedHashMap;
+ import java.util.Comparator;
+ import java.io.Serializable;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.SortBy;
-import org.opengis.filter.SortProperty;
-import org.opengis.filter.ValueReference;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.filter.SortBy;
++import org.apache.sis.pending.geoapi.filter.SortProperty;
++import org.apache.sis.pending.geoapi.filter.ValueReference;
+
+
+ /**
+ * Comparator sorting features using an array of {@link SortProperty} elements applied in order.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @param <R> the type of resources (typically {@code Feature}) to sort.
+ */
+ public final class SortByComparator<R> implements SortBy<R>, Serializable {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -7964849249532212389L;
+
+ /**
+ * The sort order specified to the constructor.
+ *
+ * @see #getSortProperties()
+ */
+ @SuppressWarnings("serial") // Most SIS implementations are serializable.
+ private final SortProperty<R>[] properties;
+
+ /**
+ * Creates a new comparator for the given sort expression.
+ * This is shortcut for the common case where there is a single expression.
+ *
+ * @param p the property to wrap in a {@link SortBy} comparator.
+ */
+ @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
+ private SortByComparator(final SortProperty<R> p) {
+ ArgumentChecks.ensureNonNullElement("properties", 0, p);
+ properties = new SortProperty[] {p};
+ }
+
+ /**
+ * Creates a new comparator with the values of given map.
+ * The map is used for removing duplicated expressions.
+ */
+ @SuppressWarnings({"unchecked","rawtypes"}) // Generic array creation.
+ private SortByComparator(final Map<?, SortProperty<R>> merged) {
+ properties = merged.values().toArray(SortProperty[]::new);
+ }
+
+ /**
+ * Creates a new comparator for the given sort expressions.
+ *
+ * @param <R> the type of resources (typically {@code Feature}) to sort.
+ * @param properties the sort order.
+ * @return the comparator, or {@code null} if the given array is empty.
+ */
+ public static <R> SortByComparator<R> create(final SortProperty<R>[] properties) {
+ switch (properties.length) {
+ case 0: return null;
+ case 1: return new SortByComparator<>(properties[0]);
+ }
+ final Map<ValueReference<R,?>, SortProperty<R>> merged = new LinkedHashMap<>();
+ addAll(Arrays.asList(properties), merged);
+ return new SortByComparator<>(merged);
+ }
+
+ /**
+ * Creates a new comparator as the concatenation of the two given comparators.
+ * The first comparator is used first, and if two resources are equal then the
+ * second comparator is used.
+ *
+ * @param <R> the type of resources (typically {@code Feature}) to sort.
+ * @param sort the first "sort by" to use, or {@code null} if none.
+ * @param comparator the second "sort by" to use.
+ * @return concatenation of the two comparators.
+ */
+ @SuppressWarnings("unchecked")
+ public static <R> SortBy<? super R> concatenate(final SortBy<? super R> sort, final Comparator<? super R> comparator) {
+ final SortBy<? super R> other;
+ if (comparator instanceof SortBy<?>) {
+ other = (SortBy<? super R>) comparator;
+ } else if (comparator instanceof SortProperty<?>) {
+ other = new SortByComparator<>((SortProperty<? super R>) comparator);
+ } else {
+ return null;
+ }
+ if (sort == null) {
+ return other;
+ }
+ /*
+ * The (SortBy<R>) casts are unsafe — they should be (SortBy<? super R>) — but are okay in this context
+ * because we create a `SortByComparator` which will only "push" instances of R to the comparators.
+ * There is no code that "pull" instances of R from the comparators, consequently no code that may
+ * be surprised to get an instance of a super type of R instead of R.
+ */
+ return concatenate((SortBy<R>) sort, (SortBy<R>) other);
+ }
+
+ /**
+ * Creates a new comparator as the concatenation of the two given comparators.
+ * The first comparator is used first, and if two resources are equal then the
+ * second comparator is used.
+ *
+ * @param <R> the type of resources (typically {@code Feature}) to sort.
+ * @param s1 the first "sort by" to use.
+ * @param s2 the second "sort by" to use.
+ * @return concatenation of the two comparators.
+ */
+ public static <R> SortBy<R> concatenate(final SortBy<R> s1, final SortBy<R> s2) {
+ final Map<ValueReference<R,?>, SortProperty<R>> merged = new LinkedHashMap<>();
+ addAll(s1.getSortProperties(), merged);
+ addAll(s2.getSortProperties(), merged);
+ return new SortByComparator<>(merged);
+ }
+
+ /**
+ * Adds all elements of the {@code properties} list into the {@code merged} map.
+ * If two {@code SortProperty} instances use the same {@link ValueReference},
+ * then only the first occurrence is retained.
+ */
+ private static <R> void addAll(final List<SortProperty<R>> properties,
+ final Map<ValueReference<R,?>, SortProperty<R>> merged)
+ {
+ final int size = properties.size();
+ for (int i=0; i<size; i++) {
+ final SortProperty<R> p = properties.get(i);
+ ArgumentChecks.ensureNonNullElement("properties", i, p);
+ merged.putIfAbsent(p.getValueReference(), p);
+ }
+ }
+
+ /**
+ * Returns the properties whose values are used for sorting.
+ * The list shall have a minimum of one element.
+ */
+ @Override
+ public List<SortProperty<R>> getSortProperties() {
+ return UnmodifiableArrayList.wrap(properties);
+ }
+
+ /**
+ * Compares two resources for order. Returns a negative number if {@code r1} should be sorted before {@code r2},
+ * a positive number if {@code r2} should be after {@code r1}, or 0 if both resources are equal.
+ * The ordering of null resources or null property values is unspecified.
+ */
+ @Override
+ public int compare(final R r1, final R r2) {
+ for (final SortProperty<R> p : properties) {
+ final int c = p.compare(r1, r2);
+ if (c != 0) return c;
+ }
+ return 0;
+ }
+
+ /**
+ * Returns a comparator as the concatenation of this comparator with the given one.
+ * This comparator is used first, and if two resources are equal then the other comparator is used.
+ *
+ * @param other the other comparator to be used when this comparator considers two resources as equal.
+ * @return concatenation of this comparator with the given one.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public Comparator<R> thenComparing(final Comparator<? super R> other) {
+ if (other instanceof SortBy<?>) {
+ /*
+ * The (SortBy<R>) cast is unsafe — it should be (SortBy<? super R>) — but it is okay in this context
+ * because we create a `SortByComparator` which will only "push" instances of R to the `other` comparator.
+ * There is no code that "pull" instances of R from the `other` comparator, consequently no code that may
+ * be surprised to get an instance of a super type of R instead of R.
+ */
+ return concatenate(this, (SortBy<R>) other);
+ }
+ return SortBy.super.thenComparing(other);
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
index 0000000000,b314efe3de..f6b47d6f17
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/privy/Visitor.java
@@@ -1,0 -1,329 +1,328 @@@
+ /*
+ * 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.filter.privy;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.Collection;
+ import java.util.Collections;
+ import java.util.function.BiConsumer;
+ import org.apache.sis.feature.internal.Resources;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.CodeList;
-import org.opengis.filter.Filter;
-import org.opengis.filter.Expression;
-import org.opengis.filter.LogicalOperatorName;
-import org.opengis.filter.SpatialOperatorName;
-import org.opengis.filter.DistanceOperatorName;
-import org.opengis.filter.TemporalOperatorName;
-import org.opengis.filter.ComparisonOperatorName;
++// Specific to the main branch:
++import org.apache.sis.filter.Filter;
++import org.apache.sis.filter.Expression;
++import org.apache.sis.pending.geoapi.filter.LogicalOperatorName;
++import org.apache.sis.pending.geoapi.filter.SpatialOperatorName;
++import org.apache.sis.pending.geoapi.filter.DistanceOperatorName;
++import org.apache.sis.pending.geoapi.filter.TemporalOperatorName;
++import org.apache.sis.pending.geoapi.filter.ComparisonOperatorName;
+
+
+ /**
+ * An executor of different actions depending on filter or expression type.
+ * Action are defined by {@link BiConsumer} where the first parameter is the filter or expression,
+ * and the second parameter is an arbitrary object used as accumulator. For example, the accumulator
+ * may be a {@link StringBuilder} where the filter is written as a SQL or CQL statement.
+ *
+ * <h2>Relationship with the visitor pattern</h2>
+ * This class provides similar functionalities than the "visitor pattern".
+ * The actions are defined by lambda functions in a {@link HashMap} instead of by overriding methods,
+ * but the results are similar.
+ *
+ * <h2>Thread-safety</h2>
+ * {@code Visitor} instances are thread-safe if protected methods are invoked at construction time only.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
- * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) used as inputs.
++ * @param <R> the type of resources (e.g. {@code Feature}) used as inputs.
+ * @param <A> type of the accumulator object where actions will write their results.
+ */
+ public abstract class Visitor<R,A> {
+ /**
+ * All filters known to this visitor.
+ * May contain an entry associated to the {@code null} key.
+ *
- * @see #setFilterHandler(CodeList, BiConsumer)
++ * @see #setFilterHandler(Enum, BiConsumer)
+ */
- private final Map<CodeList<?>, BiConsumer<Filter<R>,A>> filters;
++ private final Map<Enum<?>, BiConsumer<Filter<R>,A>> filters;
+
+ /**
+ * All expressions known to this visitor.
+ * May contain an entry associated to the {@code null} key.
+ *
+ * @see #setExpressionHandler(String, BiConsumer)
+ */
+ private final Map<String, BiConsumer<Expression<R,?>,A>> expressions;
+
+ /**
+ * Creates a new visitor.
+ */
+ protected Visitor() {
+ filters = new HashMap<>();
+ expressions = new HashMap<>();
+ }
+
+ /**
+ * Creates a new visitor which will accept only the specified type of objects.
+ *
+ * @param hasFilters whether this filter will accepts filters.
+ * @param hasExpressions whether this filter will accepts expressions.
+ */
+ protected Visitor(final boolean hasFilters, final boolean hasExpressions) {
+ filters = hasFilters ? new HashMap<>() : Collections.emptyMap();
+ expressions = hasExpressions ? new HashMap<>() : Collections.emptyMap();
+ }
+
+ /**
+ * Creates a new visitor initialized to the same handlers as the specified visitor.
+ * This constructor can be used for creating a copy from a template before to add or remove handlers.
+ * If any of the {@code copy} argument is {@code false}, it is caller's responsibility to not modify
+ * the corresponding map of handlers because it will be an instance shared with the {@code source}.
+ *
+ * @param source the visitor from which to copy the handlers.
+ * @param copyFilters whether to copy the map of filter handlers.
+ * @param copyExpressions whether to copy the map of expression handlers.
+ *
+ * @see #removeFilterHandlers(Collection)
+ */
+ protected Visitor(final Visitor<R,A> source, final boolean copyFilters, final boolean copyExpressions) {
+ filters = copyFilters ? new HashMap<>(source.filters) : source.filters;
+ expressions = copyExpressions ? new HashMap<>(source.expressions) : source.expressions;
+ }
+
+ /**
+ * Returns the action to execute for the given type of filter.
+ * The {@code null} type is legal and identifies the action to execute
+ * when the {@link Filter} instance is null or has a null type.
+ *
+ * @param type identification of the filter type (can be {@code null}).
+ * @return the action to execute when the identified filter is found, or {@code null} if none.
+ */
- protected final BiConsumer<Filter<R>,A> getFilterHandler(final CodeList<?> type) {
++ protected final BiConsumer<Filter<R>,A> getFilterHandler(final Enum<?> type) {
+ return filters.get(type);
+ }
+
+ /**
+ * Returns the action to execute for the given type of expression.
+ * The {@code null} type is legal and identifies the action to execute
+ * when the {@link Expression} instance is null.
+ *
+ * @param type identification of the expression type (can be {@code null}).
+ * @return the action to execute when the identified expression is found, or {@code null} if none.
+ */
+ protected final BiConsumer<Expression<R,?>,A> getExpressionHandler(final String type) {
+ return expressions.get(type);
+ }
+
+ /**
+ * Sets the action to execute for the given type of filter.
+ * The {@code null} type is legal and identifies the action to execute
+ * when the {@link Filter} instance is null or has a null type.
+ *
+ * @param type identification of the filter type (can be {@code null}).
+ * @param action the action to execute when the identified filter is found.
+ */
- protected final void setFilterHandler(final CodeList<?> type, final BiConsumer<Filter<R>,A> action) {
++ protected final void setFilterHandler(final Enum<?> type, final BiConsumer<Filter<R>,A> action) {
+ filters.put(type, action);
+ }
+
+ /**
+ * Sets the same action for all member of the same family of filters.
+ * The action is set in enumeration declaration order up to the last type inclusive.
+ *
+ * @param lastType identification of the last filter type (inclusive).
+ * @param action the action to execute when an identified filter is found.
+ */
- private void setFamilyHandlers(final CodeList<?> lastType, final BiConsumer<Filter<R>,A> action) {
- for (final CodeList<?> type : lastType.family()) {
++ private void setFamilyHandlers(final Enum<?> lastType, final BiConsumer<Filter<R>,A> action) {
++ for (final Enum<?> type : lastType.getClass().getEnumConstants()) {
+ filters.put(type, action);
+ if (type == lastType) break;
+ }
+ }
+
+ /**
+ * Sets the action to execute for the given type of expression.
+ * The {@code null} type is legal and identifies the action to execute
+ * when the {@link Expression} instance is null.
+ *
+ * @param type identification of the expression type (can be {@code null}).
+ * @param action the action to execute when the identified expression is found.
+ */
+ protected final void setExpressionHandler(final String type, final BiConsumer<Expression<R,?>,A> action) {
+ expressions.put(type, action);
+ }
+
+ /**
+ * Sets the same action to execute for the given types of expression.
+ *
+ * @param types identification of the expression types.
+ * @param action the action to execute when the identified expression is found.
+ */
+ private void setExpressionHandlers(final BiConsumer<Expression<R,?>,A> action, final String... types) {
+ for (final String type : types) {
+ expressions.put(type, action);
+ }
+ }
+
+ /**
+ * Sets the same action to execute for the {@code AND}, {@code OR} and {@code NOT} types filter.
+ *
+ * @param action the action to execute when one of the enumerated filters is found.
+ */
+ protected final void setLogicalHandlers(final BiConsumer<Filter<R>,A> action) {
+ setFamilyHandlers(LogicalOperatorName.NOT, action);
+ }
+
+ /**
+ * Sets the same action for both the {@code IsNull} and {@code IsNil} types of filter.
+ *
+ * @param action the action to execute when one of the enumerated filters is found.
+ */
+ protected final void setNullAndNilHandlers(final BiConsumer<Filter<R>,A> action) {
- setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NULL), action);
- setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NIL), action);
++ setFilterHandler(ComparisonOperatorName.PROPERTY_IS_NULL, action);
++ setFilterHandler(ComparisonOperatorName.PROPERTY_IS_NIL, action);
+ }
+
+ /**
+ * Sets the same action to execute for the <, >, ≤, ≥, = and ≠ types of filter.
+ *
+ * @param action the action to execute when one of the enumerated filters is found.
+ */
+ protected final void setBinaryComparisonHandlers(final BiConsumer<Filter<R>,A> action) {
+ setFamilyHandlers(ComparisonOperatorName.PROPERTY_IS_GREATER_THAN_OR_EQUAL_TO, action);
+ }
+
+ /**
+ * Sets the same action to execute for the temporal comparison operators.
+ * The operators are {@code AFTER}, {@code BEFORE}, {@code BEGINS}, {@code BEGUN_BY}, {@code CONTAINS},
+ * {@code DURING}, {@code EQUALS}, {@code OVERLAPS}, {@code MEETS}, {@code ENDS}, {@code OVERLAPPED_BY},
+ * {@code MET_BY}, {@code ENDED_BY} and {@code ANY_INTERACTS} types filter.
+ *
+ * @param action the action to execute when one of the enumerated filters is found.
+ */
+ protected final void setBinaryTemporalHandlers(final BiConsumer<Filter<R>,A> action) {
+ setFamilyHandlers(TemporalOperatorName.ANY_INTERACTS, action);
+ }
+
+ /**
+ * Sets the same action to execute for the spatial comparison operators, including the ones
+ * with a distance parameter. The operators are {@code BBOX}, {@code EQUALS}, {@code DISJOINT},
+ * {@code INTERSECTS}, {@code TOUCHES}, {@code CROSSES}, {@code WITHIN}, {@code CONTAINS},
+ * {@code OVERLAPS}, {@code DWITHIN} and {@code BEYOND} types filter.
+ *
+ * @param action the action to execute when one of the enumerated filters is found.
+ */
+ protected final void setSpatialHandlers(final BiConsumer<Filter<R>,A> action) {
+ setFamilyHandlers(SpatialOperatorName.OVERLAPS, action);
+ setFamilyHandlers(DistanceOperatorName.WITHIN, action);
+ }
+
+ /**
+ * Sets the same action to execute for the +, −, × and ÷ expressions.
+ *
+ * @param action the action to execute when one of the enumerated expressions is found.
+ */
+ protected final void setMathHandlers(final BiConsumer<Expression<R,?>,A> action) {
+ setExpressionHandlers(action, FunctionNames.Add, FunctionNames.Subtract, FunctionNames.Multiply, FunctionNames.Divide);
+ }
+
+ /**
+ * Removes all filters of the given types. Types that have no registered handlers are ignored.
+ *
+ * @param types types of filters to remove.
+ */
- protected final void removeFilterHandlers(final Collection<? extends CodeList<?>> types) {
++ protected final void removeFilterHandlers(final Collection<? extends Enum<?>> types) {
+ filters.keySet().removeAll(types);
+ }
+
+ /**
+ * Executes the registered action for the given filter.
+ * Actions are registered by calls to {@code setFooHandler(…)} before the call to this {@code visit(…)} method.
+ *
+ * <h4>Note on parameterized type</h4>
+ * This method sometimes needs to be invoked with instances of {@code Filter<? super R>},
+ * because this is the type of predicate expected by {@link java.util.function} and {@link java.util.stream}.
+ * But the parameterized type expected by this method matches the parameterized type of handlers registered by
- * {@link #setFilterHandler(CodeList, BiConsumer)} and similar methods, which use the exact {@code <R>} type.
++ * {@link #setFilterHandler(Enum, BiConsumer)} and similar methods, which use the exact {@code <R>} type.
+ * This restriction exists because when doing otherwise, parameterized types become hard to express in Java
+ * (we get a cascade of {@code super} keywords, something like {@code <? super ? super R>}).
+ * However, doing the {@code (Filter<R>) filter} cast is actually safe if the handlers do not invoke any
+ * {@code filter} method having a return value (directly or indirectly as list elements) restricted to
+ * the exact {@code <R>} type. Such methods do not exist in the GeoAPI interfaces, so the cast is safe
+ * if the {@link BiConsumer} handlers do not invoke implementation-specific methods.
+ * Since only subclasses known the details of registered handlers,
+ * the decision to cast or not is left to those subclasses.
+ *
+ * @param filter the filter for which to execute an action based on its type.
+ * @param accumulator where to write the result of all actions.
+ * @throws UnsupportedOperationException if there is no action registered for the given filter.
+ */
+ public void visit(final Filter<R> filter, final A accumulator) {
- final CodeList<?> type = (filter != null) ? filter.getOperatorType() : null;
++ final Enum<?> type = (filter != null) ? filter.getOperatorType() : null;
+ final BiConsumer<Filter<R>, A> f = filters.get(type);
+ if (f != null) {
+ f.accept(filter, accumulator);
+ } else {
+ typeNotFound(type, filter, accumulator);
+ }
+ }
+
+ /**
+ * Executes the registered action for the given expression.
+ * Actions are registered by calls to {@code setFooHandler(…)} before the call to this {@code visit(…)} method.
+ *
+ * @param expression the expression for which to execute an action based on its type.
+ * @param accumulator where to write the result of all actions.
+ * @throws UnsupportedOperationException if there is no action registered for the given expression.
+ */
+ public void visit(final Expression<R,?> expression, final A accumulator) {
+ final String type = (expression != null) ? expression.getFunctionName().tip().toString() : null;
+ final BiConsumer<Expression<R,?>, A> f = expressions.get(type);
+ if (f != null) {
+ f.accept(expression, accumulator);
+ } else {
+ typeNotFound(type, expression, accumulator);
+ }
+ }
+
+ /**
+ * Adds the value to use or throws an exception when there is no action registered for a given filter type.
+ * The default implementation throws {@link UnsupportedOperationException}.
+ *
+ * @param type the filter type which has not been found, or {@code null} if {@code filter} is null.
+ * @param filter the filter (may be {@code null}).
+ * @param accumulator where to write the result of all actions.
+ * @throws UnsupportedOperationException if there is no default action.
+ */
- protected void typeNotFound(final CodeList<?> type, final Filter<R> filter, final A accumulator) {
++ protected void typeNotFound(final Enum<?> type, final Filter<R> filter, final A accumulator) {
+ throw new UnsupportedOperationException(Resources.format(Resources.Keys.CanNotVisit_2, 0, type));
+ }
+
+ /**
+ * Adds the value to use or throws an exception when there is no action registered for a given expression type.
+ * The default implementation throws {@link UnsupportedOperationException}.
+ *
+ * @param type the expression type which has not been found, or {@code null} if {@code expression} is null.
+ * @param expression the expression (may be {@code null}).
+ * @param accumulator where to write the result of all actions.
+ * @throws UnsupportedOperationException if there is no default value.
+ */
+ protected void typeNotFound(final String type, final Expression<R,?> expression, final A accumulator) {
+ throw new UnsupportedOperationException(Resources.format(Resources.Keys.CanNotVisit_2, 1, type));
+ }
+ }
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/FunctionWithSRID.java
index e72df82622,3e56cef2da..40728be85a
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/FunctionWithSRID.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/FunctionWithSRID.java
@@@ -27,13 -27,14 +27,13 @@@ import org.apache.sis.referencing.facto
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.apache.sis.feature.builder.PropertyTypeBuilder;
import org.apache.sis.feature.builder.AttributeTypeBuilder;
- import org.apache.sis.util.internal.Constants;
+ import org.apache.sis.util.privy.Constants;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.FeatureType;
-import org.opengis.filter.Literal;
-import org.opengis.filter.Expression;
-import org.opengis.filter.InvalidFilterValueException;
+// Specific to the main branch:
+import org.apache.sis.feature.DefaultFeatureType;
+import org.apache.sis.filter.Expression;
+import org.apache.sis.pending.geoapi.filter.Literal;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
index 11d52cb6de,632b473cdf..c2c749e39a
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/Registry.java
@@@ -19,11 -19,12 +19,11 @@@ package org.apache.sis.filter.sqlmm
import java.util.Arrays;
import java.util.Collection;
import org.apache.sis.geometry.wrapper.Geometries;
- import org.apache.sis.filter.internal.FunctionRegister;
+ import org.apache.sis.filter.FunctionRegister;
import org.apache.sis.pending.jdk.JDK16;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Expression;
-import org.opengis.filter.capability.AvailableFunction;
+// Specific to the main branch:
+import org.apache.sis.filter.Expression;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/ST_Point.java
index 41f720bfbd,9aec4e3105..efe88a1f58
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/ST_Point.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/sqlmm/ST_Point.java
@@@ -22,11 -22,12 +22,11 @@@ import static java.lang.Double.isNaN
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.apache.sis.geometry.wrapper.Geometries;
import org.apache.sis.geometry.wrapper.GeometryWrapper;
- import org.apache.sis.util.internal.UnmodifiableArrayList;
+ import org.apache.sis.util.privy.UnmodifiableArrayList;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Expression;
-import org.opengis.filter.InvalidFilterValueException;
+// Specific to the main branch:
+import org.apache.sis.filter.Expression;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/SpatialOperationContext.java
index 639e994330,4076c8b663..e74b1642b4
--- 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
@@@ -44,12 -44,12 +44,12 @@@ import org.apache.sis.referencing.opera
import org.apache.sis.referencing.crs.DefaultProjectedCRS;
import org.apache.sis.measure.Units;
import org.apache.sis.util.resources.Errors;
- import org.apache.sis.util.internal.Constants;
+ import org.apache.sis.util.privy.Constants;
import org.apache.sis.metadata.iso.citation.Citations;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.SpatialOperatorName;
-import org.opengis.filter.DistanceOperatorName;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.filter.SpatialOperatorName;
+import org.apache.sis.pending.geoapi.filter.DistanceOperatorName;
/**
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
index b11150a1ba,df20efc692..96f54f796d
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/geometry/wrapper/jts/Wrapper.java
@@@ -45,9 -45,9 +45,9 @@@ import org.opengis.util.FactoryExceptio
import org.opengis.geometry.DirectPosition;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.CoordinateOperation;
-import org.opengis.referencing.operation.TransformException;
import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
- import org.apache.sis.referencing.util.ReferencingUtilities;
+ import org.apache.sis.referencing.privy.ReferencingUtilities;
import org.apache.sis.geometry.DirectPosition2D;
import org.apache.sis.geometry.GeneralDirectPosition;
import org.apache.sis.geometry.GeneralEnvelope;
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
index 278575cdb3,4c04121af6..f9c8ba8df5
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java
@@@ -44,11 -44,14 +44,11 @@@ import org.apache.sis.util.ArraysExt
import org.apache.sis.util.resources.Messages;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.measure.NumberRange;
- import org.apache.sis.util.internal.Numerics;
+ import org.apache.sis.util.privy.Numerics;
import org.apache.sis.feature.internal.Resources;
- import org.apache.sis.coverage.grid.j2d.ImageUtilities;
+ import org.apache.sis.coverage.privy.ImageUtilities;
import static org.apache.sis.pending.jdk.JDK18.ceilDiv;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.coverage.grid.SequenceType;
-
/**
* An iterator over sample values in a raster or an image. This iterator makes easier to read and write efficiently
diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/Name.java
index 2c0201bc90,0000000000..a2d6c7cf29
mode 100644,000000..100644
--- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/Name.java
+++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/pending/geoapi/filter/Name.java
@@@ -1,41 -1,0 +1,41 @@@
+/*
+ * 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.pending.geoapi.filter;
+
+import org.opengis.util.LocalName;
+import org.opengis.util.ScopedName;
- import org.apache.sis.filter.internal.FunctionNames;
++import org.apache.sis.filter.privy.FunctionNames;
+import org.apache.sis.util.iso.Names;
+
+
+/**
+ * Placeholder for GeoAPI 3.1 interfaces (not yet released).
+ * Shall not be visible in public API, as it will be deleted after next GeoAPI release.
+ */
+@SuppressWarnings("doclint:missing")
+public final class Name {
+ static final LocalName STANDARD = Names.createLocalName(null, null, "fes");
+
+ static final LocalName EXTENSION = Names.createLocalName(null, null, "extension");
+
+ public static final ScopedName LITERAL = Names.createScopedName(STANDARD, null, FunctionNames.Literal);
+
+ public static final ScopedName VALUE_REFERENCE = Names.createScopedName(STANDARD, null, FunctionNames.ValueReference);
+
+ private Name() {
+ }
+}
diff --cc endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/privy/FunctionNamesTest.java
index 0000000000,f3e8064066..27072d925f
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/privy/FunctionNamesTest.java
+++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/filter/privy/FunctionNamesTest.java
@@@ -1,0 -1,154 +1,67 @@@
+ /*
+ * 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.filter.privy;
+
+ import java.lang.reflect.Field;
+ import org.apache.sis.filter.sqlmm.SQLMM;
+
+ // Test dependencies
+ import org.junit.jupiter.api.Test;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.test.TestCase;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.List;
-import org.opengis.filter.Literal;
-import org.opengis.filter.ValueReference;
-import org.opengis.filter.ComparisonOperator;
-import org.opengis.filter.ComparisonOperatorName;
-import org.opengis.filter.BetweenComparisonOperator;
-import org.opengis.filter.NullOperator;
-import org.opengis.filter.NilOperator;
-import org.opengis.filter.LikeOperator;
-import org.opengis.filter.Expression;
-
+
+ /**
+ * Verifies the values declared in {@link FunctionNames}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class FunctionNamesTest extends TestCase {
+ /**
+ * Creates a new test case.
+ */
+ public FunctionNamesTest() {
+ }
+
+ /**
+ * Verifies that each field has the same name as its value.
+ *
+ * @throws IllegalAccessException should never happen.
+ */
+ @Test
+ public void verifyFieldNames() throws IllegalAccessException {
+ for (final Field f : FunctionNames.class.getFields()) {
+ assertEquals(f.getName(), f.get(null));
+ }
+ }
+
- /**
- * Base class for dummy implementation of filter.
- */
- private static abstract class FilterBase implements ComparisonOperator<Object> {
- @Override public List<Expression<Object,?>> getExpressions() {return List.of();}
- @Override public Class<Object> getResourceClass() {return Object.class;}
- @Override public boolean test(Object resource) {return false;}
- }
-
- /**
- * Verifies the {@value FunctionNames#PROPERTY_IS_NULL} name.
- */
- @Test
- public void verifyPropertyIsNull() {
- final class Instanciable extends FilterBase implements NullOperator<Object> {}
- assertSame(new Instanciable().getOperatorType(), ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NULL));
- }
-
- /**
- * Verifies the {@value FunctionNames#PROPERTY_IS_NIL} name.
- */
- @Test
- public void verifyPropertyIsNil() {
- final class Instanciable extends FilterBase implements NilOperator<Object> {}
- assertSame(new Instanciable().getOperatorType(), ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_NIL));
- }
-
- /**
- * Verifies the {@value FunctionNames#PROPERTY_IS_LIKE} name.
- */
- @Test
- public void verifyPropertyIsLike() {
- final class Instanciable extends FilterBase implements LikeOperator<Object> {}
- assertSame(new Instanciable().getOperatorType(), ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_LIKE));
- }
-
- /**
- * Verifies the {@value FunctionNames#PROPERTY_IS_BETWEEN} name.
- */
- @Test
- public void verifyPropertyIsBetween() {
- final class Instanciable extends FilterBase implements BetweenComparisonOperator<Object> {}
- assertSame(new Instanciable().getOperatorType(), ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_BETWEEN));
- }
-
- /**
- * Verifies the {@value FunctionNames#Literal} name.
- */
- @Test
- public void verifyLiteral() {
- final var expression = new Literal<Object,Object>() {
- @Override public Object getValue() {return null;}
- @Override public <N> Expression<Object, N> toValueType(Class<N> target) {
- throw new UnsupportedOperationException();
- }
- };
- assertEquals(expression.getFunctionName().tip().toString(), FunctionNames.Literal);
- }
-
- /**
- * Verifies the {@value FunctionNames#ValueReference} name.
- */
- @Test
- public void verifyValueReference() {
- final var expression = new ValueReference<Object,Object>() {
- @Override public String getXPath() {return null;}
- @Override public Object apply(Object o) {return null;}
- @Override public Class<Object> getResourceClass() {return Object.class;}
- @Override public <N> Expression<Object,N> toValueType(Class<N> target) {
- throw new UnsupportedOperationException();
- }
- };
- assertEquals(expression.getFunctionName().tip().toString(), FunctionNames.ValueReference);
- }
-
+ /**
+ * Verifies SQLMM names.
+ */
+ @Test
+ public void verifySQLMM() {
+ int count = 0;
+ for (final Field f : FunctionNames.class.getFields()) {
+ final String name = f.getName();
+ if (name.startsWith("ST_")) {
+ assertEquals(name, SQLMM.valueOf(name).name());
+ count++;
+ }
+ }
+ assertEquals(8, count);
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/module-info.java
index 601c0441d6,0105a2f2f9..a87c6f380c
--- a/endorsed/src/org.apache.sis.metadata/main/module-info.java
+++ b/endorsed/src/org.apache.sis.metadata/main/module-info.java
@@@ -45,14 -45,14 +45,14 @@@ module org.apache.sis.metadata
provides org.apache.sis.xml.bind.TypeRegistration
with org.apache.sis.metadata.internal.MetadataTypes;
- provides org.apache.sis.util.internal.MetadataServices
+ provides org.apache.sis.util.privy.MetadataServices
with org.apache.sis.metadata.internal.ServicesForUtility;
- uses org.apache.sis.metadata.internal.ReferencingServices;
- uses org.apache.sis.metadata.sql.util.Initializer;
+ uses org.apache.sis.metadata.privy.ReferencingServices;
+ uses org.apache.sis.metadata.sql.privy.Initializer;
uses org.apache.sis.xml.bind.AdapterReplacement;
uses org.apache.sis.xml.bind.TypeRegistration;
- uses org.opengis.temporal.TemporalFactory;
+ uses org.apache.sis.pending.geoapi.temporal.TemporalFactory;
/*
* Public API open to everyone.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/Pruner.java
index 431cfa789b,11c2ec435a..5b791fcaf5
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/Pruner.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/Pruner.java
@@@ -19,11 -19,11 +19,11 @@@ package org.apache.sis.metadata
import java.util.Iterator;
import java.util.Collection;
import org.apache.sis.util.Emptiable;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.util.privy.CollectionsExt;
import static org.apache.sis.metadata.ValueExistencePolicy.*;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.ControlledVocabulary;
+// Specific to the main branch:
+import org.opengis.util.CodeList;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/CitationConstant.java
index 0000000000,4be6930510..7e8e848a2a
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/CitationConstant.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/CitationConstant.java
@@@ -1,0 -1,244 +1,238 @@@
+ /*
+ * 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.metadata.internal;
+
+ import java.util.Date;
+ import java.util.Collection;
+ import java.util.logging.Logger;
+ import java.io.ObjectStreamException;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.citation.Citation;
+ import org.opengis.metadata.citation.CitationDate;
+ import org.opengis.metadata.citation.PresentationForm;
+ import org.opengis.metadata.citation.Series;
+ import org.opengis.util.InternationalString;
+ import org.apache.sis.xml.IdentifierSpace;
+ import org.apache.sis.metadata.sql.MetadataSource;
+ import org.apache.sis.metadata.sql.MetadataStoreException;
+ import org.apache.sis.metadata.iso.citation.Citations;
+ import org.apache.sis.metadata.simple.SimpleCitation;
+ import org.apache.sis.system.Loggers;
+ import org.apache.sis.util.privy.Strings;
+ import org.apache.sis.util.logging.Logging;
+
+ // Specific to the main and geoapi-3.1 branches:
+ import org.opengis.metadata.citation.ResponsibleParty;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.citation.OnlineResource;
-import org.opengis.metadata.identification.BrowseGraphic;
-
+
+ /**
+ * Base class for the {@code public static final Citation} constants defined in some SIS classes.
+ * This base class contains only an abbreviation (e.g. {@code "OGC"} or {@code "EPSG"}) which can
+ * be used as the primary key where to search for more information in a database. If no database
+ * is available, then that simple primary key will be used as the citation title.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @see IdentifierSpace
+ * @see Citations
+ */
+ public class CitationConstant extends SimpleCitation {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = -8429121584437634107L;
+
+ /**
+ * Where to log warnings when searching for an entry in the database.
+ */
+ private static final Logger LOGGER = Logger.getLogger(Loggers.SQL);
+
+ /**
+ * Class of {@code public static final Citation} constants which are also used as namespace for identifiers.
+ * The most typical example is the "EPSG" authority which manage the codes identifying Coordinate Reference
+ * System (CRS) objects in the EPSG namespace.
+ *
+ * @param <T> the type of object used as identifier values.
+ */
+ public static class Authority<T> extends CitationConstant implements IdentifierSpace<T> {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 2067932813561130294L;
+
+ /**
+ * Creates a new citation for an authority managing codes in the given namespace.
+ * This constructor assumes that the namespace is the same as the abbreviation given as citation title.
+ *
+ * @param namespace the namespace of codes managed by this authority (e.g. "EPSG").
+ */
+ public Authority(final String namespace) {
+ super(namespace);
+ }
+
+ /**
+ * Creates a new citation for an authority managing codes in the given namespace.
+ *
+ * @param name a human-understandable primary key for fetching more information.
+ * @param namespace the namespace of codes managed by this authority (e.g. "EPSG").
+ */
+ public Authority(final String name, final String namespace) {
+ super(name, namespace);
+ }
+
+ /**
+ * Returns the name space given at construction time. Can be one of the following:
+ * <ul>
+ * <li>Abbreviation of the authority managing the codes (e.g. {@code "EPSG"} or {@code "ISBN"}).</li>
+ * <li>XML attribute name with its prefix (e.g. {@code "gml:id"}, {@code "gco:uuid"} or {@code "xlink:href"}).</li>
+ * </ul>
+ */
+ @Override
+ public final String getName() {
+ return namespace;
+ }
+
+ /**
+ * Returns a string representation of this identifier space.
+ */
+ @Override
+ public final String toString() {
+ return Strings.bracket(IdentifierSpace.class, title);
+ }
+ }
+
+ /**
+ * The name by which this citation is known to {@link Citations#fromName(String)}.
+ * Often the same as the abbreviation that {@link CitationConstant} uses as the title.
+ * If this {@code CitationConstant} is a {@link Authority} subtype, then this is also
+ * the authority namespace.
+ */
+ public final String namespace;
+
+ /**
+ * The citation which contain the "real" data, or {@code null} if not yet created.
+ * This is usually an instance created by {@link MetadataSource}. Those instances
+ * manage their own caching, so accesses to the database should not occur often.
+ */
+ private transient volatile Citation delegate;
+
+ /**
+ * Creates a new proxy for the given primary key.
+ * The key should be readable enough for being usable as a fallback if the database is not available.
+ *
+ * @param name a human-understandable primary key for fetching more information.
+ */
+ public CitationConstant(final String name) {
+ super(name);
+ this.namespace = name;
+ }
+
+ /**
+ * Creates a new proxy for the given primary key but a different programmatic name.
+ * The key should be readable enough for being usable as a fallback if the database is not available.
+ *
+ * @param name a human-understandable primary key for fetching more information.
+ * @param namespace the name by which this citation is known to {@link Citations#fromName(String)}.
+ */
+ public CitationConstant(final String name, final String namespace) {
+ super(name);
+ this.namespace = namespace;
+ }
+
+ /**
+ * Notify this instance that the database content may have changed, or that the module path has changed.
+ */
+ public final void refresh() {
+ delegate = null;
+ }
+
+ /**
+ * Returns the citation instance which contain the actual data. That instance is provided by the
+ * {@code org.apache.sis.metadata} module, which is optional. If that module is not on the module path,
+ * then this {@code delegate()} method will use the few information provided by the current instance.
+ *
+ * <p>Note that it should be very rare to not have {@code org.apache.sis.metadata} on the module path,
+ * since that module is required by {@code org.apache.sis.referencing} which is itself required by
+ * almost all other SIS modules.</p>
+ */
+ @SuppressWarnings("DoubleCheckedLocking")
+ private Citation delegate() {
+ Citation c = delegate;
+ if (c == null) {
+ synchronized (this) {
+ c = delegate;
+ if (c == null) {
+ try {
+ c = MetadataSource.getProvided().lookup(Citation.class, title);
+ } catch (MetadataStoreException e) {
+ /*
+ * If no database was available, MetadataSource.getProvided() was supposed to fallback on
+ * the MetadataFallback class. So if we get this exception, a more serious error occurred.
+ * It is still not fatal however, since most of Citation content is informative.
+ */
+ Logging.unexpectedException(LOGGER, CitationConstant.class, "delegate", e);
+ c = new SimpleCitation(title);
+ }
+ delegate = c;
+ }
+ }
+ }
+ return c;
+ }
+
+ /**
+ * Returns the title, which is mandatory.
+ */
+ @Override
+ public InternationalString getTitle() {
+ InternationalString title = delegate().getTitle();
+ return (title != null) ? title : super.getTitle();
+ }
+
+ /**
+ * Redirects the call to the delegate citation (the instance which actually contain the data).
+ *
+ * @return the value returned by the delegate.
+ */
+ @Override public Collection<? extends InternationalString> getAlternateTitles() {return delegate().getAlternateTitles();}
+ @Override public Collection<? extends CitationDate> getDates() {return delegate().getDates();}
+ @Override public InternationalString getEdition() {return delegate().getEdition();}
+ @Override public Date getEditionDate() {return delegate().getEditionDate();}
+ @Override public Collection<? extends Identifier> getIdentifiers() {return delegate().getIdentifiers();}
+ @Override public Collection<? extends ResponsibleParty> getCitedResponsibleParties() {return delegate().getCitedResponsibleParties();}
+ @Override public Collection<PresentationForm> getPresentationForms() {return delegate().getPresentationForms();}
+ @Override public Series getSeries() {return delegate().getSeries();}
+ @Override public InternationalString getOtherCitationDetails() {return delegate().getOtherCitationDetails();}
- @Override public Collection<? extends OnlineResource> getOnlineResources() {return delegate().getOnlineResources();}
- @Override public Collection<? extends BrowseGraphic> getGraphics() {return delegate().getGraphics();}
+ @Override public String getISBN() {return delegate().getISBN();}
+ @Override public String getISSN() {return delegate().getISSN();}
+
+ /**
+ * Invoked at deserialization time in order to replace the deserialized instance by the existing
+ * instance defined in the {@link org.apache.sis.metadata.iso.citation.Citations} class.
+ *
+ * @return the instance to use, as an unique instance if possible.
+ * @throws ObjectStreamException never thrown.
+ */
+ protected Object readResolve() throws ObjectStreamException {
+ final Citation c = Citations.fromName(title);
+ return (c instanceof CitationConstant) ? c : this;
+ /*
+ * Returns 'this' should happen only if the Citation has been serialized
+ * on a more recent version of Apache SIS than the current version.
+ */
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ServicesForUtility.java
index 1da9e5395d,5b67ae91c1..e0497096ce
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ServicesForUtility.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/ServicesForUtility.java
@@@ -19,22 -19,24 +19,24 @@@ package org.apache.sis.metadata.interna
import java.text.Format;
import java.util.Locale;
import java.util.TimeZone;
-import java.util.function.Supplier;
import javax.sql.DataSource;
import java.sql.SQLException;
+import java.util.function.Supplier;
import org.opengis.metadata.citation.Citation;
- import org.apache.sis.util.Exceptions;
import org.apache.sis.util.Classes;
- import org.apache.sis.util.internal.MetadataServices;
- import org.apache.sis.metadata.sql.util.Initializer;
- import org.apache.sis.system.SystemListener;
- import org.apache.sis.xml.bind.Context;
- import org.apache.sis.util.logging.Logging;
+ import org.apache.sis.util.Exceptions;
import org.apache.sis.util.iso.Types;
+ import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.privy.MetadataServices;
+ import org.apache.sis.system.SystemListener;
+ import org.apache.sis.metadata.sql.privy.Initializer;
+ import org.apache.sis.metadata.privy.Identifiers;
+ import org.apache.sis.metadata.privy.ReferencingServices;
+ import org.apache.sis.xml.bind.Context;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.ControlledVocabulary;
+// Specific to the main branch:
+import org.opengis.util.CodeList;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultContact.java
index 4c66e3883f,99f6a6be5c..f63fedcbf8
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultContact.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultContact.java
@@@ -35,15 -35,11 +35,15 @@@ import org.apache.sis.xml.bind.FilterBy
import org.apache.sis.xml.bind.gco.InternationalStringAdapter;
import org.apache.sis.metadata.internal.Dependencies;
import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
- import org.apache.sis.xml.util.LegacyNamespaces;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
+ import org.apache.sis.util.privy.CollectionsExt;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.citation.TelephoneType;
+// Specific to the main branch:
+import org.opengis.util.CodeList;
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+import org.apache.sis.pending.geoapi.evolution.UnsupportedCodeList;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
index 347896f084,491773332f..45cece31df
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/citation/DefaultResponsibleParty.java
@@@ -28,11 -28,17 +28,11 @@@ import org.opengis.metadata.citation.Re
import org.opengis.metadata.citation.Role;
import org.opengis.util.InternationalString;
import org.apache.sis.util.iso.Types;
- import org.apache.sis.xml.util.LegacyNamespaces;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
import org.apache.sis.metadata.internal.Dependencies;
import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.citation.Party;
-import org.opengis.metadata.citation.Individual;
-import org.opengis.metadata.citation.Organisation;
-import org.opengis.metadata.citation.Responsibility;
-
/**
* Identification of, and means of communication with, person(s) and
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
index 8a631e7162,34f33f2f92..220b7587ff
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultCoverageDescription.java
@@@ -36,12 -36,10 +36,12 @@@ import org.apache.sis.metadata.internal
import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
import org.apache.sis.xml.bind.FilterByVersion;
import org.apache.sis.xml.bind.metadata.MD_Identifier;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.content.AttributeGroup;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
index 0c18db8760,5853bc5402..f6f78e7fde
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureCatalogueDescription.java
@@@ -32,13 -32,10 +32,13 @@@ import org.apache.sis.xml.bind.lan.PT_L
import org.apache.sis.xml.bind.lan.LocaleAndCharset;
import org.apache.sis.metadata.internal.Dependencies;
import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.content.FeatureTypeInfo;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Obligation.CONDITIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureTypeInfo.java
index 9ed23460fb,93c4f3e576..74ebd7fe46
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureTypeInfo.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultFeatureTypeInfo.java
@@@ -23,13 -23,10 +23,13 @@@ import org.opengis.util.GenericName
import org.apache.sis.measure.ValueRange;
import org.apache.sis.metadata.TitleProperty;
import org.apache.sis.metadata.iso.ISOMetadata;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ensurePositive;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.content.FeatureTypeInfo;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Obligation.MANDATORY;
+import static org.opengis.annotation.Specification.ISO_19115;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
index 86015b6854,2be6ff4e80..fe1e286ac3
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/content/DefaultSampleDimension.java
@@@ -35,13 -35,10 +35,13 @@@ import org.apache.sis.xml.bind.gco.GO_I
import org.apache.sis.xml.bind.gco.GO_Record;
import org.apache.sis.xml.bind.gco.GO_RecordType;
import org.apache.sis.xml.bind.metadata.MI_RangeElementDescription;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ensurePositive;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.content.SampleDimension;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Obligation.CONDITIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDigitalTransferOptions.java
index dcb550d127,de564eff23..77c6cb5978
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDigitalTransferOptions.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDigitalTransferOptions.java
@@@ -33,14 -33,9 +33,14 @@@ import org.apache.sis.measure.ValueRang
import org.apache.sis.metadata.internal.Dependencies;
import org.apache.sis.xml.bind.FilterByVersion;
import org.apache.sis.xml.bind.gts.TM_PeriodDuration;
- import org.apache.sis.util.internal.CollectionsExt;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ensurePositive;
+ import org.apache.sis.util.privy.CollectionsExt;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Technical means and media by which a resource is obtained from the distributor.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultFormat.java
index 3fd87d27ac,e522d47dde..67063f1876
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultFormat.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultFormat.java
@@@ -31,18 -31,12 +31,18 @@@ import org.apache.sis.metadata.internal
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
import org.apache.sis.xml.bind.FilterByVersion;
- import org.apache.sis.xml.util.LegacyNamespaces;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
import org.apache.sis.xml.bind.metadata.MD_Medium;
import org.apache.sis.xml.bind.metadata.CI_Citation;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.util.privy.CollectionsExt;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Obligation.MANDATORY;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Description of the computer language construct that specifies the representation
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
index 917c08b499,e3c2ed9667..fc4aed2af6
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
@@@ -41,14 -41,12 +41,14 @@@ import static org.apache.sis.metadata.p
// Specific to the main and geoapi-3.1 branches:
import org.opengis.metadata.distribution.MediumName;
- import org.apache.sis.util.internal.CodeLists;
+ import org.apache.sis.util.privy.CodeLists;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.AbstractSet;
-import java.util.Iterator;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
++import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
index f1e29c28c6,9f2a0e9a88..a4e68742e0
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultStandardOrderProcess.java
@@@ -29,14 -29,9 +29,14 @@@ import org.opengis.metadata.distributio
import org.apache.sis.xml.bind.gco.GO_RecordType;
import org.apache.sis.xml.bind.gco.GO_Record;
import org.apache.sis.metadata.iso.ISOMetadata;
- import static org.apache.sis.metadata.internal.ImplementationHelper.toDate;
- import static org.apache.sis.metadata.internal.ImplementationHelper.toMilliseconds;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.toDate;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.toMilliseconds;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Common ways in which the resource may be obtained or received, and related instructions
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/DefaultSpatialTemporalExtent.java
index 83a0645c99,6d540c999d..9fe2c6a882
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/DefaultSpatialTemporalExtent.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/DefaultSpatialTemporalExtent.java
@@@ -27,14 -27,9 +27,14 @@@ import org.opengis.metadata.extent.Geog
import org.opengis.metadata.extent.GeographicBoundingBox;
import org.opengis.metadata.extent.SpatialTemporalExtent;
import org.opengis.referencing.operation.TransformException;
- import org.apache.sis.metadata.internal.ReferencingServices;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
+ import org.apache.sis.metadata.privy.ReferencingServices;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Extent with respect to date/time and spatial boundaries.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
index 4ee83115e2,a3cb0d5f00..d649d03250
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/extent/Extents.java
@@@ -66,12 -66,11 +66,12 @@@ import org.apache.sis.util.iso.Types
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.resources.Errors;
import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
- import static org.apache.sis.util.internal.CollectionsExt.nonNull;
- import static org.apache.sis.metadata.internal.ReferencingServices.AUTHALIC_RADIUS;
+ import static org.apache.sis.util.privy.CollectionsExt.nonNull;
+ import static org.apache.sis.metadata.privy.ReferencingServices.AUTHALIC_RADIUS;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.geometry.MismatchedReferenceSystemException;
+// Specific to the main branch:
+import org.opengis.metadata.identification.DataIdentification;
+import org.apache.sis.pending.geoapi.evolution.Interim;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultCoupledResource.java
index e3bc720f6e,a87b1a60a5..214eac6692
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultCoupledResource.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultCoupledResource.java
@@@ -29,15 -29,14 +29,15 @@@ import org.apache.sis.metadata.iso.ISOM
import org.apache.sis.xml.Namespaces;
import org.apache.sis.xml.bind.FilterByVersion;
import org.apache.sis.xml.bind.metadata.SV_OperationMetadata;
- import org.apache.sis.xml.util.LegacyNamespaces;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
import org.apache.sis.xml.bind.gco.GO_GenericName;
- import org.apache.sis.util.internal.Constants;
+ import org.apache.sis.util.privy.Constants;
import org.apache.sis.util.iso.Names;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.identification.CoupledResource;
-import org.opengis.metadata.identification.OperationMetadata;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultResolution.java
index f31dfe6bd1,30eeff3d1b..5d040dd101
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultResolution.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/DefaultResolution.java
@@@ -30,13 -30,8 +30,13 @@@ import org.apache.sis.xml.bind.gco.Inte
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.measure.ValueRange;
import org.apache.sis.util.resources.Messages;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ensurePositive;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.CONDITIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Level of detail expressed as a scale factor or a ground distance.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/OperationName.java
index ba04343c30,110dcdf744..31e09beb07
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/OperationName.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/identification/OperationName.java
@@@ -19,11 -19,19 +19,11 @@@ package org.apache.sis.metadata.iso.ide
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import java.util.Collections;
-import java.io.Serializable;
-import org.opengis.metadata.citation.OnlineResource;
-import org.opengis.metadata.identification.CoupledResource;
-import org.opengis.metadata.identification.DistributedComputingPlatform;
-import org.opengis.metadata.identification.OperationMetadata;
-
/**
- * An {@code OperationMetadata} placeholder to be replaced later by a reference to another {@link OperationMetadata}.
+ * An {@code OperationMetadata} placeholder to be replaced later by a reference to another {@code OperationMetadata}.
* This temporary place holder is used when the operation name is unmarshalled before the actual operation definition.
*
* @author Martin Desruisseaux (Geomatys)
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/maintenance/DefaultScope.java
index 57cde0b37a,a58349d970..f576f3097c
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/maintenance/DefaultScope.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/maintenance/DefaultScope.java
@@@ -26,14 -26,8 +26,14 @@@ import org.opengis.metadata.maintenance
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.xml.Namespaces;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.maintenance.Scope;
+// Specific to the main branch:
+import org.opengis.metadata.quality.Scope;
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+import org.apache.sis.metadata.internal.Dependencies;
+import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
- import org.apache.sis.util.internal.CollectionsExt;
++import org.apache.sis.util.privy.CollectionsExt;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/AbstractElement.java
index 648d8c2132,1042670b0a..8db20c843a
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/AbstractElement.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/AbstractElement.java
@@@ -43,14 -43,14 +43,14 @@@ import org.apache.sis.xml.bind.gco.Inte
import org.apache.sis.metadata.iso.legacy.DateToTemporal;
import org.apache.sis.metadata.iso.legacy.TemporalToDate;
import org.apache.sis.metadata.internal.Dependencies;
- import org.apache.sis.xml.util.LegacyNamespaces;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.quality.TemporalQuality;
-import org.opengis.metadata.quality.EvaluationMethod;
-import org.opengis.metadata.quality.MeasureReference;
-import org.opengis.metadata.quality.Metaquality;
+// Specific to the main branch:
+import org.opengis.metadata.quality.TemporalAccuracy;
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.UNSPECIFIED;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultCoverageResult.java
index 2759dada28,b69dadac2d..89eb5cfd4e
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultCoverageResult.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultCoverageResult.java
@@@ -28,13 -28,8 +28,13 @@@ import org.opengis.metadata.distributio
import org.opengis.metadata.spatial.SpatialRepresentation;
import org.opengis.metadata.spatial.SpatialRepresentationType;
import org.apache.sis.xml.bind.FilterByVersion;
- import org.apache.sis.xml.util.LegacyNamespaces;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.CONDITIONAL;
+import static org.opengis.annotation.Specification.UNSPECIFIED;
+
/**
* Result of a data quality measure organising the measured values as a coverage.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
index 010af2207c,2d5f6c2e8f..4be27a275a
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
@@@ -34,12 -34,12 +34,12 @@@ import org.apache.sis.util.privy.CloneA
import org.apache.sis.util.collection.CheckedContainer;
import org.apache.sis.util.resources.Errors;
import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
- import static org.apache.sis.metadata.internal.ImplementationHelper.valueIfDefined;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.valueIfDefined;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.quality.EvaluationMethod;
-import org.opengis.metadata.quality.DataEvaluation;
-import org.opengis.metadata.quality.AggregationDerivation;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.UNSPECIFIED;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java
index 884e697ec9,f540568839..924fa1f2a9
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/DefaultMeasureReference.java
@@@ -23,13 -23,10 +23,13 @@@ import jakarta.xml.bind.annotation.XmlR
import org.opengis.util.InternationalString;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.quality.Element;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.util.privy.CollectionsExt;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.quality.MeasureReference;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Obligation.CONDITIONAL;
+import static org.opengis.annotation.Specification.UNSPECIFIED;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/spatial/DefaultDimension.java
index 4e6eb3d37a,662f68eeda..041c08bd8e
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/spatial/DefaultDimension.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/spatial/DefaultDimension.java
@@@ -30,13 -30,8 +30,13 @@@ import org.apache.sis.metadata.TitlePro
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.measure.ValueRange;
import org.apache.sis.util.ArgumentChecks;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ensurePositive;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ensurePositive;
+// Specific to the main branch:
+import org.opengis.annotation.UML;
+import static org.opengis.annotation.Obligation.OPTIONAL;
+import static org.opengis.annotation.Specification.ISO_19115;
+
/**
* Axis properties.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/Identifiers.java
index 0000000000,13a42017ea..d42c6cadd4
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/Identifiers.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/Identifiers.java
@@@ -1,0 -1,299 +1,306 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.Locale;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.citation.Citation;
+ import org.opengis.util.InternationalString;
+ import org.apache.sis.util.CharSequences;
+ import org.apache.sis.util.Characters;
+ import org.apache.sis.util.Deprecable;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.util.privy.Strings;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.privy.CollectionsExt;
+ import org.apache.sis.metadata.iso.citation.Citations;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.resources.Vocabulary;
+ import org.apache.sis.xml.NilObject;
+ import org.apache.sis.xml.NilReason;
+
++// Specific to the main branch:
++import org.opengis.referencing.ReferenceIdentifier;
++
+
+ /**
+ * Methods working on {@link Identifier} instances.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class Identifiers extends Static {
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private Identifiers() {
+ }
+
+ /**
+ * Returns {@code true} if the given code is {@code "EPSG"} while the codespace is {@code "IOGP"} or {@code "OGP"}
+ * (ignoring case). This particular combination of code and codespace is handled in a special way.
+ *
+ * <p>This method can be used for identifying where in Apache SIS source code the relationship between
+ * EPSG authority and IOGP code space is hard-coded.</p>
+ *
+ * @param codeSpace the identifier code space, or {@code null}.
+ * @param code the identifier code, or {@code null}.
+ * @return {@code true} if the given identifier is {@code "IOGP:EPSG"}.
+ *
+ * @see org.apache.sis.metadata.iso.citation.Citations#EPSG
+ */
+ public static boolean isEPSG(final String codeSpace, final String code) {
+ return Constants.EPSG.equalsIgnoreCase(code) &&
+ (Constants.IOGP.equalsIgnoreCase(codeSpace) || "OGP".equalsIgnoreCase(codeSpace) ||
+ Constants.EPSG.equalsIgnoreCase(codeSpace));
+ // "OGP" is a legacy abbreviation that existed before "IOGP".
+ }
+
+ /**
+ * Return {@code true} if the given object is deprecated.
+ */
+ private static boolean isDeprecated(final Object object) {
+ return (object instanceof Deprecable) && ((Deprecable) object).isDeprecated();
+ }
+
+ /**
+ * Returns a "unlocalized" string representation of the given international string, or {@code null} if none
+ * or if the string is deprecated or empty. This method is used by {@link #getIdentifier(Citation, boolean)},
+ * which is why we don't want the localized string.
+ */
+ private static String trimOrNull(final InternationalString title) {
+ return (title != null && !isDeprecated(title)) ? Strings.trimOrNull(title.toString(Locale.ROOT)) : null;
+ }
+
+ /**
+ * Infers an identifier from the given citation, or returns {@code null} if no identifier has been found.
+ * This method removes leading and trailing {@linkplain Character#isWhitespace(int) whitespaces}.
+ * See {@link Citations#getIdentifier(Citation)} for the public documentation of this method.
+ *
+ * <h4>Which method to use</h4>
+ * Guidelines:
+ * <ul>
+ * <li>For information purpose (e.g. some {@code toString()} methods), use {@code getIdentifier(…, false)}.</li>
+ * <li>For WKT formatting, use {@code getIdentifier(…, true)} in order to preserve formatting characters.</li>
+ * <li>For assigning a value to a {@code codeSpace} field, use {@link Citations#toCodeSpace(Citation)}.</li>
+ * </ul>
+ *
+ * Use {@code toCodeSpace(…)} method when assigning values to be returned by methods like
- * {@link Identifier#getCodeSpace()}, since those values are likely to be compared without special
++ * {@link ReferenceIdentifier#getCodeSpace()}, since those values are likely to be compared without special
+ * care about ignorable identifier characters. But if the intent is to format a more complex string
+ * like WKT or {@code toString()}, then we suggest to use {@code getIdentifier(citation, true)} instead,
+ * which will produce the same result but preserving the ignorable characters, which can be useful
+ * for formatting purpose.
+ *
+ * @param citation the citation for which to get the identifier, or {@code null}.
+ * @param strict {@code true} for returning a non-null value only if the identifier is a valid Unicode identifier.
+ * @return a non-empty identifier for the given citation without leading or trailing whitespaces,
+ * or {@code null} if the given citation is null or does not declare any identifier or title.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-201">SIS-201</a>
+ */
+ public static String getIdentifier(final Citation citation, final boolean strict) {
+ if (citation != null) {
+ boolean isUnicode = false; // Whether `identifier` is a Unicode identifier.
+ String identifier = null; // The best identifier found so far.
+ String codeSpace = null; // Code space of the identifier, or null if none.
+ for (final Identifier id : CollectionsExt.nonNull(citation.getIdentifiers())) {
+ if (id != null && !isDeprecated(id)) {
+ final String candidate = Strings.trimOrNull(id.getCode());
+ if (candidate != null) {
+ /*
+ * For a non-empty identifier, verify if both the code and its codespace are valid
+ * Unicode identifiers. If a codespace exists, then the code does not need to begin
+ * with a "Unicode identifier start" (it may be a "Unicode identifier part").
+ */
- final String cs = Strings.trimOrNull(id.getCodeSpace());
- if (cs == null) {
++ String cs = null;
++ if (id instanceof ReferenceIdentifier) {
++ cs = Strings.trimOrNull(((ReferenceIdentifier) id).getCodeSpace());
++ }
++ if (cs == null || cs.isEmpty()) {
++ cs = null;
+ isUnicode = CharSequences.isUnicodeIdentifier(candidate);
+ } else {
+ isUnicode = CharSequences.isUnicodeIdentifier(cs);
+ if (isUnicode) for (int i = 0; i < candidate.length();) {
+ final int c = candidate.codePointAt(i);
+ if (!Character.isUnicodeIdentifierPart(c) &&
+ (strict || (c != '.' && c != '-')))
+ {
+ /*
+ * Above special case for '.' and '-' characters is documented
+ * in the public Citations.getIdentifier(Citation) method.
+ */
+ isUnicode = false;
+ break;
+ }
+ i += Character.charCount(c);
+ }
+ }
+ /*
+ * If we found a Unicode identifier, we are done and we can exit the loop.
+ * Otherwise retain the first identifier and continue the search for Unicode identifier.
+ */
+ if (identifier == null || isUnicode) {
+ identifier = candidate;
+ codeSpace = cs;
+ if (isUnicode) break;
+ }
+ }
+ }
+ }
+ /*
+ * If no identifier has been found, fallback on the first title or alternate title.
+ * We search for alternate titles because ISO specification said that those titles
+ * are often used for abbreviations. Again we give preference to Unicode identifiers,
+ * which are typically alternate titles.
+ */
+ if (identifier == null) {
+ identifier = trimOrNull(citation.getTitle()); // Whitepaces removed by trimOrNull(…).
+ if (identifier != null) {
+ isUnicode = CharSequences.isUnicodeIdentifier(identifier);
+ }
+ if (!isUnicode) {
+ for (final InternationalString i18n : CollectionsExt.nonNull(citation.getAlternateTitles())) {
+ final String candidate = trimOrNull(i18n);
+ if (candidate != null) {
+ isUnicode = CharSequences.isUnicodeIdentifier(candidate);
+ if (identifier == null || isUnicode) {
+ identifier = candidate;
+ if (isUnicode) break;
+ }
+ }
+ }
+ }
+ }
+ /*
+ * Finished searching in the identifiers, title and alternate titles. If the identifier that
+ * we found is not a valid Unicode identifier, we will return it only if the caller did not
+ * asked for strictly valid Unicode identifier.
+ */
+ if (isUnicode || !strict) {
+ if (codeSpace != null && !isEPSG(codeSpace, identifier)) {
+ return codeSpace + (strict ? '_' : Constants.DEFAULT_SEPARATOR) + identifier;
+ } else {
+ return identifier;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if the given identifier authority matches the given {@code authority}.
+ * If one of the authority is null, then the comparison fallback on the given {@code codeSpace}.
+ * If the code spaces are also null, then this method conservatively returns {@code false}.
+ *
+ * @param identifier the identifier to compare.
+ * @param authority the desired authority, or {@code null}.
+ * @param codeSpace the desired code space or {@code null}, used as a fallback if an authority is null.
+ * @return {@code true} if the authority or code space (as a fallback only) matches.
+ */
+ private static boolean authorityMatches(final Identifier identifier, final Citation authority, final String codeSpace) {
+ if (authority != null) {
+ final Citation other = identifier.getAuthority();
+ if (other != null) {
+ return Citations.identifierMatches(authority, other);
+ }
+ }
- if (codeSpace != null) {
- final String other = identifier.getCodeSpace();
++ if (codeSpace != null && identifier instanceof ReferenceIdentifier) {
++ final String other = ((ReferenceIdentifier) identifier).getCodeSpace();
+ if (other != null) {
+ return CharSequences.equalsFiltered(codeSpace, other, Characters.Filter.UNICODE_IDENTIFIER, true);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether a match or mismatch is found between the two given collections of identifiers.
+ * If any of the given collections is {@code null} or empty, then this method returns {@code null}.
+ *
+ * <p>According ISO 19162 (<cite>Well known text representation of coordinate reference systems</cite>),
+ * {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#getIdentifiers() identifiers} should have precedence over
+ * {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#getName() name} for identifying {@code IdentifiedObject}s,
+ * at least in the case of {@linkplain org.apache.sis.referencing.operation.DefaultOperationMethod operation methods} and
+ * {@linkplain org.apache.sis.parameter.AbstractParameterDescriptor parameters}.</p>
+ *
+ * @param id1 the first collection of identifiers, or {@code null}.
+ * @param id2 the second collection of identifiers, or {@code null}.
+ * @return {@code TRUE} or {@code FALSE} on match or mismatch respectively, or {@code null} if this method
+ * cannot determine if there is a match or mismatch.
+ */
- public static Boolean hasCommonIdentifier(final Iterable<? extends Identifier> id1,
- final Iterable<? extends Identifier> id2)
++ public static Boolean hasCommonIdentifier(final Iterable<? extends ReferenceIdentifier> id1,
++ final Iterable<? extends ReferenceIdentifier> id2)
+ {
+ if (id1 != null && id2 != null) {
+ boolean hasFound = false;
- for (final Identifier identifier : id1) {
++ for (final ReferenceIdentifier identifier : id1) {
+ final Citation authority = identifier.getAuthority();
+ final String codeSpace = identifier.getCodeSpace();
+ for (final Identifier other : id2) {
+ if (authorityMatches(identifier, authority, codeSpace)) {
+ if (CharSequences.equalsFiltered(identifier.getCode(), other.getCode(), Characters.Filter.UNICODE_IDENTIFIER, true)) {
+ return Boolean.TRUE;
+ }
+ hasFound = true;
+ }
+ }
+ }
+ if (hasFound) {
+ return Boolean.FALSE;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns a message saying that a property is missing for an object having the given identifier.
+ *
+ * @param owner identifier of the object for which a property is missing.
+ * @param property name of the missing property.
+ * @return a message saying that a value is missing for the given property in the specified identified object.
+ */
+ public static String missingValueForProperty(final Identifier owner, final String property) {
+ return (owner == null)
+ ? Errors.format(Errors.Keys.MissingValueForProperty_1, property)
+ : Errors.format(Errors.Keys.MissingValueForProperty_2, owner, property);
+ }
+
+ /**
+ * Returns a string representation of the reason why an object is nil.
+ * If the object provides a {@link NilReason}, then its string representation is returned.
+ * Otherwise, if the given object provides identifiers, then they are assumed unresolved references.
+ * Otherwise, this method returns "unspecified" in the default locale.
+ *
+ * @param object the object for which to get the nil reason.
+ * @return string representation of the nil reason.
+ */
+ public static String getNilReason(final NilObject object) {
+ NilReason reason = object.getNilReason();
+ if (reason != null) {
+ return reason.toString();
+ }
+ if (object instanceof org.apache.sis.xml.IdentifiedObject) {
+ for (String id : ((org.apache.sis.xml.IdentifiedObject) object).getIdentifierMap().values()) {
+ if (id != null && !id.isBlank()) {
+ return "unresolved " + id;
+ }
+ }
+ }
+ return Vocabulary.format(Vocabulary.Keys.Unspecified);
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/NameToIdentifier.java
index 0000000000,1fe8975835..47c3f215fc
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/NameToIdentifier.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/NameToIdentifier.java
@@@ -1,0 -1,313 +1,321 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.Locale;
+ import java.util.Objects;
+ import java.util.Collection;
+ import org.opengis.util.NameSpace;
+ import org.opengis.util.GenericName;
+ import org.opengis.util.InternationalString;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.citation.Citation;
+ import org.apache.sis.metadata.iso.citation.Citations;
+ import org.apache.sis.util.CharSequences;
+ import org.apache.sis.util.iso.DefaultNameSpace;
+ import static org.apache.sis.util.Characters.Filter.LETTERS_AND_DIGITS;
+
+ // Specific to the main and geoapi-3.1 branches:
+ import org.opengis.referencing.ReferenceIdentifier;
+
+
+ /**
+ * Does the unobvious mapping between {@link Identifier} properties and {@link GenericName} ones.
+ * This class also implements the {@link #isHeuristicMatchForName(Identifier, Collection, CharSequence, Simplifier)}
+ * method since that method involves a mix of names and identifiers.
+ *
+ * <h2>Limitations</h2>
+ * Current version does not yet work with URN or HTTP syntax.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class NameToIdentifier implements ReferenceIdentifier {
+ /**
+ * The name from which to infer the identifier attributes.
+ */
+ private final GenericName name;
+
+ /**
+ * Infers the attributes from the given name.
+ *
+ * @param name the name from which to infer the identifier properties.
+ */
+ public NameToIdentifier(final GenericName name) {
+ this.name = Objects.requireNonNull(name);
+ }
+
+ /**
+ * Returns the scope of the given name if it is not global.
+ * This method is null-safe, including paranoiac checks against null scope.
+ *
+ * @param name the name from which to get the scope, or {@code null}.
+ * @return the scope of the given name, or {@code null} if the given name was null or has a global scope.
+ */
+ private static GenericName scope(final GenericName name) {
+ if (name != null) {
+ final NameSpace scope = name.scope();
+ if (scope != null && !scope.isGlobal()) {
+ return scope.name();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Infers the authority from the scope if any, or from the code space otherwise.
+ *
+ * @return the authority, or {@code null} if none.
+ */
+ @Override
+ public Citation getAuthority() {
+ GenericName scope = scope(name);
+ if (scope == null) {
+ scope = scope(name.tip());
+ if (scope == null) {
+ return null;
+ }
+ }
+ return Citations.fromName(scope.head().toString());
+ }
+
+ /**
+ * Takes the element before the tip as the code space.
+ *
+ * @param name the name from which to get the code space, or {@code null}.
+ * @param locale the locale, or {@code null} for a call to {@code name.toString()}.
+ * @return the code space, or {@code null} if none.
+ */
+ public static String getCodeSpace(final GenericName name, final Locale locale) {
+ final GenericName scope = scope(name.tip());
+ return (scope != null) ? toString(scope.tip(), locale) : null;
+ }
+
+ /**
+ * Takes everything except the tip as the code space.
+ */
+ @Override
+ public String getCodeSpace() {
+ return getCodeSpace(name, null);
+ }
+
+ /**
+ * Takes the last element as the code.
+ */
+ @Override
+ public String getCode() {
+ 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.
+ */
+ @Override
+ public int hashCode() {
+ return ~Objects.hashCode(name);
+ }
+
+ /**
+ * Compares this object with the given one for equality.
+ *
+ * @param object the object to compare with this identifier.
+ * @return {@code true} if both objects are equal.
+ */
+ @Override
+ public boolean equals(final Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object != null && object.getClass() == getClass()) {
+ return Objects.equals(name, ((NameToIdentifier) object).name);
+ }
+ return false;
+ }
+
+ /**
+ * Returns the string representation of this identifier.
+ *
+ * @return the string representation of this identifier.
+ */
+ @Override
+ public String toString() {
+ final String code = getCode();
+ final String cs = getCodeSpace();
+ if (cs != null && !cs.isEmpty()) {
+ return cs + DefaultNameSpace.DEFAULT_SEPARATOR + code;
+ }
+ return code;
+ }
+
+ /**
+ * Returns a string representation of the given name in the given locale, with paranoiac checks against null value.
+ * Such null values should never happen since the properties used here are mandatory, but we try to make this class
+ * robust to broken implementations.
+ *
+ * @param name the name from which to get the localized string, or {@code null}.
+ * @param locale the locale, or {@code null} for a call to {@code name.toString()}.
+ * @return the localized string representation, or {@code null} if the given name was null.
+ */
+ public static String toString(final GenericName name, final Locale locale) {
+ if (name != null) {
+ if (locale != null) {
+ final InternationalString i18n = name.toInternationalString();
+ if (i18n != null) {
+ final String s = i18n.toString(locale);
+ if (s != null) {
+ return s;
+ }
+ }
+ }
+ return name.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if the given identifier to search matches one of the object identifiers.
+ *
+ * @param identifiers the identifiers to compare against {@code toSearch}.
+ * @param toSearch the identifier to check for equality.
+ * @return {@code true} if the identifier to search is found in the given set of identifiers.
+ */
- public static boolean isHeuristicMatchForIdentifier(final Iterable<? extends Identifier> identifiers, final String toSearch) {
++ public static boolean isHeuristicMatchForIdentifier(final Iterable<? extends ReferenceIdentifier> identifiers, final String toSearch) {
+ if (toSearch != null && identifiers != null) {
+ int s = toSearch.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR);
+ if (s < 0) {
+ // no codespace in searched name
+ for (final Identifier id : identifiers) {
+ if (toSearch.equalsIgnoreCase(id.getCode())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ do {
+ final String codespace = toSearch.substring(0, s).trim();
+ final String code = toSearch.substring(++s).trim();
- for (final Identifier id : identifiers) {
++ for (final ReferenceIdentifier id : identifiers) {
+ if (codespace.equalsIgnoreCase(id.getCodeSpace()) && code.equalsIgnoreCase(id.getCode())) {
+ return true;
+ }
+ }
+ s = toSearch.indexOf(DefaultNameSpace.DEFAULT_SEPARATOR, s);
+ } while (s >= 0);
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the given {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#getName()
+ * primary name} or one of the given aliases matches the given name. The comparison ignores case, some Latin
+ * diacritical signs and any characters that are not letters or digits.
+ *
+ * @param name the name of the {@code IdentifiedObject} to check.
+ * @param aliases the list of aliases in the {@code IdentifiedObject} (may be {@code null}).
+ * This method will never modify that list, so the given list can be a direct
+ * reference to an internal list.
+ * @param toSearch the name for which to check for equality.
+ * @param simplifier a function for simplifying the names before comparison.
+ * @return {@code true} if the primary name or at least one alias matches the given {@code name}.
+ */
+ public static boolean isHeuristicMatchForName(final Identifier name, final Collection<GenericName> aliases,
+ CharSequence toSearch, final Simplifier simplifier)
+ {
+ if (toSearch != null) {
+ CharSequence code = (name != null) ? name.getCode() : null;
+ if (toSearch.equals(code)) {
+ return true; // Optimization for a common case.
+ }
+ toSearch = simplifier.apply(toSearch);
+ code = simplifier.apply(code);
+ if (CharSequences.equalsFiltered(toSearch, code, LETTERS_AND_DIGITS, true)) {
+ return true;
+ }
+ if (aliases != null) {
+ for (final GenericName alias : aliases) {
+ if (alias != null) { // Paranoiac check.
+ final CharSequence tip = simplifier.apply(alias.tip().toString());
+ if (CharSequences.equalsFiltered(toSearch, tip, LETTERS_AND_DIGITS, true)) {
+ return true;
+ }
+ /*
+ * Note: a previous version compared also the scoped names. We removed that part,
+ * because experience has shown that this method is used only for the "code" part
+ * of an object name. If we really want to compare scoped name, it would probably
+ * be better to take a GenericName argument instead of String.
+ */
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns {@code true} if the {@linkplain org.apache.sis.referencing.AbstractIdentifiedObject#getName()
+ * primary name} or an aliases of the given object matches the given name. The comparison ignores case,
+ * some Latin diacritical signs and any characters that are not letters or digits.
+ *
+ * @param object the object for which to check the name or alias.
+ * @param name the name to compare with the object name or aliases.
+ * @return {@code true} if the primary name of at least one alias matches the specified {@code name}.
+ */
+ public static boolean isHeuristicMatchForName(final IdentifiedObject object, final String name) {
+ return isHeuristicMatchForName(object.getName(), object.getAlias(), name, Simplifier.DEFAULT);
+ }
+
+ /**
+ * A function for simplifying an {@link org.opengis.referencing.IdentifiedObject} name before comparison with
+ * {@link NameToIdentifier#isHeuristicMatchForName(Identifier, Collection, CharSequence, Simplifier)}.
+ */
+ public static class Simplifier {
+ /**
+ * The prefix used by ESRI at the beginning of datum names.
+ * This prefix should be omitted when simplifying a datum name.
+ */
+ public static final String ESRI_DATUM_PREFIX = "D_";
+
+ /**
+ * The default instance, which replaces some non-ASCII characters by ASCII ones.
+ */
+ public static final Simplifier DEFAULT = new Simplifier();
+
+ /**
+ * For subclasses and default instance only.
+ */
+ protected Simplifier() {
+ }
+
+ /**
+ * Simplifies the given name.
+ *
+ * @param name the object name (may be {@code null}).
+ * @return the name to use for comparison purpose, or {@code null}.
+ */
+ protected CharSequence apply(final CharSequence name) {
+ return CharSequences.toASCII(name);
+ }
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/ReferencingServices.java
index 0000000000,eed74e2426..c71336c2b0
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/ReferencingServices.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/ReferencingServices.java
@@@ -1,0 -1,301 +1,314 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.Locale;
+ import java.util.TimeZone;
+ import java.util.ServiceLoader;
+ import java.text.Format;
+ import org.opengis.geometry.Envelope;
+ import org.opengis.geometry.DirectPosition;
+ import org.opengis.parameter.ParameterDescriptor;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.operation.CoordinateOperationFactory;
+ import org.opengis.referencing.operation.TransformException;
+ import org.opengis.util.FactoryException;
+ import org.apache.sis.metadata.iso.extent.DefaultExtent;
+ import org.apache.sis.metadata.iso.extent.DefaultVerticalExtent;
+ import org.apache.sis.metadata.iso.extent.DefaultTemporalExtent;
+ import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
+ import org.apache.sis.metadata.iso.extent.DefaultSpatialTemporalExtent;
+ import org.apache.sis.system.OptionalDependency;
+ import org.apache.sis.system.Modules;
+
++// Specific to the main branch:
++import org.opengis.util.TypeName;
++
+
+ /**
+ * Provides access to services defined in the {@code org.apache.sis.referencing} module.
+ * This class also opportunistically defines some methods and constants related
+ * to <q>referencing by coordinates</q> but needed by metadata.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public class ReferencingServices extends OptionalDependency {
+ /**
+ * The length of one nautical mile, which is {@value} metres.
+ */
+ public static final double NAUTICAL_MILE = 1852;
+
+ /**
+ * The GRS80 {@linkplain org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius() authalic radius},
+ * which is {@value} metres. This is close to the WGS84 authalic radius, which is about 6371007.180918474 when
+ * computed with {@code double} precision.
+ */
+ public static final double AUTHALIC_RADIUS = 6371007;
+
+ /**
+ * The services, fetched when first needed.
+ * Guaranteed non-null after the search was done, even if the service implementation was not found.
+ *
+ * @see #getInstance()
+ */
+ private static volatile ReferencingServices instance;
+
+ /**
+ * For subclass only. This constructor registers this instance as a
+ * {@link org.apache.sis.system.SystemListener} in order to
+ * force a new {@code ReferencingServices} lookup if the module path changes.
+ */
+ protected ReferencingServices() {
+ super(Modules.METADATA, Modules.REFERENCING);
+ }
+
+ /**
+ * Invoked when the module path changed. Resets the {@link #instance} to {@code null}
+ * in order to force the next call to {@link #getInstance()} to fetch a new one,
+ * which may be different.
+ */
+ @Override
+ protected final void classpathChanged() {
+ synchronized (ReferencingServices.class) {
+ instance = null;
+ }
+ }
+
+ /**
+ * Returns the singleton instance.
+ *
+ * @return the singleton instance.
+ */
+ @SuppressWarnings("DoubleCheckedLocking")
+ public static ReferencingServices getInstance() {
+ ReferencingServices c = instance;
+ if (c == null) {
+ synchronized (ReferencingServices.class) {
+ c = instance;
+ if (c == null) {
+ /*
+ * Double-checked locking: okay since Java 5 provided that the `instance` field is volatile.
+ * In the particular case of this class, the intent is to ensure that SystemListener.add(…)
+ * is invoked only once.
+ */
+ c = getInstance(ReferencingServices.class, ServiceLoader.load(ReferencingServices.class), Modules.REFERENCING);
+ if (c == null) {
+ c = new ReferencingServices();
+ }
+ instance = c;
+ }
+ }
+ }
+ return c;
+ }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ //// ////
+ //// SERVICES FOR ISO 19115 METADATA ////
+ //// ////
+ ///////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sets a geographic bounding box from the specified envelope.
+ * If the envelope contains a CRS which is not geographic, then the bounding box will be transformed
+ * to a geographic CRS (without datum shift if possible). Otherwise, the envelope is assumed already
+ * in a geographic CRS using (<var>longitude</var>, <var>latitude</var>) axis order.
+ *
+ * <p>If {@code findOpCaller} is non-null, then this method is invoked for computing a <em>hint</em>
+ * for choosing a coordinate operation between a pair of reference systems. It changes the behavior
+ * of this methods in two ways:</p>
+ *
+ * <ul>
+ * <li>Some failures will cause this method to return {@code null} instead of throwing an exception.
+ * Those exception will be logged on the assumption that {@code findOpCaller} is a public method
+ * of {@code Envelopes}.</li>
+ * <li>The bounding box may be conservatively expanded to the whole world.</li>
+ * </ul>
+ *
+ * @param envelope the source envelope.
+ * @param target the target bounding box, or {@code null} for creating it automatically.
+ * @param findOpCaller non-null for computing a hint rather than an exact bounding box.
+ * @return the bounding box, or {@code null} on failure (in hint mode) or if no horizontal component was found.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ * @throws TransformException if the given envelope cannot be transformed.
+ */
+ public DefaultGeographicBoundingBox setBounds(Envelope envelope, DefaultGeographicBoundingBox target, String findOpCaller)
+ throws TransformException
+ {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Sets a vertical extent with the value inferred from the given envelope.
+ * Only the vertical coordinates are extracted; all other coordinates are ignored.
+ *
+ * @param envelope the source envelope.
+ * @param target the target vertical extent.
+ * @return whether the envelope contains a vertical component.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ */
+ public boolean setBounds(Envelope envelope, DefaultVerticalExtent target) {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Sets a temporal extent with the value inferred from the given envelope.
+ * Only the temporal coordinates are extracted; all other coordinates are ignored.
+ *
+ * @param envelope the source envelope.
+ * @param target the target temporal extent.
+ * @return whether the envelope contains a temporal component.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ */
+ public boolean setBounds(Envelope envelope, DefaultTemporalExtent target) {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Sets the geographic, vertical and temporal extents with the values inferred from the given envelope.
+ * If the given {@code target} has more geographic or vertical extents than needed (0 or 1), then the
+ * extraneous extents are removed.
+ *
+ * <p>Behavior regarding missing dimensions:</p>
+ * <ul>
+ * <li>If the given envelope has no horizontal component, then all geographic extents are removed
+ * from the given {@code target}. Non-geographic extents (e.g. descriptions and polygons) are
+ * left unchanged.</li>
+ * <li>If the given envelope has no vertical component, then the vertical extent is set to {@code null}.</li>
+ * <li>If the given envelope has no temporal component, then the temporal extent is set to {@code null}.</li>
+ * </ul>
+ *
+ * @param envelope the source envelope.
+ * @param target the target spatiotemporal extent.
+ * @return whether the envelope contains a spatial or temporal component.
+ * @throws TransformException if a coordinate transformation was required and failed.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ */
+ public boolean setBounds(Envelope envelope, DefaultSpatialTemporalExtent target) throws TransformException {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Initializes a horizontal, vertical and temporal extent with the values inferred from the given envelope.
+ *
+ * @param envelope the source envelope.
+ * @param target the target extent.
+ * @return whether the envelope contains a spatial or temporal component.
+ * @throws TransformException if a coordinate transformation was required and failed.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ */
+ public boolean addElements(Envelope envelope, DefaultExtent target) throws TransformException {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Creates a two-dimensional geographic position associated to the default geographic CRS.
+ * Axis order is (longitude, latitude).
+ *
+ * @param λ the longitude value.
+ * @param φ the latitude value.
+ * @return the direct position for the given geographic coordinate.
+ */
+ public DirectPosition geographic(final double λ, final double φ) {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Returns an identifier for the given object, giving precedence to EPSG identifier if available.
+ * The returned string should be of the form {@code "AUTHORITY:CODE"} if possible (no guarantees).
+ *
+ * @param object the object for which to get an identifier.
+ * @return an identifier for the given object, with preference given to EPSG codes.
+ * @throws FactoryException if an error occurred while searching for the EPSG code.
+ */
+ public String getPreferredIdentifier(final IdentifiedObject object) throws FactoryException {
+ throw moduleNotFound();
+ }
+
+
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////
+ //// ////
+ //// OTHER REFERENCING SERVICES ////
+ //// ////
+ ///////////////////////////////////////////////////////////////////////////////////////
+
++ /**
++ * Returns the name of the type of values.
++ *
++ * @param parameter parameter for which to get the name of type of values, or {@code null}.
++ * @return name of type of values, or {@code null} if not supported by given implementation.
++ */
++ public TypeName getValueType(ParameterDescriptor<?> parameter) {
++ return null;
++ }
++
+ /**
+ * Returns a fully implemented parameter descriptor.
+ *
+ * @param <T> the type of values.
+ * @param parameter a partially implemented parameter descriptor, or {@code null}.
+ * @return a fully implemented parameter descriptor, or {@code null} if the given argument was null.
+ * @throws UnsupportedOperationException if the {@code org.apache.sis.referencing} module has not been found on the module path.
+ */
+ public <T> ParameterDescriptor<T> toImplementation(ParameterDescriptor<T> parameter) {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Creates a format for {@link DirectPosition} instances.
+ *
+ * @param locale the locale for the new {@code Format}, or {@code null} for {@code Locale.ROOT}.
+ * @param timezone the timezone, or {@code null} for UTC.
+ * @return a {@link org.apache.sis.geometry.CoordinateFormat}.
+ */
+ public Format createCoordinateFormat(final Locale locale, final TimeZone timezone) {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Returns the default coordinate operation factory.
+ *
+ * @return the coordinate operation factory to use.
+ */
+ public CoordinateOperationFactory getCoordinateOperationFactory() {
+ throw moduleNotFound();
+ }
+
+ /**
+ * Returns information about the Apache SIS configuration to be reported in {@link org.apache.sis.setup.About}.
+ * This method is invoked only for aspects that depends on other modules than {@code org.apache.sis.util}.
+ *
+ * <p>Current keys are:</p>
+ * <ul>
+ * <li>{@code "EPSG"}: version of EPSG database.</li>
+ * </ul>
+ *
+ * @param key a key identifying the information to return.
+ * @param locale language to use if possible.
+ * @return the information, or {@code null} if none.
+ *
+ * @see org.apache.sis.util.privy.MetadataServices#getInformation(String, Locale)
+ */
+ public String getInformation(String key, Locale locale) {
+ return null;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/TemporalUtilities.java
index 0000000000,8274143c87..3adf0d4cd1
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/TemporalUtilities.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/privy/TemporalUtilities.java
@@@ -1,0 -1,144 +1,144 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.Date;
+ import java.util.ServiceLoader;
+ import org.opengis.temporal.TemporalPrimitive;
+ import org.apache.sis.system.Modules;
+ import org.apache.sis.system.Reflect;
+ import org.apache.sis.system.SystemListener;
+ import org.apache.sis.pending.temporal.DefaultTemporalFactory;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.Instant;
-import org.opengis.temporal.Period;
-import org.opengis.temporal.TemporalFactory;
++// Specific to the main branch:
++import org.apache.sis.pending.geoapi.temporal.Instant;
++import org.apache.sis.pending.geoapi.temporal.Period;
++import org.apache.sis.pending.geoapi.temporal.TemporalFactory;
+
+
+ /**
+ * Utilities related to ISO 19108 objects. This class may disappear after we reviewed
+ * the GeoAPI-pending temporal interfaces.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @author Guilhem Legal (Geomatys)
+ */
+ public final class TemporalUtilities extends SystemListener {
+ /**
+ * The default factory to use for implementations.
+ */
+ private static volatile TemporalFactory implementation;
+
+ static {
+ SystemListener.add(new TemporalUtilities());
+ }
+
+ /**
+ * For the singleton system listener only.
+ */
+ private TemporalUtilities() {
+ super(Modules.METADATA);
+ }
+
+ /**
+ * Discards the cached factory when the module path has changed.
+ */
+ @Override
+ protected void classpathChanged() {
+ implementation = null;
+ }
+
+ /**
+ * Returns a temporal factory, or a default implementation if none.
+ *
+ * @return the temporal factory.
+ */
+ public static TemporalFactory getTemporalFactory() {
+ TemporalFactory factory = implementation;
+ if (factory == null) {
+ factory = ServiceLoader.load(TemporalFactory.class, Reflect.getContextClassLoader())
+ .findFirst().orElseGet(DefaultTemporalFactory::provider);
+ implementation = factory;
+ }
+ return factory;
+ }
+
+ /**
+ * Creates an instant for the given date using the given factory.
+ */
+ private static Instant createInstant(final TemporalFactory factory, final Date date) {
+ return factory.createInstant(date);
+ }
+
+ /**
+ * Creates an instant for the given date.
+ *
+ * @param time the date for which to create instant, or {@code null}.
+ * @return the instant, or {@code null} if the given time was null.
+ * @throws UnsupportedOperationException if the temporal factory is not available on the module path.
+ */
+ public static Instant createInstant(final Date time) throws UnsupportedOperationException {
+ return (time != null) ? createInstant(getTemporalFactory(), time) : null;
+ }
+
+ /**
+ * Creates a period for the given begin and end dates. The given arguments can be null if the
+ * {@link TemporalFactory#createInstant(Date)} method accepts null dates, which stand for
+ * undetermined position.
+ *
+ * @param begin the begin date, inclusive.
+ * @param end the end date, inclusive.
+ * @return the period.
+ * @throws UnsupportedOperationException if the temporal factory is not available on the module path.
+ */
+ public static Period createPeriod(final Date begin, final Date end) throws UnsupportedOperationException {
+ final TemporalFactory factory = getTemporalFactory();
+ return factory.createPeriod(createInstant(factory, begin), createInstant(factory, end));
+ }
+
+ /**
+ * Infers a value from the extent as a {@link Date} object.
+ * This method is used for compatibility with legacy API and may disappear in future SIS version.
+ *
+ * @param time the instant or period for which to get a date, or {@code null}.
+ * @return the requested time as a Java date, or {@code null} if none.
+ */
+ public static Date getDate(final TemporalPrimitive time) {
+ Instant instant;
+ if (time instanceof Instant) {
+ instant = (Instant) time;
+ } else if (time instanceof Period) {
+ instant = ((Period) time).getEnding();
+ if (instant == null) {
+ instant = ((Period) time).getBeginning();
+ }
+ } else {
+ return null;
+ }
+ return instant.getDate();
+ }
+
+ /**
+ * Temporary method, to be removed after we upgraded metadata to {@link java.time}.
+ *
+ * @param instant the Java instant, or {@code null}.
+ * @return the legacy Java date, or {@code null}.
+ */
+ public static Date toDate(final java.time.Instant instant) {
+ return (instant != null) ? Date.from(instant) : null;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleCitation.java
index cb66428e0f,ef28b60e0b..32ffb58faa
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleCitation.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleCitation.java
@@@ -21,18 -21,8 +21,18 @@@ import java.io.Serializable
import org.opengis.metadata.citation.Citation;
import org.opengis.util.InternationalString;
import org.apache.sis.util.SimpleInternationalString;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
+// Specific to the main branch:
+import java.util.Date;
+import java.util.Collection;
+import java.util.Collections;
+import org.opengis.metadata.Identifier;
+import org.opengis.metadata.citation.CitationDate;
+import org.opengis.metadata.citation.PresentationForm;
+import org.opengis.metadata.citation.ResponsibleParty;
+import org.opengis.metadata.citation.Series;
+
/**
* A trivial implementation of {@link Citation} containing only a title.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleDuration.java
index dc2db3159b,ca7d4a9c7e..cc1b740ee4
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleDuration.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleDuration.java
@@@ -16,10 -16,10 +16,10 @@@
*/
package org.apache.sis.metadata.simple;
- import org.apache.sis.util.internal.Numerics;
+ import org.apache.sis.util.privy.Numerics;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.Duration;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.temporal.Duration;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleExtent.java
index bf90988817,0c3f18d82d..195b46c59a
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleExtent.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleExtent.java
@@@ -21,11 -21,8 +21,11 @@@ import org.opengis.metadata.extent.Exte
import org.opengis.metadata.extent.GeographicExtent;
import org.opengis.metadata.extent.TemporalExtent;
import org.opengis.metadata.extent.VerticalExtent;
- import static org.apache.sis.util.internal.CollectionsExt.singletonOrEmpty;
+ import static org.apache.sis.util.privy.CollectionsExt.singletonOrEmpty;
+// Specific to the main branch:
+import org.opengis.util.InternationalString;
+
/**
* A trivial implementation of {@link Extent} containing only geographic, vertical and temporal extent.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleFormat.java
index 6a534aa5df,c60f33a316..5ac0d5a56a
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleFormat.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleFormat.java
@@@ -20,10 -20,10 +20,10 @@@ import java.util.Collection
import java.util.Collections;
import org.opengis.util.InternationalString;
import org.opengis.metadata.distribution.Format;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.citation.Citation;
+// Specific to the main branch:
+import org.opengis.metadata.distribution.Distributor;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java
index 980084434d,54a7bc5a76..7c24697c1c
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java
@@@ -46,15 -46,14 +46,15 @@@ import org.apache.sis.metadata.TypeValu
import org.apache.sis.metadata.ValueExistencePolicy;
import org.apache.sis.metadata.TitleProperty;
import org.apache.sis.metadata.iso.citation.Citations;
- import org.apache.sis.metadata.sql.util.SQLBuilder;
- import org.apache.sis.metadata.sql.util.Reflection;
- import org.apache.sis.util.internal.Constants;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.metadata.sql.privy.SQLBuilder;
+ import org.apache.sis.metadata.sql.privy.Reflection;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.privy.Strings;
import org.apache.sis.xml.IdentifiedObject;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.ControlledVocabulary;
+// Specific to the main branch:
+import org.opengis.util.CodeList;
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordSchema.java
index 2d9ec927c8,8b29bd616c..f556a041c2
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordSchema.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/DefaultRecordSchema.java
@@@ -37,8 -37,11 +37,8 @@@ import org.apache.sis.util.resources.Er
import org.apache.sis.util.collection.WeakValueHashMap;
import org.apache.sis.metadata.simple.SimpleAttributeType;
import org.apache.sis.converter.SurjectiveConverter;
- import org.apache.sis.util.internal.Strings;
+ import org.apache.sis.util.privy.Strings;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.AttributeType;
-
/**
* A collection of record types in a given namespace.
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/RecordDefinition.java
index e75e38fcdd,2cc12414c3..29ea7632df
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/RecordDefinition.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/util/iso/RecordDefinition.java
@@@ -28,11 -28,11 +28,11 @@@ import org.opengis.util.MemberName
import org.apache.sis.util.Classes;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.CharSequences;
- import org.apache.sis.util.internal.CollectionsExt;
+ import org.apache.sis.util.privy.CollectionsExt;
import org.apache.sis.pending.jdk.JDK19;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.feature.AttributeType;
+// Specific to the main branch:
+import org.apache.sis.metadata.simple.SimpleAttributeType;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/SpecializedIdentifier.java
index 677fe13679,b1f6810ba2..ca2e078830
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/SpecializedIdentifier.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/SpecializedIdentifier.java
@@@ -28,11 -28,11 +28,11 @@@ import org.apache.sis.xml.IdentifierMap
import org.apache.sis.xml.IdentifierSpace;
import org.apache.sis.xml.ValueConverter;
import org.apache.sis.util.resources.Messages;
- import org.apache.sis.util.internal.CloneAccess;
+ import org.apache.sis.util.privy.CloneAccess;
import org.apache.sis.metadata.iso.citation.Citations;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListUID.java
index fc68d434bb,e99dd9c49c..d75499ff48
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListUID.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/cat/CodeListUID.java
@@@ -24,11 -24,11 +24,11 @@@ import jakarta.xml.bind.annotation.XmlV
import org.opengis.util.CodeList;
import org.apache.sis.util.iso.Types;
import org.apache.sis.xml.bind.Context;
- import static org.apache.sis.metadata.internal.ImplementationHelper.ISO_NAMESPACE;
+ import static org.apache.sis.metadata.privy.ImplementationHelper.ISO_NAMESPACE;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.ControlledVocabulary;
-import org.opengis.annotation.ResourceBundles;
+// Specific to the main branch:
+import java.util.ResourceBundle;
- import org.apache.sis.util.internal.CodeLists;
++import org.apache.sis.util.privy.CodeLists;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
index 9526a3e513,51233a96ab..a629d31582
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TM_Primitive.java
@@@ -19,15 -19,15 +19,15 @@@ package org.apache.sis.xml.bind.gml
import java.util.Date;
import jakarta.xml.bind.annotation.XmlElement;
import org.opengis.temporal.TemporalPrimitive;
- import org.apache.sis.xml.util.XmlUtilities;
+ import org.apache.sis.xml.privy.XmlUtilities;
import org.apache.sis.xml.bind.Context;
import org.apache.sis.xml.bind.gco.PropertyType;
- import org.apache.sis.metadata.internal.TemporalUtilities;
+ import org.apache.sis.metadata.privy.TemporalUtilities;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.Period;
-import org.opengis.temporal.Instant;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.temporal.Period;
+import org.apache.sis.pending.geoapi.temporal.Instant;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimeInstant.java
index 92bfd07cc7,d643f739aa..764b47d09b
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimeInstant.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimeInstant.java
@@@ -23,11 -23,11 +23,11 @@@ import jakarta.xml.bind.annotation.XmlT
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import org.apache.sis.xml.bind.Context;
- import org.apache.sis.util.internal.Strings;
- import org.apache.sis.xml.util.XmlUtilities;
+ import org.apache.sis.util.privy.Strings;
+ import org.apache.sis.xml.privy.XmlUtilities;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.Instant;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.temporal.Instant;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriod.java
index 824c683909,e1ce2c79d1..317af7172e
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriod.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gml/TimePeriod.java
@@@ -21,11 -21,11 +21,11 @@@ import jakarta.xml.bind.annotation.XmlE
import jakarta.xml.bind.annotation.XmlElements;
import jakarta.xml.bind.annotation.XmlRootElement;
import org.apache.sis.xml.bind.Context;
- import org.apache.sis.util.internal.Strings;
- import static org.apache.sis.xml.util.LegacyNamespaces.VERSION_3_0;
+ import org.apache.sis.util.privy.Strings;
+ import static org.apache.sis.xml.privy.LegacyNamespaces.VERSION_3_0;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.Period;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.temporal.Period;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gts/TM_PeriodDuration.java
index 68ed3c7776,6024d50a5f..a2d7efbdd7
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gts/TM_PeriodDuration.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gts/TM_PeriodDuration.java
@@@ -25,13 -25,13 +25,13 @@@ import jakarta.xml.bind.annotation.XmlE
import org.opengis.temporal.PeriodDuration;
import org.opengis.util.InternationalString;
import org.apache.sis.xml.bind.Context;
- import org.apache.sis.xml.util.XmlUtilities;
+ import org.apache.sis.xml.privy.XmlUtilities;
import org.apache.sis.xml.bind.gco.PropertyType;
- import org.apache.sis.metadata.internal.TemporalUtilities;
+ import org.apache.sis.metadata.privy.TemporalUtilities;
import org.apache.sis.util.SimpleInternationalString;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.temporal.TemporalFactory;
+// Specific to the main branch:
+import org.apache.sis.pending.geoapi.temporal.TemporalFactory;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/Parameter.java
index d780f5367f,bc9760a5eb..c8f349f4df
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/Parameter.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/Parameter.java
@@@ -22,12 -22,11 +22,12 @@@ import org.opengis.metadata.Identifier
import org.opengis.parameter.ParameterValue;
import org.opengis.parameter.ParameterDescriptor;
import org.apache.sis.metadata.simple.SimpleIdentifiedObject;
- import org.apache.sis.metadata.internal.ReferencingServices;
+ import org.apache.sis.metadata.privy.ReferencingServices;
import org.apache.sis.util.ComparisonMode;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import static org.apache.sis.util.Utilities.deepEquals;
+// Specific to the main branch:
+import java.util.Set;
+import javax.measure.Unit;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
index 9aab6b4b1c,b05975e830..feb608a93c
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/QualityParameter.java
@@@ -36,8 -36,10 +36,8 @@@ import org.apache.sis.util.iso.Names
// Specific to the main and geoapi-3.1 branches:
import org.opengis.referencing.ReferenceIdentifier;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.coverage.Coverage;
-import org.opengis.metadata.quality.Description;
-import org.opengis.metadata.quality.ValueStructure;
+// Specific to the main branch:
- import org.apache.sis.metadata.internal.ReferencingServices;
++import org.apache.sis.metadata.privy.ReferencingServices;
/**
diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
index a35ed88b98,a6b2c50ba1..f5365cd727
--- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
+++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/metadata/replace/ServiceParameter.java
@@@ -37,8 -37,9 +37,8 @@@ import static org.apache.sis.util.privy
// Specific to the main and geoapi-3.1 branches:
import org.opengis.referencing.ReferenceIdentifier;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.parameter.ParameterDirection;
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
- import org.apache.sis.metadata.internal.ReferencingServices;
++import org.apache.sis.metadata.privy.ReferencingServices;
/**
diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/IdentifiersTest.java
index 0000000000,0a2c00c26b..2fe41a3f3a
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/IdentifiersTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/IdentifiersTest.java
@@@ -1,0 -1,111 +1,121 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.List;
+ import java.util.Arrays;
+ import java.util.ArrayList;
+ import org.opengis.metadata.Identifier;
+ import org.apache.sis.metadata.iso.DefaultIdentifier;
+ import org.apache.sis.metadata.iso.citation.DefaultCitation;
+
+ // Test dependencies
+ import org.junit.jupiter.api.Test;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.test.TestCase;
+
++// Specific to the main branch:
++import org.opengis.referencing.ReferenceIdentifier;
++
+
+ /**
+ * Tests {@link Identifiers}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @versio 1.0
+ */
+ public final class IdentifiersTest extends TestCase {
+ /**
+ * Creates a new test case.
+ */
+ public IdentifiersTest() {
+ }
+
+ /**
+ * Creates a citation with the given title and the given identifiers.
+ */
+ private static DefaultCitation citation(final String title, final Identifier... identifiers) {
+ DefaultCitation cit = new DefaultCitation(title);
+ cit.setIdentifiers(Arrays.asList(identifiers));
+ return cit;
+ }
+
+ /**
+ * Creates an identifier with a code space.
+ */
- private static Identifier identifier(final String codeSpace, final String code) {
- return new DefaultIdentifier(codeSpace, code, null);
++ private static ReferenceIdentifier identifier(final String codeSpace, final String code) {
++ return new Id(codeSpace, code);
++ }
++
++ @SuppressWarnings("serial")
++ private static final class Id extends DefaultIdentifier implements ReferenceIdentifier {
++ Id(String codeSpace, String code) {
++ super(codeSpace, code, null);
++ }
+ }
+
+ /**
+ * Tests {@link Identifiers#hasCommonIdentifier(Iterable, Iterable)}.
+ */
+ @Test
+ public void testHasCommonIdentifier() {
- final List<Identifier> id1 = new ArrayList<>(3);
- final List<Identifier> id2 = new ArrayList<>(2);
++ final List<ReferenceIdentifier> id1 = new ArrayList<>(3);
++ final List<ReferenceIdentifier> id2 = new ArrayList<>(2);
+ assertNull(Identifiers.hasCommonIdentifier(id1, id2));
+ /*
+ * Add codes for two Operation Methods which are implemented in Apache SIS by the same class:
+ *
+ * - EPSG:9804 — "Mercator (variant A)" (formerly known as "Mercator (1SP)").
+ * - EPSG:1026 — "Mercator (Spherical)"
+ * - GeoTIFF:7 — "CT_Mercator"
+ */
+ id1.add(identifier("EPSG", "9804"));
+ id1.add(identifier("EPSG", "1026"));
+ id1.add(identifier("GeoTIFF", "7"));
+ assertNull(Identifiers.hasCommonIdentifier(id1, id2));
+ /*
+ * EPSG:9841 is a legacy (now deprecated) code for "Mercator (1SP)".
+ * We could have declared it as a deprecated code in the above list,
+ * but for the sake of this test we do not.
+ */
+ id2.add(identifier("EPSG", "9841"));
+ assertEquals(Boolean.FALSE, Identifiers.hasCommonIdentifier(id1, id2));
+ id2.add(identifier("EPSG", "9804"));
+ assertEquals(Boolean.TRUE, Identifiers.hasCommonIdentifier(id1, id2));
+ }
+
+ /**
+ * Tests {@link Identifiers#getIdentifier(Citation, boolean)}.
+ */
+ @Test
+ public void testGetIdentifier() {
+ DefaultCitation citation = new DefaultCitation(" Not an identifier ");
+ assertEquals("Not an identifier", Identifiers.getIdentifier(citation, false));
+ assertNull(Identifiers.getIdentifier(citation, true));
+
+ citation = new DefaultCitation(" ValidIdentifier ");
+ assertEquals("ValidIdentifier", Identifiers.getIdentifier(citation, false));
+ assertEquals("ValidIdentifier", Identifiers.getIdentifier(citation, true));
+ /*
+ * Following test uses '-' in the first identifier, which is an invalid Unicode identifier part.
+ * Consequently, the identifier that we get depends on whether we ask for strict Unicode or not.
+ */
+ citation = citation("Web Map Server", identifier("OGC", "06-042"), identifier("ISO", "19128"));
+ assertEquals("OGC:06-042", Identifiers.getIdentifier(citation, false));
+ assertEquals("ISO_19128", Identifiers.getIdentifier(citation, true));
+ }
+ }
diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/MergerTest.java
index 0000000000,9320f39740..80c3a43e5f
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/MergerTest.java
+++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/privy/MergerTest.java
@@@ -1,0 -1,134 +1,131 @@@
+ /*
+ * 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.metadata.privy;
+
+ import java.util.Map;
+ import java.util.Set;
+ import java.util.List;
+ import java.util.Locale;
+ import java.util.Iterator;
+ import java.nio.charset.StandardCharsets;
+ import org.opengis.metadata.citation.Citation;
+ import org.opengis.metadata.content.ContentInformation;
+ import org.opengis.metadata.content.FeatureCatalogueDescription;
+ import org.opengis.metadata.content.ImagingCondition;
+ import org.opengis.metadata.content.ImageDescription;
+ import org.apache.sis.metadata.iso.DefaultIdentifier;
+ import org.apache.sis.metadata.iso.DefaultMetadata;
+ import org.apache.sis.metadata.iso.citation.DefaultCitation;
+ import org.apache.sis.metadata.iso.content.DefaultCoverageDescription;
+ import org.apache.sis.metadata.iso.content.DefaultFeatureCatalogueDescription;
+ import org.apache.sis.metadata.iso.content.DefaultImageDescription;
+
+ // Test dependencies
+ import org.junit.jupiter.api.Test;
+ import static org.junit.jupiter.api.Assertions.*;
+ import org.apache.sis.test.TestCase;
+ import static org.apache.sis.test.Assertions.assertSetEquals;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.content.CoverageDescription;
-
+
+ /**
+ * Tests the {@link Merger} class.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class MergerTest extends TestCase {
+ /**
+ * Creates a new test case.
+ */
+ public MergerTest() {
+ }
+
+ /**
+ * Creates a metadata sample with 3 content information of different kind.
+ */
+ private static DefaultMetadata createSample1() {
+ final DefaultFeatureCatalogueDescription features = new DefaultFeatureCatalogueDescription();
+ final DefaultCoverageDescription coverage = new DefaultCoverageDescription();
+ final DefaultImageDescription image = new DefaultImageDescription();
+ final DefaultMetadata metadata = new DefaultMetadata();
+ features.setFeatureCatalogueCitations(Set.of(new DefaultCitation("Shapefile")));
+ features.setIncludedWithDataset(Boolean.TRUE);
+ metadata.getContentInfo().add(features);
+
+ coverage.setProcessingLevelCode(new DefaultIdentifier("Level 1"));
+ metadata.getContentInfo().add(coverage);
+
+ image.setImagingCondition(ImagingCondition.CLOUD);
+ image.setCloudCoverPercentage(0.8);
+ metadata.getContentInfo().add(image);
+
+ metadata.setLocalesAndCharsets(Map.of(Locale.JAPANESE, StandardCharsets.UTF_16));
+ return metadata;
+ }
+
+ /**
+ * Creates a metadata sample with content information of different kind in a different order
+ * than the one created by {@link #createSample1()}.
+ */
+ private static DefaultMetadata createSample2() {
+ final DefaultFeatureCatalogueDescription features = new DefaultFeatureCatalogueDescription();
+ final DefaultImageDescription image = new DefaultImageDescription();
+ final DefaultMetadata metadata = new DefaultMetadata();
+ image.setProcessingLevelCode(new DefaultIdentifier("Level 2"));
+ metadata.getContentInfo().add(image);
+
+ features.setFeatureCatalogueCitations(Set.of(new DefaultCitation("GPX file")));
+ features.setIncludedWithDataset(Boolean.TRUE);
+ metadata.getContentInfo().add(features);
+
+ metadata.setLocalesAndCharsets(Map.of(Locale.FRENCH, StandardCharsets.UTF_8));
+ return metadata;
+ }
+
+ /**
+ * Tests a merge operation that merge also the collection elements. Such deep merge is a
+ * little bit aggressive; it may be desired in some occasions, but may also be dangerous.
+ */
+ @Test
+ public void testDeepMerge() {
+ final DefaultMetadata source = createSample1();
+ final DefaultMetadata target = createSample2();
+ final Merger merger = new Merger(null);
+ merger.copy(source, target);
+
+ assertSetEquals(List.of(Locale.JAPANESE, Locale.FRENCH),
+ target.getLocalesAndCharsets().keySet());
+ assertSetEquals(List.of(StandardCharsets.UTF_16, StandardCharsets.UTF_8),
+ target.getLocalesAndCharsets().values());
+
+ final Iterator<ContentInformation> it = target.getContentInfo().iterator();
+ final ImageDescription image = (ImageDescription) it.next();
+ final FeatureCatalogueDescription features = (FeatureCatalogueDescription) it.next();
- final CoverageDescription coverage = (CoverageDescription) it.next();
++ final DefaultCoverageDescription coverage = (DefaultCoverageDescription) it.next();
+ assertFalse(it.hasNext());
+
+ assertEquals(ImagingCondition.CLOUD, image .getImagingCondition());
+ assertEquals(Double.valueOf(0.8), image .getCloudCoverPercentage());
+ assertEquals("Level 2", image .getProcessingLevelCode().getCode());
+ assertEquals("Level 1", coverage.getProcessingLevelCode().getCode());
+ assertEquals(Boolean.TRUE, features.isIncludedWithDataset());
+
+ final Iterator<? extends Citation> ci = features.getFeatureCatalogueCitations().iterator();
+ assertEquals("GPX file", ci.next().getTitle().toString());
+ assertEquals("Shapefile", ci.next().getTitle().toString());
+ assertFalse(ci.hasNext());
+ }
+ }
diff --cc endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/style/se1/StyleFactory.java
index c500734661,90e2f83f46..40b2e80ede
--- a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/style/se1/StyleFactory.java
+++ b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/style/se1/StyleFactory.java
@@@ -19,12 -19,13 +19,12 @@@ package org.apache.sis.style.se1
import java.net.URI;
import java.awt.Color;
import java.util.Objects;
- import org.apache.sis.feature.internal.AttributeConvention;
+ import org.apache.sis.feature.privy.AttributeConvention;
import org.apache.sis.metadata.iso.citation.DefaultOnlineResource;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.filter.Literal;
-import org.opengis.filter.FilterFactory;
-import org.opengis.filter.ValueReference;
+// Specific to the main branch:
+import org.apache.sis.filter.Expression;
+import org.apache.sis.filter.DefaultFilterFactory;
/**
diff --cc endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ModifiableLocationTypeAdapter.java
index e3a7f657fb,0000000000..b2652152c0
mode 100644,000000..100644
--- a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ModifiableLocationTypeAdapter.java
+++ b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ModifiableLocationTypeAdapter.java
@@@ -1,101 -1,0 +1,101 @@@
+/*
+ * 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.gazetteer;
+
+import java.util.List;
+import java.util.Map;
+import java.util.IdentityHashMap;
+import org.opengis.util.InternationalString;
- import org.apache.sis.util.internal.UnmodifiableArrayList;
++import org.apache.sis.util.privy.UnmodifiableArrayList;
+
+
+/**
+ * Workaround for the lack of {@code LocationType} interface in GeoAPI 3.0.
+ * This workaround will be removed if a future GeoAPI version publish that interface,
+ * or if {@link AbstractLocationType} is made public.
+ */
+final class ModifiableLocationTypeAdapter extends ModifiableLocationType {
+ /**
+ * The reference system of the original type.
+ * This is the only information not stored in {@link ModifiableLocationType}.
+ */
+ private final ReferencingByIdentifiers referenceSystem;
+
+ /**
+ * Copies all information from the given type.
+ */
+ private ModifiableLocationTypeAdapter(final AbstractLocationType type,
+ final Map<AbstractLocationType,ModifiableLocationTypeAdapter> previous)
+ {
+ super(type.getName());
+ setTheme(type.getTheme());
+ setDefinition(type.getDefinition());
+ setTerritoryOfUse(type.getTerritoryOfUse());
+ setOwner(type.getOwner());
+ for (final InternationalString s : type.getIdentifications()) {
+ addIdentification(s);
+ }
+ referenceSystem = type.getReferenceSystem();
+ for (final AbstractLocationType c : type.getChildren()) {
+ ModifiableLocationTypeAdapter p = previous.get(c);
+ if (p == null) {
+ p = new ModifiableLocationTypeAdapter(c, previous);
+ previous.put(c, p);
+ }
+ p.addParent(this);
+ }
+ }
+
+ /**
+ * Returns type type as-is if it is already an instance of {@code ModifiableLocationType},
+ * or returns a copy otherwise.
+ */
+ static ModifiableLocationType copy(final AbstractLocationType type) {
+ if (type instanceof ModifiableLocationType) {
+ return (ModifiableLocationType) type;
+ } else {
+ return new ModifiableLocationTypeAdapter(type,
+ new IdentityHashMap<AbstractLocationType,ModifiableLocationTypeAdapter>());
+ }
+ }
+
+ /**
+ * Copies a list of location types.
+ */
+ static List<ModifiableLocationType> copy(final List<? extends AbstractLocationType> types) {
+ final Map<AbstractLocationType,ModifiableLocationTypeAdapter> previous = new IdentityHashMap<>();
+ final ModifiableLocationType[] nt = new ModifiableLocationType[types.size()];
+ for (int i=0; i<nt.length; i++) {
+ final AbstractLocationType c = types.get(i);
+ ModifiableLocationTypeAdapter p = previous.get(c);
+ if (p == null) {
+ p = new ModifiableLocationTypeAdapter(c, previous);
+ previous.put(c, p);
+ }
+ nt[i] = p;
+ }
+ return UnmodifiableArrayList.wrap(nt);
+ }
+
+ /**
+ * Returns the reference system of the type given to the constructor.
+ */
+ @Override
+ public ReferencingByIdentifiers getReferenceSystem() {
+ return referenceSystem;
+ }
+}
diff --cc endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ReferencingByIdentifiers.java
index 7b10d323c7,c7dd878f07..7d35fb2f14
--- a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ReferencingByIdentifiers.java
+++ b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/ReferencingByIdentifiers.java
@@@ -40,13 -40,16 +40,13 @@@ import org.apache.sis.io.wkt.ElementKin
import org.apache.sis.io.wkt.FormattableObject;
import org.apache.sis.metadata.iso.extent.Extents;
import org.apache.sis.metadata.iso.citation.Citations;
- import org.apache.sis.referencing.util.WKTUtilities;
+ import org.apache.sis.referencing.privy.WKTUtilities;
import org.apache.sis.system.Modules;
- import org.apache.sis.util.internal.Constants;
+ import org.apache.sis.util.privy.Constants;
import org.apache.sis.util.resources.Vocabulary;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.citation.Party;
-import org.opengis.referencing.gazetteer.Location;
-import org.opengis.referencing.gazetteer.LocationType;
-import org.opengis.referencing.gazetteer.ReferenceSystemUsingIdentifiers;
+// Specific to the main branch:
+import org.apache.sis.metadata.iso.citation.AbstractParty;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/GeneralEnvelope.java
index 0b9d7c46de,ac6e5d7a85..13be0b558b
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/GeneralEnvelope.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/geometry/GeneralEnvelope.java
@@@ -35,9 -35,10 +35,9 @@@ import org.opengis.referencing.crs.Coor
import org.opengis.geometry.Envelope;
import org.opengis.geometry.DirectPosition;
import org.opengis.geometry.MismatchedDimensionException;
-import org.opengis.geometry.MismatchedReferenceSystemException;
import org.opengis.metadata.extent.GeographicBoundingBox;
- import org.apache.sis.referencing.util.TemporalAccessor;
- import org.apache.sis.referencing.util.AxisDirections;
+ import org.apache.sis.referencing.privy.TemporalAccessor;
+ import org.apache.sis.referencing.privy.AxisDirections;
import org.apache.sis.util.ArgumentCheckByAssertion;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.referencing.CRS;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
index 159812dccf,71eeef9f3d..4e2dccc237
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
@@@ -55,13 -55,13 +55,13 @@@ import org.apache.sis.util.OptionalCand
import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.system.Loggers;
- import org.apache.sis.util.internal.Constants;
- import org.apache.sis.util.internal.StandardDateFormat;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.privy.StandardDateFormat;
import org.apache.sis.referencing.ImmutableIdentifier;
- import org.apache.sis.referencing.util.ReferencingFactoryContainer;
+ import org.apache.sis.referencing.privy.ReferencingFactoryContainer;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.apache.sis.metadata.iso.DefaultIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
index 4438dae6b8,583077a13e..d4d929128d
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterTableRow.java
@@@ -40,15 -40,12 +40,15 @@@ import org.apache.sis.util.Characters
import org.apache.sis.util.Deprecable;
import org.apache.sis.measure.Range;
import org.apache.sis.measure.RangeFormat;
- import org.apache.sis.metadata.internal.NameToIdentifier;
- import org.apache.sis.util.internal.X364;
- import static org.apache.sis.util.internal.X364.*;
+ import org.apache.sis.metadata.privy.NameToIdentifier;
+ import org.apache.sis.util.privy.X364;
+ import static org.apache.sis.util.privy.X364.*;
import static org.apache.sis.util.CharSequences.spaces;
- import static org.apache.sis.util.internal.Constants.DEFAULT_SEPARATOR;
+ import static org.apache.sis.util.privy.Constants.DEFAULT_SEPARATOR;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
+
/**
* A row in the table to be formatted by {@link ParameterFormat}.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
index 329b57d998,d6dfbb4462..d7a3cbd102
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeographicCRS.java
@@@ -33,14 -33,14 +33,14 @@@ import org.apache.sis.referencing.cs.Ab
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.measure.Longitude;
import org.apache.sis.util.resources.Errors;
- import static org.apache.sis.util.internal.Constants.CRS;
- import static org.apache.sis.util.internal.Constants.EPSG;
- import static org.apache.sis.util.internal.Constants.CRS27;
- import static org.apache.sis.util.internal.Constants.CRS83;
- import static org.apache.sis.util.internal.Constants.CRS84;
+ import static org.apache.sis.util.privy.Constants.CRS;
+ import static org.apache.sis.util.privy.Constants.EPSG;
+ import static org.apache.sis.util.privy.Constants.CRS27;
+ import static org.apache.sis.util.privy.Constants.CRS83;
+ import static org.apache.sis.util.privy.Constants.CRS84;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
index df30b214ed,9114ae99ff..10ea0de84d
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultEngineeringDatum.java
@@@ -22,11 -22,11 +22,11 @@@ import jakarta.xml.bind.annotation.XmlR
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
import org.opengis.referencing.datum.EngineeringDatum;
- import org.apache.sis.referencing.util.WKTKeywords;
+ import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.io.wkt.Formatter;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
index 72f895f4fe,c586bc1316..14d96f2598
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java
@@@ -53,10 -53,10 +53,10 @@@ import org.apache.sis.io.wkt.Formatter
import static org.apache.sis.util.Utilities.deepEquals;
import static org.apache.sis.util.ArgumentChecks.ensureNonNull;
import static org.apache.sis.util.ArgumentChecks.ensureNonNullElement;
- import static org.apache.sis.referencing.util.WKTUtilities.toFormattable;
+ import static org.apache.sis.referencing.privy.WKTUtilities.toFormattable;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java
index f6980fc4e8,1d5096e042..312bd0afef
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultParametricDatum.java
@@@ -20,9 -20,12 +20,9 @@@ import java.util.Date
import java.util.Map;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlType;
- import org.apache.sis.referencing.util.WKTKeywords;
+ import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.io.wkt.Formatter;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.referencing.datum.ParametricDatum;
-
/**
* Defines the origin of a parametric coordinate reference system.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
index 5a304dc830,f9b65af4de..44ef22bf56
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java
@@@ -28,13 -28,13 +28,13 @@@ import org.opengis.referencing.datum.Ve
import org.apache.sis.io.wkt.Formatter;
import org.apache.sis.util.ComparisonMode;
import org.apache.sis.xml.bind.Context;
- import org.apache.sis.xml.util.LegacyNamespaces;
- import org.apache.sis.referencing.util.WKTKeywords;
+ import org.apache.sis.xml.privy.LegacyNamespaces;
+ import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.referencing.internal.VerticalDatumTypes;
- import org.apache.sis.metadata.internal.ImplementationHelper;
+ import org.apache.sis.metadata.privy.ImplementationHelper;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
index eea7d36618,49478aaa44..7ff3d2f510
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/EPSGDataAccess.java
@@@ -118,14 -118,11 +118,14 @@@ import org.apache.sis.measure.NumberRan
import org.apache.sis.measure.Units;
import org.apache.sis.pending.jdk.JDK16;
import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
- import static org.apache.sis.util.internal.StandardDateFormat.UTC;
+ import static org.apache.sis.util.privy.StandardDateFormat.UTC;
import static org.apache.sis.referencing.internal.ServicesForMetadata.CONNECTION;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.apache.sis.referencing.internal.ServicesForMetadata;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+import org.apache.sis.referencing.factory.GeodeticObjectFactory;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
index ee688c4ccf,cf1b8f5f42..1682f75053
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/sql/TableInfo.java
@@@ -22,14 -22,9 +22,14 @@@ import org.opengis.referencing.crs.*
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.parameter.ParameterDescriptor;
- import org.apache.sis.referencing.util.WKTKeywords;
+ import org.apache.sis.referencing.privy.WKTKeywords;
import org.apache.sis.util.CharSequences;
+// Specific to the main branch:
+import org.apache.sis.referencing.crs.DefaultParametricCRS;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+
/**
* Information about a specific table. The MS-Access dialect of SQL is assumed;
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
index 9477b5795a,a5abfd686b..73d7c3de26
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Legacy.java
@@@ -27,21 -27,8 +27,21 @@@ import org.apache.sis.referencing.Commo
import org.apache.sis.referencing.cs.CoordinateSystems;
import org.apache.sis.referencing.cs.DefaultCartesianCS;
import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
- import org.apache.sis.referencing.util.ReferencingUtilities;
+ import org.apache.sis.referencing.privy.ReferencingUtilities;
+// Specific to the main branch:
+import java.util.List;
+import java.util.Objects;
+import java.util.Collection;
+import org.opengis.referencing.ReferenceSystem;
+import org.opengis.referencing.IdentifiedObject;
+import org.opengis.util.InternationalString;
+import org.opengis.metadata.extent.Extent;
+import org.opengis.referencing.datum.Datum;
+import org.opengis.referencing.operation.CoordinateOperation;
+import org.apache.sis.referencing.DefaultObjectDomain;
+import org.apache.sis.referencing.AbstractIdentifiedObject;
+
/**
* Utilities related to version 1 of Well Known Text format, or to ISO 19111:2007.
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ServicesForMetadata.java
index 8aed5ce6bc,4a6635c71b..e4d5b83d55
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ServicesForMetadata.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/ServicesForMetadata.java
@@@ -46,7 -46,6 +46,7 @@@ import org.apache.sis.geometry.Envelope
import org.apache.sis.geometry.AbstractEnvelope;
import org.apache.sis.geometry.DirectPosition2D;
import org.apache.sis.geometry.CoordinateFormat;
- import org.apache.sis.metadata.internal.ReferencingServices;
++import org.apache.sis.metadata.privy.ReferencingServices;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.IdentifiedObjects;
@@@ -69,26 -69,8 +69,26 @@@ import org.apache.sis.util.privy.Consta
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.logging.Logging;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import java.util.Map;
+import org.opengis.util.NoSuchIdentifierException;
+import org.opengis.util.TypeName;
+import org.opengis.referencing.ReferenceIdentifier;
+import org.opengis.referencing.crs.CRSFactory;
+import org.opengis.referencing.cs.CSFactory;
+import org.opengis.referencing.cs.CoordinateSystemAxis;
+import org.opengis.referencing.operation.MathTransformFactory;
+import org.opengis.referencing.operation.OperationMethod;
+import org.opengis.referencing.operation.SingleOperation;
+import org.opengis.referencing.datum.Datum;
+import org.opengis.referencing.datum.DatumFactory;
- import org.apache.sis.metadata.internal.NameToIdentifier;
++import org.apache.sis.metadata.privy.NameToIdentifier;
+import org.apache.sis.util.Deprecable;
+import org.apache.sis.referencing.cs.DefaultParametricCS;
+import org.apache.sis.referencing.datum.DefaultParametricDatum;
+import org.apache.sis.referencing.factory.GeodeticObjectFactory;
+import org.apache.sis.referencing.factory.InvalidGeodeticParameterException;
+import org.apache.sis.metadata.iso.citation.DefaultCitation;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
index 48cd4acc77,093ba91f45..118f2a75d7
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/projection/NormalizedProjection.java
@@@ -49,15 -49,15 +49,15 @@@ import org.apache.sis.referencing.opera
import org.apache.sis.referencing.operation.transform.MathTransformProvider;
import org.apache.sis.referencing.operation.transform.DomainDefinition;
import org.apache.sis.referencing.operation.provider.MapProjection;
- import org.apache.sis.referencing.util.CoordinateOperations;
- import org.apache.sis.referencing.util.Formulas;
+ import org.apache.sis.referencing.privy.CoordinateOperations;
+ import org.apache.sis.referencing.privy.Formulas;
import org.apache.sis.system.Modules;
- import org.apache.sis.util.internal.Constants;
- import org.apache.sis.util.internal.Numerics;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.privy.Numerics;
import org.apache.sis.util.resources.Errors;
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
+// Specific to the main branch:
+import org.opengis.referencing.ReferenceIdentifier;
/**
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
index 0000000000,f660488a99..408a075fb6
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/AxisDirections.java
@@@ -1,0 -1,834 +1,831 @@@
+ /*
+ * 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.privy;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.Objects;
+ import javax.measure.Unit;
+ import javax.measure.quantity.Angle;
+ import org.opengis.annotation.UML;
+ import org.opengis.referencing.cs.AxisDirection;
+ import org.opengis.referencing.cs.CoordinateSystem;
+ import org.opengis.referencing.cs.CoordinateSystemAxis;
+ import static org.opengis.referencing.cs.AxisDirection.*;
+ import static org.opengis.annotation.Obligation.CONDITIONAL;
+ import org.apache.sis.metadata.privy.NameToIdentifier;
+ import org.apache.sis.util.ComparisonMode;
+ import org.apache.sis.util.Characters;
+ import org.apache.sis.util.Utilities;
+ import org.apache.sis.util.ArraysExt;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.util.iso.Types;
+ import org.apache.sis.measure.Units;
+ import static org.apache.sis.util.CharSequences.*;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import static org.opengis.annotation.Specification.ISO_19162;
++// Specific to the main branch:
++import static org.opengis.annotation.Specification.UNSPECIFIED;
+
+
+ /**
+ * Utilities methods related to {@link AxisDirection}.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class AxisDirections extends Static {
+ /**
+ * Number of directions like "North", "North-North-East", "North-East", etc.
+ * The first of those directions is {@link AxisDirection#NORTH}.
+ */
+ public static final int COMPASS_COUNT = 16;
+
+ /**
+ * Number of geocentric directions.
+ * The first of those directions is {@link AxisDirection#GEOCENTRIC_X}.
+ */
+ public static final int GEOCENTRIC_COUNT = 3;
+
+ /**
+ * Number of directions like "Display right", "Display down", etc.
+ * The first of those directions is {@link AxisDirection#DISPLAY_RIGHT}.
+ */
+ public static final int DISPLAY_COUNT = 4;
+
+ /**
+ * Ordinal of the last element in the {@link AxisDirection} code list.
+ * This is used for differentiating the standard codes from the user-defined ones.
+ *
+ * @see #isUserDefined(AxisDirection)
+ */
+ private static final int LAST_ORDINAL = DISPLAY_DOWN.ordinal();
+
+ /**
+ * Forward direction.
+ * For an observer at the centre of the object this is will be towards its front, bow or nose.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="forward", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="forward", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection FORWARD = AxisDirection.valueOf("FORWARD");
- /*
- * TODO: remove @Disabled in `WKTParserTest` after the code list values in this class have been removed.
- */
+
+ /**
+ * Starboard direction.
+ * For an observer at the centre of the object this will be towards its right.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="starboard", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="starboard", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection STARBOARD = AxisDirection.valueOf("STARBOARD");
+
+ /**
+ * Port direction.
+ * For an observer at the centre of the object this will be towards its left.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="port", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection PORT = AxisDirection.valueOf("PORT");
+
+ /**
+ * Direction of geographic angles (bearing).
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="clockwise", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="clockwise", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection CLOCKWISE = AxisDirection.valueOf("CLOCKWISE");
+
+ /**
+ * Direction of arithmetic angles. Used in polar coordinate systems.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="counterClockwise", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection COUNTER_CLOCKWISE = AxisDirection.valueOf("COUNTER_CLOCKWISE");
+
+ /**
+ * Distance from the origin in a polar coordinate system.
+ * Added in ISO 19111:2019 (was not in ISO 19111:2007).
+ */
- @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=ISO_19162)
++ @UML(identifier="awayFrom", obligation=CONDITIONAL, specification=UNSPECIFIED)
+ public static final AxisDirection AWAY_FROM = AxisDirection.valueOf("AWAY_FROM");
+
+ /**
+ * For each direction, the opposite direction.
+ * This map shall be immutable after construction.
+ */
+ private static final Map<AxisDirection,AxisDirection> OPPOSITES = new HashMap<>(20);
+ static {
+ put(OTHER, OTHER);
+ put(NORTH, SOUTH);
+ put(NORTH_NORTH_EAST, SOUTH_SOUTH_WEST);
+ put(NORTH_EAST, SOUTH_WEST);
+ put(EAST_NORTH_EAST, WEST_SOUTH_WEST);
+ put(EAST, WEST);
+ put(EAST_SOUTH_EAST, WEST_NORTH_WEST);
+ put(SOUTH_EAST, NORTH_WEST);
+ put(SOUTH_SOUTH_EAST, NORTH_NORTH_WEST);
+ put(UP, DOWN);
+ put(FUTURE, PAST);
+ put(COLUMN_POSITIVE, COLUMN_NEGATIVE);
+ put(ROW_POSITIVE, ROW_NEGATIVE);
+ put(DISPLAY_RIGHT, DISPLAY_LEFT);
+ put(DISPLAY_UP, DISPLAY_DOWN);
+ put(COUNTER_CLOCKWISE, CLOCKWISE);
+ }
+
+ /**
+ * Stores the given directions in the {@link #OPPOSITES} array.
+ */
+ private static void put(final AxisDirection dir, final AxisDirection opposite) {
+ OPPOSITES.put(dir, opposite);
+ OPPOSITES.put(opposite, dir);
+ }
+
+ /**
+ * Proposed abbreviations for some axis directions.
+ */
+ private static final Map<AxisDirection,String> ABBREVIATIONS = Map.of(
+ FUTURE, "t",
+ COLUMN_POSITIVE, "i",
+ ROW_POSITIVE, "j",
+ DISPLAY_RIGHT, "x",
+ DISPLAY_UP, "y",
+ OTHER, "z", // Arbitrary abbreviation, may change in any future SIS version.
+ AWAY_FROM, "r",
+ COUNTER_CLOCKWISE, "θ");
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private AxisDirections() {
+ }
+
+ /**
+ * Returns the "absolute" direction of the given direction.
+ * This "absolute" operation is similar to the {@code Math.abs(int)} method in that "negative" directions like
+ * ({@code SOUTH}, {@code WEST}, {@code DOWN}, {@code PAST}) are changed for their "positive" counterparts
+ * ({@code NORTH}, {@code EAST}, {@code UP}, {@code FUTURE}).
+ * More specifically, the following conversion table is applied:
+ *
+ * <table class="sis">
+ * <caption>Mapping to "absolute" directions</caption><tr>
+ * <tr><th>Directions</th> <th>Absolute value</th></tr>
+ * <tr><td>{@code NORTH}, {@code SOUTH}</td> <td>{@code NORTH}</td></tr>
+ * <tr><td>{@code EAST}, {@code WEST}</td> <td>{@code EAST}</td></tr>
+ * <tr><td>{@code UP}, {@code DOWN}</td> <td>{@code UP}</td></tr>
+ * <tr><td>{@code FUTURE}, {@code PAST}</td> <td>{@code FUTURE}</td></tr>
+ * <tr><td>{@code COLUMN_POSITIVE}, {@code COLUMN_NEGATIVE}</td> <td>{@code COLUMN_POSITIVE}</td></tr>
+ * <tr><td>{@code ROW_POSITIVE}, {@code ROW_NEGATIVE}</td> <td>{@code ROW_POSITIVE}</td></tr>
+ * <tr><td>{@code DISPLAY_RIGHT}, {@code DISPLAY_LEFT}</td> <td>{@code DISPLAY_RIGHT}</td></tr>
+ * <tr><td>{@code DISPLAY_UP}, {@code DISPLAY_DOWN}</td> <td>{@code DISPLAY_UP}</td></tr>
+ * <tr><td>{@code CLOCKWISE}, {@code COUNTERCLOCKWISE}</td> <td>{@code COUNTERCLOCKWISE}</td></tr>
+ * <tr><td>{@code OTHER}</td> <td>{@code OTHER}</td></tr>
+ * </table>
+ *
+ * @param dir the direction for which to return the absolute direction, or {@code null}.
+ * @return the direction from the above table, or {@code null} if the given direction was null.
+ */
+ public static AxisDirection absolute(AxisDirection dir) {
+ final AxisDirection opposite = opposite(dir);
+ if (opposite != null) {
+ if (opposite.ordinal() < dir.ordinal()) {
+ dir = opposite;
+ }
+ // Below is a temporary patch pending integration of code list values into GeoAPI.
+ // We need this patch because we cannot rely on ordinal() value for custom codes.
+ if (dir == CLOCKWISE) {
+ dir = COUNTER_CLOCKWISE;
+ }
+ }
+ return dir;
+ }
+
+ /**
+ * Returns the opposite direction of the given direction. The opposite direction of
+ * {@code NORTH} is {@code SOUTH}, and the opposite direction of {@code SOUTH} is {@code NORTH}.
+ * The same applies to {@code EAST}-{@code WEST}, {@code UP}-{@code DOWN} and {@code FUTURE}-{@code PAST},
+ * <i>etc.</i> If the given axis direction has no opposite, then this method returns {@code null}.
+ *
+ * @param dir the direction for which to return the opposite direction, or {@code null}.
+ * @return the opposite direction, or {@code null} if none or unknown.
+ */
+ public static AxisDirection opposite(AxisDirection dir) {
+ return OPPOSITES.get(dir);
+ }
+
+ /**
+ * Returns {@code true} if the given direction is an "opposite" direction.
+ * If the given argument is {@code null} or is not a known direction, then
+ * this method conservatively returns {@code false}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is an "opposite".
+ */
+ public static boolean isOpposite(final AxisDirection dir) {
+ final AxisDirection opposite = opposite(dir);
+ return (opposite != null) && opposite.ordinal() < dir.ordinal();
+ }
+
+ /**
+ * Returns {@code true} if the specified direction is a compass direction.
+ * Compass directions are {@code NORTH}, {@code EAST}, {@code NORTH_EAST}, etc.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is a compass direction.
+ *
+ * @see #angleForCompass(AxisDirection, AxisDirection)
+ */
+ public static boolean isCompass(final AxisDirection dir) {
+ if (dir == null) return false;
+ final int n = dir.ordinal() - NORTH.ordinal();
+ return n >= 0 && n < COMPASS_COUNT;
+ }
+
+ /**
+ * Returns {@code true} if the specified direction is cardinal direction.
+ * Cardinal directions are {@code NORTH}, {@code SOUTH}, {@code EAST} and {@code WEST}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is a cardinal direction.
+ */
+ public static boolean isCardinal(final AxisDirection dir) {
+ if (dir == null) return false;
+ final int n = dir.ordinal() - NORTH.ordinal();
+ return n >= 0 && n < COMPASS_COUNT && (n & 3) == 0;
+ }
+
+ /**
+ * Returns {@code true} if the specified direction is an inter-cardinal direction.
+ * Inter-cardinal directions are {@code NORTH_EAST}, {@code SOUTH_SOUTH_EAST}, etc.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is an inter-cardinal direction.
+ */
+ public static boolean isIntercardinal(final AxisDirection dir) {
+ if (dir == null) return false;
+ final int n = dir.ordinal() - NORTH.ordinal();
+ return n >= 0 && n < COMPASS_COUNT && (n & 3) != 0;
+ }
+
+ /**
+ * Returns {@code true} if the given direction is {@code UP} or {@code DOWN}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the direction is vertical, or {@code false} otherwise.
+ */
+ public static boolean isVertical(final AxisDirection dir) {
+ return dir == UP || dir == DOWN;
+ }
+
+ /**
+ * Returns {@code true} if the given direction is {@code FUTURE} or {@code PAST}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the direction is temporal, or {@code false} otherwise.
+ */
+ public static boolean isTemporal(final AxisDirection dir) {
+ return dir == FUTURE || dir == PAST;
+ }
+
+ /**
+ * Returns {@code true} if the given direction is {@code GEOCENTRIC_X}, {@code GEOCENTRIC_Y}
+ * or {@code GEOCENTRIC_Z}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is one of geocentric directions.
+ */
+ public static boolean isGeocentric(final AxisDirection dir) {
+ return dir == GEOCENTRIC_X || dir == GEOCENTRIC_Y || dir == GEOCENTRIC_Z;
+ }
+
+ /**
+ * Returns {@code true} if the given direction is a spatial axis direction (including vertical and geocentric axes).
+ * The current implementation conservatively returns {@code true} for every non-null directions except a hard-coded
+ * set of directions which are known to be non-spatial. We conservatively accept unknown axis directions because
+ * some of them are created from strings like "South along 90°E".
+ *
+ * <p>If the {@code image} argument is {@code true}, then this method additionally accepts grid and display
+ * axis directions.</p>
+ *
+ * <p>The rules implemented by this method may change in any future SIS version.</p>
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @param image {@code true} for accepting grid and image axis directions in addition to spatial ones.
+ * @return {@code true} if the given direction is presumed for spatial CS.
+ */
+ public static boolean isSpatialOrUserDefined(final AxisDirection dir, final boolean image) {
+ if (dir == null) return false;
+ final int ordinal = dir.ordinal();
+ return ordinal < FUTURE.ordinal() || ordinal > (image ? PAST : DISPLAY_DOWN).ordinal();
+ }
+
+ /**
+ * Returns {@code true} if the given direction is a user-defined direction (i.e. is not defined by GeoAPI).
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is user-defined.
+ */
+ public static boolean isUserDefined(final AxisDirection dir) {
+ return (dir != null) && dir.ordinal() > LAST_ORDINAL;
+ }
+
+ /**
+ * Returns {@code true} if the given direction is {@code COLUMN_POSITIVE}, {@code COLUMN_NEGATIVE},
+ * {@code ROW_POSITIVE} or {@code ROW_NEGATIVE}.
+ *
+ * @param dir the direction to test, or {@code null}.
+ * @return {@code true} if the given direction is presumed for grid CS.
+ */
+ public static boolean isGrid(final AxisDirection dir) {
+ if (dir == null) return false;
+ final int ordinal = dir.ordinal();
+ return ordinal >= COLUMN_POSITIVE.ordinal() && ordinal <= ROW_NEGATIVE.ordinal();
+ }
+
+ /**
+ * Arithmetic angle between forward/aft/port/starboard directions only.
+ * This is the angle as viewed from above the vehicle.
+ *
+ * @param source the start direction.
+ * @param target the final direction.
+ * @return the angle as a multiple of 90°, or {@link Integer#MIN_VALUE} if none.
+ */
+ public static int angleForVehicle(final AxisDirection source, final AxisDirection target) {
+ if (source == STARBOARD && target == FORWARD) return +1;
+ if (source == FORWARD && target == STARBOARD) return -1;
+ return Integer.MIN_VALUE;
+ }
+
+ /**
+ * Angle between geocentric directions only.
+ *
+ * @param source the start direction.
+ * @param target the final direction.
+ * @return the angle as a multiple of 90°, or {@link Integer#MIN_VALUE} if none.
+ */
+ public static int angleForGeocentric(final AxisDirection source, final AxisDirection target) {
+ final int base = GEOCENTRIC_X.ordinal();
+ final int src = source.ordinal() - base;
+ if (src >= 0 && src < GEOCENTRIC_COUNT) {
+ final int tgt = target.ordinal() - base;
+ if (tgt >= 0 && tgt < GEOCENTRIC_COUNT) {
+ int n = (tgt - src);
+ n -= GEOCENTRIC_COUNT * (n/2); // If -2 add 3. If +2 subtract 3. Otherwise do nothing.
+ return n;
+ }
+ }
+ return Integer.MIN_VALUE;
+ }
+
+ /**
+ * Arithmetic angle between compass directions only (not for angle between direction along meridians).
+ *
+ * @param source the start direction.
+ * @param target the final direction.
+ * @return the arithmetic angle as a multiple of 360/{@link #COMPASS_COUNT}, or {@link Integer#MIN_VALUE} if none.
+ *
+ * @see #isCompass(AxisDirection)
+ */
+ public static int angleForCompass(final AxisDirection source, final AxisDirection target) {
+ final int base = NORTH.ordinal();
+ final int src = source.ordinal() - base;
+ if (src >= 0 && src < COMPASS_COUNT) {
+ final int tgt = target.ordinal() - base;
+ if (tgt >= 0 && tgt < COMPASS_COUNT) {
+ int n = src - tgt;
+ if (n < -COMPASS_COUNT/2) {
+ n += COMPASS_COUNT;
+ } else if (n > COMPASS_COUNT/2) {
+ n -= COMPASS_COUNT;
+ }
+ return n;
+ }
+ }
+ return Integer.MIN_VALUE;
+ }
+
+ /**
+ * Arithmetic angle between display directions only.
+ *
+ * @param source the start direction.
+ * @param target the final direction.
+ * @return the arithmetic angle as a multiple of 360/{@link #DISPLAY_COUNT}, or {@link Integer#MIN_VALUE} if none.
+ */
+ public static int angleForDisplay(final AxisDirection source, final AxisDirection target) {
+ final int base = DISPLAY_RIGHT.ordinal();
+ int src = source.ordinal() - base;
+ if (src >= 0 && src < DISPLAY_COUNT) {
+ int tgt = target.ordinal() - base;
+ if (tgt >= 0 && tgt < DISPLAY_COUNT) {
+ /*
+ * Display directions are RIGHT, LEFT, UP, DOWN. We need to reorder them as UP, RIGHT, DOWN, LEFT.
+ */
+ src = DISPLAY_ORDER[src];
+ tgt = DISPLAY_ORDER[tgt];
+ int n = src - tgt;
+ if (n < -DISPLAY_COUNT/2) {
+ n += DISPLAY_COUNT;
+ } else if (n > DISPLAY_COUNT/2) {
+ n -= DISPLAY_COUNT;
+ }
+ return n;
+ }
+ }
+ return Integer.MIN_VALUE;
+ }
+
+ /**
+ * Maps RIGHT, LEFT, UP, DOWN display order to UP, RIGHT, DOWN, LEFT.
+ */
+ private static final byte[] DISPLAY_ORDER = {1, 3, 0, 2};
+
+ /**
+ * Returns the angular unit of the specified coordinate system.
+ * The preference will be given to the longitude axis, if found.
+ *
+ * @param cs the coordinate system from which to get the angular unit, or {@code null}.
+ * @param fallback the default unit to return if no angular unit is found.
+ * @return the angular unit, of {@code unit} if no angular unit was found.
+ *
+ * @see org.apache.sis.referencing.privy.ReferencingUtilities#getUnit(CoordinateSystem)
+ */
+ public static Unit<Angle> getAngularUnit(final CoordinateSystem cs, Unit<Angle> fallback) {
+ if (cs != null) {
+ for (int i = cs.getDimension(); --i>=0;) {
+ final CoordinateSystemAxis axis = cs.getAxis(i);
+ if (axis != null) { // Paranoiac check.
+ final Unit<?> candidate = axis.getUnit();
+ if (Units.isAngular(candidate)) {
+ fallback = candidate.asType(Angle.class);
+ if (absolute(axis.getDirection()) == AxisDirection.EAST) {
+ break; // Found the longitude axis.
+ }
+ }
+ }
+ }
+ }
+ return fallback;
+ }
+
+ /**
+ * Finds the dimension of an axis having the given direction or its opposite.
+ * If more than one axis has the given direction, only the first occurrence is returned.
+ * If both the given direction and its opposite exist, then the dimension for the given
+ * direction has precedence over the opposite direction.
+ *
+ * @param cs the coordinate system to inspect, or {@code null}.
+ * @param direction the direction of the axis to search.
+ * @return the dimension of the axis using the given direction or its opposite, or -1 if none.
+ */
+ public static int indexOfColinear(final CoordinateSystem cs, final AxisDirection direction) {
+ int fallback = -1;
+ if (cs != null) {
+ final int dimension = cs.getDimension();
+ for (int i=0; i<dimension; i++) {
+ final AxisDirection d = cs.getAxis(i).getDirection();
+ if (direction.equals(d)) {
+ return i;
+ }
+ if (fallback < 0 && d.equals(opposite(direction))) {
+ fallback = i;
+ }
+ }
+ }
+ return fallback;
+ }
+
+ /**
+ * Returns the index of the first dimension in {@code cs} where axes are colinear with the {@code subCS} axes.
+ * If no such dimension is found, returns -1. If more than one sequence of {@code cs} axes are colinear with
+ * all {@code subCS} axes, then the following rules apply:
+ *
+ * <ol>
+ * <li>If a sequence of {@code cs} axes are equal to the {@code subCS} axes, that sequence has precedence.</li>
+ * <li>Otherwise if a sequence of {@code cs} axes have similar names than the {@code subCS} axes (as determined
+ * by {@linkplain org.apache.sis.referencing.IdentifiedObjects#isHeuristicMatchForName heuristic match},
+ * that sequence has precedence.</li>
+ * <li>Otherwise the index of the first sequence is returned, regardless axis names.</li>
+ * </ol>
+ *
+ * Note that colinear axes are normally not allowed, except in the case of {@link org.opengis.referencing.crs.TemporalCRS}
+ * when one time axis is the runtime (the date where a numerical model has been executed) and the other time axis is the
+ * forecast time (the date at which a prevision is made).
+ *
+ * @param cs the coordinate system which contains all axes, or {@code null}.
+ * @param subCS the coordinate system to search into {@code cs}.
+ * @return the first dimension of a sequence of axes colinear with {@code subCS} axes, or {@code -1} if none.
+ */
+ public static int indexOfColinear(final CoordinateSystem cs, final CoordinateSystem subCS) {
+ int fallback = -1;
+ if (cs != null) {
+ boolean fallbackMatches = false;
+ final int subDim = subCS.getDimension();
+ final int limit = cs.getDimension() - subDim;
+ next: for (int i=0; i <= limit; i++) {
+ boolean equal = true;
+ boolean match = true;
+ for (int j=0; j<subDim; j++) {
+ final CoordinateSystemAxis expected = subCS.getAxis(j);
+ final CoordinateSystemAxis actual = cs.getAxis(i + j);
+ if (!isColinear(expected.getDirection(), actual.getDirection())) {
+ continue next;
+ }
+ if (equal) {
+ equal = Utilities.deepEquals(expected, actual, ComparisonMode.BY_CONTRACT);
+ if (equal) continue;
+ }
+ if (match) {
+ match = NameToIdentifier.isHeuristicMatchForName(actual, expected.getName().getCode());
+ }
+ }
+ if (equal) {
+ return i;
+ }
+ if (fallback < 0 | (match & !fallbackMatches)) {
+ fallbackMatches = match;
+ fallback = i;
+ }
+ }
+ }
+ return fallback;
+ }
+
+ /**
+ * Returns whether the second axis is colinear with the first axis. This method returns {@code true}
+ * if the {@linkplain #absolute absolute} direction of the given directions are equal.
+ * For example, "down" is considered colinear with "up".
+ *
+ * @param d1 the first axis direction to compare.
+ * @param d2 the second axis direction to compare.
+ * @return {@code true} if both directions are colinear.
+ */
+ public static boolean isColinear(final AxisDirection d1, final AxisDirection d2) {
+ return Objects.equals(absolute(d1), absolute(d2));
+ }
+
+ /**
+ * Returns the indices of {@code cs} axes presumed covariant with {@code subCS} axes.
+ * The mapping is based on axis directions only, with colinear axes mapped in priority.
+ * If some axes cannot be mapped using collinearity criterion, then directions from poles
+ * (e.g. <q>South along 90°E</q>) are arbitrarily handled as if they were covariant
+ * with East and North directions, in that order.
+ *
+ * @param cs the coordinate system which contains all axes, or {@code null}.
+ * @param subCS the coordinate system for which to search axes into {@code cs}.
+ * @return indices in {@code cs} of axes covariant with {@code subCS} axes in the order they appear in {@code subCS},
+ * or {@code null} if at least one {@code subCS} axis cannot be mapped to a {@code cs} axis.
+ *
+ * @see #indexOfColinear(CoordinateSystem, CoordinateSystem)
+ */
+ public static int[] indicesOfLenientMapping(final CoordinateSystem cs, final CoordinateSystem subCS) {
+ final int index = indexOfColinear(cs, subCS); // More robust than fallback below.
+ if (index >= 0) {
+ return ArraysExt.range(index, index + subCS.getDimension());
+ }
+ return AxesMapper.indices(cs, subCS);
+ }
+
+ /**
+ * Searches for an axis direction having the given name in the specified list of directions.
+ * This method compares the given name with the name of each {@code AxisDirection} in a lenient way:
+ *
+ * <ul>
+ * <li>Comparisons are case-insensitive.</li>
+ * <li>Any character which is not a letter or a digit is ignored. For example, {@code "NorthEast"},
+ * {@code "North-East"} and {@code "NORTH_EAST"} are considered equivalent.</li>
+ * <li>This method accepts abbreviations as well, for example if the given {@code name} is {@code "W"},
+ * then it will be considered equivalent to {@code "WEST"}.</li>
+ * </ul>
+ *
+ * @param name the name of the axis direction to search.
+ * @param directions the list of axis directions in which to search.
+ * @return the first axis direction having a name matching the given one, or {@code null} if none.
+ */
+ public static AxisDirection find(final String name, final AxisDirection[] directions) {
+ for (final AxisDirection candidate : directions) {
+ final String identifier = candidate.name();
+ if (equalsFiltered(name, identifier, Characters.Filter.LETTERS_AND_DIGITS, true)
+ || isAcronymForWords(name, identifier))
+ {
+ return candidate;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Searches predefined {@link AxisDirection} for a given name. This method searches for a match in the set
+ * of known axis directions as returned by {@link AxisDirection#values()}, plus a few special cases like
+ * <q>Geocentre > equator/90°E</q>. The latter are used in the EPSG database for geocentric CRS.
+ *
+ * <p>This method does not know about {@code org.apache.sis.referencing.cs.DirectionAlongMeridian}.
+ * The latter is a parser which may create new directions, while this method searches only in a set
+ * of predefined directions and never create new ones.</p>
+ *
+ * @param name the name of the axis direction to search.
+ * @return the first axis direction having a name matching the given one, or {@code null} if none.
+ */
+ public static AxisDirection valueOf(String name) {
+ name = name.replace('_', ' ').strip();
+ final AxisDirection[] directions = AxisDirection.values();
+ AxisDirection candidate = find(name, directions);
+ if (candidate == null) {
+ /*
+ * No match found when using the predefined axis name. Searches among
+ * the set of geocentric directions. Expected directions are:
+ *
+ * Geocentre > equator/PM or Geocentre > equator/0°E
+ * Geocentre > equator/90dE or Geocentre > equator/90°E
+ * Geocentre > north pole
+ */
+ int d = name.indexOf('>');
+ if (d >= 0 && equalsIgnoreCase(name, 0, skipTrailingWhitespaces(name, 0, d), "Geocentre")) {
+ final int length = name.length();
+ d = skipLeadingWhitespaces(name, d+1, length);
+ int s = name.indexOf('/', d);
+ if (s < 0) {
+ if (equalsIgnoreCase(name, d, length, "north pole")) {
+ return GEOCENTRIC_Z; // "Geocentre > north pole"
+ }
+ } else if (equalsIgnoreCase(name, d, skipTrailingWhitespaces(name, d, s), "equator")) {
+ s = skipLeadingWhitespaces(name, s+1, length);
+ if (equalsIgnoreCase(name, s, length, "PM")) {
+ return GEOCENTRIC_X; // "Geocentre > equator/PM"
+ }
+ /*
+ * At this point, the name may be "Geocentre > equator/0°E",
+ * "Geocentre > equator/90°E" or "Geocentre > equator/90dE".
+ * Parse the number, limiting the scan to 6 characters for
+ * avoiding a NumberFormatException.
+ */
+ final int stopAt = Math.min(s + 6, length);
+ for (int i=s; i<stopAt; i++) {
+ final char c = name.charAt(i);
+ if (c < '0' || c > '9') {
+ if (i == s) break;
+ final int n = Integer.parseInt(name.substring(s, i));
+ i = skipLeadingWhitespaces(name, i, length);
+ if (equalsIgnoreCase(name, i, length, "°E") || equalsIgnoreCase(name, i, length, "dE")) {
+ switch (n) {
+ case 0: return GEOCENTRIC_X; // "Geocentre > equator/0°E"
+ case 90: return GEOCENTRIC_Y; // "Geocentre > equator/90°E"
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return candidate;
+ }
+
+ /**
+ * Returns {@code true} if the given sub-sequence is equal to the given keyword, ignoring case.
+ */
+ private static boolean equalsIgnoreCase(final String name, final int lower, final int upper, final String keyword) {
+ final int length = upper - lower;
+ return (length == keyword.length()) && name.regionMatches(true, lower, keyword, 0, length);
+ }
+
+ /**
+ * Returns {@code true} if the given name starts or ends with the given keyword, ignoring case.
+ *
+ * @param end {@code false} if the given keyword is expected at the beginning of the name,
+ * or {@code true} if expected at the end.
+ */
+ private static boolean contains(final String name, final String keyword, final boolean end) {
+ final int length = keyword.length();
+ return name.regionMatches(true, end ? name.length() - length : 0, keyword, 0, length);
+ }
+
+ /**
+ * Suggests an abbreviation for the given axis direction. The unit of measurement may be used
+ * for resolving some ambiguities like whether {@link AxisDirection#EAST} is for "x" (Easting)
+ * or "λ" (Longitude).
+ *
+ * @param name the axis name for which to suggest an abbreviation.
+ * @param direction the axis direction for which to suggest an abbreviation.
+ * @param unit the axis unit of measurement, for disambiguation.
+ * @return a suggested abbreviation.
+ */
+ public static String suggestAbbreviation(final String name, final AxisDirection direction, final Unit<?> unit) {
+ if (name.length() == 1) {
+ return name; // Most common cases are "x", "y", "z", "t", "i" and "j".
+ }
+ /*
+ * Direction may be both "compass" (e.g. "North") or "non-compass" (e.g. "Away from").
+ * Even if the radius at θ = 0° is oriented toward North, we do not want the "N" abbreviation.
+ */
+ if (contains(name, "radius", true)) {
+ return "r";
+ }
+ if (isCompass(direction)) {
+ /*
+ * NORTH, EAST, SOUTH, WEST and all intercardinal directions (SOUTH_SOUTH_WEST, etc.):
+ * we will use the acronym (e.g. "SE" for SOUTH_EAST), unless the axis is likely to be
+ * a longitude or latitude axis. We detect those later cases by the unit of measurement.
+ */
+ if (!isIntercardinal(direction) && Units.isAngular(unit)) {
+ if (contains(name, "Spherical", false)) {
+ return NORTH.equals(absolute(direction)) ? "Ω" : "θ";
+ } else {
+ return NORTH.equals(absolute(direction)) ? "φ" : "λ";
+ }
+ }
+ } else {
+ /*
+ * All cases other than NORTH, SOUTH, etc. The most common direction is UP, in which case the
+ * abbreviation shall be "H" for Gravity-related height and "h" for ellipsoidal height. Those
+ * two names are specified by ISO 19111, but we will use "Gravity" as a criterion because we
+ * use "h" as the fallback for unknown vertical axis.
+ */
+ if (UP.equals(direction)) {
+ if (Units.isAngular(unit)) return "α"; // Elevation angle
+ if (contains(name, "Gravity", false)) return "H"; // Gravity-related height
+ if (contains(name, "Geocentric", false)) return "r"; // Geocentric radius
+ return "h"; // Ellipsoidal height
+ } else if (DOWN.equals(direction)) {
+ return "D"; // Depth
+ } else if (isGeocentric(direction)) {
+ // For GEOCENTRIC_X, GEOCENTRIC_Y or GEOCENTRIC_Z, just take the last letter.
+ final String dir = direction.name();
+ return dir.substring(dir.length() - 1).trim();
+ }
+ final String abbreviation = ABBREVIATIONS.get(absolute(direction));
+ if (abbreviation != null) {
+ return abbreviation;
+ }
+ }
+ final String id = direction.identifier(); // UML identifier, or null if none.
+ return camelCaseToAcronym(id != null ? id : direction.name()).toString().intern();
+ }
+
+ /**
+ * Returns an axis direction for the given abbreviation. This method is (partially) the converse
+ * of {@link #suggestAbbreviation(String, AxisDirection, Unit)}. Current implementation does not
+ * recognize all abbreviation generated by above method, but only the main ones. This method is
+ * defined here for making easier to maintain consistency with {@code suggestAbbreviation(…)}.
+ *
+ * @param abbreviation the abbreviation.
+ * @return axis direction for the given abbreviation, or {@code null} if unrecognized.
+ */
+ public static AxisDirection fromAbbreviation(char abbreviation) {
+ if (abbreviation >= 'a' && abbreviation <= 'z') {
+ abbreviation -= ('a' - 'A'); // To upper case, but only for latin characters.
+ }
+ final AxisDirection dir;
+ switch (abbreviation) {
+ default: dir = null; break;
+ case 'W': dir = AxisDirection.WEST; break;
+ case 'S': dir = AxisDirection.SOUTH; break;
+ case 'θ': case 'λ': case 'E': dir = AxisDirection.EAST; break;
+ case 'Ω': case 'φ': case 'N': dir = AxisDirection.NORTH; break;
+ case 'R': case 'H': dir = AxisDirection.UP; break;
+ case 'D': dir = AxisDirection.DOWN; break;
+ case 'T': dir = AxisDirection.FUTURE; break;
+ }
+ return dir;
+ }
+
+ /**
+ * Builds a coordinate system name from the given array of axes.
+ * This method expects a {@code StringBuilder} pre-filled with the coordinate system name.
+ * The axis directions and abbreviations will be appended after the CS name.
+ * Examples:
+ *
+ * <ul>
+ * <li>Ellipsoidal CS: North (°), East (°).</li>
+ * <li>Cartesian CS: East (km), North (km).</li>
+ * <li>Compound CS: East (km), North (km), Up (m).</li>
+ * </ul>
+ *
+ * @param buffer a buffer pre-filled with the name header.
+ * @param axes the axes to append in the given buffer.
+ * @return a name for the given coordinate system type and axes.
+ */
+ public static String appendTo(final StringBuilder buffer, final CoordinateSystemAxis[] axes) {
+ String separator = ": ";
+ for (final CoordinateSystemAxis axis : axes) {
+ buffer.append(separator).append(Types.getCodeLabel(axis.getDirection()));
+ separator = ", ";
+ final Unit<?> unit = axis.getUnit();
+ if (unit != null) {
+ final String symbol = unit.toString();
+ if (symbol != null && !symbol.isEmpty()) {
+ buffer.append(" (").append(symbol).append(')');
+ }
+ }
+ }
+ return buffer.append('.').toString();
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ExtendedPrecisionMatrix.java
index 0000000000,a35348924b..07f7c25896
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ExtendedPrecisionMatrix.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ExtendedPrecisionMatrix.java
@@@ -1,0 -1,149 +1,150 @@@
+ /*
+ * 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.privy;
+
+ import org.opengis.referencing.operation.Matrix;
+ import org.apache.sis.util.resources.Errors;
+
+
+ /**
+ * A matrix capable to store elements with extended precision.
+ * Apache SIS uses double-double arithmetic for extended precision,
+ * but we want to hide that implementation details from public API.
+ * The double-double arithmetic is implemented by a specialized {@link Number} subclass.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public interface ExtendedPrecisionMatrix extends Matrix {
+ /**
+ * A sentinel value for {@link org.apache.sis.referencing.operation.matrix.Matrices#create(int, int, Number[])}
+ * meaning that we request an extended precision matrix with all elements initialized to zero.
+ * This is a non-public feature because we try to hide our extended-precision mechanism from the users.
+ */
+ Number[] CREATE_ZERO = new Number[0];
+
+ /**
+ * A sentinel value for {@link org.apache.sis.referencing.operation.matrix.Matrices#create(int, int, Number[])}
+ * meaning that we request an extended precision matrix initialized to the identity (or diagonal) matrix.
+ * This is a non-public feature because we try to hide our extended-precision mechanism from the users.
+ */
+ Number[] CREATE_IDENTITY = new Number[0];
+
+ /**
+ * Returns {@code true} if the given number is zero.
+ * This is the criterion used for identifying which {@link Number} elements shall be {@code null}.
+ *
+ * @param element the value to test (may be {@code null}).
+ * @return whether the given value is zero or null.
+ */
+ static boolean isZero(final Number element) {
+ return (element == null) || element.doubleValue() == 0;
+ }
+
+ /**
+ * {@return an extended-precision view of the given matrix}.
+ * The returned matrix should be assumed read-only.
+ *
+ * @param m the matrix to cast or wrap.
+ *
+ * @see org.apache.sis.referencing.operation.matrix.MatrixSIS#asExtendedPrecision(Matrix)
+ */
+ @SuppressWarnings("CloneDoesntCallSuperClone")
+ static ExtendedPrecisionMatrix castOrWrap(final Matrix m) {
+ if (m == null || m instanceof ExtendedPrecisionMatrix) {
+ return (ExtendedPrecisionMatrix) m;
+ }
+ return new ExtendedPrecisionMatrix() {
+ @Override public Number getElementOrNull(int j, int i) {
+ final double v = m.getElement(j, i);
+ return (v != 0) ? v : null;
+ }
+
+ @Override public double getElement(int j, int i) {return m.getElement(j, i);}
+ @Override public int getNumRow() {return m.getNumRow();}
+ @Override public int getNumCol() {return m.getNumCol();}
+ @Override public String toString() {return m.toString();}
+ @Override public Matrix clone() {return m.clone();}
++ @Override public boolean isIdentity() {return m.isIdentity();}
+ };
+ }
+
+ /**
+ * Returns all matrix elements in a flat, row-major (column indices vary fastest) array.
+ * The array length is <code>{@linkplain #getNumRow()} * {@linkplain #getNumCol()}</code>.
+ * Zero values <em>shall</em> be null.
+ *
+ * <p>The {@code writable} argument shall be {@code true} if the caller may write in the returned array.
+ * If {@code false}, then the returned array shall be considered read-only because it may be a reference
+ * to the array stored internally by this matrix.</p>
+ *
+ * <p>Note that if the {@code writable} argument is {@code false}, then the values of the returned array
+ * may change if the matrix is mutable. If the caller need a stable array, use {@code true}.</p>
+ *
+ * @param writable whether the caller may write in the returned array.
+ * @return a copy of all current matrix elements in a row-major array.
+ */
+ default Number[] getElementAsNumbers(final boolean writable) {
+ final int numCol = getNumCol();
+ final Number[] elements = new Number[getNumRow() * numCol];
+ for (int i=0; i<elements.length; i++) {
+ elements[i] = getElementOrNull(i / numCol, i % numCol);
+ }
+ return elements;
+ }
+
+ /**
+ * Retrieves the value at the specified row and column if different than zero.
+ * If the value is zero, then this method <em>shall</em> return {@code null}.
+ * The use of {@code null} for zero is a way to identify zero easily no matter
+ * the value type.
+ *
+ * @param row the row index, from 0 inclusive to {@link #getNumRow()} exclusive.
+ * @param column the column index, from 0 inclusive to {@link #getNumCol()} exclusive.
+ * @return the current value at the given row and column, or {@code null} if the value is zero.
+ * @throws IndexOutOfBoundsException if the specified row or column is out of bounds.
+ */
+ Number getElementOrNull(int row, int column);
+
+ /**
+ * Retrieves the value at the specified row and column of this matrix.
+ *
+ * @param row the row index, from 0 inclusive to {@link #getNumRow()} exclusive.
+ * @param column the column index, from 0 inclusive to {@link #getNumCol()} exclusive.
+ * @return the current value at the given row and column.
+ * @throws IndexOutOfBoundsException if the specified row or column is out of bounds.
+ */
+ @Override
+ default double getElement(int row, int column) {
+ final Number value = getElementOrNull(row, column);
+ return (value != null) ? value.doubleValue() : 0;
+ }
+
+ /**
+ * Modifies the value at the specified row and column of this matrix.
+ * The default implementation assumes that the matrix is unmodifiable.
+ *
+ * @param row the row number of the value to set (zero indexed).
+ * @param column the column number of the value to set (zero indexed).
+ * @param value the new matrix element value.
+ * @throws IndexOutOfBoundsException if the specified row or column is out of bounds.
+ * @throws UnsupportedOperationException if this matrix is unmodifiable.
+ */
+ @Override
+ default void setElement(int row, int column, double value) {
+ throw new UnsupportedOperationException(Errors.format(Errors.Keys.UnmodifiableObject_1, getClass()));
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/Formulas.java
index 0000000000,27bf6fbe93..357eb6a265
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/Formulas.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/Formulas.java
@@@ -1,0 -1,292 +1,292 @@@
+ /*
+ * 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.privy;
+
+ import static java.lang.Math.*;
+ import org.opengis.referencing.datum.Ellipsoid;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.measure.Latitude;
+ import org.apache.sis.util.privy.Numerics;
+ import org.apache.sis.system.Configuration;
+ import org.apache.sis.referencing.datum.DefaultEllipsoid;
+ import static org.apache.sis.math.MathFunctions.atanh;
+ import static org.apache.sis.metadata.privy.ReferencingServices.NAUTICAL_MILE;
+
+
+ /**
+ * Miscellaneous numerical utilities which should not be put in public API.
+ * This class contains methods that depend on hard-coded arbitrary tolerance threshold, and we
+ * do not want to expose publicly those arbitrary values (or at least not in a too direct way).
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class Formulas extends Static {
+ /**
+ * Default tolerance threshold for comparing coordinate values in a projected CRS,
+ * assuming that the unit of measurement is metre. This constant determines also
+ * (indirectly) the minimum accuracy of iterative methods in map projections.
+ *
+ * <h4>Maintenance</h4>
+ * If this value is modified, then all usages of this constant should be verified.
+ * Some usages may need to be compensated. For example, {@code GeodesicsOnEllipsoid}
+ * uses a millimetric precision by dividing the tolerance by 10 or more. We way want
+ * to keep the same precision there even if {@code LINEAR_TOLERANCE} was made smaller.
+ *
+ * @see #ANGULAR_TOLERANCE
+ * @see org.apache.sis.util.privy.Numerics#COMPARISON_THRESHOLD
+ */
+ @Configuration
+ public static final double LINEAR_TOLERANCE = 0.01;
+
+ /**
+ * Default tolerance threshold for comparing coordinate values in a geographic CRS,
+ * assuming that the unit of measurement is decimal degrees and using the standard
+ * nautical mile length.
+ *
+ * <p>For a {@link #LINEAR_TOLERANCE} of 1 centimetre, this is slightly less than 1E-7°.</p>
+ *
+ * @see #LINEAR_TOLERANCE
+ * @see org.apache.sis.util.privy.Numerics#COMPARISON_THRESHOLD
+ */
+ @Configuration
+ public static final double ANGULAR_TOLERANCE = LINEAR_TOLERANCE / (NAUTICAL_MILE * 60);
+
+ /**
+ * Default tolerance threshold for comparing coordinate values in temporal CRS,
+ * assuming that the unit of measurement is second. Current value is arbitrary
+ * and may change in any future Apache SIS version.
+ */
+ @Configuration
+ public static final double TEMPORAL_TOLERANCE = 60; // One minute.
+
+ /**
+ * The maximal longitude value before normalization if a centimetric precision is desired.
+ * This is about 4×10⁸ degrees.
+ *
+ * @see org.apache.sis.measure.Longitude#normalize(double)
+ */
+ public static final double LONGITUDE_MAX = Numerics.MAX_INTEGER_CONVERTIBLE_TO_DOUBLE/2 * ANGULAR_TOLERANCE;
+
+ /**
+ * The length of a <i>Julian year</i> in milliseconds.
+ * From Wikipedia, <q>In astronomy, a Julian year (symbol: <b>a</b>) is a unit of measurement of time
+ * defined as exactly 365.25 days of 86,400 SI seconds each.</q>.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Julian_year_%28astronomy%29">Wikipedia: Julian year (astronomy)</a>
+ */
+ public static final long JULIAN_YEAR_LENGTH = 31557600000L;
+
+ /**
+ * Maximum number of iterations for iterative computations. Defined in this {@code Formulas} class as a default value,
+ * but some classes may use a derived value (for example twice this amount). This constant is mostly useful for identifying
+ * places where iterations occur.
+ *
+ * <p>Current value has been determined empirically for allowing {@code GeodesicsOnEllipsoidTest} to pass.</p>
+ */
+ @Configuration
+ public static final int MAXIMUM_ITERATIONS = 18;
+
+ /**
+ * Whether to use {@link Math#fma(double, double, double)} for performance reasons.
+ * We do not use this flag when the goal is to get better accuracy rather than performance.
+ * Use of FMA brings performance benefits on machines having hardware support,
+ * but come at a high cost on older machines without hardware support.
+ */
+ @Configuration
- public static final boolean USE_FMA = true;
++ public static final boolean USE_FMA = false;
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private Formulas() {
+ }
+
+ /**
+ * Returns {@code true} if {@code ymin} is the south pole and {@code ymax} is the north pole.
+ *
+ * @param ymin the minimal latitude to test.
+ * @param ymax the maximal latitude to test.
+ * @return {@code true} if the given latitudes are south pole to north pole respectively.
+ */
+ public static boolean isPoleToPole(final double ymin, final double ymax) {
+ return abs(ymin - Latitude.MIN_VALUE) <= ANGULAR_TOLERANCE &&
+ abs(ymax - Latitude.MAX_VALUE) <= ANGULAR_TOLERANCE;
+ }
+
+ /**
+ * Returns the size of a planet described by the given ellipsoid compared to earth.
+ * This method returns a ratio of given planet authalic radius compared to WGS84.
+ * This can be used for adjusting {@link #LINEAR_TOLERANCE} and {@link #ANGULAR_TOLERANCE} to another planet.
+ *
+ * @param planet ellipsoid of the other planet to compare to Earth, or {@code null}.
+ * @return ratio of planet authalic radius on WGS84 authalic radius, or {@code NaN} if the given ellipsoid is null.
+ */
+ public static double scaleComparedToEarth(final Ellipsoid planet) {
+ return getAuthalicRadius(planet) / 6371007.180918474;
+ }
+
+ /**
+ * Returns the radius of a hypothetical sphere having the same surface as the given ellipsoid.
+ *
+ * @param ellipsoid the ellipsoid for which to get the radius, or {@code null}.
+ * @return the authalic radius, or {@link Double#NaN} if the given ellipsoid is null.
+ */
+ public static double getAuthalicRadius(final Ellipsoid ellipsoid) {
+ if (ellipsoid == null) {
+ return Double.NaN;
+ } else if (ellipsoid instanceof DefaultEllipsoid) {
+ return ((DefaultEllipsoid) ellipsoid).getAuthalicRadius(); // Give a chance to subclasses to override.
+ } else {
+ return getAuthalicRadius(ellipsoid.getSemiMajorAxis(),
+ ellipsoid.getSemiMinorAxis());
+ }
+ }
+
+ /**
+ * Returns the radius of a hypothetical sphere having the same surface as the ellipsoid
+ * specified by the given axis length.
+ *
+ * @param a the semi-major axis length.
+ * @param b the semi-minor axis length.
+ * @return the radius of a sphere having the same surface as the specified ellipsoid.
+ *
+ * @see org.apache.sis.referencing.datum.DefaultEllipsoid#getAuthalicRadius()
+ */
+ public static double getAuthalicRadius(final double a, final double b) {
+ if (a != b) {
+ final double f = 1 - b/a;
+ final double e = sqrt(2*f - f*f);
+ return sqrt(0.5 * (a*a + b*b*atanh(e)/e));
+ } else {
+ return a;
+ }
+ }
+
+ /**
+ * Returns the radius of the conformal sphere at a given latitude.
+ * The radius of conformal sphere is computed as below:
+ *
+ * <blockquote>Rc = √(1 – ℯ²) / (1 – ℯ²sin²φ) where ℯ² = 1 - (b/a)²</blockquote>
+ *
+ * This is a function of latitude and therefore not constant.
+ *
+ * @param ellipsoid the ellipsoid for which to compute the radius of conformal sphere.
+ * @param φ the latitude in radians where to compute the radius of conformal sphere.
+ * @return radius of the conformal sphere at latitude φ.
+ */
+ public static double radiusOfConformalSphere(final Ellipsoid ellipsoid, final double φ) {
+ final double sinφ = Math.sin(φ);
+ final double a = ellipsoid.getSemiMajorAxis();
+ final double r = ellipsoid.getSemiMinorAxis() / a;
+ return a * (r / (1 - (1 - r*r) * (sinφ*sinφ)));
+ }
+
+ /**
+ * Returns the geocentric radius at the given latitude.
+ *
+ * @param ellipsoid the ellipsoid for which to compute the radius.
+ * @param φ the latitude in radians where to compute the radius.
+ * @return radius at latitude φ.
+ *
+ * @see <a href="https://en.wikipedia.org/wiki/Earth_radius#Geocentric_radius">Geocentric radius on Wikipedia</a>
+ */
+ public static double geocentricRadius(final Ellipsoid ellipsoid, final double φ) {
+ final double a = ellipsoid.getSemiMajorAxis();
+ final double b = ellipsoid.getSemiMinorAxis();
+ double at = a * Math.cos(φ); at *= at;
+ double bt = b * Math.sin(φ); bt *= bt;
+ return Math.sqrt((a*a*at + b*b*bt) / (at + bt));
+ }
+
+ /**
+ * Computes the semi-minor axis length from the given semi-major axis and inverse flattening factor.
+ *
+ * @param semiMajorAxis the semi-major axis length.
+ * @param inverseFlattening the inverse flattening factor.
+ * @return the semi-minor axis length.
+ */
+ public static double getSemiMinor(final double semiMajorAxis, final double inverseFlattening) {
+ /*
+ * Note: double-double arithmetic does not increase the accuracy here, unless the inverse flattening
+ * factor given to this method is very high (i.e. the planet is very close to a perfect sphere).
+ */
+ return semiMajorAxis * (1 - 1/inverseFlattening);
+ }
+
+ /**
+ * Computes the inverse flattening factor from the given axis lengths.
+ *
+ * @param semiMajorAxis the semi-major axis length.
+ * @param semiMinorAxis the semi-minor axis length.
+ * @return the inverse flattening factor.
+ */
+ public static double getInverseFlattening(final double semiMajorAxis, final double semiMinorAxis) {
+ /*
+ * Note: double-double arithmetic here sometimes change the last digit. We ignore for now.
+ * We may consider using double-double arithmetic in a future SIS version, not for more
+ * accurate map projection but rather for being able to find back the original value after
+ * we convert back and forward betwen inverse flattening and semi-minor axis length.
+ */
+ return semiMajorAxis / (semiMajorAxis - semiMinorAxis);
+ }
+
+ /**
+ * Returns {@code sqrt(x² + y²)} for coordinate values on an ellipsoid of semi-major axis length of 1.
+ * This method does not provides the accuracy guarantees offered by {@link Math#hypot(double, double)}.
+ * However for values close to 1, this approximation seems to stay within 1 ULP of {@code Math.hypot(…)}.
+ * We tested with random values in ranges up to [-6 … +6].
+ *
+ * <p>We define this method because {@link Math#hypot(double, double)} has been measured with JMH as 6 times
+ * slower than {@link Math#sqrt(double)} on Java 14. According posts on internet, the same performance cost
+ * is observed in C/C++ too. Despite its cost, {@code hypot(…)} is generally recommended because computing a
+ * hypotenuse from large magnitudes has accuracy problems. But in the context of {@code NormalizedProjection}
+ * where semi-axis lengths are close to 1, input values should be (x,y) coordinates in the [−1 … +1] range.
+ * The actual range may be greater (e.g. [−5 … +5]), but it still far from ranges requiring protection against
+ * overflow.</p>
+ *
+ * <h4>Caution</h4>
+ * We may not need the full {@code Math.hypot(x,y)} accuracy in the context of map projections on ellipsoids.
+ * However, some projection formulas require that {@code fastHypot(x,y) ≥ max(|x|,|y|)}, otherwise normalizations
+ * such as {@code x/hypot(x,y)} could result in values larger than 1, which in turn result in {@link Double#NaN}
+ * when given to {@link Math#asin(double)}. The assumption on x, y and {@code sqrt(x²+y²)} relative magnitude is
+ * broken when x=0 and |y| ≤ 1.4914711209038602E-154 or conversely. This method does not check for such cases;
+ * it is caller responsibility to add this check is necessary, for example as below:
+ *
+ * {@snippet lang="java" :
+ * double D = max(fastHypot(x, y), max(abs(x), abs(y)));
+ * }
+ *
+ * According JMH, above check is 1.65 time slower than {@code fastHypot} without checks.
+ * We define this {@code fastHypot(…)} method for tracing where {@code sqrt(x² + y²)} is used,
+ * so we can verify if it is used in context where the inaccuracy is acceptable.
+ *
+ * <h4>When to use</h4>
+ * We reserve this method to ellipsoidal formulas where approximations are used anyway. Implementations using
+ * exact formulas, such as spherical formulas, should use {@link Math#hypot(double, double)} for its accuracy.
+ *
+ * @param x one side of the triangle. Should be approximately in the [-1 … +1] range.
+ * @param y other side of the triangle. Should be approximately in the [-1 … +1] range.
+ * @return hypotenuse, not smaller than {@code max(|x|,|y|)} unless the values are less than 1.5E-154.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/NUMBERS-143">Investigate Math.hypot for computing the absolute of a complex number</a>
+ * @see <a href="https://scicomp.stackexchange.com/questions/27758/is-there-any-point-to-using-hypot-for-sqrt1c2-0-le-c-le-1-for-real/27766">Is
+ * there any point to using <code>hypot(1, c)</code> for <code>sqrt(1 + c²)</code>, 0 ≤ c ≤ 1</a>
+ */
+ public static double fastHypot(final double x, final double y) {
+ return sqrt(x*x + y*y);
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/NilReferencingObject.java
index 0000000000,5c60061a72..efc6503e34
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/NilReferencingObject.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/NilReferencingObject.java
@@@ -1,0 -1,77 +1,99 @@@
+ /*
+ * 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.privy;
+
+ import org.opengis.util.InternationalString;
+ import org.opengis.referencing.ReferenceSystem;
+ import org.apache.sis.xml.NilReason;
+ import org.apache.sis.xml.NilObject;
+ import org.apache.sis.referencing.NamedIdentifier;
+ import org.apache.sis.util.resources.Vocabulary;
+
+ // Specific to the main and geoapi-3.1 branches:
+ import org.opengis.referencing.ReferenceIdentifier;
+
++// Specific to the main branch:
++import java.util.Set;
++import java.util.Collection;
++import org.opengis.util.GenericName;
++import org.opengis.metadata.extent.Extent;
++import org.apache.sis.io.wkt.UnformattableObjectException;
++
+
+ /**
+ * A referencing object for which every methods return {@code null} or a neutral value.
+ * <strong>This is not a valid object</strong>. It is used only for initialization of
+ * objects to be used by JAXB at unmarshalling time, as a way to simulate "no-argument"
+ * constructor required by JAXB.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class NilReferencingObject implements NilObject, ReferenceSystem {
+ /**
+ * The default name of {@code NilReferencingObject} instances.
+ * We use this value because {@link ReferenceSystem#getName()}
+ * is a mandatory property and not all code is tolerant to null name.
+ *
+ * <h4>Usage note</h4>
+ * In theory we do not need a default name because it will be replaced by
+ * the value of the {@code <gml:name>} element anyway at XML unmarshalling time.
+ * But not all XML documents are valid, so the {@code <gml:name>} may be missing.
+ */
+ public static final ReferenceIdentifier UNNAMED = new NamedIdentifier(null, Vocabulary.format(Vocabulary.Keys.Unnamed));
+
+ /**
+ * The unique instance.
+ */
+ public static final NilReferencingObject INSTANCE = new NilReferencingObject();
+
+ /**
+ * Do not allow other instantiation of {@link #INSTANCE}.
+ */
+ private NilReferencingObject() {
+ }
+
+ /**
+ * This object is empty because the value will be provided later.
+ */
+ @Override
+ public NilReason getNilReason() {
+ return NilReason.TEMPLATE;
+ }
+
+ /*
+ * Simple properties. Not all of them are optional, but SIS is tolerant to null values.
+ * Returning null for collection are okay in the particular case of SIS implementation,
+ * because the constructor will replace empty collections by null references anyway.
+ */
- @Override public ReferenceIdentifier getName() {return UNNAMED;}
- @Override public InternationalString getScope() {return null;}
++ @Override public ReferenceIdentifier getName() {return UNNAMED;}
++ @Override public Collection<GenericName> getAlias() {return null;}
++ @Override public Set<ReferenceIdentifier> getIdentifiers() {return null;}
++ @Override public InternationalString getRemarks() {return null;}
++ @Override public InternationalString getScope() {return null;}
++ @Override public Extent getDomainOfValidity() {return null;}
++
++ /**
++ * Throws the exception in all cases.
++ *
++ * @return never return.
++ * @throws UnformattableObjectException always thrown.
++ */
++ @Override
++ public String toWKT() throws UnformattableObjectException {
++ throw new UnformattableObjectException();
++ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/PositionalAccuracyConstant.java
index 0000000000,7370c6c5e6..81c656787d
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/PositionalAccuracyConstant.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/PositionalAccuracyConstant.java
@@@ -1,0 -1,246 +1,246 @@@
+ /*
+ * 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.privy;
+
+ import java.util.Set;
+ import java.util.Collection;
+ import java.io.ObjectStreamException;
+ import jakarta.xml.bind.annotation.XmlTransient;
+ import javax.measure.Unit;
+ import javax.measure.quantity.Length;
+ import org.opengis.util.Record;
+ import org.opengis.util.InternationalString;
+ import org.opengis.metadata.quality.PositionalAccuracy;
+ import org.opengis.metadata.quality.EvaluationMethodType;
+ import org.opengis.metadata.quality.QuantitativeResult;
+ import org.opengis.metadata.quality.Result;
+ import org.opengis.referencing.operation.ConcatenatedOperation;
+ import org.opengis.referencing.operation.Conversion;
+ import org.opengis.referencing.operation.CoordinateOperation;
+ import org.opengis.referencing.operation.Transformation;
+ import org.apache.sis.measure.Units;
+ import org.apache.sis.metadata.iso.citation.Citations;
+ import org.apache.sis.metadata.iso.quality.DefaultConformanceResult;
+ import org.apache.sis.metadata.iso.quality.DefaultAbsoluteExternalPositionalAccuracy;
+ import org.apache.sis.system.Configuration;
+ import org.apache.sis.referencing.internal.Resources;
+ import org.apache.sis.util.resources.Vocabulary;
+
+
+ /**
+ * Predefined positional accuracy resulting from some coordinate operations.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ *
+ * @see org.opengis.referencing.operation.Transformation#getCoordinateOperationAccuracy()
+ */
+ @XmlTransient
+ public final class PositionalAccuracyConstant extends DefaultAbsoluteExternalPositionalAccuracy {
+ /**
+ * Serial number for inter-operability with different versions.
+ */
+ private static final long serialVersionUID = -2554090935254116470L;
+
+ /**
+ * Presumed worst case error when no datum shift information was found.
+ * The highest value found in the EPSG database 6.7 is 999 metres (worst datum shift), so this error
+ * should be yet higher. I have seen 3 kilometres mentioned in some documentation somewhere.
+ *
+ * <p>If this value is modified, please update {@code getLinearAccuracy()} public javadoc accordingly.</p>
+ *
+ * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getLinearAccuracy()
+ */
+ @Configuration
+ public static final double UNKNOWN_ACCURACY = 3000;
+
+ /**
+ * Default accuracy of datum shift, if not explicitly provided in the EPSG database.
+ * The 25 meters value is the next highest value (after 999 metres) found in the EPSG
+ * database version 6.7 for a significant number of transformations.
+ *
+ * <p>If this value is modified, please update {@code getLinearAccuracy()} public javadoc accordingly.</p>
+ *
+ * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getLinearAccuracy()
+ */
+ @Configuration
+ private static final double DATUM_SHIFT_ACCURACY = 25;
+
+ /**
+ * Default accuracy of datum shifts when using an intermediate datum (typically WGS 84).
+ * Since this is a concatenation of two datum shifts, we use twice {@link #DATUM_SHIFT_ACCURACY}.
+ * The result is multiplied by 2 again as a margin because we have no guarantees that the domain
+ * of validity of the two datum are close enough for making this concatenation valid.
+ */
+ @Configuration
+ public static final double INDIRECT_SHIFT_ACCURACY = 100;
+
+ /**
+ * Indicates that a {@linkplain org.opengis.referencing.operation.Transformation transformation}
+ * requires a datum shift and some method has been applied. Datum shift methods often use
+ * {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa Wolf parameters},
+ * but other kind of method may have been applied as well.
+ */
+ public static final PositionalAccuracy DATUM_SHIFT_APPLIED;
+
+ /**
+ * Indicates that a {@linkplain org.opengis.referencing.operation.Transformation transformation}
+ * requires a datum shift, but no method has been found applicable. This usually means that no
+ * {@linkplain org.apache.sis.referencing.datum.BursaWolfParameters Bursa Wolf parameters} have
+ * been found. Such datum shifts are approximations and may have 1 kilometer error.
+ */
+ public static final PositionalAccuracy DATUM_SHIFT_OMITTED;
+
+ /**
+ * Indicates that a {@linkplain org.opengis.referencing.operation.Transformation transformation}
+ * requires a datum shift, but only an indirect method has been found. The indirect method uses
+ * an intermediate datum, typically WGS 84.
+ */
+ public static final PositionalAccuracy INDIRECT_SHIFT_APPLIED;
+ static {
+ final InternationalString desc = Vocabulary.formatInternational(Vocabulary.Keys.TransformationAccuracy);
+ final InternationalString eval = Resources .formatInternational(Resources.Keys.ConformanceMeansDatumShift);
+ DATUM_SHIFT_APPLIED = new PositionalAccuracyConstant(desc, eval, true);
+ DATUM_SHIFT_OMITTED = new PositionalAccuracyConstant(desc, eval, false);
+ INDIRECT_SHIFT_APPLIED = new PositionalAccuracyConstant(desc, eval, true);
+ }
+
+ /**
+ * Creates an positional accuracy initialized to the given result.
+ */
+ @SuppressWarnings("deprecation")
+ private PositionalAccuracyConstant(final InternationalString measureDescription,
+ final InternationalString evaluationMethodDescription, final boolean pass)
+ {
+ DefaultConformanceResult result = new DefaultConformanceResult(Citations.SIS, evaluationMethodDescription, pass);
+ setResults(Set.of(result));
+ setMeasureDescription(measureDescription);
+ setEvaluationMethodDescription(evaluationMethodDescription);
+ setEvaluationMethodType(EvaluationMethodType.DIRECT_INTERNAL);
+ transitionTo(State.FINAL);
+ }
+
+ /**
+ * Invoked on deserialization. Replace this instance by one of the constants, if applicable.
+ *
+ * @return the object to use after deserialization.
+ * @throws ObjectStreamException if the serialized object defines an unknown data type.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ if (equals(DATUM_SHIFT_APPLIED)) return DATUM_SHIFT_APPLIED;
+ if (equals(DATUM_SHIFT_OMITTED)) return DATUM_SHIFT_OMITTED;
+ if (equals(INDIRECT_SHIFT_APPLIED)) return INDIRECT_SHIFT_APPLIED;
+ return this;
+ }
+
+ /**
+ * Convenience method returning the accuracy in meters for the specified operation.
+ * This method tries each of the following procedures and returns the first successful one:
+ *
+ * <ul>
+ * <li>If at least one {@link QuantitativeResult} is found with a linear unit, then the largest
+ * accuracy estimate is converted to {@linkplain Units#METRE metres} and returned.</li>
+ * <li>Otherwise, if the operation is a {@link Conversion}, then returns 0 since a conversion
+ * is by definition accurate up to rounding errors.</li>
+ * <li>Otherwise, if the operation is a {@link Transformation}, then checks if the datum shift
+ * were applied with the help of Bursa-Wolf parameters. This procedure looks for SIS-specific
+ * {@link #DATUM_SHIFT_APPLIED} and {@link #DATUM_SHIFT_OMITTED DATUM_SHIFT_OMITTED} constants.</li>
+ * <li>Otherwise, if the operation is a {@link ConcatenatedOperation}, returns the sum of the accuracy
+ * of all components. This is a conservative scenario where we assume that errors cumulate linearly.
+ * Note that this is not necessarily the "worst case" scenario since the accuracy could be worst
+ * if the math transforms are highly non-linear.</li>
+ * </ul>
+ *
+ * If the above is modified, please update {@code AbstractCoordinateOperation.getLinearAccuracy()} javadoc.
+ *
+ * @param operation the operation to inspect for accuracy.
+ * @return the accuracy estimate (always in meters), or NaN if unknown.
+ *
+ * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getLinearAccuracy()
+ */
+ public static double getLinearAccuracy(final CoordinateOperation operation) {
+ double accuracy = Double.NaN;
+ final Collection<PositionalAccuracy> accuracies = operation.getCoordinateOperationAccuracy();
+ for (final PositionalAccuracy metadata : accuracies) {
+ for (final Result result : metadata.getResults()) {
+ if (result instanceof QuantitativeResult) {
+ final QuantitativeResult quantity = (QuantitativeResult) result;
+ final Collection<? extends Record> records = quantity.getValues();
+ if (records != null) {
+ final Unit<?> unit = quantity.getValueUnit();
+ if (Units.isLinear(unit)) {
+ final Unit<Length> unitOfLength = unit.asType(Length.class);
+ for (final Record record : records) {
- for (final Object value : record.getFields().values()) {
++ for (final Object value : record.getAttributes().values()) {
+ if (value instanceof Number) {
+ double v = ((Number) value).doubleValue();
+ v = unitOfLength.getConverterTo(Units.METRE).convert(v);
+ if (v >= 0 && !(v <= accuracy)) { // '!' is for replacing the NaN value.
+ accuracy = v;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (Double.isNaN(accuracy)) {
+ /*
+ * No quantitative (linear) accuracy were found. If the coordinate operation is actually
+ * a conversion, the accuracy is up to rounding error (i.e. conceptually 0) by definition.
+ */
+ if (operation instanceof Conversion) {
+ return 0;
+ }
+ /*
+ * If the coordinate operation is actually a transformation, checks if Bursa-Wolf parameters
+ * were available for the datum shift. This is SIS-specific. See field javadoc for a rational
+ * about the return values chosen.
+ */
+ if (operation instanceof Transformation) {
+ for (final PositionalAccuracy element : accuracies) {
+ /*
+ * Really need identity comparisons, not Object.equals(Object), because the latter
+ * does not distinguish between DATUM_SHIFT_APPLIED and INDIRECT_SHIFT_APPLIED.
+ */
+ if (element == DATUM_SHIFT_APPLIED) return DATUM_SHIFT_ACCURACY;
+ if (element == DATUM_SHIFT_OMITTED) return UNKNOWN_ACCURACY;
+ if (element == INDIRECT_SHIFT_APPLIED) return INDIRECT_SHIFT_ACCURACY;
+ }
+ }
+ /*
+ * If the coordinate operation is a compound of other coordinate operations, returns the sum of their accuracy,
+ * skipping unknown ones. Making the sum is a conservative approach (not exactly the "worst case" scenario,
+ * since it could be worst if the transforms are highly non-linear).
+ */
+ if (operation instanceof ConcatenatedOperation) {
+ for (final CoordinateOperation op : ((ConcatenatedOperation) operation).getOperations()) {
+ final double candidate = Math.abs(getLinearAccuracy(op));
+ if (!Double.isNaN(candidate)) {
+ if (Double.isNaN(accuracy)) {
+ accuracy = candidate;
+ } else {
+ accuracy += candidate;
+ }
+ }
+ }
+ }
+ }
+ return accuracy;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingFactoryContainer.java
index 0000000000,aa9fbde384..c0007a6018
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingFactoryContainer.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingFactoryContainer.java
@@@ -1,0 -1,398 +1,403 @@@
+ /*
+ * 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.privy;
+
+ import java.util.Map;
+ import org.opengis.util.FactoryException;
+ import org.opengis.util.NameFactory;
+ import org.opengis.referencing.cs.CartesianCS;
+ import org.opengis.referencing.cs.CSFactory;
+ import org.opengis.referencing.cs.CSAuthorityFactory;
+ import org.opengis.referencing.crs.CRSFactory;
+ import org.opengis.referencing.crs.CRSAuthorityFactory;
+ import org.opengis.referencing.datum.DatumFactory;
+ import org.opengis.referencing.datum.DatumAuthorityFactory;
+ import org.opengis.referencing.operation.MathTransformFactory;
+ import org.opengis.referencing.operation.CoordinateOperationFactory;
+ import org.opengis.referencing.operation.CoordinateOperationAuthorityFactory;
+ import org.apache.sis.referencing.CRS;
+ import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+ import org.apache.sis.referencing.factory.NoSuchAuthorityFactoryException;
+ import org.apache.sis.referencing.factory.GeodeticObjectFactory;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.iso.DefaultNameFactory;
+ import org.apache.sis.util.resources.Errors;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.util.Factory;
++// Specific to the main branch:
++import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
+
+
+ /**
+ * A container of factories frequently used together.
+ * Provides also some utility methods working with factories.
+ *
+ * This class may be temporary until we choose a dependency injection framework
+ * See <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>.
+ *
+ * <p>This class is not thread safe. Synchronization, if needed, is caller's responsibility.</p>
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-102">SIS-102</a>
+ */
+ public class ReferencingFactoryContainer {
+ /**
+ * The key for specifying a {@link NameFactory} instance to use for geodetic object constructions.
+ */
+ public static final String NAME_FACTORY = "nameFactory";
+
+ /**
+ * The key for specifying a {@link DatumFactory} instance to use for geodetic object constructions.
+ */
+ public static final String DATUM_FACTORY = "datumFactory";
+
+ /**
+ * The key for specifying a {@link CSFactory} instance to use for geodetic object constructions.
+ */
+ public static final String CS_FACTORY = "csFactory";
+
+ /**
+ * The key for specifying a {@link CRSFactory} instance to use for geodetic object constructions.
+ */
+ public static final String CRS_FACTORY = "crsFactory";
+
+ /**
+ * The key for specifying a {@link CoordinateOperationFactory} instance to use for geodetic object constructions.
+ */
+ public static final String OPERATION_FACTORY = "copFactory";
+
+ /**
+ * The key for specifying a {@link MathTransformFactory} instance to use for geodetic object constructions.
+ * This is usually not needed for CRS construction, except in the special case of a derived CRS created
+ * from a defining conversion.
+ */
+ public static final String MT_FACTORY = "mtFactory";
+
+ /**
+ * The factory for creating coordinate reference systems from authority codes.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private CRSAuthorityFactory crsAuthorityFactory;
+
+ /**
+ * The {@linkplain org.opengis.util.GenericName name} factory.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private NameFactory nameFactory;
+
+ /**
+ * The {@linkplain org.opengis.referencing.datum.Datum datum} factory.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private DatumFactory datumFactory;
+
+ /**
+ * The {@linkplain org.opengis.referencing.cs.CoordinateSystem coordinate system} factory.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private CSFactory csFactory;
+
+ /**
+ * The {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system} factory.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private CRSFactory crsFactory;
+
+ /**
+ * Factory for fetching operation methods and creating defining conversions.
+ * This is needed only for user-defined projected coordinate reference system.
+ */
- private CoordinateOperationFactory operationFactory;
++ private DefaultCoordinateOperationFactory operationFactory;
+
+ /**
+ * The {@linkplain org.opengis.referencing.operation.MathTransform math transform} factory.
+ * If null, then a default factory will be created only when first needed.
+ */
+ private MathTransformFactory mtFactory;
+
+ /**
+ * The default properties, or an empty map if none. This map shall not be modified since its
+ * reference may be shared without cloning. This field will be cleared when no longer needed.
+ */
+ private Map<String,?> defaultProperties;
+
+ /**
+ * Creates a new instance for the default factories.
+ */
+ public ReferencingFactoryContainer() {
+ }
+
+ /**
+ * Creates a new instance with factories fetched from the given map of properties.
+ * Factories that are not present in the map will be left to their default value.
+ * This method recognizes the keys declared as static {@link String} constants in this class.
+ * Other entries are ignored.
+ *
+ * @param properties the factories.
+ */
+ public ReferencingFactoryContainer(final Map<String,?> properties) {
+ nameFactory = (NameFactory) properties.get(NAME_FACTORY);
+ datumFactory = (DatumFactory) properties.get(DATUM_FACTORY);
+ csFactory = (CSFactory) properties.get(CS_FACTORY);
+ crsFactory = (CRSFactory) properties.get(CRS_FACTORY);
- operationFactory = (CoordinateOperationFactory) properties.get(OPERATION_FACTORY);
++ operationFactory = (DefaultCoordinateOperationFactory) properties.get(OPERATION_FACTORY);
+ mtFactory = (MathTransformFactory) properties.get(MT_FACTORY);
+ }
+
+ /**
+ * Creates a new instance which will use the given factories.
+ * Any factory given in argument may be {@code null} if lazy instantiation is desired.
+ *
+ * @param defaultProperties default properties to give to the objects to create (will not be cloned).
+ * @param crsFactory the factory to use for creating coordinate reference systems.
+ * @param csFactory the factory to use for creating coordinate systems.
+ * @param datumFactory the factory to use for creating datum.
+ * @param operationFactory the factory to use for creating (defining) conversions.
+ * @param mtFactory the factory to use for creating transform objects.
+ */
+ public ReferencingFactoryContainer(final Map<String,?> defaultProperties,
+ final CRSFactory crsFactory,
+ final CSFactory csFactory,
+ final DatumFactory datumFactory,
+ final CoordinateOperationFactory operationFactory,
+ final MathTransformFactory mtFactory)
+ {
+ this.defaultProperties = defaultProperties;
+ this.crsFactory = crsFactory;
+ this.csFactory = csFactory;
+ this.datumFactory = datumFactory;
- this.operationFactory = operationFactory;
++ this.operationFactory = (DefaultCoordinateOperationFactory) operationFactory; // Because of GeoAPI 3.0 limitation.
+ this.mtFactory = mtFactory;
+ }
+
+ /**
+ * Sets one of the factories managed by this container.
+ * The given {@code type} argument can be one of the following values:
+ *
+ * <ul>
+ * <li><code>{@linkplain NameFactory}.class</code></li>
+ * <li><code>{@linkplain DatumFactory}.class</code></li>
+ * <li><code>{@linkplain CSFactory}.class</code></li>
+ * <li><code>{@linkplain CRSFactory}.class</code></li>
+ * <li><code>{@linkplain CoordinateOperationFactory}.class</code></li>
+ * <li><code>{@linkplain MathTransformFactory}.class</code></li>
+ * </ul>
+ *
+ * Note that authority factories are not yet handled by this method
+ * for consistency with {@link #getFactory(Class)}.
+ *
+ * @param <T> the compile-time type of the {@code type} argument.
+ * @param type the factory type.
+ * @param factory the factory to use for the given type, or {@code null} for the default.
+ * @return {@code true} if the factory changed as a result of this method call.
+ * @throws IllegalArgumentException if the {@code type} argument is not one of the valid values.
+ */
- public final <T extends Factory> boolean setFactory(final Class<T> type, final T factory) {
++ public final <T> boolean setFactory(final Class<T> type, final T factory) {
+ if (type == NameFactory.class) return nameFactory != (nameFactory = (NameFactory) factory);
+ if (type == DatumFactory.class) return datumFactory != (datumFactory = (DatumFactory) factory);
+ if (type == CSFactory.class) return csFactory != (csFactory = (CSFactory) factory);
+ if (type == CRSFactory.class) return crsFactory != (crsFactory = (CRSFactory) factory);
- if (type == CoordinateOperationFactory.class) return operationFactory != (operationFactory = (CoordinateOperationFactory) factory);
++ if (type == CoordinateOperationFactory.class) return operationFactory != (operationFactory = (DefaultCoordinateOperationFactory) factory);
+ if (type == MathTransformFactory.class) return mtFactory != (mtFactory = (MathTransformFactory) factory);
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "type", type));
+ }
+
+ /**
+ * Returns one of the factories managed by this container.
+ * The given {@code type} argument can be one of the following values:
+ *
+ * <ul>
+ * <li><code>{@linkplain NameFactory}.class</code></li>
+ * <li><code>{@linkplain DatumFactory}.class</code></li>
+ * <li><code>{@linkplain CSFactory}.class</code></li>
+ * <li><code>{@linkplain CRSFactory}.class</code></li>
+ * <li><code>{@linkplain CoordinateOperationFactory}.class</code></li>
+ * <li><code>{@linkplain MathTransformFactory}.class</code></li>
+ * </ul>
+ *
+ * Note that authority factories are not yet handled by this method, since we would have to expose
+ * the current restriction to EPSG geodetic dataset and to handle {@link FactoryException}.
+ *
+ * @param <T> the compile-time type of the {@code type} argument.
+ * @param type the factory type.
+ * @return the factory for the given type.
+ * @throws IllegalArgumentException if the {@code type} argument is not one of the valid values.
+ */
- public final <T extends Factory> T getFactory(final Class<T> type) {
- final Factory f;
++ public final <T> T getFactory(final Class<T> type) {
++ final Object f;
+ if (type == NameFactory.class) f = getNameFactory();
+ else if (type == DatumFactory.class) f = getDatumFactory();
+ else if (type == CSFactory.class) f = getCSFactory();
+ else if (type == CRSFactory.class) f = getCRSFactory();
+ else if (type == CoordinateOperationFactory.class) f = getCoordinateOperationFactory();
+ else if (type == MathTransformFactory.class) f = getMathTransformFactory();
+ else {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "type", type));
+ }
+ return type.cast(f);
+ }
+
+ /**
+ * Returns the factory for creating generic name.
+ *
+ * @return the Generic Name factory (never {@code null}).
+ */
+ public final NameFactory getNameFactory() {
+ if (nameFactory == null) {
+ nameFactory = DefaultNameFactory.provider();
+ }
+ return nameFactory;
+ }
+
+ /**
+ * Returns the factory for creating datum, prime meridians and ellipsoids.
+ *
+ * @return the Datum factory (never {@code null}).
+ */
+ public final DatumFactory getDatumFactory() {
+ if (datumFactory == null) {
+ datumFactory = GeodeticObjectFactory.provider();
+ }
+ return datumFactory;
+ }
+
+ /**
+ * Returns the factory for creating coordinate systems and their axes.
+ *
+ * @return the Coordinate System factory (never {@code null}).
+ */
+ public final CSFactory getCSFactory() {
+ if (csFactory == null) {
+ csFactory = GeodeticObjectFactory.provider();
+ }
+ return csFactory;
+ }
+
+ /**
+ * Returns the factory for creating coordinate reference systems.
+ *
+ * @return the Coordinate Reference System factory (never {@code null}).
+ */
+ public final CRSFactory getCRSFactory() {
+ if (crsFactory == null) {
+ crsFactory = GeodeticObjectFactory.provider();
+ }
+ return crsFactory;
+ }
+
+ /**
+ * Returns the factory for fetching operation methods and creating defining conversions.
+ * This is needed only for user-defined projected coordinate reference system.
+ * The factory is fetched when first needed.
+ *
+ * @return the Coordinate Operation factory (never {@code null}).
+ */
- public final CoordinateOperationFactory getCoordinateOperationFactory() {
++ public final DefaultCoordinateOperationFactory getCoordinateOperationFactory() {
+ if (operationFactory == null) {
- operationFactory = CoordinateOperations.getCoordinateOperationFactory(defaultProperties, mtFactory, crsFactory, csFactory);
++ CoordinateOperationFactory op = CoordinateOperations.getCoordinateOperationFactory(defaultProperties, mtFactory, crsFactory, csFactory);
+ defaultProperties = null; // Not needed anymore.
++ if (op instanceof DefaultCoordinateOperationFactory) {
++ operationFactory = (DefaultCoordinateOperationFactory) op;
++ } else {
++ operationFactory = DefaultCoordinateOperationFactory.provider();
++ }
+ }
+ return operationFactory;
+ }
+
+ /**
+ * Returns the factory for creating parameterized transforms.
+ *
+ * @return the Math Transform factory (never {@code null}).
+ */
+ public final MathTransformFactory getMathTransformFactory() {
+ if (mtFactory == null) {
+ mtFactory = DefaultMathTransformFactory.provider();
+ }
+ return mtFactory;
+ }
+
+ /**
+ * Returns the factory for creating datum from authority codes.
+ * Currently only EPSG codes are supported.
+ *
+ * @return the Datum authority factory (never {@code null}).
+ * @throws FactoryException if the authority factory cannot be obtained.
+ */
+ public final DatumAuthorityFactory getDatumAuthorityFactory() throws FactoryException {
+ final CRSAuthorityFactory factory = getCRSAuthorityFactory();
+ if (factory instanceof DatumAuthorityFactory) { // This is the case for SIS implementation.
+ return (DatumAuthorityFactory) factory;
+ }
+ throw new NoSuchAuthorityFactoryException(null, Constants.EPSG);
+ }
+
+ /**
+ * Returns the factory for creating coordinate systems from authority codes.
+ * Currently only EPSG codes are supported.
+ *
+ * @return the Coordinate System authority factory (never {@code null}).
+ * @throws FactoryException if the authority factory cannot be obtained.
+ */
+ public final CSAuthorityFactory getCSAuthorityFactory() throws FactoryException {
+ final CRSAuthorityFactory factory = getCRSAuthorityFactory();
+ if (factory instanceof CSAuthorityFactory) { // This is the case for SIS implementation.
+ return (CSAuthorityFactory) factory;
+ }
+ throw new NoSuchAuthorityFactoryException(null, Constants.EPSG);
+ }
+
+ /**
+ * Returns the factory for creating coordinate reference systems from authority codes.
+ * Currently only EPSG codes are supported.
+ *
+ * @return the Coordinate Reference System authority factory (never {@code null}).
+ * @throws FactoryException if the authority factory cannot be obtained.
+ */
+ public final CRSAuthorityFactory getCRSAuthorityFactory() throws FactoryException {
+ if (crsAuthorityFactory == null) {
+ crsAuthorityFactory = CRS.getAuthorityFactory(Constants.EPSG);
+ }
+ return crsAuthorityFactory;
+ }
+
+ /**
+ * Returns the factory for creating coordinate operations from authority codes.
+ * Currently only EPSG codes are supported.
+ *
+ * @return the Coordinate Operation authority factory (never {@code null}).
+ * @throws FactoryException if the authority factory cannot be obtained.
+ */
+ public final CoordinateOperationAuthorityFactory getCoordinateOperationAuthorityFactory() throws FactoryException {
+ final CRSAuthorityFactory factory = getCRSAuthorityFactory();
+ if (factory instanceof CoordinateOperationAuthorityFactory) { // This is the case for SIS implementation.
+ return (CoordinateOperationAuthorityFactory) factory;
+ }
+ throw new NoSuchAuthorityFactoryException(null, Constants.EPSG);
+ }
+
+ /**
+ * Returns the a coordinate system for map projections with (easting, northing) axes in metres.
+ * EPSG::4400 — Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m.
+ *
+ * @return a coordinate system with (easting, northing) axes in metres.
+ * @throws FactoryException if an error occurred while creating the coordinate system.
+ */
+ public final CartesianCS getStandardProjectedCS() throws FactoryException {
+ return getCSAuthorityFactory().createCartesianCS("4400");
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
index 0000000000,9a1ca4c79f..af3a37f029
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/ReferencingUtilities.java
@@@ -1,0 -1,684 +1,687 @@@
+ /*
+ * 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.privy;
+
+ import java.util.Map;
+ import java.util.HashMap;
+ import java.util.Collection;
+ import java.util.NoSuchElementException;
+ import javax.measure.Unit;
+ import javax.measure.quantity.Angle;
+ import org.opengis.annotation.UML;
+ import org.opengis.annotation.Specification;
+ import org.opengis.metadata.Identifier;
+ import org.opengis.metadata.citation.Citation;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.parameter.ParameterDescriptorGroup;
+ import org.opengis.parameter.GeneralParameterDescriptor;
+ import org.opengis.util.FactoryException;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.cs.*;
+ import org.opengis.referencing.crs.*;
+ import org.opengis.referencing.datum.Datum;
+ import org.opengis.referencing.datum.Ellipsoid;
+ import org.opengis.referencing.datum.PrimeMeridian;
+ import org.opengis.referencing.datum.GeodeticDatum;
+ import org.opengis.referencing.datum.VerticalDatum;
+ import org.opengis.referencing.datum.VerticalDatumType;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.opengis.referencing.operation.MathTransformFactory;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.util.Classes;
+ import org.apache.sis.util.Utilities;
+ import org.apache.sis.util.CharSequences;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.resources.Vocabulary;
+ import org.apache.sis.referencing.CommonCRS;
+ import org.apache.sis.referencing.NamedIdentifier;
+ import org.apache.sis.referencing.IdentifiedObjects;
+ import org.apache.sis.referencing.AbstractIdentifiedObject;
+ import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
+ import org.apache.sis.referencing.crs.DefaultGeographicCRS;
+ import org.apache.sis.referencing.cs.AxesConvention;
+ import org.apache.sis.referencing.cs.DefaultEllipsoidalCS;
+ import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory;
+ import org.apache.sis.referencing.operation.transform.DefaultMathTransformFactory.Context;
+ import org.apache.sis.metadata.privy.Identifiers;
+ import org.apache.sis.xml.NilObject;
+
++// Specific to the main branch:
++import org.opengis.referencing.ReferenceIdentifier;
++
+
+ /**
+ * A set of static methods working on GeoAPI referencing objects.
+ * Some of those methods may be useful, but not really rigorous.
+ * This is why they do not appear in the public packages.
+ *
+ * <p><strong>Do not rely on this API!</strong> It may change in incompatible way in any future release.</p>
+ *
+ * @author Martin Desruisseaux (IRD, Geomatys)
+ */
+ public final class ReferencingUtilities extends Static {
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private ReferencingUtilities() {
+ }
+
+ /**
+ * Returns the longitude value relative to the Greenwich Meridian, expressed in the specified units.
+ * This method provides the same functionality as {@link DefaultPrimeMeridian#getGreenwichLongitude(Unit)},
+ * but on arbitrary implementation.
+ *
+ * @param primeMeridian the prime meridian from which to get the Greenwich longitude, or {@code null}.
+ * @param unit the unit for the prime meridian to return.
+ * @return the prime meridian in the given units, or {@code 0} if the given prime meridian was null.
+ *
+ * @see DefaultPrimeMeridian#getGreenwichLongitude(Unit)
+ * @see org.apache.sis.referencing.CRS#getGreenwichLongitude(GeodeticCRS)
+ */
+ public static double getGreenwichLongitude(final PrimeMeridian primeMeridian, final Unit<Angle> unit) {
+ if (primeMeridian == null) {
+ return 0;
+ } else if (primeMeridian instanceof DefaultPrimeMeridian) { // Maybe the user overrode some methods.
+ return ((DefaultPrimeMeridian) primeMeridian).getGreenwichLongitude(unit);
+ } else {
+ return primeMeridian.getAngularUnit().getConverterTo(unit).convert(primeMeridian.getGreenwichLongitude());
+ }
+ }
+
+ /**
+ * Returns the unit used for all axes in the given coordinate system.
+ * If not all axes use the same unit, then this method returns {@code null}.
+ *
+ * <p>This method is used either when the coordinate system is expected to contain exactly one axis,
+ * or for operations that support only one units for all axes, for example Well Know Text version 1
+ * (WKT 1) formatting.</p>
+ *
+ * @param cs the coordinate system for which to get the unit, or {@code null}.
+ * @return the unit for all axis in the given coordinate system, or {@code null}.
+ *
+ * @see org.apache.sis.referencing.privy.AxisDirections#getAngularUnit(CoordinateSystem, Unit)
+ */
+ public static Unit<?> getUnit(final CoordinateSystem cs) {
+ Unit<?> unit = null;
+ if (cs != null) {
+ for (int i=cs.getDimension(); --i>=0;) {
+ final CoordinateSystemAxis axis = cs.getAxis(i);
+ if (axis != null) { // Paranoiac check.
+ final Unit<?> candidate = axis.getUnit();
+ if (candidate != null) {
+ if (unit == null) {
+ unit = candidate;
+ } else if (!unit.equals(candidate)) {
+ return null;
+ }
+ }
+ }
+ }
+ }
+ return unit;
+ }
+
+ /**
+ * Returns the unit used for all axes in the given coordinate reference system.
+ * If not all axes use the same unit, then this method returns {@code null}.
+ *
+ * <p>This method is used either when the CRS is expected to contain exactly one axis,
+ * or for operations that support only one units for all axes.</p>
+ *
+ * @param crs the coordinate reference system for which to get the unit, or {@code null}.
+ * @return the unit for all axis in the given coordinate system, or {@code null}.
+ */
+ public static Unit<?> getUnit(final CoordinateReferenceSystem crs) {
+ return (crs != null) ? getUnit(crs.getCoordinateSystem()) : null;
+ }
+
+ /**
+ * Returns the number of dimensions of the given CRS, or 0 if {@code null}.
+ *
+ * @param crs the CRS from which to get the number of dimensions, or {@code null}.
+ * @return the number of dimensions, or 0 if the given CRS or its coordinate system is null.
+ */
+ public static int getDimension(final CoordinateReferenceSystem crs) {
+ if (crs != null) {
+ final CoordinateSystem cs = crs.getCoordinateSystem();
+ if (cs != null) { // Paranoiac check.
+ return cs.getDimension();
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the GeoAPI interface implemented by the given object, or the implementation class
+ * if the interface is unknown. This method can be used when the base type (CRS, CS, Datum…)
+ * is unknown, for example when preparing an error message. If the base type is known, then
+ * the method expecting a {@code baseType} argument should be preferred.
+ *
+ * @param object the object for which to get the GeoAPI interface, or {@code null}.
+ * @return GeoAPI interface or implementation class of the given object, or {@code null} if the given object is null.
+ */
+ @SuppressWarnings("unchecked")
+ public static Class<?> getInterface(final Object object) {
+ if (object instanceof AbstractIdentifiedObject) {
+ return ((AbstractIdentifiedObject) object).getInterface();
+ } else {
+ return getInterface(IdentifiedObject.class, (Class) Classes.getClass(object));
+ }
+ }
+
+ /**
+ * Returns the GeoAPI interface implemented by the given object, or the implementation class
+ * if the interface is unknown.
+ *
+ * @param <T> compile-time value of {@code baseType}.
+ * @param baseType parent interface of the desired type.
+ * @param object the object for which to get the GeoAPI interface, or {@code null}.
+ * @return GeoAPI interface or implementation class of the given object, or {@code null} if the given object is null.
+ */
+ public static <T extends IdentifiedObject> Class<? extends T> getInterface(final Class<T> baseType, final T object) {
+ if (object instanceof AbstractIdentifiedObject) {
+ return ((AbstractIdentifiedObject) object).getInterface().asSubclass(baseType);
+ } else {
+ return getInterface(baseType, Classes.getClass(object));
+ }
+ }
+
+ /**
+ * Returns the GeoAPI interface implemented by the given class, or the class itself if the interface is unknown.
+ *
+ * @param <T> compile-time value of {@code baseType}.
+ * @param baseType parent interface of the desired type.
+ * @param type type of object for which to get the GeoAPI interface, or {@code null}.
+ * @return GeoAPI interface or implementation class, or {@code null} if the given type is null.
+ */
+ public static <T extends IdentifiedObject> Class<? extends T> getInterface(final Class<T> baseType, final Class<? extends T> type) {
+ final Class<? extends T>[] types = Classes.getLeafInterfaces(type, baseType);
+ return (types.length != 0) ? types[0] : type;
+ }
+
+ /**
+ * Copies all {@link SingleCRS} components from the given source to the given collection.
+ * For each {@link CompoundCRS} element found in the iteration, this method replaces the
+ * {@code CompoundCRS} by its {@linkplain CompoundCRS#getComponents() components}, which
+ * may themselves have other {@code CompoundCRS}. Those replacements are performed recursively
+ * until we obtain a flat view of CRS components.
+ *
+ * @param source the collection of single or compound CRS.
+ * @param addTo where to add the single CRS in order to obtain a flat view of {@code source}.
+ * @return {@code true} if this method found only single CRS in {@code source}, in which case {@code addTo}
+ * got the same content (assuming that {@code addTo} was empty prior this method call).
+ * @throws NoSuchElementException if a CRS component is missing.
+ * @throws ClassCastException if a CRS is neither a {@link SingleCRS} or a {@link CompoundCRS}.
+ *
+ * @see org.apache.sis.referencing.CRS#getSingleComponents(CoordinateReferenceSystem)
+ */
+ public static boolean getSingleComponents(final Iterable<? extends CoordinateReferenceSystem> source,
+ final Collection<? super SingleCRS> addTo) throws ClassCastException
+ {
+ boolean sameContent = true;
+ for (final CoordinateReferenceSystem candidate : source) {
+ if (candidate instanceof CompoundCRS) {
+ getSingleComponents(((CompoundCRS) candidate).getComponents(), addTo);
+ sameContent = false;
+ } else if (candidate instanceof SingleCRS) {
+ addTo.add((SingleCRS) candidate);
+ } else {
+ /*
+ * Illegal class. Try to provide a better error message, in particular when the CRS component
+ * is nil because it is an unresolved xlink in a GML document. Nil objects are proxies, which
+ * have hard to understand class names.
+ */
+ final String message;
+ if (candidate instanceof NilObject) {
+ message = Errors.format(Errors.Keys.NilObject_1, Identifiers.getNilReason((NilObject) candidate));
+ throw new NoSuchElementException(message);
+ } else {
+ message = Errors.format(Errors.Keys.NestedElementNotAllowed_1, getInterface(candidate));
+ throw new ClassCastException(message);
+ }
+ }
+ }
+ return sameContent;
+ }
+
+ /**
+ * Returns {@code true} if the type of the given datum is ellipsoidal. A vertical datum is not allowed
+ * to be ellipsoidal according ISO 19111, but Apache SIS relaxes this restriction in some limited cases,
+ * for example when parsing a string in the legacy WKT 1 format. Apache SIS should not expose those
+ * vertical heights as much as possible, and instead try to combine them with three-dimensional
+ * geographic or projected CRS as soon as it can.
+ *
+ * @param datum the datum to test, or {@code null} if none.
+ * @return {@code true} if the given datum is non null and of ellipsoidal type.
+ *
+ * @see org.apache.sis.referencing.internal.VerticalDatumTypes#ELLIPSOIDAL
+ */
+ public static boolean isEllipsoidalHeight(final VerticalDatum datum) {
+ if (datum != null) {
+ final VerticalDatumType type = datum.getVerticalDatumType();
+ if (type != null) {
+ return "ELLIPSOIDAL".equalsIgnoreCase(type.name());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the ellipsoid used by the given coordinate reference system, or {@code null} if none.
+ * More specifically:
+ *
+ * <ul>
+ * <li>If the given CRS is an instance of {@link SingleCRS} and its datum is a {@link GeodeticDatum},
+ * then this method returns the datum ellipsoid.</li>
+ * <li>Otherwise if the given CRS is an instance of {@link CompoundCRS}, then this method
+ * invokes itself recursively for each component until a geodetic datum is found.</li>
+ * <li>Otherwise this method returns {@code null}.</li>
+ * </ul>
+ *
+ * Note that this method does not check if there is more than one ellipsoid (it should never be the case).
+ *
+ * @param crs the coordinate reference system for which to get the ellipsoid.
+ * @return the ellipsoid, or {@code null} if none.
+ */
+ public static Ellipsoid getEllipsoid(final CoordinateReferenceSystem crs) {
+ if (crs != null) {
+ if (crs instanceof SingleCRS) {
+ final Datum datum = ((SingleCRS) crs).getDatum();
+ if (datum instanceof GeodeticDatum) {
+ final Ellipsoid e = ((GeodeticDatum) datum).getEllipsoid();
+ if (e != null) return e;
+ }
+ }
+ if (crs instanceof CompoundCRS) {
+ for (final CoordinateReferenceSystem c : ((CompoundCRS) crs).getComponents()) {
+ final Ellipsoid e = getEllipsoid(c);
+ if (e != null) return e;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the ellipsoid used by the specified coordinate reference system, provided that the two first dimensions
+ * use an instance of {@link GeographicCRS}. Otherwise (i.e. if the two first dimensions are not geographic),
+ * returns {@code null}.
+ *
+ * <p>This method excludes geocentric CRS on intent. Some callers needs this exclusion as a way to identify
+ * which CRS in a Geographic/Geocentric conversion is the geographic one. Another point of view is to said
+ * that if this method returns a non-null value, then the coordinates are expected to be either two-dimensional
+ * or three-dimensional with an ellipsoidal height.</p>
+ *
+ * @param crs the coordinate reference system for which to get the ellipsoid.
+ * @return the ellipsoid in the given CRS, or {@code null} if none.
+ */
+ public static Ellipsoid getEllipsoidOfGeographicCRS(CoordinateReferenceSystem crs) {
+ while (!(crs instanceof GeodeticCRS)) {
+ if (crs instanceof CompoundCRS) {
+ crs = ((CompoundCRS) crs).getComponents().get(0);
+ } else {
+ return null;
+ }
+ }
+ /*
+ * In order to determine if the CRS is geographic, checking the CoordinateSystem type is more reliable
+ * then checking if the CRS implements the GeographicCRS interface. This is because the GeographicCRS
+ * interface is GeoAPI-specific, so a CRS may be OGC-compliant without implementing that interface.
+ */
+ if (crs.getCoordinateSystem() instanceof EllipsoidalCS) {
+ return ((GeodeticCRS) crs).getDatum().getEllipsoid();
+ } else {
+ return null; // Geocentric CRS.
+ }
+ }
+
+ /**
+ * Derives a geographic CRS with (<var>longitude</var>, <var>latitude</var>) axis in the specified
+ * order and in decimal degrees. If no such CRS can be obtained or created, returns {@code null}.
+ *
+ * <p>This method does not set the prime meridian to Greenwich.
+ * Meridian rotation, if needed, shall be performed by the caller.</p>
+ *
+ * @param crs a source CRS, or {@code null}.
+ * @param latlon {@code true} for (latitude, longitude) axis order, or {@code false} for (longitude, latitude).
+ * @param allow3D whether this method is allowed to return three-dimensional CRS (with ellipsoidal height).
+ * @return a two-dimensional geographic CRS with standard axes, or {@code null} if none.
+ */
+ public static GeographicCRS toNormalizedGeographicCRS(CoordinateReferenceSystem crs, final boolean latlon, final boolean allow3D) {
+ /*
+ * ProjectedCRS instances always have a GeographicCRS as their base.
+ * More generally, derived CRS are always derived from a base, which
+ * is often (but not necessarily) geographic.
+ */
+ while (crs instanceof GeneralDerivedCRS) {
+ crs = ((GeneralDerivedCRS) crs).getBaseCRS();
+ }
+ if (crs instanceof GeodeticCRS) {
+ /*
+ * At this point we usually have a GeographicCRS, but it could also be a GeocentricCRS.
+ * If we can let `forConvention` do its job, do that first since it may return a cached
+ * instance. If the CRS is a `GeographicCRS` but not a `DefaultGeographicCRS`, create a
+ * CRS in this code instead of invoking `DefaultGeographicCRS.castOrCopy(…)` in order
+ * to create only one CRS instead of two.
+ */
+ final CoordinateSystem cs = crs.getCoordinateSystem();
+ if (!latlon && crs instanceof DefaultGeographicCRS && (allow3D || cs.getDimension() == 2)) {
+ return ((DefaultGeographicCRS) crs).forConvention(AxesConvention.NORMALIZED);
+ }
+ /*
+ * Get a normalized coordinate system with the number of dimensions authorized by the
+ * `allow3D` argument. We do not check if we can invoke `cs.forConvention(…)` because
+ * it is unlikely that `cs` will be an instance of `DefaultEllipsoidalCS` is `crs` is
+ * not a `DefaultGeographicCRS`. The code below has more chances to use cached instance.
+ */
+ EllipsoidalCS normalizedCS;
+ if (allow3D && cs.getDimension() >= 3) {
+ normalizedCS = CommonCRS.WGS84.geographic3D().getCoordinateSystem();
+ if (!latlon) {
+ normalizedCS = DefaultEllipsoidalCS.castOrCopy(normalizedCS).forConvention(AxesConvention.NORMALIZED);
+ }
+ } else if (latlon) {
+ normalizedCS = CommonCRS.WGS84.geographic().getCoordinateSystem();
+ } else {
+ normalizedCS = CommonCRS.defaultGeographic().getCoordinateSystem();
+ }
+ if (crs instanceof GeographicCRS && Utilities.equalsIgnoreMetadata(normalizedCS, cs)) {
+ return (GeographicCRS) crs;
+ }
+ return new DefaultGeographicCRS(
+ Map.of(DefaultGeographicCRS.NAME_KEY, NilReferencingObject.UNNAMED),
+ ((GeodeticCRS) crs).getDatum(), normalizedCS);
+ }
+ if (crs instanceof CompoundCRS) {
+ for (final CoordinateReferenceSystem e : ((CompoundCRS) crs).getComponents()) {
+ final GeographicCRS candidate = toNormalizedGeographicCRS(e, latlon, allow3D);
+ if (candidate != null) {
+ return candidate;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns {@code true} if the given coordinate system has at least 2 dimensions and
+ * the 2 first axis have (North, East) directions. This method is used for assertions.
+ *
+ * @param cs the coordinate system to verify.
+ * @return whether the coordinate system starts with (North, East) direction.
+ */
+ public static boolean startsWithNorthEast(final CoordinateSystem cs) {
+ final int dimension = cs.getDimension();
+ return (dimension >= 2)
+ && cs.getAxis(0).getDirection() == AxisDirection.NORTH
+ && cs.getAxis(1).getDirection() == AxisDirection.EAST;
+ }
+
+ /**
+ * Returns the properties (scope, domain of validity) except the identifiers and the EPSG namespace.
+ * The identifiers are removed because a modified CRS is no longer conform to the authoritative definition.
+ * If the name contains a namespace (e.g. "EPSG"), this method removes that namespace for the same reason.
+ * For example, "EPSG:WGS 84" will become simply "WGS 84".
+ *
+ * @param object the identified object for which to get properties map.
+ * @param overwrite properties overwriting the inherited ones, or {@code null} if none.
+ * @return the identified object properties.
+ */
+ public static Map<String,?> getPropertiesWithoutIdentifiers(final IdentifiedObject object, final Map<String,?> overwrite) {
+ final Map<String,?> properties = IdentifiedObjects.getProperties(object, IdentifiedObject.IDENTIFIERS_KEY);
- final Identifier name = object.getName();
++ final ReferenceIdentifier name = object.getName();
+ final boolean keepName = name.getCodeSpace() == null && name.getAuthority() == null;
+ if (keepName && overwrite == null) {
+ return properties;
+ }
+ final var copy = new HashMap<String,Object>(properties);
+ if (!keepName) {
+ copy.put(IdentifiedObject.NAME_KEY, new NamedIdentifier(null, name.getCode()));
+ }
+ if (overwrite != null) {
+ copy.putAll(overwrite);
+ }
+ return copy;
+ }
+
+ /**
+ * Returns the properties of the given object but potentially with a modified name.
+ * Current implement truncates the name at the first non-white character which is not
+ * a valid Unicode identifier part, with the following exception:
+ *
+ * <ul>
+ * <li>If the character is {@code '('} and the content until the closing {@code ')'} is a valid
+ * Unicode identifier, then that part is included. The intent is to keep the prime meridian
+ * name in names like <q>NTF (Paris)</q>.</li>
+ * </ul>
+ *
+ * <p>Examples:</p>
+ * <ul>
+ * <li><q>NTF (Paris)</q> is left unchanged.</li>
+ * <li><q>WGS 84 (3D)</q> is truncated as <q>WGS 84</q>.</li>
+ * <li><q>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</q>
+ * is truncated as <q>Ellipsoidal 2D CS</q>.</li>
+ * </ul>
+ *
+ * @param object the identified object to view as a properties map.
+ * @return a view of the identified object properties.
+ *
+ * @see IdentifiedObjects#getProperties(IdentifiedObject, String...)
+ */
+ public static Map<String,?> getPropertiesForModifiedCRS(final IdentifiedObject object) {
+ final Map<String,?> properties = IdentifiedObjects.getProperties(object, IdentifiedObject.IDENTIFIERS_KEY);
+ final Identifier id = (Identifier) properties.get(IdentifiedObject.NAME_KEY);
+ if (id != null) {
+ String name = id.getCode();
+ if (name != null) {
+ for (int i=0; i < name.length();) {
+ final int c = name.codePointAt(i);
+ if (!Character.isUnicodeIdentifierPart(c) && !Character.isSpaceChar(c)) {
+ if (c == '(') {
+ final int endAt = name.indexOf(')', i);
+ if (endAt >= 0) {
+ final String extra = name.substring(i+1, endAt);
+ if (CharSequences.isUnicodeIdentifier(extra)) {
+ i += extra.length() + 2;
+ }
+ }
+ }
+ name = CharSequences.trimWhitespaces(name, 0, i).toString();
+ if (!name.isEmpty()) {
+ final Map<String,Object> copy = new HashMap<>(properties);
+ copy.put(IdentifiedObject.NAME_KEY, name);
+ return copy;
+ }
+ }
+ i += Character.charCount(c);
+ }
+ }
+ }
+ return properties;
+ }
+
+ /**
+ * Returns the XML property name of the given interface.
+ *
+ * For {@link CoordinateSystem} base type, the returned value shall be one of
+ * {@code affineCS}, {@code cartesianCS}, {@code cylindricalCS}, {@code ellipsoidalCS}, {@code linearCS},
+ * {@code parametricCS}, {@code polarCS}, {@code sphericalCS}, {@code timeCS} or {@code verticalCS}.
+ *
+ * @param base the abstract base interface.
+ * @param type the interface or classes for which to get the XML property name.
+ * @return the XML property name for the given class or interface, or {@code null} if none.
+ *
+ * @see WKTUtilities#toType(Class, Class)
+ */
+ public static StringBuilder toPropertyName(final Class<?> base, final Class<?> type) {
+ final UML uml = type.getAnnotation(UML.class);
+ if (uml != null) {
+ final Specification spec = uml.specification();
- if (spec == Specification.ISO_19111 || spec == Specification.ISO_19111_2) {
++ if (spec == Specification.ISO_19111) {
+ final String name = uml.identifier();
+ final int length = name.length();
+ final StringBuilder buffer = new StringBuilder(length).append(name, name.indexOf('_') + 1, length);
+ if (buffer.length() != 0) {
+ buffer.setCharAt(0, Character.toLowerCase(buffer.charAt(0)));
+ return buffer;
+ }
+ }
+ }
+ for (final Class<?> c : type.getInterfaces()) {
+ if (base.isAssignableFrom(c)) {
+ final StringBuilder name = toPropertyName(base, c);
+ if (name != null) {
+ return name;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the given factory instance if non-null, or a default instance otherwise.
+ *
+ * @param factory the factory, which may be {@code null}.
+ * @return the instance to use.
+ */
+ public static MathTransformFactory nonNull(MathTransformFactory factory) {
+ if (factory == null) {
+ factory = DefaultMathTransformFactory.provider().caching(false);
+ }
+ return factory;
+ }
+
+ /**
+ * Creates a context with source and target ellipsoids and coordinate systems inferred from the given CRS.
+ * The ellipsoids will be non-null only if the given CRS is geodetic (geographic or geocentric).
+ *
+ * @param sourceCRS the CRS from which to get the source coordinate system and ellipsoid, or {@code null}.
+ * @param targetCRS the CRS from which to get the target coordinate system and ellipsoid, or {@code null}.
+ * @return the context to provides to math transform factory.
+ */
+ public static Context createTransformContext(final CoordinateReferenceSystem sourceCRS,
+ final CoordinateReferenceSystem targetCRS)
+ {
+ final Context context = new Context();
+ if (sourceCRS instanceof GeodeticCRS) {
+ context.setSource((GeodeticCRS) sourceCRS);
+ } else if (sourceCRS != null) {
+ context.setSource(sourceCRS.getCoordinateSystem());
+ }
+ if (targetCRS instanceof GeodeticCRS) {
+ context.setTarget((GeodeticCRS) targetCRS);
+ } else if (targetCRS != null) {
+ context.setTarget(targetCRS.getCoordinateSystem());
+ }
+ return context;
+ }
+
+ /**
+ * Substitute for the deprecated {@link MathTransformFactory#createBaseToDerived createBaseToDerived(…)} method.
+ * This substitute uses the full {@code targetCRS} instead of only the coordinate system of the target.
+ * This is needed for setting the {@code "tgt_semi_minor"} and {@code "tgt_semi_major"} parameters of
+ * Molodensky transformation for example.
+ *
+ * @param factory the factory to use for creating the transform.
+ * @param sourceCRS the source (base) coordinate reference system.
+ * @param parameters the parameter values for the transform.
+ * @param targetCRS the target (derived) coordinate system.
+ * @return the parameterized transform from {@code sourceCRS} to {@code targetCRS},
+ * including unit conversions and axis swapping.
+ * @throws FactoryException if the object creation failed. This exception is thrown
+ * if some required parameter has not been supplied, or has illegal value.
+ *
+ * @see <a href="https://issues.apache.org/jira/browse/SIS-512">SIS-512 on issues tracker</a>
+ */
+ public static MathTransform createBaseToDerived(final MathTransformFactory factory,
+ final CoordinateReferenceSystem sourceCRS, final ParameterValueGroup parameters,
+ final CoordinateReferenceSystem targetCRS) throws FactoryException
+ {
+ if (factory instanceof DefaultMathTransformFactory) {
+ return ((DefaultMathTransformFactory) factory).createParameterizedTransform(
+ parameters, createTransformContext(sourceCRS, targetCRS));
+ } else {
+ // Fallback for non-SIS implementations. Work for map projections but not for Molodensky.
+ return factory.createBaseToDerived(sourceCRS, parameters, targetCRS.getCoordinateSystem());
+ }
+ }
+
+ /**
+ * Returns the mapping between parameter identifiers and parameter names as defined by the given authority.
+ * This method assumes that the identifiers of all parameters defined by that authority are numeric.
+ * Examples of authorities defining numeric parameters are EPSG and GeoTIFF.
+ *
+ * <p>The map returned by this method is modifiable. Callers are free to add or remove entries.</p>
+ *
+ * @param parameters the parameters for which to get a mapping from identifiers to names.
+ * @param authority the authority defining the parameters.
+ * @return mapping from parameter identifiers to parameter names defined by the given authority.
+ * @throws NumberFormatException if a parameter identifier of the given authority is not numeric.
+ * @throws IllegalArgumentException if the same identifier is used for two or more parameters.
+ */
+ public static Map<Integer,String> identifierToName(final ParameterDescriptorGroup parameters, final Citation authority) {
+ final Map<Integer,String> mapping = new HashMap<>();
+ for (final GeneralParameterDescriptor descriptor : parameters.descriptors()) {
+ final Identifier id = IdentifiedObjects.getIdentifier(descriptor, authority);
+ if (id != null) {
+ String name = IdentifiedObjects.getName(descriptor, authority);
+ if (name == null) {
+ name = IdentifiedObjects.getName(descriptor, null);
+ }
+ if (mapping.put(Integer.valueOf(id.getCode()), name) != null) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.DuplicatedIdentifier_1, id));
+ }
+ }
+ }
+ return mapping;
+ }
+
+ /**
+ * Returns short names for all axes of the given CRS. This method uses short names like "Latitude" or "Height",
+ * even if the full ISO 19111 names are "Geodetic latitude" or "Ellipsoidal height". This is suitable as header
+ * for columns in a table. This method does not include abbreviation or units in the returned names.
+ *
+ * @param resources the resources from which to get "latitude" and "longitude" localized labels.
+ * @param crs the coordinate reference system from which to get axis names.
+ * @return axis names, localized if possible.
+ */
+ public static String[] getShortAxisNames(final Vocabulary resources, final CoordinateReferenceSystem crs) {
+ final boolean isGeographic = (crs instanceof GeographicCRS);
+ final boolean isProjected = (crs instanceof ProjectedCRS);
+ final CoordinateSystem cs = crs.getCoordinateSystem();
+ final String[] names = new String[cs.getDimension()];
+ for (int i=0; i<names.length; i++) {
+ short key = 0;
+ final CoordinateSystemAxis axis = cs.getAxis(i);
+ final AxisDirection direction = axis.getDirection();
+ if (AxisDirections.isCardinal(direction)) {
+ final boolean isMeridional = (direction == AxisDirection.NORTH) || (direction == AxisDirection.SOUTH);
+ if (isGeographic) {
+ key = isMeridional ? Vocabulary.Keys.Latitude : Vocabulary.Keys.Longitude;
+ } else if (isProjected) {
+ // We could add "Easting" / "Northing" here for ProjectedCRS in a future version.
+ }
+ } else if (direction == AxisDirection.UP) {
+ if (isGeographic | isProjected) {
+ key = Vocabulary.Keys.Height;
+ }
+ }
+ names[i] = (key != 0) ? resources.getString(key) : axis.getName().getCode();
+ }
+ return names;
+ }
+ }
diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTUtilities.java
index 0000000000,0ca7754a66..6f0ad35c47
mode 000000,100644..100644
--- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTUtilities.java
+++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/privy/WKTUtilities.java
@@@ -1,0 -1,548 +1,548 @@@
+ /*
+ * 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.privy;
+
+ import java.lang.reflect.Array;
+ import java.util.function.Function;
+ import java.util.logging.Logger;
+ import javax.measure.Unit;
+ import javax.measure.Quantity;
+ import javax.measure.quantity.Angle;
+ import org.opengis.parameter.ParameterValue;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.parameter.GeneralParameterValue;
+ import org.opengis.parameter.GeneralParameterDescriptor;
+ import org.opengis.referencing.IdentifiedObject;
+ import org.opengis.referencing.crs.CoordinateReferenceSystem;
+ import org.opengis.referencing.cs.CoordinateSystem;
+ import org.opengis.referencing.cs.CoordinateSystemAxis;
+ import org.opengis.referencing.datum.Datum;
+ import org.opengis.referencing.datum.GeodeticDatum;
+ import org.opengis.referencing.datum.PrimeMeridian;
+ import org.opengis.referencing.datum.Ellipsoid;
+ import org.opengis.referencing.operation.Matrix;
+ import org.opengis.referencing.operation.MathTransform;
+ import org.apache.sis.referencing.IdentifiedObjects;
+ import org.apache.sis.referencing.crs.AbstractCRS;
+ import org.apache.sis.referencing.cs.AbstractCS;
+ import org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis;
+ import org.apache.sis.referencing.datum.AbstractDatum;
+ import org.apache.sis.referencing.datum.DefaultGeodeticDatum;
+ import org.apache.sis.referencing.datum.DefaultPrimeMeridian;
+ import org.apache.sis.referencing.datum.DefaultEllipsoid;
+ import org.apache.sis.referencing.operation.transform.MathTransforms;
+ import org.apache.sis.referencing.operation.provider.Affine;
+ import org.apache.sis.system.Loggers;
+ import org.apache.sis.parameter.DefaultParameterValue;
+ import org.apache.sis.parameter.Parameterized;
+ import org.apache.sis.io.wkt.ElementKind;
+ import org.apache.sis.io.wkt.FormattableObject;
+ import org.apache.sis.io.wkt.Formatter;
+ import org.apache.sis.measure.Units;
+ import org.apache.sis.util.Static;
+ import org.apache.sis.util.CharSequences;
+ import org.apache.sis.util.resources.Vocabulary;
+ import org.apache.sis.util.privy.Constants;
+ import org.apache.sis.util.privy.Numerics;
+ import org.apache.sis.math.DecimalFunctions;
+ import org.apache.sis.math.Statistics;
+ import org.apache.sis.math.Vector;
+
-// Specific to the geoapi-3.1 and geoapi-4.0 branches:
-import org.opengis.metadata.Identifier;
++// Specific to the main branch:
++import org.opengis.referencing.ReferenceIdentifier;
+
+
+ /**
+ * Utility methods for referencing WKT formatting.
+ *
+ * This class provides a set of {@code toFormattable(…)} for various {@link IdentifiedObject} subtypes.
+ * It is important to <strong>not</strong> provide a generic {@code toFormattable(IdentifiedObject)}
+ * method, because the user may choose to implement more than one GeoAPI interface for the same object.
+ * We need to be specific in order to select the right "aspect" of the given object.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+ public final class WKTUtilities extends Static {
+ /**
+ * The logger for Well Known Text operations.
+ */
+ public static final Logger LOGGER = Logger.getLogger(Loggers.WKT);
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private WKTUtilities() {
+ }
+
+ /**
+ * Returns the WKT type of the given interface.
+ *
+ * For {@link CoordinateSystem} base type, the returned value shall be one of
+ * {@code affine}, {@code Cartesian}, {@code cylindrical}, {@code ellipsoidal}, {@code linear},
+ * {@code parametric}, {@code polar}, {@code spherical}, {@code temporal} or {@code vertical}.
+ *
+ * @param base the abstract base interface.
+ * @param type the interface or classes for which to get the WKT type.
+ * @return the WKT type for the given class or interface, or {@code null} if none.
+ *
+ * @see ReferencingUtilities#toPropertyName(Class, Class)
+ */
+ public static String toType(final Class<?> base, final Class<?> type) {
+ if (type != base) {
+ final StringBuilder name = ReferencingUtilities.toPropertyName(base, type);
+ if (name != null) {
+ int end = name.length() - 2;
+ if (CharSequences.regionMatches(name, end, "CS")) {
+ name.setLength(end);
+ if ("time".contentEquals(name)) {
+ return "temporal";
+ }
+ if (CharSequences.regionMatches(name, 0, "cartesian")) {
+ name.setCharAt(0, 'C'); // "Cartesian"
+ }
+ return name.toString();
+ }
... 1967 lines suppressed ...