sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/01: Merge branch 'geoapi-3.1'. The main topic is more development in netCDF reader, together with improvements in other modules in support of netCDF needs.
Date Wed, 17 Apr 2019 09:34:43 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sis.git

commit e55b12890fe61d1f31f852e69a92329ce8b051b6
Merge: 002121a 93dca74
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Apr 17 11:33:11 2019 +0200

    Merge branch 'geoapi-3.1'. The main topic is more development in netCDF reader, together with improvements in other modules in support of netCDF needs.

 .../sis/internal/metadata/AxisDirections.java      |  60 +-
 .../main/java/org/apache/sis/io/wkt/Warnings.java  |   2 +-
 core/sis-raster/pom.xml                            |  14 +-
 .../java/org/apache/sis/coverage/CategoryList.java |  19 +-
 .../org/apache/sis/coverage/SampleDimension.java   |  27 +-
 .../sis/coverage/grid/DisjointExtentException.java |  76 +++
 .../org/apache/sis/coverage/grid/GridCoverage.java |  69 ++-
 .../apache/sis/coverage/grid/GridDerivation.java   | 113 +++-
 .../org/apache/sis/coverage/grid/GridExtent.java   |  67 ++-
 .../org/apache/sis/coverage/grid/GridGeometry.java |  19 +-
 .../apache/sis/coverage/grid/ImageRenderer.java    | 205 +++++--
 .../java/org/apache/sis/image/DefaultIterator.java |  24 +-
 .../java/org/apache/sis/image/LinearIterator.java  |  89 +++
 .../java/org/apache/sis/image/PixelIterator.java   | 127 ++++-
 .../java/org/apache/sis/image/SequenceType.java    |  30 +
 .../internal/coverage/BufferedGridCoverage.java    | 161 ++++++
 .../internal/coverage/ConvertedGridCoverage.java   | 497 ++++++++++++++++
 .../apache/sis/internal/coverage/package-info.java |  31 +
 .../sis/internal/raster/ColorModelFactory.java     |  99 ++--
 .../apache/sis/internal/raster/RasterFactory.java  |  39 +-
 .../org/apache/sis/internal/raster/Resources.java  |  13 +-
 .../sis/internal/raster/Resources.properties       |   4 +-
 .../sis/internal/raster/Resources_fr.properties    |   3 +-
 .../sis/internal/raster/ScaledColorSpace.java      |  10 +-
 .../org/apache/sis/coverage/CategoryListTest.java  |  14 +-
 .../apache/sis/coverage/SampleDimensionTest.java   |  15 +-
 .../sis/coverage/grid/GridDerivationTest.java      | 124 +++-
 .../apache/sis/coverage/grid/GridExtentTest.java   |  42 +-
 .../org/apache/sis/image/DefaultIteratorTest.java  | 151 +++--
 .../org/apache/sis/image/LinearIteratorTest.java   | 133 +++++
 .../coverage/BufferedGridCoverageTest.java         | 117 ++++
 .../org/apache/sis/test/suite/RasterTestSuite.java |   4 +-
 .../java/org/apache/sis/geometry/Envelopes.java    |   2 +-
 .../internal/referencing/ReferencingUtilities.java | 215 -------
 .../internal/referencing/ServicesForMetadata.java  |   6 +-
 .../internal/referencing/WraparoundAdjustment.java | 453 +++++++++++++++
 .../referencing/j2d/AffineTransform2D.java         |   4 +-
 .../provider/DatumShiftGridCompressed.java         |  64 ++-
 .../referencing/provider/DatumShiftGridFile.java   | 126 ++---
 .../referencing/provider/DatumShiftGridLoader.java |   4 +-
 .../referencing/provider/Equirectangular.java      |  16 +-
 .../provider/FranceGeocentricInterpolation.java    |   2 +-
 .../sis/internal/referencing/provider/NADCON.java  |   4 +-
 .../sis/internal/referencing/provider/NTv2.java    |   2 +-
 .../referencing/provider/PseudoPlateCarree.java    |  71 +++
 .../main/java/org/apache/sis/referencing/CRS.java  |   4 +-
 .../java/org/apache/sis/referencing/CommonCRS.java |  28 +-
 .../sis/referencing/StandardDefinitions.java       |   4 +-
 .../sis/referencing/datum/DatumShiftGrid.java      | 356 +++++++-----
 .../referencing/operation/DefaultConversion.java   |   2 +-
 .../operation/builder/LinearTransformBuilder.java  | 105 +++-
 .../operation/builder/LocalizationGridBuilder.java | 114 ++--
 .../operation/builder/ProjectedTransformTry.java   |  58 +-
 .../operation/builder/ResidualGrid.java            | 147 +++--
 .../sis/referencing/operation/builder/readme.html  |  48 ++
 .../operation/transform/ContextualParameters.java  |  25 +-
 .../transform/CoordinateSystemTransform.java       |   8 +-
 .../operation/transform/DatumShiftTransform.java   | 109 +++-
 .../transform/EllipsoidToCentricTransform.java     |   6 +-
 .../transform/InterpolatedGeocentricTransform.java |  15 +-
 .../transform/InterpolatedMolodenskyTransform.java |  15 +-
 .../operation/transform/InterpolatedTransform.java | 275 ++++-----
 .../operation/transform/MolodenskyFormula.java     |   4 +-
 ...g.opengis.referencing.operation.OperationMethod |   1 +
 .../referencing/ReferencingUtilitiesTest.java      | 111 +---
 .../referencing/WraparoundAdjustmentTest.java      | 209 +++++++
 .../provider/DatumShiftGridCompressedTest.java     |  46 ++
 .../provider/DatumShiftGridFileTest.java           | 240 ++++++++
 .../referencing/provider/ProvidersTest.java        |   1 +
 .../sis/referencing/EPSGFactoryFallbackTest.java   |   4 +-
 .../builder/LocalizationGridBuilderTest.java       |  11 +-
 .../operation/builder/ResidualGridTest.java        |   4 +-
 .../transform/ContextualParametersTest.java        |   8 +-
 .../transform/InterpolatedTransformTest.java       | 205 +++++--
 .../operation/transform/MathTransformTestCase.java |  23 +-
 .../operation/transform/SinusoidalShiftGrid.java   | 175 ++++++
 .../sis/test/suite/ReferencingTestSuite.java       |   3 +
 .../java/org/apache/sis/internal/jdk9/JDK9.java    |  16 +
 .../org/apache/sis/internal/util/Constants.java    |  14 -
 .../org/apache/sis/internal/util/Numerics.java     |   2 +-
 .../java/org/apache/sis/internal/util/Strings.java |  28 +-
 .../java/org/apache/sis/math/MathFunctions.java    |   6 +-
 .../java/org/apache/sis/math/RepeatedVector.java   |  22 +-
 .../src/main/java/org/apache/sis/math/Vector.java  |  50 +-
 .../main/java/org/apache/sis/util/ArraysExt.java   |  30 +-
 .../java/org/apache/sis/util/resources/Errors.java |   2 +-
 .../apache/sis/util/resources/Errors.properties    |   2 +-
 .../org/apache/sis/math/MathFunctionsTest.java     |   7 +-
 .../org/apache/sis/math/RepeatedVectorTest.java    |  53 ++
 ide-project/NetBeans/build.xml                     |   7 +
 pom.xml                                            |  12 +-
 storage/sis-earth-observation/pom.xml              |   6 +
 .../apache/sis/internal/earth/netcdf/GCOM_C.java   | 324 +++++++++++
 .../apache/sis/internal/earth/netcdf/GCOM_W.java   | 222 ++++++++
 .../sis/internal/earth/netcdf/package-info.java    |  28 +
 .../org.apache.sis.internal.netcdf.Convention      |   2 +
 .../apache/sis/storage/geotiff/Localization.java   |   2 +
 storage/sis-netcdf/pom.xml                         |  14 +
 .../java/org/apache/sis/internal/netcdf/Axis.java  |  77 ++-
 .../org/apache/sis/internal/netcdf/CRSBuilder.java | 389 ++++++++-----
 .../org/apache/sis/internal/netcdf/Convention.java |  29 +-
 .../org/apache/sis/internal/netcdf/Decoder.java    |  86 ++-
 .../org/apache/sis/internal/netcdf/Dimension.java  |   2 +-
 .../java/org/apache/sis/internal/netcdf/Grid.java  | 117 +++-
 .../apache/sis/internal/netcdf/GridMapping.java    | 385 +++++++++++++
 .../org/apache/sis/internal/netcdf/Linearizer.java | 172 ++++++
 .../Image.java => internal/netcdf/Raster.java}     |  41 +-
 .../apache/sis/internal/netcdf/RasterResource.java | 629 +++++++++++++++++++++
 .../org/apache/sis/internal/netcdf/Resources.java  |   8 +-
 .../sis/internal/netcdf/Resources.properties       |   3 +-
 .../sis/internal/netcdf/Resources_fr.properties    |   3 +-
 .../sis/internal/netcdf/SatelliteGroundTrack.java  | 246 ++++++++
 .../org/apache/sis/internal/netcdf/Variable.java   | 530 ++++++++++++-----
 .../sis/internal/netcdf/impl/ChannelDecoder.java   |  11 +-
 .../apache/sis/internal/netcdf/impl/GridInfo.java  |  32 +-
 .../org/apache/sis/internal/netcdf/impl/HYCOM.java |   3 +-
 .../sis/internal/netcdf/impl/VariableInfo.java     |  33 +-
 .../internal/netcdf/ucar/CSBuilderFallback.java    |  93 +++
 .../sis/internal/netcdf/ucar/DecoderWrapper.java   |  23 +-
 .../sis/internal/netcdf/ucar/GridWrapper.java      |  41 +-
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |  86 ++-
 .../apache/sis/storage/netcdf/GridResource.java    | 470 ---------------
 .../apache/sis/storage/netcdf/MetadataReader.java  |   3 +-
 .../org/apache/sis/storage/netcdf/NetcdfStore.java |   3 +-
 .../org/apache/sis/internal/netcdf/GridTest.java   |   4 +-
 .../internal/netcdf/SatelliteGroundTrackTest.java  |  81 +++
 .../org/apache/sis/test/suite/NetcdfTestSuite.java |   1 +
 .../sis/internal/storage/AbstractGridResource.java | 216 ++++++-
 .../org/apache/sis/internal/storage/Resources.java |   7 +-
 .../sis/internal/storage/Resources.properties      |   5 +-
 .../sis/internal/storage/Resources_fr.properties   |   1 +
 .../sis/storage/DataStoreReferencingException.java |  10 +-
 .../org/apache/sis/storage/DataStoreRegistry.java  |   3 +-
 .../apache/sis/storage/GridCoverageResource.java   |  16 +-
 .../sis/storage/IllegalFeatureTypeException.java   |   3 +
 .../org/apache/sis/storage/WritableAggregate.java  |  14 +-
 .../org/apache/sis/storage/WritableFeatureSet.java |  43 +-
 .../sis/storage/WritableGridCoverageResource.java  | 106 ++++
 .../internal/storage/AbstractGridResourceTest.java |  93 +++
 .../apache/sis/test/suite/StorageTestSuite.java    |   1 +
 140 files changed, 8445 insertions(+), 2254 deletions(-)

diff --cc core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
index 2062d88,c8ecffa..ce770cf
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridCoverage.java
@@@ -175,11 -218,12 +215,12 @@@ public abstract class GridCoverage 
       *
       * @param  sliceExtent  a subspace of this grid coverage extent where all dimensions except two have a size of 1 cell.
       *         May be {@code null} if this grid coverage has only two dimensions with a size greater than 1 cell.
-      * @return the grid slice as a rendered image.
+      * @return the grid slice as a rendered image. Image location is relative to {@code sliceExtent}.
       * @throws SubspaceNotSpecifiedException if the given argument is not sufficient for reducing the grid to a two-dimensional slice.
+      * @throws DisjointExtentException if the given extent does not intersect this grid coverage.
 -     * @throws CannotEvaluateException if this method can not produce the rendered image for another reason.
 +     * @throws RuntimeException if this method can not produce the rendered image for another reason.
       */
 -    public abstract RenderedImage render(GridExtent sliceExtent) throws CannotEvaluateException;
 +    public abstract RenderedImage render(GridExtent sliceExtent);
  
      /**
       * Returns a string representation of this grid coverage for debugging purpose.
diff --cc core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
index 203ee2a,4db9d15..1bc6764
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/grid/GridDerivation.java
@@@ -714,13 -746,19 +743,19 @@@ public class GridDerivation 
       *       before to invoke this method.</li>
       *   <li>This method does not reduce the number of dimensions of the grid geometry.
       *       For dimensionality reduction, see {@link GridGeometry#reduce(int...)}.</li>
+      *   <li>If the given point is known to be expressed in the same CRS than the grid geometry,
+      *       then the {@linkplain DirectPosition#getCoordinateReferenceSystem() CRS of the point}
+      *       can be left unspecified ({@code null}). It may give a slight performance improvement
+      *       by avoiding the check for coordinate transformation.</li>
       * </ul>
       *
-      * @param  slicePoint   the coordinates where to get a slice.
+      * @param  slicePoint   the coordinates where to get a slice. If no coordinate reference system is attached to it,
+      *                      we consider it's the same as base grid geometry.
       * @return {@code this} for method call chaining.
-      * @throws IncompleteGridGeometryException if the base grid geometry has no extent or no "grid to CRS" transform.
+      * @throws IncompleteGridGeometryException if the base grid geometry has no extent, no "grid to CRS" transform,
+      *         or no CRS (unless {@code slicePoint} has no CRS neither, in which case the CRS are assumed the same).
       * @throws IllegalGridGeometryException if an error occurred while converting the point coordinates to grid coordinates.
 -     * @throws PointOutsideCoverageException if the given point is outside the grid extent.
 +     * @throws RuntimeException if the given point is outside the grid extent.
       */
      public GridDerivation slice(final DirectPosition slicePoint) {
          ArgumentChecks.ensureNonNull("slicePoint", slicePoint);
diff --cc core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
index 0000000,c586550..6be8974
mode 000000,100644..100644
--- a/core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/LinearIterator.java
@@@ -1,0 -1,90 +1,89 @@@
+ /*
+  * 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.image;
+ 
+ import java.awt.Dimension;
+ import java.awt.Rectangle;
+ import java.awt.image.Raster;
+ import java.awt.image.RenderedImage;
+ import java.awt.image.WritableRaster;
+ import java.awt.image.WritableRenderedImage;
 -import org.opengis.coverage.grid.SequenceType;
+ import org.apache.sis.internal.raster.Resources;
+ 
+ 
+ /**
+  * Linear iterator used when common line y line iteration is requiered.
+  * This iterator uses the {@link Raster} API for traversing the pixels of the image.
+  * Calls to {@link #next()} move the current position by increasing the following values, in order:
+  *
+  * <ol>
+  *   <li>Column index in image (from left to right)</li>
+  *   <li>Row index in image (from top to bottom).</li>
+  * </ol>
+  *
+  * @author  Johann Sorel (Geomatys)
+  * @version 1.0
+  * @since   1.0
+  * @module
+  */
+ final class LinearIterator extends DefaultIterator {
+ 
+     LinearIterator(Raster input, WritableRaster output, Rectangle subArea, Dimension window) {
+         super(input, output, subArea, window);
+     }
+ 
+     LinearIterator(final RenderedImage input, final WritableRenderedImage output, final Rectangle subArea, final Dimension window) {
+         super(input, output, subArea, window);
+     }
+ 
+     @Override
+     public SequenceType getIterationOrder() {
+         return SequenceType.LINEAR;
+     }
+ 
+     @Override
+     public boolean next() {
+ 
+         if (++x >= upperX) {
+             //move to next line
+             x = lowerX;
+             if (++y >= upperY) {
+                 x = lowerX;
+                 return false;
+             }
+         } else if (y >= upperY) {
+             x = lowerX;
+             //second time or more get in the next method, raise error
+             throw new IllegalStateException(Resources.format(Resources.Keys.IterationIsFinished));
+         }
+ 
+         if (image != null) {
+             final int tx = Math.floorDiv(x - tileGridXOffset, tileWidth);
+             final int ty = Math.floorDiv(y - tileGridYOffset, tileHeight);
+             if (tx != tileX || ty != tileY) {
+                 close(); // Release current writable raster, if any.
+                 tileX = tx;
+                 tileY = ty;
+                 int ry = y;
+                 fetchTile();
+                 y = ry; //y is changed by fetchTile method
+             }
+         }
+         return true;
+     }
+ 
+ }
diff --cc core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
index c27154c,5499140..f5cdd96
--- a/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/PixelIterator.java
@@@ -24,8 -26,14 +26,13 @@@ import java.awt.image.Raster
  import java.awt.image.RenderedImage;
  import java.awt.image.WritableRaster;
  import java.awt.image.WritableRenderedImage;
+ import java.awt.image.SampleModel;
+ import java.awt.image.SinglePixelPackedSampleModel;
+ import java.awt.image.MultiPixelPackedSampleModel;
  import java.util.NoSuchElementException;
 -import org.opengis.coverage.grid.SequenceType;
+ import org.apache.sis.util.resources.Errors;
  import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.measure.NumberRange;
  
  import static java.lang.Math.floorDiv;
  import static org.apache.sis.internal.util.Numerics.ceilDiv;
@@@ -243,6 -257,32 +256,32 @@@ public abstract class PixelIterator 
          }
  
          /**
+          * Sets the desired iteration order.
+          * The {@code order} argument can have the following values:
+          *
+          * <table class="sis">
+          *   <caption>Supported iteration order</caption>
+          *   <tr><th>Value</th>                         <th>Iteration order</th>                                <th>Supported on</th></tr>
+          *   <tr><td>{@code null}</td>                  <td>Most efficient iteration order.</td>                <td>Image and raster</td></tr>
+          *   <tr><td>{@link SequenceType#LINEAR}</td>   <td>From left to right, then from top to bottom.</td>   <td>Raster only</td></tr>
+          * </table>
+          *
+          * Any other {@code order} value will cause an {@link IllegalArgumentException} to be thrown.
+          * More iteration orders may be supported in future Apache SIS versions.
+          *
+          * @param  order  the desired iteration order, or {@code null} for a default order.
+          * @return {@code this} for method call chaining.
+          */
 -        public Builder setIteratorOrder(final SequenceType order) {
++        final Builder setIteratorOrder(final SequenceType order) {
+             if (order == null || order.equals(SequenceType.LINEAR)) {
+                 this.order = order;
+             } else {
+                 throw new IllegalArgumentException(Errors.format(Errors.Keys.UnsupportedType_1, order));
+             }
+             return this;
+         }
+ 
+         /**
           * Creates a read-only iterator for the given raster.
           *
           * @param  data  the raster which contains the sample values on which to iterate.
@@@ -358,6 -408,78 +407,78 @@@
      }
  
      /**
+      * Returns the range of sample values that can be stored in each band of the rendered image or raster.
+      * The ranges depend on the data type (byte, integer, <i>etc.</i>) and the number of bits per sample.
+      * If the samples are stored as floating point values, then the ranges are infinite (unbounded).
+      *
+      * <p>Usually, the range is the same for all bands. A situation where the ranges may differ is when an
+      * image uses {@link SinglePixelPackedSampleModel}, in which case the number of bits per pixel may vary
+      * for different bands.</p>
+      *
+      * @return the ranges of valid sample values for each band. Ranges may be {@linkplain NumberRange#isBounded() unbounded}.
+      */
+     public NumberRange<?>[] getSampleRanges() {
+         final SampleModel model = (currentRaster != null) ? currentRaster.getSampleModel() : image.getSampleModel();
+         final NumberRange<?>[] ranges = new NumberRange<?>[model.getNumBands()];
+         final NumberRange<?> range;
+         if (model instanceof MultiPixelPackedSampleModel) {
+             /*
+              * This model supports only unsigned integer types: DataBuffer.TYPE_BYTE, DataBuffer.TYPE_USHORT
+              * or DataBuffer.TYPE_INT (considered unsigned in the context of this sample model).  The number
+              * of bits per sample is defined by the "pixel bit stride".
+              */
+             final int numBits = ((MultiPixelPackedSampleModel) model).getPixelBitStride();
+             range = NumberRange.create(0, true, (1 << numBits) - 1, true);
+         } else if (model instanceof SinglePixelPackedSampleModel) {
+             /*
+              * This model supports only unsigned integer types: TYPE_BYTE, TYPE_USHORT, TYPE_INT (considered
+              * unsigned in the context of this sample model). The number of bits may vary for each band.
+              */
+             final int[] masks = ((SinglePixelPackedSampleModel) model).getBitMasks();
+             for (int i=0; i<masks.length; i++) {
+                 final int numBits = Integer.bitCount(masks[i]);
+                 ranges[i] = NumberRange.create(0, true, (1 << numBits) - 1, true);
+             }
+             return ranges;
+         } else {
+             /*
+              * For all other sample models, the range is determined by the data type.
+              * The following cases invoke the NumberRange constructor which best fit the data type.
+              */
+             final int type = model.getDataType();
+             switch (type) {
+                 case DataBuffer.TYPE_BYTE:   range = NumberRange.create((short) 0,                 true,  (short)   0xFF,            true);  break;
+                 case DataBuffer.TYPE_USHORT: range = NumberRange.create(        0,                 true,          0xFFFF,            true);  break;
+                 case DataBuffer.TYPE_SHORT:  range = NumberRange.create(Short.  MIN_VALUE,         true,  Short.  MAX_VALUE,         true);  break;
+                 case DataBuffer.TYPE_INT:    range = NumberRange.create(Integer.MIN_VALUE,         true,  Integer.MAX_VALUE,         true);  break;
+                 case DataBuffer.TYPE_FLOAT:  range = NumberRange.create(Float.  NEGATIVE_INFINITY, false, Float.  POSITIVE_INFINITY, false); break;
+                 case DataBuffer.TYPE_DOUBLE: range = NumberRange.create(Double. NEGATIVE_INFINITY, false, Double. POSITIVE_INFINITY, false); break;
+                 default: throw new IllegalStateException(Errors.format(Errors.Keys.UnknownType_1, type));
+             }
+         }
+         Arrays.fill(ranges, range);
+         return ranges;
+     }
+ 
+     /**
+      * Returns the order in which pixels are traversed. {@link SequenceType#LINEAR} means that pixels on the first
+      * row are traversed from left to right, then pixels on the second row from left to right, <i>etc.</i>
+      * A {@code null} value means that the iteration order is unspecified.
+      *
+      * @return order in which pixels are traversed, or {@code null} if unspecified.
+      */
 -    public abstract SequenceType getIterationOrder();
++    abstract SequenceType getIterationOrder();
+ 
+     /**
+      * Returns the number of bands (samples per pixel) in the image or raster.
+      *
+      * @return number of bands.
+      */
+     public int getNumBands() {
+         return numBands;
+     }
+ 
+     /**
       * Returns the pixel coordinates of the region where this iterator is doing the iteration.
       * If no region was specified at construction time, then this method returns the image or raster bounds.
       *
@@@ -506,8 -628,8 +627,8 @@@
       * <var>(number of bands)</var> × <var>(window width)</var> × <var>(window height)</var>.
       * Values are always stored with band index varying fastest, then column index, then row index.
       * Columns are traversed from left to right and rows are traversed from top to bottom
 -     * ({@link SequenceType#LINEAR} iteration order).
 +     * (linear iteration order).
-      * That order is the same regardless the iteration order of this iterator.
+      * That order is the same regardless the {@linkplain #getIterationOrder() iteration order} of this iterator.
       *
       * <div class="note"><b>Example:</b>
       * for an RGB image, the 3 first values are the red, green and blue components of the pixel at
diff --cc core/sis-raster/src/main/java/org/apache/sis/image/SequenceType.java
index 0000000,7291456..8e42823
mode 000000,100644..100644
--- a/core/sis-raster/src/main/java/org/apache/sis/image/SequenceType.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/image/SequenceType.java
@@@ -1,0 -1,31 +1,30 @@@
+ /*
+  * 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.image;
++
+ 
+ /**
 - * Filters features according their properties.
 - * A <cite>filter expression</cite> is a construct used to constraint a feature set to a subset.
++ * Placeholder for {@code org.opengis.coverage.grid.SequenceType}.
+  *
 - * @author  Johann Sorel (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.0
 - *
 - * @see <a href="http://docs.opengeospatial.org/is/09-026r2/09-026r2.html">OGC® Filter Encoding 2.0 Encoding Standard</a>
 - *
 - * @since 1.0
++ * @since   1.0
+  * @module
+  */
 -package org.apache.sis.filter;
++enum SequenceType {
++    LINEAR
++}
diff --cc core/sis-raster/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
index 0000000,0fae561..5f75624
mode 000000,100644..100644
--- a/core/sis-raster/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/internal/coverage/BufferedGridCoverage.java
@@@ -1,0 -1,164 +1,161 @@@
+ /*
+  * Licensed to the Apache Software Foundation (ASF) under one or more
+  * contributor license agreements.  See the NOTICE file distributed with
+  * this work for additional information regarding copyright ownership.
+  * The ASF licenses this file to You under the Apache License, Version 2.0
+  * (the "License"); you may not use this file except in compliance with
+  * the License.  You may obtain a copy of the License at
+  *
+  *     http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  */
+ package org.apache.sis.internal.coverage;
+ 
+ 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.DataBufferUShort;
+ import java.awt.image.RasterFormatException;
+ import java.awt.image.RenderedImage;
+ import java.awt.image.SampleModel;
+ import java.util.Collection;
+ import org.apache.sis.coverage.SampleDimension;
+ import org.apache.sis.coverage.grid.GridCoverage;
+ import org.apache.sis.coverage.grid.GridExtent;
+ import org.apache.sis.coverage.grid.GridGeometry;
+ import org.apache.sis.coverage.grid.ImageRenderer;
+ import org.apache.sis.util.ArgumentChecks;
+ import org.apache.sis.util.resources.Errors;
+ 
 -// Branch-specific imports
 -import org.opengis.coverage.CannotEvaluateException;
 -
+ 
+ /**
+  * A {@link GridCoverage} with data stored in an in-memory Java2D buffer.
+  * Those data can be shown as {@link RenderedImage}.
+  *
+  * @author  Johann Sorel (Geomatys)
+  * @version 1.0
+  * @since   1.0
+  * @module
+  */
+ public class BufferedGridCoverage extends GridCoverage {
+     /**
+      * The sample values, potentially multi-banded. The bands may be stored either in a single bank (pixel interleaved image)
+      * or in different banks (banded image). This class detects automatically which of those two sample models is used when
+      * {@link #render(GridExtent)} is invoked.
+      */
+     protected final DataBuffer data;
+ 
+     /**
+      * Result of the call to {@link #forConvertedValues(boolean)}, created when first needed.
+      */
+     private GridCoverage converted;
+ 
+     /**
+      * Constructs a grid coverage using the specified grid geometry, sample dimensions and data buffer.
+      * This method stores the given buffer by reference (no copy).
+      *
+      * @param grid   the grid extent, CRS and conversion from cell indices to CRS.
+      * @param bands  sample dimensions for each image band.
+      * @param data   the sample values, potentially multi-banded.
+      */
+     public BufferedGridCoverage(final GridGeometry grid, final Collection<? extends SampleDimension> bands, final DataBuffer data) {
+         super(grid, bands);
+         this.data = data;
+         ArgumentChecks.ensureNonNull("data", data);
+     }
+ 
+     /**
+      * Constructs a grid coverage using the specified grid geometry, sample dimensions and data type.
+      * This constructor create a single-bank {@link DataBuffer} (pixel interleaved sample model) with
+      * all sample values initialized to zero.
+      *
+      * @param  grid      the grid extent, CRS and conversion from cell indices to CRS.
+      * @param  bands     sample dimensions for each image band.
+      * @param  dataType  one of {@code DataBuffer.TYPE_*} constants, the native data type used to store the coverage values.
+      * @throws ArithmeticException if the grid size is too large.
+      */
+     public BufferedGridCoverage(final GridGeometry grid, final Collection<? extends SampleDimension> bands, final int dataType) {
+         super(grid, bands);
+         long nbSamples = bands.size();
+         final GridExtent extent = grid.getExtent();
+         for (int i = grid.getDimension(); --i >= 0;) {
+             nbSamples = Math.multiplyExact(nbSamples, extent.getSize(i));
+         }
+         final int n = Math.toIntExact(nbSamples);
+         switch (dataType) {
+             case DataBuffer.TYPE_BYTE:   data = new DataBufferByte  (n); break;
+             case DataBuffer.TYPE_SHORT:  data = new DataBufferShort (n); break;
+             case DataBuffer.TYPE_USHORT: data = new DataBufferUShort(n); break;
+             case DataBuffer.TYPE_INT:    data = new DataBufferInt   (n); break;
+             case DataBuffer.TYPE_FLOAT:  data = new DataBufferFloat (n); break;
+             case DataBuffer.TYPE_DOUBLE: data = new DataBufferDouble(n); break;
+             default: throw new IllegalArgumentException(Errors.format(Errors.Keys.UnknownType_1, dataType));
+         }
+     }
+ 
+     /**
+      * Returns a two-dimensional slice of grid data as a rendered image.
+      * This method returns a view; sample values are not copied.
+      *
+      * @return the grid slice as a rendered image.
+      */
+     @Override
+     public RenderedImage render(final GridExtent sliceExtent) {
+         try {
+             final ImageRenderer renderer = new ImageRenderer(this, sliceExtent);
+             renderer.setData(data);
+             return renderer.image();
+         } catch (IllegalArgumentException | ArithmeticException | RasterFormatException e) {
 -            throw new CannotEvaluateException(e.getMessage(), e);
++            throw new RuntimeException(e.getMessage(), e);
+         }
+     }
+ 
+     /**
+      * Returns a grid coverage that contains real values or sample values, depending if {@code converted} is {@code true}
+      * or {@code false} respectively.
+      *
+      * If the given value is {@code false}, then the default implementation returns a grid coverage which produces
+      * {@link RenderedImage} views. Those views convert each sample value on the fly. This is known to be very slow
+      * if an entire raster needs to be processed, but this is temporary until another implementation is provided in
+      * a future SIS release.
+      *
+      * @return a coverage containing converted or packed values, depending on {@code converted} argument value.
+      */
+     @Override
+     public GridCoverage forConvertedValues(final boolean converted) {
+         if (converted) {
+             synchronized (this) {
+                 if (this.converted == null) {
+                     this.converted = convert(this);
+                 }
+                 return this.converted;
+             }
+         }
+         return this;
+     }
+ 
+     /**
+      * Returns a coverage for converted values. If the given coverage is already converted,
+      * then this method returns the given {@code coverage} unchanged.
+      *
+      * <p><b>WARNING: this is a temporary implementation.</b>
+      * This method uses a special {@link SampleModel} in departure with the contract documented in JDK javadoc.
+      * That sample model does not only define the sample layout (pixel stride, scanline stride, <i>etc.</i>), but
+      * also converts the sample values. This may be an issue for optimized pipelines accessing {@link DataBuffer}
+      * directly. This method may be replaced by another mechanism (creating new tiles) in a future SIS version.</p>
+      *
+      * @param  packed  the coverage containing packed values to convert.
+      * @return the converted coverage. May be {@code coverage}.
+      */
+     public static GridCoverage convert(final GridCoverage packed) {
+         return ConvertedGridCoverage.create(packed);
+     }
+ }
diff --cc core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
index 0000000,b355c7f..728bd1c
mode 000000,100644..100644
--- a/core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
+++ b/core/sis-raster/src/test/java/org/apache/sis/image/LinearIteratorTest.java
@@@ -1,0 -1,134 +1,133 @@@
+ /*
+  * 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.image;
+ 
+ import java.awt.Dimension;
+ import java.awt.Rectangle;
+ import java.awt.image.DataBuffer;
+ import java.awt.image.WritableRaster;
+ import java.awt.image.WritableRenderedImage;
 -import org.opengis.coverage.grid.SequenceType;
+ import org.junit.Ignore;
+ import org.junit.Test;
+ 
+ import static org.junit.Assert.*;
+ 
+ 
+ /**
+  * Tests the linear read-write iterator on signed short integer values.
+  *
+  * @author  Johann Sorel (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  * @version 1.0
+  * @since   1.0
+  */
+ public final strictfp class LinearIteratorTest extends DefaultIteratorTest {
+     /**
+      * Creates a new test case.
+      */
+     public LinearIteratorTest() {
+         super(DataBuffer.TYPE_SHORT);
+     }
+ 
+     /**
+      * Returns the sequence of (x,y) coordinates expected for the iterator being tested.
+      *
+      * @param  subArea  the ranges of pixel coordinates in which to iterate.
+      * @return sequence of (x,y) tuples inside the given ranges, in the order to be traversed by the iterator.
+      */
+     @Override
+     int[] getCoordinatesInExpectedOrder(final Rectangle subArea) {
+         final int[] coordinates = new int[subArea.width * subArea.height * 2];
+         final int subMaxX = subArea.x + subArea.width;
+         final int subMaxY = subArea.y + subArea.height;
+         int i = 0;
+         for (int y = subArea.y; y < subMaxY; y++) {
+             for (int x = subArea.x; x < subMaxX; x++) {
+                 coordinates[i++] = x;
+                 coordinates[i++] = y;
+             }
+         }
+         assertEquals(coordinates.length, i);
+         return coordinates;
+     }
+ 
+     /**
+      * Returns the index in iteration of the given coordinates. For example a return value of 2 means
+      * that the given (x,y) should be the third point in iteration (iteration starts at index zero).
+      * This method must be overridden for each kind of iterator to test.
+      *
+      * @param  bounds  the image bounds.
+      * @param  x       <var>x</var> coordinate for which the iterator position is desired.
+      * @param  y       <var>y</var> coordinate for which the iterator position is desired.
+      * @return point index in iterator order for the given (x,y) coordinates.
+      */
+     @Override
+     int getIndexOf(final Rectangle bounds, int x, int y) {
+         x -= bounds.x;
+         y -= bounds.y;
+         return y * bounds.width + x;
+     }
+ 
+     /**
+      * Returns the expected sequence type.
+      *
+      * @param  singleTile  {@code true} if iteration occurs in a single tile, or {@code false} for the whole image.
+      * @return the iteration order, which is {@link SequenceType#LINEAR} for the iterator to be tested in this class.
+      */
+     @Override
+     SequenceType getIterationOrder(boolean singleTile) {
+         return SequenceType.LINEAR;
+     }
+ 
+     /**
+      * Creates a {@code PixelIterator} for a sub-area of given raster.
+      */
+     @Override
+     void createPixelIterator(WritableRaster raster, Rectangle subArea) {
+         iterator = new LinearIterator(raster, isWritable ? raster : null, subArea, null);
+         assertEquals("getIterationOrder()", SequenceType.LINEAR, iterator.getIterationOrder());
+         assertEquals("isWritable", isWritable, iterator.isWritable());
+     }
+ 
+     /**
+      * Creates a {@code PixelIterator} for a sub-area of given image.
+      */
+     @Override
+     void createPixelIterator(WritableRenderedImage image, Rectangle subArea) {
+         iterator = new LinearIterator(image, isWritable ? image : null, subArea, null);
+         assertEquals("isWritable", isWritable, iterator.isWritable());
+     }
+ 
+     /**
+      * Creates a {@code PixelIterator} for a window in the given image.
+      * The iterator shall be assigned to the {@link #iterator} field.
+      */
+     @Override
+     void createWindowIterator(WritableRenderedImage image, Dimension window) {
+         iterator = new LinearIterator(image, isWritable ? image : null, null, window);
+         assertEquals("isWritable", isWritable, iterator.isWritable());
+     }
+ 
+     /**
+      * Tests {@link PixelIterator#createWindow(TransferType)} on a tiled image.
+      */
+     @Test
+     @Override
+     @Ignore("TODO")
+     public void testWindowOnImage() {
+     }
+ }
diff --cc storage/sis-netcdf/pom.xml
index 0bb0931,34dd5b5..9998c70
--- a/storage/sis-netcdf/pom.xml
+++ b/storage/sis-netcdf/pom.xml
@@@ -120,7 -120,22 +120,21 @@@
        <optional>true</optional>
      </dependency>
  
 -    <!-- Leverage GeoAPI tests. -->
      <dependency>
+       <groupId>org.apache.sis.core</groupId>
+       <artifactId>sis-referencing</artifactId>
+       <version>${project.version}</version>
+       <type>test-jar</type>
+       <scope>test</scope>
+     </dependency>
+     <dependency>
+       <groupId>org.apache.sis.core</groupId>
+       <artifactId>sis-metadata</artifactId>
+       <version>${project.version}</version>
+       <type>test-jar</type>
+       <scope>test</scope>
+     </dependency>
+     <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <scope>test</scope>
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
index 4c2579a,3a62463..54773a2
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
@@@ -48,6 -53,6 +53,9 @@@ import org.apache.sis.util.resources.Er
  import org.apache.sis.measure.Units;
  import org.apache.sis.math.Vector;
  
++// Branch-dependent imports
++import org.apache.sis.referencing.operation.DefaultCoordinateOperationFactory;
++
  
  /**
   * Temporary object for building a coordinate reference system from the variables in a netCDF file.
@@@ -576,17 -609,51 +612,52 @@@ previous:   for (int i=components.size(
              }
          }
  
-         /** Creates the coordinate reference system from datum and coordinate system computed in previous steps. */
+         /**
+          * Creates the coordinate reference system from datum and coordinate system computed in previous steps.
+          * This method is invoked under conditions similar to the ones of above {@code createCS(…)} method.
+          */
          @Override void createCRS(CRSFactory factory, Map<String,?> properties) throws FactoryException {
-             referenceSystem =  factory.createGeographicCRS(properties, datum, coordinateSystem);
+             referenceSystem = factory.createGeographicCRS(properties, datum, coordinateSystem);
          }
-     };
+     }
+ 
+ 
+ 
  
      /**
-      * Projected CRS with (E,N,h) axes.
+      * Projected CRS with (E,N,h) axes. There is not enough information in a netCDF files for creating the right
+      * map projection, unless {@code "grid_mapping"} attributes are specified. If insufficient information, this
+      * class creates an unknown map projection based on Plate Carrée. Note that this map projection may be replaced
+      * by {@link GridMapping#crs} at a later stage.
       */
      private static final class Projected extends Geodetic<CartesianCS> {
-         /** Creates a new builder (invoked by lambda function). */
+         /**
+          * The spherical variant of {@link CRSBuilder#DEFAULT}.
+          * Currently based upon the GRS 1980 Authalic Sphere.
+          */
+         private static final CommonCRS SPHERICAL = CommonCRS.SPHERE;
+ 
+         /**
+          * Defining conversion for "Not specified (presumed Plate Carrée)". This conversion use spherical formulas.
+          * Consequently it should be used with {@link #SPHERICAL} instead of {@link CommonCRS#DEFAULT}.
+          */
+         private static final Conversion UNKNOWN_PROJECTION;
+         static {
 -            final CoordinateOperationFactory factory = DefaultFactories.forBuildin(CoordinateOperationFactory.class);
++            final DefaultCoordinateOperationFactory factory = DefaultFactories.forBuildin(
++                    CoordinateOperationFactory.class, DefaultCoordinateOperationFactory.class);
+             try {
+                 final OperationMethod method = factory.getOperationMethod(Equirectangular.NAME);
+                 UNKNOWN_PROJECTION = factory.createDefiningConversion(
+                         properties("Not specified (presumed Plate Carrée)"),
+                         method, method.getParameters().createValue());
+             } catch (FactoryException e) {
+                 throw new ExceptionInInitializerError(e);
+             }
+         }
+ 
+         /**
+          * Creates a new builder (invoked by lambda function).
+          */
          public Projected() {
              super((byte) 2);
          }
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
index 252a9b4,75c575f..42a8605
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
@@@ -115,8 -135,9 +138,9 @@@ public abstract class Decoder extends R
          Objects.requireNonNull(listeners);
          this.geomlib     = geomlib;
          this.listeners   = listeners;
 -        this.nameFactory = DefaultFactories.forBuildin(NameFactory.class);
 +        this.nameFactory = DefaultFactories.forBuildin(NameFactory.class, DefaultNameFactory.class);
          this.datumCache  = new Datum[CRSBuilder.DATUM_CACHE_SIZE];
+         this.gridMapping = new HashMap<>();
      }
  
      /**
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
index 37a6be3,ffefe7b..1144c9b
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Raster.java
@@@ -20,8 -20,8 +20,7 @@@ import java.util.List
  import java.awt.image.DataBuffer;
  import java.awt.image.RenderedImage;
  import java.awt.image.RasterFormatException;
 -import org.opengis.coverage.CannotEvaluateException;
  import org.apache.sis.coverage.SampleDimension;
- import org.apache.sis.coverage.grid.GridCoverage;
  import org.apache.sis.coverage.grid.GridGeometry;
  import org.apache.sis.coverage.grid.GridExtent;
  import org.apache.sis.coverage.grid.ImageRenderer;
@@@ -65,9 -82,12 +81,12 @@@ final class Raster extends BufferedGrid
          try {
              final ImageRenderer renderer = new ImageRenderer(this, target);
              renderer.setData(data);
+             if (bandOffsets != null) {
+                 renderer.setInterleavedPixelOffsets(pixelStride, bandOffsets);
+             }
              return renderer.image();
          } catch (IllegalArgumentException | ArithmeticException | RasterFormatException e) {
 -            throw new CannotEvaluateException(Resources.format(Resources.Keys.CanNotRender_2, label, e), e);
 +            throw new RuntimeException(Resources.format(Resources.Keys.CanNotRender_2, label, e), e);
          }
      }
  }
diff --cc storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalFeatureTypeException.java
index 667b6f3,267c21a..c2571ed
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalFeatureTypeException.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/IllegalFeatureTypeException.java
@@@ -23,6 -23,9 +23,9 @@@ import org.opengis.util.GenericName
  
  /**
   * Thrown when a store can not write the given feature because its type is not one of the supported types.
 - * The {@link org.opengis.feature.FeatureType} is given by {@link org.opengis.feature.Feature#getType()},
++ * The {@code FeatureType} is given by {@code Feature.getType()},
+  * and the type expected by the data store is given by {@link FeatureSet#getType()}. Those two values must
+  * match, except when the type of the feature set is {@linkplain WritableFeatureSet#updateType updated}.
   *
   * @author  Martin Desruisseaux (Geomatys)
   * @version 0.8
diff --cc storage/sis-storage/src/main/java/org/apache/sis/storage/WritableFeatureSet.java
index 559f638,29cc254..12b7458
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableFeatureSet.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/WritableFeatureSet.java
@@@ -49,11 -51,20 +51,20 @@@ public interface WritableFeatureSet ext
       * @throws IllegalFeatureTypeException if the given type is not compatible with the types supported by the store.
       * @throws DataStoreException if another error occurred while changing the feature type.
       */
-     void updateType(DefaultFeatureType newType) throws IllegalFeatureTypeException, DataStoreException;
 -    void updateType(FeatureType newType) throws DataStoreException;
++    void updateType(DefaultFeatureType newType) throws DataStoreException;
  
      /**
-      * Inserts new features in this {@code FeatureSet}.
-      * Any feature already present in the {@link FeatureSet} will remain unmodified.
+      * Inserts new feature instances in this {@code FeatureSet}.
+      * Any feature already present in this {@link FeatureSet} will remain unmodified.
 -     * If a {@linkplain Feature#getProperty feature property} is used as unique identifier, then:
++     * If a {@linkplain AbstractFeature#getProperty feature property} is used as unique identifier, then:
+      *
+      * <ul>
+      *   <li>If a given feature assigns to that property a value already in use, an exception will be thrown.</li>
+      *   <li>If given features do not assign value to that property, identifiers should be generated by the data store.</li>
+      * </ul>
+      *
+      * After successful insertion, the new features may appear after the features already present
+      * but not necessarily; ordering is {@link DataStore} specific.
       *
       * <div class="note"><b>API note:</b>
       * this method expects an {@link Iterator} rather then a {@link Stream} for easing
@@@ -61,36 -72,38 +72,38 @@@
       * than implementing a {@link Stream}. On the other side if the user has a {@link Stream},
       * obtaining an {@link Iterator} can be done by a call to {@link Stream#iterator()}.</div>
       *
-      * @param  features features to insert in this {@code FeatureSet}.
+      * @param  features feature instances to insert or copy in this {@code FeatureSet}.
+      * @throws IllegalFeatureTypeException if a feature given by the iterator is not of the type expected by this {@code FeatureSet}.
       * @throws DataStoreException if another error occurred while storing new features.
       */
 -    void add(Iterator<? extends Feature> features) throws DataStoreException;
 +    void add(Iterator<? extends AbstractFeature> features) throws DataStoreException;
  
      /**
-      * Removes all features from this {@code FeatureSet} which matches the given predicate.
+      * Removes all feature instances from this {@code FeatureSet} which matches the given predicate.
       *
-      * @param  filter  a predicate which returns true for resources to be removed.
+      * @param  filter  a predicate which returns {@code true} for feature instances to be removed.
       * @return {@code true} if any elements were removed.
-      * @throws DataStoreException if another error occurred while removing features.
+      * @throws DataStoreException if an error occurred while removing features.
       */
 -    boolean removeIf(Predicate<? super Feature> filter) throws DataStoreException;
 +    boolean removeIf(Predicate<? super AbstractFeature> filter) throws DataStoreException;
  
      /**
-      * Updates all features from this {@code FeatureSet} which matches the given predicate.
+      * Updates all feature instances from this {@code FeatureSet} which match the given predicate.
 -     * For each {@link Feature} instance matching the given {@link Predicate},
 +     * For each {@code Feature} instance matching the given {@link Predicate},
       * the <code>{@linkplain UnaryOperator#apply UnaryOperator.apply(Feature)}</code> method will be invoked.
-      * {@code UnaryOperator}s are free to modify the given {@code Feature} <i>in-place</i> or to return a
-      * different feature instance. Two behaviors are possible:
+      * {@code UnaryOperator}s are free to modify the given {@code Feature} <i>in-place</i>
+      * or to return a different feature instance. Two behaviors are possible:
+      *
       * <ul>
 -     *   <li>If the operator returns a non-null {@link Feature}, then the modified feature is stored
 +     *   <li>If the operator returns a non-null {@code Feature}, then the modified feature is stored
       *       in replacement of the previous feature (not necessarily at the same location).</li>
       *   <li>If the operator returns {@code null}, then the feature will be removed from the {@code FeatureSet}.</li>
       * </ul>
       *
-      * @param  filter   a predicate which returns true for resources to be updated.
-      * @param  updater  operation called for each matching {@code Feature}.
+      * @param  filter   a predicate which returns {@code true} for feature instances to be updated.
 -     * @param  updater  operation called for each matching {@link Feature} instance.
++     * @param  updater  operation called for each matching {@code Feature} instance.
+      * @throws IllegalFeatureTypeException if a feature given by the operator is not of the type expected by this {@code FeatureSet}.
       * @throws DataStoreException if another error occurred while replacing features.
       */
-     void replaceIf(Predicate<? super AbstractFeature> filter, UnaryOperator<AbstractFeature> updater)
-             throws DataStoreException;
 -    void replaceIf(Predicate<? super Feature> filter, UnaryOperator<Feature> updater) throws DataStoreException;
++    void replaceIf(Predicate<? super AbstractFeature> filter, UnaryOperator<AbstractFeature> updater) throws DataStoreException;
  }


Mime
View raw message