This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/sis.git
commit b8782f10292d86e5b153801e7e37d6d110144663 Merge: 1b04543131 b7f39770a2 Author: Martin Desruisseaux <[email protected]> AuthorDate: Sat Aug 15 13:37:45 2026 +0200 Merge branch 'geoapi-3.1'. The DGGS module has been temporarily excluded because it depends on the geometry module, which is available only in the GeoAPI 3.1/4.0 branches. .../apache/sis/buildtools/gradle/Dependency.java | 2 +- .../org/apache/sis/console/HelpCommandTest.java | 1 + .../org.apache.sis.feature/main/module-info.java | 1 + .../sis/coverage/grid/ClippedGridCoverage.java | 4 +- .../apache/sis/coverage/grid/GridCRSBuilder.java | 156 +++--- .../org/apache/sis/coverage/grid/GridCoverage.java | 17 +- .../apache/sis/coverage/grid/GridCoverage2D.java | 100 ++-- .../sis/coverage/grid/GridCoverageBuilder.java | 6 +- .../apache/sis/coverage/grid/GridDerivation.java | 2 +- .../org/apache/sis/coverage/grid/GridExtent.java | 136 ++--- .../org/apache/sis/coverage/grid/GridGeometry.java | 43 +- .../apache/sis/coverage/grid/ImageRenderer.java | 121 +---- .../apache/sis/coverage/grid/SliceGeometry.java | 3 +- .../org/apache/sis/feature/AbstractFeature.java | 7 +- .../org/apache/sis/feature/internal/Resources.java | 15 + .../sis/feature/internal/Resources.properties | 3 + .../sis/feature/internal/Resources_fr.properties | 3 + .../internal/shared/AttributeConvention.java | 6 +- .../main/org/apache/sis/image/BandSelectImage.java | 4 +- .../apache/sis/image/BandedSampleConverter.java | 6 +- .../main/org/apache/sis/image/ComputedImage.java | 12 +- .../main/org/apache/sis/image/ImageLayout.java | 38 +- .../main/org/apache/sis/image/ImageOverlay.java | 2 +- .../org/apache/sis/image/MultiSourcePrefetch.java | 5 +- .../main/org/apache/sis/image/PlanarImage.java | 128 ++--- .../main/org/apache/sis/image/PrefetchedImage.java | 4 +- .../org/apache/sis/image/SourceAlignedImage.java | 2 +- .../apache/sis/image/WritableComputedImage.java | 8 +- .../org/apache/sis/image/internal/Summarizer.java | 275 +++++++++++ .../apache/sis/image/internal}/package-info.java | 11 +- .../image/internal/shared/BatchComputedImage.java | 27 +- .../image/internal/shared/ColorModelFactory.java | 54 +- .../image/internal/shared/DeferredProperty.java | 47 +- .../sis/image/internal/shared/ImageUtilities.java | 36 +- .../sis/image/internal/shared/RasterFactory.java | 21 +- .../sis/image/internal/shared/ReshapedImage.java | 115 ++++- .../image/internal/shared/ScaledColorSpace.java | 13 +- .../sis/image/internal/shared/TileOpExecutor.java | 7 +- .../sis/image/internal/shared/TiledImage.java | 6 +- .../image/internal/shared/WritableTiledImage.java | 16 +- ...ervableImage.java => WritableUntiledImage.java} | 272 ++++++++-- .../apache/sis/coverage/grid/GridExtentTest.java | 57 ++- .../coverage/grid/ResampledGridCoverageTest.java | 6 +- .../test/org/apache/sis/image/ImageLayoutTest.java | 9 + .../test/org/apache/sis/image/TiledImageMock.java | 71 ++- .../image/internal/shared/ReshapedImageTest.java | 5 +- .../org/apache/sis/metadata/AbstractMetadata.java | 6 +- .../main/org/apache/sis/metadata/CacheKey.java | 9 +- .../org/apache/sis/metadata/MetadataCopier.java | 2 +- .../org/apache/sis/metadata/MetadataStandard.java | 393 ++++++++++----- .../org/apache/sis/metadata/MetadataVisitor.java | 15 +- .../apache/sis/metadata/ModifiableMetadata.java | 46 +- .../org/apache/sis/metadata/PropertyAccessor.java | 3 +- .../main/org/apache/sis/metadata/Pruner.java | 4 +- .../main/org/apache/sis/metadata/StateChanger.java | 2 +- .../main/org/apache/sis/metadata/TreeNode.java | 10 +- .../apache/sis/metadata/UnresolvedTypePolicy.java} | 40 +- .../apache/sis/metadata/internal/Resources.java | 5 + .../sis/metadata/internal/Resources.properties | 1 + .../sis/metadata/internal/Resources_fr.properties | 1 + .../sis/metadata/internal/shared/Merger.java | 88 +++- .../iso/extent/DefaultGeographicBoundingBox.java | 2 +- .../main/org/apache/sis/metadata/package-info.java | 2 +- .../sis/metadata/sql/internal/shared/Dialect.java | 43 +- .../metadata/sql/internal/shared/SQLBuilder.java | 11 +- .../sis/metadata/sql/internal/shared/Supports.java | 7 +- .../org/apache/sis/util/iso/DefaultRecordType.java | 2 +- .../apache/sis/xml/bind/cat/CodeListAdapter.java | 2 +- .../test/org/apache/sis/metadata/HashCodeTest.java | 4 +- .../apache/sis/metadata/InformationMapTest.java | 1 + .../apache/sis/metadata/MetadataStandardTest.java | 28 +- .../test/org/apache/sis/metadata/NameMapTest.java | 1 + .../test/org/apache/sis/metadata/PrunerTest.java | 1 + .../org/apache/sis/metadata/SpecialCasesTest.java | 1 + .../apache/sis/metadata/TreeNodeChildrenTest.java | 2 +- .../test/org/apache/sis/metadata/TypeMapTest.java | 1 + .../sis/metadata/internal/shared/MergerTest.java | 24 +- .../org/apache/sis/map/coverage/RenderingData.java | 24 +- .../apache/sis/map/coverage/RenderingDataTest.java | 256 ++++++++++ .../main/org/apache/sis/geometry/Envelopes.java | 51 +- .../org/apache/sis/parameter/MatrixParameters.java | 2 +- .../sis/referencing/datum/DatumOrEnsemble.java | 2 +- .../operation/CoordinateOperationContext.java | 6 +- .../operation/CoordinateOperationFinder.java | 32 +- .../referencing/operation/SubOperationInfo.java | 40 +- .../operation/provider/DatumShiftMethod.java | 6 +- .../transform/AbstractLinearTransform.java | 7 +- .../operation/transform/PassThroughTransform.java | 6 +- .../operation/transform/TransformJoiner.java | 11 +- .../org/apache/sis/storage/geotiff/DataSubset.java | 2 +- .../org/apache/sis/storage/netcdf/base/Axis.java | 23 +- .../apache/sis/storage/netcdf/base/Variable.java | 7 + .../sis/storage/netcdf/ucar/VariableWrapper.java | 22 +- .../sis/storage/sql/feature/SelectionClause.java | 11 +- .../storage/sql/feature/SelectionClauseWriter.java | 6 +- .../sis/storage/sql/postgis/RasterReader.java | 4 +- .../org/apache/sis/storage/sql/SQLStoreTest.java | 2 +- .../org/apache/sis/storage/gpx/MetadataTest.java | 8 +- .../org/apache/sis/storage/gpx/ReaderTest.java | 85 +++- .../org.apache.sis.storage/main/module-info.java | 6 +- .../main/org/apache/sis/storage/DataStore.java | 4 +- .../main/org/apache/sis/storage/FeatureQuery.java | 4 +- .../main/org/apache/sis/storage/Resource.java | 4 +- .../org/apache/sis/storage/esri/RasterStore.java | 13 +- .../sis/storage/event/CascadedStoreEvent.java | 2 +- .../apache/sis/storage/event/StoreListener.java | 6 +- .../apache/sis/storage/event/StoreListeners.java | 16 +- .../sis/storage/metadata/MetadataBuilder.java | 2 +- .../sis/storage/tiling/ArtificiallyTiledImage.java | 261 ++++++++++ .../apache/sis/storage/tiling/ImageTileMatrix.java | 2 +- .../apache/sis/storage/tiling/TileReadEvent.java | 63 ++- .../sis/storage/tiling/TiledDeferredImage.java | 2 +- .../sis/storage/tiling/TiledGridCoverage.java | 10 +- .../storage/tiling/TiledGridCoverageResource.java | 547 +++++++++++++++++---- .../org/apache/sis/storage/DataStoreTestCase.java | 4 +- .../sis/storage/event/StoreListenersTest.java | 6 +- .../main/org/apache/sis/math/Statistics.java | 3 + .../main/org/apache/sis/util/Classes.java | 9 +- .../sis/util/collection/WeakValueHashMap.java | 4 +- .../apache/sis/util/internal/shared/Numerics.java | 2 +- .../main/org/apache/sis/util/resources/Errors.java | 5 + .../apache/sis/util/resources/Errors.properties | 1 + .../apache/sis/util/resources/Errors_fr.properties | 1 + .../sis/util/resources/IndexedResourceBundle.java | 14 +- .../org/apache/sis/util/resources/Messages.java | 5 + .../apache/sis/util/resources/Messages.properties | 1 + .../sis/util/resources/Messages_fr.properties | 3 +- .../org/apache/sis/util/resources/Vocabulary.java | 25 +- .../sis/util/resources/Vocabulary.properties | 5 +- .../sis/util/resources/Vocabulary_fr.properties | 7 +- .../apache/sis/util/collection/RangeSetTest.java | 4 +- incubator/build.gradle.kts | 19 +- .../coveragejson/binding/CategoryEncoding.java | 82 --- .../storage/coveragejson/binding/Dictionary.java | 73 --- .../sis/storage/coveragejson/binding/I18N.java | 125 ----- .../storage/coveragejson/binding/Parameters.java | 78 --- .../sis/storage/coveragejson/binding/Ranges.java | 74 --- .../org.apache.sis.storage.DataStoreProvider | 0 .../main/module-info.java | 27 +- .../storage/coveragejson/CoverageJsonStore.java | 17 +- .../coveragejson/CoverageJsonStoreProvider.java | 0 .../sis/storage/coveragejson/CoverageResource.java | 18 +- .../sis/storage/coveragejson/binding/Axe.java | 9 +- .../sis/storage/coveragejson/binding/Axes.java | 16 +- .../sis/storage/coveragejson/binding/Category.java | 10 +- .../coveragejson/binding/CategoryEncoding.java} | 16 +- .../sis/storage/coveragejson/binding/Coverage.java | 47 +- .../coveragejson/binding/CoverageCollection.java | 10 +- .../coveragejson/binding/CoverageJsonObject.java | 37 +- .../sis/storage/coveragejson/binding/Domain.java | 7 +- .../coveragejson/binding/GeographicCRS.java | 7 +- .../sis/storage/coveragejson/binding/I18N.java | 77 +++ .../storage/coveragejson/binding/IdentifierRS.java | 10 +- .../sis/storage/coveragejson/binding/NdArray.java | 7 +- .../coveragejson/binding/ObservedProperty.java | 9 +- .../storage/coveragejson/binding/Parameter.java | 7 +- .../coveragejson/binding/ParameterGroup.java | 7 +- .../storage/coveragejson/binding/ProjectedCRS.java | 7 +- .../binding/ReferenceSystemConnection.java | 9 +- .../sis/storage/coveragejson/binding/Symbol.java | 9 +- .../coveragejson/binding/TargetConcept.java | 9 +- .../storage/coveragejson/binding/TemporalRS.java | 7 +- .../sis/storage/coveragejson/binding/TileSet.java | 9 +- .../storage/coveragejson/binding/TiledNdArray.java | 9 +- .../sis/storage/coveragejson/binding/Unit.java | 9 +- .../storage/coveragejson/binding/VerticalCRS.java | 7 +- .../storage/coveragejson/binding/package-info.java | 0 .../sis/storage/coveragejson/package-info.java | 0 .../org/apache/sis/storage/geojson/GeoJSON.java | 165 +++++++ .../apache/sis/storage/geojson/GeoJSONMapper.java | 479 ++++++++++++++++++ .../storage/geojson/binding/GeoJSONFeature.java | 318 ++++++++++++ .../geojson/binding/GeoJSONFeatureCollection.java | 322 ++++++++++++ .../storage/geojson/binding/GeoJSONGeometry.java | 134 +++++ .../geojson/binding/GeoJSONGeometryCollection.java | 84 ++++ .../storage/geojson/binding/GeoJSONLineString.java | 85 ++++ .../geojson/binding/GeoJSONMultiLineString.java | 85 ++++ .../storage/geojson/binding/GeoJSONMultiPoint.java | 85 ++++ .../geojson/binding/GeoJSONMultiPolygon.java | 85 ++++ .../sis/storage/geojson/binding/GeoJSONObject.java | 104 ++++ .../sis/storage/geojson/binding/GeoJSONPoint.java | 88 ++++ .../storage/geojson/binding/GeoJSONPolygon.java | 85 ++++ .../geojson/binding/JSONFGCircularString.java | 88 ++++ .../geojson/binding/JSONFGCompoundCurve.java | 88 ++++ .../storage/geojson/binding/JSONFGCoordRefSys.java | 131 +++++ .../geojson/binding/JSONFGCurvePolygon.java | 89 ++++ .../storage/geojson/binding/JSONFGMeasures.java | 123 +++++ .../storage/geojson/binding/JSONFGMultiCurve.java | 89 ++++ .../geojson/binding/JSONFGMultiPolyhedron.java | 85 ++++ .../storage/geojson/binding/JSONFGMultiPrism.java | 85 ++++ .../geojson/binding/JSONFGMultiSurface.java | 88 ++++ .../storage/geojson/binding/JSONFGPolyhedron.java | 86 ++++ .../sis/storage/geojson/binding/JSONFGPrism.java | 127 +++++ .../sis/storage/geojson/binding/JSONFGTime.java | 124 +++++ .../sis/storage/geojson}/binding/package-info.java | 8 +- .../sis/storage/json/DataTransferObject.java | 100 ++++ .../movingfeature/binding/MFCoordRefSys.java | 110 +++++ .../movingfeature/binding/MFMovingFeature.java | 277 +++++++++++ .../binding/MFMovingFeatureCollection.java | 270 ++++++++++ .../binding/MFMovingGeometryBase.java | 101 ++++ .../binding/MFMovingGeometryCollection.java | 93 ++++ .../movingfeature/binding/MFMovingLineString.java | 87 ++++ .../movingfeature/binding/MFMovingPoint.java | 86 ++++ .../movingfeature/binding/MFMovingPointCloud.java | 89 ++++ .../movingfeature/binding/MFMovingPolygon.java | 88 ++++ .../movingfeature/binding/MFOrientation.java | 103 ++++ .../movingfeature/binding/MFTemporalGeometry.java | 128 +++++ .../binding/MFTemporalPrimitiveGeometry.java | 152 ++++++ .../binding/MFTemporalPropertyGroup.java | 94 ++++ .../binding/MFTemporalPropertyValue.java | 186 +++++++ .../movingfeature}/binding/package-info.java | 7 +- .../main/org/apache/sis/storage/ogcjson/Link.java | 280 +++++++++++ .../apache/sis/storage/ogcjson}/package-info.java | 6 +- .../coveragejson/CoverageJsonStoreTest.java | 0 .../storage/coveragejson/binding/BindingTest.java | 69 +-- .../sis/storage/coveragejson/binding/LICENSE.md | 0 .../storage/coveragejson/binding/axe_bounds.json | 0 .../storage/coveragejson/binding/axe_polygon.json | 0 .../storage/coveragejson/binding/axe_regular.json | 0 .../storage/coveragejson/binding/axe_tuples.json | 0 .../binding/coverage_vertical_profile.json | 0 .../binding/coverage_vertical_profile_nocs.json | 0 .../coveragejson/binding/coveragecollection.json | 0 .../storage/coveragejson/binding/domain_grid.json | 0 .../coveragejson/binding/domain_trajectory.json | 0 .../coveragejson/binding/domaintype_grid.json | 0 .../binding/domaintype_multipoint.json | 0 .../binding/domaintype_multipointseries.json | 0 .../binding/domaintype_multipolygon.json | 0 .../binding/domaintype_multipolygonseries.json | 0 .../coveragejson/binding/domaintype_point.json | 0 .../binding/domaintype_pointseries.json | 0 .../coveragejson/binding/domaintype_polygon.json | 0 .../binding/domaintype_polygonseries.json | 0 .../coveragejson/binding/domaintype_section.json | 0 .../binding/domaintype_trajectory.json | 0 .../binding/domaintype_vertical_profile.json | 0 .../binding/geographiccrs_longlat.json | 0 .../binding/geographiccrs_longlatheight.json | 0 .../sis/storage/coveragejson/binding/ndarray.json | 0 .../binding/parameter_categoricaldata.json | 0 .../binding/parameter_continuousdata.json | 0 .../binding/parametergroup_uncertainty.json | 0 .../binding/parametergroup_vectorquantity.json | 0 .../binding/projectedcrs_britishnationalgrid.json | 0 .../binding/reference_system_connection.json | 0 .../storage/coveragejson/binding/temporalrs.json | 0 .../storage/coveragejson/binding/tiledndarray.json | 0 .../coveragejson/binding/verticalcrs_navd88.json | 0 .../sis/storage/coveragejson/coverage_xyzt.json | 0 .../sis/storage/geojson/binding/BindingTest.java} | 15 +- .../storage/geojson/binding/geojson_feature.json | 10 + .../geojson/binding/geojson_featurecollection.json | 48 ++ .../binding/geojson_geometrycollection.json | 13 + .../geojson/binding/geojson_linestring.json | 7 + .../geojson/binding/geojson_multilinestring.json | 13 + .../geojson/binding/geojson_multipoint.json | 7 + .../geojson/binding/geojson_multipolygon.json | 30 ++ .../sis/storage/geojson/binding/geojson_point.json | 4 + .../storage/geojson/binding/geojson_polygon.json | 12 + .../binding/jsonfg_circularstring_measures.json | 13 + .../binding/jsonfg_circularstring_no_measures.json | 8 + .../binding/jsonfg_compoundcurve_measures.json | 25 + .../binding/jsonfg_compoundcurve_no_measures.json | 20 + .../geojson/binding/jsonfg_coordrefsys.json | 18 + .../binding/jsonfg_curvepolygon_measures.json | 20 + .../binding/jsonfg_curvepolygon_no_measures.json | 15 + .../storage/geojson/binding/jsonfg_feature.json | 49 ++ .../jsonfg_geometrycollection_measures.json | 21 + .../jsonfg_geometrycollection_no_measures.json | 16 + .../binding/jsonfg_linestring_measures.json | 12 + .../binding/jsonfg_linestring_no_measures.json | 7 + .../binding/jsonfg_multicurve_measures.json | 25 + .../binding/jsonfg_multicurve_no_measures.json | 20 + .../binding/jsonfg_multilinestring_measures.json | 18 + .../jsonfg_multilinestring_no_measures.json | 13 + .../binding/jsonfg_multipoint_measures.json | 12 + .../binding/jsonfg_multipoint_no_measures.json | 7 + .../binding/jsonfg_multipolygon_measures.json | 35 ++ .../binding/jsonfg_multipolygon_no_measures.json | 30 ++ .../binding/jsonfg_multipolyhedron_measures.json | 23 + .../jsonfg_multipolyhedron_no_measures.json | 18 + .../binding/jsonfg_multiprism_measures.json | 27 + .../binding/jsonfg_multiprism_no_measures.json | 22 + .../binding/jsonfg_multisurface_measures.json | 37 ++ .../binding/jsonfg_multisurface_no_measures.json | 32 ++ .../geojson/binding/jsonfg_point_measures.json | 9 + .../geojson/binding/jsonfg_point_no_measures.json | 4 + .../geojson/binding/jsonfg_polygon_measures.json | 17 + .../binding/jsonfg_polygon_no_measures.json | 12 + .../binding/jsonfg_polyhedron_measures.json | 21 + .../binding/jsonfg_polyhedron_no_measures.json | 16 + .../geojson/binding/jsonfg_prism_measures.json | 22 + .../geojson/binding/jsonfg_prism_no_measures.json | 17 + .../storage/geojson/binding/jsonfg_time_date.json | 15 + .../geojson/binding/jsonfg_time_interval.json | 18 + .../binding/jsonfg_time_interval_open_end.json | 18 + .../binding/jsonfg_time_interval_open_start.json | 18 + .../geojson/binding/jsonfg_time_timestamp.json | 15 + .../sis/storage/json/AbstractBindingTest.java | 76 +++ .../movingfeature/binding/BindingTest.java} | 15 +- netbeans-project/ivy.xml | 3 + netbeans-project/nbproject/project.properties | 2 +- .../apache/sis/gui/coverage/CoverageCanvas.java | 211 ++------ .../apache/sis/gui/coverage/CoverageControls.java | 2 +- .../apache/sis/gui/coverage/CoverageExplorer.java | 2 +- .../org/apache/sis/gui/coverage/GridControls.java | 2 +- .../main/org/apache/sis/gui/coverage/GridView.java | 2 +- .../apache/sis/gui/coverage/RectangleMerger.java | 111 +++++ .../sis/gui/coverage/StyledRenderingData.java | 5 + .../apache/sis/gui/coverage/TileReadListener.java | 327 ++++++++++++ .../org/apache/sis/gui/dataset/WindowHandler.java | 2 +- .../org/apache/sis/gui/internal/LogHandler.java | 2 +- .../apache/sis/gui/internal/ShapeConverter.java | 5 +- .../org/apache/sis/gui/map/EvanescentPane.java | 1 + .../main/org/apache/sis/gui/map/MapCanvas.java | 40 +- .../main/org/apache/sis/gui/map/MultiCanvas.java | 2 +- .../org/apache/sis/gui/map/ValuesFormatter.java | 7 +- .../org/apache/sis/gui/map/ValuesUnderCursor.java | 2 +- .../org/apache/sis/storage/gdal/TiledResource.java | 16 +- 319 files changed, 10574 insertions(+), 1979 deletions(-) diff --cc buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java index 3eb4604a65,4bcec1da8c..5a668b537e --- a/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java +++ b/buildSrc/src/main/java/org/apache/sis/buildtools/gradle/Dependency.java @@@ -76,7 -78,8 +76,7 @@@ public final class Dependency Map.entry("storage.gsf", "storage:sis-gsf"), Map.entry("storage.gdal", "storage:sis-gdal"), Map.entry("storage.geopackage", "storage:sis-geopackage"), - Map.entry("storage.coveragejson", "storage:sis-coveragejson"), + Map.entry("storage.json", "storage:sis-json"), - Map.entry("portrayal.map", "core:sis-portrayal-map"), Map.entry("webapp", "application:sis-webapp") ); diff --cc endorsed/src/org.apache.sis.feature/main/module-info.java index ef37ed8ccb,3ba6d33f15..87168cb542 --- a/endorsed/src/org.apache.sis.feature/main/module-info.java +++ b/endorsed/src/org.apache.sis.feature/main/module-info.java @@@ -69,8 -68,11 +69,9 @@@ module org.apache.sis.feature org.apache.sis.storage.sql, org.apache.sis.storage.netcdf, org.apache.sis.storage.shapefile, // In the "incubator" sub-project. + org.apache.sis.storage.json, // In the "incubator" sub-project. - org.apache.sis.referencing.dggs, // In the "incubator" sub-project. org.apache.sis.storage.gdal, // In the "optional" sub-project. org.apache.sis.portrayal, - org.apache.sis.portrayal.map, // In the "incubator" sub-project. org.apache.sis.gui; // In the "optional" sub-project. exports org.apache.sis.geometry.wrapper to diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java index 0425941c15,7ebf7e57a3..4217928a76 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/coverage/grid/GridCRSBuilder.java @@@ -67,11 -66,14 +66,14 @@@ import org.apache.sis.util.logging.Logg import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.util.collection.Containers; import org.apache.sis.util.collection.BackingStoreException; + import org.apache.sis.util.internal.shared.Numerics; import org.apache.sis.util.iso.Types; import org.apache.sis.measure.Units; + import org.apache.sis.util.resources.Errors; -// Specific to the geoapi-3.1 and geoapi-4.0 branches: -import org.opengis.referencing.ObjectDomain; -import org.opengis.coordinate.MismatchedDimensionException; +// Specific to the main branch: +import org.opengis.referencing.datum.Datum; ++import org.opengis.geometry.MismatchedDimensionException; /** diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java index 31b84f7e15,17408ae4a8..c41c7b7a0c --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/AbstractFeature.java @@@ -569,13 -531,13 +564,13 @@@ public abstract class AbstractFeature i * @param property name of the property for which to get a characteristic. * @param characteristic name of the characteristic of the property of the given name. * @return default value of the specified characteristic on the specified property. -- * @throws PropertyNotFoundException if the {@code property} argument is not the name of a property. ++ * @throws IllegalArgumentException if the {@code property} argument is not the name of a property. */ final Optional<?> getDefaultCharacteristicValue(final String property, final String characteristic) { - final PropertyType p = type.getProperty(property); - if (p instanceof AttributeType<?>) { - return Optional.ofNullable(((AttributeType<?>) p).characteristics().get(characteristic)) - .map(AttributeType::getDefaultValue); + final AbstractIdentifiedType p = type.getProperty(property); + if (p instanceof DefaultAttributeType<?>) { + return Optional.ofNullable(((DefaultAttributeType<?>) p).characteristics().get(characteristic)) + .map(DefaultAttributeType::getDefaultValue); } return Optional.empty(); } diff --cc endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/AttributeConvention.java index f6131f6227,395db909dc..ef66b1d810 --- a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/AttributeConvention.java +++ b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/shared/AttributeConvention.java @@@ -284,7 -290,7 +284,7 @@@ public final class AttributeConvention * @param feature the feature instance from which to get the <abbr>CRS</abbr> of an attribute. * @param property name of the property for which to get the <abbr>CRS</abbr>. * @return the Coordinate Reference System characteristic of the specified property, or {@code null} if none. -- * @throws PropertyNotFoundException if the {@code property} argument is not the name of a property of the given feature. ++ * @throws IllegalArgumentException if the {@code property} argument is not the name of a property of the given feature. * @throws ClassCastException if {@link #CRS_CHARACTERISTIC} has been found but is associated * to an object which is not a {@link CoordinateReferenceSystem} instance. * @@@ -332,7 -338,7 +332,7 @@@ * @param feature the feature instance from which to get the maximal length of an attribute. * @param property the name of the property for which to get the maximal length. * @return the maximal length characteristic of the specified property, or {@code null} if none. -- * @throws PropertyNotFoundException if the {@code property} argument is not the name of a property of the given feature. ++ * @throws IllegalArgumentException if the {@code property} argument is not the name of a property of the given feature. * @throws ClassCastException if {@link #MAXIMAL_LENGTH_CHARACTERISTIC} has been found but is associated * to an object which is not an {@link Integer} instance. * @@@ -390,10 -396,27 +390,10 @@@ * @param property name of the property for which to get the characteristic. * @param characteristic name of the characteristic from which to get the value or default value. * @return the value or default value of the specified characteristic in the specified property, or {@code null} if none. -- * @throws PropertyNotFoundException if the {@code property} argument is not the name of a property of the given feature. ++ * @throws IllegalArgumentException if the {@code property} argument is not the name of a property of the given feature. */ - private static Object getCharacteristic(final Feature feature, final String property, final String characteristic) { - if (feature instanceof AbstractFeature) { - return ((AbstractFeature) feature).getCharacteristicValue(property, characteristic).orElse(null); - } - final Property attribute = feature.getProperty(property); - if (attribute instanceof Attribute<?>) { - final Attribute<?> at = ((Attribute<?>) attribute).characteristics().get(characteristic); - if (at != null) { - final Object value = at.getValue(); - if (value != null) { - return value; - } - } - final AttributeType<?> type = ((Attribute<?>) attribute).getType().characteristics().get(characteristic); - if (type != null) { - return type.getDefaultValue(); - } - } - return null; + private static Object getCharacteristic(final AbstractFeature feature, final String property, final String characteristic) { + return feature.getCharacteristicValue(property, characteristic).orElse(null); } /** diff --cc endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/internal/shared/MergerTest.java index d419b2d3e2,9c79d7677b..09c66d15eb --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/internal/shared/MergerTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/internal/shared/MergerTest.java @@@ -111,10 -114,10 +111,10 @@@ public final class MergerTest extends T assertSetEquals(List.of(StandardCharsets.UTF_16, StandardCharsets.UTF_8), target.getLocalesAndCharsets().values()); - final Iterator<ContentInformation> it = target.getContentInfo().iterator(); - final ImageDescription image = (ImageDescription) it.next(); - final FeatureCatalogueDescription features = (FeatureCatalogueDescription) it.next(); - final DefaultCoverageDescription coverage = (DefaultCoverageDescription) it.next(); + final Iterator<ContentInformation> it = target.getContentInfo().iterator(); + final var image = assertInstanceOf(ImageDescription.class, it.next()); + final var features = assertInstanceOf(FeatureCatalogueDescription.class, it.next()); - final var coverage = assertInstanceOf(CoverageDescription.class, it.next()); ++ final var coverage = assertInstanceOf(DefaultCoverageDescription.class, it.next()); assertFalse(it.hasNext()); assertEquals(ImagingCondition.CLOUD, image .getImagingCondition()); diff --cc endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubOperationInfo.java index 69c93aedb1,c692d2cbca..62e545cbc5 --- a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubOperationInfo.java +++ b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/SubOperationInfo.java @@@ -31,10 -31,10 +31,13 @@@ import org.apache.sis.referencing.inter import org.apache.sis.referencing.operation.matrix.Matrices; import org.apache.sis.referencing.operation.matrix.MatrixSIS; import org.apache.sis.referencing.operation.transform.MathTransforms; + import org.apache.sis.measure.NumberRange; + import org.apache.sis.util.Classes; + import org.apache.sis.util.internal.shared.Strings; +// Specific to the main branch: +import org.apache.sis.referencing.crs.DefaultParametricCRS; + /** * Information about the operation from a source component to a target component in {@code CompoundCRS} instances. diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java index 8a1b2ad0d2,fb0355910e..e1299735df --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClause.java @@@ -181,10 -190,17 +181,17 @@@ public final class SelectionClause exte } /** - * Writes a literal value, or marks this SQL as invalid if the value cannot be formatted. + * Writes a literal value, or marks this <abbr>SQL</abbr> as invalid if the value cannot be formatted. + * The value can be {@code null}, a character string, a Boolean, a number, a temporal object, + * a geographic bounding box or an envelope. If the given object cannot be formatted, + * then this clause is {@linkplain #invalidate() marked as invalid}. */ final void appendLiteral(final Object value) { - if (value instanceof GeographicBoundingBox) { + if (value == null) { + append(NULL); + } else if (value instanceof CharSequence) { - appendLiteralOrPattern(value.toString()); ++ appendValue(value.toString()); + } else if (value instanceof GeographicBoundingBox) { appendGeometry(null, new GeneralEnvelope((GeographicBoundingBox) value)); } else if (value instanceof Envelope) { appendGeometry(null, (Envelope) value); diff --cc endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java index 386016294c,114a2fff87..52c88c92b4 --- a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java +++ b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/SelectionClauseWriter.java @@@ -83,8 -87,8 +83,8 @@@ public class SelectionClauseWriter exte setFilterHandler(ComparisonOperatorName.PROPERTY_IS_GREATER_THAN_OR_EQUAL_TO, new Comparison(" >= ")); setFilterHandler(ComparisonOperatorName.PROPERTY_IS_LESS_THAN, new Comparison(" < ")); setFilterHandler(ComparisonOperatorName.PROPERTY_IS_LESS_THAN_OR_EQUAL_TO, new Comparison(" <= ")); - setFilterHandler(ComparisonOperatorName.PROPERTY_IS_BETWEEN, (f,sql) -> { - setFilterHandler(ComparisonOperatorName.valueOf(FunctionNames.PROPERTY_IS_BETWEEN), (f, sql) -> { - final var filter = (BetweenComparisonOperator<Feature>) f; ++ setFilterHandler(ComparisonOperatorName.PROPERTY_IS_BETWEEN, (f, sql) -> { + final var filter = (BetweenComparisonOperator<AbstractFeature>) f; /* Nothing to append */ if (write(sql, filter.getExpression())) return; sql.append(" BETWEEN "); if (write(sql, filter.getLowerBoundary())) return; sql.append(" AND "); write(sql, filter.getUpperBoundary()); diff --cc endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java index d7741d7460,e0f04f8e94..c090a2ac23 --- a/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java +++ b/endorsed/src/org.apache.sis.storage.sql/test/org/apache/sis/storage/sql/SQLStoreTest.java @@@ -446,10 -451,10 +446,10 @@@ public final class SQLStoreTest extend */ final Object[] result; final FeatureSet cities = dataset.findResource("Cities"); - try (Stream<Feature> features = cities.subset(query).features(false)) { + try (Stream<AbstractFeature> features = cities.subset(query).features(false)) { result = features.map(f -> f.getPropertyValue("native_name")).toArray(); } - assertSetEquals(Arrays.asList("Montréal", "Québec"), Arrays.asList(result)); + assertSetEquals(List.of("Montréal", "Québec"), Arrays.asList(result)); } /** diff --cc endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java index ffedca3a5e,56fcd3e904..0e44121b2e --- a/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java +++ b/endorsed/src/org.apache.sis.storage.xml/test/org/apache/sis/storage/gpx/ReaderTest.java @@@ -95,8 -105,9 +105,9 @@@ public final class ReaderTest extends T */ private static void assertEnvelopeEquals(final double xmin, final double xmax, final double ymin, final double ymax, - final Envelope actual) - final Feature f) ++ final AbstractFeature f) { + final Envelope actual = assertInstanceOf(Envelope.class, f.getPropertyValue("sis:envelope")); assertEquals(2, actual.getDimension(), "dimension"); assertEquals(actual.getMinimum(0), xmin, "xmin"); assertEquals(actual.getMaximum(0), xmax, "xmax"); @@@ -207,10 -255,10 +255,10 @@@ @Test public void testWayPoint100() throws DataStoreException { try (Store reader = create(TestData.V1_0, TestData.WAYPOINT)) { - verifyAlmostEmptyMetadata((Metadata) reader.getMetadata()); + verifyAlmostEmptyMetadata(getMetadata(reader)); assertEquals(StoreProvider.V1_0, reader.getVersion()); - try (Stream<Feature> features = reader.features(false)) { - final Iterator<Feature> it = features.iterator(); + try (Stream<AbstractFeature> features = reader.features(false)) { + final Iterator<AbstractFeature> it = features.iterator(); verifyPoint(it.next(), 0, false); verifyPoint(it.next(), 1, false); verifyPoint(it.next(), 2, false); @@@ -227,10 -275,10 +275,10 @@@ @Test public void testWayPoint110() throws DataStoreException { try (Store reader = create(TestData.V1_1, TestData.WAYPOINT)) { - verifyAlmostEmptyMetadata((Metadata) reader.getMetadata()); + verifyAlmostEmptyMetadata(getMetadata(reader)); assertEquals(StoreProvider.V1_1, reader.getVersion()); - try (Stream<Feature> features = reader.features(false)) { - final Iterator<Feature> it = features.iterator(); + try (Stream<AbstractFeature> features = reader.features(false)) { + final Iterator<AbstractFeature> it = features.iterator(); verifyPoint(it.next(), 0, true); verifyPoint(it.next(), 1, true); verifyPoint(it.next(), 2, true); @@@ -247,10 -295,10 +295,10 @@@ @Test public void testRoute100() throws DataStoreException { try (Store reader = create(TestData.V1_0, TestData.ROUTE)) { - verifyAlmostEmptyMetadata((Metadata) reader.getMetadata()); + verifyAlmostEmptyMetadata(getMetadata(reader)); assertEquals(StoreProvider.V1_0, reader.getVersion()); - try (Stream<Feature> features = reader.features(false)) { - final Iterator<Feature> it = features.iterator(); + try (Stream<AbstractFeature> features = reader.features(false)) { + final Iterator<AbstractFeature> it = features.iterator(); verifyRoute(it.next(), false, 1); verifyEmpty(it.next(), "rtept"); assertFalse(it.hasNext()); @@@ -352,10 -400,10 +400,10 @@@ @Test public void testTrack100() throws DataStoreException { try (Store reader = create(TestData.V1_0, TestData.TRACK)) { - verifyAlmostEmptyMetadata((Metadata) reader.getMetadata()); + verifyAlmostEmptyMetadata(getMetadata(reader)); assertEquals(StoreProvider.V1_0, reader.getVersion()); - try (Stream<Feature> features = reader.features(false)) { - final Iterator<Feature> it = features.iterator(); + try (Stream<AbstractFeature> features = reader.features(false)) { + final Iterator<AbstractFeature> it = features.iterator(); verifyTrack(it.next(), false, 1); verifyEmpty(it.next(), "trkseg"); assertFalse(it.hasNext()); @@@ -371,10 -419,10 +419,10 @@@ @Test public void testTrack110() throws DataStoreException { try (Store reader = create(TestData.V1_1, TestData.TRACK)) { - verifyAlmostEmptyMetadata((Metadata) reader.getMetadata()); + verifyAlmostEmptyMetadata(getMetadata(reader)); assertEquals(StoreProvider.V1_1, reader.getVersion()); - try (Stream<Feature> features = reader.features(false)) { - final Iterator<Feature> it = features.iterator(); + try (Stream<AbstractFeature> features = reader.features(false)) { + final Iterator<AbstractFeature> it = features.iterator(); verifyTrack(it.next(), true, 3); verifyEmpty(it.next(), "trkseg"); assertFalse(it.hasNext()); @@@ -586,16 -634,17 +634,17 @@@ * @throws DataStoreException if reader failed to be created or failed at reading. */ @Test + @SuppressWarnings("ConvertToTryWithResources") public void testConcurrentReads() throws DataStoreException { try (Store reader = createFromURL()) { - final Stream<Feature> f1 = reader.features(false); - final Iterator<Feature> i1 = f1.iterator(); + final Stream<AbstractFeature> f1 = reader.features(false); + final Iterator<AbstractFeature> i1 = f1.iterator(); verifyRoute(i1.next(), true, 3); - final Stream<Feature> f2 = reader.features(false); - final Iterator<Feature> i2 = f2.iterator(); + final Stream<AbstractFeature> f2 = reader.features(false); + final Iterator<AbstractFeature> i2 = f2.iterator(); verifyEmpty(i1.next(), "rtept"); - final Stream<Feature> f3 = reader.features(false); - final Iterator<Feature> i3 = f3.iterator(); + final Stream<AbstractFeature> f3 = reader.features(false); + final Iterator<AbstractFeature> i3 = f3.iterator(); verifyRoute(i2.next(), true, 3); verifyRoute(i3.next(), true, 3); verifyEmpty(i3.next(), "rtept"); diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java index 63b6b1b81f,b780a79674..9c5845162a --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java @@@ -622,7 -621,7 +622,7 @@@ public class FeatureQuery extends Quer * @param builder the builder where to add the property. * @return whether the property has been successfully added. * @throws InvalidFilterValueException if {@linkplain #expression} is invalid. -- * @throws PropertyNotFoundException if the property was not found in {@code builder.source()}. ++ * @throws IllegalArgumentException if the property was not found in {@code builder.source()}. * @throws UnconvertibleObjectException if the property default value cannot be converted to the expected type. */ final boolean addTo(final FeatureProjectionBuilder builder) { @@@ -788,7 -787,7 +788,7 @@@ * @param sourceType the feature type to project. * @param locale locale for error messages, or {@code null} for the default locale. * @throws InvalidFilterValueException if an {@linkplain NamedExpression#expression expression} is invalid. -- * @throws PropertyNotFoundException if a property referenced by an expression was not found in {@code sourceType}. ++ * @throws IllegalArgumentException if a property referenced by an expression was not found in {@code sourceType}. * @throws UnconvertibleObjectException if a property default value cannot be converted to the expected type. * @throws UnsupportedOperationException if there is an attempt to rename a property which is used by an operation. */ diff --cc incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSON.java index 0000000000,d805dfbcef..ee2b202e19 mode 000000,100644..100644 --- a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSON.java +++ b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSON.java @@@ -1,0 -1,165 +1,165 @@@ + /* + * 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.geojson; + + import java.util.List; + import java.util.Map.Entry; ++import org.apache.sis.feature.AbstractFeature; ++import org.apache.sis.feature.DefaultFeatureType; + import org.apache.sis.feature.builder.AttributeRole; + import org.apache.sis.feature.builder.FeatureTypeBuilder; + import org.apache.sis.feature.internal.shared.AttributeConvention; + import org.apache.sis.storage.DataStoreException; + import org.apache.sis.storage.geojson.binding.GeoJSONFeature; + import org.apache.sis.storage.geojson.binding.GeoJSONGeometry; + import org.apache.sis.storage.geojson.binding.GeoJSONGeometryCollection; + import org.apache.sis.storage.geojson.binding.GeoJSONLineString; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiLineString; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiPoint; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiPolygon; + import org.apache.sis.storage.geojson.binding.GeoJSONPoint; + import org.apache.sis.storage.geojson.binding.GeoJSONPolygon; + import org.locationtech.jts.geom.Coordinate; + import org.locationtech.jts.geom.CoordinateXY; + import org.locationtech.jts.geom.CoordinateXYZM; + import org.locationtech.jts.geom.Geometry; + import org.locationtech.jts.geom.GeometryFactory; + import org.locationtech.jts.geom.LineString; + import org.locationtech.jts.geom.LinearRing; + import org.locationtech.jts.geom.Point; + import org.locationtech.jts.geom.Polygon; -import org.opengis.feature.Feature; -import org.opengis.feature.FeatureType; + + /** + * + * @author Johann Sorel (Geomatys) + */ + public final class GeoJSON { + + private static final GeometryFactory GF = new GeometryFactory(); + + private GeoJSON() { + + } + - public static Feature fromGeoJSON(GeoJSONFeature json, FeatureType type) throws DataStoreException { ++ public static AbstractFeature fromGeoJSON(GeoJSONFeature json, DefaultFeatureType type) throws DataStoreException { + + final GeoJSONGeometry jsongeom = json.getGeometry(); + Geometry geom = null; + if (jsongeom != null) { + geom = fromGeoJSON(jsongeom); + } + + if (type == null) { + final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); + ftb.setName("geojson"); + + if (json.getId() != null) { + ftb.addAttribute(json.getId().getClass()).setName(AttributeConvention.IDENTIFIER_PROPERTY).addRole(AttributeRole.IDENTIFIER_COMPONENT); + } + if (geom != null) { + ftb.addAttribute(geom.getClass()).setName(AttributeConvention.GEOMETRY_PROPERTY).addRole(AttributeRole.DEFAULT_GEOMETRY); + } + for (Entry<String,Object> entry : json.getProperties().entrySet()) { + ftb.addAttribute(entry.getValue().getClass()).setName(entry.getKey()); + } + type = ftb.build(); + } + - final Feature feature = type.newInstance(); ++ final AbstractFeature feature = type.newInstance(); + + if (json.getId() != null) { + feature.setPropertyValue(AttributeConvention.IDENTIFIER, json.getId()); + } + + if (geom != null) { + feature.setPropertyValue(AttributeConvention.GEOMETRY, geom); + } + + for (Entry<String,Object> entry : json.getProperties().entrySet()) { + feature.setPropertyValue(entry.getKey(), entry.getValue()); + } + return feature; + } + + public static Geometry fromGeoJSON(GeoJSONGeometry geom) throws DataStoreException { + if (geom instanceof GeoJSONPoint json) { + return GF.createPoint(toCoordinate(json.getCoordinates())); + } else if (geom instanceof GeoJSONLineString json) { + return GF.createLineString(toCoordinates(json.getCoordinates())); + } else if (geom instanceof GeoJSONPolygon json) { + return toPolygon(json.getCoordinates()); + } else if (geom instanceof GeoJSONMultiPoint json) { + final List<List<Double>> coordinates = json.getCoordinates(); + final Point[] points = new Point[coordinates.size()]; + for (int i = 0; i < points.length; i++) { + points[i] = GF.createPoint(toCoordinate(coordinates.get(i))); + } + return GF.createMultiPoint(points); + } else if (geom instanceof GeoJSONMultiLineString json) { + final List<List<List<Double>>> coordinates = json.getCoordinates(); + final LineString[] lines = new LineString[coordinates.size()]; + for (int i = 0; i < lines.length; i++) { + lines[i] = GF.createLineString(toCoordinates(coordinates.get(i))); + } + return GF.createMultiLineString(lines); + } else if (geom instanceof GeoJSONMultiPolygon json) { + final List<List<List<List<Double>>>> coordinates = json.getCoordinates(); + final Polygon[] polygons = new Polygon[coordinates.size()]; + for (int i = 0; i < polygons.length; i++) { + polygons[i] = toPolygon(coordinates.get(i)); + } + return GF.createMultiPolygon(polygons); + } else if (geom instanceof GeoJSONGeometryCollection json) { + final List<GeoJSONGeometry> geometries = json.getGeometries(); + final Geometry[] children = new Geometry[geometries.size()]; + for (int i = 0; i < children.length; i++) { + children[i] = fromGeoJSON(geometries.get(i)); + } + return GF.createGeometryCollection(children); + } else { + throw new DataStoreException("Geometry not supported " + geom); + } + } + + private static Polygon toPolygon(List<List<List<Double>>> rings) throws DataStoreException { + final LinearRing shell = GF.createLinearRing(toCoordinates(rings.get(0))); + final LinearRing[] holes = new LinearRing[rings.size() - 1]; + for (int i = 0; i < holes.length; i++) { + holes[i] = GF.createLinearRing(toCoordinates(rings.get(i + 1))); + } + return GF.createPolygon(shell, holes); + } + + private static Coordinate[] toCoordinates(List<List<Double>> positions) throws DataStoreException { + final Coordinate[] coords = new Coordinate[positions.size()]; + for (int i = 0; i < coords.length; i++) { + coords[i] = toCoordinate(positions.get(i)); + } + return coords; + } + + private static Coordinate toCoordinate(List<Double> coordinates) throws DataStoreException { + final int size = coordinates.size(); + switch (size) { + case 2 : return new CoordinateXY(coordinates.get(0), coordinates.get(1)); + case 3 : return new Coordinate(coordinates.get(0), coordinates.get(1), coordinates.get(2)); + case 4 : return new CoordinateXYZM(coordinates.get(0), coordinates.get(1), coordinates.get(2), coordinates.get(3)); + default : throw new DataStoreException("Geometry " + size + "not supported"); + } + } + + } diff --cc incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSONMapper.java index 0000000000,f480f04d6e..65bc1df0f6 mode 000000,100644..100644 --- a/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSONMapper.java +++ b/incubator/src/org.apache.sis.storage.json/main/org/apache/sis/storage/geojson/GeoJSONMapper.java @@@ -1,0 -1,481 +1,479 @@@ + /* + * 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.geojson; + + import java.util.ArrayList; + import java.util.Iterator; + import java.util.LinkedHashMap; + import java.util.List; + import java.util.Map; + import java.util.function.Predicate; + import java.util.stream.Collectors; + import java.util.stream.Stream; ++import org.apache.sis.feature.AbstractFeature; ++import org.apache.sis.feature.AbstractIdentifiedType; ++import org.apache.sis.feature.AbstractOperation; ++import org.apache.sis.feature.DefaultAttributeType; ++import org.apache.sis.feature.DefaultFeatureType; + import org.apache.sis.feature.internal.shared.AttributeConvention; + import org.apache.sis.referencing.IdentifiedObjects; + import org.apache.sis.storage.DataStoreException; + import org.apache.sis.storage.FeatureSet; + import org.apache.sis.storage.geojson.binding.GeoJSONFeature; + import org.apache.sis.storage.geojson.binding.GeoJSONFeatureCollection; + import org.apache.sis.storage.geojson.binding.GeoJSONGeometry; + import org.apache.sis.storage.geojson.binding.GeoJSONGeometryCollection; + import org.apache.sis.storage.geojson.binding.GeoJSONLineString; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiLineString; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiPoint; + import org.apache.sis.storage.geojson.binding.GeoJSONMultiPolygon; + import org.apache.sis.storage.geojson.binding.GeoJSONPoint; + import org.apache.sis.storage.geojson.binding.GeoJSONPolygon; + import org.locationtech.jts.geom.CoordinateSequence; + import org.locationtech.jts.geom.Envelope; + import org.locationtech.jts.geom.Geometry; + import org.locationtech.jts.geom.GeometryCollection; + 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; -import org.opengis.feature.AttributeType; -import org.opengis.feature.Feature; -import org.opengis.feature.FeatureType; -import org.opengis.feature.IdentifiedType; -import org.opengis.feature.Operation; -import org.opengis.feature.PropertyNotFoundException; -import org.opengis.feature.PropertyType; + import org.opengis.referencing.crs.CoordinateReferenceSystem; + + /** + * + * @author Johann Sorel (Geomatys) + */ + public final class GeoJSONMapper { + + private boolean bboxOnCollection = false; + private boolean bboxOnFeature = false; + private boolean bboxOnGeometry = false; + private boolean includeFeatureId = true; + private boolean includeTypeOnFeature = false; + private boolean includeTypeOnCollection = false; + private boolean includeCoordRefSysOnCollection = false; + private boolean includeCoordRefSysOnFeature = false; + private boolean includeCoordRefSysOnGeometry = false; + + public void setBboxOnCollection(boolean bboxOnCollection) { + this.bboxOnCollection = bboxOnCollection; + } + + public boolean isBboxOnCollection() { + return bboxOnCollection; + } + + public void setBboxOnFeature(boolean bboxOnFeature) { + this.bboxOnFeature = bboxOnFeature; + } + + public boolean isBboxOnFeature() { + return bboxOnFeature; + } + + public void setBboxOnGeometry(boolean bboxOnGeometry) { + this.bboxOnGeometry = bboxOnGeometry; + } + + public boolean isBboxOnGeometry() { + return bboxOnGeometry; + } + + public void setIncludeFeatureId(boolean includeFeatureId) { + this.includeFeatureId = includeFeatureId; + } + + public boolean isIncludeFeatureId() { + return includeFeatureId; + } + + public void setIncludeTypeOnFeature(boolean includeTypeOnFeature) { + this.includeTypeOnFeature = includeTypeOnFeature; + } + + public boolean isIncludeTypeOnFeature() { + return includeTypeOnFeature; + } + + public void setIncludeTypeOnCollection(boolean includeTypeOnCollection) { + this.includeTypeOnCollection = includeTypeOnCollection; + } + + public boolean isIncludeTypeOnCollection() { + return includeTypeOnCollection; + } + + public void setIncludeCoordRefSysOnCollection(boolean includeCoordRefSysOnCollection) { + this.includeCoordRefSysOnCollection = includeCoordRefSysOnCollection; + } + + public boolean isIncludeCoordRefSysOnCollection() { + return includeCoordRefSysOnCollection; + } + + public void setIncludeCoordRefSysOnFeature(boolean includeCoordRefSysOnFeature) { + this.includeCoordRefSysOnFeature = includeCoordRefSysOnFeature; + } + + public boolean isIncludeCoordRefSysOnFeature() { + return includeCoordRefSysOnFeature; + } + + public void setIncludeCoordRefSysOnGeometry(boolean includeCoordRefSysOnGeometry) { + this.includeCoordRefSysOnGeometry = includeCoordRefSysOnGeometry; + } + + public boolean isIncludeCoordRefSysOnGeometry() { + return includeCoordRefSysOnGeometry; + } + - public GeoJSONFeature transform(Feature feature) throws DataStoreException { - final FeatureType type = feature.getType(); ++ public GeoJSONFeature transform(AbstractFeature feature) throws DataStoreException { ++ final DefaultFeatureType type = feature.getType(); + + final GeoJSONFeature gf = new GeoJSONFeature(); + + if (includeTypeOnFeature) { + gf.setFeatureType(getTypeName(type)); + } + + if (includeFeatureId && feature.getType().hasProperty(AttributeConvention.IDENTIFIER)) { // TODO: should be determined in advance. + Object id = feature.getPropertyValue(AttributeConvention.IDENTIFIER); + if (id != null) { + gf.setId(id); + } + } + if (includeCoordRefSysOnFeature) { + gf.setCoordRefSys(getCoordRefSys(type)); + } + + if (feature.getType().hasProperty(AttributeConvention.GEOMETRY)) { // TODO: should be determined in advance. + Object geom = feature.getPropertyValue(AttributeConvention.GEOMETRY); + if (geom instanceof Geometry g) { + GeoJSONGeometry json = transform(g); + gf.setGeometry(json); + + if (bboxOnFeature) { + Envelope env = g.getEnvelopeInternal(); + if (env != null && !env.isNull()) { + final List<Double> bbox = new ArrayList<>(); + bbox.add(env.getMinX()); + bbox.add(env.getMinY()); + bbox.add(env.getMaxX()); + bbox.add(env.getMaxY()); + gf.setBbox(bbox); + } + } + } + } + + final Map<String,Object> properties = new LinkedHashMap(); + gf.setProperties(properties); - for (PropertyType pt : type.getProperties(true)) { ++ for (AbstractIdentifiedType pt : type.getProperties(true)) { + if (AttributeConvention.contains(pt.getName())) continue; - if (pt instanceof AttributeType) { ++ if (pt instanceof DefaultAttributeType) { + final String name = pt.getName().toString(); + final Object value = feature.getPropertyValue(name); + properties.put(name, value); + } + } + + return gf; + } + + public GeoJSONFeatureCollection transform(FeatureSet features) throws DataStoreException { - final FeatureType type = features.getType(); ++ final DefaultFeatureType type = features.getType(); + + final GeoJSONFeatureCollection cl = new GeoJSONFeatureCollection(); + final List<GeoJSONFeature> lst = new ArrayList<>(); - try (Stream<Feature> stream = features.features(false)) { - final Iterator<Feature> ite = stream.iterator(); ++ try (Stream<AbstractFeature> stream = features.features(false)) { ++ final Iterator<AbstractFeature> ite = stream.iterator(); + while (ite.hasNext()) { + lst.add(transform(ite.next())); + } + } + + cl.setFeatures(lst); + + if (includeTypeOnCollection) { + cl.setFeatureType(getTypeName(type)); + } + if (includeCoordRefSysOnCollection) { + cl.setCoordRefSys(getCoordRefSys(type)); + } + if (bboxOnCollection) { + org.opengis.geometry.Envelope env = features.getEnvelope().orElse(null); + if (env != null) { + final List<Double> bbox = new ArrayList<>(); + bbox.add(env.getMinimum(0)); + bbox.add(env.getMinimum(1)); + bbox.add(env.getMaximum(0)); + bbox.add(env.getMaximum(1)); + cl.setBbox(bbox); + } + } + + return cl; + } + + public GeoJSONGeometry transform(Geometry geom) throws DataStoreException { + + final GeoJSONGeometry res; + if (geom instanceof Point cdt) { + final GeoJSONPoint json = new GeoJSONPoint(); + CoordinateSequence cs = cdt.getCoordinateSequence(); + json.setCoordinates(toList(cs).get(0)); + res = json; + } else if (geom instanceof LineString cdt) { + final GeoJSONLineString json = new GeoJSONLineString(); + CoordinateSequence cs = cdt.getCoordinateSequence(); + json.setCoordinates(toList(cs)); + res = json; + } else if (geom instanceof Polygon cdt) { + final GeoJSONPolygon json = new GeoJSONPolygon(); + final List<List<List<Double>>> lst = new ArrayList<>(); + lst.add(toList(cdt.getExteriorRing().getCoordinateSequence())); + for (int i = 0, n = cdt.getNumInteriorRing(); i < n; i++) { + lst.add(toList(cdt.getInteriorRingN(i).getCoordinateSequence())); + } + json.setCoordinates(lst); + res = json; + } else if (geom instanceof MultiPoint cdt) { + final GeoJSONMultiPoint json = new GeoJSONMultiPoint(); + final List<List<Double>> lst = new ArrayList<>(); + for (int i = 0, n = cdt.getNumGeometries(); i < n; i++) { + lst.add(toList(((Point)cdt.getGeometryN(i)).getCoordinateSequence()).get(0)); + } + json.setCoordinates(lst); + res = json; + } else if (geom instanceof MultiLineString cdt) { + final GeoJSONMultiLineString json = new GeoJSONMultiLineString(); + final List<List<List<Double>>> lst = new ArrayList<>(); + for (int i = 0, n = cdt.getNumGeometries(); i < n; i++) { + lst.add(toList(((LineString)cdt.getGeometryN(i)).getCoordinateSequence())); + } + json.setCoordinates(lst); + res = json; + } else if (geom instanceof MultiPolygon cdt) { + final GeoJSONMultiPolygon json = new GeoJSONMultiPolygon(); + final List<List<List<List<Double>>>> lst = new ArrayList<>(); + for (int i = 0, n = cdt.getNumGeometries(); i < n; i++) { + final Polygon pl = (Polygon) cdt.getGeometryN(i); + final List<List<List<Double>>> sublst = new ArrayList<>(); + sublst.add(toList(pl.getExteriorRing().getCoordinateSequence())); + for (int j = 0, k = pl.getNumInteriorRing(); j < k; j++) { + sublst.add(toList(pl.getInteriorRingN(j).getCoordinateSequence())); + } + lst.add(sublst); + } + json.setCoordinates(lst); + res = json; + } else if (geom instanceof GeometryCollection cdt) { + final GeoJSONGeometryCollection json = new GeoJSONGeometryCollection(); + final List<GeoJSONGeometry> geometries = new ArrayList<>(); + for (int i = 0, n = cdt.getNumGeometries(); i < n; i++) { + final Geometry pl = (Geometry) cdt.getGeometryN(i); + geometries.add(transform(pl)); + } + json.setGeometries(geometries); + res = json; + } else { + throw new DataStoreException("Geometry not supported yet " + geom); + } + + if (bboxOnGeometry) { + Envelope env = geom.getEnvelopeInternal(); + if (env != null && !env.isNull()) { + final List<Double> bbox = new ArrayList<>(); + bbox.add(env.getMinX()); + bbox.add(env.getMinY()); + bbox.add(env.getMaxX()); + bbox.add(env.getMaxY()); + res.setBbox(bbox); + } + } + + if (includeCoordRefSysOnGeometry) { + Object userData = geom.getUserData(); + if (userData instanceof CoordinateReferenceSystem crs) { + res.setCoordRefSys(getCoordRefSys(crs)); + } + } + + return res; + } + + private List<List<Double>> toList(CoordinateSequence cs) throws DataStoreException { + final int dim = cs.getDimension(); + final int size = cs.size(); + final List<List<Double>> lst = new ArrayList<>(size); + switch (dim) { + case 2 : { + for (int i = 0; i < size; i++) { + lst.add(List.of(cs.getOrdinate(i, 0), cs.getOrdinate(i, 1))); + } + } break; + case 3 : { + for (int i = 0; i < size; i++) { + lst.add(List.of(cs.getOrdinate(i, 0), cs.getOrdinate(i, 1), cs.getOrdinate(i, 2))); + } + } break; + case 4 : { + for (int i = 0; i < size; i++) { + lst.add(List.of(cs.getOrdinate(i, 0), cs.getOrdinate(i, 1), cs.getOrdinate(i, 2), cs.getOrdinate(i, 3))); + } + } break; + default: throw new DataStoreException("Unexpected coordinate sequence dimension " + dim); + } + return lst; + } + - private static String getTypeName(FeatureType type) { ++ private static String getTypeName(DefaultFeatureType type) { + return type.getName().toString(); + } + + /** + * Returns the 'coordRefSys' value to use for the given feature type: a plain CRS + * identifier URI {@link String}, per the common encoding used by real JSON-FG producers + * (see JSON-FG version 1.0.0, {@code coordrefsys.json}). + */ - private static Object getCoordRefSys(FeatureType ft) { ++ private static Object getCoordRefSys(DefaultFeatureType ft) { + final CoordinateReferenceSystem crs = getCRS(ft); + return getCoordRefSys(crs); + } + + private static Object getCoordRefSys(CoordinateReferenceSystem crs) { + if (crs == null) return null; + return IdentifiedObjects.getIdentifierOrName(crs); + } + + + /** + * Extract the coordinate reference system associated to the primary geometry + * of input data type. + * + * @implNote - * Primary geometry is determined using {@link #getDefaultGeometry(org.opengis.feature.FeatureType) }. ++ * Primary geometry is determined using {@link #getDefaultGeometry(DefaultFeatureType)}. + * + * @param type The data type to extract reference system from. + * @return The CRS associated to the default geometry of this data type, or + * a null value if we cannot determine what is the primary geometry of the + * data type. Note that a null value is also returned if a geometry property + * is found, but no CRS characteristics is associated with it. + */ - private static CoordinateReferenceSystem getCRS(FeatureType type){ ++ private static CoordinateReferenceSystem getCRS(DefaultFeatureType type){ + try { + return getCRS(getDefaultGeometry(type)); + } catch (IllegalArgumentException|IllegalStateException ex) { + //no default geometry property + return null; + } + } + + /** + * Extract CRS characteristic if it exist. + * + * @param type + * @return CoordinateReferenceSystem or null + */ - private static CoordinateReferenceSystem getCRS(PropertyType type){ ++ private static CoordinateReferenceSystem getCRS(AbstractIdentifiedType type){ + return getCharacteristicValue(type, AttributeConvention.CRS, null); + } + + /** + * Extract characteristic value if it exist. + * + * @param <T> expected value class + * @param type base type to search in + * @param charName characteristic name + * @param defaulValue default value if characteristic is missing or null. + * @return characteristic value or default value is not found + */ - private static <T> T getCharacteristicValue(PropertyType type, String charName, T defaulValue){ - while(type instanceof Operation){ - type = (PropertyType) ((Operation)type).getResult(); ++ private static <T> T getCharacteristicValue(AbstractIdentifiedType type, String charName, T defaulValue){ ++ while(type instanceof AbstractOperation){ ++ type = (AbstractIdentifiedType) ((AbstractOperation)type).getResult(); + } - if(type instanceof AttributeType){ - final AttributeType at = (AttributeType) ((AttributeType)type).characteristics().get(charName); ++ if(type instanceof DefaultAttributeType){ ++ final DefaultAttributeType at = (DefaultAttributeType) ((DefaultAttributeType)type).characteristics().get(charName); + if(at!=null){ + T val = (T) at.getDefaultValue(); + return val==null ? defaulValue : val; + } + } + return defaulValue; + } + + /** + * Search for the main geometric property in the given type. We'll search + * for an SIS convention first (see + * {@link AttributeConvention#GEOMETRY_PROPERTY}. If no convention is set on + * the input type, we'll check if it contains a single geometric property. + * If it's the case, we return it. Otherwise (no or multiple geometries), we + * throw an exception. + * + * @param type The data type to search into. + * @return The main geometric property we've found. - * @throws PropertyNotFoundException If no geometric property is available ++ * @throws IllegalArgumentException If no geometric property is available + * in the given type. + * @throws IllegalStateException If no convention is set (see + * {@link AttributeConvention#GEOMETRY_PROPERTY}), and we've found more than + * one geometry. + */ - private static PropertyType getDefaultGeometry(final FeatureType type) throws PropertyNotFoundException, IllegalStateException { - PropertyType geometry; ++ private static AbstractIdentifiedType getDefaultGeometry(final DefaultFeatureType type) throws IllegalArgumentException, IllegalStateException { ++ AbstractIdentifiedType geometry; + try { + geometry = type.getProperty(AttributeConvention.GEOMETRY); - } catch (PropertyNotFoundException e) { ++ } catch (IllegalArgumentException e) { + try { + geometry = searchForGeometry(type); + } catch (RuntimeException e2) { + e2.addSuppressed(e); + throw e2; + } + } + return geometry; + } + + /** + * Search for a geometric attribute outside SIS conventions. More accurately, + * we expect the given type to have a single geometry attribute. If many are + * found, an exception is thrown. + * + * @param type The data type to search into. + * @return The only geometric property we've found. - * @throws PropertyNotFoundException If no geometric property is available in ++ * @throws IllegalArgumentException If no geometric property is available in + * the given type. + * @throws IllegalStateException If we've found more than one geometry. + */ - private static PropertyType searchForGeometry(final FeatureType type) throws PropertyNotFoundException, IllegalStateException { - final Predicate<IdentifiedType> isNotConvention = p -> !AttributeConvention.contains(p.getName()); - final List<? extends PropertyType> geometries = type.getProperties(true).stream() ++ private static AbstractIdentifiedType searchForGeometry(final DefaultFeatureType type) throws IllegalArgumentException, IllegalStateException { ++ final Predicate<AbstractIdentifiedType> isNotConvention = p -> !AttributeConvention.contains(p.getName()); ++ final List<? extends AbstractIdentifiedType> geometries = type.getProperties(true).stream() + .filter(isNotConvention) + .filter(AttributeConvention::isGeometryAttribute) + .collect(Collectors.toList()); + + if (geometries.size() < 1) { - throw new PropertyNotFoundException("No geometric property can be found outside of sis convention."); ++ throw new IllegalArgumentException("No geometric property can be found outside of sis convention."); + } else if (geometries.size() > 1) { + throw new IllegalStateException("Multiple geometries found. We don't know which one to select."); + } else { + return geometries.get(0); + } + } + + } diff --cc netbeans-project/ivy.xml index 9e591bf630,4e265323ba..9be4d52407 --- a/netbeans-project/ivy.xml +++ b/netbeans-project/ivy.xml @@@ -39,5 -38,10 +39,8 @@@ <dependency org="gov.nist.math" name="jama" rev="1.0.3"/> <dependency org="net.sf.geographiclib" name="GeographicLib-Java" rev="2.0"/> <dependency org="org.xerial" name="sqlite-jdbc" rev="3.45.1.0"/> - <dependency org="org.xerial" name="sqlite-jdbc" rev="3.45.1.0"/> - <dependency org="org.xerial" name="sqlite-jdbc" rev="3.45.1.0"/> + <dependency org="com.fasterxml.jackson.core" name="jackson-core" rev="2.22.1"/> + <dependency org="com.fasterxml.jackson.core" name="jackson-annotations" rev="2.22"/> + <dependency org="com.fasterxml.jackson.core" name="jackson-databind" rev="2.22.1"/> </dependencies> </ivy-module>
