ucar.nc2.iosp.hdf4.H4iosp Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
* See LICENSE for license information.
*/
package ucar.nc2.iosp.hdf4;
import ucar.nc2.constants.DataFormatType;
import ucar.nc2.iosp.*;
import ucar.nc2.NetcdfFile;
import ucar.nc2.Variable;
import ucar.nc2.Structure;
import ucar.nc2.util.CancelTask;
import ucar.nc2.util.IO;
import ucar.unidata.io.RandomAccessFile;
import ucar.unidata.io.PositioningDataInputStream;
import ucar.ma2.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.List;
import java.nio.ByteBuffer;
/**
* @author caron
* @since Dec 17, 2007
*/
public class H4iosp extends AbstractIOServiceProvider {
static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(H4iosp.class);
static private boolean showLayoutTypes = false;
private H4header header = new H4header();
public boolean isValidFile(RandomAccessFile raf) throws IOException {
return H4header.isValidFile(raf);
}
public String getFileTypeId() {
if (header.isEos()) return "HDF4-EOS";
return DataFormatType.HDF4.getDescription();
}
public String getFileTypeDescription() {
return "Hierarchical Data Format, version 4";
}
public void open(RandomAccessFile raf, NetcdfFile ncfile, CancelTask cancelTask) throws IOException {
super.open(raf, ncfile, cancelTask);
header.read(raf, ncfile);
ncfile.finish();
}
public Array readData(Variable v, Section section) throws IOException, InvalidRangeException {
if (v instanceof Structure)
return readStructureData((Structure) v, section);
H4header.Vinfo vinfo = (H4header.Vinfo) v.getSPobject();
DataType dataType = v.getDataType();
vinfo.setLayoutInfo(); // make sure needed info is present
// make sure section is complete
section = Section.fill(section, v.getShape());
if (vinfo.hasNoData) {
Object arr = (vinfo.fillValue == null) ? IospHelper.makePrimitiveArray((int) section.computeSize(), dataType) :
IospHelper.makePrimitiveArray((int) section.computeSize(), dataType, vinfo.fillValue);
if (dataType == DataType.CHAR)
arr = IospHelper.convertByteToChar((byte[]) arr);
return Array.factory(dataType, section.getShape(), arr);
}
if (!vinfo.isCompressed) {
if (!vinfo.isLinked && !vinfo.isChunked) {
Layout layout = new LayoutRegular(vinfo.start, v.getElementSize(), v.getShape(), section);
Object data = IospHelper.readDataFill(raf, layout, dataType, vinfo.fillValue, -1);
return Array.factory(dataType, section.getShape(), data);
} else if (vinfo.isLinked) {
Layout layout = new LayoutSegmented(vinfo.segPos, vinfo.segSize, v.getElementSize(), v.getShape(), section);
Object data = IospHelper.readDataFill(raf, layout, dataType, vinfo.fillValue, -1);
return Array.factory(dataType, section.getShape(), data);
} else if (vinfo.isChunked) {
H4ChunkIterator chunkIterator = new H4ChunkIterator(vinfo);
Layout layout = new LayoutTiled(chunkIterator, vinfo.chunkSize, v.getElementSize(), section);
Object data = IospHelper.readDataFill(raf, layout, dataType, vinfo.fillValue, -1);
return Array.factory(dataType, section.getShape(), data);
}
} else {
if (!vinfo.isLinked && !vinfo.isChunked) {
if (showLayoutTypes) System.out.println("***notLinked, compressed");
Layout index = new LayoutRegular(0, v.getElementSize(), v.getShape(), section);
InputStream is = getCompressedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Object data = IospHelper.readDataFill(dataSource, index, dataType, vinfo.fillValue);
return Array.factory(dataType, section.getShape(), data);
} else if (vinfo.isLinked) {
if (showLayoutTypes) System.out.println("***Linked, compressed");
Layout index = new LayoutRegular(0, v.getElementSize(), v.getShape(), section);
InputStream is = getLinkedCompressedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Object data = IospHelper.readDataFill(dataSource, index, dataType, vinfo.fillValue);
return Array.factory(dataType, section.getShape(), data);
} else if (vinfo.isChunked) {
LayoutBBTiled.DataChunkIterator chunkIterator = new H4CompressedChunkIterator(vinfo);
LayoutBB layout = new LayoutBBTiled(chunkIterator, vinfo.chunkSize, v.getElementSize(), section);
Object data = IospHelper.readDataFill(layout, dataType, vinfo.fillValue);
return Array.factory(dataType, section.getShape(), data);
}
}
throw new IllegalStateException();
}
/**
* Structures must be fixed sized
*
* @param s the record structure
* @param section the record range to read
* @return an ArrayStructure, with all the data read in.
* @throws IOException on error
* @throws ucar.ma2.InvalidRangeException if invalid section
*/
private ucar.ma2.ArrayStructure readStructureData(ucar.nc2.Structure s, Section section) throws java.io.IOException, InvalidRangeException {
H4header.Vinfo vinfo = (H4header.Vinfo) s.getSPobject();
vinfo.setLayoutInfo(); // make sure needed info is present
int recsize = vinfo.elemSize;
// create the ArrayStructure
StructureMembers members = s.makeStructureMembers();
for (StructureMembers.Member m : members.getMembers()) {
Variable v2 = s.findVariable(m.getName());
H4header.Minfo minfo = (H4header.Minfo) v2.getSPobject();
m.setDataParam(minfo.offset);
}
members.setStructureSize(recsize);
ArrayStructureBB structureArray = new ArrayStructureBB(members, section.getShape()); // LOOK subset
// loop over records
byte[] result = structureArray.getByteBuffer().array();
/*if (vinfo.isChunked) {
InputStream is = getChunkedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Layout layout = new LayoutRegular(vinfo.start, recsize, s.getShape(), section);
IospHelper.readData(dataSource, layout, DataType.STRUCTURE, result); */
if (!vinfo.isLinked && !vinfo.isCompressed) {
Layout layout = new LayoutRegular(vinfo.start, recsize, s.getShape(), section);
IospHelper.readData(raf, layout, DataType.STRUCTURE, result, -1, true);
/* option 1
} else if (vinfo.isLinked && !vinfo.isCompressed) {
Layout layout = new LayoutSegmented(vinfo.segPos, vinfo.segSize, recsize, s.getShape(), section);
IospHelper.readData(raf, layout, DataType.STRUCTURE, result, -1); */
// option 2
} else if (vinfo.isLinked && !vinfo.isCompressed) {
InputStream is = new LinkedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Layout layout = new LayoutRegular(0, recsize, s.getShape(), section);
IospHelper.readData(dataSource, layout, DataType.STRUCTURE, result);
} else if (!vinfo.isLinked && vinfo.isCompressed) {
InputStream is = getCompressedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Layout layout = new LayoutRegular(0, recsize, s.getShape(), section);
IospHelper.readData(dataSource, layout, DataType.STRUCTURE, result);
} else if (vinfo.isLinked && vinfo.isCompressed) {
InputStream is = getLinkedCompressedInputStream(vinfo);
PositioningDataInputStream dataSource = new PositioningDataInputStream(is);
Layout layout = new LayoutRegular(0, recsize, s.getShape(), section);
IospHelper.readData(dataSource, layout, DataType.STRUCTURE, result);
} else {
throw new IllegalStateException();
}
return structureArray;
}
public String toStringDebug(Object o) {
if (o instanceof Variable) {
Variable v = (Variable) o;
H4header.Vinfo vinfo = (H4header.Vinfo) v.getSPobject();
return (vinfo != null) ? vinfo.toString() : "";
}
return null;
}
private InputStream getCompressedInputStream(H4header.Vinfo vinfo) throws IOException {
// probably could construct an input stream from a channel from a raf
// for now, just read it in.
//System.out.println(" read "+ vinfo.length+" from "+ vinfo.start);
byte[] buffer = new byte[vinfo.length];
raf.seek(vinfo.start);
raf.readFully(buffer);
ByteArrayInputStream in = new ByteArrayInputStream(buffer);
return new java.util.zip.InflaterInputStream(in);
}
private InputStream getLinkedCompressedInputStream(H4header.Vinfo vinfo) {
return new java.util.zip.InflaterInputStream(new LinkedInputStream(vinfo));
}
private class LinkedInputStream extends InputStream {
byte[] buffer;
int nsegs;
long[] segPosA;
int[] segSizeA;
int segno = -1;
int segpos = 0;
int segSize = 0;
//H4header.Vinfo vinfo;
LinkedInputStream(H4header.Vinfo vinfo) {
segPosA = vinfo.segPos;
segSizeA = vinfo.segSize;
nsegs = segSizeA.length;
}
LinkedInputStream(H4header.SpecialLinked linked) throws IOException {
List linkedBlocks = linked.getLinkedDataBlocks();
nsegs = linkedBlocks.size();
segPosA = new long[nsegs];
segSizeA = new int[nsegs];
int count = 0;
for (H4header.TagLinkedBlock tag : linkedBlocks) {
segPosA[count] = tag.offset;
segSizeA[count] = tag.length;
count++;
}
}
private boolean readSegment() throws IOException {
segno++;
if (segno == nsegs)
return false;
segSize = segSizeA[segno];
while (segSize == 0) { // for some reason may have a 0 length segment
segno++;
if (segno == nsegs)
return false;
segSize = segSizeA[segno];
}
buffer = new byte[segSize]; // Look: could do this in buffer size 4096 to save memory
raf.seek(segPosA[segno]);
raf.readFully(buffer);
segpos = 0;
return true;
}
/*public int read(byte b[]) throws IOException {
//System.out.println("request "+b.length);
return super.read(b);
}
public int read(byte b[], int off, int len) throws IOException {
//System.out.println("request "+len+" buffer size="+b.length+" offset="+off);
return super.read(b, off, len);
} */
public int read() throws IOException {
if (segpos == segSize) {
boolean ok = readSegment();
if (!ok) return -1;
}
int b = buffer[segpos] & 0xff;
//System.out.println(" byte "+b+ " at= "+segpos);
segpos++;
return b;
}
}
private InputStream getChunkedInputStream(H4header.Vinfo vinfo) {
return new ChunkedInputStream(vinfo);
}
// look not implemented : skip data
private class ChunkedInputStream extends InputStream {
List chunks;
int chunkNo;
byte[] buffer;
int segPos, segSize;
ChunkedInputStream(H4header.Vinfo vinfo) {
this.chunks = vinfo.chunks;
this.chunkNo = 0;
}
private void readChunk() throws IOException {
H4header.DataChunk chunk = chunks.get(chunkNo);
H4header.TagData chunkData = chunk.data;
chunkNo++;
if (chunkData.ext_type == TagEnum.SPECIAL_COMP) {
// read compressed data in
H4header.TagData cdata = chunkData.compress.getDataTag();
byte[] cbuffer = new byte[cdata.length];
raf.seek(cdata.offset);
raf.readFully(cbuffer);
// System.out.println(" read compress " + cdata.length + " at= " + cdata.offset);
// uncompress it
if (chunkData.compress.compress_type == TagEnum.COMP_CODE_DEFLATE) {
InputStream in = new java.util.zip.InflaterInputStream(new ByteArrayInputStream(cbuffer));
ByteArrayOutputStream out = new ByteArrayOutputStream(chunkData.compress.uncomp_length);
IO.copy(in, out);
buffer = out.toByteArray();
//System.out.println(" uncompress "+buffer.length + ", wanted="+chunkData.compress.uncomp_length);
} else {
throw new IllegalStateException("unknown compression type =" + chunkData.compress.compress_type);
}
} else {
buffer = new byte[chunkData.length];
raf.seek(chunkData.offset);
raf.readFully(buffer);
}
segPos = 0;
segSize = buffer.length;
}
public int read() throws IOException {
if (segPos == segSize)
readChunk();
int b = buffer[segPos] & 0xff;
//System.out.println(" byte "+b+ " at= "+segPos);
segPos++;
return b;
}
/* public int read(byte b[]) throws IOException {
System.out.println("request "+b.length);
return super.read(b);
}
public int read(byte b[], int off, int len) throws IOException {
System.out.println("request "+len+" buffer size="+b.length+" offset="+off);
return super.read(b, off, len);
} // */
}
private static class H4ChunkIterator implements LayoutTiled.DataChunkIterator {
List chunks;
int chunkNo;
H4ChunkIterator(H4header.Vinfo vinfo) {
this.chunks = vinfo.chunks;
this.chunkNo = 0;
}
public boolean hasNext() {
return chunkNo < chunks.size();
}
public LayoutTiled.DataChunk next() throws IOException {
H4header.DataChunk chunk = chunks.get(chunkNo);
H4header.TagData chunkData = chunk.data;
chunkNo++;
return new LayoutTiled.DataChunk(chunk.origin, chunkData.offset);
}
}
private class H4CompressedChunkIterator implements LayoutBBTiled.DataChunkIterator {
List chunks;
int chunkNo;
H4CompressedChunkIterator(H4header.Vinfo vinfo) {
this.chunks = vinfo.chunks;
this.chunkNo = 0;
}
public boolean hasNext() {
return chunkNo < chunks.size();
}
public LayoutBBTiled.DataChunk next() throws IOException {
H4header.DataChunk chunk = chunks.get(chunkNo);
H4header.TagData chunkData = chunk.data;
assert (chunkData.ext_type == TagEnum.SPECIAL_COMP);
chunkNo++;
return new DataChunk(chunk.origin, chunkData.compress);
}
}
private class DataChunk implements LayoutBBTiled.DataChunk {
private int[] offset; // offset index of this chunk, reletive to entire array
private H4header.SpecialComp compress;
private ByteBuffer bb; // the data is placed into here
DataChunk(int[] offset, H4header.SpecialComp compress) {
this.offset = offset;
this.compress = compress;
}
public int[] getOffset() {
return offset;
}
public long[] getOffsetLong() {
throw new UnsupportedOperationException("Can't use long addressing");
}
public ByteBuffer getByteBuffer() throws IOException {
if (bb == null) {
// read compressed data in
H4header.TagData cdata = compress.getDataTag();
InputStream in;
// compressed data stored in one place
if (cdata.linked == null) {
byte[] cbuffer = new byte[cdata.length];
raf.seek(cdata.offset);
raf.readFully(cbuffer);
in = new ByteArrayInputStream(cbuffer);
} else { // or compressed data stored in linked storage
in = new LinkedInputStream(cdata.linked);
}
//System.out.println(" read compress " + cdata.length + " at= " + cdata.offset);
// uncompress it
if (compress.compress_type == TagEnum.COMP_CODE_DEFLATE) {
// read the stream in and uncompress
InputStream zin = new java.util.zip.InflaterInputStream(in);
ByteArrayOutputStream out = new ByteArrayOutputStream(compress.uncomp_length);
IO.copy(zin, out);
byte[] buffer = out.toByteArray();
bb = ByteBuffer.wrap(buffer);
//System.out.println(" uncompress " + buffer.length + ", wanted=" + compress.uncomp_length);
} else if (compress.compress_type == TagEnum.COMP_CODE_NONE) {
// just read the stream in
ByteArrayOutputStream out = new ByteArrayOutputStream(compress.uncomp_length);
IO.copy(in, out);
byte[] buffer = out.toByteArray();
bb = ByteBuffer.wrap(buffer);
} else {
throw new IllegalStateException("unknown compression type =" + compress.compress_type);
}
}
return bb;
}
}
@Override
public void reacquire() throws IOException {
super.reacquire();
header.raf = this.raf;
}
public Object sendIospMessage(Object message) {
if (message.toString().equals("header"))
return header;
return super.sendIospMessage(message);
}
}