This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-3.1 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 76ac26bca233fd47b797ce44f4418b35c62bc849 Merge: 8c45b25f06 3a021d2345 Author: Martin Desruisseaux <[email protected]> AuthorDate: Tue Mar 31 16:08:05 2026 +0200 Merge branch 'geoapi-4.0' into geoapi-3.1. Contains (non-exhaustive list): - Implementation of `TileMatrixSet` by the GeoTIFF reader. - Redesign of the controls over coverage rendering in JavaFX. - Work in the geometry package (incubator). - Bug fixes. .../sis/coverage/grid/ClippedGridCoverage.java | 3 +- .../org/apache/sis/coverage/grid/GridCoverage.java | 12 +- .../apache/sis/coverage/grid/GridCoverage2D.java | 3 +- .../sis/coverage/grid/GridCoverageBuilder.java | 53 +- .../apache/sis/coverage/grid/GridDerivation.java | 2 +- .../org/apache/sis/coverage/grid/GridExtent.java | 61 +- .../org/apache/sis/coverage/grid/GridGeometry.java | 24 +- .../main/org/apache/sis/feature/FeatureFormat.java | 8 +- .../feature/internal/shared/MovingFeatures.java | 28 +- .../org/apache/sis/filter/ComparisonFilter.java | 141 ++++- .../main/org/apache/sis/filter/LogicalFilter.java | 15 +- .../main/org/apache/sis/filter/Optimization.java | 2 +- .../sis/filter/base/BinaryFunctionWidening.java | 3 +- .../apache/sis/filter/math/FilteringFunction.java | 62 ++ .../main/org/apache/sis/filter/math/Predicate.java | 28 +- .../main/org/apache/sis/image/BandedIterator.java | 23 + .../main/org/apache/sis/image/ComputedImage.java | 20 +- .../main/org/apache/sis/image/ComputedTiles.java | 26 +- .../main/org/apache/sis/image/ImageProcessor.java | 4 +- .../main/org/apache/sis/image/PlanarImage.java | 2 +- .../apache/sis/image/WritablePixelIterator.java | 50 +- .../sis/image/internal/shared/ReshapedImage.java | 128 ++++- .../main/org/apache/sis/image/package-info.java | 2 +- .../sis/coverage/grid/ClippedGridCoverageTest.java | 1 + .../apache/sis/coverage/grid/GridExtentTest.java | 10 +- .../apache/sis/filter/ComparisonFilterTest.java | 98 ++-- .../apache/sis/filter/DynamicOptimizationTest.java | 19 + .../image/internal/shared/ReshapedImageTest.java | 15 +- .../iso/extent/NotSpatioTemporalException.java | 4 +- .../metadata/sql/internal/shared/ScriptRunner.java | 2 +- .../org.apache.sis.portrayal/main/module-info.java | 2 +- .../coverage/MultiResolutionCoverageLoader.java | 46 +- .../org/apache/sis/map/coverage/RenderingData.java | 53 +- .../org/apache/sis/map/internal/Resources.java | 102 ++++ .../apache/sis/map/internal/Resources.properties | 23 + .../org/apache/sis/map/internal/Resources_en.java | 14 +- .../org/apache/sis/map/internal/Resources_fr.java | 14 +- .../sis/map/internal/Resources_fr.properties | 28 + .../org/apache/sis/map/internal}/package-info.java | 11 +- .../main/org/apache/sis/portrayal/Observable.java | 2 - .../org/apache/sis/portrayal/PlanarCanvas.java | 7 +- .../MultiResolutionCoverageLoaderTest.java | 18 +- .../main/org/apache/sis/geometry/Shapes2D.java | 2 +- .../main/org/apache/sis/referencing/CommonCRS.java | 8 +- .../referencing/internal/shared/WKTUtilities.java | 4 +- .../operation/CoordinateOperationContext.java | 31 +- .../operation/CoordinateOperationFinder.java | 30 +- .../operation/CoordinateOperationRegistry.java | 63 +- .../DefaultCoordinateOperationFactory.java | 58 +- .../referencing/operation/SubOperationInfo.java | 8 +- .../sis/referencing/operation/matrix/Matrices.java | 4 +- .../sis/referencing/operation/package-info.java | 2 +- .../operation/transform/PassThroughTransform.java | 2 +- .../main/module-info.java | 2 +- .../sis/storage/geotiff/CompressedSubset.java | 18 +- .../org/apache/sis/storage/geotiff/DataCube.java | 65 +-- .../org/apache/sis/storage/geotiff/DataSubset.java | 36 +- .../apache/sis/storage/geotiff/GeoTiffStore.java | 2 +- .../sis/storage/geotiff/ImageFileDirectory.java | 218 +++++-- .../sis/storage/geotiff/MultiResolutionImage.java | 286 --------- .../org/apache/sis/storage/geotiff/Reader.java | 22 +- .../apache/sis/storage/geotiff/package-info.java | 2 +- .../sis/storage/geotiff/GeoTiffStoreTest.java | 56 +- .../org/apache/sis/storage/geotiff/ReaderTest.java | 124 ++++ .../org/apache/sis/storage/geotiff/WriterTest.java | 5 +- .../org/apache/sis/storage/geotiff/untiled.tiff | Bin 0 -> 2284 bytes .../apache/sis/storage/netcdf/base/FeatureSet.java | 62 +- .../sis/storage/netcdf/base/RasterResource.java | 2 + .../org/apache/sis/io/stream/ChannelDataInput.java | 2 +- .../sis/storage/AbstractGridCoverageResource.java | 82 ++- .../org/apache/sis/storage/CoverageSubset.java | 11 +- .../main/org/apache/sis/storage/DataStore.java | 18 +- .../apache/sis/storage/GridCoverageResource.java | 41 +- .../main/org/apache/sis/storage/Resource.java | 4 +- .../org/apache/sis/storage/StorageConnector.java | 25 +- .../sis/storage/UnsupportedQueryException.java | 1 + .../aggregate/BandAggregateGridResource.java | 4 +- .../aggregate/ConcatenatedGridResource.java | 12 +- .../apache/sis/storage/aggregate/GridSlice.java | 12 +- .../sis/storage/aggregate/GridSliceLocator.java | 5 +- .../sis/storage/base/GridResourceWrapper.java | 6 +- .../main/org/apache/sis/storage/csv/Store.java | 13 +- .../org/apache/sis/storage/event/StoreEvent.java | 2 +- .../apache/sis/storage/event/StoreListeners.java | 14 +- .../org/apache/sis/storage/event/package-info.java | 4 +- .../apache/sis/storage/image/SingleImageStore.java | 6 +- .../storage/image/WritableSingleImageStore.java | 4 +- .../org/apache/sis/storage/internal/Resources.java | 5 + .../sis/storage/internal/Resources.properties | 1 + .../sis/storage/internal/Resources_fr.properties | 1 + .../apache/sis/storage/tiling/ImagePyramid.java | 472 +++++++++++++++ .../apache/sis/storage/tiling/ImageTileMatrix.java | 532 +++++++++++++++++ .../apache/sis/storage/tiling/IterationDomain.java | 221 +++++++ .../main/org/apache/sis/storage/tiling/Tile.java | 4 +- .../org/apache/sis/storage/tiling/TileMatrix.java | 11 +- .../apache/sis/storage/tiling/TileMatrixSet.java | 12 +- .../sis/storage/tiling/TileMatrixSetFormat.java | 524 +++++++++++++++++ .../apache/sis/storage/tiling/TileReadEvent.java | 234 ++++++++ .../org/apache/sis/storage/tiling/TileStatus.java | 2 +- .../{base => tiling}/TiledDeferredImage.java | 21 +- .../{base => tiling}/TiledGridCoverage.java | 500 ++++++++++------ .../TiledGridCoverageResource.java} | 599 +++++++++++++++++-- .../apache/sis/storage/tiling/TiledResource.java | 4 +- .../apache/sis/storage/tiling/package-info.java | 10 +- .../org/apache/sis/storage/CoverageQueryTest.java | 8 +- .../storage/MemoryGridCoverageResourceTest.java | 6 +- .../sis/storage/test/CoverageReadConsistency.java | 2 +- .../main/org/apache/sis/io/TableAppender.java | 16 +- .../main/org/apache/sis/io/TabularFormat.java | 4 +- .../main/org/apache/sis/io/package-info.java | 2 +- .../main/org/apache/sis/math/DecimalFunctions.java | 29 +- .../main/org/apache/sis/math/NumberType.java | 2 +- .../main/org/apache/sis/math/Vector.java | 2 +- .../main/org/apache/sis/util/ArgumentChecks.java | 26 +- .../main/org/apache/sis/util/collection/Cache.java | 4 +- .../org/apache/sis/util/collection/Containers.java | 30 +- .../apache/sis/util/collection/DerivedList.java | 31 +- .../sis/util/collection/WeakValueHashMap.java | 6 +- .../sis/util/collection/WritableDerivedList.java | 143 +++++ .../sis/util/internal/shared/AbstractMap.java | 2 +- .../apache/sis/util/internal/shared/Numerics.java | 118 +++- .../org/apache/sis/util/resources/Vocabulary.java | 20 + .../sis/util/resources/Vocabulary.properties | 4 + .../sis/util/resources/Vocabulary_fr.properties | 4 + .../org/apache/sis/math/DecimalFunctionsTest.java | 32 +- .../{DerivedSetTest.java => DerivedListTest.java} | 66 +-- .../apache/sis/util/collection/DerivedMapTest.java | 1 + .../apache/sis/util/collection/DerivedSetTest.java | 1 + .../BilinearGrid.java | 3 +- .../{parametriccurvesurfaces => }/Cone.java | 4 +- .../{parametriccurvesurfaces => }/Cylinder.java | 4 +- .../main/org/apache/sis/geometries/Geometries.java | 24 +- .../ParametricCurveSurface.java | 2 +- .../apache/sis/geometries/{rhumb => }/Rhumb.java | 2 +- .../main/org/apache/sis/geometries/Sphere.java | 66 ++- .../apache/sis/geometries/math/AbstractAffine.java | 163 ++++++ .../apache/sis/geometries/math/AbstractArray.java | 21 +- .../apache/sis/geometries/math/AbstractCursor.java | 2 +- .../apache/sis/geometries/math/AbstractMatrix.java | 636 +++++++++++++++++++++ .../sis/geometries/math/AbstractSimilarity.java | 329 +++++++++++ .../apache/sis/geometries/math/AbstractTuple.java | 4 +- .../org/apache/sis/geometries/math/Affine.java | 122 ++++ .../org/apache/sis/geometries/math/Affine1D.java | 161 ++++++ .../org/apache/sis/geometries/math/Affine2D.java | 226 ++++++++ .../org/apache/sis/geometries/math/Affine3D.java | 365 ++++++++++++ .../org/apache/sis/geometries/math/Affine4D.java | 482 ++++++++++++++++ .../org/apache/sis/geometries/math/AffineND.java | 99 ++++ .../org/apache/sis/geometries/math/Affines.java} | 22 +- .../main/org/apache/sis/geometries/math/Array.java | 30 +- .../sis/geometries/math/ArrayConcatenated.java | 11 +- .../apache/sis/geometries/math/ArrayFactory.java | 278 +++++++++ .../sis/geometries/math/ArrayFactoryFFM.java | 489 ++++++++++++++++ .../{ArrayMemory.java => ArrayFactoryJava.java} | 428 ++++---------- .../sis/geometries/math/ArraySpliterator.java | 6 +- .../sis/geometries/math/ArrayUnmodifiable.java | 11 +- .../org/apache/sis/geometries/math/Cursor.java | 2 +- .../sis/geometries/math/CursorUnmodifiable.java | 8 +- .../main/org/apache/sis/geometries/math/Maths.java | 32 +- .../org/apache/sis/geometries/math/Matrices.java | 338 +---------- .../org/apache/sis/geometries/math/Matrix.java | 137 ++++- .../org/apache/sis/geometries/math/Matrix2D.java | 214 ++++++- .../org/apache/sis/geometries/math/Matrix3D.java | 471 ++++++++++++++- .../org/apache/sis/geometries/math/Matrix4D.java | 470 +++++++++++++-- .../org/apache/sis/geometries/math/MatrixND.java | 477 ++++++++++++++++ .../org/apache/sis/geometries/math/NDArray.java | 15 +- .../org/apache/sis/geometries/math/NDArrays.java | 83 +-- .../org/apache/sis/geometries/math/Quaternion.java | 53 +- .../apache/sis/geometries/math/Quaternions.java | 2 +- .../org/apache/sis/geometries/math/ReadOnly.java | 592 +++++++++++++++++++ .../org/apache/sis/geometries/math/Similarity.java | 200 +++++++ .../apache/sis/geometries/math/Similarity3D.java | 197 +++++++ .../apache/sis/geometries/math/SimilarityND.java | 195 +++++++ .../sis/geometries/math/SimplifiedTransform.java | 106 ++++ .../org/apache/sis/geometries/math/Transform.java | 134 +++++ .../{CursorUnmodifiable.java => Transform1D.java} | 54 +- .../main/org/apache/sis/geometries/math/Tuple.java | 237 +------- .../sis/geometries/math/TupleUnmodifiable.java | 24 +- .../org/apache/sis/geometries/math/Vector.java | 93 +-- .../org/apache/sis/geometries/math/Vector1D.java | 26 +- .../org/apache/sis/geometries/math/Vector2D.java | 26 +- .../org/apache/sis/geometries/math/Vector3D.java | 31 +- .../org/apache/sis/geometries/math/Vector4D.java | 42 +- .../org/apache/sis/geometries/math/VectorND.java | 63 +- .../org/apache/sis/geometries/math/Vectors.java | 30 +- .../apache/sis/geometries/math/package-info.java | 20 +- .../apache/sis/geometries/mesh/MeshPrimitive.java | 14 +- .../geometries/operation/GeometryOperations.java | 10 +- .../{ => operation}/simplify/TextureAtlas.java | 2 +- .../simplify/greedyinsert/Edge.java | 2 +- .../simplify/greedyinsert/TINBuilder.java | 2 +- .../simplify/greedyinsert/TinDistance.java | 2 +- .../simplify/greedyinsert/WTriangle.java | 8 +- .../{ => operation}/triangulate/EarClipping.java | 18 +- .../triangulate/delaunay/Delaunay.java | 2 +- .../triangulate/delaunay/OrientedEdge.java | 2 +- .../triangulate/delaunay/OrientedTriangle.java | 2 +- .../geometries/parametriccurvesurfaces/Sphere.java | 30 - .../processor/spatialedition/ToPrimitive.java | 2 +- .../AbstractArray.java => scene/Animation.java} | 67 +-- .../AnimationSampler.java} | 63 +- .../org/apache/sis/geometries/scene/Camera.java | 242 ++++++++ .../org/apache/sis/geometries/scene/Material.java | 627 ++++++++++++++++++++ .../org/apache/sis/geometries/scene/Model.java | 216 +++++++ .../apache/sis/geometries/scene/MorphTarget.java | 110 ++++ .../org/apache/sis/geometries/scene/Sampler.java | 172 ++++++ .../org/apache/sis/geometries/scene/SceneNode.java | 510 +++++++++++++++++ .../{math/AbstractArray.java => scene/Skin.java} | 73 ++- .../org/apache/sis/geometries/scene/Surface.java | 117 ++++ .../org/apache/sis/geometries/scene/Texture.java | 142 +++++ .../apache/sis/geometries/scene}/package-info.java | 12 +- .../geometries/splinesurfaces/BSplineSurface.java | 2 +- .../apache/sis/geometries/math/ArrayNbTest.java | 2 +- .../apache/sis/geometries/math/ArrayNdTest.java | 2 +- .../apache/sis/geometries/math/ArrayNfTest.java | 2 +- .../apache/sis/geometries/math/ArrayNiTest.java | 2 +- .../apache/sis/geometries/math/ArrayNlTest.java | 2 +- .../apache/sis/geometries/math/ArrayNsTest.java | 2 +- .../apache/sis/geometries/math/ArrayNubTest.java | 2 +- .../apache/sis/geometries/math/ArrayNuiTest.java | 2 +- .../apache/sis/geometries/math/ArrayNusTest.java | 2 +- .../apache/sis/geometries/math/NDArraysTest.java | 4 +- .../apache/sis/geometries/math/QuaternionTest.java | 35 +- .../{ => operation}/simplify/TextureAtlasTest.java | 2 +- .../triangulate/delaunay/DelaunayTest.java | 2 +- .../triangulate/delaunay/OrientedEdgeTest.java | 2 +- .../main/org/apache/sis/map/MapItem.java | 8 +- .../apache/sis/storage/geoheif/FromImageIO.java | 9 +- .../main/org/apache/sis/storage/geoheif/Image.java | 3 +- .../apache/sis/storage/geoheif/ImageResource.java | 34 +- .../src/org.apache.sis.gui/main/module-info.java | 2 +- .../main/org/apache/sis/gui/Widget.java | 8 +- .../apache/sis/gui/controls/FormatApplicator.java | 6 +- .../apache/sis/gui/controls/SyncWindowList.java | 6 +- .../apache/sis/gui/controls/ValueColorMapper.java | 12 +- .../apache/sis/gui/coverage/BandRangeTable.java | 8 +- .../apache/sis/gui/coverage/CoverageCanvas.java | 335 +++++++++-- .../apache/sis/gui/coverage/CoverageControls.java | 155 +++-- .../apache/sis/gui/coverage/CoverageExplorer.java | 36 +- .../apache/sis/gui/coverage/CoverageStyling.java | 16 +- .../apache/sis/gui/coverage/GridSliceSelector.java | 12 +- .../sis/gui/coverage/ImagePropertyExplorer.java | 6 +- ...IsolineRenderer.java => IsolineController.java} | 82 ++- .../apache/sis/gui/coverage/StyleController.java | 173 ++++++ .../sis/gui/coverage/StyledRenderingData.java | 6 +- .../apache/sis/gui/coverage/TileMatrixSetPane.java | 499 ++++++++++++++++ .../apache/sis/gui/coverage/ViewAndControls.java | 22 +- .../org/apache/sis/gui/coverage/package-info.java | 2 +- .../main/org/apache/sis/gui/dataset/LogViewer.java | 5 +- .../org/apache/sis/gui/dataset/ResourceCell.java | 138 +++-- .../apache/sis/gui/dataset/ResourceExplorer.java | 52 +- .../org/apache/sis/gui/dataset/ResourceItem.java | 178 ++++-- .../org/apache/sis/gui/dataset/ResourceTree.java | 50 +- .../org/apache/sis/gui/dataset/RootResource.java | 37 +- .../org/apache/sis/gui/dataset/WindowHandler.java | 27 +- .../org/apache/sis/gui/dataset/package-info.java | 2 +- .../apache/sis/gui/internal/AlignedTableCell.java | 87 +++ .../apache/sis/gui/internal/BackgroundThreads.java | 8 +- .../apache/sis/gui/internal/DataStoreOpener.java | 57 +- .../apache/sis/gui/internal/ExceptionReporter.java | 36 +- .../org/apache/sis/gui/internal/GUIUtilities.java | 16 - .../org/apache/sis/gui/internal/LogHandler.java | 2 +- .../org/apache/sis/gui/internal/Resources.java | 20 + .../apache/sis/gui/internal/Resources.properties | 4 + .../sis/gui/internal/Resources_fr.properties | 4 + .../apache/sis/gui/internal/ShapeConverter.java | 153 +++++ .../main/org/apache/sis/gui/internal/Styles.java | 6 + .../apache/sis/gui/internal/io/FileAccessView.java | 4 +- .../org/apache/sis/gui/map/GestureFollower.java | 18 +- .../main/org/apache/sis/gui/map/MapCanvas.java | 43 +- .../main/org/apache/sis/gui/map/MapCanvasAWT.java | 14 +- .../main/org/apache/sis/gui/map/MapMenu.java | 39 +- .../main/org/apache/sis/gui/map/StatusBar.java | 165 ++++-- .../main/org/apache/sis/gui/map/package-info.java | 2 +- .../apache/sis/gui/map/style/ItemController.java | 89 +++ .../apache/sis/gui/map/style/MapContextView.java | 143 +++++ .../main/org/apache/sis/gui/map/style/MapItem.java | 35 +- .../org/apache/sis/gui/map/style/MapLayer.java | 37 +- .../org/apache/sis/gui/map/style/package-info.java | 14 +- .../sis/gui/metadata/IdentificationInfo.java | 2 +- .../apache/sis/gui/metadata/MetadataSummary.java | 8 +- .../org/apache/sis/gui/referencing/MenuSync.java | 57 +- .../gui/referencing/RecentReferenceSystems.java | 103 ++-- .../apache/sis/gui/referencing/package-info.java | 2 +- .../sis/gui/controls/ValueColorMapperApp.java | 7 +- .../main/org/apache/sis/storage/gdal/GDAL.java | 147 +---- .../apache/sis/storage/gdal/GDALStoreProvider.java | 126 +++- .../org/apache/sis/storage/gdal/TiledCoverage.java | 12 +- .../org/apache/sis/storage/gdal/TiledResource.java | 33 +- .../org/apache/sis/storage/gdal/package-info.java | 2 +- .../apache/sis/storage/panama/LibraryLoader.java | 192 ++++--- .../sis/storage/panama/LibraryReference.java | 207 +++++++ .../apache/sis/storage/panama/LibraryStatus.java | 51 +- .../apache/sis/storage/panama/NativeFunctions.java | 91 +-- .../org/apache/sis/storage/gdal/GDALStoreTest.java | 3 +- 294 files changed, 17472 insertions(+), 3629 deletions(-) diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java index 0000000000,a132628bb7..abfcbcd9d2 mode 000000,100644..100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TileMatrixSetFormat.java @@@ -1,0 -1,521 +1,524 @@@ + /* + * 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.tiling; + + import java.io.PrintWriter; + import java.io.StringWriter; + import java.io.IOException; + import java.io.UncheckedIOException; + import java.text.Format; + import java.util.Locale; + import java.util.List; + import java.util.ArrayList; + import java.util.Map; + import java.util.HashMap; + import java.util.TimeZone; + import java.util.Objects; + import java.util.Optional; + import java.util.Arrays; + import java.util.StringJoiner; + import java.text.NumberFormat; + import java.text.ParseException; + import java.text.ParsePosition; + import org.opengis.util.GenericName; + import org.opengis.referencing.crs.CoordinateReferenceSystem; + import org.opengis.referencing.operation.TransformException; + import org.opengis.metadata.extent.GeographicBoundingBox; + import org.apache.sis.util.ArraysExt; + import org.apache.sis.util.CharSequences; + import org.apache.sis.util.logging.Logging; + import org.apache.sis.util.resources.Errors; + import org.apache.sis.util.resources.Vocabulary; + import org.apache.sis.util.internal.shared.Numerics; + import org.apache.sis.util.collection.TableColumn; + import org.apache.sis.util.collection.TreeTableFormat; + import org.apache.sis.util.collection.BackingStoreException; + import org.apache.sis.referencing.IdentifiedObjects; + import org.apache.sis.coverage.grid.GridExtent; + import org.apache.sis.coverage.grid.IncompleteGridGeometryException; + import org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox; + import org.apache.sis.io.CompoundFormat; + import org.apache.sis.io.TableAppender; + + + /** + * Formats Tile Matrix Sets (<abbr>TMS</abbr>) in a tabular format. + * This format assumes a monospaced font and a character encoding which supports the drawing of box characters, + * such as <abbr>UTF</abbr>-8. + * + * <h2>Limitations</h2> + * <ul> + * <li>The current implementation can only format tile matrices — parsing is supported.</li> + * <li>{@code TileMatrixSetFormat}, like most {@code java.text.Format} subclasses, is not thread-safe.</li> + * </ul> + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.7 + * @since 1.7 + */ + public class TileMatrixSetFormat extends CompoundFormat<TileMatrixSet> { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 2612127094991996016L; + + /** + * Helper class for string representation of an image pyramid as a table. + * Each instance describes one table row for one {@link ImageTileMatrix}. + */ + private static final class Row { + /** The tile matrix identifier. */ + final String identifier; + + /** The tile matrix resolution in each <abbr>CRS</abbr> dimensions. */ + final double[] resolution; + + /** The string representations of the tile matrix resolution. */ + final String[] formattedResolution; + + /** The number of tiles in each grid dimension. */ + final String[] tileCount; + + /** The tile sizes in pixels. */ + final String[] tileSize; + + /** The extent of the tiling scheme. */ + private final GridExtent tilingScheme; + + /** + * Creates one row in the table for the given matrix. + * + * @param matrix the matrix for which to store information. + * @param integerFormat a number format configured for integer values. + * @throws BackingStoreException if an error occurred while extracting information. + * @throws IncompleteGridGeometryException if the tiling scheme has not extent or resolution. + * Tile matrices with such tiling scheme should not have been constructed in first place. + */ + Row(final TileMatrix matrix, final NumberFormat integerFormat) { + final GenericName id = matrix.getIdentifier(); + identifier = (id != null) ? id.toString() : ""; + resolution = matrix.getResolution(); + formattedResolution = new String[resolution.length]; + tilingScheme = matrix.getTilingScheme().getExtent(); + tileCount = new String[tilingScheme.getDimension()]; + for (int i=0; i<tileCount.length; i++) { + tileCount[i] = integerFormat.format(tilingScheme.getSize(i)); + } + final int[] ts = ImageTileMatrix.getTileSize(matrix); + tileSize = new String[ts != null ? ts.length : 0]; + for (int i=0; i<tileSize.length; i++) { + tileSize[i] = integerFormat.format(ts[i]); + } + } + + /** + * Updates the axis name in the dimension <var>i</var> of the grid extent. + * This method verifies that grid dimensions in all rows have the same axis names. + */ + private void searchCommonAxisName(final String[] gridAxes, final int i) { + tilingScheme.getAxisType(i).ifPresent((axis) -> { - final String name = axis.identifier().orElseGet(() -> axis.name().toLowerCase(Locale.US)); ++ String name = axis.identifier(); ++ if (name == null) { ++ name = axis.name().toLowerCase(Locale.US); ++ } + final String current = gridAxes[i]; + if (current == null) { + gridAxes[i] = name; + } else if (!current.equals(name)) { + gridAxes[i] = ""; + } + }); + } + + /** + * Creates the string representation of the resolutions in all resolution columns of all rows. + * This method computes the number of fraction digits based on the resolution values of all rows. + * + * @param rows the rows in which to format the resolutions. + * @param format the number formats to use. Its number of fraction digits will be updated. + * @return the maximal number of resolution values found in all rows. + * This value should be equal to the number of dimensions in the <abbr>CRS</abbr>. + */ + static int formatResolutions(final List<Row> rows, final NumberFormat format) { + final var values = new double[rows.size()]; + for (int i=0; ; i++) { + int count = 0; + for (final Row row : rows) { + if (i < row.resolution.length) { + values[count++] = row.resolution[i]; + } + } + if (count == 0) return i; + final int n = Numerics.suggestFractionDigits(ArraysExt.resize(values, count)); + format.setMinimumFractionDigits(n); + format.setMaximumFractionDigits(n); + final int column = i; // Because lambda requires final values. + final String[] formatted = Numerics.formatAndTrimTrailingZeros(format, values.length, (j) -> { + final double[] resolution = rows.get(j).resolution; + return (column < resolution.length) ? resolution[column] : Double.NaN; + }); + for (int j=0; j<values.length; j++) { + final String[] resolution = rows.get(j).formattedResolution; + if (i < resolution.length) { + resolution[i] = formatted[j]; + } + } + } + } + } + + /** + * The error that occurred while formatting a Tile Matrix Set. + */ + private transient Throwable error; + + /** + * Creates a new formatter using the given locale and timezone. + * + * @param locale the locale for the new {@code Format}, or {@code null} for {@code Locale.ROOT}. + * @param timezone the timezone, or {@code null} for UTC. + */ + public TileMatrixSetFormat(final Locale locale, final TimeZone timezone) { + super(locale, timezone); + } + + /** + * Returns the type of values formatted by this {@code Format} instance. + * + * @return the type of values formatted by this {@code Format} instance. + */ + @Override + public final Class<TileMatrixSet> getValueType() { + return TileMatrixSet.class; + } + + /** + * Formats the properties of the given Tile Matrix Set for presentation before the main table. + * The properties are formatted as {@link String}s using the locale given at construction time. + * The returned map contains the following entries if the corresponding properties were found: + * + * <table class="sis"> + * <caption>Tile Matrix Set (<abbr>TMS</abbr>) formatted properties</caption> + * <tr><th>Key</th> <th>Value type</th> <th>Description</th></tr> + * <tr><td>{@code "identifier"}</td> <td>{@link String}</td> <td>Identifier of the <abbr>TMS</abbr>.</td></tr> + * <tr><td>{@code "referencing"}</td><td>{@link String}</td> <td>Name of the <abbr>CRS</abbr>, or reason why not available.</td></tr> + * <tr><td>{@code "crs"}</td> <td>{@link CoordinateReferenceSystem}</td> <td>The <abbr>CRS</abbr>.</td></tr> + * <tr><td>{@code "bbox"}</td> <td>{@link DefaultGeographicBoundingBox}</td> <td>Bounding box of the <abbr>TMS</abbr>.</td></tr> + * </table> + * + * <p>If the {@code "crs"} property is non-null, then the {@code "referencing"} property is the <abbr>CRS</abbr> name. + * Conversely, if the {@code "crs"} property is null, then the {@code "referencing"} property is a message saying that + * the <abbr>CRS</abbr> was not available or the reason why it was not available.</p> + * + * The returned properties can be completed by a call to {@link #formatTable(Iterable, Map)}. + * + * @param matrices the tile matrices to format. + * @return properties of the header of the given tile matrix set. + */ + public Map<String, Object> formatHeader(final TileMatrixSet matrices) { + final var addTo = new HashMap<String, Object>(); + addTo.put("identifier", matrices.getIdentifier()); + try { + try { + final CoordinateReferenceSystem crs = matrices.getCoordinateReferenceSystem(); + addTo.put("referencing", IdentifiedObjects.getDisplayName(crs, getLocale())); + addTo.put("crs", crs); + } catch (IncompleteGridGeometryException e) { + addTo.put("referencing", e.getLocalizedMessage()); + } + matrices.getEnvelope().ifPresent((envelope) -> { + try { + final var bbox = new DefaultGeographicBoundingBox(); + bbox.setBounds(envelope); + bbox.setInclusion(null); + addTo.put("bbox", bbox); + } catch (TransformException e) { + // Ignore because this exception may be normal if the envelope has no spatial component. + Logging.ignorableException(ImageTileMatrix.LOGGER, TileMatrixSetFormat.class, "format", e); + } + }); + } catch (BackingStoreException e) { + // The CRS or envelope may be missing in the header, but we may still be able to format the table. + addError(e.getCause()); + } + addTo.values().removeIf(Objects::isNull); + return addTo; + } + + /** + * Formats the properties of the given Tile Matrices in a way suitable to a tabular format. + * The properties are formatted as {@link String}s using the locale given at construction time. + * The returned map contains the following entries if the corresponding properties were found: + * + * <table class="sis"> + * <caption>Tile Matrices formatted properties</caption> + * <tr><th>Key</th> <th>Value type</th> <th>Description</th></tr> + * <tr><td>{@code "identifiers"}</td> <td>{@code String[]}</td> <td>Column of the identifier of each Tile Matrix.</td></tr> + * <tr><td>{@code "resolutions"}</td> <td>{@code String[][]}</td> <td>Columns of the resolution of each Tile Matrix.</td></tr> + * <tr><td>{@code "tileCounts"}</td> <td>{@code String[][]}</td> <td>Columns of the number of tiles of each Tile Matrix.</td></tr> + * <tr><td>{@code "tileSizes"}</td> <td>{@code String[][]}</td> <td>Columns of the tile size of each Tile Matrix.</td></tr> + * <tr><td>{@code "gridAxes"}</td> <td>{@code String[]}</td> <td>Name of grid axes.</td></tr> + * </table> + * + * The {@code "tileSizes"} property may be absent if not applicable. + * Implementations other than the default implementation may also choose to add or remove more properties. + * + * <p>This method returns the number of rows in the table to format. + * The length of the {@code "identifiers"} array is that number of rows. + * All other arrays have a length equal to the number of columns in a group of columns, + * which is 2 in the usual case of two-dimensional Tile Matrix Sets. + * The {@code "resolutions[i]"}, {@code "tileCounts[i]"} and {@code "tileSizes[i]"} arrays, + * where <var>i</var> is a column index, all have a length equal to the number of rows. + * Some element may be {@code null} if the corresponding data could not be extracted.</p> + * + * @param matrices the tile matrices to format. + * @param addTo where to put the properties of the given tile matrix set. + * @return number of rows in the table to format. + */ + public int formatTable(final Iterable<? extends TileMatrix> matrices, final Map<String, Object> addTo) { + final var rows = new ArrayList<Row>(); + int gridDimension = 0, sizeDimension = 0; + final var integerFormat = (NumberFormat) getFormat(Long.class); + for (final TileMatrix matrix : matrices) try { + final var row = new Row(matrix, integerFormat); + gridDimension = Math.max(gridDimension, row.tileCount.length); + sizeDimension = Math.max(sizeDimension, row.tileSize.length); + rows.add(row); // Add only on success. + } catch (RuntimeException e) { + addError(e); // Skip the row. Next row will be tried. + } + final int crsDimension = Row.formatResolutions(rows, (NumberFormat) getFormat(Double.class)); + final int numRows = rows.size(); + final var identifiers = new String[numRows]; + final var resolutions = new String[ crsDimension][numRows]; + final var tileCounts = new String[gridDimension][numRows]; + final var tileSizes = new String[sizeDimension][numRows]; + final var gridAxes = new String[gridDimension]; + for (int j=0; j<numRows; j++) { + final Row row = rows.get(j); + identifiers[j] = row.identifier; + for (int i = Math.min(crsDimension, row.resolution.length); --i >= 0;) { + resolutions[i][j] = row.formattedResolution[i]; + } + for (int i = Math.min(gridDimension, row.tileCount.length); --i >= 0;) { + tileCounts[i][j] = row.tileCount[i]; + row.searchCommonAxisName(gridAxes, i); + } + for (int i = Math.min(sizeDimension, row.tileSize.length); --i >= 0;) { + tileSizes[i][j] = row.tileSize[i]; + } + } + if (numRows != 0) addTo.put("identifiers", identifiers); + if (crsDimension != 0) addTo.put("resolutions", resolutions); + if (gridDimension != 0) addTo.put("tileCounts", tileCounts); + if (sizeDimension != 0) addTo.put("tileSizes", tileSizes); + for (int i=0; i<gridAxes.length; i++) { + final String name = gridAxes[i]; + if (name != null && name.isBlank()) { + gridAxes[i] = null; + } + } + if (!ArraysExt.allEquals(gridAxes, null)) { + addTo.put("gridAxes", gridAxes); + } + return numRows; + } + + /** + * Returns localized resources for the table header. + */ + private Vocabulary vocabulary() { + return Vocabulary.forLocale(getLocale()); + } + + /** + * Formats a textual representation of the given tile matrices. + * This method delegates to {@link #format(TileMatrixSet, Appendable)} + * with a temporary buffer. + * + * @param matrices the tile matrices to format. + * @return a string representation of the given Tile Matrix Set. + */ + public String format(TileMatrixSet matrices) { + return format(matrices, false); + } + + /** + * Formats the given tile matrices, optionally with a report of exceptions. + * + * @param matrices the tile matrices to format. + * @param reportError whether to report exceptions. + * @return a string representation of the given Tile Matrix Set. + */ + final String format(final TileMatrixSet matrices, final boolean reportError) { + final var buffer = new StringBuilder(1000); + try { + format(matrices, buffer); + if (!reportError || error == null) { + return buffer.toString(); + } + final var writer = new StringWriter(buffer.length()).append(buffer); + vocabulary().appendLabel(Vocabulary.Keys.Warnings, writer); + error.printStackTrace(new PrintWriter(writer.append(' '))); + return writer.append(System.lineSeparator()).toString(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** + * Formats a textual representation of the given tile matrices. + * First, a header is formatted with the <abbr>TMS</abbr> identifier, + * the Coordinate Reference System and the geographic bounding box. + * Then, each tile matrix is formatted as a row in a table. + * + * @param matrices the tile matrices to format. + * @param toAppendTo where to format the object. + * @throws IOException if an error occurred while writing to the given appendable. + */ + @Override + public void format(final TileMatrixSet matrices, final Appendable toAppendTo) throws IOException { + final Vocabulary vocabulary = vocabulary(); + final Map<String, Object> properties = formatHeader(matrices); + final int numRows = formatTable(matrices.getTileMatrices().values(), properties); + Object value = properties.get("identifier"); + if (value != null) { + toAppendTo.append(vocabulary.getString(Vocabulary.Keys.TileMatrixSets)).append(' ') + .append(vocabulary.getString(Vocabulary.Keys.Quoted_1, value)) + .append(System.lineSeparator()); + } + value = properties.get("referencing"); + if (value != null) { + if (properties.get("crs") != null) { + vocabulary.appendLabel(Vocabulary.Keys.ReferenceSystem, toAppendTo); + toAppendTo.append(' '); + } + toAppendTo.append(value.toString()).append(System.lineSeparator()); + } + value = properties.get("bbox"); + if (value != null) { + final var bbox = (DefaultGeographicBoundingBox) value; + final var mf = (TreeTableFormat) getFormat(GeographicBoundingBox.class); + mf.setColumns(TableColumn.NAME, TableColumn.VALUE); + toAppendTo.append(mf.format(bbox.asTreeTable())); + } + /* + * The header was formatted by above code. The following code formats the main table. + * First, we get the values to format in all columns and prepend the number of spaces + * needed for right alignment. + */ + final var identifiers = (String[]) properties.get("identifiers"); + final var resolutions = (String[][]) properties.get("resolutions"); + final var tileCounts = (String[][]) properties.get("tileCounts"); + final var tileSizes = (String[][]) properties.get("tileSizes"); + String[][][] columnGroups = {resolutions, tileCounts, tileSizes}; + columnGroups = ArraysExt.resize(columnGroups, ArraysExt.removeNulls(columnGroups)); + for (final String[][] columns : columnGroups) { + for (final String[] column : columns) { + Arrays.stream(column).mapToInt(String::length).max().ifPresent((length) -> { + for (int j=0; j<column.length; j++) { + String e = column[j]; + column[j] = CharSequences.spaces(length - e.length()) + e; + } + }); + } + } + final var table = new TableAppender(toAppendTo); + table.appendHorizontalSeparator(); + if (identifiers != null) table.append(vocabulary.getString(Vocabulary.Keys.Identifier)).nextColumn(); + if (resolutions != null) table.append(vocabulary.getString(Vocabulary.Keys.Resolution)).nextColumn(); + if (tileCounts != null) table.append(vocabulary.getString(Vocabulary.Keys.TileCount)) .nextColumn(); + if (tileSizes != null) table.append(vocabulary.getString(Vocabulary.Keys.TileSize)) .nextColumn(); + table.appendHorizontalSeparator(); + for (int j=0; j<numRows; j++) { + if (identifiers != null) { + table.setCellAlignment(TableAppender.ALIGN_LEFT); + table.append(identifiers[j]).nextColumn(); + table.setCellAlignment(TableAppender.ALIGN_RIGHT); + } + for (final String[][] columns : columnGroups) { + final var joiner = new StringJoiner(" × "); + for (String[] column : columns) { + joiner.add(column[j]); + } + table.append(joiner.toString()).nextColumn(); + } + table.nextLine(); + } + table.appendHorizontalSeparator(); + table.flush(); + } + + /** + * Not supported. + * + * @return currently never return. + * @throws ParseException currently always thrown. + * @hidden Not implemented. + */ + @Override + public TileMatrixSet parse(CharSequence text, ParsePosition pos) throws ParseException { + throw new ParseException(Errors.forLocale(getLocale()) + .getString(Errors.Keys.UnsupportedOperation_1, "parse"), pos.getIndex()); + } + + /** + * Creates a new format to use for formatting values of the given type. + * This method adds the creation of {@link TreeTableFormat}. + * + * @param valueType the base type of values to parse or format. + * @return the format to use for parsing of formatting values of the given type, or {@code null} if none. + * @hidden the addition compared to parent class are implementation details. + */ + @Override + protected Format createFormat(final Class<?> valueType) { + if (valueType == GeographicBoundingBox.class) { + return new TreeTableFormat(getLocale(), getTimeZone()); + } + return super.createFormat(valueType); + } + + /** + * Records that an error occurred. + * + * @param e the error that occurred. + */ + private void addError(final Throwable e) { + if (error == null) { + error = e; + } else { + error.addSuppressed(e); + } + } + + /** + * If an error occurred while formatting the Tile Matrix Set, returns that error. + * + * @return the error that occurred during formatting. + */ + public Optional<Throwable> getError() { + return Optional.ofNullable(error); + } + + /** + * Clears the error status. This method should be invoked if the same {@code TileMatrixSetFormat} + * instance is used for formatting many {@code TileMatrixSet} instances. + */ + public void clear() { + error = null; + } + } diff --cc endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java index 5b9a4d65e8,e820ce116b..9569abb254 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/tiling/TiledGridCoverage.java @@@ -55,11 -53,16 +56,13 @@@ import org.opengis.geometry.MismatchedD // Specific to the geoapi-3.1 and geoapi-4.0 branches: import org.opengis.coverage.CannotEvaluateException; -// Specific to the geoapi-4.0 branch: -import org.opengis.coordinate.MismatchedDimensionException; - /** - * Base class of grid coverage read from a resource where data are stored in tiles. + * Base class of grid coverages that are read from resources with data stored in tiles. * This grid coverage may represent only a subset of the coverage resource. - * Tiles are read from the storage only when first needed. + * Tiles are read from the storage when first needed, then cached using weak references. + * The reading of tiles is done by the {@link #readTiles(TileIterator)} method, + * which must be defined by subclasses. * * <h2>Cell coordinates</h2> * When there is no subsampling, this coverage uses the same cell coordinates as the originating resource. diff --cc endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArgumentChecks.java index cafa992617,ca28d60e38..c393e45edb --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArgumentChecks.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/ArgumentChecks.java @@@ -773,7 -773,31 +773,31 @@@ public final class ArgumentChecks if (indices != null) { final int dimension = indices.length; if (dimension != expected) { - throw new MismatchedDimensionException(Errors.format( + throw new org.opengis.geometry.MismatchedDimensionException(Errors.format( + Errors.Keys.MismatchedDimension_3, name, expected, dimension)); + } + } + } + + /** + * Ensures that the given array of indices, if non-null, has the expected number of dimensions + * (taken as its length). This method does nothing if the given array is null. + * + * @param name the name of the argument to be checked. Used only if an exception is thrown. + * @param expected the expected number of dimensions. + * @param indices the array of indices to check for its number of dimensions, or {@code null}. + * @throws MismatchedDimensionException if the given array of indices is non-null and does not have + * the expected number of dimensions (taken as its length). + * + * @since 1.7 + */ + public static void ensureDimensionMatches(final String name, final int expected, final long[] indices) + throws MismatchedDimensionException + { + if (indices != null) { + final int dimension = indices.length; + if (dimension != expected) { + throw new MismatchedDimensionException(Errors.format( Errors.Keys.MismatchedDimension_3, name, expected, dimension)); } } diff --cc incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Sphere.java index bceafeae88,4160a45cc5..9b8faafe56 --- a/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Sphere.java +++ b/incubator/src/org.apache.sis.geometry/main/org/apache/sis/geometries/Sphere.java @@@ -24,6 -25,10 +25,10 @@@ import org.apache.sis.geometries.math.V import org.apache.sis.geometries.math.Vectors; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.util.ArgumentChecks; + import static org.opengis.annotation.Specification.ISO_19107; + import org.opengis.annotation.UML; + import org.opengis.geometry.DirectPosition; -import org.opengis.metadata.Identifier; ++import org.opengis.referencing.ReferenceIdentifier; /** @@@ -120,4 -127,61 +127,61 @@@ public final class Sphere extends Abstr return env; } + // methods from ParametricCurveSurface + + @Override + public int getRows() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public int getColumns() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List<DirectPosition> getControlPoints() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List<DirectPosition> getDataPoints() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public GeometryType getHorizontalCurveType() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public GeometryType getVerticalCurveType() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public List<Knot> getKnots() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Curve getHorizontalCurve(double v) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Curve getVerticalCurve(double u) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public DirectPosition getSurface(double u, double v) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override - public Identifier getName() { ++ public ReferenceIdentifier getName() { + throw new UnsupportedOperationException("Not supported yet."); + } + }
