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(); + } + } }