This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 0e2089b1900656085bf926a2fe60ebeaf7363bc7 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Sat Apr 15 17:50:45 2023 +0200 If the resources to aggregate are instances of `MemoryGridResource`, aggregate directly the underlying `GridCoverage` instances. --- .../aggregate/BandAggregateGridResource.java | 39 ++++++++++++++-- .../sis/storage/aggregate/CoverageAggregator.java | 52 ++++++++++++++++++---- .../aggregate/BandAggregateGridResourceTest.java | 34 ++++++++++++-- 3 files changed, 110 insertions(+), 15 deletions(-) diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/BandAggregateGridResource.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/BandAggregateGridResource.java index f7d502f9b8..301f28a259 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/BandAggregateGridResource.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/BandAggregateGridResource.java @@ -28,13 +28,14 @@ import org.apache.sis.coverage.grid.GridCoverageProcessor; import org.apache.sis.coverage.grid.IllegalGridGeometryException; import org.apache.sis.internal.coverage.MultiSourceArgument; import org.apache.sis.internal.coverage.RangeArgument; +import org.apache.sis.internal.storage.MetadataBuilder; +import org.apache.sis.internal.storage.MemoryGridResource; import org.apache.sis.storage.Resource; import org.apache.sis.storage.GridCoverageResource; import org.apache.sis.storage.AbstractGridCoverageResource; import org.apache.sis.storage.RasterLoadingStrategy; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.event.StoreListeners; -import org.apache.sis.internal.storage.MetadataBuilder; import org.apache.sis.internal.util.UnmodifiableArrayList; import org.apache.sis.util.collection.BackingStoreException; @@ -139,7 +140,7 @@ final class BandAggregateGridResource extends AbstractGridCoverageResource imple * @param parentListeners listeners of the parent resource, or {@code null} if none. * @param sources resources whose bands shall be aggregated, in order. At least one resource must be provided. * @param bandsPerSource sample dimensions for each source. May be {@code null} or may contain {@code null} elements. - * @param processor the processor to use for creating grid coverages, or {@code null} for a default processor. + * @param processor the processor to use for creating grid coverages. * @throws DataStoreException if an error occurred while fetching the grid geometry or sample dimensions from a resource. * @throws IllegalGridGeometryException if a grid geometry is not compatible with the others. * @throws IllegalArgumentException if some band indices are duplicated or outside their range of validity. @@ -157,7 +158,7 @@ final class BandAggregateGridResource extends AbstractGridCoverageResource imple this.gridGeometry = aggregate.domain(BandAggregateGridResource::domain); this.sampleDimensions = List.copyOf(aggregate.ranges()); this.bandsPerSource = aggregate.bandsPerSource(false); - this.processor = (processor != null) ? processor : new GridCoverageProcessor(); + this.processor = processor; } catch (BackingStoreException e) { throw e.unwrapOrRethrow(DataStoreException.class); } @@ -201,6 +202,38 @@ final class BandAggregateGridResource extends AbstractGridCoverageResource imple } } + /** + * Creates a new range aggregation of grid coverage resources, + * potentially unwrapping in-memory resources for efficiency. + * + * @param parentListeners listeners of the parent resource, or {@code null} if none. + * @param sources resources whose bands shall be aggregated, in order. At least one resource must be provided. + * @param bandsPerSource sample dimensions for each source. May be {@code null} or may contain {@code null} elements. + * @param processor the processor to use for creating grid coverages. + * @return the band aggregated grid resource. + * @throws DataStoreException if an error occurred while fetching the grid geometry or sample dimensions from a resource. + * @throws IllegalGridGeometryException if a grid geometry is not compatible with the others. + * @throws IllegalArgumentException if some band indices are duplicated or outside their range of validity. + */ + static GridCoverageResource create(final StoreListeners parentListeners, + final GridCoverageResource[] sources, final int[][] bandsPerSource, + final GridCoverageProcessor processor) throws DataStoreException + { + GridCoverageResource source = sources[0]; + if (source instanceof MemoryGridResource) { + final var coverages = new GridCoverage[sources.length]; + int i = 0; + do { + coverages[i] = ((MemoryGridResource) source).coverage; + if (++i >= sources.length) { + GridCoverage coverage = processor.aggregateRanges(coverages, bandsPerSource); + return new MemoryGridResource(parentListeners, coverage); + } + } while ((source = sources[i]) instanceof MemoryGridResource); + } + return new BandAggregateGridResource(parentListeners, sources, bandsPerSource, processor); + } + /** Not applicable to this implementation. */ @Override public Resource apply(MergeStrategy strategy) {return this;} diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/CoverageAggregator.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/CoverageAggregator.java index edd4825342..828c045584 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/CoverageAggregator.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/aggregate/CoverageAggregator.java @@ -132,7 +132,7 @@ public final class CoverageAggregator extends Group<GroupBySample> { private GridCoverageProcessor processor; /** - * Creates an initially empty aggregator with no listeners and a default grid coverage processor. + * Creates an initially empty aggregator with no listeners. * * @since 1.4 */ @@ -141,7 +141,7 @@ public final class CoverageAggregator extends Group<GroupBySample> { } /** - * Creates an initially empty aggregator. + * Creates an initially empty aggregator with the given listeners. * * @param listeners listeners of the parent resource, or {@code null} if none. * This is usually the listeners of the {@link org.apache.sis.storage.DataStore}. @@ -151,6 +151,18 @@ public final class CoverageAggregator extends Group<GroupBySample> { aggregates = new HashMap<>(); } + /** + * Creates an initially empty aggregator with the given listeners and coverage processor. + * + * @param listeners listeners of the parent resource, or {@code null} if none. + * @param processor the processor to use if an operation needs to be applied on coverages, + * or {@code null} for a default processor. + */ + CoverageAggregator(final StoreListeners listeners, final GridCoverageProcessor processor) { + this(listeners); + this.processor = processor; + } + /** * Creates a name for this group for use in metadata (not a persistent identifier). * This is used only if this aggregator find resources having different sample dimensions. @@ -295,12 +307,32 @@ public final class CoverageAggregator extends Group<GroupBySample> { * This method combines homogeneous grid coverage resources by "stacking" their sample dimensions (bands). * The grid geometry is typically the same for all resources, but some variations described below are allowed. * The number of sample dimensions in the aggregated coverage is the sum of the number of sample dimensions in - * each individual resource, unless a subset of sample dimensions is specified. + * each individual resource. + * + * <p>This convenience method delegates to {@link #addRangeAggregate(GridCoverageResource[], int[][])}. + * See that method for more information on restrictions.</p> + * + * @param sources resources whose bands shall be aggregated, in order. + * @throws DataStoreException if an error occurred while fetching the grid geometry or sample dimensions from a resource. + * @throws IllegalGridGeometryException if a grid geometry is not compatible with the others. * - * <p>The {@code bandsPerSource} argument specifies the bands to select in each resource. - * That array can be {@code null} for selecting all bands in all resources, - * or may contain {@code null} elements for selecting all bands of the corresponding resource. - * An empty array element (i.e. zero band to select) discards the corresponding resource.</p> + * @see #getColorizer() + * @see GridCoverageProcessor#aggregateRanges(GridCoverage...) + * + * @since 1.4 + */ + public void addRangeAggregate(final GridCoverageResource... sources) throws DataStoreException { + addRangeAggregate(sources, (int[][]) null); + } + + /** + * Adds a resource whose range is the aggregation of the specified bands of a sequence of resources. + * This method performs the same work than {@link #addRangeAggregate(GridCoverageResource...)}, + * but with the possibility to specify the sample dimensions to retain in each source coverage. + * The {@code bandsPerSource} argument specifies the sample dimensions to keep, in order. + * That array can be {@code null} for selecting all sample dimensions in all source coverages, + * or may contain {@code null} elements for selecting all sample dimensions of the corresponding coverage. + * An empty array element (i.e. zero sample dimension to select) discards the corresponding source coverage. * * <h4>Restrictions</h4> * <ul> @@ -314,7 +346,7 @@ public final class CoverageAggregator extends Group<GroupBySample> { * * Some of those restrictions may be relaxed in future Apache SIS versions. * - * @param sources resources whose bands shall be aggregated, in order. At least one resource must be provided. + * @param sources resources whose bands shall be aggregated, in order. * @param bandsPerSource sample dimensions for each source. May be {@code null} or may contain {@code null} elements. * @throws DataStoreException if an error occurred while fetching the grid geometry or sample dimensions from a resource. * @throws IllegalGridGeometryException if a grid geometry is not compatible with the others. @@ -326,7 +358,9 @@ public final class CoverageAggregator extends Group<GroupBySample> { * @since 1.4 */ public void addRangeAggregate(final GridCoverageResource[] sources, final int[][] bandsPerSource) throws DataStoreException { - add(new BandAggregateGridResource(listeners, sources, bandsPerSource, processor())); + if (sources.length != 0) { + add(BandAggregateGridResource.create(listeners, sources, bandsPerSource, processor())); + } } /** diff --git a/storage/sis-storage/src/test/java/org/apache/sis/storage/aggregate/BandAggregateGridResourceTest.java b/storage/sis-storage/src/test/java/org/apache/sis/storage/aggregate/BandAggregateGridResourceTest.java index a1669ab993..9af75669e8 100644 --- a/storage/sis-storage/src/test/java/org/apache/sis/storage/aggregate/BandAggregateGridResourceTest.java +++ b/storage/sis-storage/src/test/java/org/apache/sis/storage/aggregate/BandAggregateGridResourceTest.java @@ -30,6 +30,7 @@ import org.apache.sis.coverage.grid.GridExtent; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.coverage.grid.GridCoverage; import org.apache.sis.coverage.grid.BufferedGridCoverage; +import org.apache.sis.coverage.grid.GridCoverageProcessor; import org.apache.sis.internal.storage.MemoryGridResource; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.GridCoverageResource; @@ -59,10 +60,16 @@ public final class BandAggregateGridResourceTest extends TestCase { */ private final GridGeometry domain; + /** + * The processor to give to {@link BandAggregateGridResource} constructor. + */ + private final GridCoverageProcessor processor; + /** * Creates a new test case. */ public BandAggregateGridResourceTest() { + processor = new GridCoverageProcessor(); domain = new GridGeometry(new GridExtent(WIDTH, HEIGHT), PixelInCell.CELL_CENTER, MathTransforms.identity(2), HardCodedCRS.WGS84); } @@ -75,8 +82,8 @@ public final class BandAggregateGridResourceTest extends TestCase { * @throws DataStoreException if an error occurred while fetching the grid geometry or sample dimensions from a resource. * @throws IllegalGridGeometryException if a grid geometry is not compatible with the others. */ - private static BandAggregateGridResource create(final GridCoverageResource... sources) throws DataStoreException { - return new BandAggregateGridResource(null, sources, null, null); + private BandAggregateGridResource create(final GridCoverageResource... sources) throws DataStoreException { + return new BandAggregateGridResource(null, sources, null, processor); } /** @@ -120,7 +127,7 @@ public final class BandAggregateGridResourceTest extends TestCase { final LocalName testName = Names.createLocalName(null, null, "test-name"); aggregation = new BandAggregateGridResource(null, new GridCoverageResource[] {firstAndSecondBands, thirdAndFourthBands, fifthAndSixthBands}, - new int[][] {null, new int[] {1, 0}, new int[] {1}}, null); + new int[][] {null, new int[] {1, 0}, new int[] {1}}, processor); aggregation.setIdentifier(testName); assertEquals(testName, aggregation.getIdentifier().orElse(null)); @@ -128,6 +135,27 @@ public final class BandAggregateGridResourceTest extends TestCase { assertAllPixelsEqual(aggregation.read(null, 2, 4), 104, 106); } + /** + * Tests aggregation of resources using {@link CoverageAggregator}. + * + * @throws DataStoreException if an error occurred while reading a resource. + */ + @Test + public void usingCoverageAggregator() throws DataStoreException { + final GridCoverageResource first = singleValuePerBand(17); + final GridCoverageResource second = singleValuePerBand(23); + final var aggregator = new CoverageAggregator(null, processor); + aggregator.addRangeAggregate(first, second); + /* + * If the result is not an instance of `MemoryGridResource`, + * this is a bug in `BandAggregateGridResource.create(…)`. + */ + final var aggregation = (MemoryGridResource) aggregator.build(null); + assertAllPixelsEqual(aggregation.read(null), 17, 23); + assertAllPixelsEqual(aggregation.read(null, 0), 17); + assertAllPixelsEqual(aggregation.read(null, 1), 23); + } + /** * Creates a new grid coverage resource with bands having the given values. * The length of the {@code bandValues} array is the number of bands to create.