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 589b2aa29aa3fa6a54142cb0d518992ec9cebcba Merge: 4c46d7bb98 2563f4abe5 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Aug 30 15:49:57 2024 +0200 Merge branch 'geoapi-3.1'. buildSrc/build.gradle.kts | 4 +- .../apache/sis/buildtools/gradle/Conventions.java | 3 +- .../apache/sis/buildtools/gradle/Dependency.java | 5 + .../sis/buildtools/gradle/ModularCompilation.java | 2 +- .../org/apache/sis/buildtools/maven/Generator.java | 52 +- .../apache/sis/coverage/grid/GridDerivation.java | 1 + .../sis/coverage/privy/ColorModelFactory.java | 2 - .../sis/feature/builder/AttributeTypeBuilder.java | 2 +- .../apache/sis/geometry/wrapper/GeometryType.java | 178 +- .../apache/sis/geometry/wrapper/esri/Factory.java | 2 +- .../apache/sis/geometry/wrapper/j2d/Factory.java | 2 +- .../sis/geometry/wrapper/GeometryTypeTest.java | 25 +- .../org.apache.sis.metadata/main/module-info.java | 1 + .../org/apache/sis/metadata/iso/ISOMetadata.java | 2 +- .../iso/quality/DefaultQuantitativeResult.java | 2 +- .../apache/sis/metadata/sql/MetadataWriter.java | 14 +- .../org/apache/sis/metadata/sql/privy/Dialect.java | 81 +- .../apache/sis/metadata/sql/privy/SQLBuilder.java | 40 + .../sis/metadata/sql/privy/SQLUtilities.java | 22 +- .../sis/metadata/sql/privy/ScriptRunner.java | 6 +- .../apache/sis/metadata/sql/privy/Supports.java | 74 + .../org/apache/sis/util/iso/DefaultScopedName.java | 5 +- .../sis/metadata/sql/IdentifierGeneratorTest.java | 2 +- .../org/apache/sis/metadata/sql/TestDatabase.java | 10 +- .../gazetteer/ReferencingByIdentifiers.java | 6 +- .../main/module-info.java | 1 + .../org/apache/sis/geometry/ArrayEnvelope.java | 22 +- .../org/apache/sis/geometry/DirectPosition1D.java | 2 +- .../apache/sis/geometry/GeneralDirectPosition.java | 6 +- .../main/org/apache/sis/io/wkt/Formatter.java | 5 +- .../sis/parameter/DefaultParameterDescriptor.java | 3 + .../parameter/DefaultParameterDescriptorGroup.java | 4 + .../org/apache/sis/parameter/TensorValues.java | 6 + .../sis/referencing/AbstractIdentifiedObject.java | 3 + .../apache/sis/referencing/crs/AbstractCRS.java | 2 + .../sis/referencing/crs/AbstractDerivedCRS.java | 6 +- .../sis/referencing/crs/AbstractSingleCRS.java | 6 +- .../sis/referencing/crs/DefaultCompoundCRS.java | 6 +- .../sis/referencing/crs/DefaultDerivedCRS.java | 4 +- .../sis/referencing/crs/DefaultProjectedCRS.java | 4 +- .../org/apache/sis/referencing/cs/AbstractCS.java | 4 + .../cs/DefaultCoordinateSystemAxis.java | 2 + .../sis/referencing/datum/AbstractDatum.java | 65 +- .../sis/referencing/datum/BursaWolfParameters.java | 20 +- .../referencing/datum/DefaultDatumEnsemble.java | 5 +- .../sis/referencing/datum/DefaultEllipsoid.java | 4 + .../referencing/datum/DefaultGeodeticDatum.java | 159 +- .../sis/referencing/datum/DefaultImageDatum.java | 4 + .../referencing/datum/DefaultPrimeMeridian.java | 4 + .../referencing/datum/DefaultTemporalDatum.java | 4 + .../referencing/datum/DefaultVerticalDatum.java | 91 + .../apache/sis/referencing/datum/PseudoDatum.java | 33 +- .../sis/referencing/datum/TimeDependentBWP.java | 16 +- .../referencing/factory/GeodeticObjectFactory.java | 70 +- .../referencing/factory/sql/EPSGDataAccess.java | 14 +- .../operation/AbstractCoordinateOperation.java | 2 + .../operation/DefaultConcatenatedOperation.java | 6 +- .../operation/DefaultOperationMethod.java | 2 + .../operation/DefaultPassThroughOperation.java | 6 +- .../operation/matrix/GeneralMatrix.java | 2 +- .../operation/matrix/NonSquareMatrix.java | 2 +- .../operation/transform/ConcatenatedTransform.java | 2 +- .../operation/transform/CopyTransform.java | 2 +- .../operation/transform/DatumShiftTransform.java | 6 +- .../transform/EllipsoidToCentricTransform.java | 6 +- .../transform/ExponentialTransform1D.java | 4 +- .../operation/transform/InterpolatedTransform.java | 6 +- .../operation/transform/LinearTransform1D.java | 4 +- .../operation/transform/MolodenskyTransform.java | 6 +- .../operation/transform/PassThroughTransform.java | 6 +- .../operation/transform/PoleRotation.java | 4 + .../operation/transform/PowerTransform1D.java | 4 +- .../operation/transform/ProjectiveTransform.java | 4 +- .../operation/transform/ScaleTransform.java | 4 +- .../transform/SpecializableTransform.java | 9 +- .../operation/transform/TranslationTransform.java | 4 +- .../operation/transform/WraparoundTransform.java | 4 + .../main/org/apache/sis/storage/landsat/Band.java | 2 +- .../apache/sis/storage/landsat/LandsatStore.java | 7 +- .../apache/sis/storage/landsat/package-info.java | 2 +- .../apache/sis/storage/geotiff/GeoTiffStore.java | 6 +- .../sis/storage/geotiff/GeoTiffStoreProvider.java | 8 +- .../org/apache/sis/storage/geotiff/Writer.java | 5 + .../apache/sis/storage/geotiff/package-info.java | 3 +- .../sis/storage/geotiff/spi/SchemaModifier.java | 2 +- .../storage/geotiff/writer/CompressionChannel.java | 6 +- .../sis/storage/geotiff/writer/PixelChannel.java | 1 + .../storage/geotiff/writer/PredictorChannel.java | 1 + .../org/apache/sis/storage/geotiff/writer/ZIP.java | 1 + .../org/apache/sis/storage/netcdf/NetcdfStore.java | 7 +- .../apache/sis/storage/netcdf/package-info.java | 2 +- .../main/module-info.java | 24 +- .../org/apache/sis/storage/sql/DataAccess.java | 338 +++ .../apache/sis/storage/sql/ResourceDefinition.java | 47 +- .../main/org/apache/sis/storage/sql/SQLStore.java | 503 +++- .../apache/sis/storage/sql/SQLStoreProvider.java | 16 +- .../apache/sis/storage/sql/SimpleFeatureStore.java | 168 ++ .../apache/sis/storage/sql/feature/Analyzer.java | 172 +- .../sis/storage/sql/feature/CRSEncoding.java} | 28 +- .../apache/sis/storage/sql/feature/Database.java | 507 ++-- .../sis/storage/sql/feature/FeatureAnalyzer.java | 3 +- .../sis/storage/sql/feature/FeatureIterator.java | 14 +- .../sis/storage/sql/feature/FeatureStream.java | 11 +- .../sis/storage/sql/feature/GeometryGetter.java | 4 +- .../storage/sql/feature/GeometryTypeEncoding.java | 56 + .../sis/storage/sql/feature/InfoStatements.java | 729 +++-- .../sis/storage/sql/feature/QueryAnalyzer.java | 2 +- .../sis/storage/sql/feature/SchemaModifier.java | 2 +- .../sis/storage/sql/feature/SpatialSchema.java | 212 ++ .../org/apache/sis/storage/sql/feature/Table.java | 5 +- .../sis/storage/sql/feature/TableAnalyzer.java | 1 - .../sis/storage/sql/feature/ValueGetter.java | 6 +- .../org/apache/sis/storage/sql/package-info.java | 32 +- .../sis/storage/sql/postgis/ExtendedInfo.java | 7 +- .../sis/storage/sql/postgis/ExtentEstimator.java | 2 +- .../apache/sis/storage/sql/postgis/Postgres.java | 42 +- .../org/apache/sis/util/stream/DeferredStream.java | 64 +- .../org/apache/sis/storage/sql/DataAccessTest.java | 69 + .../org/apache/sis/storage/sql/SQLStoreTest.java | 45 +- .../storage/sql/feature/InfoStatementsTest.java | 161 ++ .../sql/feature/SelectionClauseWriterTest.java | 2 +- .../sql/feature/TemporalValueGetterTest.java | 2 +- .../sis/storage/sql/postgis/PostgresTest.java | 4 +- .../org.apache.sis.storage/main/module-info.java | 4 + .../apache/sis/io/stream/InternalOptionKey.java | 25 + .../main/org/apache/sis/storage/DataStore.java | 22 +- .../org/apache/sis/storage/StorageConnector.java | 91 +- .../main/org/apache/sis/storage/URLDataSource.java | 198 ++ .../org/apache/sis/storage/WritableAggregate.java | 2 +- .../apache/sis/storage/base/MetadataFetcher.java | 126 +- .../apache/sis/storage/base/TiledGridCoverage.java | 18 +- .../org/apache/sis/storage/internal/Resources.java | 5 + .../sis/storage/internal/Resources.properties | 1 + .../sis/storage/internal/Resources_fr.properties | 1 + .../src/org.apache.sis.util/main/module-info.java | 1 + .../main/org/apache/sis/util/privy/Constants.java | 2 +- .../org/apache/sis/test/TestConfiguration.java | 18 +- incubator/build.gradle.kts | 66 + .../org.apache.sis.storage.DataStoreProvider | 4 + .../main/module-info.java | 57 + .../org/apache/sis/storage/geopackage/Content.java | 466 ++++ .../sis/storage/geopackage/ContentHandler.java | 91 + .../sis/storage/geopackage/ContentWriter.java | 232 ++ .../org/apache/sis/storage/geopackage/Core.sql | 126 + .../sis/storage/geopackage/Extension Metadata.sql | 38 + .../sis/storage/geopackage/Extension Schema.sql | 40 + .../Extension Tiled Gridded Coverage Data.sql | 47 + .../apache/sis/storage/geopackage/GpkgStore.java | 456 +++ .../sis/storage/geopackage/GpkgStoreProvider.java | 284 ++ .../apache/sis/storage/geopackage/Initializer.java | 178 ++ .../sis/storage/geopackage/package-info.java | 22 +- .../apache/sis/storage/geopackage/FeatureSet.sql | 164 ++ .../storage/geopackage/GpkgStoreProviderTest.java | 32 +- .../sis/storage/geopackage/GpkgStoreTest.java | 339 +++ .../conformance/CoreConformanceTest.java | 219 ++ .../org.apache.sis.storage.DataStoreProvider | 4 + ...org.apache.sis.storage.gimi.isobmff.BoxRegistry | 8 + .../main/module-info.java | 25 +- .../org/apache/sis/storage/gimi/GimiProvider.java | 108 + .../org/apache/sis/storage/gimi/GimiStore.java | 177 ++ .../main/org/apache/sis/storage/gimi/GimiTile.java | 66 + .../apache/sis/storage/gimi/GimiTileMatrix.java | 116 + .../apache/sis/storage/gimi/GimiTileMatrixSet.java | 72 + .../org/apache/sis/storage/gimi/GimiUtils.java | 52 + .../main/org/apache/sis/storage/gimi/Item.java | 136 + .../org/apache/sis/storage/gimi/ResourceGrid.java | 134 + .../apache/sis/storage/gimi/ResourceImageJpeg.java | 61 + .../storage/gimi/ResourceImageUncompressed.java | 181 ++ .../apache/sis/storage/gimi/ResourcePyramid.java | 145 + .../apache/sis/storage/gimi/ResourceUnknown.java | 34 +- .../storage/gimi/internal/MatrixGridRessource.java | 137 + .../sis/storage/gimi/internal/ScaleSortedMap.java | 104 + .../sis/storage/gimi/internal/StringUtilities.java | 63 + .../sis/storage/gimi/internal/TileMatrices.java | 64 + .../org/apache/sis/storage/gimi/isobmff/Box.java | 278 ++ .../sis/storage/gimi/isobmff/BoxRegistry.java | 64 + .../apache/sis/storage/gimi/isobmff/FullBox.java | 23 +- .../sis/storage/gimi/isobmff/ISOBMFFReader.java | 157 ++ .../apache/sis/storage/gimi/isobmff/gimi/GIMI.java | 65 + .../gimi/isobmff/gimi/ModelTiePointProperty.java | 63 + .../isobmff/gimi/ModelTransformationProperty.java | 34 +- .../gimi/isobmff/gimi/WellKnownText2Property.java | 27 +- .../gimi/isobmff/iso14496_10/ContentDescribes.java | 19 +- .../gimi/isobmff/iso14496_10/ISO14496_10.java | 60 + .../isobmff/iso14496_12/ColourInformation.java | 55 + .../gimi/isobmff/iso14496_12/CombinaisonType.java | 33 +- .../gimi/isobmff/iso14496_12/EntityToGroup.java | 28 +- .../gimi/isobmff/iso14496_12/ExtendedType.java | 25 +- .../gimi/isobmff/iso14496_12/Extension.java | 19 +- .../isobmff/iso14496_12/FDItemInfoExtension.java | 28 +- .../storage/gimi/isobmff/iso14496_12/FileType.java | 16 +- .../gimi/isobmff/iso14496_12/FreeSpace.java | 19 +- .../gimi/isobmff/iso14496_12/GeneralType.java | 52 + .../gimi/isobmff/iso14496_12/GroupList.java | 23 +- .../gimi/isobmff/iso14496_12/HandlerReference.java | 33 +- .../gimi/isobmff/iso14496_12/ISO14496_12.java | 114 + .../isobmff/iso14496_12/IdentifiedMediaData.java | 49 + .../storage/gimi/isobmff/iso14496_12/ItemData.java | 28 +- .../gimi/isobmff/iso14496_12/ItemFullProperty.java | 16 +- .../storage/gimi/isobmff/iso14496_12/ItemInfo.java | 42 +- .../gimi/isobmff/iso14496_12/ItemInfoEntry.java | 74 + .../gimi/isobmff/iso14496_12/ItemLocation.java | 155 ++ .../gimi/isobmff/iso14496_12/ItemProperties.java | 23 +- .../gimi/isobmff/iso14496_12/ItemProperty.java | 16 +- .../iso14496_12/ItemPropertyAssociation.java | 68 + .../isobmff/iso14496_12/ItemPropertyContainer.java | 23 +- .../gimi/isobmff/iso14496_12/ItemReference.java | 49 + .../gimi/isobmff/iso14496_12/MediaData.java | 28 +- .../sis/storage/gimi/isobmff/iso14496_12/Meta.java | 24 +- .../storage/gimi/isobmff/iso14496_12/Movie.java | 24 +- .../gimi/isobmff/iso14496_12/MovieHeader.java | 30 +- .../gimi/isobmff/iso14496_12/OriginalFileType.java | 23 +- .../gimi/isobmff/iso14496_12/PrimaryItem.java | 32 +- .../iso14496_12/ProgressiveDownloadInfo.java | 28 +- .../iso14496_12/SingleItemTypeReference.java | 49 + .../iso14496_12/SingleItemTypeReferenceLarge.java | 26 +- .../storage/gimi/isobmff/iso14496_12/Track.java | 24 +- .../gimi/isobmff/iso14496_12/TrackHeader.java | 28 +- .../gimi/isobmff/iso23001_17/ChromaLocation.java | 27 +- .../isobmff/iso23001_17/ComponentDefinition.java | 67 + .../gimi/isobmff/iso23001_17/ComponentPalette.java | 57 + .../iso23001_17/ComponentPatternDefinition.java | 49 + .../iso23001_17/ComponentReferenceLevel.java | 27 +- .../iso23001_17/DepthMappingInformation.java | 27 +- .../isobmff/iso23001_17/DisparityInformation.java | 27 +- .../iso23001_17/FieldInterlaceProperty.java | 27 +- .../isobmff/iso23001_17/FieldInterlaceType.java | 27 +- .../iso23001_17/FramePackingInformation.java | 27 +- .../gimi/isobmff/iso23001_17/ISO23001_17.java | 92 + .../iso23001_17/PolarizationPatternDefinition.java | 27 +- .../isobmff/iso23001_17/SensorBadPixelsMap.java | 27 +- .../iso23001_17/SensorNonUniformityCorrection.java | 27 +- .../gimi/isobmff/iso23001_17/TAIClockInfo.java | 48 + .../gimi/isobmff/iso23001_17/TAITimeStamp.java | 45 + .../iso23001_17/UncompressedFrameConfig.java | 92 + .../isobmff/iso23008_12/DerivedImageReference.java | 19 +- .../gimi/isobmff/iso23008_12/ISO23008_12.java | 71 + .../iso23008_12/ImagePyramidEntityGroup.java | 62 + .../isobmff/iso23008_12/ImageSpatialExtents.java | 44 +- .../iso23008_12/PixelInformationProperty.java | 30 +- .../iso23008_12/UserDescriptionProperty.java | 33 +- .../org/apache/sis/storage/gimi/package-info.java | 14 +- .../org/apache/sis/storage/gimi/StoreTest.java | 17 +- .../main/module-info.java | 15 +- .../main/org/apache/sis/storage/gsf/Attitude.java | 89 + .../main/org/apache/sis/storage/gsf/Comment.java | 63 + .../main/org/apache/sis/storage/gsf/DataID.java | 69 + .../main/org/apache/sis/storage/gsf/GSF.java | 1786 ++++++++++++ .../apache/sis/storage/gsf/GSFRecordReader.java | 88 + .../apache/sis/storage/gsf/HVNavigationError.java | 89 + .../main/org/apache/sis/storage/gsf/Header.java | 40 +- .../main/org/apache/sis/storage/gsf/History.java | 105 + .../apache/sis/storage/gsf/NavigationError.java | 96 + .../main/org/apache/sis/storage/gsf/Position.java | 74 + .../apache/sis/storage/gsf/PositionOffsets.java | 86 + .../sis/storage/gsf/ProcessingParameters.java | 65 + .../main/org/apache/sis/storage/gsf/Records.java | 146 + .../apache/sis/storage/gsf/SBSensorSpecific.java | 110 + .../main/org/apache/sis/storage/gsf/SVP.java | 90 + .../org/apache/sis/storage/gsf/ScaleFactors.java | 72 + .../main/org/apache/sis/storage/gsf/ScaleInfo.java | 85 + .../apache/sis/storage/gsf/SensorParameters.java | 106 + .../org/apache/sis/storage/gsf/SensorSpecific.java | 108 + .../org/apache/sis/storage/gsf/SingleBeamPing.java | 216 ++ .../org/apache/sis/storage/gsf/StructClass.java | 219 ++ .../org/apache/sis/storage/gsf/SwathBathyPing.java | 354 +++ .../apache/sis/storage/gsf/SwathBathySummary.java | 94 + .../main/org/apache/sis/storage/gsf/TimeSpec.java | 60 + .../org/apache/sis/storage/gsf/package-info.java | 14 +- .../sis/storage/gsf/specific/BDBSpecific.java | 133 + .../sis/storage/gsf/specific/CmpSassSpecific.java | 73 + .../sis/storage/gsf/specific/DeltaTSpecific.java | 405 +++ .../sis/storage/gsf/specific/EM100Specific.java | 152 + .../sis/storage/gsf/specific/EM121ASpecific.java | 454 +++ .../sis/storage/gsf/specific/EM12Specific.java | 356 +++ .../storage/gsf/specific/EM3ImagerySpecific.java | 488 ++++ .../sis/storage/gsf/specific/EM3RawSpecific.java | 875 ++++++ .../sis/storage/gsf/specific/EM3RawTxSector.java | 488 ++++ .../sis/storage/gsf/specific/EM3RunTime.java | 1313 +++++++++ .../sis/storage/gsf/specific/EM3Specific.java | 539 ++++ .../storage/gsf/specific/EM4ImagerySpecific.java | 670 +++++ .../sis/storage/gsf/specific/EM4Specific.java | 832 ++++++ .../sis/storage/gsf/specific/EM4TxSector.java | 533 ++++ .../sis/storage/gsf/specific/EM950Specific.java | 320 +++ .../sis/storage/gsf/specific/EMPUStatus.java | 356 +++ .../apache/sis/storage/gsf/specific/EMRunTime.java | 1531 ++++++++++ .../sis/storage/gsf/specific/EchotracSpecific.java | 184 ++ .../sis/storage/gsf/specific/ElacMkIISpecific.java | 364 +++ .../storage/gsf/specific/GeoSwathPlusSpecific.java | 1212 ++++++++ .../sis/storage/gsf/specific/KMALLExtraDet.java | 218 ++ .../storage/gsf/specific/KMALLImagerySpecific.java | 126 + .../sis/storage/gsf/specific/KMALLSpecific.java | 988 +++++++ .../sis/storage/gsf/specific/KMALLTxSector.java | 670 +++++ .../gsf/specific/Klein5410BssImagerySpecific.java | 297 ++ .../storage/gsf/specific/Klein5410BssSpecific.java | 851 ++++++ .../sis/storage/gsf/specific/MGD77Specific.java | 320 +++ .../sis/storage/gsf/specific/NOSHDBSpecific.java | 140 + .../gsf/specific/R2SonicImagerySpecific.java | 1355 +++++++++ .../sis/storage/gsf/specific/R2SonicSpecific.java | 1568 +++++++++++ .../gsf/specific/Reson7100ImagerySpecific.java | 173 ++ .../storage/gsf/specific/Reson7100Specific.java | 2267 +++++++++++++++ .../gsf/specific/Reson8100ImagerySpecific.java | 127 + .../storage/gsf/specific/Reson8100Specific.java | 1301 +++++++++ .../gsf/specific/ResonTSeriesImagerySpecific.java | 173 ++ .../storage/gsf/specific/ResonTSeriesSpecific.java | 2923 ++++++++++++++++++++ .../sis/storage/gsf/specific/SBAmpSpecific.java | 320 +++ .../sis/storage/gsf/specific/SBBDBSpecific.java | 444 +++ .../storage/gsf/specific/SBEchotracSpecific.java | 309 +++ .../sis/storage/gsf/specific/SBMGD77Specific.java | 400 +++ .../sis/storage/gsf/specific/SBNOSHDBSpecific.java | 218 ++ .../storage/gsf/specific/SBNavisoundSpecific.java | 173 ++ .../storage/gsf/specific/SeaBat8101Specific.java | 850 ++++++ .../sis/storage/gsf/specific/SeaBatIISpecific.java | 490 ++++ .../sis/storage/gsf/specific/SeaBatSpecific.java | 320 +++ .../storage/gsf/specific/SeaBeam2112Specific.java | 524 ++++ .../sis/storage/gsf/specific/SeabeamSpecific.java | 94 + .../sis/storage/gsf/specific/SeamapSpecific.java | 521 ++++ .../sis/storage/gsf/specific/TypeIIISpecific.java | 319 +++ .../test/org/apache/sis/storage/gsf/StoreTest.java | 17 +- .../apache/sis/storage/shapefile/dbf/DBFField.java | 22 +- .../sis/storage/shapefile/dbf/DBFHeader.java | 3 +- netbeans-project/ivy.xml | 17 +- netbeans-project/nbproject/project.properties | 9 + netbeans-project/nbproject/project.xml | 1 + optional/build.gradle.kts | 95 +- .../apache/sis/gui/coverage/CoverageCanvas.java | 4 +- .../main/META-INF/NOTICE | 1 + .../org.apache.sis.setup.InstallationResources | 4 + .../main/module-info.java | 43 + .../sis/resources/embedded/EmbeddedResources.java | 153 + .../sis/resources/embedded/package-info.java | 35 + .../resources/embedded/EmbeddedResourcesTest.java | 141 + .../apache/sis/resources/embedded/Generator.java | 222 ++ .../main/META-INF/NOTICE | 10 + .../org.apache.sis.setup.InstallationResources | 4 + .../main/module-info.java | 41 + .../sis/referencing/factory/sql/epsg/.gitignore | 7 + .../sis/referencing/factory/sql/epsg/README.md | 23 + .../factory/sql/epsg/ScriptProvider.java | 104 + .../referencing/factory/sql/epsg/package-info.java | 37 + .../factory/sql/epsg/DataScriptFormatter.java | 3 +- .../factory/sql/epsg/DataScriptFormatterTest.java | 2 +- .../referencing/factory/sql/epsg/DebugTools.sql | 0 .../sis/referencing/factory/sql/epsg/README.md | 3 +- .../factory/sql/epsg/ScriptProviderTest.java | 91 + settings.gradle.kts | 20 +- 346 files changed, 46246 insertions(+), 1672 deletions(-) diff --cc buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java index 1ac9152a01,45f821fdd7..b95757d8c3 --- a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java +++ b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java @@@ -70,7 -72,11 +72,10 @@@ public final class Dependency Map.entry("gui", "application:sis-javafx"), // Optional. Map.entry("cql", "core:sis-cql"), // Incubator. Map.entry("storage.shapefile", "core:sis-shapefile"), + Map.entry("storage.gimi", "core:sis-gimi"), + Map.entry("storage.gsf", "core:sis-gsf"), + Map.entry("storage.geopackage", "core:sis-geopackage"), Map.entry("storage.coveragejson", "core:sis-coveragejson"), - Map.entry("portrayal.map", "core:sis-portrayal-map"), Map.entry("webapp", "application:sis-webapp") ); diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java index aade867a1e,d99f986546..1bb9d6cded --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java @@@ -87,11 -87,9 +87,12 @@@ import org.apache.sis.geometry.Abstract import org.apache.sis.geometry.AbstractEnvelope; import org.apache.sis.xml.NilObject; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.util.ControlledVocabulary; -import org.opengis.referencing.ObjectDomain; +// Specific to the main branch: +import org.opengis.util.CodeList; +import org.opengis.referencing.ReferenceIdentifier; +import org.apache.sis.referencing.DefaultObjectDomain; ++import org.apache.sis.referencing.datum.AbstractDatum; +import org.apache.sis.referencing.internal.Legacy; /** @@@ -889,8 -887,8 +890,8 @@@ public class Formatter implements Local private void appendForSubtypes(final IdentifiedObject object) { InternationalString anchor = null, scope = null; Extent area = null; -- if (object instanceof Datum) { - anchor = ((Datum) object).getAnchorPoint(); - anchor = ((Datum) object).getAnchorDefinition().orElse(null); ++ if (object instanceof AbstractDatum) { ++ anchor = ((AbstractDatum) object).getAnchorPoint(); } else if (!(object instanceof ReferenceSystem || object instanceof CoordinateOperation)) { return; } diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java index 8619fca435,7454658ac6..a4bcb99c7e --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java @@@ -576,6 -567,6 +576,9 @@@ public class AbstractIdentifiedObject e * The domain includes a scope (description of the primary purpose of this object) together * with a domain of validity (spatial and temporal extent in which the object can be used). * ++ * <div class="note"><b>Upcoming API change:</b> ++ * the type of collection elements may become {@code ObjectDomain} after the next GeoAPI release.</div> ++ * * @return scopes and domains of validity of this object. * * @since 1.4 diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java index 3cd29c850f,7fc8ec1690..cabcc1e865 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/AbstractDatum.java @@@ -46,10 -46,9 +46,11 @@@ import org.apache.sis.io.wkt.Formatter import static org.apache.sis.util.Utilities.deepEquals; import static org.apache.sis.util.collection.Containers.property; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.datum.DynamicReferenceFrame; -import org.opengis.metadata.Identifier; +// Specific to the main branch: +import org.opengis.referencing.ReferenceIdentifier; +import org.opengis.metadata.extent.Extent; +import org.apache.sis.referencing.internal.Legacy; ++import org.apache.sis.pending.geoapi.referencing.DynamicReferenceFrame; /** @@@ -360,35 -335,20 +361,46 @@@ public class AbstractDatum extends Abst @XmlSchemaType(name = "date") @XmlElement(name = "realizationEpoch") public Date getRealizationEpoch() { - return Datum.super.getRealizationEpoch(); + return getAnchorEpoch().map(Legacy::toDate).orElse(null); + } + + /** + * Returns the region or timeframe in which this datum is valid, or {@code null} if unspecified. + * + * @return area or region or timeframe in which this datum is valid, or {@code null}. + * + * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. + */ + @Override + @Deprecated(since = "1.4") + public Extent getDomainOfValidity() { + return Legacy.getDomainOfValidity(getDomains()); + } + + /** + * Returns the domain or limitations of usage, or {@code null} if unspecified. + * + * @return description of domain of usage, or limitations of usage, for which this datum object is valid. + * + * @deprecated Replaced by {@link #getDomains()} as of ISO 19111:2019. + */ + @Override + @Deprecated(since = "1.4") + public InternationalString getScope() { + return Legacy.getScope(getDomains()); } + /** + * Returns the frame reference epoch if this datum is dynamic, or {@code null} if this datum is static. + * This method is overridden with public access in Apache SIS {@code Dynamic} subclasses. + * The default implementation should be suitable for non-SIS implementations. + * + * @return the reference epoch if this datum is dynamic, or {@code null} if this datum is static. + */ + Temporal getFrameReferenceEpoch() { + return (this instanceof DynamicReferenceFrame) ? ((DynamicReferenceFrame) this).getFrameReferenceEpoch() : null; + } + /** * Returns the date on which the datum definition was published. * @@@ -471,6 -433,12 +483,12 @@@ * {@linkplain #getAnchorDefinition() anchor definition}, {@linkplain #getAnchorEpoch() anchor epoch}, * and the {@linkplain #getDomains() domains}. * + * <h4>Static versus dynamic datum</h4> - * If this datum implements the {@link DynamicReferenceFrame} interface, then the given object needs ++ * If this datum implements the {@code DynamicReferenceFrame} interface, then the given object needs + * to also implement that interfaces and provide the same reference epoch for being considered equal. - * Conversely, if this datum does not implement {@link DynamicReferenceFrame}, then the given object ++ * Conversely, if this datum does not implement {@code DynamicReferenceFrame}, then the given object + * also need to <em>not</em> implement that interface for being considered equal. + * * @param object the object to compare to {@code this}. * @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or * {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only @@@ -485,13 -453,18 +503,24 @@@ switch (mode) { case STRICT: { final var that = (AbstractDatum) object; - return Objects.equals(anchorEpoch, that.anchorEpoch) && - Objects.equals(anchorDefinition, that.anchorDefinition); + return Objects.equals(anchorEpoch, that.anchorEpoch) && + Objects.equals(anchorDefinition, that.anchorDefinition) && + Objects.equals(publicationDate, that.publicationDate) && + Objects.equals(conventionalRS, that.conventionalRS); } case BY_CONTRACT: { - final Datum that = (Datum) object; - return deepEquals(getRealizationEpoch(), that.getRealizationEpoch(), mode) && - deepEquals(getAnchorPoint(), that.getAnchorPoint(), mode); - final var that = (Datum) object; ++ if (!(object instanceof AbstractDatum)) { ++ return getAnchorEpoch().isEmpty() && ++ getAnchorDefinition().isEmpty() && ++ getPublicationDate().isEmpty() && ++ getConventionalRS().isEmpty(); ++ } ++ final var that = (AbstractDatum) object; + return compareDynamicReferenceFrames(that, mode) && + deepEquals(getAnchorEpoch(), that.getAnchorEpoch(), mode) && + deepEquals(getAnchorDefinition(), that.getAnchorDefinition(), mode) && + deepEquals(getPublicationDate(), that.getPublicationDate(), mode) && + deepEquals(getConventionalRS(), that.getConventionalRS(), mode); } default: { /* diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultGeodeticDatum.java index d01ff32b00,b95286c5f9..bb9ca3308d --- 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 @@@ -54,8 -52,9 +52,9 @@@ import static org.apache.sis.util.Argum import static org.apache.sis.util.ArgumentChecks.ensureNonNullElement; import static org.apache.sis.referencing.privy.WKTUtilities.toFormattable; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.datum.DynamicReferenceFrame; -import org.opengis.metadata.Identifier; +// Specific to the main branch: +import org.opengis.referencing.ReferenceIdentifier; ++import org.apache.sis.pending.geoapi.referencing.DynamicReferenceFrame; /** @@@ -511,6 -495,112 +495,115 @@@ public class DefaultGeodeticDatum exten return selector.best(); } + /** + * Returns the position vector transformation (geocentric domain) as an affine transform. + * If this datum is dynamic, the frame reference epoch is used. + * Otherwise, a time is computed from the temporal area of interest. + * - * @see DynamicReferenceFrame#getFrameReferenceEpoch() + * @see BursaWolfParameters#getPositionVectorTransformation(Temporal) + */ + private Matrix createTransformation(final BursaWolfParameters bursaWolf, final Extent areaOfInterest) { + Temporal epoch = null; + /* + * Implementation note: we know that we do not need to compute an instant if the parameters is + * not a subclass of BursaWolfParameters. This optimisation covers the vast majority of cases. + */ + if (bursaWolf.getClass() != BursaWolfParameters.class) { + epoch = getFrameReferenceEpoch(); + if (epoch == null) { + epoch = Extents.getInstant(areaOfInterest, null, 0.5).orElse(null); + // 0.5 is for choosing the instant midway between start and end. + } + } + return bursaWolf.getPositionVectorTransformation(epoch); + } + + /** + * A geodetic reference frame in which some of the defining parameters have time dependency. + * The parameter values are valid at the time given by the + * {@linkplain #getFrameReferenceEpoch() frame reference epoch}. + * ++ * <div class="note"><b>Upcoming API change:</b> ++ * this class may implement a {@code DynamicReferenceFrame} interface from the GeoAPI standard ++ * after the next GeoAPI release. In the meantime, {@code DynamicReferenceFrame} is not a public API.</div> ++ * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * @since 1.5 + */ + public static class Dynamic extends DefaultGeodeticDatum implements DynamicReferenceFrame { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 6117199873814779662L; + + /** + * The epoch to which the definition of the dynamic reference frame is referenced. + */ + @SuppressWarnings("serial") // Standard Java implementations are serializable. + private final Temporal frameReferenceEpoch; + + /** + * Creates a dynamic reference frame from the given properties. + * See super-class constructor for more information. + * + * @param properties the properties to be given to the identified object. + * @param ellipsoid the ellipsoid. + * @param primeMeridian the prime meridian. + * @param epoch the epoch to which the definition of the dynamic reference frame is referenced. + */ + public Dynamic(Map<String,?> properties, Ellipsoid ellipsoid, PrimeMeridian primeMeridian, Temporal epoch) { + super(properties, ellipsoid, primeMeridian); + frameReferenceEpoch = Objects.requireNonNull(epoch); + } + + /** + * Creates a new datum with the same values as the specified datum, which must be dynamic. + * + * @param datum the datum to copy. - * @throws ClassCastException if the given datum is not an instance of {@link DynamicReferenceFrame}. ++ * @throws ClassCastException if the given datum is not an instance of {@code DynamicReferenceFrame}. + * + * @see #castOrCopy(GeodeticDatum) + */ + protected Dynamic(final GeodeticDatum datum) { + super(datum); + frameReferenceEpoch = Objects.requireNonNull(((DynamicReferenceFrame) datum).getFrameReferenceEpoch()); + } + + /** + * Returns the epoch to which the coordinates of stations defining the dynamic reference frame are referenced. + * The type of the returned object depends on the epoch accuracy and the calendar in use. + * It may be merely a {@link java.time.Year}. + * + * @return the epoch to which the definition of the dynamic reference frame is referenced. + */ + @Override + public Temporal getFrameReferenceEpoch() { + return frameReferenceEpoch; + } + + /** + * Compares the specified object with this datum for equality. + * + * @hidden because nothing new to said. + */ + @Override + public boolean equals(final Object object, final ComparisonMode mode) { + return super.equals(object) && (mode != ComparisonMode.STRICT || + frameReferenceEpoch.equals(((Dynamic) object).frameReferenceEpoch)); + } + + /** + * Invoked by {@code hashCode()} for computing the hash code when first needed. + * + * @hidden because nothing new to said. + */ + @Override + protected long computeHashCode() { + return super.computeHashCode() + 31 * frameReferenceEpoch.hashCode(); + } + } + /** * Returns {@code true} if either the {@linkplain #getName() primary name} or at least * one {@linkplain #getAlias() alias} matches the given string according heuristic rules. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/DefaultVerticalDatum.java index fd46ba3bd7,0ea312039c..2f07233dd9 --- 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 @@@ -35,8 -36,11 +36,9 @@@ import org.apache.sis.metadata.privy.Im // Specific to the main and geoapi-3.1 branches: import org.opengis.referencing.datum.VerticalDatumType; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import java.util.Optional; -import org.opengis.referencing.datum.DynamicReferenceFrame; -import org.opengis.referencing.datum.RealizationMethod; -import org.opengis.metadata.Identifier; +// Specific to the main branch: +import org.opengis.referencing.ReferenceIdentifier; ++import org.apache.sis.pending.geoapi.referencing.DynamicReferenceFrame; /** @@@ -231,12 -248,96 +233,97 @@@ public class DefaultVerticalDatum exten * but in a programmatic way more suitable to coordinate transformation engines. * * @return the type of this vertical datum. - * - * @deprecated As of ISO 19111:2019, the {@code VerticalDatumType} argument is replaced by {@code RealizationMethod}. */ @Override - @Deprecated(since = "2.0") // Temporary version number until this branch is released. public VerticalDatumType getVerticalDatumType() { - return type; + return type(); } + /** + * A vertical reference frame in which some of the defining parameters have time dependency. + * The parameter values are valid at the time given by the + * {@linkplain #getFrameReferenceEpoch() frame reference epoch}. + * ++ * <div class="note"><b>Upcoming API change:</b> ++ * this class may implement a {@code DynamicReferenceFrame} interface from the GeoAPI standard ++ * after the next GeoAPI release. In the meantime, {@code DynamicReferenceFrame} is not a public API.</div> ++ * + * @author Martin Desruisseaux (Geomatys) + * @version 1.5 + * @since 1.5 + */ + public static class Dynamic extends DefaultVerticalDatum implements DynamicReferenceFrame { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = -2047994195060747008L; + + /** + * The epoch to which the definition of the dynamic reference frame is referenced. + */ + @SuppressWarnings("serial") // Standard Java implementations are serializable. + private final Temporal frameReferenceEpoch; + + /** + * Creates a dynamic reference frame from the given properties. + * See super-class constructor for more information. + * - * @param properties the properties to be given to the identified object. - * @param epoch the epoch to which the definition of the dynamic reference frame is referenced. ++ * @param properties the properties to be given to the identified object. ++ * @param type the type of this vertical datum. ++ * @param epoch the epoch to which the definition of the dynamic reference frame is referenced. + */ - public Dynamic(Map<String,?> properties, RealizationMethod method, Temporal epoch) { - super(properties, method); ++ public Dynamic(Map<String,?> properties, VerticalDatumType type, Temporal epoch) { ++ super(properties, type); + frameReferenceEpoch = Objects.requireNonNull(epoch); + } + + /** + * Creates a new datum with the same values as the specified datum, which must be dynamic. + * + * @param datum the datum to copy. - * @param method the realization method (geoid, tidal, <i>etc.</i>), or {@code null} if unspecified. - * @throws ClassCastException if the given datum is not an instance of {@link DynamicReferenceFrame}. ++ * @throws ClassCastException if the given datum is not an instance of {@code DynamicReferenceFrame}. + * + * @see #castOrCopy(VerticalDatum) + */ + protected Dynamic(final VerticalDatum datum) { + super(datum); + frameReferenceEpoch = Objects.requireNonNull(((DynamicReferenceFrame) datum).getFrameReferenceEpoch()); + } + + /** + * Returns the epoch to which the coordinates of stations defining the dynamic reference frame are referenced. + * The type of the returned object depends on the epoch accuracy and the calendar in use. + * It may be merely a {@link java.time.Year}. + * + * @return the epoch to which the definition of the dynamic reference frame is referenced. + */ + @Override + public Temporal getFrameReferenceEpoch() { + return frameReferenceEpoch; + } + + /** + * Compares the specified object with this datum for equality. + * + * @hidden because nothing new to said. + */ + @Override + public boolean equals(final Object object, final ComparisonMode mode) { + return super.equals(object) && (mode != ComparisonMode.STRICT || + frameReferenceEpoch.equals(((Dynamic) object).frameReferenceEpoch)); + } + + /** + * Invoked by {@code hashCode()} for computing the hash code when first needed. + * + * @hidden because nothing new to said. + */ + @Override + protected long computeHashCode() { + return super.computeHashCode() + 31 * frameReferenceEpoch.hashCode(); + } + } + /** * Compares this vertical datum with the specified object for equality. * @@@ -245,8 -346,11 +332,10 @@@ * {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only * properties relevant to coordinate transformations. * @return {@code true} if both objects are equal. + * + * @hidden because nothing new to said. */ @Override - @SuppressWarnings("deprecation") public boolean equals(final Object object, final ComparisonMode mode) { if (object == this) { return true; // Slight optimization. diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java index c215bd1211,eed9024fad..ed0dc4a523 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/datum/PseudoDatum.java @@@ -43,8 -43,9 +43,9 @@@ import java.util.Date import org.opengis.referencing.ReferenceIdentifier; import org.opengis.metadata.extent.Extent; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import java.time.temporal.Temporal; -import org.opengis.referencing.ObjectDomain; +// Specific to the main branch: ++import org.apache.sis.referencing.DefaultObjectDomain; +import static org.apache.sis.pending.geoapi.referencing.MissingMethods.getDatumEnsemble; /** @@@ -427,6 -465,18 +431,20 @@@ public abstract class PseudoDatum<D ext return ensemble.getIdentifiers(); } + /** + * Returns the usage of the datum ensemble. + * ++ * <div class="note"><b>Upcoming API change:</b> ++ * the type of collection elements may become {@code ObjectDomain} after the next GeoAPI release.</div> ++ * + * @return {@code ensemble.getDomains()}. + * + * @hidden because nothing new to said. + */ - @Override - public Collection<ObjectDomain> getDomains() { ++ public Collection<DefaultObjectDomain> getDomains() { + return ensemble.getDomains(); + } + /** * Returns the domain of validity common to all datum members, if any. * diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java index ae296a5de6,098500a7cf..2a4cf23176 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/GeodeticObjectFactory.java @@@ -25,6 -25,6 +25,7 @@@ import java.util.logging.Level import java.util.logging.Logger; import java.util.logging.LogRecord; import java.util.concurrent.atomic.AtomicReference; ++import java.time.temporal.Temporal; import java.lang.reflect.Constructor; import jakarta.xml.bind.JAXBException; import javax.measure.Unit; @@@ -721,6 -701,39 +722,37 @@@ public class GeodeticObjectFactory exte return unique("createGeodeticDatum", datum); } + /** + * Creates a dynamic geodetic reference frame from ellipsoid and frame reference epoch. + * The arguments are the same as for the {@linkplain #createGeodeticDatum(Map, Ellipsoid, + * PrimeMeridian) static datum}, with the addition of a mandatory frame reference epoch. - * The returned object implements the {@link DynamicReferenceFrame} interface. + * + * @param properties name and other properties to give to the new object. + * @param ellipsoid the ellipsoid to use in new geodetic reference frame. + * @param primeMeridian the prime meridian to use in new geodetic reference frame. + * @param epoch the epoch to which the definition of the dynamic reference frame is referenced. + * @throws FactoryException if the object creation failed. + * + * @see DefaultGeodeticDatum.Dynamic#Dynamic(Map, Ellipsoid, PrimeMeridian, Temporal) + * @see GeodeticAuthorityFactory#createGeodeticDatum(String) + * + * @since 1.5 + */ - @Override + public GeodeticDatum createGeodeticDatum(final Map<String,?> properties, + final Ellipsoid ellipsoid, + final PrimeMeridian primeMeridian, + final Temporal epoch) + throws FactoryException + { + final DefaultGeodeticDatum datum; + try { + datum = new DefaultGeodeticDatum.Dynamic(complete(properties), ellipsoid, primeMeridian, epoch); + } catch (IllegalArgumentException exception) { + throw new InvalidGeodeticParameterException(exception); + } + return unique("createGeodeticDatum", datum); + } + /** * Creates a prime meridian, relative to Greenwich. * Defines the origin from which longitude values are determined. @@@ -1068,22 -1082,30 +1100,21 @@@ } /** - * Creates a vertical <abbr>CRS</abbr> from a reference frame. - * This is a shortcut for the {@linkplain #createVerticalCRS(Map, VerticalDatum, DefaultDatumEnsemble, VerticalCS) - * more generic method} without datum ensemble. + * Creates a static vertical datum from a realization method. + * The default implementation creates a {@link DefaultVerticalDatum} instance. * * @param properties name and other properties to give to the new object. - * @param method the realization method of the vertical datum, or {@code null} if none. + * Available properties are {@linkplain ObjectFactory listed there}. + * @param datum vertical datum to use in created CRS. + * @param cs the vertical coordinate system for the created CRS. + * @return the coordinate reference system for the given properties. * @throws FactoryException if the object creation failed. - * - * @see DefaultVerticalDatum#DefaultVerticalDatum(Map, RealizationMethod) - * @see GeodeticAuthorityFactory#createVerticalDatum(String) - * - * @since 2.0 (temporary version number until this branch is released) */ @Override - public VerticalDatum createVerticalDatum(final Map<String,?> properties, - final RealizationMethod method) + public VerticalCRS createVerticalCRS(Map<String, ?> properties, VerticalDatum datum, VerticalCS cs) throws FactoryException { - final DefaultVerticalDatum datum; - try { - datum = new DefaultVerticalDatum(complete(properties), method); - } catch (IllegalArgumentException exception) { - throw new InvalidGeodeticParameterException(exception); - } - return unique("createVerticalDatum", datum); + return createVerticalCRS(properties, datum, null, cs); } /** @@@ -1110,6 -1135,37 +1141,35 @@@ return unique("createVerticalDatum", datum); } + /** - * Creates a dynamic vertical datum from a realization method and a frame reference epoch. - * The arguments are the same as for the {@linkplain #createVerticalDatum(Map, RealizationMethod) ++ * Creates a dynamic vertical datum from an enumerated type value and a frame reference epoch. ++ * The arguments are the same as for the {@linkplain #createVerticalDatum(Map, VerticalDatumType) + * static datum}, with the addition of a mandatory frame reference epoch. - * The returned object implements the {@link DynamicReferenceFrame} interface. + * + * @param properties name and other properties to give to the new object. - * @param method the realization method of the vertical datum, or {@code null} if none. ++ * @param type the type of this vertical datum (often geoidal). + * @param epoch the epoch to which the definition of the dynamic reference frame is referenced. + * @throws FactoryException if the object creation failed. + * - * @see DefaultVerticalDatum.Dynamic#Dynamic(Map, RealizationMethod, Temporal) ++ * @see DefaultVerticalDatum.Dynamic#Dynamic(Map, VerticalDatumType, Temporal) + * @see GeodeticAuthorityFactory#createVerticalDatum(String) + * - * @since 2.0 (temporary version number until this branch is released) ++ * @since 1.5 + */ - @Override + public VerticalDatum createVerticalDatum(final Map<String,?> properties, - final RealizationMethod method, ++ final VerticalDatumType type, + final Temporal epoch) + throws FactoryException + { + final DefaultVerticalDatum datum; + try { - datum = new DefaultVerticalDatum.Dynamic(complete(properties), method, epoch); ++ datum = new DefaultVerticalDatum.Dynamic(complete(properties), type, epoch); + } catch (IllegalArgumentException exception) { + throw new InvalidGeodeticParameterException(exception); + } + return unique("createVerticalDatum", datum); + } + /** * Creates a vertical coordinate system. * This coordinate system can be used with vertical and derived CRS. diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java index fd0419bee0,468f211524..6011e0d287 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/SQLStore.java @@@ -82,10 -124,14 +124,14 @@@ public abstract class SQLStore extends private final GeometryLibrary geomLibrary; /** - * The result of inspecting database schema for deriving {@link org.opengis.feature.FeatureType}s. + * The result of inspecting database schema for deriving {@code FeatureType}s. * Created when first needed. May be discarded and recreated if the store needs a refresh. + * + * @see #model() + * @see #model(Connection) + * @see #refresh() */ - private Database<?> model; + private volatile Database<?> model; /** * Fully qualified names (including catalog and schema) of the tables to include in this store. diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureIterator.java index d8d2e69b31,ba0836a33d..30b9a8dfce --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureIterator.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureIterator.java @@@ -27,14 -27,13 +27,13 @@@ import java.sql.ResultSet import java.sql.SQLException; import org.apache.sis.metadata.sql.privy.SQLBuilder; import org.apache.sis.storage.InternalDataStoreException; - import org.apache.sis.util.collection.BackingStoreException; import org.apache.sis.util.collection.WeakValueHashMap; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.feature.Feature; -import org.opengis.filter.SortOrder; -import org.opengis.filter.SortProperty; -import org.opengis.filter.SortBy; +// Specific to the main branch: +import org.apache.sis.feature.AbstractFeature; +import org.apache.sis.pending.geoapi.filter.SortOrder; +import org.apache.sis.pending.geoapi.filter.SortProperty; +import org.apache.sis.pending.geoapi.filter.SortBy; /** @@@ -214,13 -214,11 +214,11 @@@ final class FeatureIterator implements * Gives the next feature to the given consumer. */ @Override - public boolean tryAdvance(final Consumer<? super Feature> action) { + public boolean tryAdvance(final Consumer<? super AbstractFeature> action) { try { return fetch(action, false); - } catch (RuntimeException e) { - throw e; } catch (Exception e) { - throw new BackingStoreException(e); + throw FeatureStream.cannotExecute(e); } } @@@ -228,13 -226,11 +226,11 @@@ * Gives all remaining features to the given consumer. */ @Override - public void forEachRemaining(final Consumer<? super Feature> action) { + public void forEachRemaining(final Consumer<? super AbstractFeature> action) { try { fetch(action, true); - } catch (RuntimeException e) { - throw e; } catch (Exception e) { - throw new BackingStoreException(e); + throw FeatureStream.cannotExecute(e); } } diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureStream.java index 78d71280e5,f1485ce02f..1ad73ac900 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureStream.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/FeatureStream.java @@@ -34,14 -35,12 +35,12 @@@ import org.apache.sis.util.privy.String import org.apache.sis.util.stream.DeferredStream; import org.apache.sis.util.stream.PaginedStream; import org.apache.sis.filter.privy.SortByComparator; - import org.apache.sis.util.privy.Strings; import org.apache.sis.storage.DataStoreException; - import org.apache.sis.util.collection.BackingStoreException; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.feature.Feature; -import org.opengis.filter.Filter; -import org.opengis.filter.SortBy; +// Specific to the main branch: +import org.apache.sis.filter.Filter; +import org.apache.sis.feature.AbstractFeature; +import org.apache.sis.pending.geoapi.filter.SortBy; /** diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java index 50d68debce,35f9163eab..a535c3b43b --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/InfoStatements.java @@@ -49,10 -57,9 +57,9 @@@ import org.apache.sis.util.privy.Consta import org.apache.sis.io.wkt.Convention; import org.apache.sis.io.wkt.WKTFormat; import org.apache.sis.io.wkt.Warnings; - import org.apache.sis.referencing.factory.IdentifiedObjectFinder; -// 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; /** @@@ -475,23 -624,25 +624,25 @@@ public class InfoStatements implements /* * First, iterate over the identifiers declared in the CRS object. * If we cannot find an identifier that we can map to a SRID, then this loop may be - * executed more times with CRS from EPSG database that are equal, ignore axis order. + * executed more times with CRS from EPSG database that are equal, ignoring axis order. */ - for (final Identifier id : candidate.getIdentifiers()) { + for (final ReferenceIdentifier id : candidate.getIdentifiers()) { final String authority = id.getCodeSpace(); if (authority == null) continue; - final String code = id.getCode(); - if (!done.add(new SimpleImmutableEntry<>(authority, code))) { - continue; // Skip "authority:code" that we already tried. - } - final int codeValue; + final int code; try { - codeValue = Integer.parseInt(code); + code = Integer.parseInt(id.getCode()); } catch (NumberFormatException e) { - if (error == null) error = e; - else error.addSuppressed(e); + if (tryWithGivenCRS) { + if (error == null) error = e; + else error.addSuppressed(e); + } continue; // Ignore codes that are not integers. } + final SRID search = new SRID(crs, authority, code); + if (done.putIfAbsent(search, code > 0) != null) { + continue; // Skip "authority:code" that we already tried. + } /* * Found an "authority:code" pair that we did not tested before. * Get the WKT and verifies if the CRS is approximately equal. diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/package-info.java index bf8c64d3e1,ec2b149779..f650b19a16 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/package-info.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/package-info.java @@@ -17,26 -17,31 +17,31 @@@ /** - * Data store capable to read and create features from a JDBC connection to a database. - * {@link org.apache.sis.storage.sql.SQLStore} takes a one or more tables at construction time. + * Data store capable to read and write features using a JDBC connection to a database. + * {@link org.apache.sis.storage.sql.SimpleFeatureStore} takes one or more tables at construction time. - * Each enumerated table is represented by a {@link org.opengis.feature.FeatureType}. - * Each row in those table represents a {@link org.opengis.feature.Feature} instance. - * Each relation defined by a foreigner key is represented by an {@link org.opengis.feature.FeatureAssociationRole} + * Each enumerated table is represented by a {@code FeatureType}. + * Each row in those table represents a {@code Feature} instance. + * Each relation defined by a foreigner key is represented by an {@code FeatureAssociationRole} * to another feature (with transitive dependencies automatically resolved), and the other columns are represented - * by {@link org.opengis.feature.AttributeType}. + * by {@code AttributeType}. * - * <p>The storage of spatial features in SQL databases is described by the + * <p>The storage of spatial features in <abbr>SQL</abbr> databases is described by the * <a href="https://www.ogc.org/standards/sfs">OGC Simple feature access - Part 2: SQL option</a> - * international standard, also known as ISO 19125-2. Implementation of geometric types and operations must - * be provided by the database (sometimes through an extension, for example PostGIS on PostgreSQL databases). - * This Java package uses those provided types and operations.</p> + * international standard, also known as ISO 19125-2. + * The implementation of geometric objects and their operations must be provided by the database. + * This is sometimes provided by an extension that needs to be installed explicitly. + * For example, when using PostgreSQL, the PostGIS extension is recommended.</p> + * + * <p>The tables to use as {@linkplain org.apache.sis.storage.sql.ResourceDefinition resource definitions} + * must be specified at construction time. There is no automatic discovery mechanism. Note that discovery + * may be done by other modules. For example, Geopackage module uses the {@code "gpkg_contents"} table.</p> * * <h2>Performance tips</h2> * <p>A subset of features can be obtained by applying filters on the stream returned by * {@link org.apache.sis.storage.FeatureSet#features(boolean)}. * While the filter can be any {@link java.util.function.Predicate}, - * performances will be much better if they are instances of {@link org.opengis.filter.Filter} + * performances will be much better if they are instances of {@link org.apache.sis.filter.Filter} - * because Apache SIS will know how to translate some of them to SQL statements.</p> + * because Apache SIS will know how to translate some of them to <abbr>SQL</abbr> statements.</p> * * <p>In filter expressions like {@code ST_Intersects(A,B)} where the <var>A</var> and <var>B</var> parameters are * two sub-expressions evaluating to geometry values, if one of those expressions is a literal, then that literal diff --cc endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java index ed3f348bfb,80a484c799..3f8bb5a415 --- a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java +++ b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java @@@ -163,10 -174,10 +172,10 @@@ public final class SQLStoreTest extend * Creates a {@link SQLStore} instance with the specified table as a resource, then tests some queries. */ private void testTableQuery(final StorageConnector connector, final ResourceDefinition table) throws Exception { - try (SQLStore store = new SQLStore(new SQLStoreProvider(), connector, table)) { + try (SimpleFeatureStore store = new SimpleFeatureStore(new SQLStoreProvider(), connector, table)) { verifyFeatureTypes(store); final Map<String,Integer> countryCount = new HashMap<>(); - try (Stream<Feature> features = store.findResource("Cities").features(false)) { + try (Stream<AbstractFeature> features = store.findResource("Cities").features(false)) { features.forEach((f) -> verifyContent(f, countryCount)); } assertEquals(Integer.valueOf(2), countryCount.remove("CAN")); @@@ -490,8 -503,8 +499,8 @@@ * Tests a user supplied query followed by another query built from filters. */ private void verifyNestedSQLQuery(final StorageConnector connector) throws Exception { - try (SQLStore store = new SQLStore(null, connector, ResourceDefinition.query("MyParks", + try (SimpleFeatureStore store = new SimpleFeatureStore(null, connector, ResourceDefinition.query("MyParks", - "SELECT * FROM " + SCHEMA + ".\"Parks\""))) + "SELECT * FROM " + SCHEMA + ".\"Parks\" ORDER BY \"native_name\" DESC"))) { final FeatureSet parks = store.findResource("MyParks"); /* diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataFetcher.java index 5d3028e619,b9d249cb25..9cd162687b --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataFetcher.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataFetcher.java @@@ -40,11 -44,13 +44,13 @@@ import org.opengis.metadata.spatial.Cel import org.opengis.metadata.spatial.Georectified; import org.opengis.metadata.spatial.SpatialRepresentation; import org.opengis.metadata.spatial.GridSpatialRepresentation; + import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.util.collection.CodeListSet; + import org.apache.sis.temporal.TemporalDate; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.metadata.citation.Party; -import org.opengis.metadata.citation.Responsibility; +// Specific to the main branch: +import org.opengis.metadata.citation.ResponsibleParty; +import org.apache.sis.metadata.iso.DefaultMetadata; /** @@@ -58,6 -64,21 +64,21 @@@ * @param <T> type of temporal objects. */ public abstract class MetadataFetcher<T> { + /** + * Types of date to accept as a date of last update, in preference order. + */ + private static final DateType[] LAST_UPDATE_TYPES = { - DateType.LAST_UPDATE, - DateType.LAST_REVISION, ++ DateType.valueOf("LAST_UPDATE"), ++ DateType.valueOf("LAST_REVISION"), + DateType.REVISION, - DateType.IN_FORCE, - DateType.RELEASED, - DateType.DISTRIBUTION, ++ DateType.valueOf("IN_FORCE"), ++ DateType.valueOf("RELEASED"), ++ DateType.valueOf("DISTRIBUTION"), + DateType.PUBLICATION, - DateType.ADOPTED, ++ DateType.valueOf("ADOPTED"), + DateType.CREATION + }; + /** * Title of the resource, or {@code null} if none. * @@@ -397,4 -457,47 +460,47 @@@ * @return subclass-dependent object representing the given date. */ protected abstract T convertDate(final Date date); + + /** + * Returns the first date of type {@link DateType#LAST_UPDATE}. + * If there is no last update, then this method fallbacks on {@link DateType#LAST_REVISION}. + * If there is no last revision, then this method fallbacks on {@link DateType#REVISION}, <i>etc.</i> + * + * @param metadata the metadata from which to get the date of last update. + * @param zone the timezone to use if the time is local, or {@code null} if none. + * @param listeners where to report warnings, or {@code null} if none. + * @return date of last update, or {@code null} if none. + * + * @see #lastUpdate + */ + public static Instant lastUpdate(final Metadata metadata, final ZoneId zone, final StoreListeners listeners) { + Temporal lastUpdate = null; + if (metadata != null) { + int lastUpdateType = LAST_UPDATE_TYPES.length; + search: for (Identification info : metadata.getIdentificationInfo()) { + final Citation citation = info.getCitation(); + if (citation != null) { + for (CitationDate date : citation.getDates()) { + final DateType type = date.getDateType(); + for (int i = lastUpdateType; --i >= 0;) { + if (LAST_UPDATE_TYPES[i].equals(type)) { + lastUpdateType = i; - lastUpdate = date.getReferenceDate(); ++ lastUpdate = TemporalDate.toTemporal(date.getDate()); + if (i == 0) break search; + } + } + } + } + } + } + try { + return TemporalDate.toInstant(lastUpdate, zone); + } catch (DateTimeException e) { + if (listeners == null) { + throw e; + } + listeners.warning(e); + return null; + } + } } diff --cc endorsed/src/org.apache.sis.util/main/module-info.java index a0950420fe,150970c359..08bbfa92ad --- a/endorsed/src/org.apache.sis.util/main/module-info.java +++ b/endorsed/src/org.apache.sis.util/main/module-info.java @@@ -119,7 -119,10 +119,8 @@@ module org.apache.sis.util org.apache.sis.storage.netcdf, org.apache.sis.storage.geotiff, org.apache.sis.storage.earthobservation, - org.apache.sis.cql, // In the "incubator" sub-project. - org.apache.sis.portrayal.map, // In the "incubator" sub-project. org.apache.sis.portrayal, + org.apache.sis.storage.gsf, // In the "incubator" sub-project. org.apache.sis.cloud.aws, org.apache.sis.console, org.apache.sis.gui, // In the "optional" sub-project. diff --cc incubator/build.gradle.kts index 8dbc812029,a60706f6ae..d4a0e06d86 --- a/incubator/build.gradle.kts +++ b/incubator/build.gradle.kts @@@ -47,6 -47,8 +47,7 @@@ dependencies api(files("../endorsed/build/classes/java/main/org.apache.sis.referencing")) api(files("../endorsed/build/classes/java/main/org.apache.sis.feature")) api(files("../endorsed/build/classes/java/main/org.apache.sis.storage")) + api(files("../endorsed/build/classes/java/main/org.apache.sis.storage.sql")) - api(files("../endorsed/build/classes/java/main/org.apache.sis.portrayal")) // Test dependencies testImplementation(tests.junit5) @@@ -95,11 -102,45 +98,38 @@@ fun addRead(args : MutableList<String> args.add(module + '=' + dependencies) } + /* + * Adds a JVM argument for making an internal package accessible to another module. + * This is for making internal packages accessible to JUnit or to some test classes + * defined in other modules. + */ + fun addExport(args : MutableList<String>, module : String, pkg : String, consumers : String) { + args.add("--add-exports") + args.add(module + '/' + pkg + '=' + consumers) + } + + /* + * Add compiler and runtime options for patching the Apache SIS main modules with the test classes. + * The same options are required for both compiling and executing the tests. + */ + fun addExportForTests(args : MutableList<String>) { + /* + * Some test classes need access to more internal packages than requested by the main classes. + * The following lines may need to be edited when export statements are added or removed in a + * module-info.java file of main source code, or when a test class starts using or stop using + * an internal API. + */ + // ――――――――――――― Module name ――――――――――――――――――――――― Package to export ――――――――――――――― - addExport(args, "org.apache.sis.feature", "org.apache.sis.geometry.wrapper.jts", - "org.apache.sis.portrayal.map") - + addExport(args, "org.apache.sis.feature", "org.apache.sis.geometry.wrapper", + "org.apache.sis.storage.geopackage") - - addExport(args, "org.apache.sis.storage", "org.apache.sis.storage.base", - "org.apache.sis.portrayal.map") + } + /* * Discover and execute JUnit-based tests. */ tasks.test { val args = mutableListOf("-enableassertions") + addExportForTests(args) - addExport(args, "org.apache.sis.cql", "org.apache.sis.cql", "ALL-UNNAMED") setAllJvmArgs(args) testLogging { events("FAILED", "STANDARD_OUT", "STANDARD_ERROR") diff --cc incubator/src/org.apache.sis.storage.geopackage/test/org/apache/sis/storage/geopackage/GpkgStoreTest.java index 0000000000,950a00ba9c..a64f58c76d mode 000000,100644..100644 --- a/incubator/src/org.apache.sis.storage.geopackage/test/org/apache/sis/storage/geopackage/GpkgStoreTest.java +++ b/incubator/src/org.apache.sis.storage.geopackage/test/org/apache/sis/storage/geopackage/GpkgStoreTest.java @@@ -1,0 -1,338 +1,339 @@@ + /* + * 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.storage.geopackage; + + import java.net.URL; + import java.net.URI; + import java.net.URISyntaxException; + import java.io.IOException; + import java.io.File; + import java.nio.file.Files; + import java.nio.file.Path; + import java.nio.file.StandardOpenOption; + import java.sql.Connection; + import java.sql.DriverManager; + import java.sql.SQLException; + import java.sql.Statement; + import java.util.List; + import java.util.stream.Stream; -import org.opengis.feature.AttributeType; -import org.opengis.feature.Feature; -import org.opengis.feature.PropertyType; + import org.apache.sis.geometry.wrapper.Geometries; + import org.apache.sis.geometry.wrapper.GeometryWrapper; + import org.apache.sis.referencing.IdentifiedObjects; + import org.apache.sis.setup.OptionKey; + import org.apache.sis.storage.DataStore; + import org.apache.sis.storage.DataStoreException; + import org.apache.sis.storage.DataStores; + import org.apache.sis.storage.FeatureSet; + import org.apache.sis.storage.StorageConnector; + ++import org.apache.sis.feature.AbstractFeature; ++import org.apache.sis.feature.AbstractIdentifiedType; ++import org.apache.sis.feature.DefaultAttributeType; ++ + import org.junit.jupiter.api.Test; + import org.junit.jupiter.api.parallel.Execution; + import org.junit.jupiter.api.parallel.ExecutionMode; + import static org.junit.jupiter.api.Assertions.*; + import static org.junit.jupiter.api.Assumptions.assumeTrue; + + + /** + * Tests the Geopackage store. + * + * @author Johann Sorel (Geomatys) + */ + @Execution(ExecutionMode.SAME_THREAD) + public final class GpkgStoreTest { + /** + * Creates a new test case. + */ + public GpkgStoreTest() { + } + + /** + * Returns the URL to the {@code FeatureSet.gpkg} file. + * If that file does not exists, it is created from the SQL script. + * + * @return {@link URL} or {@link Path} to the test Geopackage file. + * @throws DataStoreException if the Geopackage test file cannot be created. + */ + private static Object getTestFile() throws DataStoreException { + URL file = GpkgStoreTest.class.getResource("FeatureSet.gpkg"); + if (file == null) try { + final Path source = Path.of(GpkgStoreTest.class.getResource("FeatureSet.sql").toURI()); + final Path target = source.resolveSibling("FeatureSet.gpkg").toAbsolutePath(); + try (Connection c = DriverManager.getConnection("jdbc:sqlite:" + target)) { + try (Statement s = c.createStatement()) { + s.executeUpdate("PRAGMA main.application_id = " + GpkgStoreProvider.APPLICATION_ID + ';' + + "PRAGMA main.user_version = " + GpkgStoreProvider.VERSION + ';'); + s.executeUpdate(Files.readString(source)); + } + } + return target; + } catch (URISyntaxException | IOException | SQLException e) { + throw new DataStoreException("Cannot create the Geopackage test file.", e); + } + return file; + } + + /** + * Tests reading a feature set from a Geopackage file. + * + * @throws DataStoreException if the Geopackage test file cannot be created or read. + */ + @Test + public void testFeatureSet() throws DataStoreException { + try (DataStore store = DataStores.open(getTestFile())) { + final var gpkg = assertInstanceOf(GpkgStore.class, store); + assertEquals(7, gpkg.components().size()); + /* + * Reads and verifies two features from the Geopackage test file. + */ + FeatureSet fs = getResource(gpkg, "nogeom"); + assertPropertiesEqual(fs, null, "sis:identifier", "fid", "id"); + { // For keeping `the features` scope locale. - final Feature[] features = getFeatures(fs).toArray(Feature[]::new); ++ final AbstractFeature[] features = getFeatures(fs).toArray(AbstractFeature[]::new); + assertEquals(2, features.length); + assertEquals(1, features[0].getPropertyValue("fid")); + assertEquals(1, features[0].getPropertyValue("id")); + assertEquals(2, features[1].getPropertyValue("fid")); + assertEquals(2, features[1].getPropertyValue("id")); + } + /* + * Reads and verifies a feature containing a point. + */ + fs = getResource(gpkg, "point"); + assertPropertiesEqual(fs, "Point", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geometry", + "text", + "int32", + "int64", + "float", + "date", + "datetime", + "bool"); + + assertGeometryEquals(fs, "OGC:CRS84", "POINT (3.8691634241245 43.64618798638135)"); + { // For keeping the `feature` scope local. - final Feature feature = getFeatures(fs).get(0); ++ final AbstractFeature feature = getFeatures(fs).get(0); + assertEquals("some text", feature.getPropertyValue("text")); + assertEquals(123, feature.getPropertyValue("int32")); + assertEquals(123456, feature.getPropertyValue("int64")); + assertEquals(123.456, feature.getPropertyValue("float")); + assertEquals("2024-08-07", feature.getPropertyValue("date")); + assertEquals("2024-08-07T12:34:56.000", feature.getPropertyValue("datetime")); + assertEquals(1, feature.getPropertyValue("bool")); + } + /* + * Reads and verifies a feature containing a multi-point geometry. + */ + fs = getResource(gpkg, "multipoint"); + assertPropertiesEqual(fs, "MultiPoint", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geom"); + + assertGeometryEquals(fs, "EPSG:2154", + "MULTIPOINT ((770147.9089501069 6283260.318250759), (770224.707537184 6283233.947140678))"); + /* + * Reads and verifies a feature containing a line string. + */ + fs = getResource(gpkg, "line"); + assertPropertiesEqual(fs, "LineString", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geom"); + + assertGeometryEquals(fs, "EPSG:3857", + "LINESTRING (430669.3103960207 5411208.981453437, 430655.7744657199 5410643.121416978)"); + /* + * Reads and verifies a feature containing a multi-lines geometry. + */ + fs = getResource(gpkg, "multiline"); + assertPropertiesEqual(fs, "MultiLineString", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geom"); + + assertGeometryEquals(fs, "EPSG:3395", + "MULTILINESTRING ((430750.52597782505 5381116.187674649, 430967.10086263693 5380950.761655851), " + + "(430753.08021745336 5381153.51545164, 430917.9595504581 5381182.577731505))"); + /* + * Reads and verifies a feature containing a polygon. + */ + fs = getResource(gpkg, "polygon"); + assertPropertiesEqual(fs, "Polygon", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geom"); + + assertGeometryEquals(fs, "EPSG:3857", + "POLYGON ((430696.4716464551 5410782.480781593, 430696.03196823364 5410729.011400482, " + + "430716.6968446369 5410730.2266102545, 430718.01587930095 5410780.050348468, " + + "430696.4716464551 5410782.480781593))"); + /* + * Reads and verifies a feature containing a multi-polygons. + */ + fs = getResource(gpkg, "multipolygon"); + assertPropertiesEqual(fs, "MultiPolygon", + "sis:identifier", + "sis:envelope", + "sis:geometry", + "fid", + "geom"); + + assertGeometryEquals(fs, "EPSG:3395", + "MULTIPOLYGON (((430795.5091658133 5381110.830395544, " + + "430796.60836136667 5381129.2969556395, 430832.2222972956 5381126.118282802, " + + "430831.0131821869 5381108.711284476, 430795.5091658133 5381110.830395544)), " + + "((430849.25982837286 5381155.483207256, 430849.1499088174 5381151.396330676, " + + "430855.0855648057 5381151.547696444, 430855.4153234717 5381154.877743942, " + + "430849.25982837286 5381155.483207256)))"); + } + } + + /** + * Returns the resource of the given name. + * + * @param store the store from which to get the feature set. + * @param name name of the desired feature set. + * @return the requested feature set. + * @throws DataStoreException if no feature set of the given name was found. + */ + private static FeatureSet getResource(final GpkgStore store, final String name) throws DataStoreException { + return assertInstanceOf(FeatureSet.class, store.findResource(name)); + } + + /** + * Asserts that the properties of the given feature set have the specified names. + * The geometry is expected to be always in the property at index 4 for all features + * (this is the case for the {@code FeatureSet.gpkg} test file). + * + * @param fs the feature set from which to get the properties. + * @param geometryIndex index of the geometry property. + * @param propertyNames the expected names of all properties. + */ + private static void assertPropertiesEqual(final FeatureSet fs, final String geometryType, + final String... propertyNames) throws DataStoreException + { - final PropertyType[] properties = fs.getType().getProperties(true).toArray(PropertyType[]::new); ++ final AbstractIdentifiedType[] properties = fs.getType().getProperties(true).toArray(AbstractIdentifiedType[]::new); + assertEquals(propertyNames.length, properties.length, "properties.size()"); + for (int i=0; i<propertyNames.length; i++) { + assertEquals(propertyNames[i], properties[i].getName().toString()); + } + if (geometryType != null) { - var attribute = assertInstanceOf(AttributeType.class, properties[4]); ++ var attribute = assertInstanceOf(DefaultAttributeType.class, properties[4]); + assertEquals(geometryType, attribute.getValueClass().getSimpleName()); + } + } + + /** + * Returns all feature instances as a list. + * + * @param fs the feature set from which to get the feature instances. + * @return all feature instances. + */ - private static List<Feature> getFeatures(final FeatureSet fs) throws DataStoreException { - try (Stream<Feature> stream = fs.features(false)) { ++ private static List<AbstractFeature> getFeatures(final FeatureSet fs) throws DataStoreException { ++ try (Stream<AbstractFeature> stream = fs.features(false)) { + return stream.toList(); + } + } + + /** + * Asserts that the geometry of the given feature set is equal to the expected WKT. + * This method opportunistically verifies that the value of the {@code "fid"} property is 1. + * + * @param resource the feature set from which to get a single feature instance. + * @param expectedCRS identifier of the expected CRS. + * @param expectedWKT expected geometry in WKT format. + */ + private static void assertGeometryEquals(final FeatureSet resource, final String expectedCRS, final String expectedWKT) + throws DataStoreException + { - final List<Feature> features = getFeatures(resource); ++ final List<AbstractFeature> features = getFeatures(resource); + assertEquals(1, features.size()); - final Feature feature = features.get(0); ++ final AbstractFeature feature = features.get(0); + + GeometryWrapper geometry = Geometries.wrap(feature.getPropertyValue("sis:geometry")).get(); + assertEquals(expectedCRS, IdentifiedObjects.getIdentifierOrName(geometry.getCoordinateReferenceSystem())); + assertEquals(expectedWKT, geometry.formatWKT(1)); + assertEquals(1, feature.getPropertyValue("fid")); + } + + /** + * Tests opening the file from a JDBC URL. + * + * @throws Exception if a <abbr>SQL</abbr>, I/O or other error occurred. + */ + @Test + public void testOpeningFromJDBC_URL() throws Exception { + final Object file = getTestFile(); + final Path path = (file instanceof Path) ? (Path) file : Path.of(((URL) file).toURI()); + try (DataStore store = DataStores.open(new URI("jdbc:sqlite:" + path))) { + final var gpkg = assertInstanceOf(GpkgStore.class, store); + final var fs = getResource(gpkg, "polygon"); + final var fi = fs.features(false).findAny().orElseThrow(); + assertEquals(1, fi.getPropertyValue("fid")); + } + } + + /** + * Tests creating an empty database, then opening it as a read-only file. + * + * @throws Exception if a <abbr>SQL</abbr>, I/O or other error occurred. + */ + @Test + public void testOpeningReadOnlyDatabase() throws Exception { + final File file = File.createTempFile("sis-test", ".gpkg"); + try { + // Create an empty database. + final var connector = new StorageConnector(file); + connector.setOption(OptionKey.OPEN_OPTIONS, new StandardOpenOption[] { + StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE, + StandardOpenOption.READ + }); + try (GpkgStore store = new GpkgStore(null, connector)) { + final var gpkg = assertInstanceOf(GpkgStore.class, store); + assertTrue(gpkg.components().isEmpty()); + } + assertNotEquals(0, file.length()); + + // Make file read only, skip test if we cannot do that. + assumeTrue(file.setWritable(false)); + try (DataStore store = DataStores.open(file)) { + final var gpkg = assertInstanceOf(GpkgStore.class, store); + assertTrue(gpkg.components().isEmpty()); // Should not raise any exception. + } + } finally { + assertTrue(file.setWritable(true)); + assertTrue(file.delete()); + } + } + } diff --cc incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java index 0000000000,ab1b01b566..1f4398b0e3 mode 000000,100644..100644 --- a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java +++ b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java @@@ -1,0 -1,60 +1,64 @@@ + /* + * 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.storage.gimi.internal; + + import java.util.stream.LongStream; + import java.util.stream.Stream; + import org.apache.sis.coverage.grid.GridExtent; + + /** + * + * @author Johann Sorel (Geomatys) + */ + public class TileMatrices { + + private TileMatrices(){} + + + /** + * Create a stream of point in the GridExtent. + * + * TODO : make a more efficient implementation. + */ + public static Stream<long[]> pointStream(GridExtent extent) { + final int dimension = extent.getDimension(); - final long[] low = extent.getLow().getCoordinateValues(); - final long[] high = extent.getHigh().getCoordinateValues(); ++ final long[] low = new long[dimension]; ++ final long[] high = new long[dimension]; ++ for (int i=0; i<dimension; i++) { ++ low[i] = extent.getLow(i); ++ high[i] = extent.getHigh(i); ++ } + + Stream<long[]> stream = LongStream.range(low[0], high[0]+1) + .mapToObj((long value) -> { + final long[] array = new long[dimension]; + array[0] = value; + return array; + }); + for (int i = 1; i <dimension; i++) { + final int idx = i; + stream = stream.flatMap((long[] t) -> LongStream.range(low[idx], high[idx]+1) + .mapToObj((long value) -> { + final long[] array = t.clone(); + array[idx] = value; + return array; + })); + } + return stream; + } + + } diff --cc netbeans-project/ivy.xml index 9532a5ffd2,02d580d8dc..5f97b17f2c --- a/netbeans-project/ivy.xml +++ b/netbeans-project/ivy.xml @@@ -11,10 -11,8 +11,10 @@@ <ivy-module version="2.0"> <info organisation="org.apache" module="sis"/> <dependencies defaultconf="default"> + <dependency org="org.opengis" name="geoapi" rev="3.0.2"/> + <dependency org="org.opengis" name="geoapi-conformance" rev="3.0.2"/> <dependency org="javax.measure" name="unit-api" rev="2.1.3"/> - <dependency org="org.glassfish.jaxb" name="jaxb-runtime" rev="4.0.4"/> + <dependency org="org.glassfish.jaxb" name="jaxb-runtime" rev="4.0.5"/> <dependency org="org.eclipse" name="yasson" rev="3.0.3"/> <dependency org="com.esri.geometry" name="esri-geometry-api" rev="2.2.4"/> <dependency org="org.locationtech.jts" name="jts-core" rev="1.19.0"/> diff --cc netbeans-project/nbproject/project.properties index da94918e12,b157430750..9f48612b3e --- a/netbeans-project/nbproject/project.properties +++ b/netbeans-project/nbproject/project.properties @@@ -110,9 -117,12 +117,11 @@@ test.options = --add-modules jama,Geogr --add-exports org.apache.sis.metadata/org.apache.sis.xml.privy=org.apache.sis.storage.geotiff \ --add-exports org.apache.sis.metadata/org.apache.sis.xml.bind.gcx=org.apache.sis.referencing \ --add-exports org.apache.sis.metadata/org.apache.sis.metadata.privy=org.apache.sis.referencing.gazetteer \ + --add-exports org.apache.sis.metadata/org.apache.sis.metadata.sql.privy=org.apache.sis.referencing.epsg \ --add-exports org.apache.sis.referencing/org.apache.sis.referencing.internal=org.apache.sis.openoffice \ --add-exports org.apache.sis.feature/org.apache.sis.feature.privy=org.apache.sis.storage.sql \ - --add-exports org.apache.sis.feature/org.apache.sis.geometry.wrapper.jts=org.apache.sis.storage.sql,org.apache.sis.portrayal.map \ + --add-exports org.apache.sis.feature/org.apache.sis.geometry.wrapper.jts=org.apache.sis.storage.sql \ + --add-exports org.apache.sis.feature/org.apache.sis.geometry.wrapper=org.apache.sis.storage.geopackage \ - --add-exports org.apache.sis.storage/org.apache.sis.storage.base=org.apache.sis.portrayal.map \ --add-exports org.apache.sis.storage/org.apache.sis.storage.test=${modules.list} # diff --cc optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java index 0000000000,80e2813ab1..8858f0d601 mode 000000,100644..100644 --- a/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java +++ b/optional/src/org.apache.sis.referencing.database/test/org/apache/sis/resources/embedded/EmbeddedResourcesTest.java @@@ -1,0 -1,140 +1,141 @@@ + /* + * 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.resources.embedded; + + import java.io.IOException; + import java.sql.Connection; + import java.sql.ResultSet; + import java.sql.Statement; + import java.util.ServiceLoader; + import javax.sql.DataSource; + import org.opengis.util.FactoryException; -import org.opengis.referencing.crs.CoordinateReferenceSystem; + import org.apache.sis.setup.InstallationResources; + import org.apache.sis.metadata.sql.privy.Initializer; + import org.apache.sis.system.DataDirectory; + import org.apache.sis.referencing.CRS; + import org.apache.sis.referencing.factory.sql.epsg.ScriptProvider; + ++import org.apache.sis.referencing.crs.AbstractCRS; ++ + // Test dependencies + import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.*; + import static org.junit.jupiter.api.Assumptions.assumeTrue; + import org.apache.sis.test.TestUtilities; + + + /** + * Tests {@link EmbeddedResources}. + * + * @author Martin Desruisseaux (Geomatys) + */ + public final strictfp class EmbeddedResourcesTest { + /** + * Whether the database has been created. + */ + private static boolean databaseCreated; + + /** + * Creates a new test case. + */ + public EmbeddedResourcesTest() { + } + + /** + * Skips the test if the EPSG scripts are not present. + * This method uses {@code LICENSE.txt} as a sentinel file. + */ + private static void assumeDataPresent() { + assumeTrue(ScriptProvider.class.getResource("LICENSE.txt") != null, + "EPSG resources not found. See `README.md` for manual installation."); + } + + /** + * Returns the {@link EmbeddedResources} instance declared in the {@code META-INF/services/} directory. + * The provider may coexist with providers defined in other modules, so we need to filter them. + */ + private static synchronized InstallationResources getInstance() { + if (!databaseCreated) try { + new Generator().run(); + databaseCreated = true; + } catch (Exception e) { + throw new AssertionError(e); + } + + InstallationResources provider = null; + for (InstallationResources candidate : ServiceLoader.load(InstallationResources.class)) { + if (candidate instanceof EmbeddedResources) { + assertNull(provider, "Expected only one instance."); + provider = candidate; + } + } + assertNotNull(provider, "Expected an instance."); + return provider; + } + + /** + * Tests fetching the licenses. + * + * @throws IOException if an error occurred while reading a license. + */ + @Test + public void testLicences() throws IOException { + assumeDataPresent(); + final InstallationResources provider = getInstance(); + assertTrue(provider.getLicense("Embedded", null, "text/plain").contains("IOGP")); + assertTrue(provider.getLicense("Embedded", null, "text/html" ).contains("IOGP")); + } + + /** + * Tests connecting to the database. + * + * @throws Exception if an error occurred while fetching the data source, or connecting to the database. + */ + @Test + public void testConnection() throws Exception { + assumeDataPresent(); + final String dir = DataDirectory.getenv(); + assertTrue((dir == null) || dir.isEmpty(), "The SIS_DATA environment variable must be unset for enabling this test."); + final DataSource ds = Initializer.getDataSource(); + assertNotNull(ds, "Cannot find the data source."); + try (Connection c = ds.getConnection()) { + assertEquals("jdbc:derby:classpath:SIS_DATA/Databases/spatial-metadata", c.getMetaData().getURL(), "URL"); + try (Statement s = c.createStatement()) { + try (ResultSet r = s.executeQuery("SELECT COORD_REF_SYS_NAME FROM EPSG.\"Coordinate Reference System\" WHERE COORD_REF_SYS_CODE = 4326")) { + assertTrue(r.next(), "ResultSet.next()"); + assertEquals(r.getString(1), "WGS 84"); + assertFalse(r.next(), "ResultSet.next()"); + } + } + } + } + + /** + * Tests {@link CRS#forCode(String)} with the embedded database. This test asks for a CRS for which + * no hard-coded fallback exists in {@link org.apache.sis.referencing.CommonCRS}. Consequently this + * test should fail if we do not have a connection to a complete EPSG database. + * + * @throws FactoryException if an error occurred while creating the CRS. + */ + @Test + public void testCrsforCode() throws FactoryException { + assumeDataPresent(); - CoordinateReferenceSystem crs = CRS.forCode("EPSG:6676"); ++ var crs = assertInstanceOf(AbstractCRS.class, CRS.forCode("EPSG:6676")); + String area = TestUtilities.getSingleton(crs.getDomains()).getDomainOfValidity().getDescription().toString(); + assertTrue(area.contains("Japan"), area); + } + } diff --cc settings.gradle.kts index f312fd21d4,f2921b2bec..a5e37ecf51 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@@ -57,10 -59,10 +57,10 @@@ dependencyResolutionManagement */ versionCatalogs { create("libs") { - library("geoapi", "org.opengis", "geoapi-pending") .version {strictly(geoapiVersion)} + library("geoapi", "org.opengis", "geoapi") .version {strictly(geoapiVersion)} library("units", "javax.measure", "unit-api") .version {strictly("[2.1, 3.0["); prefer("2.1.3")} - library("jaxb.api", "jakarta.xml.bind", "jakarta.xml.bind-api").version {strictly("[4.0, 5.0["); prefer("4.0.1")} - library("jaxb.impl", "org.glassfish.jaxb", "jaxb-runtime") .version {strictly("[4.0, 5.0["); prefer("4.0.4")} + library("jaxb.api", "jakarta.xml.bind", "jakarta.xml.bind-api").version {strictly("[4.0, 5.0["); prefer("4.0.2")} + library("jaxb.impl", "org.glassfish.jaxb", "jaxb-runtime") .version {strictly("[4.0, 5.0["); prefer("4.0.5")} library("yasson", "org.eclipse", "yasson") .version {strictly("[3.0, 4.0["); prefer("3.0.3")} library("jts.core", "org.locationtech.jts", "jts-core") .version {strictly("[1.15, 2.0["); prefer("1.19.0")} library("esri.geometry", "com.esri.geometry", "esri-geometry-api") .version {strictly("[2.0, 3.0["); prefer("2.2.4")}