You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by am...@apache.org on 2022/11/28 08:36:27 UTC

[sis] branch feat/resource-processor created (now cbf9f26ab9)

This is an automated email from the ASF dual-hosted git repository.

amanin pushed a change to branch feat/resource-processor
in repository https://gitbox.apache.org/repos/asf/sis.git


      at cbf9f26ab9 Resource : add ResourceProcessor with converted samples operation

This branch includes the following new commits:

     new cbf9f26ab9 Resource : add ResourceProcessor with converted samples operation

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sis] 01/01: Resource : add ResourceProcessor with converted samples operation

Posted by am...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

amanin pushed a commit to branch feat/resource-processor
in repository https://gitbox.apache.org/repos/asf/sis.git

commit cbf9f26ab9ac8170759dc1b7f5f5fc81a7489453
Author: jsorel <jo...@geomatys.com>
AuthorDate: Tue Sep 13 14:35:19 2022 +0200

    Resource : add ResourceProcessor with converted samples operation
---
 .../storage/ConvertedCoverageResource.java         | 166 +++++++++++++++++++++
 .../org/apache/sis/storage/ResourceProcessor.java  |  69 +++++++++
 .../storage/ConvertedCoverageResourceTest.java     |  74 +++++++++
 .../apache/sis/test/suite/StorageTestSuite.java    |   1 +
 4 files changed, 310 insertions(+)

diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConvertedCoverageResource.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConvertedCoverageResource.java
new file mode 100644
index 0000000000..1c80113270
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConvertedCoverageResource.java
@@ -0,0 +1,166 @@
+/*
+ * 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.internal.storage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Function;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridCoverageProcessor;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.storage.AbstractGridCoverageResource;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.Query;
+import org.apache.sis.storage.RasterLoadingStrategy;
+import org.apache.sis.storage.UnsupportedQueryException;
+import org.apache.sis.util.ArgumentChecks;
+import org.opengis.geometry.Envelope;
+import org.opengis.referencing.operation.MathTransform1D;
+import org.opengis.util.GenericName;
+
+/**
+ * Create a coverage resource with sample values converted by the given functions.
+ * The number of sample dimensions in the returned coverage is the length of the {@code converters} array,
+ * which must be greater than 0 and not greater than the number of sample dimensions in the source coverage.
+ * If the {@code converters} array length is less than the number of source sample dimensions,
+ * then all sample dimensions at index ≥ {@code converters.length} will be ignored.
+ *
+ * <h2>Sample dimensions customization</h2>
+ * By default, this method creates new sample dimensions with the same names and categories than in the
+ * previous coverage, but with {@linkplain org.apache.sis.coverage.Category#getSampleRange() sample ranges}
+ * converted using the given converters and with {@linkplain SampleDimension#getUnits() units of measurement}
+ * omitted. This behavior can be modified by specifying a non-null {@code sampleDimensionModifier} function.
+ * If non-null, that function will be invoked with, as input, a pre-configured sample dimension builder.
+ * The {@code sampleDimensionModifier} function can {@linkplain SampleDimension.Builder#setName(CharSequence)
+ * change the sample dimension name} or {@linkplain SampleDimension.Builder#categories() rebuild the categories}.
+ *
+ * <h2>Result relationship with source</h2>
+ * If the source coverage is backed by a {@link java.awt.image.WritableRenderedImage},
+ * then changes in the source coverage are reflected in the returned coverage and conversely.
+ *
+ * @see GridCoverageProcessor#convert(org.apache.sis.coverage.grid.GridCoverage, org.opengis.referencing.operation.MathTransform1D[], java.util.function.Function)
+ * @see ImageProcessor#convert(RenderedImage, NumberRange<?>[], MathTransform1D[], DataType, ColorModel)
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class ConvertedCoverageResource extends AbstractGridCoverageResource {
+
+    private final GridCoverageResource source;
+    private final MathTransform1D[] converters;
+    private final Function<SampleDimension.Builder, SampleDimension> sampleDimensionModifier;
+    private List<SampleDimension> sampleDimensions;
+
+    /**
+     *
+     * @param source the coverage resource for which to convert sample values.
+     * @param converters the transfer functions to apply on each sample dimension of the source coverage.
+     * @param sampleDimensionModifier a callback for modifying the {@link SampleDimension.Builder} default
+     *         configuration for each sample dimension of the target coverage, or {@code null} if none.
+     */
+    public ConvertedCoverageResource(GridCoverageResource source, MathTransform1D[] converters,
+            Function<SampleDimension.Builder, SampleDimension> sampleDimensionModifier) {
+        super(null, false);
+        ArgumentChecks.ensureNonNull("base", source);
+        ArgumentChecks.ensureNonNull("converters", converters);
+        this.source = source;
+        this.converters = converters;
+        this.sampleDimensionModifier = sampleDimensionModifier;
+    }
+
+    @Override
+    public GridGeometry getGridGeometry() throws DataStoreException {
+        return source.getGridGeometry();
+    }
+
+    @Override
+    public synchronized List<SampleDimension> getSampleDimensions() throws DataStoreException {
+        if (sampleDimensions == null) {
+            sampleDimensions = new ArrayList<>(source.getSampleDimensions());
+            if (this.sampleDimensionModifier != null) {
+                for (int i = 0; i < converters.length; i++) {
+                    final SampleDimension.Builder builder = new SampleDimension.Builder();
+                    final SampleDimension band = sampleDimensions.get(i);
+                    final MathTransform1D converter = converters[i];
+                    band.getBackground().ifPresent(builder::setBackground);
+                    band.getCategories().forEach((category) -> {
+                        if (category.isQuantitative()) {
+                            // Unit is assumed different as a result of conversion.
+                            builder.addQuantitative(category.getName(), category.getSampleRange(), converter, null);
+                        } else {
+                            builder.addQualitative(category.getName(), category.getSampleRange());
+                        }
+                    });
+                    builder.setName(band.getName());
+                    sampleDimensions.set(i, sampleDimensionModifier.apply(builder));
+                }
+            }
+            sampleDimensions = Collections.unmodifiableList(sampleDimensions);
+        }
+        return sampleDimensions;
+    }
+
+    @Override
+    public List<double[]> getResolutions() throws DataStoreException {
+        return source.getResolutions();
+    }
+
+    @Override
+    public GridCoverageResource subset(Query query) throws UnsupportedQueryException, DataStoreException {
+        final GridCoverageResource subset = source.subset(query);
+        return new ConvertedCoverageResource(subset, converters, sampleDimensionModifier);
+    }
+
+    @Override
+    public GridCoverage read(GridGeometry domain, int... range) throws DataStoreException {
+        final GridCoverage coverage = source.read(domain, range);
+        final MathTransform1D[] trs;
+        if (range != null) {
+            trs = new MathTransform1D[range.length];
+            for (int i = 0; i < trs.length; i++) {
+                trs[i] = converters[range[i]];
+            }
+        } else {
+            trs = converters.clone();
+        }
+        return new GridCoverageProcessor().convert(coverage, trs, sampleDimensionModifier);
+    }
+
+    @Override
+    public RasterLoadingStrategy getLoadingStrategy() throws DataStoreException {
+        return source.getLoadingStrategy();
+    }
+
+    @Override
+    public boolean setLoadingStrategy(RasterLoadingStrategy strategy) throws DataStoreException {
+        return source.setLoadingStrategy(strategy);
+    }
+
+    @Override
+    public Optional<Envelope> getEnvelope() throws DataStoreException {
+        return source.getEnvelope();
+    }
+
+    @Override
+    public Optional<GenericName> getIdentifier() throws DataStoreException {
+        return source.getIdentifier();
+    }
+
+}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceProcessor.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceProcessor.java
new file mode 100644
index 0000000000..f8228daadf
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/ResourceProcessor.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+import java.util.function.Function;
+import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.internal.storage.ConvertedCoverageResource;
+import org.opengis.referencing.operation.MathTransform1D;
+
+/**
+ * A predefined set of operations on resources as convenience methods.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.3
+ * @since 1.3
+ * @module
+ */
+public class ResourceProcessor implements Cloneable {
+
+    /**
+     * Creates a new processor with default configuration.
+     */
+    public ResourceProcessor(){
+
+    }
+
+    /**
+     * Returns a coverage resource with sample values converted by the given functions.
+     * The number of sample dimensions in the returned coverage is the length of the {@code converters} array,
+     * which must be greater than 0 and not greater than the number of sample dimensions in the source coverage.
+     * If the {@code converters} array length is less than the number of source sample dimensions,
+     * then all sample dimensions at index ≥ {@code converters.length} will be ignored.
+     *
+     * <h4>Sample dimensions customization</h4>
+     * By default, this method creates new sample dimensions with the same names and categories than in the
+     * previous coverage, but with {@linkplain org.apache.sis.coverage.Category#getSampleRange() sample ranges}
+     * converted using the given converters and with {@linkplain SampleDimension#getUnits() units of measurement}
+     * omitted. This behavior can be modified by specifying a non-null {@code sampleDimensionModifier} function.
+     * If non-null, that function will be invoked with, as input, a pre-configured sample dimension builder.
+     * The {@code sampleDimensionModifier} function can {@linkplain SampleDimension.Builder#setName(CharSequence)
+     * change the sample dimension name} or {@linkplain SampleDimension.Builder#categories() rebuild the categories}.
+     *
+     * <h4>Result relationship with source</h4>
+     * If the source coverage is backed by a {@link java.awt.image.WritableRenderedImage},
+     * then changes in the source coverage are reflected in the returned coverage and conversely.
+     *
+     * @see GridCoverageProcessor#convert(org.apache.sis.coverage.grid.GridCoverage, org.opengis.referencing.operation.MathTransform1D[], java.util.function.Function)
+     * @see ImageProcessor#convert(RenderedImage, NumberRange<?>[], MathTransform1D[], DataType, ColorModel)
+     */
+    public GridCoverageResource convert(final GridCoverageResource source, MathTransform1D[] converters,
+            Function<SampleDimension.Builder, SampleDimension> sampleDimensionModifier)
+    {
+        return new ConvertedCoverageResource(source, converters, sampleDimensionModifier);
+    }
+}
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ConvertedCoverageResourceTest.java b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ConvertedCoverageResourceTest.java
new file mode 100644
index 0000000000..cd42680f52
--- /dev/null
+++ b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/ConvertedCoverageResourceTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.internal.storage;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.RenderedImage;
+import org.apache.sis.coverage.grid.GridCoverage2D;
+import org.apache.sis.coverage.grid.GridExtent;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.coverage.grid.GridOrientation;
+import org.apache.sis.image.PixelIterator;
+import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.CommonCRS;
+import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.test.TestCase;
+import static org.junit.Assert.*;
+import org.junit.Test;
+import org.opengis.referencing.operation.MathTransform1D;
+
+/**
+ * Tests {@link ConvertedCoverageResource}.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 1.3
+ * @since   1.3
+ * @module
+ */
+public final class ConvertedCoverageResourceTest extends TestCase {
+
+
+    /**
+     * Tests {@link ConvertedCoverageResource}.
+     */
+    @Test
+    public void testConvert() throws DataStoreException {
+
+        //source coverage
+        final BufferedImage data = new BufferedImage(360, 180, BufferedImage.TYPE_BYTE_GRAY);
+        final GridGeometry grid = new GridGeometry(new GridExtent(360,180), CRS.getDomainOfValidity(CommonCRS.WGS84.normalizedGeographic()), GridOrientation.HOMOTHETY);
+        final GridCoverage2D coverage = new GridCoverage2D(grid, null, data);
+        final GridCoverageResource source = new MemoryGridResource(null, coverage);
+
+        //converted coverage
+        final MathTransform1D converter = (MathTransform1D) MathTransforms.linear(2, 10);
+        final ConvertedCoverageResource converted = new ConvertedCoverageResource(source, new MathTransform1D[]{converter}, null);
+
+        //ensure structure is preserved
+        assertEquals(source.getGridGeometry(), converted.getGridGeometry());
+        assertEquals(source.getSampleDimensions(), converted.getSampleDimensions());
+        assertEquals(source.getIdentifier(), converted.getIdentifier());
+
+        //ensure values are modified
+        final RenderedImage convertedImage = converted.read(grid, 0).render(grid.getExtent());
+        final PixelIterator ite = PixelIterator.create(convertedImage);
+        ite.moveTo(0, 0);
+        assertEquals(10, ite.getSample(0));
+    }
+}
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
index ad3ab0fa51..af2d2b5554 100644
--- a/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
+++ b/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java
@@ -43,6 +43,7 @@ import org.junit.BeforeClass;
     org.apache.sis.internal.storage.MetadataBuilderTest.class,
     org.apache.sis.internal.storage.RangeArgumentTest.class,
     org.apache.sis.internal.storage.MemoryGridResourceTest.class,
+    org.apache.sis.internal.storage.ConvertedCoverageResourceTest.class,
     org.apache.sis.storage.FeatureNamingTest.class,
     org.apache.sis.storage.ProbeResultTest.class,
     org.apache.sis.storage.StorageConnectorTest.class,