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

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 66e8d26354 chore(DGGS): code cleaning, remove some unused utilities
66e8d26354 is described below

commit 66e8d26354104e84923bb40de2aa6d32534a6c1d
Author: jsorel <[email protected]>
AuthorDate: Mon Aug 3 12:20:04 2026 +0200

    chore(DGGS): code cleaning, remove some unused utilities
---
 .../main/module-info.java                          |   1 -
 .../main/org/apache/sis/referencing/dggs/Zone.java |   2 +-
 .../shared/AbstractDiscreteGlobalGrid.java         |   2 +-
 .../AbstractDiscreteGlobalGridHierarchy.java       |   2 +-
 .../main/org/apache/sis/referencing/rs/Code.java   |  13 +-
 .../dggs/DiscreteGlobalGridCoverageProcessor.java  |  30 +-
 .../storage/dggs/DiscreteGlobalGridGeometry.java   |  10 +-
 .../storage/dggs/DiscreteGlobalGridResource.java   |   8 +-
 .../storage/dggs/DiscreteGlobalGridSystems.java    |  10 +-
 .../shared/AbstractDiscreteGlobalGridCoverage.java |  12 +-
 ...ingTiledDiscreteGlobalGridCoverageResource.java |   2 +-
 .../FeatureSetAsDiscreteGlobalGridResource.java    |   4 +-
 .../shared/GridAsDiscreteGlobalGridResource.java   |   2 +-
 .../shared/IndexedDiscreteGlobalGridCoverage.java  |  19 +-
 .../shared/RasterDiscreteGlobalGridCoverage.java   |   4 +-
 .../shared/TiledDiscreteGlobalGridCoverage.java    |   2 +-
 .../TiledDiscreteGlobalGridCoverageResource.java   |   4 +-
 .../sis/storage/image/internal/BufferedImages.java | 614 ---------------------
 .../sis/storage/image/internal/ImageBuilder.java   | 226 ++++++++
 .../internal/MergeOperator.java}                   |  14 +-
 .../org/apache/sis/storage/rs/CodedCoverages.java  |   4 +-
 .../org/apache/sis/storage/rs/CodedGeometry.java   |  18 +-
 .../org/apache/sis/storage/rs/CodedResource.java   |   2 +-
 .../sis/storage/rs/CompoundCodedGeometry.java      |   2 +-
 .../rs/internal/shared/AbstractCodedCoverage.java  |  12 +-
 .../rs/internal/shared/AddDimCodedCoverage.java    |   2 +-
 .../storage/rs/internal/shared/CodeTransforms.java |   4 +-
 .../internal/shared/CodedCoverageAsFeatureSet.java |  18 +-
 .../rs/internal/shared/CompoundCodedResource.java  |   5 +-
 .../rs/internal/shared/GridAsCodedResource.java    |  18 +-
 .../apache/sis/storage/util/StringUtilities.java   | 500 -----------------
 31 files changed, 342 insertions(+), 1224 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java 
b/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
index 97651953a3..5093857c59 100644
--- a/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
+++ b/incubator/src/org.apache.sis.referencing.dggs/main/module-info.java
@@ -34,7 +34,6 @@ module org.apache.sis.referencing.dggs {
     exports org.apache.sis.storage.coverage;
     exports org.apache.sis.storage.dggs;
     exports org.apache.sis.storage.rs;
-    exports org.apache.sis.storage.util;
 
     uses 
org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystemFactory;
     provides 
org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystemFactory
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/Zone.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/Zone.java
index 1300937660..dc71cc6b6e 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/Zone.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/Zone.java
@@ -22,8 +22,8 @@ import org.apache.sis.geometries.Geometry;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.CommonCRS;
-import org.apache.sis.util.SimpleInternationalString;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridSystems;
+import org.apache.sis.util.SimpleInternationalString;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.referencing.gazetteer.Location;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGrid.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGrid.java
index 96be942dc7..8d96b67a07 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGrid.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGrid.java
@@ -19,8 +19,8 @@ package org.apache.sis.referencing.dggs.internal.shared;
 import javax.measure.Quantity;
 import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
-import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
+import org.apache.sis.referencing.operation.transform.AbstractMathTransform;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.Matrix;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGridHierarchy.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGridHierarchy.java
index f23a881bc7..2c40bba0ae 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGridHierarchy.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/dggs/internal/shared/AbstractDiscreteGlobalGridHierarchy.java
@@ -20,10 +20,10 @@ import java.util.AbstractList;
 import java.util.List;
 import javax.measure.IncommensurableException;
 import javax.measure.Quantity;
-import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.util.ArgumentChecks;
 
 /**
  *
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/rs/Code.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/rs/Code.java
index 037cc395ce..385e77ea22 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/rs/Code.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/referencing/rs/Code.java
@@ -17,7 +17,6 @@
 package org.apache.sis.referencing.rs;
 
 import java.time.Instant;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -34,7 +33,6 @@ import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.TemporalCRS;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
-import org.apache.sis.storage.util.StringUtilities;
 
 /**
  * Holds the ordinates for geometry/area/zone/point within some reference 
system.
@@ -150,7 +148,7 @@ public final class Code {
 
     @Override
     public String toString() {
-        final List<String> parts = new ArrayList<>();
+        final StringBuilder sb = new StringBuilder("Code");
 
         final ReferenceSystem rs = getReferenceSystem();
         final List<ReferenceSystem> singleComponents = 
ReferenceSystems.getSingleComponents(rs, true);
@@ -158,17 +156,16 @@ public final class Code {
             final ReferenceSystem srs = singleComponents.get(i);
             if (srs instanceof TemporalCRS tcrs) {
                 Instant instant = 
DefaultTemporalCRS.castOrCopy(tcrs).toInstant(((Number)ordinates[i]).doubleValue());
-                parts.add(instant + " " + tcrs.getName().toString());
+                sb.append("\n  " + instant + " " + tcrs.getName().toString());
             } else if (srs instanceof CoordinateReferenceSystem crs) {
-                parts.add(ordinates[i] + " " + crs.getName().toString());
+                sb.append("\n  " + ordinates[i] + " " + 
crs.getName().toString());
             } else if (srs instanceof ReferencingByIdentifiers rbi) {
-                parts.add(ordinates[i] + " " + rbi.getName().toString());
+                sb.append("\n  " + ordinates[i] + " " + 
rbi.getName().toString());
             } else {
                 throw new UnsupportedOperationException("todo");
             }
         }
-
-        return StringUtilities.toStringTree("Location", parts);
+        return sb.toString();
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridCoverageProcessor.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridCoverageProcessor.java
index 58ad6714fb..18ed8a8bfe 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridCoverageProcessor.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridCoverageProcessor.java
@@ -16,11 +16,7 @@
  */
 package org.apache.sis.storage.dggs;
 
-import org.apache.sis.geometry.DirectPosition1D;
-import org.apache.sis.geometry.GeneralEnvelope;
-import org.apache.sis.referencing.dggs.Zone;
 import java.awt.image.RenderedImage;
-import 
org.apache.sis.storage.dggs.internal.shared.ArrayDiscreteGlobalGridCoverage;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -42,37 +38,41 @@ import org.apache.sis.coverage.grid.GridOrientation;
 import org.apache.sis.feature.Features;
 import org.apache.sis.feature.internal.shared.AttributeConvention;
 import org.apache.sis.filter.DefaultFilterFactory;
-import org.apache.sis.geometries.math.DataType;
-import org.apache.sis.geometries.math.SampleSystem;
 import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.DataType;
 import org.apache.sis.geometries.math.NDArrays;
+import org.apache.sis.geometries.math.SampleSystem;
 import org.apache.sis.geometries.math.Vector1D;
+import org.apache.sis.geometry.DirectPosition1D;
 import org.apache.sis.geometry.GeneralDirectPosition;
+import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.wrapper.jts.JTS;
 import org.apache.sis.math.Statistics;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.referencing.rs.Code;
+import org.apache.sis.referencing.rs.CodeOperation;
+import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureQuery;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.NoSuchDataException;
 import org.apache.sis.storage.RasterLoadingStrategy;
-import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.Utilities;
-import org.apache.sis.util.iso.Names;
 import org.apache.sis.storage.coverage.FeatureExt;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.referencing.rs.Code;
-import org.apache.sis.referencing.rs.CodeOperation;
-import org.apache.sis.referencing.rs.ReferenceSystems;
+import 
org.apache.sis.storage.dggs.internal.shared.ArrayDiscreteGlobalGridCoverage;
 import org.apache.sis.storage.rs.CodeTransform;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.WritableCodeIterator;
 import org.apache.sis.storage.rs.internal.shared.AddDimCodedCoverage;
 import org.apache.sis.storage.rs.internal.shared.ArrayCodedCoverage;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.Utilities;
+import org.apache.sis.util.iso.Names;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.Point;
 import org.locationtech.jts.geom.Polygon;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridGeometry.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridGeometry.java
index bbc80d4a04..98722f364d 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridGeometry.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridGeometry.java
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.storage.dggs;
 
-import org.apache.sis.referencing.dggs.Zone;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import java.util.List;
 import javax.measure.IncommensurableException;
 import javax.measure.Quantity;
@@ -27,18 +25,20 @@ import 
org.apache.sis.geometries.operation.GeometryOperations;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.referencing.CRS;
-import org.apache.sis.util.Utilities;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.storage.rs.CodeTransform;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.internal.shared.CodeTransforms;
+import org.apache.sis.util.Utilities;
 import org.opengis.geometry.Envelope;
+import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.MathTransform;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
-import org.apache.sis.storage.rs.CodeTransform;
-import org.opengis.metadata.extent.GeographicExtent;
 
 /**
  * DGGRS coverage geometry.
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridResource.java
index a6270176e1..ff586da86e 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridResource.java
@@ -16,9 +16,6 @@
  */
 package org.apache.sis.storage.dggs;
 
-import org.apache.sis.referencing.dggs.Zone;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
 import java.util.List;
 import java.util.Optional;
 import java.util.stream.Stream;
@@ -26,9 +23,12 @@ import javax.measure.IncommensurableException;
 import javax.measure.Quantity;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.measure.NumberRange;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
 import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.DataStoreException;
+import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.CodedResource;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridSystems.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridSystems.java
index b7302dfe6d..86a4254938 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridSystems.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/DiscreteGlobalGridSystems.java
@@ -16,15 +16,12 @@
  */
 package org.apache.sis.storage.dggs;
 
-import org.apache.sis.referencing.dggs.Zone;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import com.google.common.geometry.S2Polygon;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
-import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
 import java.util.List;
 import java.util.Set;
 import java.util.function.LongFunction;
@@ -32,9 +29,9 @@ import java.util.stream.Stream;
 import javax.measure.IncommensurableException;
 import org.apache.sis.geometries.LinearRing;
 import org.apache.sis.geometries.PointSequence;
+import org.apache.sis.geometries.internal.shared.ArraySequence;
 import org.apache.sis.geometries.math.Array;
 import org.apache.sis.geometries.math.NDArrays;
-import org.apache.sis.geometries.internal.shared.ArraySequence;
 import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.geometry.wrapper.GeometryWrapper;
@@ -42,14 +39,17 @@ import org.apache.sis.measure.AngleFormat;
 import org.apache.sis.measure.Latitude;
 import org.apache.sis.measure.Longitude;
 import org.apache.sis.measure.Units;
-import org.apache.sis.referencing.internal.shared.GeodeticObjectBuilder;
 import org.apache.sis.referencing.CommonCRS;
 import org.apache.sis.referencing.datum.DatumOrEnsemble;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.referencing.internal.shared.GeodeticObjectBuilder;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.dggs.internal.shared.ComputedZoneIndexList;
 import 
org.apache.sis.storage.dggs.internal.shared.FeatureSetAsDiscreteGlobalGridResource;
+import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
 import 
org.apache.sis.storage.dggs.internal.shared.MemoryDiscreteGlobalGridResource;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedResource;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/AbstractDiscreteGlobalGridCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/AbstractDiscreteGlobalGridCoverage.java
index 447c11fdb1..a8236cbd96 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/AbstractDiscreteGlobalGridCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/AbstractDiscreteGlobalGridCoverage.java
@@ -31,13 +31,13 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.geometry.DirectPosition2D;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.util.Utilities;
-import org.apache.sis.storage.image.internal.BufferedImages;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.storage.image.internal.ImageBuilder;
 import org.apache.sis.storage.rs.CodedCoverage;
+import org.apache.sis.util.Utilities;
 import org.opengis.coverage.CannotEvaluateException;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
@@ -94,7 +94,11 @@ public abstract class AbstractDiscreteGlobalGridCoverage 
extends CodedCoverage{
             final long lowX = extent.getLow(0);
             final long lowY = extent.getLow(1);
             final MathTransform gridToCRS = 
tileArea.getGridToCRS(PixelInCell.CELL_CENTER);
-            final BufferedImage image = BufferedImages.createImage(width, 
height, sampleDimensions.size(), DataBuffer.TYPE_DOUBLE);
+            final BufferedImage image = new ImageBuilder()
+                    .setSize(width, height)
+                    .setNumBands(sampleDimensions.size())
+                    .setDataType(DataBuffer.TYPE_DOUBLE)
+                    .createBufferedImage();
             final WritableRaster raster = image.getRaster();
 
             // Verify no overflow is possible before allocating any array
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/CachingTiledDiscreteGlobalGridCoverageResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/CachingTiledDiscreteGlobalGridCoverageResource.java
index 884cca084e..1669dd9cde 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/CachingTiledDiscreteGlobalGridCoverageResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/CachingTiledDiscreteGlobalGridCoverageResource.java
@@ -24,8 +24,8 @@ import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.measure.NumberRange;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridCoverageProcessor;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridResource;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/FeatureSetAsDiscreteGlobalGridResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/FeatureSetAsDiscreteGlobalGridResource.java
index 44f8b19f10..82588cd4a3 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/FeatureSetAsDiscreteGlobalGridResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/FeatureSetAsDiscreteGlobalGridResource.java
@@ -26,14 +26,14 @@ import org.apache.sis.geometry.Envelopes;
 import org.apache.sis.geometry.GeneralEnvelope;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.NoSuchDataException;
+import org.apache.sis.storage.dggs.DiscreteGlobalGridCoverageProcessor;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridResource;
-import org.apache.sis.storage.dggs.DiscreteGlobalGridCoverageProcessor;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.locationtech.jts.geom.Geometry;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/GridAsDiscreteGlobalGridResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/GridAsDiscreteGlobalGridResource.java
index 946b944153..50bda11e03 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/GridAsDiscreteGlobalGridResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/GridAsDiscreteGlobalGridResource.java
@@ -29,12 +29,12 @@ import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.GeodeticCalculator;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridCoverageProcessor;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridResource;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/IndexedDiscreteGlobalGridCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/IndexedDiscreteGlobalGridCoverage.java
index ce6dbf42cb..0e3bb7f209 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/IndexedDiscreteGlobalGridCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/IndexedDiscreteGlobalGridCoverage.java
@@ -42,13 +42,13 @@ import org.apache.sis.geometry.wrapper.jts.JTS;
 import org.apache.sis.image.PixelIterator;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.apache.sis.storage.image.internal.BufferedImages;
 import org.apache.sis.storage.coverage.CoverageUtilities;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.referencing.dggs.Zone;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridSystems;
+import org.apache.sis.storage.image.internal.ImageBuilder;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.locationtech.jts.geom.Polygon;
 import org.opengis.coverage.CannotEvaluateException;
@@ -111,7 +111,11 @@ public abstract class IndexedDiscreteGlobalGridCoverage 
extends AbstractDiscrete
             final int width = Math.toIntExact(extent.getSize(0));
             final int height = Math.toIntExact(extent.getSize(1));
             final MathTransform gridToCRS = 
tileArea.getGridToCRS(PixelInCell.CELL_CENTER);
-            final BufferedImage maskImage = BufferedImages.createImage(width, 
height, 1, DataBuffer.TYPE_BYTE);
+            final BufferedImage maskImage = new ImageBuilder()
+                    .setSize(width, height)
+                    .setNumBands(1)
+                    .setDataType(DataBuffer.TYPE_BYTE)
+                    .createBufferedImage();
             final Graphics2D g = maskImage.createGraphics();
             g.setColor(Color.WHITE);
             g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, 
RenderingHints.VALUE_ANTIALIAS_OFF);
@@ -125,7 +129,12 @@ public abstract class IndexedDiscreteGlobalGridCoverage 
extends AbstractDiscrete
             //prepare the coverage
             final double[] fillValue = new double[sampleDimensions.size()];
             Arrays.fill(fillValue, Double.NaN);
-            final BufferedImage image = BufferedImages.createImage(width, 
height, sampleDimensions.size(), DataBuffer.TYPE_DOUBLE, fillValue);
+            final BufferedImage image = new ImageBuilder()
+                    .setSize(width, height)
+                    .setNumBands(sampleDimensions.size())
+                    .setDataType(DataBuffer.TYPE_DOUBLE)
+                    .setFillValue(fillValue)
+                    .createBufferedImage();
             final WritableRaster raster = image.getRaster();
 
             final DiscreteGlobalGridReferenceSystem.Coder coder = 
dggrs.createCoder();
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/RasterDiscreteGlobalGridCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/RasterDiscreteGlobalGridCoverage.java
index b8d0acb50f..6e7e2f00a1 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/RasterDiscreteGlobalGridCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/RasterDiscreteGlobalGridCoverage.java
@@ -23,10 +23,10 @@ import java.util.function.Function;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.image.PixelIterator;
 import org.apache.sis.image.WritablePixelIterator;
-import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.WritableCodeIterator;
 import org.opengis.referencing.operation.TransformException;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverage.java
index 9de4ac68b5..7c244b6799 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverage.java
@@ -20,8 +20,8 @@ import java.util.Arrays;
 import java.util.List;
 import org.apache.sis.coverage.BandedCoverage;
 import org.apache.sis.coverage.SampleDimension;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.CodedCoverage;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverageResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverageResource.java
index b6f1acbb58..68d29d1def 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverageResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/dggs/internal/shared/TiledDiscreteGlobalGridCoverageResource.java
@@ -21,10 +21,10 @@ import java.util.HashSet;
 import java.util.List;
 import javax.measure.IncommensurableException;
 import org.apache.sis.measure.NumberRange;
-import org.apache.sis.storage.AbstractResource;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.storage.AbstractResource;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridResource;
 import org.apache.sis.storage.rs.CodedCoverage;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/BufferedImages.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/BufferedImages.java
deleted file mode 100644
index 06d42077e4..0000000000
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/BufferedImages.java
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- * 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.image.internal;
-
-import java.awt.Point;
-import java.awt.Rectangle;
-import java.awt.image.BandedSampleModel;
-import java.awt.image.BufferedImage;
-import java.awt.image.ColorModel;
-import java.awt.image.DataBuffer;
-import java.awt.image.DataBufferByte;
-import java.awt.image.DataBufferDouble;
-import java.awt.image.DataBufferFloat;
-import java.awt.image.DataBufferInt;
-import java.awt.image.DataBufferShort;
-import java.awt.image.ImagingOpException;
-import java.awt.image.Raster;
-import java.awt.image.RenderedImage;
-import java.awt.image.SampleModel;
-import java.awt.image.WritableRaster;
-import java.awt.image.WritableRenderedImage;
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.function.LongFunction;
-import java.util.stream.LongStream;
-import java.util.stream.Stream;
-import org.apache.sis.image.PixelIterator;
-import org.apache.sis.image.PlanarImage;
-import org.apache.sis.image.WritablePixelIterator;
-import org.apache.sis.image.internal.shared.ColorModelFactory;
-import org.apache.sis.image.internal.shared.WritableUntiledImage;
-import org.apache.sis.image.internal.shared.FillValues;
-import org.apache.sis.storage.util.TriFunction;
-import org.apache.sis.util.ArgumentChecks;
-
-/**
- *
- * @author Johann Sorel (Geomatys)
- */
-public class BufferedImages {
-
-    /**
-     * Create a new image, trying to preserve raster, sample model and color 
model
-     * when possible.
-     *
-     * @param reference not null
-     * @param width if null reference image width is copied
-     * @param height if null reference image height is copied
-     * @param nbBand if null reference image number of bands is copied
-     * @param dataType if null reference image data type is copied
-     */
-    public static BufferedImage createImage(final RenderedImage reference, 
Integer width, Integer height, Integer nbBand, Integer dataType) throws 
IllegalArgumentException{
-        return createImage(reference, width, height, nbBand, dataType, null);
-    }
-
-    /**
-     * Create a new image, trying to preserve raster, sample model and color 
model
-     * when possible.
-     *
-     * @param reference not null
-     * @param width if null reference image width is copied
-     * @param height if null reference image height is copied
-     * @param nbBand if null reference image number of bands is copied
-     * @param dataType if null reference image data type is copied
-     * @param fillValue if not null, samples to fill image
-     */
-    public static BufferedImage createImage(final RenderedImage reference, 
Integer width, Integer height, Integer nbBand, Integer dataType, double[] 
fillValue) throws IllegalArgumentException{
-        final SampleModel sm = reference.getSampleModel();
-        if (width == null) width = reference.getWidth();
-        if (height == null) height = reference.getHeight();
-        if (nbBand == null) nbBand = sm.getNumBands();
-        if (dataType == null) dataType = sm.getDataType();
-        ArgumentChecks.ensureStrictlyPositive("width", width);
-        ArgumentChecks.ensureStrictlyPositive("height", height);
-        ArgumentChecks.ensureStrictlyPositive("nbBand", nbBand);
-
-        if (nbBand == sm.getNumBands() && dataType == sm.getDataType()) {
-            //we can preserver color model and raster configuration
-            final Raster anyTile = reference.getTile(reference.getMinTileX(), 
reference.getMinTileY());
-            final WritableRaster raster = 
anyTile.createCompatibleWritableRaster(width, height);
-            if (fillValue != null && !isAllZero(fillValue)) setAll(raster, 
fillValue);
-            ColorModel cm = reference.getColorModel();
-            if (cm == null) {
-                cm = ColorModelFactory.createGrayScale(dataType, nbBand, 0, 0, 
1);
-            }
-            final BufferedImage resultImage = new WritableUntiledImage(cm, 
raster, cm.isAlphaPremultiplied(), null);
-            return resultImage;
-        } else {
-            //we need to create a new image
-            return createImage(width, height, nbBand, dataType, fillValue);
-        }
-    }
-
-    /**
-     * Create a new image of the same type with a different size.
-     */
-    public static BufferedImage createImage(final int width, final int height, 
RenderedImage reference) throws IllegalArgumentException {
-        return createImage(reference, width, height, null, null);
-    }
-
-    public static BufferedImage createImage(final int width, final int height, 
final int nbBand, final int dataType) throws IllegalArgumentException{
-        return createImage(width, height, nbBand, dataType, null);
-    }
-
-    public static BufferedImage createImage(final int width, final int height, 
final int nbBand, final int dataType, double[] fillValue) throws 
IllegalArgumentException{
-        ArgumentChecks.ensureStrictlyPositive("width", width);
-        ArgumentChecks.ensureStrictlyPositive("height", height);
-        final Point upperLeft = new Point(0,0);
-        final WritableRaster raster = createRaster(width, height, nbBand, 
dataType, upperLeft, fillValue);
-
-        //TODO try to reuse java colormodel if possible
-        //create a temporary fallback colormodel which will always work
-        //extract grayscale min/max from sample dimension
-        final ColorModel graycm = ColorModelFactory.createGrayScale(dataType, 
nbBand, 0, 0, 1);
-        return new WritableUntiledImage(graycm, raster, false, null);
-    }
-
-    public static WritableRaster createRaster(int width, int height, int 
nbBand, int dataType, Point upperLeft) throws IllegalArgumentException{
-        return createRaster(width, height, nbBand, dataType, upperLeft, null);
-    }
-
-    public static WritableRaster createRaster(int width, int height, int 
nbBand, int dataType, Point upperLeft, double[] fillValue) throws 
IllegalArgumentException{
-        ArgumentChecks.ensureStrictlyPositive("width", width);
-        ArgumentChecks.ensureStrictlyPositive("height", height);
-
-        if (fillValue != null) {
-            if (fillValue.length != nbBand) {
-                throw new IllegalArgumentException("Fill value size " + 
fillValue.length + "do not match nb band " + nbBand);
-            }
-            /*
-            Created rasters are filled with 0 by default, set fillValue to 
null if all samples are 0
-            */
-            if (isAllZero(fillValue)) {
-                fillValue = null;
-            }
-        }
-
-        final WritableRaster raster;
-        if (nbBand == 1) {
-            if (dataType == DataBuffer.TYPE_BYTE || dataType == 
DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT) {
-                raster = WritableRaster.createBandedRaster(dataType, width, 
height, nbBand, upperLeft);
-                if (fillValue != null) {
-                    fill(raster, fillValue[0]);
-                }
-            } else {
-                //create it ourself
-                final int bufferSize = Math.multiplyExact(width, height);
-
-                final DataBuffer buffer;
-                if (dataType == DataBuffer.TYPE_SHORT) {
-                    final short[] data = new short[bufferSize];
-                    if (fillValue != null) Arrays.fill(data, (short) 
fillValue[0]);
-                    buffer = new DataBufferShort(data, bufferSize);
-                } else if(dataType == DataBuffer.TYPE_FLOAT) {
-                    final float[] data = new float[bufferSize];
-                    if (fillValue != null) Arrays.fill(data, (float) 
fillValue[0]);
-                    buffer = new DataBufferFloat(data, bufferSize);
-                } else if(dataType == DataBuffer.TYPE_DOUBLE) {
-                    final double[] data = new double[bufferSize];
-                    if (fillValue != null) Arrays.fill(data, fillValue[0]);
-                    buffer = new DataBufferDouble(data, bufferSize);
-                } else {
-                    throw new IllegalArgumentException("Type not supported 
"+dataType);
-                }
-                final int[] zero = new int[1];
-                //TODO create our own raster factory to avoid JAI
-                raster = 
org.apache.sis.image.internal.shared.RasterFactory.createRaster(buffer, width, 
height, 1, width, zero, zero, upperLeft);
-                //raster = RasterFactory.createBandedRaster(buffer, width, 
height, width, zero, zero, upperLeft);
-            }
-
-        } else {
-            if (dataType == DataBuffer.TYPE_BYTE || dataType == 
DataBuffer.TYPE_USHORT) {
-                raster = WritableRaster.createInterleavedRaster(dataType, 
width, height, nbBand, upperLeft);
-                if (fillValue != null) {
-                    setAll(raster, fillValue);
-                }
-            } else {
-                //create it ourself
-                final int size = Math.multiplyExact(Math.multiplyExact(width, 
height), nbBand);
-                final DataBuffer buffer;
-                switch (dataType) {
-                    case DataBuffer.TYPE_SHORT: buffer = new 
DataBufferShort(size); break;
-                    case DataBuffer.TYPE_INT: buffer = new 
DataBufferInt(size); break;
-                    case DataBuffer.TYPE_FLOAT: buffer = new 
DataBufferFloat(size); break;
-                    case DataBuffer.TYPE_DOUBLE: buffer = new 
DataBufferDouble(size); break;
-                    default: throw new IllegalArgumentException("Type not 
supported "+dataType);
-                }
-                final int[] bankIndices = new int[nbBand];
-                final int[] bandOffsets = new int[nbBand];
-                for(int i=1;i<nbBand;i++){
-                    bandOffsets[i] = bandOffsets[i-1] + width*height;
-                }
-                /*
-                 * The following line creates a `BandedSampleModel` with only 
one bank shared by all bands.
-                 * This is unusual, as the standard pattern of 
`BandedSampleModel` convenience constructors
-                 * is to create one bank per band. It is possible to have many 
banks wrapping the same array
-                 * at different offsets. The use of a single bank, as done 
below, is more typically done with
-                 * `PixelInterleavedSampleModel`.
-                 *
-                 * However, despite being unusual, Java2D seems to accept this 
configuration. The following
-                 * code uses that configuration because this is what the 
initial version of this method was
-                 * doing. But it should not be used as a source of inspiration 
for new codes.
-                 */
-                var sm = new BandedSampleModel(dataType, width, height, width, 
bankIndices, bandOffsets);
-                raster = Raster.createWritableRaster(sm, buffer, upperLeft);
-                if (fillValue != null) {
-                    setAll(raster, fillValue);
-                }
-            }
-        }
-        return raster;
-    }
-
-    /**
-     * Convert a primitive array to a DataBuffer.
-     * This DataBuffer can then be used to create a WritableRaster.
-     * The array is directly used by the buffer, they are not copied.
-     *
-     * @param data primitive array object with 1 or 2 dimensions.
-     * @return DataBuffer never null
-     * @throws IllegalArgumentException if the array type is not supported.
-     */
-    public static DataBuffer toDataBuffer(Object data) throws 
IllegalArgumentException{
-        if(data instanceof byte[]){
-            return new DataBufferByte((byte[])data,Array.getLength(data));
-        }else if(data instanceof short[]){
-            return new DataBufferShort((short[])data,Array.getLength(data));
-        }else if(data instanceof int[]){
-            return new DataBufferInt((int[])data,Array.getLength(data));
-        }else if(data instanceof float[]){
-            return new DataBufferFloat((float[])data,Array.getLength(data));
-        }else if(data instanceof double[]){
-            return new DataBufferDouble((double[])data,Array.getLength(data));
-        }
-
-        else if(data instanceof byte[][]){
-            return new 
DataBufferByte((byte[][])data,Array.getLength(Array.get(data, 0)));
-        }else if(data instanceof short[][]){
-            return new 
DataBufferShort((short[][])data,Array.getLength(Array.get(data, 0)));
-        }else if(data instanceof int[][]){
-            return new 
DataBufferInt((int[][])data,Array.getLength(Array.get(data, 0)));
-        }else if(data instanceof float[][]){
-            return new 
DataBufferFloat((float[][])data,Array.getLength(Array.get(data, 0)));
-        }else if(data instanceof double[][]){
-            return new 
DataBufferDouble((double[][])data,Array.getLength(Array.get(data, 0)));
-        }
-
-        else{
-            throw new IllegalArgumentException("Unexpected array type 
"+data.getClass());
-        }
-    }
-
-    public static DataBuffer toDataBuffer1D(Object data) throws 
ArithmeticException {
-        if (data instanceof byte[][]) {
-            final byte[][] matrix = (byte[][]) data;
-            final int height = matrix.length;
-            final int width = matrix[0].length;
-            final int size = Math.multiplyExact(height,width);
-            final byte[] datas = new byte[size];
-            for (int i = 0, offset=0; i < matrix.length; i++,offset+=width) {
-                System.arraycopy(matrix[i], 0, datas, offset, width);
-            }
-            return new DataBufferByte(datas, datas.length);
-        } else if (data instanceof short[][]) {
-            final short[][] matrix = (short[][]) data;
-            final int height = matrix.length;
-            final int width = matrix[0].length;
-            final int size = Math.multiplyExact(height,width);
-            final short[] datas = new short[size];
-            for (int i = 0, offset=0; i < matrix.length; i++,offset+=width) {
-                System.arraycopy(matrix[i], 0, datas, offset, width);
-            }
-            return new DataBufferShort(datas, datas.length);
-        } else if (data instanceof int[][]) {
-            final int[][] matrix = (int[][]) data;
-            final int height = matrix.length;
-            final int width = matrix[0].length;
-            final int size = Math.multiplyExact(height,width);
-            final int[] datas = new int[size];
-            for (int i = 0, offset=0; i < matrix.length; i++,offset+=width) {
-                System.arraycopy(matrix[i], 0, datas, offset, width);
-            }
-            return new DataBufferInt(datas, datas.length);
-        } else if (data instanceof float[][]) {
-            final float[][] matrix = (float[][]) data;
-            final int height = matrix.length;
-            final int width = matrix[0].length;
-            final int size = Math.multiplyExact(height,width);
-            final float[] datas = new float[size];
-            for (int i = 0, offset=0; i < matrix.length; i++,offset+=width) {
-                System.arraycopy(matrix[i], 0, datas, offset, width);
-            }
-            return new DataBufferFloat(datas, datas.length);
-        } else if (data instanceof double[][]) {
-            final double[][] matrix = (double[][]) data;
-            final int height = matrix.length;
-            final int width = matrix[0].length;
-            final int size = Math.multiplyExact(height,width);
-            final double[] datas = new double[size];
-            for (int i = 0, offset=0; i < matrix.length; i++,offset+=width) {
-                System.arraycopy(matrix[i], 0, datas, offset, width);
-            }
-            return new DataBufferDouble(datas, datas.length);
-        } else {
-            throw new IllegalArgumentException("Unexpected array type 
"+data.getClass());
-        }
-    }
-
-    /**
-     * Compare the pixels of given image to reference pixel and return true
-     * if all pixels share those same samples.
-     *
-     * @param img image to test
-     * @param pixel reference pixel to compare with
-     * @return true if all pixels is image are equal to reference pixel.
-     */
-    public static boolean isAll(RenderedImage img, double[] pixel) {
-        final PixelIterator ite = PixelIterator.create(img);
-        final double[] buffer = new double[pixel.length];
-        while (ite.next()) {
-            ite.getPixel(buffer);
-            if (!Arrays.equals(pixel, buffer)) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public static void setAll(WritableRenderedImage img, double[] pixel) {
-
-        final int minTileX = img.getMinTileX();
-        final int minTileY = img.getMinTileY();
-        final int numXTiles = img.getNumXTiles();
-        final int numYTiles = img.getNumYTiles();
-
-        for (int y = minTileY, yn = minTileY + numYTiles; y < yn; y++) {
-            for (int x = minTileX, xn = minTileX + numXTiles; x < xn; x++) {
-                WritableRaster raster = img.getWritableTile(x, y);
-                setAll(raster, pixel);
-                img.releaseWritableTile(x, y);
-            }
-        }
-    }
-
-    public static void setAll(WritableRaster raster, double[] pixel) {
-        final Number[] values = new Number[pixel.length];
-        for (int i = 0; i < values.length; i++) values[i] = pixel[i];
-        new FillValues(raster.getSampleModel(), values, true).fill(raster);
-    }
-
-    /**
-     * Tests if all pixels in the image are identical and images have the same 
geometry.
-     *
-     * @return true if pixels are identical
-     */
-    public static boolean isPixelsIdenticals(RenderedImage image1, 
RenderedImage image2) {
-        final PixelIterator ite1 = PixelIterator.create(image1);
-        final PixelIterator ite2 = PixelIterator.create(image2);
-        if (!ite1.getDomain().equals(ite2.getDomain())) {
-            return false;
-        }
-        final double[] pixel1 = new double[ite1.getNumBands()];
-        final double[] pixel2 = new double[ite2.getNumBands()];
-
-        while (ite1.next()) {
-            final Point position = ite1.getPosition();
-            ite2.moveTo(position.x, position.y);
-            ite1.getPixel(pixel1);
-            ite2.getPixel(pixel2);
-            if (!Arrays.equals(pixel1, pixel2)) {
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    /**
-     * Create a stream of point in the rectangle.
-     */
-    public static Stream<Point> pointStream(Rectangle rectangle) {
-        return LongStream.range(0, rectangle.width * 
rectangle.height).mapToObj((long value) -> {
-            final int x = (int) (value % rectangle.width);
-            final int y = (int) (value / rectangle.width);
-            return new Point(rectangle.x + x, rectangle.y + y);
-        });
-    }
-
-    /**
-     * Create a stream of rectangle covering each tile in the image.
-     * <p>
-     * A margin on each side can be defined to expend the rectangle in the 
limit of the image size.
-     * This behavior allows to overlaps tile border in case the algorithm 
requires it.
-     * </p>
-     *
-     * @param top top tile margin
-     * @param right right tile margin
-     * @param bottom bottom tile margin
-     * @param left  left tile margin
-     * @return Stream of Rectangle for each tile in the image.
-     */
-    public static Stream<Rectangle> tileStream(RenderedImage image, int top, 
int right, int bottom, int left) {
-        final int tileWidth = image.getTileWidth();
-        final int tileHeight = image.getTileHeight();
-        final int numXTiles = image.getNumXTiles();
-        final int numYTiles = image.getNumYTiles();
-        final int minTileX = image.getMinTileX();
-        final int minTileY = image.getMinTileY();
-        final int tileGridXOffset = image.getTileGridXOffset();
-        final int tileGridYOffset = image.getTileGridYOffset();
-        final long nbTile = (long)numXTiles * numYTiles;
-        final Rectangle imageBounds = new Rectangle(image.getMinX(), 
image.getMinY(), image.getWidth(), image.getHeight());
-
-        final int rectWidth = tileWidth + right + left;
-        final int rectHeight = tileHeight + top + bottom;
-
-        return LongStream.range(0, nbTile)
-                .mapToObj(new LongFunction<Rectangle>() {
-                    @Override
-                    public Rectangle apply(long value) {
-                        final int tileX = (int) (value % numXTiles);
-                        final int tileY = (int) (value / numXTiles);
-                        final int rectX = (minTileX + tileX) * tileWidth + 
tileGridXOffset;
-                        final int rectY = (minTileY + tileY) * tileHeight + 
tileGridYOffset;
-                        return new Rectangle(rectX - left,rectY - 
bottom,rectWidth,rectHeight).intersection(imageBounds);
-                    }
-                });
-    }
-
-    /**
-     * Ensure the given raster is compatible with raster template.
-     * If not the data will be copied to a new raster.
-     *
-     * @param in raster to verify and transform if needed
-     * @param rasterTemplate reference raster to compare with
-     * @return compatible raster
-     * @throws ImagingOpException if raster can not be transformed
-     */
-    public static Raster makeConform(Raster in, Raster rasterTemplate) throws 
ImagingOpException {
-        final int inNumBands = in.getNumBands();
-        final int outNumBands = rasterTemplate.getNumBands();
-        if (inNumBands != outNumBands) {
-            //this is a severe issue, the raster do no respect the expected 
number of samples
-            throw new ImagingOpException("Mosaic tile image declares " + 
inNumBands
-                    + " bands, but sample dimensions have " + outNumBands);
-        }
-
-        final int inDataType = in.getDataBuffer().getDataType();
-        final int outDataType = rasterTemplate.getDataBuffer().getDataType();
-        if (inDataType != outDataType) {
-            //difference in input and output types, this may be caused by an 
aggregated resource
-            final int x = 0;
-            final int y = 0;
-            final int width = in.getWidth();
-            final int height = in.getHeight();
-            final WritableRaster raster = 
rasterTemplate.createCompatibleWritableRaster(in.getMinX(), in.getMinY(), 
width, height);
-            final int nbSamples = width * height * inNumBands;
-            switch (outDataType) {
-                case DataBuffer.TYPE_BYTE :
-                case DataBuffer.TYPE_SHORT :
-                case DataBuffer.TYPE_USHORT :
-                case DataBuffer.TYPE_INT :
-                    int[] arrayi = new int[nbSamples];
-                    in.getPixels(x, y, width, height, arrayi);
-                    raster.setPixels(x, y, width, height, arrayi);
-                    break;
-                case DataBuffer.TYPE_FLOAT :
-                    float[] arrayf = new float[nbSamples];
-                    in.getPixels(x, y, width, height, arrayf);
-                    raster.setPixels(x, y, width, height, arrayf);
-                    break;
-                case DataBuffer.TYPE_DOUBLE :
-                default :
-                    double[] arrayd = new double[nbSamples];
-                    in.getPixels(x, y, width, height, arrayd);
-                    raster.setPixels(x, y, width, height, arrayd);
-                    break;
-            }
-            in = raster;
-        }
-        return in;
-    }
-
-    /**
-     * Merge images with a user defined function.
-     * Images may have different sample models, number of bands or data types 
but must have the same geometry.
-     *
-     * @param source image to read from
-     * @param target image to read and write into
-     * @param filterByMask only apply merge operation where the source mask is 
valid, if mask is undefined this property has no effect.
-     * @param merger function transforming values before writing. parameters 
are in order : position in image, source pixel, target pixel, result pixel
-     *               if result pixel is null, the target pixel values are 
unchanged.
-     * @throws IllegalArgumentException if source and target images have 
different geometries
-     */
-    public static void mergeImages(RenderedImage source, WritableRenderedImage 
target, boolean filterByMask, TriFunction<Point, double[], double[], double[]> 
merger) throws IllegalArgumentException{
-
-        if (  source.getMinX() != target.getMinX()
-           || source.getMinY() != target.getMinY()
-           || source.getWidth() != target.getWidth()
-           || source.getHeight() != target.getHeight()) {
-            throw new IllegalArgumentException("Source and target images must 
have the same geometry.");
-        }
-
-
-        RenderedImage sourceMask = null;
-        if (filterByMask) {
-            Object cdt = source.getProperty(PlanarImage.MASK_KEY);
-            if (cdt instanceof RenderedImage) {
-                sourceMask = (RenderedImage) cdt;
-            }
-        }
-
-        final PixelIterator sourceIte = new 
PixelIterator.Builder().create(source);
-        final WritablePixelIterator targetIte = new 
WritablePixelIterator.Builder().createWritable(target);
-        final double[] pixelr = new double[sourceIte.getNumBands()];
-        final double[] pixelw = new double[targetIte.getNumBands()];
-
-        Point position;
-
-        if (sourceMask != null) {
-            final PixelIterator sourceMaskIte = new 
PixelIterator.Builder().create(sourceMask);
-            while (sourceMaskIte.next()) {
-
-                //check the source mask
-                if (sourceMaskIte.getSample(0) == 0) {
-                    position = sourceMaskIte.getPosition();
-
-                    sourceIte.moveTo(position.x, position.y);
-                    targetIte.moveTo(position.x, position.y);
-                    sourceIte.getPixel(pixelr);
-                    targetIte.getPixel(pixelw);
-
-                    final double[] result = merger.apply(position, pixelr, 
pixelw);
-                    if (result != null) {
-                        targetIte.setPixel(result);
-                    }
-                }
-            }
-        } else {
-            while (sourceIte.next()) {
-                position = sourceIte.getPosition();
-
-                targetIte.moveTo(position.x, position.y);
-                sourceIte.getPixel(pixelr);
-                targetIte.getPixel(pixelw);
-
-                final double[] result = merger.apply(position, pixelr, pixelw);
-                if (result != null) {
-                    targetIte.setPixel(result);
-                }
-            }
-        }
-    }
-
-    private static boolean isAllZero(double[] array) {
-        for (double s : array) {
-            if (s != 0) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * Sets every samples in the given image to the given value. This method 
is typically used
-     * for clearing an image content.
-     *
-     * @param image The image to fill (modified in-place).
-     * @param value The value to be given to every samples.
-     * @see FillValues used pixel filling engine.
-     */
-    public static void fill(final WritableRenderedImage image, final Number 
value) {
-        final SampleModel sampleModel = image.getSampleModel();
-        final int numBands = sampleModel.getNumBands();
-        final Number[] fillValues = new Number[numBands];
-        Arrays.fill(fillValues, value);
-        final FillValues filler = new FillValues(sampleModel, fillValues, 
true);
-        int y = image.getMinTileY();
-        for (int ny = image.getNumYTiles(); --ny >= 0; y++) {
-            int x = image.getMinTileX();
-            for (int nx = image.getNumXTiles(); --nx >= 0; x++) {
-                final WritableRaster raster = image.getWritableTile(x, y);
-                try {
-                    filler.fill(raster);
-                } finally {
-                    image.releaseWritableTile(x, y);
-                }
-            }
-        }
-    }
-
-    /**
-     * @deprecated Use Apache SIS utilities instead.
-     * @see FillValues#fill(WritableRaster)
-     */
-    @Deprecated
-    public static void fill(final WritableRaster raster, final Number value) {
-        new FillValues(raster.getSampleModel(), new Number[]{ value }, 
true).fill(raster);
-    }
-}
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/ImageBuilder.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/ImageBuilder.java
new file mode 100644
index 0000000000..8cab876220
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/ImageBuilder.java
@@ -0,0 +1,226 @@
+/*
+ * 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.image.internal;
+
+import java.awt.Point;
+import java.awt.image.BandedSampleModel;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferDouble;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.awt.image.WritableRenderedImage;
+import java.util.Arrays;
+import org.apache.sis.image.internal.shared.ColorModelFactory;
+import org.apache.sis.image.internal.shared.FillValues;
+import org.apache.sis.image.internal.shared.WritableUntiledImage;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class ImageBuilder {
+
+    private RenderedImage reference;
+    private int width = 1;
+    private int height = 1;
+    private int dataType = DataBuffer.TYPE_BYTE;
+    private int nbBand = 1;
+    private double[] fillValue;
+
+    public ImageBuilder() {
+    }
+
+    public ImageBuilder copyFrom(RenderedImage model) {
+        this.reference = model;
+        final SampleModel sm = reference.getSampleModel();
+        width = reference.getWidth();
+        height = reference.getHeight();
+        nbBand = sm.getNumBands();
+        dataType = sm.getDataType();
+        return this;
+    }
+
+    public ImageBuilder setSize(int width, int height) {
+        this.width = width;
+        this.height = height;
+        return this;
+    }
+
+    public ImageBuilder setDataType(int dataType) {
+        this.dataType = dataType;
+        return this;
+    }
+
+    public ImageBuilder setNumBands(int numBands) {
+        this.nbBand = numBands;
+        return this;
+    }
+
+    public ImageBuilder setFillValue(double[] fillValue) {
+        this.fillValue = fillValue;
+        return this;
+    }
+
+    public BufferedImage createBufferedImage() {
+
+        if (reference != null) {
+            final SampleModel sm = reference.getSampleModel();
+
+            if (nbBand == sm.getNumBands() && dataType == sm.getDataType()) {
+                //we can preserver color model and raster configuration
+                final Raster anyTile = 
reference.getTile(reference.getMinTileX(), reference.getMinTileY());
+                final WritableRaster raster = 
anyTile.createCompatibleWritableRaster(width, height);
+                if (fillValue != null && !isAllZero(fillValue)) setAll(raster, 
fillValue);
+                ColorModel cm = reference.getColorModel();
+                if (cm == null) {
+                    cm = ColorModelFactory.createGrayScale(dataType, nbBand, 
0, 0, 1);
+                }
+                final BufferedImage resultImage = new WritableUntiledImage(cm, 
raster, cm.isAlphaPremultiplied(), null);
+                return resultImage;
+            } else {
+                //we need to create a new image
+            }
+        }
+
+        //create a new image
+        final WritableRaster raster = createRaster();
+
+        //TODO try to reuse java colormodel if possible
+        //create a temporary fallback colormodel which will always work
+        //extract grayscale min/max from sample dimension
+        final ColorModel graycm = ColorModelFactory.createGrayScale(dataType, 
nbBand, 0, 0, 1);
+        return new WritableUntiledImage(graycm, raster, false, null);
+
+    }
+
+    public WritableRenderedImage createImage() {
+        return createBufferedImage();
+    }
+
+    public WritableRaster createRaster() {
+        final Point upperLeft = new Point(0,0);
+
+        if (fillValue != null) {
+            if (fillValue.length != nbBand) {
+                throw new IllegalArgumentException("Fill value size " + 
fillValue.length + "do not match nb band " + nbBand);
+            }
+            /*
+            Created rasters are filled with 0 by default, set fillValue to 
null if all samples are 0
+            */
+            if (isAllZero(fillValue)) {
+                fillValue = null;
+            }
+        }
+
+        final WritableRaster raster;
+        if (nbBand == 1) {
+            if (dataType == DataBuffer.TYPE_BYTE || dataType == 
DataBuffer.TYPE_USHORT || dataType == DataBuffer.TYPE_INT) {
+                raster = WritableRaster.createBandedRaster(dataType, width, 
height, nbBand, upperLeft);
+                if (fillValue != null) {
+                    new FillValues(raster.getSampleModel(), new Number[]{ 
fillValue[0] }, true).fill(raster);
+                }
+            } else {
+                //create it ourself
+                final int bufferSize = Math.multiplyExact(width, height);
+
+                final DataBuffer buffer;
+                if (dataType == DataBuffer.TYPE_SHORT) {
+                    final short[] data = new short[bufferSize];
+                    if (fillValue != null) Arrays.fill(data, (short) 
fillValue[0]);
+                    buffer = new DataBufferShort(data, bufferSize);
+                } else if(dataType == DataBuffer.TYPE_FLOAT) {
+                    final float[] data = new float[bufferSize];
+                    if (fillValue != null) Arrays.fill(data, (float) 
fillValue[0]);
+                    buffer = new DataBufferFloat(data, bufferSize);
+                } else if(dataType == DataBuffer.TYPE_DOUBLE) {
+                    final double[] data = new double[bufferSize];
+                    if (fillValue != null) Arrays.fill(data, fillValue[0]);
+                    buffer = new DataBufferDouble(data, bufferSize);
+                } else {
+                    throw new IllegalArgumentException("Type not supported 
"+dataType);
+                }
+                final int[] zero = new int[1];
+                //TODO create our own raster factory to avoid JAI
+                raster = 
org.apache.sis.image.internal.shared.RasterFactory.createRaster(buffer, width, 
height, 1, width, zero, zero, upperLeft);
+                //raster = RasterFactory.createBandedRaster(buffer, width, 
height, width, zero, zero, upperLeft);
+            }
+
+        } else {
+            if (dataType == DataBuffer.TYPE_BYTE || dataType == 
DataBuffer.TYPE_USHORT) {
+                raster = WritableRaster.createInterleavedRaster(dataType, 
width, height, nbBand, upperLeft);
+                if (fillValue != null) {
+                    setAll(raster, fillValue);
+                }
+            } else {
+                //create it ourself
+                final int size = Math.multiplyExact(Math.multiplyExact(width, 
height), nbBand);
+                final DataBuffer buffer;
+                switch (dataType) {
+                    case DataBuffer.TYPE_SHORT: buffer = new 
DataBufferShort(size); break;
+                    case DataBuffer.TYPE_INT: buffer = new 
DataBufferInt(size); break;
+                    case DataBuffer.TYPE_FLOAT: buffer = new 
DataBufferFloat(size); break;
+                    case DataBuffer.TYPE_DOUBLE: buffer = new 
DataBufferDouble(size); break;
+                    default: throw new IllegalArgumentException("Type not 
supported "+dataType);
+                }
+                final int[] bankIndices = new int[nbBand];
+                final int[] bandOffsets = new int[nbBand];
+                for(int i=1;i<nbBand;i++){
+                    bandOffsets[i] = bandOffsets[i-1] + width*height;
+                }
+                /*
+                 * The following line creates a `BandedSampleModel` with only 
one bank shared by all bands.
+                 * This is unusual, as the standard pattern of 
`BandedSampleModel` convenience constructors
+                 * is to create one bank per band. It is possible to have many 
banks wrapping the same array
+                 * at different offsets. The use of a single bank, as done 
below, is more typically done with
+                 * `PixelInterleavedSampleModel`.
+                 *
+                 * However, despite being unusual, Java2D seems to accept this 
configuration. The following
+                 * code uses that configuration because this is what the 
initial version of this method was
+                 * doing. But it should not be used as a source of inspiration 
for new codes.
+                 */
+                var sm = new BandedSampleModel(dataType, width, height, width, 
bankIndices, bandOffsets);
+                raster = Raster.createWritableRaster(sm, buffer, upperLeft);
+                if (fillValue != null) {
+                    setAll(raster, fillValue);
+                }
+            }
+        }
+        return raster;
+    }
+
+    private static boolean isAllZero(double[] array) {
+        for (double s : array) {
+            if (s != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static void setAll(WritableRaster raster, double[] pixel) {
+        final Number[] values = new Number[pixel.length];
+        for (int i = 0; i < values.length; i++) values[i] = pixel[i];
+        new FillValues(raster.getSampleModel(), values, true).fill(raster);
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/TriFunction.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/MergeOperator.java
similarity index 74%
rename from 
incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/TriFunction.java
rename to 
incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/MergeOperator.java
index 07067fbe31..a5459a2ea4 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/TriFunction.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/image/internal/MergeOperator.java
@@ -14,19 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.storage.util;
+package org.apache.sis.storage.image.internal;
+
+import java.awt.Point;
 
 /**
- * Similar to BiFunction but with three input parameters.
  *
  * @author Johann Sorel (Geomatys)
- * @param <A> first parameter type
- * @param <B> second parameter type
- * @param <C> third parameter type
- * @param <R> returned type
  */
-public interface TriFunction<A,B,C,R> {
-
-    R apply(A a, B b, C c);
+public interface MergeOperator {
 
+    double[] apply(Point pt, double[] source, double[] target);
 }
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedCoverages.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedCoverages.java
index 76bbebd439..a68b20532a 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedCoverages.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedCoverages.java
@@ -18,13 +18,13 @@ package org.apache.sis.storage.rs;
 
 import javax.measure.IncommensurableException;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.storage.GridCoverageResource;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
-import org.apache.sis.storage.rs.internal.shared.GridAsCodedResource;
 import org.apache.sis.storage.rs.internal.shared.CodedCoverageAsFeatureSet;
+import org.apache.sis.storage.rs.internal.shared.GridAsCodedResource;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedGeometry.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedGeometry.java
index 2b88eae597..222998dfaa 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedGeometry.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedGeometry.java
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.storage.rs;
 
-import org.apache.sis.referencing.rs.ReferenceSystems;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -27,11 +25,11 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.IncompleteGridGeometryException;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.geometry.Envelopes;
-import org.apache.sis.util.ArraysExt;
-import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.rs.internal.shared.CodeTransforms;
-import org.apache.sis.storage.util.StringUtilities;
+import org.apache.sis.util.ArraysExt;
 import org.opengis.geometry.Envelope;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.referencing.ReferenceSystem;
@@ -339,11 +337,11 @@ public class CodedGeometry {
 
     @Override
     public String toString() {
-        final List lst = new ArrayList();
-        if (rs != null) lst.add(rs);
-        if (extent != null) lst.add(extent);
-        if (gridToRS != null) lst.add(gridToRS);
-        return StringUtilities.toStringTree("ReferencedGridGeometry", lst);
+        final StringBuilder sb = new StringBuilder("CodedGeometry");
+        if (rs != null) sb.append("\n").append(rs);
+        if (extent != null) sb.append("\n").append(extent);
+        if (gridToRS != null) sb.append("\n").append(gridToRS);
+        return sb.toString();
     }
 
     @Override
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedResource.java
index 67ee9e7fc9..7904f15a94 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CodedResource.java
@@ -27,12 +27,12 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
 import org.apache.sis.referencing.CRS;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGrid;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridHierarchy;
 import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.referencing.dggs.Zone;
 import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.coverage.BandedCoverageResource;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CompoundCodedGeometry.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CompoundCodedGeometry.java
index beb1059f3e..cda717db17 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CompoundCodedGeometry.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/CompoundCodedGeometry.java
@@ -19,9 +19,9 @@ package org.apache.sis.storage.rs;
 import java.util.List;
 import java.util.Optional;
 import org.apache.sis.coverage.grid.GridExtent;
-import org.apache.sis.util.ArraysExt;
 import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.rs.internal.shared.CodeTransforms;
+import org.apache.sis.util.ArraysExt;
 import org.opengis.metadata.extent.GeographicExtent;
 import org.opengis.referencing.ReferenceSystem;
 
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AbstractCodedCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AbstractCodedCoverage.java
index 83cd25403e..9cf18b9f1b 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AbstractCodedCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AbstractCodedCoverage.java
@@ -16,8 +16,6 @@
  */
 package org.apache.sis.storage.rs.internal.shared;
 
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.storage.dggs.*;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBuffer;
 import java.awt.image.WritableRaster;
@@ -40,9 +38,11 @@ import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.geometry.DirectPosition2D;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
-import org.apache.sis.storage.image.internal.BufferedImages;
 import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
+import org.apache.sis.storage.image.internal.ImageBuilder;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
@@ -173,7 +173,11 @@ public abstract class AbstractCodedCoverage extends 
CodedCoverage{
             final long lowX = extent.getLow(0);
             final long lowY = extent.getLow(1);
             final MathTransform gridToCRS = 
tileArea.getGridToCRS(PixelInCell.CELL_CENTER);
-            final BufferedImage image = BufferedImages.createImage(width, 
height, sampleDimensions.size(), DataBuffer.TYPE_DOUBLE);
+            final BufferedImage image = new ImageBuilder()
+                    .setSize(width, height)
+                    .setNumBands(sampleDimensions.size())
+                    .setDataType(DataBuffer.TYPE_DOUBLE)
+                    .createBufferedImage();
             final WritableRaster raster = image.getRaster();
 
             // Verify no overflow is possible before allocating any array
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AddDimCodedCoverage.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AddDimCodedCoverage.java
index e1912c4cbe..042dee358e 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AddDimCodedCoverage.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/AddDimCodedCoverage.java
@@ -24,8 +24,8 @@ import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.geometry.Envelopes;
-import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.rs.CodeIterator;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodeTransforms.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodeTransforms.java
index 959f58a1ad..29af53472e 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodeTransforms.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodeTransforms.java
@@ -29,14 +29,14 @@ import 
org.apache.sis.referencing.operation.transform.MathTransforms;
 import org.apache.sis.referencing.operation.transform.TransformSeparator;
 import org.apache.sis.referencing.rs.Code;
 import org.apache.sis.referencing.rs.ReferenceSystems;
+import org.apache.sis.storage.rs.CodeTransform;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.Matrix;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
-import org.apache.sis.storage.rs.CodeTransform;
-import org.opengis.referencing.operation.Matrix;
 
 /**
  *
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodedCoverageAsFeatureSet.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodedCoverageAsFeatureSet.java
index fb0f0b5988..a53af721ab 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodedCoverageAsFeatureSet.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CodedCoverageAsFeatureSet.java
@@ -30,18 +30,20 @@ import org.apache.sis.feature.builder.AttributeRole;
 import org.apache.sis.feature.builder.AttributeTypeBuilder;
 import org.apache.sis.feature.builder.FeatureTypeBuilder;
 import org.apache.sis.feature.internal.shared.AttributeConvention;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.AbstractFeatureSet;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.util.collection.BackingStoreException;
-import org.apache.sis.util.iso.Names;
-import org.apache.sis.util.internal.shared.AbstractIterator;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridSystems;
-import org.apache.sis.referencing.dggs.Zone;
+import org.apache.sis.storage.rs.CodeIterator;
+import org.apache.sis.storage.rs.CodeTransform;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
-import org.apache.sis.referencing.rs.ReferenceSystems;
-import org.apache.sis.storage.rs.CodeIterator;
+import org.apache.sis.storage.rs.WritableCodeIterator;
+import org.apache.sis.util.collection.BackingStoreException;
+import org.apache.sis.util.internal.shared.AbstractIterator;
+import org.apache.sis.util.iso.Names;
 import org.locationtech.jts.geom.CoordinateXY;
 import org.locationtech.jts.geom.Geometry;
 import org.locationtech.jts.geom.Point;
@@ -51,8 +53,6 @@ import org.opengis.feature.PropertyNotFoundException;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.GenericName;
-import org.apache.sis.storage.rs.CodeTransform;
-import org.apache.sis.storage.rs.WritableCodeIterator;
 
 /**
  * View a DGGS Coverage as a FeatureSet.
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CompoundCodedResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CompoundCodedResource.java
index d17daebb78..64d4ebbc52 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CompoundCodedResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/CompoundCodedResource.java
@@ -31,16 +31,15 @@ import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.PixelInCell;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
 import org.apache.sis.referencing.operation.transform.MathTransforms;
+import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.dggs.DiscreteGlobalGridGeometry;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.CodedResource;
-import org.opengis.feature.FeatureType;
 import org.opengis.referencing.ReferenceSystem;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
 import org.opengis.referencing.crs.VerticalCRS;
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
index f9f110e00c..93509afe54 100644
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
+++ 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/rs/internal/shared/GridAsCodedResource.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.storage.rs.internal.shared;
 
-import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedHashSet;
@@ -32,23 +31,26 @@ 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.coverage.grid.GridRoundingMode;
-import org.apache.sis.geometries.math.DataType;
-import org.apache.sis.geometries.math.SampleSystem;
 import org.apache.sis.geometries.math.Array;
+import org.apache.sis.geometries.math.DataType;
 import org.apache.sis.geometries.math.NDArrays;
+import org.apache.sis.geometries.math.SampleSystem;
 import org.apache.sis.referencing.CRS;
+import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
+import org.apache.sis.referencing.rs.Code;
+import org.apache.sis.referencing.rs.ReferenceSystems;
 import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.NoSuchDataException;
 import org.apache.sis.storage.RasterLoadingStrategy;
-import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.referencing.dggs.DiscreteGlobalGridReferenceSystem;
-import org.apache.sis.referencing.rs.Code;
-import org.apache.sis.referencing.rs.ReferenceSystems;
+import 
org.apache.sis.storage.dggs.internal.shared.GridAsDiscreteGlobalGridResource;
+import org.apache.sis.storage.rs.CodeTransform;
 import org.apache.sis.storage.rs.CodedCoverage;
 import org.apache.sis.storage.rs.CodedGeometry;
 import org.apache.sis.storage.rs.CodedResource;
+import org.apache.sis.storage.rs.WritableCodeIterator;
+import org.apache.sis.util.ArgumentChecks;
 import org.opengis.geometry.DirectPosition;
 import org.opengis.geometry.Envelope;
 import org.opengis.referencing.ReferenceSystem;
@@ -57,8 +59,6 @@ import org.opengis.referencing.crs.SingleCRS;
 import org.opengis.referencing.operation.TransformException;
 import org.opengis.util.FactoryException;
 import org.opengis.util.GenericName;
-import org.apache.sis.storage.rs.CodeTransform;
-import org.apache.sis.storage.rs.WritableCodeIterator;
 
 /**
  * View a grid coverage resource as a dggrs coverage resource.
diff --git 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/StringUtilities.java
 
b/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/StringUtilities.java
deleted file mode 100644
index 11d7a17091..0000000000
--- 
a/incubator/src/org.apache.sis.referencing.dggs/main/org/apache/sis/storage/util/StringUtilities.java
+++ /dev/null
@@ -1,500 +0,0 @@
-/*
- * 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.util;
-
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.*;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import org.apache.sis.util.ArgumentChecks;
-import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.StringBuilders;
-
-
-/**
- *
- * @author Cédric Briançon (Geomatys)
- * @author Guilhem Legal (Geomatys)
- */
-public final class StringUtilities {
-
-    public static final String TREE_BLANK = "\u00A0\u00A0\u00A0\u00A0";
-    public static final String TREE_LINE  = "\u00A0\u00A0\u2502\u00A0";
-    public static final String TREE_CROSS = "\u00A0\u00A0\u251C\u2500";
-    public static final String TREE_END   = "\u00A0\u00A0\u2514\u2500";
-    private static final String START = "\u001B[";
-    private static final char END = 'm';
-
-    private static final int[] EMPTY_INT_ARRAY = new int[0];
-
-    private StringUtilities() {}
-
-    /**
-     * Encode the specified string with MD5 algorithm.
-     *
-     * @param key   the string to encode.
-     * @return the value (string) hexadecimal on 32 bits
-     */
-    public static String MD5encode(final String key) {
-
-        final byte[] uniqueKey = key.getBytes();
-        byte[] hash = null;
-        try {
-            // we get an object allowing to crypt the string
-            hash = MessageDigest.getInstance("MD5").digest(uniqueKey);
-
-        } catch (NoSuchAlgorithmException e) {
-            throw new Error("no MD5 support in this VM");
-        }
-        final StringBuffer hashString = new StringBuffer();
-        for (int i = 0; i < hash.length; ++i) {
-            final String hex = Integer.toHexString(hash[i]);
-            if (hex.length() == 1) {
-                hashString.append('0');
-                hashString.append(hex.charAt(hex.length() - 1));
-            } else {
-                hashString.append(hex.substring(hex.length() - 2));
-            }
-        }
-        return hashString.toString();
-    }
-
-    /**
-     * Clean a string from its leading and trailing whitespaces, and the 
tabulation or end of line
-     * characters.
-     */
-    public static String clean(String s) {
-        s = s.trim();
-        s = s.replace("\t", "");
-        s = s.replace("\n", "");
-        s = s.replace("\r", "");
-        return s;
-    }
-
-    /**
-     * Clean a list of String by removing all the white space, tabulation and 
carriage in all the strings.
-     */
-    public static List<String> cleanCharSequences(final List<String> list) {
-        final List<String> result = new ArrayList<String>();
-        for (String s : list) {
-            //we remove the bad character before the real value
-           s = s.replace(" ", "");
-           s = s.replace("\t", "");
-           s = s.replace("\n", "");
-           result.add(s);
-        }
-        return result;
-    }
-
-    /**
-     * Returns true if the list contains a string in one of the list elements.
-     * This test is not case sensitive.
-     *
-     * @see org.apache.sis.util.ArraysExt#containsIgnoreCase(String[], String)
-     *
-     * @param list A list of String.
-     * @param str The value searched.
-     */
-    public static boolean containsIgnoreCase(final List<String> list, final 
String str) {
-        boolean strAvailable = false;
-        if (list != null) {
-            for (String s : list) {
-                if (s.equalsIgnoreCase(str)) {
-                    strAvailable = true;
-                    break;
-                }
-            }
-        }
-        return strAvailable;
-    }
-
-    /**
-     * Generate a {@code String} whose first character will be in upper case.
-     * <p>
-     * For example: {@code firstToUpper("hello")} would return {@code "Hello"},
-     * while {@code firstToUpper("Hi!") would return {@code "Hi!"} unchanged.
-     *
-     * @param s The {@code String} to evaluate, not {@code null}.
-     *
-     * @return A {@code String} with the first character in upper case.
-     */
-    public static String firstToUpper(final String s) {
-        if (s != null && !s.isEmpty()) {
-            final String first = s.substring(0, 1);
-            String result      = s.substring(1);
-            result             = first.toUpperCase() + result;
-            return result;
-        }
-        return s;
-    }
-
-    /**
-     * Search a string for all occurrence of the char.
-     *
-     * @param s  String to search in
-     * @param occ  Occurrence to search
-     * @return array of all occurrence indexes
-     */
-    public static int[] getIndexes(final String s, final char occ) {
-        int pos = s.indexOf(occ);
-        if (pos <0) {
-            return EMPTY_INT_ARRAY;
-        } else {
-            int[] indexes = new int[]{pos};
-            pos = s.indexOf(occ, pos+1);
-            for(; pos >= 0; pos = s.indexOf(occ, pos+1)){
-                int end = indexes.length;
-                indexes = ArraysExt.resize(indexes, end+1);
-                indexes[end] = pos;
-            }
-            return indexes;
-        }
-
-    }
-
-    /**
-     * Convert the given string into a string recognize by HTML.
-     *
-     * @param text The string to convert.
-     * @return The string wit no special chars, which are not recognized in 
HTML.
-     */
-    public static String htmlEncodeSpecialChars(final String text) {
-        final StringBuilder sb = new StringBuilder();
-        for (int i=0; i<text.length(); i++) {
-            final char c = text.charAt(i);
-            if (c > 127) { // special chars
-                sb.append("&#").append((int)c).append(";");
-            } else {
-                sb.append(c);
-            }
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Returns true if one of the {@code String} elements in a {@code List}
-     * matches the given {@code String}, insensitive to case.
-     *
-     * @param list A {@code List<String>} with elements to be tested.
-     * @param str  The {@code String} to evaluate.
-     *
-     * @return {@code true}, if at least one element of the list matches the
-     *           parameter, {@code false} otherwise.
-     */
-    public static boolean matchesStringfromList(final List<String> list,final 
String str) {
-        boolean strAvailable = false;
-        for (String s : list) {
-            final Pattern pattern = 
Pattern.compile(str,Pattern.CASE_INSENSITIVE | Pattern.CANON_EQ);
-            final Matcher matcher = pattern.matcher(s);
-            if (matcher.find()) {
-                strAvailable = true;
-            }
-        }
-        return strAvailable;
-    }
-
-    /**
-     * Replace all the <ns**:localPart and </ns**:localPart by 
<prefix:localPart and </prefix:localPart>
-     */
-    public static String replacePrefix(final String s, final String localPart, 
final String prefix) {
-        return s.replaceAll("[a-zA-Z0-9]*:" + localPart, prefix + ":" + 
localPart);
-    }
-
-    /**
-     * Remove all the XML namespace declaration.
-     *
-     * @deprecated Used only in test classes. Tests should use XML comparator 
instead.
-     */
-    @Deprecated
-    public static String removeXmlns(final String xml) {
-        String s = xml;
-        s = s.replaceAll("xmlns=\"[^\"]*\" ", "");
-        s = s.replaceAll("xmlns=\"[^\"]*\"", "");
-        s = s.replaceAll("xmlns:[^=]*=\"[^\"]*\" ", "");
-        s = s.replaceAll("xmlns:[^=]*=\"[^\"]*\"", "");
-        return s;
-    }
-
-    /**
-     * Remove the prefix on propertyName.
-     * example : removePrefix(csw:GetRecords) return "GetRecords".
-     */
-    public static String removePrefix(String s) {
-        final int i = s.indexOf(':');
-        if ( i != -1) {
-            s = s.substring(i + 1, s.length());
-        }
-        return s;
-    }
-
-    /**
-     * Returns the values of the list separated by commas.
-     *
-     * @param values The collection to extract values.
-     * @return A string which contains values concatened with comma(s), or an 
empty
-     *         string if the list is empty or {@code null}.
-     */
-    public static String toCommaSeparatedValues(final Collection<?> values) {
-        if (values == null || values.isEmpty()) {
-            return "";
-        }
-        final StringBuilder builder = new StringBuilder();
-        boolean first = true;
-        for(Object obj : values){
-            if(first){
-                first = false;
-            }else{
-                builder.append(',');
-            }
-            builder.append(obj);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Returns the values of the array separated by commas.
-     *
-     * @param values The array to extract values.
-     * @return A string which contains values concatened with comma(s), or an 
empty
-     *         string if the array is empty or {@code null}.
-     */
-    public static String toCommaSeparatedValues(final Object ... values) {
-        if (values == null || values.length == 0) {
-            return "";
-        }
-        final StringBuilder builder = new StringBuilder();
-        final int size = values.length;
-        for (int i=0; i<size; i++) {
-            if (i>0) {
-                builder.append(',');
-            }
-            builder.append(values[i]);
-        }
-        return builder.toString();
-    }
-
-    /**
-     * Split a string on commas, and return it as a list.
-     *
-     * @param commaSeparatedString A string which contains comma(s).
-     * @return A list of elements that were contained in the string separated 
by comma(s).
-     */
-    public static List<String> toStringList(final String commaSeparatedString) 
{
-        return toStringList(commaSeparatedString, ',');
-    }
-
-    /**
-     * Split a string on a special character, and return it as a list.
-     *
-     * @param toSplit   A string to split on a specific character.
-     * @param separator The special character on which the given string will 
be splitted.
-     * @return A list of elements that were contained in the string separated 
by the special
-     *         character.
-     *
-     * @deprecated Replaced by {@link 
org.apache.sis.util.CharSequences#split(CharSequence, char)}.
-     */
-    @Deprecated
-    public static List<String> toStringList(String toSplit, final char 
separator) {
-        if (toSplit == null) {
-            return Collections.emptyList();
-        }
-        final List<String> strings = new ArrayList<>();
-        int last = 0;
-        toSplit = toSplit.trim();
-        for (int i=toSplit.indexOf(separator); i>=0; 
i=toSplit.indexOf(separator, i)) {
-            strings.add(toSplit.substring(last, i).trim());
-            last = ++i;
-        }
-        strings.add(toSplit.substring(last).trim());
-        return strings;
-    }
-
-    /**
-     * Formats the given elements as a (typically) comma-separated list. This 
method is similar to
-     * {@link java.util.AbstractCollection#toString()} or {@link 
java.util.Arrays#toString(Object[])}
-     * except for the following:
-     *
-     * <ul>
-     *   <li>There is no leading {@code '['} or trailing {@code ']'} 
characters.</li>
-     *   <li>Null elements are ignored instead than formatted as {@code 
"null"}.</li>
-     *   <li>If the {@code collection} argument is null or contains only null 
elements,
-     *       then this method returns {@code null}.</li>
-     *   <li>In the common case where the collection contains a single {@link 
String} element,
-     *       that string is returned directly (no object duplication).</li>
-     * </ul>
-     *
-     * @param  collection The elements to format in a (typically) 
comma-separated list, or {@code null}.
-     * @param  separator  The element separator, which is usually {@code ", "}.
-     * @return The (typically) comma-separated list, or {@code null} if the 
given {@code collection}
-     *         was null or contains only null elements.
-     *
-     * @see java.util.StringJoiner
-     * @see java.util.Arrays#toString(Object[])
-     */
-    public static String toString(final Iterable<?> collection, final String 
separator) {
-        ArgumentChecks.ensureNonNull("separator", separator);
-        String list = null;
-        if (collection != null) {
-            StringBuilder buffer = null;
-            for (final Object element : collection) {
-                if (element != null) {
-                    if (list == null) {
-                        list = element.toString();
-                    } else {
-                        if (buffer == null) {
-                            buffer = new StringBuilder(list);
-                        }
-                        buffer.append(separator);
-                        if (element instanceof CharSequence) {
-                            // StringBuilder has numerous optimizations for 
this case.
-                            buffer.append((CharSequence) element);
-                        } else {
-                            buffer.append(element);
-                        }
-                    }
-                }
-            }
-            if (buffer != null) {
-                list = buffer.toString();
-            }
-        }
-        return list;
-    }
-
-    /**
-     * Transform an exception code into the OWS specification.
-     * Example : MISSING_PARAMETER_VALUE become MissingParameterValue.
-     */
-    public static String transformCodeName(String code) {
-        final StringBuilder result = new StringBuilder();
-        while (code.indexOf('_') != -1) {
-            final String tmp = code.substring(0, 
code.indexOf('_')).toLowerCase();
-            result.append(firstToUpper(tmp));
-            code = code.substring(code.indexOf('_') + 1, code.length());
-        }
-        code = code.toLowerCase();
-        result.append(firstToUpper(code));
-        return result.toString();
-    }
-
-    public static String toStringTree(final Object ... objects){
-        return toStringTree("", Arrays.asList(objects));
-    }
-
-    /**
-     * Returns a graphical representation of the specified objects. This 
representation can be
-     * printed to the {@linkplain System#out standard output stream} (for 
example) if it uses
-     * a monospaced font and supports unicode.
-     *
-     * @param  root  The root name of the tree to format.
-     * @param  objects The objects to format as root children.
-     * @return A string representation of the tree.
-     */
-    public static String toStringTree(String root, final Iterable<?> objects) {
-        final StringBuilder sb = new StringBuilder();
-        if (root != null) {
-            sb.append(root);
-        }
-        if (objects != null) {
-            final Iterator<?> ite = objects.iterator();
-            while (ite.hasNext()) {
-                sb.append('\n');
-                final Object next = ite.next();
-                final boolean last = !ite.hasNext();
-                sb.append(last ? "\u2514\u2500 " : "\u251C\u2500 ");
-
-                final String[] parts = String.valueOf(next).split("\n");
-                sb.append(parts[0]);
-                for (int k=1;k<parts.length;k++) {
-                    sb.append('\n');
-                    sb.append(last ? ' ' : '\u2502');
-                    sb.append("  ");
-                    sb.append(parts[k]);
-                }
-            }
-        }
-        return sb.toString();
-    }
-
-
-    /**
-     * Replaces escape codes in the given string by HTML {@code <font>} 
instructions.
-     * If no HTML instruction is associated to the given escape code, then the 
escape
-     * sequence is removed.
-     *
-     * @param  text The text with X3.64 sequences.
-     * @return The text with HTML {@code <font>} instructions.
-     */
-    public static String X364toHTML(final String text) {
-
-        final StringBuilder buffer = new StringBuilder(text);
-        StringBuilders.replace(buffer, "&", "&amp;");
-        StringBuilders.replace(buffer, "<", "&lt;");
-        StringBuilders.replace(buffer, ">", "&gt;");
-        boolean fontApplied = false;
-        StringBuilder tmp = null;
-        for (int i=buffer.indexOf(START); i>=0; i=buffer.indexOf(START, i)) {
-            int lower  = i + START.length();
-            int upper  = lower;
-            int length = buffer.length();
-            while (upper < length) {
-                if (buffer.charAt(upper++) == END) {
-                    break;
-                }
-            }
-            final int code;
-            try {
-                code = Integer.parseInt(buffer.substring(lower, upper-1));
-            } catch (NumberFormatException e) {
-                buffer.delete(i, upper);
-                continue;
-            }
-            final String color;
-            switch (code) {
-                case 31: color="red";     break;
-                case 32: color="green";   break;
-                case 33: color="olive";   break; // "yellow" is too bright.
-                case 34: color="blue";    break;
-                case 35: color="magenta"; break;
-                case 36: color="teal";    break; // "cyan" is not in HTML 4, 
while "teal" is.
-                case 37: color="gray";    break;
-                case 39: // Fall through
-                case 0:  color=null; break;
-                default: {
-                    buffer.delete(i, upper);
-                    continue;
-                }
-            }
-            if (tmp == null) {
-                tmp = new StringBuilder(24);
-            }
-            if (fontApplied) {
-                tmp.append("</font>");
-                fontApplied = false;
-            }
-            if (color != null) {
-                tmp.append("<font color=\"").append(color).append("\">");
-                fontApplied = true;
-            }
-            buffer.replace(i, upper, tmp.toString());
-            tmp.setLength(0);
-        }
-        final String result = buffer.toString();
-        return result.equals(text) ? text : result;
-    }
-}

Reply via email to