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

jsorel 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 36af97c7b8 Improve support of tilematrix and jpef items in GIMI
36af97c7b8 is described below

commit 36af97c7b8e208a2482dd2aee65c00a400ecafb7
Author: jsorel <johann.so...@geomatys.com>
AuthorDate: Fri Jul 26 17:19:00 2024 +0200

    Improve support of tilematrix and jpef items in GIMI
---
 .../org/apache/sis/storage/gimi/GimiStore.java     | 328 +++++++++++++++++++--
 .../sis/storage/gimi/internal/ScaleSortedMap.java  | 104 +++++++
 .../sis/storage/gimi/internal/TileMatrices.java    |  60 ++++
 .../org/apache/sis/storage/gimi/isobmff/Box.java   |   4 +-
 .../sis/storage/gimi/isobmff/ISOBMFFReader.java    |  10 +
 .../gimi/isobmff/iso14496_10/ContentDescribes.java |  19 +-
 .../EntityToGroup.java}                            |  20 +-
 .../gimi/isobmff/iso14496_12/ItemReference.java    |   7 +-
 .../gimi/isobmff/iso23008_12/ISO23008_12.java      |   2 +
 .../ImagePyramidEntityGroup.java}                  |  39 ++-
 10 files changed, 518 insertions(+), 75 deletions(-)

diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/GimiStore.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/GimiStore.java
index 0387851d39..c7f505d10e 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/GimiStore.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/GimiStore.java
@@ -18,13 +18,22 @@ package org.apache.sis.storage.gimi;
 
 import java.awt.image.BufferedImage;
 import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
+import java.util.SortedMap;
+import java.util.function.Function;
 import java.util.stream.IntStream;
+import java.util.stream.Stream;
+import javax.imageio.ImageIO;
+import javax.imageio.stream.ImageInputStream;
 import org.apache.sis.coverage.SampleDimension;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridCoverageBuilder;
@@ -36,12 +45,17 @@ import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.CRS;
 import org.apache.sis.referencing.privy.AffineTransform2D;
 import org.apache.sis.storage.AbstractGridCoverageResource;
+import org.apache.sis.storage.AbstractResource;
 import org.apache.sis.storage.Aggregate;
 import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.base.StoreResource;
+import org.apache.sis.storage.gimi.internal.ScaleSortedMap;
+import org.apache.sis.storage.gimi.internal.TileMatrices;
 import org.apache.sis.storage.gimi.isobmff.Box;
 import org.apache.sis.storage.gimi.isobmff.ISOBMFFReader;
 import org.apache.sis.storage.gimi.isobmff.gimi.ModelTiePointProperty;
@@ -61,12 +75,19 @@ import 
org.apache.sis.storage.gimi.isobmff.iso14496_12.SingleItemTypeReference;
 import org.apache.sis.storage.gimi.isobmff.iso23001_17.ComponentDefinition;
 import org.apache.sis.storage.gimi.isobmff.iso23001_17.UncompressedFrameConfig;
 import org.apache.sis.storage.gimi.isobmff.iso23008_12.ImageSpatialExtents;
+import org.apache.sis.storage.tiling.Tile;
+import org.apache.sis.storage.tiling.TileMatrix;
 import org.apache.sis.storage.tiling.TileMatrixSet;
+import org.apache.sis.storage.tiling.TileStatus;
 import org.apache.sis.storage.tiling.TiledResource;
+import org.apache.sis.util.iso.Names;
+import org.opengis.geometry.Envelope;
 import org.opengis.metadata.Metadata;
 import org.opengis.parameter.ParameterValueGroup;
 import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.operation.MathTransform;
 import org.opengis.util.FactoryException;
+import org.opengis.util.GenericName;
 
 /**
  *
@@ -77,6 +98,7 @@ public final class GimiStore extends DataStore implements 
Aggregate {
     private final Path gimiPath;
 
     private List<Resource> components;
+    private Map<Integer,Resource> componentIndex;
 
     //cache the reader
     private ISOBMFFReader reader;
@@ -125,6 +147,7 @@ public final class GimiStore extends DataStore implements 
Aggregate {
         if (components != null) return components;
 
         components = new ArrayList<>();
+        componentIndex = new HashMap<>();
         try {
             final ISOBMFFReader reader = getReader();
 
@@ -141,16 +164,22 @@ public final class GimiStore extends DataStore implements 
Aggregate {
             iinf.readPayload(reader.channel);
             for (ItemInfoEntry iie : iinf.entries) {
                 final Item item = new Item(iie);
-                if (UncompressedImage.TYPE.equals(iie.itemType)) {
+                Resource resource;
+                if (UncompressedImage.TYPE_UNCOMPRESSED.equals(iie.itemType)) {
                     //uncompressed image
-                    components.add(new UncompressedImage(item));
-                } else if (Grid.TYPE.equals(iie.itemType)) {
+                    resource = new UncompressedImage(item);
+                } else if (Jpeg.TYPE_JPEG.equals(iie.itemType)) {
                     //tiled image
-                    components.add(new Grid(item));
+                    resource = new Jpeg(item);
+                }  else if (Grid.TYPE.equals(iie.itemType)) {
+                    //tiled image
+                    resource = new Grid(item);
                 } else {
                     //TODO
-                    //ignore all others for now
+                    resource = new UnknownResource(item);
                 }
+                components.add(resource);
+                componentIndex.put(iie.itemId, resource);
             }
 
         } catch (Exception ex) {
@@ -160,21 +189,36 @@ public final class GimiStore extends DataStore implements 
Aggregate {
         return components;
     }
 
+    private final class UnknownResource extends AbstractResource implements 
StoreResource{
+
+        private final Item item;
+
+        public UnknownResource(Item item) throws DataStoreException {
+            super(GimiStore.this);
+            this.item = item;
+
+        }
+
+        @Override
+        public DataStore getOriginator() {
+            return GimiStore.this;
+        }
+    }
+
     /**
      * A single uncompressed image.
      */
-    private final class UncompressedImage extends AbstractGridCoverageResource 
implements StoreResource {
+    private class UncompressedImage extends AbstractGridCoverageResource 
implements StoreResource {
 
-        public static final String TYPE = "unci";
+        public static final String TYPE_UNCOMPRESSED = "unci";
 
-        private final Item item;
-        private ComponentDefinition compDef;
-        private ImageSpatialExtents imageExt;
-        private UncompressedFrameConfig frameConf;
-        private ModelTransformationProperty modelTrs;
-        private ModelTiePointProperty modelTp;
-        private WellKnownText2Property modelWkt;
-        private MediaData mediaData;
+        protected final Item item;
+        protected ComponentDefinition compDef;
+        protected ImageSpatialExtents imageExt;
+        protected UncompressedFrameConfig frameConf;
+        protected ModelTransformationProperty modelTrs;
+        protected ModelTiePointProperty modelTp;
+        protected WellKnownText2Property modelWkt;
 
         public UncompressedImage(Item item) throws DataStoreException {
             super(GimiStore.this);
@@ -238,9 +282,9 @@ public final class GimiStore extends DataStore implements 
Aggregate {
             for (int y =  0; y < 1024; y++) {
                 for (int x = 0; x < 2048; x++) {
                     int offset = y*2048 + x;
-                    raster.setSample(x, y, 0, mediaData.data[offset*3] & 0xFF);
-                    raster.setSample(x, y, 1, mediaData.data[offset*3+1] & 
0xFF);
-                    raster.setSample(x, y, 2, mediaData.data[offset*3+2] & 
0xFF);
+                    raster.setSample(x, y, 0, data[offset*3] & 0xFF);
+                    raster.setSample(x, y, 1, data[offset*3+1] & 0xFF);
+                    raster.setSample(x, y, 2, data[offset*3+2] & 0xFF);
                 }
             }
 
@@ -256,40 +300,261 @@ public final class GimiStore extends DataStore 
implements Aggregate {
 
     }
 
+    private final class Jpeg extends UncompressedImage {
+
+        public static final String TYPE_JPEG = "jpeg";
+
+        public Jpeg(Item item) throws DataStoreException {
+            super(item);
+        }
+
+        @Override
+        public GridCoverage read(GridGeometry gg, int... ints) throws 
DataStoreException {
+            final byte[] data = item.getData();
+
+            ImageInputStream iis;
+            BufferedImage img;
+            try {
+                iis = ImageIO.createImageInputStream(new 
ByteArrayInputStream(data));
+                img = ImageIO.read(iis);
+            } catch (IOException ex) {
+                throw new DataStoreException(ex);
+            }
+
+            final GridGeometry gridGeometry = getGridGeometry();
+            GridCoverageBuilder gcb = new GridCoverageBuilder();
+            gcb.setDomain(gridGeometry);
+            //gcb.setRanges(getSampleDimensions());
+            //gcb.setValues(db, new 
Dimension((int)gridGeometry.getExtent().getSize(0), 
(int)gridGeometry.getExtent().getSize(1)));
+            gcb.setValues(img);
+            return gcb.build();
+        }
+
+    }
+
+
     private final class Grid extends AbstractGridCoverageResource implements 
TiledResource, StoreResource {
 
         public static final String TYPE = "grid";
 
         private final Item item;
+        private final GenericName identifier;
+
+        //filled after initialize
+        private GridCoverageResource first;
+        private CoordinateReferenceSystem crs;
+        private TileMatrix tileMatrix;
+        private GimiTileMatrixSet tileMatrixSet;
 
         public Grid(Item item) throws DataStoreException {
             super(GimiStore.this);
             this.item = item;
+            if (item.entry.itemName == null || item.entry.itemName.isBlank()) {
+                this.identifier = Names.createLocalName(null, null, 
Integer.toString(item.entry.itemId));
+            } else {
+                this.identifier = Names.createLocalName(null, null, 
item.entry.itemName);
+            }
         }
 
         @Override
-        public GridGeometry getGridGeometry() throws DataStoreException {
-            throw new UnsupportedOperationException("Not supported yet."); // 
Generated from 
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+        public Optional<GenericName> getIdentifier() {
+            return Optional.of(identifier);
+        }
+
+        @Override
+        public DataStore getOriginator() {
+            return GimiStore.this;
+        }
+
+        private synchronized void initialize() throws DataStoreException {
+            final Resource first = 
componentIndex.get(item.references.get(0).toItemId[0]);
+            if (first instanceof GridCoverageResource) {
+                this.first = (GridCoverageResource) first;
+            } else {
+                throw new DataStoreException("Expecting a GridCoverageResource 
tile but was a " + first.getClass().getName());
+            }
+
+            final GridGeometry firstTileGridGeom = 
this.first.getGridGeometry();
+            this.crs = firstTileGridGeom.getCoordinateReferenceSystem();
+            final GridExtent tileExtent = firstTileGridGeom.getExtent();
+            final int[] tileSize = new 
int[]{Math.toIntExact(tileExtent.getSize(0)), 
Math.toIntExact(tileExtent.getSize(1))};
+            final MathTransform matrixGridToCrs = 
firstTileGridGeom.derive().subgrid(null, 
tileSize).build().getGridToCRS(PixelInCell.CELL_CENTER);
+
+            for (Box b : item.properties) {
+                if (b instanceof ImageSpatialExtents) {
+                    final ImageSpatialExtents ext = (ImageSpatialExtents) b;
+                    final long matrixWidth = ext.imageWidth / 
tileExtent.getSize(0);
+                    final long matrixHeight = ext.imageHeight / 
tileExtent.getSize(1);
+
+                    //create tile matrix
+                    final GridGeometry tilingScheme = new GridGeometry(new 
GridExtent(matrixWidth, matrixHeight), PixelInCell.CELL_CENTER, 
matrixGridToCrs, crs);
+                    tileMatrix = new GimiTileMatrix(this, tilingScheme, 
tileSize);
+
+                    //create tile matrix set
+                    tileMatrixSet = new 
GimiTileMatrixSet(Names.createLocalName(null, null, identifier.tip().toString() 
+ "_tms"), crs);
+                    tileMatrixSet.matrices.insertByScale(tileMatrix);
+                }
+            }
         }
 
         @Override
         public List<SampleDimension> getSampleDimensions() throws 
DataStoreException {
-            throw new UnsupportedOperationException("Not supported yet."); // 
Generated from 
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+            initialize();
+            return first.getSampleDimensions();
+        }
+
+        @Override
+        public Collection<? extends TileMatrixSet> getTileMatrixSets() throws 
DataStoreException {
+            initialize();
+            return List.of(tileMatrixSet);
+        }
+
+        @Override
+        public GridGeometry getGridGeometry() throws DataStoreException {
+            throw new UnsupportedOperationException("Not supported yet.");
         }
 
+
         @Override
         public GridCoverage read(GridGeometry domain, int... ranges) throws 
DataStoreException {
-            throw new UnsupportedOperationException("Not supported yet."); // 
Generated from 
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+            throw new UnsupportedOperationException("Not supported yet.");
+        }
+
+    }
+
+    private final class GimiTile implements Tile {
+
+        private final long[] indices;
+        private final int itemId;
+
+        public GimiTile(long[] indices, int itemId) {
+            this.indices = indices;
+            this.itemId = itemId;
         }
 
         @Override
-        public Collection<? extends TileMatrixSet> getTileMatrixSets() throws 
DataStoreException {
-            throw new UnsupportedOperationException("Not supported yet."); // 
Generated from 
nbfs://nbhost/SystemFileSystem/Templates/Classes/Code/GeneratedMethodBody
+        public long[] getIndices() {
+            return indices.clone();
         }
 
         @Override
-        public DataStore getOriginator() {
-            return GimiStore.this;
+        public TileStatus getStatus() {
+            return componentIndex.containsKey(itemId) ? TileStatus.EXISTS : 
TileStatus.MISSING;
+        }
+
+        @Override
+        public Resource getResource() throws DataStoreException {
+            final Resource res = componentIndex.get(itemId);
+            if (res == null) throw new DataStoreContentException("Missing tile 
at " + Arrays.toString(indices) + ", it should not be possible with GIMI 
model");
+            return res;
+        }
+    }
+
+    private final class GimiTileMatrix implements TileMatrix {
+
+        private final Grid grid;
+        private final GenericName identifier;
+        private final GridGeometry tilingScheme;
+        private final int[] tileSize;
+
+        public GimiTileMatrix(Grid grid, GridGeometry tilingScheme, int[] 
tileSize) {
+            this.grid = grid;
+            this.identifier = Names.createLocalName(null, null, 
grid.getIdentifier().get().tip().toString()+"_tm");
+            this.tilingScheme = tilingScheme;
+            this.tileSize = tileSize;
+        }
+
+        @Override
+        public GenericName getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public double[] getResolution() {
+            double[] resolution = tilingScheme.getResolution(true);
+            resolution[0] /= tileSize[0];
+            resolution[1] /= tileSize[1];
+            return resolution;
+        }
+
+        @Override
+        public GridGeometry getTilingScheme() {
+            return tilingScheme;
+        }
+
+        @Override
+        public TileStatus getTileStatus(long... indices) throws 
DataStoreException {
+            return tilingScheme.getExtent().contains(indices) ? 
TileStatus.EXISTS : TileStatus.OUTSIDE_EXTENT;
+        }
+
+        @Override
+        public Optional<Tile> getTile(long... indices) throws 
DataStoreException {
+            final int itemIdx = Math.toIntExact(indices[0] + indices[1] * 
tilingScheme.getExtent().getSize(0));
+            return Optional.of(new GimiTile(indices, 
grid.item.references.get(0).toItemId[itemIdx]));
+        }
+
+        @Override
+        public Stream<Tile> getTiles(GridExtent indicesRanges, boolean 
parallel) throws DataStoreException {
+            Stream<long[]> stream = TileMatrices.pointStream(indicesRanges);
+            stream = parallel ? stream.parallel() : stream.sequential();
+            return stream.map(new Function<long[], Tile>(){
+                @Override
+                public Tile apply(final long[] indices) {
+                    try {
+                        return getTile(indices).orElse(null);
+                    } catch (DataStoreException ex) {
+                        return new Tile() {
+                            @Override
+                            public long[] getIndices() {
+                                return indices;
+                            }
+
+                            @Override
+                            public TileStatus getStatus() {
+                                return TileStatus.IN_ERROR;
+                            }
+
+                            @Override
+                            public Resource getResource() throws 
DataStoreException {
+                                throw ex;
+                            }
+                        };
+                    }
+                }
+            });
+        }
+    }
+
+    private final class GimiTileMatrixSet implements TileMatrixSet {
+
+        private final GenericName identifier;
+        private final CoordinateReferenceSystem crs;
+        private final ScaleSortedMap<TileMatrix> matrices = new 
ScaleSortedMap<>();
+
+        public GimiTileMatrixSet(GenericName identifier, 
CoordinateReferenceSystem crs) {
+            this.identifier = identifier;
+            this.crs = crs;
+        }
+
+        @Override
+        public GenericName getIdentifier() {
+            return identifier;
+        }
+
+        @Override
+        public CoordinateReferenceSystem getCoordinateReferenceSystem() {
+            return crs;
+        }
+
+        @Override
+        public Optional<Envelope> getEnvelope() {
+            if (matrices.isEmpty()) return Optional.empty();
+            return 
Optional.of(matrices.lastEntry().getValue().getTilingScheme().getEnvelope());
+        }
+
+        @Override
+        public SortedMap<GenericName, ? extends TileMatrix> getTileMatrices() {
+            return matrices;
         }
 
     }
@@ -310,11 +575,11 @@ public final class GimiStore extends DataStore implements 
Aggregate {
 
             final ISOBMFFReader reader = getReader();
             final Box meta = root.getChild(Meta.FCC, null, reader.channel);
+            ISOBMFFReader.load(meta, reader.channel);
 
             //is item primary
             final PrimaryItem primaryItem = (PrimaryItem) 
meta.getChild(PrimaryItem.FCC, null, reader.channel);
             if (primaryItem != null) {
-                primaryItem.readPayload(reader.channel);
                 isPrimary = primaryItem.itemId == entry.itemId;
             } else {
                 isPrimary = true;
@@ -323,12 +588,9 @@ public final class GimiStore extends DataStore implements 
Aggregate {
             //extract properties
             final Box itemProperties = meta.getChild(ItemProperties.FCC, null, 
reader.channel);
             if (itemProperties != null) {
-                itemProperties.readPayload(reader.channel);
                 final ItemPropertyContainer itemPropertiesContainer = 
(ItemPropertyContainer) itemProperties.getChild(ItemPropertyContainer.FCC, 
null, reader.channel);
-                itemPropertiesContainer.readPayload(reader.channel);
                 final List<Box> allProperties = 
itemPropertiesContainer.getChildren(reader.channel);
                 final ItemPropertyAssociation itemPropertiesAssociations = 
(ItemPropertyAssociation) itemProperties.getChild(ItemPropertyAssociation.FCC, 
null, reader.channel);
-                itemPropertiesAssociations.readPayload(reader.channel);
 
                 for (ItemPropertyAssociation.Entry en : 
itemPropertiesAssociations.entries) {
                     if (en.itemId == entry.itemId) {
@@ -343,7 +605,11 @@ public final class GimiStore extends DataStore implements 
Aggregate {
             //extract outter references
             final ItemReference itemReferences = (ItemReference) 
meta.getChild(ItemReference.FCC, null, reader.channel);
             if (itemReferences != null) {
-
+                for (SingleItemTypeReference sitr : itemReferences.references) 
{
+                    if (sitr.fromItemId == entry.itemId) {
+                        references.add(sitr);
+                    }
+                }
             }
 
 
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/ScaleSortedMap.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/ScaleSortedMap.java
new file mode 100644
index 0000000000..6b88d6d537
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/ScaleSortedMap.java
@@ -0,0 +1,104 @@
+/*
+ * 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.gimi.internal;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.storage.tiling.TileMatrix;
+import org.opengis.util.GenericName;
+
+/**
+ * SortedMap of TileMatrix sorted by first axe resolution.
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public final class ScaleSortedMap<T extends TileMatrix> extends 
TreeMap<GenericName, T>{
+
+    public ScaleSortedMap() {
+        super(new ScaleComparator());
+        comparator();
+    }
+
+    public void insertByScale(T tileMatrix) {
+        final GenericName id = tileMatrix.getIdentifier();
+        ArgumentChecks.ensureNonNull("identifier", id);
+        final ScaleComparator comparator = (ScaleComparator) comparator();
+        if (comparator.matricesByScale.containsKey(id)) {
+            throw new IllegalArgumentException("Key " + id + "already exist");
+        }
+        final double resolution = tileMatrix.getResolution()[0];
+        comparator.matricesByScale.put(id, resolution);
+        super.put(id, tileMatrix);
+    }
+
+    public void removeByScale(T tileMatrix) {
+        final GenericName id = tileMatrix.getIdentifier();
+        ArgumentChecks.ensureNonNull("identifier", id);
+        final ScaleComparator comparator = (ScaleComparator) comparator();
+        if (comparator.matricesByScale.remove(id) != null) {
+            super.remove(id);
+        }
+    }
+
+    @Override
+    public T put(GenericName key, T value) {
+        throw new IllegalArgumentException("Should not be used");
+    }
+
+    @Override
+    public void putAll(Map<? extends GenericName, ? extends T> map) {
+        throw new IllegalArgumentException("Should not be used");
+    }
+
+    @Override
+    public T putIfAbsent(GenericName key, T value) {
+        throw new IllegalArgumentException("Should not be used");
+    }
+
+    @Override
+    public T remove(Object key) {
+        throw new IllegalArgumentException("Should not be used");
+    }
+
+    @Override
+    public boolean remove(Object key, Object value) {
+        throw new IllegalArgumentException("Should not be used");
+    }
+
+    /**
+     * Compare TileMatrix by scale.
+     * Entries are sorted from coarser resolution (highest scale denominator) 
to most detailed resolution (lowest scale denominator).
+     */
+    private static class ScaleComparator implements Comparator<GenericName> {
+        private final Map<GenericName,Double> matricesByScale = new 
HashMap<>();
+
+        @Override
+        public int compare(GenericName o1, GenericName o2) {
+            Double d1 = matricesByScale.get(o1);
+            Double d2 = matricesByScale.get(o2);
+            if (d1 == null) d1 = Double.NaN;
+            if (d2 == null) d2 = Double.NaN;
+            int v = Double.compare(d2, d1);
+            if (v != 0) return v;
+            //we NEED ordering, otherwise entry will be replaced.
+            return o1.toString().compareTo(o2.toString());
+        }
+    }
+}
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java
new file mode 100644
index 0000000000..ab1b01b566
--- /dev/null
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/internal/TileMatrices.java
@@ -0,0 +1,60 @@
+/*
+ * 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.gimi.internal;
+
+import java.util.stream.LongStream;
+import java.util.stream.Stream;
+import org.apache.sis.coverage.grid.GridExtent;
+
+/**
+ *
+ * @author Johann Sorel (Geomatys)
+ */
+public class TileMatrices {
+
+    private TileMatrices(){}
+
+
+    /**
+     * Create a stream of point in the GridExtent.
+     *
+     * TODO : make a more efficient implementation.
+     */
+    public static Stream<long[]> pointStream(GridExtent extent) {
+        final int dimension = extent.getDimension();
+        final long[] low = extent.getLow().getCoordinateValues();
+        final long[] high = extent.getHigh().getCoordinateValues();
+
+        Stream<long[]> stream = LongStream.range(low[0], high[0]+1)
+                .mapToObj((long value) -> {
+                    final long[] array = new long[dimension];
+                    array[0] = value;
+                    return array;
+        });
+        for (int i = 1; i <dimension; i++) {
+            final int idx = i;
+            stream = stream.flatMap((long[] t) -> LongStream.range(low[idx], 
high[idx]+1)
+                    .mapToObj((long value) -> {
+                        final long[] array = t.clone();
+                        array[idx] = value;
+                        return array;
+                    }));
+        }
+        return stream;
+    }
+
+}
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/Box.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/Box.java
index 37318ac1a4..14733fd16e 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/Box.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/Box.java
@@ -117,6 +117,7 @@ public class Box {
                         final Box box = ISOBMFFReader.readBox(cdi);
                         cdi.seek(box.boxOffset + box.size);
                         children.add(box);
+                        if (box.size == 0) break; //last box
                     }
                 } catch (EOFException ex) {
                     //expected
@@ -126,6 +127,7 @@ public class Box {
                     final Box box = ISOBMFFReader.readBox(cdi);
                     cdi.seek(box.boxOffset + box.size);
                     children.add(box);
+                    if (box.size == 0) break; //last box
                 }
             }
 
@@ -243,7 +245,7 @@ public class Box {
 
             final List<Field> fields = new ArrayList<>();
             while (!(clazz == Box.class || clazz == FullBox.class) && clazz != 
null) {
-                fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
+                fields.addAll(0, Arrays.asList(clazz.getDeclaredFields()));
                 clazz = clazz.getSuperclass();
             }
 
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/ISOBMFFReader.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/ISOBMFFReader.java
index 46cb4155ac..fab59b5555 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/ISOBMFFReader.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/ISOBMFFReader.java
@@ -149,4 +149,14 @@ public final class ISOBMFFReader {
         cdi.readByte(); //skip string 0/null terminal marker
         return str;
     }
+
+    /**
+     * Load box payload and all children payload recursively.
+     */
+    public static void load(Box box, ChannelDataInput cdi) throws IOException {
+        box.readPayload(cdi);
+        for (Box b : box.getChildren(cdi)) {
+            load(b, cdi);
+        }
+    }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
index bda82d3f11..c057a52cf0 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
@@ -16,30 +16,15 @@
  */
 package org.apache.sis.storage.gimi.isobmff.iso14496_10;
 
-import java.io.IOException;
-import org.apache.sis.io.stream.ChannelDataInput;
-import org.apache.sis.storage.gimi.isobmff.Box;
+import org.apache.sis.storage.gimi.isobmff.iso14496_12.SingleItemTypeReference;
 
 /**
  *
  * @author Johann Sorel (Geomatys)
  */
-public class ContentDescribes extends Box{
+public class ContentDescribes extends SingleItemTypeReference{
 
     public static final String FCC = "cdsc";
 
-    public int fromId;
-    public int[] toId;
-
-    @Override
-    protected void readProperties(ChannelDataInput cdi) throws IOException {
-        fromId = cdi.readUnsignedShort();
-        toId = new int[cdi.readUnsignedShort()];
-        for (int i = 0; i < toId.length ; i++) {
-            toId[i] = cdi.readUnsignedShort();
-        }
-    }
-
-
 
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/EntityToGroup.java
similarity index 70%
copy from 
incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
copy to 
incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/EntityToGroup.java
index bda82d3f11..f927a670f0 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/EntityToGroup.java
@@ -14,32 +14,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.storage.gimi.isobmff.iso14496_10;
+package org.apache.sis.storage.gimi.isobmff.iso14496_12;
 
 import java.io.IOException;
 import org.apache.sis.io.stream.ChannelDataInput;
-import org.apache.sis.storage.gimi.isobmff.Box;
+import org.apache.sis.storage.gimi.isobmff.FullBox;
 
 /**
  *
  * @author Johann Sorel (Geomatys)
  */
-public class ContentDescribes extends Box{
+public class EntityToGroup extends FullBox {
 
-    public static final String FCC = "cdsc";
-
-    public int fromId;
-    public int[] toId;
+    public int groupId;
+    public int[] entitiesId;
 
     @Override
     protected void readProperties(ChannelDataInput cdi) throws IOException {
-        fromId = cdi.readUnsignedShort();
-        toId = new int[cdi.readUnsignedShort()];
-        for (int i = 0; i < toId.length ; i++) {
-            toId[i] = cdi.readUnsignedShort();
-        }
+        groupId = cdi.readInt();
+        entitiesId = cdi.readInts(cdi.readInt());
     }
 
 
-
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/ItemReference.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/ItemReference.java
index 405e7bf6d2..a553f10590 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/ItemReference.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_12/ItemReference.java
@@ -32,7 +32,7 @@ public class ItemReference extends FullBox {
 
     public static final String FCC = "iref";
 
-    public List<Box> references;
+    public List<SingleItemTypeReference> references;
 
     @Override
     public void readProperties(ChannelDataInput cdi) throws IOException {
@@ -40,9 +40,12 @@ public class ItemReference extends FullBox {
 
         while (cdi.getStreamPosition() < boxOffset+size) {
             final Box box = ISOBMFFReader.readBox(cdi);
+            if (!(box instanceof SingleItemTypeReference)) {
+                throw new IOException("Expected only SingleItemTypeReference 
boxes in ItemReference but encounter a " + box.getClass().getSimpleName());
+            }
             box.readPayload(cdi);
             cdi.seek(box.boxOffset + box.size);
-            references.add(box);
+            references.add((SingleItemTypeReference) box);
         }
     }
 }
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ISO23008_12.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ISO23008_12.java
index fca0498f3e..ea4ef9a216 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ISO23008_12.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ISO23008_12.java
@@ -29,6 +29,7 @@ public final class ISO23008_12 implements BoxRegistry {
 
     private static final Set<String> BOXES = Set.of(
             DerivedImageReference.FCC,
+            ImagePyramidEntityGroup.FCC,
             ImageSpatialExtents.FCC,
             PixelInformationProperty.FCC,
             UserDescriptionProperty.FCC
@@ -54,6 +55,7 @@ public final class ISO23008_12 implements BoxRegistry {
     public Box create(String fourCC) throws IllegalNameException {
         //TODO replace by String switch when SIS minimum java is updated
         if (DerivedImageReference.FCC.equals(fourCC)) return new 
DerivedImageReference();
+        else if (ImagePyramidEntityGroup.FCC.equals(fourCC)) return new 
ImagePyramidEntityGroup();
         else if (ImageSpatialExtents.FCC.equals(fourCC)) return new 
ImageSpatialExtents();
         else if (PixelInformationProperty.FCC.equals(fourCC)) return new 
PixelInformationProperty();
         else if (UserDescriptionProperty.FCC.equals(fourCC)) return new 
UserDescriptionProperty();
diff --git 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ImagePyramidEntityGroup.java
similarity index 50%
copy from 
incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
copy to 
incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ImagePyramidEntityGroup.java
index bda82d3f11..e6b2d11f83 100644
--- 
a/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso14496_10/ContentDescribes.java
+++ 
b/incubator/src/org.apache.sis.storage.gimi/main/org/apache/sis/storage/gimi/isobmff/iso23008_12/ImagePyramidEntityGroup.java
@@ -14,32 +14,49 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.storage.gimi.isobmff.iso14496_10;
+package org.apache.sis.storage.gimi.isobmff.iso23008_12;
 
 import java.io.IOException;
 import org.apache.sis.io.stream.ChannelDataInput;
 import org.apache.sis.storage.gimi.isobmff.Box;
+import org.apache.sis.storage.gimi.isobmff.iso14496_12.EntityToGroup;
 
 /**
  *
  * @author Johann Sorel (Geomatys)
  */
-public class ContentDescribes extends Box{
+public final class ImagePyramidEntityGroup extends EntityToGroup{
 
-    public static final String FCC = "cdsc";
+    public static final String FCC = "pymd";
 
-    public int fromId;
-    public int[] toId;
+    public static final class Matrix {
+        public int layerBinning;
+        public int tilesInLayerRowMinus1;
+        public int tilesInLayerColumnMinus1;
 
-    @Override
-    protected void readProperties(ChannelDataInput cdi) throws IOException {
-        fromId = cdi.readUnsignedShort();
-        toId = new int[cdi.readUnsignedShort()];
-        for (int i = 0; i < toId.length ; i++) {
-            toId[i] = cdi.readUnsignedShort();
+        @Override
+        public String toString() {
+            return Box.beanToString(this);
         }
     }
 
+    public int tileSizeX;
+    public int tileSizeY;
+    public Matrix[] matrices;
 
+    @Override
+    protected void readProperties(ChannelDataInput cdi) throws IOException {
+        super.readProperties(cdi);
+        tileSizeX = cdi.readUnsignedShort();
+        tileSizeY = cdi.readUnsignedShort();
+
+        matrices = new Matrix[entitiesId.length];
+        for (int i = 0; i < matrices.length; i++) {
+            matrices[i] = new Matrix();
+            matrices[i].layerBinning = cdi.readUnsignedShort();
+            matrices[i].tilesInLayerRowMinus1 = cdi.readUnsignedShort();
+            matrices[i].tilesInLayerColumnMinus1 = cdi.readUnsignedShort();
+        }
+    }
 
 }


Reply via email to