package au.gov.amsa.util.netcdf;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import ucar.ma2.Array;
import ucar.ma2.DataType;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.NetcdfFileWriter;
import ucar.nc2.NetcdfFileWriter.Version;
import ucar.nc2.Variable;
public class NetCdfWriter implements AutoCloseable {
private final NetcdfFileWriter f;
private final Map<Var<?>, List<?>> map = new HashMap<>();
public NetCdfWriter(File file, String version) {
try {
f = NetcdfFileWriter.createNew(Version.netcdf3, file.getPath());
// add version attribute
f.addGroupAttribute(null, new Attribute("version", version));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public NetcdfFileWriter writer() {
return f;
}
public NetCdfWriter addAttribute(String name, String value) {
f.addGroupAttribute(null, new Attribute(name, value));
return this;
}
public <T> VarBuilder<T> addVariable(String shortName, Class<T> cls) {
return new VarBuilder<T>(this, shortName, cls);
}
public <T> Var<T> addVariable(String shortName, Optional<String> longName,
Optional<String> units, Optional<String> encoding, Class<T> cls, int numRecords) {
Preconditions.checkNotNull(shortName);
Preconditions.checkNotNull(longName);
Preconditions.checkNotNull(units);
Preconditions.checkNotNull(encoding);
Preconditions.checkNotNull(cls);
Dimension dimension = f.addDimension(null, shortName, numRecords);
Variable variable = f.addVariable(null, shortName, toDataType(cls),
Arrays.asList(dimension));
if (longName.isPresent())
variable.addAttribute(new Attribute("long_name", longName.get()));
if (units.isPresent())
variable.addAttribute(new Attribute("units", units.get()));
if (encoding.isPresent())
variable.addAttribute(new Attribute("encoding", encoding.get()));
return new Var<T>(this, variable, cls);
}
public <T> NetCdfWriter add(Var<T> variable, T value) {
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) map.get(variable);
if (list == null) {
list = Lists.newArrayList();
map.put(variable, list);
}
list.add(value);
return this;
}
@Override
public void close() {
try {
f.create();
for (Var<?> var : map.keySet()) {
List<?> list = map.get(var);
int[] shape = new int[] { list.size() };
Array data = Array.factory(DataType.getType(var.cls()), shape);
for (int i = 0; i < list.size(); i++) {
data.setObject(i, list.get(i));
}
f.write(var.variable(), data);
}
f.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InvalidRangeException e) {
throw new RuntimeException(e);
}
}
private static DataType toDataType(Class<?> cls) {
return DataType.DOUBLE;
}
public static class VarBuilder<T> {
private final NetCdfWriter writer;
final String shortName;
final Class<T> cls;
Optional<String> longName = Optional.absent();
Optional<String> units = Optional.absent();
Optional<String> encoding = Optional.absent();
Optional<Integer> numRecords = Optional.absent();
VarBuilder(NetCdfWriter writer, String shortName, Class<T> cls) {
this.writer = writer;
this.shortName = shortName;
this.cls = cls;
}
public VarBuilder<T> longName(String s) {
longName = Optional.of(s);
return this;
}
public VarBuilder<T> units(String s) {
longName = Optional.of(s);
return this;
}
public VarBuilder<T> encoding(String s) {
longName = Optional.of(s);
return this;
}
public VarBuilder<T> numRecords(int n) {
this.numRecords = Optional.of(n);
return this;
}
public Var<T> build() {
return writer.addVariable(shortName, longName, units, encoding, cls, numRecords.get());
}
}
public static class Var<T> {
private final Variable variable;
private final Class<T> cls;
private final NetCdfWriter writer;
public Var(NetCdfWriter writer, Variable variable, Class<T> cls) {
this.writer = writer;
this.variable = variable;
this.cls = cls;
}
public Variable variable() {
return variable;
}
public Class<T> cls() {
return cls;
}
public NetCdfWriter writer() {
return writer;
}
public Var<T> add(T t) {
writer.add(this, t);
return this;
}
}
}