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 b9cb14901d4f3b6ba40a7ff92174347d12ae6951 Merge: fafbe8a7b6 fb00d38fa4 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Nov 23 16:08:22 2023 +0100 Merge branch 'geoapi-3.1'. The main works are in Shapefile, GeoTIFF and metadata "nil values". .../org/apache/sis/buildtools/book/Assembler.java | 14 +- .../apache/sis/buildtools/book/CodeColorizer.java | 2 +- .../apache/sis/buildtools/book/package-info.java | 2 +- .../org.apache.sis.console/main/module-info.java | 3 +- .../main/org/apache/sis/console/Command.java | 4 +- .../main/org/apache/sis/console/CommandRunner.java | 26 + .../org/apache/sis/console/Commands.properties | 1 + .../org/apache/sis/console/Commands_fr.properties | 17 +- .../main/org/apache/sis/console/HelpCommand.java | 3 +- .../main/org/apache/sis/console/Option.java | 8 +- .../main/org/apache/sis/console/Options.properties | 3 +- .../org/apache/sis/console/Options_fr.properties | 23 +- .../org/apache/sis/console/TransformCommand.java | 6 +- .../org/apache/sis/console/TranslateCommand.java | 105 ++++ .../main/org/apache/sis/console/package-info.java | 11 +- .../org.apache.sis.feature/main/module-info.java | 5 +- .../org/apache/sis/coverage/CoverageCombiner.java | 8 +- .../org/apache/sis/coverage/RegionOfInterest.java | 2 +- .../sis/coverage/grid/GridCoverageProcessor.java | 21 +- .../org/apache/sis/coverage/grid/GridGeometry.java | 2 +- .../org/apache/sis/filter/AssociationValue.java | 12 +- .../apache/sis/filter/DefaultFilterFactory.java | 14 +- .../main/org/apache/sis/filter/PropertyValue.java | 36 +- .../apache/sis/filter/internal/FunctionNames.java | 12 + .../main/org/apache/sis/filter/internal/XPath.java | 193 +++++-- .../main/org/apache/sis/filter/package-info.java | 2 +- .../test/org/apache/sis/filter/XPathTest.java | 40 +- .../org.apache.sis.metadata/main/module-info.java | 1 + .../org/apache/sis/metadata/AbstractMetadata.java | 122 ++++- .../main/org/apache/sis/metadata/IndexMap.java | 33 +- .../org/apache/sis/metadata/InformationMap.java | 31 +- .../org/apache/sis/metadata/MetadataColumn.java | 67 +++ .../org/apache/sis/metadata/MetadataStandard.java | 78 ++- .../apache/sis/metadata/ModifiableMetadata.java | 21 +- .../main/org/apache/sis/metadata/NameMap.java | 34 +- .../main/org/apache/sis/metadata/NilReasonMap.java | 209 ++++++++ .../org/apache/sis/metadata/PropertyAccessor.java | 43 +- .../main/org/apache/sis/metadata/PropertyMap.java | 265 +++++++--- .../main/org/apache/sis/metadata/TreeNode.java | 228 ++++++-- .../org/apache/sis/metadata/TreeNodeChildren.java | 64 ++- .../org/apache/sis/metadata/TreeTableView.java | 47 +- .../main/org/apache/sis/metadata/TypeMap.java | 33 +- .../apache/sis/metadata/ValueExistencePolicy.java | 12 + .../main/org/apache/sis/metadata/ValueMap.java | 228 +------- .../apache/sis/metadata/internal/Resources.java | 2 +- .../sis/metadata/internal/Resources.properties | 2 +- .../sis/metadata/internal/Resources_fr.properties | 2 +- .../iso/DefaultExtendedElementInformation.java | 22 +- .../apache/sis/metadata/iso/DefaultMetadata.java | 22 +- .../org/apache/sis/metadata/iso/ISOMetadata.java | 27 +- .../sis/metadata/iso/citation/DefaultCitation.java | 22 +- .../sis/metadata/iso/citation/DefaultContact.java | 22 +- .../iso/citation/DefaultResponsibility.java | 22 +- .../iso/constraint/DefaultConstraints.java | 22 +- .../iso/content/DefaultCoverageDescription.java | 22 +- .../DefaultFeatureCatalogueDescription.java | 22 +- .../iso/content/DefaultImageDescription.java | 22 +- .../iso/content/DefaultRangeDimension.java | 22 +- .../DefaultDigitalTransferOptions.java | 22 +- .../metadata/iso/distribution/DefaultMedium.java | 22 +- .../iso/identification/AbstractIdentification.java | 22 +- .../DefaultAggregateInformation.java | 22 +- .../iso/identification/DefaultBrowseGraphic.java | 22 +- .../iso/identification/DefaultCoupledResource.java | 22 +- .../identification/DefaultDataIdentification.java | 22 +- .../DefaultRepresentativeFraction.java | 22 +- .../DefaultServiceIdentification.java | 22 +- .../metadata/iso/identification/DefaultUsage.java | 22 +- .../sis/metadata/iso/lineage/DefaultLineage.java | 22 +- .../metadata/iso/lineage/DefaultProcessStep.java | 22 +- .../sis/metadata/iso/lineage/DefaultSource.java | 22 +- .../maintenance/DefaultMaintenanceInformation.java | 22 +- .../sis/metadata/iso/quality/AbstractElement.java | 22 +- .../iso/quality/DefaultConformanceResult.java | 48 +- .../sis/metadata/iso/quality/package-info.java | 3 +- .../sis/metadata/iso/spatial/DefaultDimension.java | 44 +- .../metadata/iso/spatial/DefaultGCPCollection.java | 44 +- .../spatial/DefaultGridSpatialRepresentation.java | 44 +- .../sis/metadata/iso/spatial/package-info.java | 2 +- .../main/org/apache/sis/metadata/package-info.java | 27 +- .../apache/sis/metadata/sql/util/package-info.java | 2 +- .../org/apache/sis/util/iso/DefaultLocalName.java | 22 +- .../org/apache/sis/util/iso/DefaultMemberName.java | 22 +- .../org/apache/sis/util/iso/DefaultRecord.java | 22 +- .../org/apache/sis/util/iso/DefaultRecordType.java | 22 +- .../org/apache/sis/util/iso/DefaultTypeName.java | 22 +- .../main/org/apache/sis/util/iso/Names.java | 2 + .../org/apache/sis/xml/NilInternationalString.java | 2 +- .../main/org/apache/sis/xml/NilReason.java | 49 +- .../main/org/apache/sis/xml/ValueConverter.java | 4 +- .../main/org/apache/sis/xml/XPointer.java | 2 +- .../apache/sis/xml/bind/FinalClassExtensions.java | 2 +- .../org/apache/sis/xml/bind/gco/GO_Boolean.java | 13 + .../org/apache/sis/xml/bind/gco/GO_Integer.java | 13 + .../org/apache/sis/xml/bind/gco/PropertyType.java | 51 +- .../main/org/apache/sis/xml/package-info.java | 2 +- .../org/apache/sis/metadata/NilReasonMapTest.java | 102 ++++ .../org/apache/sis/metadata/TreeTableViewTest.java | 103 +++- .../metadata/iso/quality/AbstractElementTest.java | 2 +- .../quality/AbstractPositionalAccuracyTest.java | 5 +- .../iso/quality/DefaultConformanceResultTest.java | 118 +++++ .../iso/quality/DefaultDomainConsistencyTest.java | 2 +- .../iso/quality/DefaultEvaluationMethodTest.java | 12 +- .../iso/quality/DefaultQuantitativeResultTest.java | 2 +- .../sis/metadata/iso/quality/ScopeCodeTest.java | 4 +- .../test/org/apache/sis/xml/NilReasonTest.java | 75 +-- .../test/org/apache/sis/xml/test/TestCase.java | 12 + .../apache/sis/io/wkt/GeodeticObjectParser.java | 8 + .../sis/parameter/AbstractParameterDescriptor.java | 22 +- .../sis/parameter/DefaultParameterDescriptor.java | 23 +- .../parameter/DefaultParameterDescriptorGroup.java | 22 +- .../sis/parameter/DefaultParameterValue.java | 22 +- .../sis/parameter/DefaultParameterValueGroup.java | 22 +- .../sis/referencing/AbstractIdentifiedObject.java | 22 +- .../sis/referencing/AbstractReferenceSystem.java | 22 +- .../apache/sis/referencing/AuthorityFactories.java | 2 +- .../apache/sis/referencing/crs/AbstractCRS.java | 22 +- .../sis/referencing/crs/AbstractDerivedCRS.java | 22 +- .../sis/referencing/crs/DefaultCompoundCRS.java | 22 +- .../sis/referencing/crs/DefaultDerivedCRS.java | 22 +- .../sis/referencing/crs/DefaultEngineeringCRS.java | 22 +- .../sis/referencing/crs/DefaultGeocentricCRS.java | 22 +- .../sis/referencing/crs/DefaultGeodeticCRS.java | 22 +- .../sis/referencing/crs/DefaultGeographicCRS.java | 22 +- .../sis/referencing/crs/DefaultImageCRS.java | 22 +- .../sis/referencing/crs/DefaultParametricCRS.java | 22 +- .../sis/referencing/crs/DefaultProjectedCRS.java | 22 +- .../sis/referencing/crs/DefaultTemporalCRS.java | 22 +- .../sis/referencing/crs/DefaultVerticalCRS.java | 22 +- .../org/apache/sis/referencing/cs/AbstractCS.java | 22 +- .../apache/sis/referencing/cs/DefaultAffineCS.java | 22 +- .../sis/referencing/cs/DefaultCartesianCS.java | 22 +- .../cs/DefaultCoordinateSystemAxis.java | 22 +- .../sis/referencing/cs/DefaultCylindricalCS.java | 22 +- .../sis/referencing/cs/DefaultEllipsoidalCS.java | 22 +- .../apache/sis/referencing/cs/DefaultLinearCS.java | 22 +- .../sis/referencing/cs/DefaultParametricCS.java | 22 +- .../apache/sis/referencing/cs/DefaultPolarCS.java | 22 +- .../sis/referencing/cs/DefaultSphericalCS.java | 22 +- .../apache/sis/referencing/cs/DefaultTimeCS.java | 22 +- .../sis/referencing/cs/DefaultUserDefinedCS.java | 22 +- .../sis/referencing/cs/DefaultVerticalCS.java | 22 +- .../sis/referencing/datum/AbstractDatum.java | 22 +- .../sis/referencing/datum/DefaultEllipsoid.java | 22 +- .../referencing/datum/DefaultEngineeringDatum.java | 22 +- .../referencing/datum/DefaultGeodeticDatum.java | 22 +- .../sis/referencing/datum/DefaultImageDatum.java | 22 +- .../referencing/datum/DefaultParametricDatum.java | 22 +- .../referencing/datum/DefaultPrimeMeridian.java | 22 +- .../referencing/datum/DefaultTemporalDatum.java | 22 +- .../referencing/datum/DefaultVerticalDatum.java | 22 +- .../sis/referencing/internal/MergedProperties.java | 4 +- .../operation/AbstractCoordinateOperation.java | 22 +- .../operation/AbstractSingleOperation.java | 22 +- .../operation/DefaultConcatenatedOperation.java | 22 +- .../referencing/operation/DefaultConversion.java | 22 +- .../operation/DefaultOperationMethod.java | 22 +- .../operation/DefaultPassThroughOperation.java | 22 +- .../operation/DefaultTransformation.java | 22 +- .../sis/referencing/util/AxisDirections.java | 8 + .../referencing/operation/matrix/SolverTest.java | 2 +- .../transform/CartesianToSphericalTest.java | 2 +- .../apache/sis/storage/geotiff/GeoTiffStore.java | 36 +- .../sis/storage/geotiff/GeoTiffStoreProvider.java | 5 +- .../sis/storage/geotiff/ImageFileDirectory.java | 6 +- .../org/apache/sis/storage/geotiff/Reader.java | 8 + .../apache/sis/storage/geotiff/WritableStore.java | 83 +++ .../org/apache/sis/storage/geotiff/Writer.java | 16 + .../sis/storage/geotiff/writer/TileMatrix.java | 21 +- .../org/apache/sis/storage/gpx/Description.java | 2 +- .../apache/sis/io/stream/HyperRectangleWriter.java | 98 ++-- .../sis/io/stream/SubsampledRectangleWriter.java | 331 ++++++++++++ .../main/org/apache/sis/storage/DataStores.java | 60 ++- .../main/org/apache/sis/storage/FeatureQuery.java | 4 +- .../apache/sis/storage/RasterLoadingStrategy.java | 5 +- .../sis/storage/aggregate/GroupAggregate.java | 6 +- .../apache/sis/storage/base/MetadataBuilder.java | 39 +- .../apache/sis/storage/base/SimpleAggregate.java | 57 ++ .../org/apache/sis/storage/base/URIDataStore.java | 2 + .../sis/storage/base/WritableAggregateSupport.java | 118 +++++ .../WritableGridCoverageSupport.java} | 29 +- .../main/org/apache/sis/storage/csv/Store.java | 7 +- .../org/apache/sis/storage/esri/RasterStore.java | 7 +- .../org/apache/sis/storage/esri/WritableStore.java | 6 +- .../apache/sis/storage/image/SingleImageStore.java | 5 +- .../apache/sis/storage/image/WorldFileStore.java | 6 +- .../apache/sis/storage/image/WritableResource.java | 6 +- .../org/apache/sis/storage/internal/Resources.java | 6 + .../sis/storage/internal/Resources.properties | 1 + .../sis/storage/internal/Resources_fr.properties | 1 + .../io/stream/SubsampledRectangleWriterTest.java | 192 +++++++ .../main/org/apache/sis/util/CharSequences.java | 8 + .../apache/sis/util/collection/TableColumn.java | 29 +- .../main/org/apache/sis/util/resources/Errors.java | 2 +- .../apache/sis/util/resources/Errors.properties | 2 +- .../apache/sis/util/resources/Errors_fr.properties | 2 +- .../org/apache/sis/util/resources/Vocabulary.java | 5 + .../sis/util/resources/Vocabulary.properties | 1 + .../sis/util/resources/Vocabulary_fr.properties | 1 + .../org/apache/sis/util/CharSequencesTest.java | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 +++++++++ gradlew.bat | 92 ++++ .../org.apache.sis.storage.DataStoreProvider | 4 + .../main/module-info.java | 3 + .../storage/shapefile/ListingPropertyVisitor.java | 79 +++ .../sis/storage/shapefile/ShapefileProvider.java | 4 +- .../sis/storage/shapefile/ShapefileStore.java | 573 +++++++++++++++++++-- .../apache/sis/storage/shapefile/dbf/DBFField.java | 264 +++++++++- .../sis/storage/shapefile/dbf/DBFFieldEncoder.java | 206 -------- .../sis/storage/shapefile/dbf/DBFHeader.java | 50 +- .../sis/storage/shapefile/dbf/DBFReader.java | 38 +- .../{shp/ShapeWriter.java => dbf/DBFWriter.java} | 44 +- .../shapefile/shp/ShapeGeometryEncoder.java | 303 ++++++----- .../sis/storage/shapefile/shp/ShapeHeader.java | 18 +- .../sis/storage/shapefile/shp/ShapeReader.java | 21 +- .../sis/storage/shapefile/shp/ShapeRecord.java | 34 +- .../sis/storage/shapefile/shp/ShapeWriter.java | 51 +- .../{shp/ShapeWriter.java => shx/IndexWriter.java} | 39 +- .../sis/storage/shapefile/ShapefileStoreTest.java | 109 ++++ .../sis/storage/shapefile/dbf/DBFIOTest.java | 100 +++- .../sis/storage/shapefile/shp/ShapeIOTest.java | 72 ++- netbeans-project/ivy.xml | 8 +- netbeans-project/nbproject/project.xml | 3 + optional/src/org.apache.sis.gui/bundle/bin/sis | 4 +- optional/src/org.apache.sis.gui/bundle/bin/sis.bat | 2 +- optional/src/org.apache.sis.gui/bundle/bin/sisfx | 4 +- .../src/org.apache.sis.gui/bundle/bin/sisfx.bat | 2 +- parent/README.md | 8 + .../module-info.java => parent/build.gradle.kts | 20 +- parent/pom.xml | 2 +- settings.gradle.kts | 9 +- 233 files changed, 6082 insertions(+), 2234 deletions(-) diff --cc endorsed/src/org.apache.sis.feature/main/module-info.java index ceec296980,7018c1308f..a8270aa1d2 --- a/endorsed/src/org.apache.sis.feature/main/module-info.java +++ b/endorsed/src/org.apache.sis.feature/main/module-info.java @@@ -41,14 -41,11 +41,16 @@@ module org.apache.sis.feature exports org.apache.sis.filter; exports org.apache.sis.index.tree; + exports org.apache.sis.pending.geoapi.filter to + org.apache.sis.storage, + org.apache.sis.storage.sql, ++ org.apache.sis.storage.shapefile, // In the "incubator" sub-project. + org.apache.sis.portrayal; + exports org.apache.sis.filter.internal to org.apache.sis.storage, org.apache.sis.storage.sql, + org.apache.sis.storage.shapefile, // In the "incubator" sub-project. - org.apache.sis.cql, // In the "incubator" sub-project. org.apache.sis.portrayal; exports org.apache.sis.feature.internal to @@@ -63,7 -60,9 +65,8 @@@ org.apache.sis.storage, org.apache.sis.storage.xml, org.apache.sis.storage.sql, - org.apache.sis.storage.netcdf; + org.apache.sis.storage.netcdf, - org.apache.sis.storage.shapefile, // In the "incubator" sub-project. - org.apache.sis.cql; // In the "incubator" sub-project. ++ org.apache.sis.storage.shapefile; // In the "incubator" sub-project. exports org.apache.sis.geometry.wrapper.j2d to org.apache.sis.gui; // In the "optional" sub-project. diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/AssociationValue.java index 9ec6cc3477,3b8242bf4c..c3ae920f32 --- 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,17 +24,17 @@@ 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/DefaultFilterFactory.java index b7273dbe8f,4b8a162e81..b16688724f --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/DefaultFilterFactory.java @@@ -55,9 -51,9 +55,9 @@@ import org.apache.sis.pending.geoapi.fi * * @author Johann Sorel (Geomatys) * @author Martin Desruisseaux (Geomatys) - * @version 1.4 + * @version 1.5 * - * @param <R> the type of resources (e.g. {@link org.opengis.feature.Feature}) to use as inputs. + * @param <R> the type of resources (e.g. {@link AbstractFeature}) to use as inputs. * @param <G> base class of geometry objects. The implementation-neutral type is GeoAPI {@link Geometry}, * but this factory allows the use of other implementations such as JTS * {@link org.locationtech.jts.geom.Geometry} or ESRI {@link com.esri.core.geometry.Geometry}. diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java index 5284b66341,6b77bf77e6..99b42b947d --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/PropertyValue.java @@@ -102,10 -97,11 +102,11 @@@ abstract class PropertyValue<V> extend * @throws IllegalArgumentException if the given XPath is not supported. */ @SuppressWarnings("unchecked") - static <V> ValueReference<AbstractFeature,V> create(String xpath, final Class<V> type) { - static <V> ValueReference<Feature,V> create(final String xpath, final Class<V> type) { ++ static <V> ValueReference<AbstractFeature,V> create(final String xpath, final Class<V> type) { + final var parsed = new XPath(xpath); + List<String> path = parsed.path; boolean isVirtual = false; - List<String> path = XPath.split(xpath); - split: if (path != null) { + if (parsed.isAbsolute) { /* * If the XPath is like "/∗/property" where the root "/" is the feature instance, * we interpret that as meaning "property of a feature of any type", which means diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java index 1d0a1e9380,4cacfb13fe..d0d511fc53 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/filter/internal/FunctionNames.java @@@ -27,10 -27,22 +27,22 @@@ import org.apache.sis.filter.sqlmm.SQLM * @author Martin Desruisseaux (Geomatys) */ public final class FunctionNames extends Static { - /** Value of {@link org.opengis.filter.NullOperator#getOperatorType()}. */ ++ /** Value of {@code NullOperator.getOperatorType()}. */ + public static final String PROPERTY_IS_NULL = "PROPERTY_IS_NULL"; + - /** Value of {@link org.opengis.filter.NilOperator#getOperatorType()}. */ ++ /** Value of {@code NilOperator.getOperatorType()}. */ + public static final String PROPERTY_IS_NIL = "PROPERTY_IS_NIL"; + - /** Value of {@link org.opengis.filter.LikeOperator#getOperatorType()}. */ ++ /** Value of {@code LikeOperator.getOperatorType()}. */ + public static final String PROPERTY_IS_LIKE = "PROPERTY_IS_LIKE"; + + /** Value of {@link org.opengis.filter.BetweenComparisonOperator#getOperatorType()}. */ + public static final String PROPERTY_IS_BETWEEN = "PROPERTY_IS_BETWEEN"; + - /** Value of {@link org.opengis.filter.Literal#getFunctionName()}. */ + /** Value of {@code Literal.getFunctionName()}. */ public static final String Literal = "Literal"; - /** Value of {@link org.opengis.filter.ValueReference#getFunctionName()}. */ + /** Value of {@code ValueReference.getFunctionName()}. */ public static final String ValueReference = "ValueReference"; /** The "Add" (+) arithmetic expression. */ diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java index 37f80d5989,36725fa120..8f07e02aa2 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/quality/package-info.java @@@ -67,7 -67,7 +67,6 @@@ // @XmlJavaTypeAdapter(DQM_Measure.class), // Not directly referenced, but a "weak" association exists. @XmlJavaTypeAdapter(DQM_Parameter.class), @XmlJavaTypeAdapter(DQM_SourceReference.class), - @XmlJavaTypeAdapter(GO_Boolean.class), - @XmlJavaTypeAdapter(DQM_ValueStructure.class), @XmlJavaTypeAdapter(GO_Temporal.class), @XmlJavaTypeAdapter(GO_DateTime.class), @XmlJavaTypeAdapter(GO_GenericName.class), diff --cc endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java index 86982e69fc,b49daea01c..c98cf18e67 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java @@@ -30,10 -31,10 +31,10 @@@ * defined in the {@link org.opengis.metadata} package and sub-packages. That standard is identified in SIS by the * {@link org.apache.sis.metadata.MetadataStandard#ISO_19115} constant. Other standards are defined as well, * for example the {@link org.apache.sis.metadata.MetadataStandard#ISO_19123} constant stands for the standards - * defined by the interfaces in the {@link org.opengis.coverage} package and sub-packages. + * defined by the interfaces in the {@code org.opengis.coverage} package and sub-packages. * - * <p>For each interface, the collection of declared getter methods defines its <cite>properties</cite> - * (or <cite>attributes</cite>). If a {@link org.opengis.annotation.UML} annotation is attached to the getter method, + * <p>For each interface, the collection of declared getter methods defines its <dfn>properties</dfn> + * (or <dfn>attributes</dfn>). If a {@link org.opengis.annotation.UML} annotation is attached to the getter method, * the identifier declared in that annotation is taken as the property name. This is typically the name defined by the * International Standard from which the interface is derived. Otherwise (if there is no {@code UML} annotation) * the property name is inferred from the method name like what the <cite>Java Beans</cite> framework does.</p> diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java index c3b725e6fb,bf708118a7..c14d0206d2 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/iso/quality/ScopeCodeTest.java @@@ -23,11 -23,11 +23,11 @@@ import org.opengis.metadata.maintenance import org.junit.Test; import org.apache.sis.xml.test.TestCase; - import static org.junit.Assert.*; + import static org.junit.jupiter.api.Assertions.*; import static org.apache.sis.metadata.Assertions.assertXmlEquals; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.metadata.maintenance.Scope; +// Specific to the main branch: +import org.opengis.metadata.quality.Scope; /** diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java index 559156295f,1d86c8b4dc..d97bdf1c2c --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/xml/NilReasonTest.java @@@ -27,11 -28,10 +28,10 @@@ import org.apache.sis.util.ArraysExt import org.junit.Test; import org.apache.sis.test.TestCase; - import static org.junit.Assert.*; - import static org.opengis.test.Assert.assertInstanceOf; + import static org.junit.jupiter.api.Assertions.*; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.metadata.citation.Responsibility; +// Specific to the main branch: +import org.opengis.metadata.citation.ResponsibleParty; /** diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java index 2697121021,2efa62e0c6..0b8293bd1c --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/util/AxisDirections.java @@@ -87,9 -90,17 +87,17 @@@ public final class AxisDirections exten * For an observer at the centre of the object this will be towards its right. * Added in ISO 19111:2019 (was not in ISO 19111:2007). */ - @UML(identifier="starboard", obligation=CONDITIONAL, specification=ISO_19162) + @UML(identifier="starboard", obligation=CONDITIONAL, specification=UNSPECIFIED) public static final AxisDirection STARBOARD = AxisDirection.valueOf("STARBOARD"); + /** + * Port direction. + * For an observer at the centre of the object this will be towards its left. + * Added in ISO 19111:2019 (was not in ISO 19111:2007). + */ - @UML(identifier="port", obligation=CONDITIONAL, specification=ISO_19162) ++ @UML(identifier="port", obligation=CONDITIONAL, specification=UNSPECIFIED) + public static final AxisDirection PORT = AxisDirection.valueOf("PORT"); + /** * Direction of geographic angles (bearing). * Added in ISO 19111:2019 (was not in ISO 19111:2007). diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java index 8a0a0420ff,4d22dfe327..e69005946b --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RasterStore.java @@@ -172,14 -171,10 +171,10 @@@ abstract class RasterStore extends PRJD builder.addFormatName(formatName); listeners.warning(e); } - builder.addResourceScope(ScopeCode.COVERAGE, null); + builder.addResourceScope(ScopeCode.valueOf("COVERAGE"), null); builder.addLanguage(Locale.ENGLISH, encoding, MetadataBuilder.Scope.METADATA); builder.addSpatialRepresentation(null, gridGeometry, true); - try { - builder.addExtent(gridGeometry.getEnvelope()); - } catch (TransformException e) { - listeners.warning(e); - } + builder.addExtent(gridGeometry.getEnvelope(), listeners); /* * Do not invoke `getSampleDimensions()` because computing sample dimensions without statistics * may cause the loading of the full image. Even if `GridCoverage.getSampleDimensions()` exists diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java index b5b714edcb,6da2247bb3..19acd4ed95 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/image/WorldFileStore.java @@@ -536,10 -534,10 +534,10 @@@ loop: for (int convention=0;; convent } } builder.addFormatName(format); // Does nothing if `format` is null. - builder.addResourceScope(ScopeCode.COVERAGE, null); + builder.addResourceScope(ScopeCode.valueOf("COVERAGE"), null); builder.addSpatialRepresentation(null, getGridGeometry(MAIN_IMAGE), true); if (gridGeometry.isDefined(GridGeometry.ENVELOPE)) { - builder.addExtent(gridGeometry.getEnvelope()); + builder.addExtent(gridGeometry.getEnvelope(), listeners); } addTitleOrIdentifier(builder); builder.setISOStandards(false); diff --cc incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java index 0000000000,c3f55427fd..c165bf1df0 mode 000000,100644..100644 --- a/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java +++ b/incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ListingPropertyVisitor.java @@@ -1,0 -1,82 +1,79 @@@ + /* + * 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.shapefile; + + import java.util.Collection; -import org.opengis.util.CodeList; + import org.apache.sis.filter.internal.FunctionNames; + import org.apache.sis.filter.internal.Visitor; + -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.filter.BetweenComparisonOperator; -import org.opengis.filter.Filter; -import org.opengis.filter.Expression; -import org.opengis.filter.ValueReference; -import org.opengis.filter.ComparisonOperatorName; -import org.opengis.filter.LikeOperator; -import org.opengis.filter.LogicalOperator; ++// Specific to the main branch: ++import org.apache.sis.filter.Filter; ++import org.apache.sis.filter.Expression; ++import org.apache.sis.pending.geoapi.filter.BetweenComparisonOperator; ++import org.apache.sis.pending.geoapi.filter.ValueReference; ++import org.apache.sis.pending.geoapi.filter.ComparisonOperatorName; ++import org.apache.sis.pending.geoapi.filter.LogicalOperator; + + + /** + * Expression visitor that returns a list of all Feature attributs requiered by this expression. + * + * @author Johann Sorel (Geomatys) + */ + final class ListingPropertyVisitor extends Visitor<Object,Collection<String>> { + + public static final ListingPropertyVisitor VISITOR = new ListingPropertyVisitor(); + + protected ListingPropertyVisitor() { + setLogicalHandlers((f, names) -> { + final LogicalOperator<Object> filter = (LogicalOperator<Object>) f; + for (Filter<Object> child : filter.getOperands()) { + visit(child, names); + } + }); + setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_BETWEEN), (f, names) -> { + final BetweenComparisonOperator<Object> filter = (BetweenComparisonOperator<Object>) f; + visit(filter.getExpression(), names); + visit(filter.getLowerBoundary(), names); + visit(filter.getUpperBoundary(), names); + }); + setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_LIKE), (f, names) -> { - final LikeOperator<Object> filter = (LikeOperator<Object>) f; - visit(filter.getExpressions().get(0), names); ++ visit(f.getExpressions().get(0), names); + }); + setExpressionHandler(FunctionNames.ValueReference, (e, names) -> { + final ValueReference<Object,?> expression = (ValueReference<Object,?>) e; + final String propName = expression.getXPath(); + if (!propName.trim().isEmpty()) { + names.add(propName); + } + }); + } + + @Override - protected void typeNotFound(final CodeList<?> type, final Filter<Object> filter, final Collection<String> names) { ++ protected void typeNotFound(final Enum<?> type, final Filter<Object> filter, final Collection<String> names) { + for (final Expression<? super Object, ?> f : filter.getExpressions()) { + visit(f, names); + } + } + + @Override + protected void typeNotFound(final String type, final Expression<Object, ?> expression, final Collection<String> names) { + for (final Expression<? super Object, ?> p : expression.getParameters()) { + visit(p, names); + } + } + } diff --cc incubator/src/org.apache.sis.storage.shapefile/main/org/apache/sis/storage/shapefile/ShapefileStore.java index 04774dfc66,f17560575a..68984f7c48 --- 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 @@@ -34,8 -44,12 +44,11 @@@ import java.util.concurrent.locks.Reent import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.UnaryOperator; + import java.util.logging.Level; + import java.util.logging.Logger; import java.util.stream.Stream; import java.util.stream.StreamSupport; + -import org.opengis.util.CodeList; import org.opengis.geometry.Envelope; import org.opengis.metadata.Metadata; import org.opengis.parameter.ParameterValueGroup; @@@ -67,11 -91,30 +90,30 @@@ import org.apache.sis.storage.shapefile import org.apache.sis.storage.shapefile.shp.ShapeHeader; import org.apache.sis.storage.shapefile.shp.ShapeReader; import org.apache.sis.storage.shapefile.shp.ShapeRecord; + 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.collection.BackingStoreException; + import org.locationtech.jts.geom.Geometry; + import org.locationtech.jts.geom.LineString; + import org.locationtech.jts.geom.MultiLineString; + import org.locationtech.jts.geom.MultiPoint; + import org.locationtech.jts.geom.MultiPolygon; + import org.locationtech.jts.geom.Point; + import org.locationtech.jts.geom.Polygon; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.feature.Feature; -import org.opengis.feature.FeatureType; -import org.opengis.feature.PropertyType; -import org.opengis.feature.AttributeType; -import org.opengis.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; /** @@@ -147,10 -190,61 +189,61 @@@ public final class ShapefileStore exten return featureSetView.getEnvelope(); } + @Override - public void updateType(FeatureType featureType) throws DataStoreException { ++ public void updateType(DefaultFeatureType featureType) throws DataStoreException { + featureSetView.updateType(featureType); + } + + @Override - public void add(Iterator<? extends Feature> iterator) throws DataStoreException { ++ public void add(Iterator<? extends AbstractFeature> iterator) throws DataStoreException { + featureSetView.add(iterator); + } + + @Override - public void removeIf(Predicate<? super Feature> predicate) throws DataStoreException { ++ public void removeIf(Predicate<? super AbstractFeature> predicate) throws DataStoreException { + featureSetView.removeIf(predicate); + } + + @Override - public void replaceIf(Predicate<? super Feature> predicate, UnaryOperator<Feature> unaryOperator) throws DataStoreException { ++ public void replaceIf(Predicate<? super AbstractFeature> predicate, UnaryOperator<AbstractFeature> unaryOperator) throws DataStoreException { + featureSetView.replaceIf(predicate, unaryOperator); + } + private class AsFeatureSet extends AbstractFeatureSet implements WritableFeatureSet { - private AsFeatureSet() { + private final Rectangle2D.Double filter; + private final Set<String> dbfProperties; + private final boolean readShp; + + /** + * Extracted informations + */ + private int[] dbfPropertiesIndex; + private ShapeHeader shpHeader; + private DBFHeader dbfHeader; + /** + * Name of the field used as identifier, may be null. + */ + private String idField; + private CoordinateReferenceSystem crs; - private FeatureType type; ++ private DefaultFeatureType type; + + /** + * @param filter optional shape filter, must be in data CRS + * @param properties dbf properties to read, null for all properties + */ + private AsFeatureSet(Rectangle2D.Double filter, boolean readShp, Set<String> properties) { super(null); + this.readShp = readShp; + this.filter = filter; + this.dbfProperties = properties; + } + + /** + * @return true if this view reads all data without any filter. + */ + private boolean isDefaultView() { + return filter == null && dbfProperties == null && readShp; } @Override @@@ -227,9 -344,34 +343,34 @@@ return type; } + @Override + public Optional<Envelope> getEnvelope() throws DataStoreException { + getType();//force loading headers + if (shpHeader != null && filter == null) { + final GeneralEnvelope env = new GeneralEnvelope(crs); + env.setRange(0, shpHeader.bbox.getMinimum(0), shpHeader.bbox.getMaximum(0)); + env.setRange(1, shpHeader.bbox.getMinimum(1), shpHeader.bbox.getMaximum(1)); + return Optional.of(env); + } + return super.getEnvelope(); + } + + @Override + public OptionalLong getFeatureCount() { + try { + getType();//force loading headers + if (dbfHeader != null && filter == null) { + return OptionalLong.of(dbfHeader.nbRecord); + } + } catch (DataStoreException ex) { + //do nothing + } + return super.getFeatureCount(); + } + @Override - public Stream<Feature> features(boolean parallel) throws DataStoreException { - final FeatureType type = getType(); + public Stream<AbstractFeature> features(boolean parallel) throws DataStoreException { + final DefaultFeatureType type = getType(); final ShapeReader shpreader; final DBFReader dbfreader; try { @@@ -238,28 -380,73 +379,73 @@@ } catch (IOException ex) { throw new DataStoreException("Faild to open shp and dbf files.", ex); } - final DBFHeader header = dbfreader.getHeader(); - final Spliterator spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { - @Override - public boolean tryAdvance(Consumer action) { - try { - final ShapeRecord shpRecord = shpreader.next(); - if (shpRecord == null) return false; - final DBFRecord dbfRecord = dbfreader.next(); - final AbstractFeature next = type.newInstance(); - next.setPropertyValue(GEOMETRY_NAME, shpRecord.geometry); - for (int i = 0; i < header.fields.length; i++) { - next.setPropertyValue(header.fields[i].fieldName, dbfRecord.fields[i]); + final Spliterator spliterator; + if (readShp && dbfPropertiesIndex.length > 0) { + //read both shp and dbf + final DBFHeader header = dbfreader.getHeader(); + + spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public boolean tryAdvance(Consumer action) { + try { + 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); + 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]); + } + action.accept(next); + return true; + } catch (IOException ex) { + throw new BackingStoreException(ex.getMessage(), ex); } - action.accept(next); - return true; - } catch (IOException ex) { - throw new BackingStoreException(ex.getMessage(), ex); } - } - }; + }; + } else if (readShp) { + //read only the shp + spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public boolean tryAdvance(Consumer action) { + try { + final ShapeRecord shpRecord = shpreader.next(); + if (shpRecord == null) return false; - final Feature next = type.newInstance(); ++ final AbstractFeature next = type.newInstance(); + next.setPropertyValue(GEOMETRY_NAME, shpRecord.geometry); + action.accept(next); + return true; + } catch (IOException ex) { + throw new BackingStoreException(ex.getMessage(), ex); + } + } + }; + } else { + //read only dbf + final DBFHeader header = dbfreader.getHeader(); + spliterator = new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) { + @Override + public boolean tryAdvance(Consumer action) { + try { + final DBFRecord dbfRecord = dbfreader.next(); + if (dbfRecord == null) return false; - final Feature next = type.newInstance(); ++ final AbstractFeature next = type.newInstance(); + for (int i = 0; i < dbfPropertiesIndex.length; i++) { + next.setPropertyValue(header.fields[dbfPropertiesIndex[i]].fieldName, dbfRecord.fields[i]); + } + action.accept(next); + return true; + } catch (IOException ex) { + throw new BackingStoreException(ex.getMessage(), ex); + } + } + }; + } + - final Stream<Feature> stream = StreamSupport.stream(spliterator, false); + final Stream<AbstractFeature> stream = StreamSupport.stream(spliterator, false); return stream.onClose(new Runnable() { @Override public void run() { @@@ -274,23 -461,210 +460,212 @@@ } + @Override + public FeatureSet subset(Query query) throws UnsupportedQueryException, DataStoreException { + //try to optimise the query for common cases + opti: + if (query instanceof FeatureQuery) { + final FeatureQuery fq = (FeatureQuery) query; + FeatureQuery.NamedExpression[] projection = fq.getProjection(); - Filter<? super Feature> selection = fq.getSelection(); ++ Filter<? super AbstractFeature> selection = fq.getSelection(); + + if (selection == null && projection == null) { + //no optimisation + break opti; + } + + //force loading - final FeatureType type = getType(); ++ final DefaultFeatureType type = getType(); + + //extract bbox + Envelope bbox = null; + if (selection != null) { + //run optimizations + final Optimization optimization = new Optimization(); + optimization.setFeatureType(type); + selection = optimization.apply(selection); + final Entry<Envelope, Filter> split = extractBbox(selection); + bbox = split.getKey(); + selection = split.getValue(); + } + + //extract field names + boolean simpleSelection = true; //true if there are no alias and all expressions are ValueReference + Set<String> properties = null; + if (projection != null) { + properties = new HashSet<>(); + if (selection!=null) ListingPropertyVisitor.VISITOR.visit((Filter) selection, properties); + for (FeatureQuery.NamedExpression ne : projection) { + ListingPropertyVisitor.VISITOR.visit((Expression) ne.expression, properties); + simpleSelection &= (ne.alias == null); + simpleSelection &= (ne.expression.getFunctionName().tip().toString().equals(FunctionNames.ValueReference)); + } + + //if link fields are referenced, add target fields + if (properties.contains(AttributeConvention.IDENTIFIER)) simpleSelection &= !properties.add(idField); + if (properties.contains(AttributeConvention.GEOMETRY)) simpleSelection &= !properties.add(GEOMETRY_NAME); + if (properties.contains(AttributeConvention.ENVELOPE)) simpleSelection &= !properties.add(GEOMETRY_NAME); + } + + final boolean readShp = projection == null || properties.contains(GEOMETRY_NAME); + Rectangle2D.Double area = null; + if (bbox != null) { + try { + bbox = Envelopes.transform(bbox, crs); + } catch (TransformException ex) { + throw new DataStoreException("Failed to transform bbox filter", ex); + } + area = new Rectangle2D.Double(bbox.getMinimum(0), bbox.getMinimum(1), bbox.getSpan(0), bbox.getSpan(1)); + + //combine this area with the one we already have since this is a subset + if (filter != null) { + area = (Rectangle2D.Double) area.createIntersection(filter); + } + } + + final AsFeatureSet fs = new AsFeatureSet(area, readShp, properties); + //see if there are elements we could not handle + final FeatureQuery subQuery = new FeatureQuery(); + boolean needSubProcessing = false; + if (fq.getLimit().isPresent()){ + needSubProcessing = true; + subQuery.setLimit(fq.getLimit().getAsLong()); + } + if (fq.getLinearResolution() != null) { + needSubProcessing = true; + subQuery.setLinearResolution(fq.getLinearResolution()); + } + if (fq.getOffset() != 0) { + needSubProcessing = true; + subQuery.setOffset(fq.getOffset()); + } ++ /* Unsupported on the main branch. + if (fq.getSortBy() != null) { + needSubProcessing = true; + subQuery.setSortBy(fq.getSortBy()); + } ++ */ + if (selection != null) { + needSubProcessing = true; + subQuery.setSelection(selection); + } + if (!simpleSelection) { + needSubProcessing = true; + subQuery.setProjection(projection); + } + + return needSubProcessing ? fs.parentSubSet(subQuery) : fs; + } + + return super.subset(query); + } + + private FeatureSet parentSubSet(Query query) throws DataStoreException { + return super.subset(query); + } + @Override - public void updateType(FeatureType newType) throws DataStoreException { + public void updateType(DefaultFeatureType newType) throws DataStoreException { - throw new UnsupportedOperationException("Not supported yet."); + if (true) throw new UnsupportedOperationException("Not supported yet."); + + if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state"); + if (Files.exists(shpPath)) { + throw new DataStoreException("Update type is possible only when files do not exist. It can be used to create a new shapefile but not to update one."); + } + + final ShapeHeader shpHeader = new ShapeHeader(); + final DBFHeader dbfHeader = new DBFHeader(); + Charset charset = StandardCharsets.UTF_8; + 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(); + if (Geometry.class.isAssignableFrom(valueClass)) { + if (shpHeader.shapeType != 0) { + throw new DataStoreException("Shapefile format can only contain one geometry"); + } + if (Point.class.isAssignableFrom(valueClass)) shpHeader.shapeType = ShapeType.VALUE_POINT; + else if (MultiPoint.class.isAssignableFrom(valueClass)) shpHeader.shapeType = ShapeType.VALUE_MULTIPOINT; + else if (LineString.class.isAssignableFrom(valueClass) || MultiLineString.class.isAssignableFrom(valueClass)) shpHeader.shapeType = ShapeType.VALUE_POLYLINE; + else if (Polygon.class.isAssignableFrom(valueClass) || MultiPolygon.class.isAssignableFrom(valueClass)) shpHeader.shapeType = ShapeType.VALUE_POLYGON; + else throw new DataStoreException("Unsupported geometry type " + valueClass); + + Object cdt = at.characteristics().get(AttributeConvention.CRS_CHARACTERISTIC); + if (cdt instanceof CoordinateReferenceSystem) { + crs = (CoordinateReferenceSystem) cdt; + } + + } else if (String.class.isAssignableFrom(valueClass)) { + + } else if (Integer.class.isAssignableFrom(valueClass)) { + + } else if (Long.class.isAssignableFrom(valueClass)) { + + } else if (Float.class.isAssignableFrom(valueClass)) { + + } else if (Double.class.isAssignableFrom(valueClass)) { + + } else if (LocalDate.class.isAssignableFrom(valueClass)) { + + } else { + LOGGER.log(Level.WARNING, "Shapefile writing, field {0} is not supported", pt.getName()); + } + } else { + LOGGER.log(Level.WARNING, "Shapefile writing, field {0} is not supported", pt.getName()); + } + } + + //write shapefile + try (ShapeWriter writer = new ShapeWriter(ShpFiles.openWriteChannel(files.shpFile))) { + writer.write(shpHeader); + } catch (IOException ex){ + throw new DataStoreException("Failed to create shapefile (shp).", ex); + } + + //write shx + try (IndexWriter writer = new IndexWriter(ShpFiles.openWriteChannel(files.shxFile))) { + writer.write(shpHeader); + } catch (IOException ex){ + throw new DataStoreException("Failed to create shapefile (shx).", ex); + } + + //write dbf + try (DBFWriter writer = new DBFWriter(ShpFiles.openWriteChannel(files.dbfFile))) { + writer.write(dbfHeader); + } catch (IOException ex){ + throw new DataStoreException("Failed to create shapefile (dbf).", ex); + } + + //write cpg + try { + CpgFiles.write(charset, files.cpgFile); + } catch (IOException ex) { + throw new DataStoreException("Failed to create shapefile (cpg).", ex); + } + + //write prj + //todo + + } @Override - public void add(Iterator<? extends Feature> features) throws DataStoreException { + public void add(Iterator<? extends AbstractFeature> features) throws DataStoreException { + if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state"); throw new UnsupportedOperationException("Not supported yet."); } @Override - public void removeIf(Predicate<? super Feature> filter) throws DataStoreException { + public void removeIf(Predicate<? super AbstractFeature> filter) throws DataStoreException { + if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state"); throw new UnsupportedOperationException("Not supported yet."); } @Override - public void replaceIf(Predicate<? super Feature> filter, UnaryOperator<Feature> updater) throws DataStoreException { + public void replaceIf(Predicate<? super AbstractFeature> filter, UnaryOperator<AbstractFeature> updater) throws DataStoreException { + if (!isDefaultView()) throw new DataStoreException("Resource not writable in current filter state"); throw new UnsupportedOperationException("Not supported yet."); } } @@@ -382,4 -756,98 +757,98 @@@ } } + + /** + * Will only split bbox with direct value reference to the geometry. + * + * @param filter to split, not null + * @return entry with key is a BBox filter and value what remains of the filter. + * each filter can be null but not both. + */ + private static Entry<Envelope,Filter> extractBbox(Filter<?> filter) { + - final CodeList operatorType = filter.getOperatorType(); ++ final Enum operatorType = filter.getOperatorType(); + + if (SpatialOperatorName.BBOX.equals(operatorType)) { + Envelope env = isDirectBbox(filter); + if (env != null) { + return new AbstractMap.SimpleImmutableEntry<>(env, null); + } else { + return new AbstractMap.SimpleImmutableEntry<>(null, filter); + } + + } else if (LogicalOperatorName.AND.equals(operatorType)) { + + boolean rebuildAnd = false; + List<Filter<?>> lst = (List<Filter<?>>) ((LogicalOperator<?>)filter).getOperands(); + Envelope bbox = null; + for (int i = 0; i < lst.size(); i++) { + final Filter<?> f = lst.get(i); + final Entry<Envelope, Filter> split = extractBbox(f); + Envelope cdtBbox = split.getKey(); + Filter cdtOther = split.getValue(); + if (cdtBbox != null) { + if (bbox == null) { + bbox = cdtBbox; + } else { + throw new RuntimeException("Combine bbox"); + } + + //see if we need to rebuild the AND filter + if (cdtOther != f) { + if (!rebuildAnd) { + rebuildAnd = true; + lst = new ArrayList<>(lst); + } + //replace in list + if (cdtOther != null) { + lst.set(i, cdtOther); + } else { + lst.remove(i); + i--; + } + } + } + } + + if (rebuildAnd) { + if (lst.isEmpty()) { + filter = null; + } else if (lst.size() == 1) { + filter = lst.get(0); + } else { + filter = DefaultFilterFactory.forFeatures().and((List)lst); + } + } + + return new AbstractMap.SimpleImmutableEntry<>(bbox, filter); + } else { + //can do nothing + return new AbstractMap.SimpleImmutableEntry<>(null, filter); + } + } + + /** + * Returns envelope if the other expression is a direct value reference. + * @param bbox + * @return filter envelope + */ + private static Envelope isDirectBbox(Filter<?> bbox) { + Envelope env = null; + for (Expression exp : bbox.getExpressions()) { + if (exp instanceof ValueReference) { + final ValueReference<Object,?> expression = (ValueReference<Object,?>) exp; + final String propName = expression.getXPath(); + if ( !(GEOMETRY_NAME.equals(propName) || AttributeConvention.GEOMETRY.equals(propName))) { + return null; + } + } else if (exp instanceof Literal) { + Object value = ((Literal) exp).getValue(); + env = Geometries.wrap(value).get().getEnvelope(); + } + } + return env; + } + + } diff --cc incubator/src/org.apache.sis.storage.shapefile/test/org/apache/sis/storage/shapefile/ShapefileStoreTest.java index 9eb5e07bea,cfa9d2645e..5dbc92b826 --- 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 @@@ -87,4 -94,107 +93,107 @@@ public class ShapefileStoreTest assertFalse(iterator.hasNext()); } } + + /** + * Test optimized envelope filter. + */ + @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 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()); + } + } + + /** + * Test optimized field selection. + */ + @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<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()); + } + } + + /** + * Test creating a new shapefile. + */ + @Ignore + @Test + public void testCreate() throws URISyntaxException, DataStoreException { + //todo + } + + /** + * Test adding features to a shapefile. + */ + @Ignore + @Test + public void testAddFeatures() throws URISyntaxException, DataStoreException { + //todo + } + + /** + * Test remove features from a shapefile. + */ + @Ignore + @Test + public void testRemoveFeatures() throws URISyntaxException, DataStoreException { + //todo + } + + /** + * Test replacing features in a shapefile. + */ + @Ignore + @Test + public void testReplaceFeatures() throws URISyntaxException, DataStoreException { + //todo + } + } diff --cc netbeans-project/ivy.xml index 6dfb805b1a,536f6fffc6..fcc8fc0de2 --- 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.3"/> + <dependency org="org.glassfish.jaxb" name="jaxb-runtime" rev="4.0.4"/> <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 settings.gradle.kts index d26ccc2bee,60f13a1608..6c83faf896 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@@ -21,6 -21,8 +21,7 @@@ val geoapiVersion = "3.0.2 * The sub-projects to include in the build. * They are directory names relative to this file. */ -include("geoapi") + include("parent") include("endorsed") include("incubator") if (System.getenv("PATH_TO_FX") != null) { @@@ -56,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.3")} + library("jaxb.impl", "org.glassfish.jaxb", "jaxb-runtime") .version {strictly("[4.0, 5.0["); prefer("4.0.4")} 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")}