ucar.nc2.iosp.netcdf3.N3streamWriter Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cdm Show documentation
Show all versions of cdm Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
The newest version!
// $Id: $
/*
* Copyright 1998-2009 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.nc2.iosp.netcdf3;
import ucar.nc2.*;
import ucar.ma2.DataType;
import ucar.nc2.constants.CDM;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import java.io.DataOutputStream;
/**
* Common superclass for N3outputStreamWriter and N3channelStreamWriter.
* Experimental
* @author john
*/
public abstract class N3streamWriter {
////////////////////////////////////////////////////////////////////////////////////////////////////////
protected ucar.nc2.NetcdfFile ncfile;
protected Map vinfoMap = new HashMap();
protected List vinfoList = new ArrayList(); // output order of the variables
protected boolean debug=false, debugPos=false, debugWriteData = false;
protected int recStart, recSize;
protected boolean usePadding = true;
protected long filePos = 0;
protected N3streamWriter(ucar.nc2.NetcdfFile ncfile) {
this.ncfile = ncfile;
}
/**
* Write the header to a stream.
*
* @param stream write to this stream.
* @param numrec pass in number of record is you know it, else -1 for "streaming" format variant
* @throws IOException if write fails
*/
public void writeHeader(DataOutputStream stream, int numrec) throws IOException {
// make sure ncfile structures were finished
ncfile.finish();
// magic number
stream.write(N3header.MAGIC);
int count = N3header.MAGIC.length;
// numrecs
Dimension udim = ncfile.getUnlimitedDimension();
if (numrec < 0) {
numrec = (udim == null) ? 0 : -1; // -1 means "streaming" - calc numrec through file length
}
stream.writeInt(numrec);
count += 4;
// dims
List dims = ncfile.getDimensions();
int numdims = dims.size();
if (numdims == 0) {
stream.writeInt(0);
stream.writeInt(0);
} else {
stream.writeInt(N3header.MAGIC_DIM);
stream.writeInt(numdims);
}
count += 8;
for (int i = 0; i < numdims; i++) {
Dimension dim = (Dimension) dims.get(i);
count += writeString(stream, N3iosp.makeValidNetcdfObjectName( dim.getShortName()));
stream.writeInt(dim.isUnlimited() ? 0 : dim.getLength());
count += 4;
}
// global attributes
count += writeAtts(stream, ncfile.getGlobalAttributes());
if (debug) System.out.println("vars header starts at "+count);
// variables
List vars = ncfile.getVariables();
int nvars = vars.size();
if (nvars == 0) {
stream.writeInt(0);
stream.writeInt(0);
} else {
stream.writeInt(N3header.MAGIC_VAR);
stream.writeInt(nvars);
}
count += 8;
/* Note on padding: In the special case of only a single record variable of character, byte, or short
// type, no padding is used between data values.
if (nvars == 1) {
Variable var = vars.get(0);
DataType dtype = var.getDataType();
if ((dtype == DataType.CHAR) || (dtype == DataType.BYTE) || (dtype == DataType.SHORT))
usePadding = false;
} */
// we have to calculate how big the header is before we can actually write it
// so we set stream = null
for (int i = 0; i < nvars; i++) {
Variable var = (Variable) vars.get(i);
if (var instanceof Structure) continue;
Vinfo vinfo = writeVar(null, var, 0);
count += vinfo.hsize;
}
// now calculate where things go
int dataStart = count; // data starts right after the header
int offset = dataStart; // track data offset
if (debug) System.out.println(" non-record vars start at "+dataStart);
// do all non-record variables first
for (int i = 0; i < nvars; i++) {
Variable var = (Variable) vars.get(i);
//if (var instanceof Structure) continue;
if (!var.isUnlimited()) {
Vinfo vinfo = writeVar(stream, var, offset);
vinfoMap.put(var, vinfo);
if (debugPos)
System.out.println(" " + var.getNameAndDimensions() + " begin at = " + offset + " end=" + (offset + vinfo.vsize));
offset += vinfo.vsize;
vinfoList.add(vinfo);
}
}
if (debug) System.out.println(" record vars start at "+offset);
recStart = offset; // record variables' data starts here
recSize = 0;
// do all record variables
for (int i = 0; i < nvars; i++) {
Variable var = (Variable) vars.get(i);
if (var.isUnlimited()) {
if (var instanceof Structure) continue;
Vinfo vinfo = writeVar(stream, var, offset);
vinfoMap.put(var, vinfo);
if (debugPos)
System.out.println(" " + var.getNameAndDimensions() + "(record) begin at = " + offset + " end=" + (offset + vinfo.vsize) + " size=" + vinfo.vsize);
offset += vinfo.vsize;
recSize += vinfo.vsize;
vinfoList.add(vinfo);
}
}
filePos = count;
if (debugPos) System.out.println("header written filePos= " + filePos+" recsize= "+recSize);
}
private Vinfo writeVar(DataOutputStream stream, Variable var, int offset) throws IOException {
int hsize = 0;
hsize += writeString(stream, N3iosp.makeValidNetcdfObjectName( var.getShortName()));
// dimensions
int vsize = var.getDataType().getSize();
List dims = var.getDimensions();
if (null != stream) stream.writeInt(dims.size());
hsize += 4;
for (Dimension dim : dims) {
int dimIndex = findDimensionIndex(dim);
if (null != stream) stream.writeInt(dimIndex);
hsize += 4;
if (!dim.isUnlimited())
vsize *= dim.getLength();
}
int pad = (usePadding) ? N3header.padding(vsize) : 0;
vsize += pad;
// variable attributes
hsize += writeAtts(stream, var.getAttributes());
// data type, variable size, beginning file position
int type = N3header.getType(var.getDataType());
if (null != stream) {
stream.writeInt(type);
stream.writeInt(vsize);
stream.writeInt(offset);
}
hsize += 12;
//if (debug) out.println(" name= "+name+" type="+type+" vsize="+vsize+" begin= "+begin+" isRecord="+isRecord+"\n");
return new Vinfo(var, hsize, vsize, offset, pad, var.isUnlimited());
}
private int writeAtts(DataOutputStream stream, List atts) throws IOException {
int natts = atts.size();
if (null != stream) {
if (natts == 0) {
stream.writeInt(0);
stream.writeInt(0);
} else {
stream.writeInt(N3header.MAGIC_ATT);
stream.writeInt(natts);
}
}
int hsize = 8;
for (int i = 0; i < natts; i++) {
Attribute att = atts.get(i);
hsize += writeString(stream, N3iosp.makeValidNetcdfObjectName( att.getShortName()));
int type = N3header.getType(att.getDataType());
if (null != stream) stream.writeInt(type);
hsize += 4;
if (type == 2) {
hsize += writeStringValues(stream, att);
} else {
int nelems = att.getLength();
if (null != stream) stream.writeInt(nelems);
hsize += 4;
int nbytes = 0;
for (int j = 0; j < nelems; j++)
nbytes += writeAttributeValue(stream, att.getNumericValue(j));
hsize += nbytes;
hsize += pad(stream, nbytes, (byte) 0);
}
}
return hsize;
}
private int writeStringValues(DataOutputStream stream, Attribute att) throws IOException {
int n = att.getLength();
if (n == 1)
return writeString(stream, att.getStringValue());
else {
StringBuilder values = new StringBuilder();
for (int i = 0; i < n; i++)
values.append(att.getStringValue(i));
return writeString(stream, values.toString());
}
}
private int writeAttributeValue(DataOutputStream stream, Number numValue) throws IOException {
if (numValue instanceof Byte) {
if (null != stream) stream.write(numValue.byteValue());
return 1;
} else if (numValue instanceof Short) {
if (null != stream) stream.writeShort(numValue.shortValue());
return 2;
} else if (numValue instanceof Integer) {
if (null != stream) stream.writeInt(numValue.intValue());
return 4;
} else if (numValue instanceof Float) {
if (null != stream) stream.writeFloat(numValue.floatValue());
return 4;
} else if (numValue instanceof Double) {
if (null != stream) stream.writeDouble(numValue.doubleValue());
return 8;
}
throw new IllegalStateException("unknown attribute type == " + numValue.getClass().getName());
}
// write a string then pad to 4 byte boundary
private int writeString(DataOutputStream stream, String s) throws IOException {
byte[] b = s.getBytes(CDM.utf8Charset);
if (null != stream) {
stream.writeInt(b.length);
stream.write(b);
}
int n = pad(stream, b.length, (byte) 0);
return n + 4 + b.length;
}
private int findDimensionIndex(Dimension wantDim) {
List dims = ncfile.getDimensions();
for (int i = 0; i < dims.size(); i++) {
Dimension dim = (Dimension) dims.get(i);
if (dim.equals(wantDim)) return i;
}
throw new IllegalStateException("unknown Dimension == " + wantDim);
}
// pad to a 4 byte boundary
protected int pad(DataOutputStream stream, int nbytes, byte fill) throws IOException {
int pad = N3header.padding(nbytes);
if (null != stream) {
for (int i = 0; i < pad; i++)
stream.write(fill);
}
return pad;
}
// variable info for reading/writing
static protected class Vinfo {
Variable v;
int hsize; // header size
int vsize; // size of array in bytes. if isRecord, size per record. includes padding
int offset; // offset of start of data from start of file
int pad; // number of padding bytes
boolean isRecord; // is it a record variable?
Vinfo(Variable v, int hsize, int vsize, int offset, int pad, boolean isRecord) {
this.v = v;
this.hsize = hsize;
this.vsize = vsize;
this.offset = offset;
this.pad = pad;
this.isRecord = isRecord;
}
public String toString() { return v.getFullName()+" vsize= "+vsize+" pad="+pad; }
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy