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 0665f030cdcc6f5b2bb0625ea9fc922b27ddbf02 Merge: 54a8cfd70f e55d695764 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Nov 27 19:17:10 2023 +0100 Merge branch 'geoapi-3.1' buildSrc/build.gradle.kts | 24 +- .../org/apache/sis/buildtools/book/Assembler.java | 0 .../apache/sis/buildtools/book/BookException.java | 0 .../org/apache/sis/buildtools/book/Characters.java | 0 .../apache/sis/buildtools/book/CodeColorizer.java | 0 .../apache/sis/buildtools/book/package-info.java | 0 .../sis/buildtools/coding/ReorganizeImports.java | 78 +++---- .../buildtools/coding/VerifyVersionInJavadoc.java | 0 .../apache/sis/buildtools/coding/package-info.java | 0 .../org/apache/sis/buildtools/doclet/Doclet.java | 0 .../org/apache/sis/buildtools/doclet/Include.java | 0 .../org/apache/sis/buildtools/doclet/Rewriter.java | 0 .../org/apache/sis/buildtools/doclet/Taglet.java | 0 .../apache/sis/buildtools/doclet/package-info.java | 0 .../apache/sis/buildtools/gradle/Assembler.java | 0 .../apache/sis/buildtools/gradle/BuildHelper.java | 0 .../apache/sis/buildtools/gradle/Conventions.java | 0 .../apache/sis/buildtools/gradle/Dependency.java | 0 .../apache/sis/buildtools/gradle/JavaMaker.java | 0 .../sis/buildtools/gradle/ModularCompilation.java | 0 .../apache/sis/buildtools/gradle/ModularJAR.java | 0 .../sis/buildtools/gradle/ModularJavadoc.java | 0 .../sis/buildtools/gradle/ModularPublishing.java | 0 .../sis/buildtools/gradle/ModularSources.java | 0 .../apache/sis/buildtools/gradle/ModularTest.java | 0 .../sis/buildtools/gradle/SISBuildException.java | 0 .../org/apache/sis/buildtools/gradle/UnoPkg.java | 0 .../apache/sis/buildtools/gradle/ZipWriter.java | 0 .../apache/sis/buildtools/gradle/package-info.java | 0 .../org/apache/sis/buildtools/maven/Element.java | 0 .../org/apache/sis/buildtools/maven/Generator.java | 0 .../apache/sis/buildtools/maven/package-info.java | 0 .../sis/buildtools/maven/workaround/Argument.java | 0 .../maven/workaround/ProxyGenerator.java | 0 .../buildtools/maven/workaround/package-info.java | 0 .../resources/IndexedResourceCompiler.java | 0 .../sis/buildtools/resources/package-info.java | 0 .../main/kotlin/sis.library-conventions.gradle.kts | 68 ++++++ .../org/apache/sis/buildtools/book/GEOAPI.lst | 0 .../org/apache/sis/buildtools/book/OGC.lst | 0 .../org/apache/sis/buildtools/book/SIS.lst | 0 .../org/apache/sis/buildtools/book/XML_PREFIX.lst | 0 .../resources/IndexedResourceCompilerTest.java | 0 endorsed/build.gradle.kts | 27 +-- .../main/org/apache/sis/console/package-info.java | 2 +- .../sis/coverage/grid/DimensionAppender.java | 186 ++++++++++++++++ .../sis/coverage/grid/DimensionalityReduction.java | 15 +- .../sis/coverage/grid/GridCoverageProcessor.java | 85 +++++++ .../org/apache/sis/coverage/grid/GridExtent.java | 69 +++++- .../org/apache/sis/coverage/grid/GridGeometry.java | 63 +++++- .../sis/coverage/grid/j2d/SampleModelFactory.java | 4 +- .../org/apache/sis/feature/internal/Resources.java | 10 + .../sis/feature/internal/Resources.properties | 2 + .../sis/feature/internal/Resources_fr.properties | 2 + .../org/apache/sis/filter/AssociationValue.java | 7 +- .../org/apache/sis/filter/ComparisonFilter.java | 2 +- .../main/org/apache/sis/filter/PropertyValue.java | 18 +- .../apache/sis/filter/internal/FunctionNames.java | 2 +- .../main/org/apache/sis/filter/internal/XPath.java | 74 +++++-- .../main/org/apache/sis/filter/sqlmm/Registry.java | 4 +- .../apache/sis/geometry/wrapper/j2d/Polyline.java | 4 +- .../main/org/apache/sis/image/MaskedImage.java | 2 +- .../main/org/apache/sis/image/PixelIterator.java | 2 +- .../main/org/apache/sis/image/PlanarImage.java | 6 +- .../apache/sis/image/processing/TiledProcess.java | 6 +- .../sis/coverage/grid/DimensionAppenderTest.java | 131 +++++++++++ .../coverage/grid/DimensionalityReductionTest.java | 4 +- .../apache/sis/coverage/grid/GridExtentTest.java | 115 +++++----- .../apache/sis/coverage/grid/GridGeometryTest.java | 140 +++++++----- .../test/org/apache/sis/filter/XPathTest.java | 20 ++ .../test/org/apache/sis/image/TiledImageMock.java | 6 +- .../gazetteer/GeohashReferenceSystem.java | 6 +- .../main/org/apache/sis/referencing/CommonCRS.java | 68 ++++-- .../org/apache/sis/referencing/package-info.java | 2 +- .../org/apache/sis/referencing/util/Formulas.java | 2 +- .../org/apache/sis/referencing/CommonCRSTest.java | 1 + .../apache/sis/storage/landsat/LandsatStore.java | 2 +- .../apache/sis/storage/landsat/MetadataReader.java | 2 +- .../org/apache/sis/storage/geotiff/DataSubset.java | 2 +- .../sis/storage/geotiff/ImageFileDirectory.java | 5 +- .../sis/storage/geotiff/MultiResolutionImage.java | 4 +- .../org/apache/sis/storage/geotiff/Writer.java | 2 +- .../geotiff/inflater/CompressionChannel.java | 4 +- .../geotiff/inflater/HorizontalPredictor.java | 4 +- .../sis/storage/geotiff/inflater/Inflater.java | 5 +- .../storage/geotiff/inflater/PredictorChannel.java | 4 +- .../sis/storage/geotiff/writer/TileMatrix.java | 4 +- .../org/apache/sis/storage/geotiff/WriterTest.java | 9 +- .../org/apache/sis/storage/netcdf/base/Axis.java | 4 +- .../sis/storage/sql/postgis/RasterReader.java | 4 +- .../org.apache.sis.storage/main/module-info.java | 1 + .../main/org/apache/sis/io/stream/IOUtilities.java | 16 ++ .../main/org/apache/sis/io/stream/Region.java | 2 +- .../sis/storage/AbstractGridCoverageResource.java | 21 +- .../main/org/apache/sis/storage/FeatureQuery.java | 18 +- .../apache/sis/storage/GridCoverageResource.java | 23 +- .../org/apache/sis/storage/StorageConnector.java | 35 ++- .../aggregate/ConcatenatedGridCoverage.java | 27 ++- .../aggregate/ConcatenatedGridResource.java | 5 +- .../sis/storage/aggregate/CoverageAggregator.java | 162 +++++++++++++- .../sis/storage/aggregate/DimensionAppender.java | 246 +++++++++++++++++++++ .../apache/sis/storage/aggregate/package-info.java | 2 +- .../apache/sis/storage/base/TiledGridCoverage.java | 2 +- .../org/apache/sis/storage/csv/StoreProvider.java | 2 +- .../apache/sis/storage/esri/CharactersView.java | 6 +- .../org/apache/sis/storage/esri/RasterStore.java | 14 -- .../apache/sis/storage/esri/RawRasterReader.java | 2 +- .../apache/sis/storage/esri/RawRasterStore.java | 2 +- .../main/org/apache/sis/storage/folder/Store.java | 46 ++-- .../apache/sis/storage/folder/WritableStore.java | 5 +- .../src/org.apache.sis.util/main/module-info.java | 2 + .../main/org/apache/sis/math/DecimalFunctions.java | 15 +- .../main/org/apache/sis/math/Fraction.java | 9 +- .../main/org/apache/sis/math/MathFunctions.java | 7 +- .../org/apache/sis/measure/QuantityFormat.java | 20 +- .../apache/sis/measure/SexagesimalConverter.java | 3 +- .../main/org/apache/sis/measure/package-info.java | 2 +- .../sis/pending/jdk/{JDK17.java => JDK13.java} | 42 +--- .../main/org/apache/sis/pending/jdk/JDK16.java | 28 ++- .../main/org/apache/sis/pending/jdk/JDK18.java | 65 ++++++ .../main/org/apache/sis/pending/jdk/JDK19.java | 24 +- .../main/org/apache/sis/system/Modules.java | 5 - .../main/org/apache/sis/util/ArraysExt.java | 39 +++- .../main/org/apache/sis/util/Classes.java | 19 +- .../org/apache/sis/util/collection/Containers.java | 4 + .../org/apache/sis/util/internal/DoubleDouble.java | 3 +- .../org/apache/sis/util/internal/Numerics.java | 87 +++----- .../main/org/apache/sis/util/logging/Logging.java | 17 +- .../org/apache/sis/util/resources/Vocabulary.java | 10 + .../sis/util/resources/Vocabulary.properties | 2 + .../sis/util/resources/Vocabulary_fr.properties | 2 + .../org/apache/sis/math/MathFunctionsTest.java | 12 +- .../org/apache/sis/util/internal/NumericsTest.java | 11 +- incubator/build.gradle.kts | 24 +- .../sis/storage/shapefile/ShapefileStore.java | 66 +++++- .../apache/sis/storage/shapefile/dbf/DBFField.java | 2 +- .../sis/storage/shapefile/dbf/DBFHeader.java | 2 +- .../sis/storage/shapefile/dbf/DBFReader.java | 4 +- .../sis/storage/shapefile/shp/ShapeHeader.java | 2 +- .../sis/storage/shapefile/ShapefileStoreTest.java | 196 ++++++++-------- optional/build.gradle.kts | 25 +-- 141 files changed, 1992 insertions(+), 706 deletions(-) diff --cc buildSrc/src/main/kotlin/sis.library-conventions.gradle.kts index 0000000000,75fca2a089..230a5d5810 mode 000000,100644..100644 --- a/buildSrc/src/main/kotlin/sis.library-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/sis.library-conventions.gradle.kts @@@ -1,0 -1,68 +1,68 @@@ + /* + * 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. + */ + group = "org.apache.sis" -version = "1.x-SNAPSHOT" ++version = "1.5-SNAPSHOT" + + /* + * "org.apache.sis.buildtools" is a custom Gradle plugin for building a project with Module Source Hierarchy + * as specified in https://docs.oracle.com/en/java/javase/21/docs/specs/man/javac.html#directory-hierarchies + * The expected hierarchy is: + * + * endorsed + * ├─ build + * └─ src + * ├─ org.apache.sis.metadata + * │ ├─ main + * │ │ ├─ module-info.java + * │ │ └─ org/apache/sis/metadata/… + * │ └─ test + * │ └─ org/apache/sis/metadata/… + * ├─ org.apache.sis.referencing + * │ ├─ main + * │ │ ├─ module-info.java + * │ │ └─ org/apache/sis/referencing/… + * │ └─ test + * │ └─ org/apache/sis/referencing/… + * └─ etc. + */ + plugins { + `java-library` + `maven-publish` + signing + } + + /* + * Configuration of the repositories where to deploy artifacts. + */ + publishing { + repositories { + maven { + name = "Apache" + url = uri(if (version.toString().endsWith("SNAPSHOT")) + "https://repository.apache.org/content/repositories/snapshots" else + "https://repository.apache.org/service/local/staging/deploy/maven2") + credentials { + val asfNexusUsername = providers.gradleProperty("asfNexusUsername") + val asfNexusPassword = providers.gradleProperty("asfNexusPassword") + if (asfNexusUsername.isPresent() && asfNexusPassword.isPresent()) { + username = asfNexusUsername.get() + password = asfNexusPassword.get() + } + } + } + } + } diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java index 0000000000,d3fac44fe9..788de0ff10 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/DimensionAppender.java @@@ -1,0 -1,184 +1,186 @@@ + /* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.apache.sis.coverage.grid; + + import java.awt.image.RenderedImage; + import org.opengis.util.FactoryException; + import org.apache.sis.image.DataType; + import org.apache.sis.util.ArraysExt; -import org.apache.sis.util.ArgumentChecks; + import org.apache.sis.util.internal.Numerics; + import org.apache.sis.feature.internal.Resources; + import org.apache.sis.coverage.SubspaceNotSpecifiedException; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coverage.CannotEvaluateException; ++// Specific to the main branch: ++import org.apache.sis.coverage.CannotEvaluateException; ++import org.opengis.geometry.MismatchedDimensionException; + + + /** + * A grid coverage with extra dimensions appended. + * All extra dimensions have a grid size of one cell. + * + * @author Martin Desruisseaux (Geomatys) + */ + final class DimensionAppender extends GridCoverage { + /** + * The source grid coverage for which to append extra dimensions. + */ + private final GridCoverage source; + + /** + * The dimensions added to the source grid coverage. + * Should have a grid size of one cell in all dimensions. + */ + private final GridGeometry dimToAdd; + + /** + * Creates a new dimension appender for the given grid coverage. + * This constructor does not verify the grid geometry validity. + * It is caller's responsibility to verify that the size is 1 cell. + * + * @param source the source grid coverage for which to append extra dimensions. + * @param dimToAdd the dimensions to add to the source grid coverage. + * @throws FactoryException if the compound CRS cannot be created. + * @throws IllegalArgumentException if the concatenation results in duplicated + * {@linkplain GridExtent#getAxisType(int) grid axis types}. + */ + private DimensionAppender(final GridCoverage source, final GridGeometry dimToAdd) throws FactoryException { + super(source, new GridGeometry(source.getGridGeometry(), dimToAdd)); + this.source = source; + this.dimToAdd = dimToAdd; + } + + /** + * Creates a grid coverage augmented with the given dimensions. + * The grid extent of {@code dimToAdd} shall have a grid size of one cell in all dimensions. + * + * @param source the source grid coverage for which to append extra dimensions. + * @param dimToAdd the dimensions to add to the source grid coverage. + * @throws FactoryException if the compound CRS cannot be created. + * @throws IllegalGridGeometryException if a dimension has more than one grid cell. + * @throws IllegalArgumentException if the concatenation results in duplicated + * {@linkplain GridExtent#getAxisType(int) grid axis types}. + */ + static GridCoverage create(GridCoverage source, GridGeometry dimToAdd) throws FactoryException { + final GridExtent extent = dimToAdd.getExtent(); + int i = extent.getDimension(); + if (i == 0) { + return source; + } + do { + final long size = extent.getSize(--i); + if (size != 1) { + throw new IllegalGridGeometryException(Resources.format(Resources.Keys.NotASlice_2, + extent.getAxisIdentification(i,i), size)); + } + } while (i != 0); + if (source instanceof DimensionAppender) { + final var a = (DimensionAppender) source; + dimToAdd = new GridGeometry(a.dimToAdd, dimToAdd); + source = a.source; + } + return new DimensionAppender(source, dimToAdd); + } + + /** + * Returns a grid coverage with a subset of the grid dimensions, or {@code null} if not possible by this method. + * + * @param gridAxesToPass the grid dimensions to keep. Indices must be in strictly increasing order. + * @return a grid coverage with the specified dimensions, or {@code null}. + * @throws FactoryException if the compound CRS cannot be created. + */ + final GridCoverage selectDimensions(final int[] gridAxesToPass) throws FactoryException { + final int sourceDim = source.getGridGeometry().getDimension(); + final int dimension = gridAxesToPass.length; + if (dimension < sourceDim || gridAxesToPass[0] != 0 || gridAxesToPass[sourceDim - 1] != sourceDim - 1) { + return null; + } + if (dimension == sourceDim) { + return source; + } + final int[] selected = new int[dimension - sourceDim]; + for (int i=sourceDim; i<dimension; i++) { + selected[i - sourceDim] = gridAxesToPass[i] - sourceDim; + } + return create(source, dimToAdd.selectDimensions(selected)); + } + + /** + * Returns the data type identifying the primitive type used for storing sample values in each band. + */ + @Override + final DataType getBandType() { + return source.getBandType(); + } + + /** + * Creates the grid coverage instance for the converted or packed values. + * This method is invoked only when first needed, and the result is cached by the caller. + */ + @Override + protected GridCoverage createConvertedValues(final boolean converted) { + try { + return new DimensionAppender(source.forConvertedValues(converted), dimToAdd); + } catch (FactoryException e) { + throw new CannotEvaluateException(e.getMessage(), e); + } + } + + /** + * Creates a new function for computing or interpolating sample values at given locations. + * This implementation returns the evaluator of the source coverage on the assumption that + * it should be able to do dimensionality reduction of the coordinates given to it. + */ + @Override + public Evaluator evaluator() { + return source.evaluator(); + } + + /** + * Returns a two-dimensional slice of grid data as a rendered image. + * + * @param sliceExtent a subspace of this grid coverage where all dimensions except two have a size of 1 cell. + * May be {@code null} if this grid coverage has only two dimensions with a size greater than 1 cell. + * @return the grid slice as a rendered image. Image location is relative to {@code sliceExtent}. + */ + @Override + public RenderedImage render(GridExtent sliceExtent) { + if (sliceExtent != null) { + final int sourceDim = source.getGridGeometry().getDimension(); + final int dimension = dimToAdd.getDimension() + sourceDim; - ArgumentChecks.ensureDimensionMatches("sliceExtent", dimension, sliceExtent); ++ if (dimension != sliceExtent.getDimension()) { ++ throw new MismatchedDimensionException(); ++ } + for (int i=sourceDim; i<dimension; i++) { + final long size = sliceExtent.getSize(i); + if (size != 1) { + throw new SubspaceNotSpecifiedException(Resources.format(Resources.Keys.NoNDimensionalSlice_3, + sourceDim, sliceExtent.getAxisIdentification(i,i), Numerics.toUnsignedDouble(size))); + } + if (dimToAdd.extent != null) { + final long actual = sliceExtent.getLow(i); + final long expected = dimToAdd.extent.getLow(i - sourceDim); + if (actual != expected) { + throw new DisjointExtentException(sliceExtent.getAxisIdentification(i,i), expected, expected, actual, actual); + } + } + } + sliceExtent = sliceExtent.selectDimensions(ArraysExt.range(0, sourceDim)); + } + return source.render(sliceExtent); + } + } diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java index 57fd762ce1,d58c9da9df..a6fb8b707e --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridExtent.java @@@ -93,10 -92,10 +94,10 @@@ import org.apache.sis.coverage.PointOut * @author Martin Desruisseaux (IRD, Geomatys) * @author Alexis Manin (Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.4 + * @version 1.5 * @since 1.0 */ -public class GridExtent implements GridEnvelope, LenientComparable, Serializable { +public class GridExtent implements Serializable, LenientComparable { /** * Serial number for inter-operability with different versions. */ diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java index c3ae920f32,71fd0754e3..9e1848e87c --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java @@@ -24,17 -24,16 +24,16 @@@ import java.util.Optional import org.apache.sis.feature.Features; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.feature.builder.PropertyTypeBuilder; - import org.apache.sis.filter.internal.XPath; import org.apache.sis.math.FunctionProperty; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.feature.Feature; -import org.opengis.feature.FeatureAssociationRole; -import org.opengis.feature.FeatureType; -import org.opengis.feature.PropertyType; -import org.opengis.feature.PropertyNotFoundException; -import org.opengis.filter.Expression; -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.DefaultAssociationRole; +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/sqlmm/Registry.java index c1ed330c19,fe74f5d3f7..11d52cb6de --- 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 @@@ -20,10 -20,11 +20,10 @@@ 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.pending.jdk.JDK17; + 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; /** @@@ -61,9 -62,21 +61,9 @@@ public final class Registry implements */ @Override public Collection<String> getNames() { - return JDK17.toList(Arrays.stream(SQLMM.values()).map(SQLMM::name)); + return JDK16.toList(Arrays.stream(SQLMM.values()).map(SQLMM::name)); } - /** - * Describes the parameters of a function. - * - * @param name name of the function to describe. - * @return description of the function parameters. - * @throws IllegalArgumentException if function name is unknown.. - */ - @Override - public AvailableFunction describe(final String name) { - return SQLMM.valueOf(name).description(library); - } - /** * Create a new function of the given name with given parameters. * It is caller's responsibility to ensure that the given array is non-null, diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/image/PixelIterator.java index 217a7c69c5,7a13096dad..fae6178ac5 --- 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 @@@ -46,8 -46,11 +46,8 @@@ import org.apache.sis.measure.NumberRan import org.apache.sis.util.internal.Numerics; import org.apache.sis.feature.internal.Resources; import org.apache.sis.coverage.grid.j2d.ImageUtilities; - import static org.apache.sis.util.internal.Numerics.ceilDiv; + 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/test/org/apache/sis/coverage/grid/DimensionAppenderTest.java index 0000000000,b3e40a1eec..0476c35d4a mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/DimensionAppenderTest.java +++ b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/coverage/grid/DimensionAppenderTest.java @@@ -1,0 -1,131 +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.coverage.grid; + + import java.time.Instant; + import java.time.Duration; + import java.awt.image.BufferedImage; + 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.apache.sis.referencing.operation.matrix.Matrix3; + import org.apache.sis.referencing.operation.matrix.Matrix4; + import org.apache.sis.referencing.operation.matrix.Matrices; + import org.apache.sis.referencing.operation.transform.MathTransforms; + import org.apache.sis.util.ArraysExt; + + // Test dependencies + import org.junit.Test; + import static org.junit.jupiter.api.Assertions.*; + import org.apache.sis.test.TestCase; + import org.apache.sis.referencing.crs.HardCodedCRS; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import static org.opengis.test.Assert.assertMatrixEquals; ++// Specific to the main branch: ++import static org.apache.sis.test.GeoapiAssert.assertMatrixEquals; + + + /** + * Tests {@link DimensionAppender}. This is partially the converse of {@link DimensionalityReductionTest}. + * + * @author Martin Desruisseaux (Geomatys) + */ + public final class DimensionAppenderTest extends TestCase { + /** + * Creates a new test case. + */ + public DimensionAppenderTest() { + } + + /** + * Returns a grid coverage to use as a starting point. + * + * @param width image width in pixels. + * @param height image height in pixels. + */ + private static GridCoverage initial(final int width, final int height) { + var extent = new GridExtent(width, height); + var gridToCRS = new Matrix3( + 4, 0, 100, + 0, 3, -20, + 0, 0, 1); + + var gg = new GridGeometry(extent, PixelInCell.CELL_CORNER, MathTransforms.linear(gridToCRS), HardCodedCRS.WGS84); + return new GridCoverage2D(gg, null, new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY)); + } + + /** + * Asserts that the grid geometry of the given coverage has the expected properties. + * + * @param actual the coverage for which to verify the grid geometry. + * @param gridToCRS expected "grid to CRS" transform as a matrix. + * @param gridIndices expected lower grid coordinate values. + */ + private static void assertGridGeometryEquals(final GridCoverage actual, final Matrix gridToCRS, final long... gridIndices) { + final GridGeometry gg = actual.getGridGeometry(); + assertMatrixEquals("gridToCRS", gridToCRS, MathTransforms.getMatrix(gg.getGridToCRS(PixelInCell.CELL_CORNER)), STRICT); + assertArrayEquals(gridIndices, gg.getExtent().getLow().getCoordinateValues()); + } + + /** + * Verifies that the conversion of lower grid coordinates to CRS produces the expected values. + * + * @param actual the coverage for which to verify the coordinate conversion. + * @param expected expected coordinates in units of the CRS. + * @throws TransformException if the conversion failed. + */ + private static void verifyTransformLower(final GridCoverage actual, final double... expected) throws TransformException { + final GridGeometry gg = actual.getGridGeometry(); + final MathTransform gridToCRS = gg.getGridToCRS(PixelInCell.CELL_CORNER); + final double[] coordinates = ArraysExt.copyAsDoubles(gg.getExtent().getLow().getCoordinateValues()); + gridToCRS.transform(coordinates, 0, coordinates, 0, 1); + assertArrayEquals(expected, coordinates); + } + + /** + * Tests the {@link GridCoverageProcessor} convenience methods. + * + * @throws TransformException if the coordinate conversion failed. + */ + @Test + public void testUsingProcessor() throws TransformException { + final var coverage2D = initial(16, 8); + final var processor = new GridCoverageProcessor(); + final var coverage3D = processor.appendDimension(coverage2D, 260, 5, HardCodedCRS.GRAVITY_RELATED_HEIGHT); + assertSame(coverage2D, processor.selectGridDimensions(coverage3D, 0, 1)); + verifyTransformLower(coverage3D, 100, -20, 260); + assertGridGeometryEquals(coverage3D, new Matrix4( + 4, 0, 0, 100, + 0, 3, 0, -20, + 0, 0, 5, 0, + 0, 0, 0, 1), 0, 0, 52); + + final var coverage4D = processor.appendDimension(coverage3D, Instant.parse("2022-06-18T00:00:00Z"), Duration.parse("P21D")); + assertSame(coverage2D, processor.selectGridDimensions(coverage3D, 0, 1)); + verifyTransformLower(coverage4D, 100, -20, 260, 19748); + assertGridGeometryEquals(coverage4D, Matrices.create(5, 5, new double[] { + 4, 0, 0, 0, 100, + 0, 3, 0, 0, -20, + 0, 0, 5, 0, 0, + 0, 0, 0, 21, 8, + 0, 0, 0, 0, 1}), 0, 0, 52, 940); + + // Easy way to check that the correct dimensions were selected. + verifyTransformLower(processor.selectGridDimensions(coverage4D, 0, 1, 2), 100, -20, 260); + verifyTransformLower(processor.selectGridDimensions(coverage4D, 0, 1, 3), 100, -20, 19748); + } + } diff --cc endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java index 631c19bdb2,a69353edce..2304842712 --- a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java +++ b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/GeohashReferenceSystem.java @@@ -41,10 -41,14 +41,10 @@@ import org.apache.sis.util.Utilities import org.apache.sis.util.Workaround; import org.apache.sis.util.ComparisonMode; import org.apache.sis.util.ArgumentChecks; - import org.apache.sis.util.internal.Numerics; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.resources.Vocabulary; + import org.apache.sis.pending.jdk.JDK18; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.gazetteer.Location; -import org.opengis.referencing.gazetteer.LocationType; - /** * Geographic coordinates represented as <cite>geohashes</cite> strings. diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/DimensionAppender.java index 0000000000,d25ac1af63..21eb277837 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/DimensionAppender.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/aggregate/DimensionAppender.java @@@ -1,0 -1,244 +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.storage.aggregate; + + import java.util.Arrays; + import java.util.List; + import java.util.Optional; + import org.opengis.util.GenericName; + import org.opengis.util.FactoryException; + import org.opengis.metadata.Metadata; + import org.opengis.referencing.datum.PixelInCell; + import org.opengis.referencing.operation.TransformException; + import org.apache.sis.coverage.SampleDimension; + import org.apache.sis.coverage.grid.GridExtent; + import org.apache.sis.coverage.grid.GridGeometry; + import org.apache.sis.coverage.grid.GridCoverage; + import org.apache.sis.coverage.grid.GridCoverageProcessor; + import org.apache.sis.coverage.grid.IllegalGridGeometryException; + import org.apache.sis.storage.Query; + import org.apache.sis.storage.GridCoverageResource; + import org.apache.sis.storage.RasterLoadingStrategy; + import org.apache.sis.storage.DataStoreException; + import org.apache.sis.storage.DataStoreReferencingException; + import org.apache.sis.storage.base.StoreUtilities; + import org.apache.sis.storage.event.StoreEvent; + import org.apache.sis.storage.event.StoreListener; + import org.apache.sis.storage.internal.Resources; -import org.apache.sis.util.ArraysExt; + import org.apache.sis.util.ArgumentChecks; + import org.apache.sis.util.logging.Logging; + + + /** + * A wrapper over an existing grid coverage resource with dimensions appended. + * This wrapper delegates the work to {@link GridCoverageProcessor} after a coverage has been read. + * + * @author Martin Desruisseaux (Geomatys) + */ + final class DimensionAppender implements GridCoverageResource { + /** + * The grid coverage processor to use for creating grid coverages with extra dimensions. + */ + private final GridCoverageProcessor processor; + + /** + * The source grid coverage resource for which to append extra dimensions. + */ + private final GridCoverageResource source; + + /** + * The dimensions added to the source grid coverage. + * Should have a grid size of one cell in all dimensions. + */ + private final GridGeometry dimToAdd; + + /** + * The grid geometry with dimensions appended. + * Created when first requested. + * + * @see #getGridGeometry() + */ + private volatile GridGeometry gridGeometry; + + /** + * Creates a new dimension appender for the given grid coverage resource. + * This constructor does not verify the grid geometry validity. + * It is caller's responsibility to verify that the size is 1 cell. + * + * @param processor the grid coverage processor to use for creating grid coverages with extra dimensions. + * @param source the source grid coverage for which to append extra dimensions. + * @param dimToAdd the dimensions to add to the source grid coverage. + */ + private DimensionAppender(final GridCoverageProcessor processor, final GridCoverageResource source, final GridGeometry dimToAdd) { + this.processor = processor; + this.source = source; + this.dimToAdd = dimToAdd; + } + + /** + * Creates a grid coverage resource augmented with the given dimensions. + * The grid extent of {@code dimToAdd} shall have a grid size of one cell in all dimensions. + * + * @param processor the grid coverage processor to use for creating grid coverages with extra dimensions. + * @param source the source grid coverage for which to append extra dimensions. + * @param dimToAdd the dimensions to add to the source grid coverage. + * @throws IllegalGridGeometryException if a dimension has more than one grid cell. + */ + static GridCoverageResource create(final GridCoverageProcessor processor, GridCoverageResource source, GridGeometry dimToAdd) { + ArgumentChecks.ensureNonNull("source", source); + final GridExtent extent = dimToAdd.getExtent(); + int i = extent.getDimension(); + if (i == 0) { + return source; + } + do { + final long size = extent.getSize(--i); + if (size != 1) { + Object name = extent.getAxisType(i).orElse(null); + if (name == null) name = i; + throw new IllegalGridGeometryException(Resources.format(Resources.Keys.NoSliceSpecified_2, name, size)); + } + } while (i != 0); + if (source instanceof DimensionAppender) try { + final var a = (DimensionAppender) source; + dimToAdd = new GridGeometry(a.dimToAdd, dimToAdd); + source = a.source; + } catch (FactoryException e) { + throw new IllegalGridGeometryException(e.getMessage(), e); + } + return new DimensionAppender(processor, source, dimToAdd); + } + + /** + * Returns the identifier of the original resource. + */ + @Override + public Optional<GenericName> getIdentifier() throws DataStoreException { + return source.getIdentifier(); + } + + /** + * Returns the metadata of the original resource. + */ + @Override + public Metadata getMetadata() throws DataStoreException { + return source.getMetadata(); + } + + /** + * Returns the sample dimensions of the original resource. + * Those dimensions are not impacted by the change of domain dimensions. + */ + @Override + public List<SampleDimension> getSampleDimensions() throws DataStoreException { + return source.getSampleDimensions(); + } + + /** + * Returns the grid geometry of the original resource augmented with the dimensions to append. + */ + @Override + public GridGeometry getGridGeometry() throws DataStoreException { + GridGeometry gg = gridGeometry; + if (gg == null) try { + gg = new GridGeometry(source.getGridGeometry(), dimToAdd); + gridGeometry = gg; + } catch (FactoryException | RuntimeException e) { + throw new DataStoreReferencingException(e.getMessage(), e); + } + return gg; + } + + /** + * Returns a subset of this grid coverage resource. + * The result will have the same "dimensions to add" than this resource. + * + * @param query the query to execute. + * @return subset of this coverage resource. + */ + @Override + public GridCoverageResource subset(final Query query) throws DataStoreException { + final GridCoverageResource subset = source.subset(query); + if (subset == source) return this; + return new DimensionAppender(processor, subset, dimToAdd); + } + + /** + * Reads the data and wraps the result with the dimensions to add. + */ + @Override + public GridCoverage read(GridGeometry domain, int... ranges) throws DataStoreException { + return processor.appendDimensions(source.read(domain, ranges), dimToAdd); + } + + /** + * Returns an indication about when the "physical" loading of raster data will happen. + */ + @Override + public RasterLoadingStrategy getLoadingStrategy() throws DataStoreException { + return source.getLoadingStrategy(); + } + + /** + * Sets the preferred strategy about when to do the "physical" loading of raster data. + */ + @Override + public boolean setLoadingStrategy(RasterLoadingStrategy strategy) throws DataStoreException { + return source.setLoadingStrategy(strategy); + } + + /** + * Registers a listener to notify when the specified kind of event occurs in this resource or in children. + */ + @Override + public <T extends StoreEvent> void addListener(Class<T> eventType, StoreListener<? super T> listener) { + source.addListener(eventType, listener); + } + + /** + * Unregisters a listener previously added to this resource for the given type of events. + */ + @Override + public <T extends StoreEvent> void removeListener(Class<T> eventType, StoreListener<? super T> listener) { + source.removeListener(eventType, listener); + } + + /** + * Returns a string representation of this wrapper for debugging purposes. + */ + @Override + public String toString() { + final var sb = new StringBuilder(40); + sb.append(source).append(" + dimensions["); + final GridExtent extent = dimToAdd.getExtent(); - final double[] coordinates = ArraysExt.copyAsDoubles(extent.getLow().getCoordinateValues()); ++ final double[] coordinates = new double[extent.getDimension()]; ++ for (int i=0; i<coordinates.length; i++) { ++ coordinates[i] = extent.getLow(i); ++ } + try { + dimToAdd.getGridToCRS(PixelInCell.CELL_CORNER).transform(coordinates, 0, coordinates, 0, 1); + } catch (RuntimeException | TransformException e) { + // Should never happen because the transform should be linear. + Logging.unexpectedException(StoreUtilities.LOGGER, DimensionAppender.class, "toString", e); + Arrays.fill(coordinates, Double.NaN); + } + for (int i=0; i<coordinates.length; i++) { + if (i != 0) sb.append(", "); + extent.getAxisType(i).ifPresent((type) -> sb.append(type.name()).append('=')); + sb.append(coordinates[i]); + } + return sb.append(']').toString(); + } + } diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java index 085c3cc974,c78ffa373b..6dce073f48 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/TiledGridCoverage.java @@@ -46,10 -46,10 +46,10 @@@ import org.apache.sis.storage.tiling.Ti import org.apache.sis.storage.internal.Resources; import org.apache.sis.util.collection.WeakValueHashMap; import org.apache.sis.util.resources.Errors; - import static org.apache.sis.util.internal.Numerics.ceilDiv; + import static org.apache.sis.pending.jdk.JDK18.ceilDiv; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.coverage.CannotEvaluateException; +// Specific to the main branch: +import org.apache.sis.coverage.CannotEvaluateException; /** diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/Store.java index e493a8d8ed,f6821a7fd4..58b1996488 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/Store.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/folder/Store.java @@@ -269,8 -260,11 +260,11 @@@ class Store extends DataStore implement public synchronized Metadata getMetadata() { if (metadata == null) { final MetadataBuilder mb = new MetadataBuilder(); - mb.addResourceScope(ScopeCode.COLLECTION, Resources.formatInternational(Resources.Keys.DirectoryContent_1, getDisplayName())); + mb.addResourceScope(ScopeCode.valueOf("COLLECTION"), Resources.formatInternational(Resources.Keys.DirectoryContent_1, getDisplayName())); - mb.addLanguage(locale, encoding, MetadataBuilder.Scope.RESOURCE); + mb.addLanguage(configuration.getOption(OptionKey.LOCALE), + configuration.getOption(OptionKey.ENCODING), + MetadataBuilder.Scope.RESOURCE); + final GenericName identifier = identifier(null); String name = null; if (identifier != null) { diff --cc incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java index 5d2a1958ca,303b6d6f20..928948f815 --- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java +++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java @@@ -99,20 -103,22 +103,21 @@@ import org.apache.sis.storage.shapefile import org.apache.sis.storage.shapefile.shp.ShapeType; import org.apache.sis.storage.shapefile.shp.ShapeWriter; import org.apache.sis.storage.shapefile.shx.IndexWriter; + import org.apache.sis.util.ArraysExt; import org.apache.sis.util.collection.BackingStoreException; -// 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.FeatureType; -import org.opengis.feature.PropertyType; -import org.opengis.feature.AttributeType; -import org.opengis.filter.Expression; -import org.opengis.filter.Filter; -import org.opengis.filter.Literal; -import org.opengis.filter.LogicalOperator; -import org.opengis.filter.LogicalOperatorName; -import org.opengis.filter.SpatialOperatorName; -import org.opengis.filter.ValueReference; +// Specific to the main branch: +import org.apache.sis.feature.AbstractFeature; +import org.apache.sis.feature.DefaultFeatureType; +import org.apache.sis.feature.AbstractIdentifiedType; +import org.apache.sis.feature.DefaultAttributeType; +import org.apache.sis.filter.Expression; +import org.apache.sis.filter.Filter; +import org.apache.sis.pending.geoapi.filter.Literal; +import org.apache.sis.pending.geoapi.filter.LogicalOperator; +import org.apache.sis.pending.geoapi.filter.LogicalOperatorName; +import org.apache.sis.pending.geoapi.filter.SpatialOperatorName; +import org.apache.sis.pending.geoapi.filter.ValueReference; /** @@@ -391,9 -410,10 +409,10 @@@ public final class ShapefileStore exten final ShapeRecord shpRecord = shpreader.next(); if (shpRecord == null) return false; //move dbf to record offset, some shp record might have been skipped because of filter - dbfreader.moveToOffset(header.headerSize + (shpRecord.recordNumber-1) * header.recordSize); + long offset = (long)header.headerSize + ((long)(shpRecord.recordNumber-1)) * ((long)header.recordSize); + dbfreader.moveToOffset(offset); final DBFRecord dbfRecord = dbfreader.next(); - final Feature next = type.newInstance(); + final AbstractFeature next = type.newInstance(); next.setPropertyValue(GEOMETRY_NAME, shpRecord.geometry); for (int i = 0; i < dbfPropertiesIndex.length; i++) { next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, dbfRecord.fields[i]); @@@ -574,13 -597,17 +598,17 @@@ final ShapeHeader shpHeader = new ShapeHeader(); final DBFHeader dbfHeader = new DBFHeader(); - Charset charset = StandardCharsets.UTF_8; + final Charset charset = userDefinedCharSet == null ? StandardCharsets.UTF_8 : userDefinedCharSet; CoordinateReferenceSystem crs = CommonCRS.WGS84.normalizedGeographic(); - for (PropertyType pt : newType.getProperties(true)) { - if (pt instanceof AttributeType) { - final AttributeType at = (AttributeType) pt; + for (AbstractIdentifiedType pt : newType.getProperties(true)) { + if (pt instanceof DefaultAttributeType) { + final DefaultAttributeType at = (DefaultAttributeType) pt; final Class valueClass = at.getValueClass(); + + Integer length = AttributeConvention.getMaximalLengthCharacteristic(newType, pt); + if (length == 0) length = 255; + if (Geometry.class.isAssignableFrom(valueClass)) { if (shpHeader.shapeType != 0) { throw new DataStoreException("Shapefile format can only contain one geometry"); diff --cc incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java index 35b95f1a28,6d0152954f..f2c3c8ff0d --- a/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java +++ b/incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java @@@ -50,49 -52,50 +51,50 @@@ public class ShapefileStoreTest @Test public void testStream() throws URISyntaxException, DataStoreException { final URL url = ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp"); - final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI())); - - //check feature type - final DefaultFeatureType type = store.getType(); - assertEquals("point", type.getName().toString()); - assertEquals(9, type.getProperties(true).size()); - assertNotNull(type.getProperty("sis:identifier")); - assertNotNull(type.getProperty("sis:envelope")); - assertNotNull(type.getProperty("sis:geometry")); - final var geomProp = (DefaultAttributeType) type.getProperty("geometry"); - final var idProp = (DefaultAttributeType) type.getProperty("id"); - final var textProp = (DefaultAttributeType) type.getProperty("text"); - final var integerProp = (DefaultAttributeType) type.getProperty("integer"); - final var floatProp = (DefaultAttributeType) type.getProperty("float"); - final var dateProp = (DefaultAttributeType) type.getProperty("date"); - assertEquals(Point.class, geomProp.getValueClass()); - assertEquals(Long.class, idProp.getValueClass()); - assertEquals(String.class, textProp.getValueClass()); - assertEquals(Long.class, integerProp.getValueClass()); - assertEquals(Double.class, floatProp.getValueClass()); - assertEquals(LocalDate.class, dateProp.getValueClass()); - - try (Stream<AbstractFeature> stream = store.features(false)) { - Iterator<AbstractFeature> iterator = stream.iterator(); - assertTrue(iterator.hasNext()); - AbstractFeature feature1 = iterator.next(); - assertEquals(1L, feature1.getPropertyValue("id")); - assertEquals("text1", feature1.getPropertyValue("text")); - assertEquals(10L, feature1.getPropertyValue("integer")); - assertEquals(20.0, feature1.getPropertyValue("float")); - assertEquals(LocalDate.of(2023, 10, 27), feature1.getPropertyValue("date")); - Point pt1 = (Point) feature1.getPropertyValue("geometry"); - - assertTrue(iterator.hasNext()); - AbstractFeature feature2 = iterator.next(); - assertEquals(2L, feature2.getPropertyValue("id")); - assertEquals("text2", feature2.getPropertyValue("text")); - assertEquals(40L, feature2.getPropertyValue("integer")); - assertEquals(60.0, feature2.getPropertyValue("float")); - assertEquals(LocalDate.of(2023, 10, 28), feature2.getPropertyValue("date")); - Point pt2 = (Point) feature2.getPropertyValue("geometry"); - - assertFalse(iterator.hasNext()); + try (final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI()))) { + + //check feature type - final FeatureType type = store.getType(); ++ final DefaultFeatureType type = store.getType(); + assertEquals("point", type.getName().toString()); + assertEquals(9, type.getProperties(true).size()); + assertNotNull(type.getProperty("sis:identifier")); + assertNotNull(type.getProperty("sis:envelope")); + assertNotNull(type.getProperty("sis:geometry")); - final var geomProp = (AttributeType) type.getProperty("geometry"); - final var idProp = (AttributeType) type.getProperty("id"); - final var textProp = (AttributeType) type.getProperty("text"); - final var integerProp = (AttributeType) type.getProperty("integer"); - final var floatProp = (AttributeType) type.getProperty("float"); - final var dateProp = (AttributeType) type.getProperty("date"); ++ final var geomProp = (DefaultAttributeType) type.getProperty("geometry"); ++ final var idProp = (DefaultAttributeType) type.getProperty("id"); ++ final var textProp = (DefaultAttributeType) type.getProperty("text"); ++ final var integerProp = (DefaultAttributeType) type.getProperty("integer"); ++ final var floatProp = (DefaultAttributeType) type.getProperty("float"); ++ final var dateProp = (DefaultAttributeType) type.getProperty("date"); + assertEquals(Point.class, geomProp.getValueClass()); + assertEquals(Long.class, idProp.getValueClass()); + assertEquals(String.class, textProp.getValueClass()); + assertEquals(Long.class, integerProp.getValueClass()); + assertEquals(Double.class, floatProp.getValueClass()); + assertEquals(LocalDate.class, dateProp.getValueClass()); + - try (Stream<Feature> stream = store.features(false)) { - Iterator<Feature> iterator = stream.iterator(); ++ try (Stream<AbstractFeature> stream = store.features(false)) { ++ Iterator<AbstractFeature> iterator = stream.iterator(); + assertTrue(iterator.hasNext()); - Feature feature1 = iterator.next(); ++ AbstractFeature feature1 = iterator.next(); + assertEquals(1L, feature1.getPropertyValue("id")); + assertEquals("text1", feature1.getPropertyValue("text")); + assertEquals(10L, feature1.getPropertyValue("integer")); + assertEquals(20.0, feature1.getPropertyValue("float")); + assertEquals(LocalDate.of(2023, 10, 27), feature1.getPropertyValue("date")); + Point pt1 = (Point) feature1.getPropertyValue("geometry"); + + assertTrue(iterator.hasNext()); - Feature feature2 = iterator.next(); ++ AbstractFeature feature2 = iterator.next(); + assertEquals(2L, feature2.getPropertyValue("id")); + assertEquals("text2", feature2.getPropertyValue("text")); + assertEquals(40L, feature2.getPropertyValue("integer")); + assertEquals(60.0, feature2.getPropertyValue("float")); + assertEquals(LocalDate.of(2023, 10, 28), feature2.getPropertyValue("date")); + Point pt2 = (Point) feature2.getPropertyValue("geometry"); + + assertFalse(iterator.hasNext()); + } } } @@@ -102,32 -105,33 +104,33 @@@ @Test public void testEnvelopeFilter() throws URISyntaxException, DataStoreException { final URL url = ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp"); - final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI())); - - final DefaultFilterFactory<AbstractFeature, Object, Object> ff = DefaultFilterFactory.forFeatures(); - - final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic()); - env.setRange(0, 2, 3); - env.setRange(1, 42, 43); - - final FeatureQuery query = new FeatureQuery(); - query.setSelection(ff.bbox(ff.property("geometry"), env)); - FeatureSet featureset = store.subset(query); - //ensure we obtained an optimized version - assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", featureset.getClass().getName()); - - try (Stream<AbstractFeature> stream = featureset.features(false)) { - Iterator<AbstractFeature> iterator = stream.iterator(); - assertTrue(iterator.hasNext()); - AbstractFeature feature = iterator.next(); - assertEquals(2L, feature.getPropertyValue("id")); - assertEquals("text2", feature.getPropertyValue("text")); - assertEquals(40L, feature.getPropertyValue("integer")); - assertEquals(60.0, feature.getPropertyValue("float")); - assertEquals(LocalDate.of(2023, 10, 28), feature.getPropertyValue("date")); - Point pt2 = (Point) feature.getPropertyValue("geometry"); - - assertFalse(iterator.hasNext()); + try (final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI()))) { + - final FilterFactory<Feature, Object, Object> ff = DefaultFilterFactory.forFeatures(); ++ final DefaultFilterFactory<AbstractFeature, Object, Object> ff = DefaultFilterFactory.forFeatures(); + + final GeneralEnvelope env = new GeneralEnvelope(CommonCRS.WGS84.normalizedGeographic()); + env.setRange(0, 2, 3); + env.setRange(1, 42, 43); + + final FeatureQuery query = new FeatureQuery(); + query.setSelection(ff.bbox(ff.property("geometry"), env)); + FeatureSet featureset = store.subset(query); + //ensure we obtained an optimized version + assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", featureset.getClass().getName()); + - try (Stream<Feature> stream = featureset.features(false)) { - Iterator<Feature> iterator = stream.iterator(); ++ try (Stream<AbstractFeature> stream = featureset.features(false)) { ++ Iterator<AbstractFeature> iterator = stream.iterator(); + assertTrue(iterator.hasNext()); - Feature feature = iterator.next(); ++ AbstractFeature feature = iterator.next(); + assertEquals(2L, feature.getPropertyValue("id")); + assertEquals("text2", feature.getPropertyValue("text")); + assertEquals(40L, feature.getPropertyValue("integer")); + assertEquals(60.0, feature.getPropertyValue("float")); + assertEquals(LocalDate.of(2023, 10, 28), feature.getPropertyValue("date")); + Point pt2 = (Point) feature.getPropertyValue("geometry"); + + assertFalse(iterator.hasNext()); + } } } @@@ -137,28 -141,41 +140,41 @@@ @Test public void testFieldFilter() throws URISyntaxException, DataStoreException { final URL url = ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp"); - final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI())); - - - final FeatureQuery query = new FeatureQuery(); - query.setProjection("text", "float"); - FeatureSet featureset = store.subset(query); - //ensure we obtained an optimized version - assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", featureset.getClass().getName()); - - try (Stream<AbstractFeature> stream = featureset.features(false)) { - Iterator<AbstractFeature> iterator = stream.iterator(); - assertTrue(iterator.hasNext()); - AbstractFeature feature1 = iterator.next(); - assertEquals("text1", feature1.getPropertyValue("text")); - assertEquals(20.0, feature1.getPropertyValue("float")); - - assertTrue(iterator.hasNext()); - AbstractFeature feature2 = iterator.next(); - assertEquals("text2", feature2.getPropertyValue("text")); - assertEquals(60.0, feature2.getPropertyValue("float")); + try (final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI()))) { + final FeatureQuery query = new FeatureQuery(); + query.setProjection("text", "float"); + FeatureSet featureset = store.subset(query); + //ensure we obtained an optimized version + assertEquals("org.apache.sis.storage.shapefile.ShapefileStore$AsFeatureSet", featureset.getClass().getName()); + - try (Stream<Feature> stream = featureset.features(false)) { - Iterator<Feature> iterator = stream.iterator(); ++ try (Stream<AbstractFeature> stream = featureset.features(false)) { ++ Iterator<AbstractFeature> iterator = stream.iterator(); + assertTrue(iterator.hasNext()); - Feature feature1 = iterator.next(); ++ AbstractFeature feature1 = iterator.next(); + assertEquals("text1", feature1.getPropertyValue("text")); + assertEquals(20.0, feature1.getPropertyValue("float")); + + assertTrue(iterator.hasNext()); - Feature feature2 = iterator.next(); ++ AbstractFeature feature2 = iterator.next(); + assertEquals("text2", feature2.getPropertyValue("text")); + assertEquals(60.0, feature2.getPropertyValue("float")); + + assertFalse(iterator.hasNext()); + } + } + } - assertFalse(iterator.hasNext()); + @Test + public void testFiles() throws URISyntaxException, DataStoreException { + final URL url = ShapefileStoreTest.class.getResource("/org/apache/sis/storage/shapefile/point.shp"); + try (final ShapefileStore store = new ShapefileStore(Paths.get(url.toURI()))) { + Path[] componentFiles = store.getComponentFiles(); + assertEquals(5, componentFiles.length); + componentFiles[0].toString().endsWith("point.shp"); + componentFiles[1].toString().endsWith("point.shx"); + componentFiles[2].toString().endsWith("point.dbf"); + componentFiles[3].toString().endsWith("point.prj"); + componentFiles[4].toString().endsWith("point.cpg"); } }