ucar.nc2.dt.fmrc.ForecastModelRunInventory Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netcdf Show documentation
Show all versions of netcdf Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
/*
* 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.dt.fmrc;
import org.jdom.output.XMLOutputter;
import org.jdom.output.Format;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import java.io.*;
import java.util.*;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.dataset.NetcdfDataset;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.VariableEnhanced;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.units.DateUnit;
import ucar.nc2.units.DateFormatter;
import ucar.nc2.NetcdfFile;
import ucar.nc2.iosp.IOServiceProvider;
import ucar.nc2.iosp.grid.GridServiceProvider;
import ucar.nc2.Variable;
import ucar.nc2.util.DiskCache2;
import ucar.nc2.util.IO;
import ucar.nc2.dt.GridDataset;
import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dt.fmr.FmrcCoordSys;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.LatLonPoint;
import ucar.unidata.geoloc.LatLonPointImpl;
/**
* This reads and writes XML files to summarize the inventory for a single ForecastModelRun.
* The underlying dataset is a GridDataset.
*
* Tracks unique TimeCoords (aka "valid times" aka "forecast times" aka "offset hours"), and tracks the list of
* variables (aka grids) that use that TimeCoord.
*
* Tracks unique VertCoords; grids have a reference to one if they are 3D.
*
*
* Data Structures
* List VertCoord
* double[] values
*
* List TimeCoord
* double[] offsetHour
* List Grid
* VertCoord (optional)
* List Misssing
*
*
* @author caron
*/
public class ForecastModelRunInventory {
public static final int OPEN_NORMAL = 1; // try to open XML, if fail, open dataset and write XML
public static final int OPEN_FORCE_NEW = 2; // only open dataset and write XML new
public static final int OPEN_XML_ONLY = 3; // only open XML, if not exist, return
static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ForecastModelRunInventory.class);
private String name;
private List times = new ArrayList(); // list of TimeCoord
private List vaxes = new ArrayList(); // list of VertCoord
private List eaxes = new ArrayList(); // list of EnsCoord
private Date runDate; // date of the run
private String runTime; // string representation of the date of the run
private GridDataset gds; // underlying dataset - may be null if read from XML
private LatLonRect bb;
private boolean debugMissing = false;
private ForecastModelRunInventory() {
}
private ForecastModelRunInventory(ucar.nc2.dt.GridDataset gds, Date runDate) {
this.gds = gds;
name = gds.getTitle();
NetcdfFile ncfile = gds.getNetcdfFile();
if (runDate == null) {
runTime = ncfile.findAttValueIgnoreCase(null, _Coordinate.ModelBaseDate, null);
if (runTime == null)
runTime = ncfile.findAttValueIgnoreCase(null, _Coordinate.ModelRunDate, null);
if (runTime == null)
throw new IllegalArgumentException("File must have " + _Coordinate.ModelBaseDate + " or " +
_Coordinate.ModelRunDate + " attribute ");
this.runDate = DateUnit.getStandardOrISO(runTime);
if (this.runDate == null)
throw new IllegalArgumentException(_Coordinate.ModelRunDate + " must be ISO date string " + runTime);
} else {
this.runDate = runDate;
DateFormatter df = new DateFormatter();
this.runTime = df.toDateTimeStringISO(runDate);
}
getIosp();
// add each variable
for (GridDatatype gg : gds.getGrids()) {
GridCoordSystem gcs = gg.getCoordinateSystem();
Grid grid = new Grid(gg.getName());
VariableEnhanced ve = gg.getVariable();
Variable v = ve.getOriginalVariable(); // LOOK why original variable ??
addMissing(v, gcs, grid);
// LOOK: Note this assumes a dense coordinate system
CoordinateAxis1D axis = gcs.getTimeAxis1D();
if (axis != null) {
TimeCoord tc = getTimeCoordinate(axis);
tc.vars.add(grid);
grid.parent = tc;
}
CoordinateAxis1D eaxis = gcs.getEnsembleAxis();
if (eaxis != null) {
int[] einfo = getEnsInfo( v );
grid.ec = getEnsCoordinate(eaxis, einfo );
}
CoordinateAxis1D vaxis = gcs.getVerticalAxis();
if (vaxis != null) {
grid.vc = getVertCoordinate(vaxis);
}
LatLonRect rect = gcs.getLatLonBoundingBox();
if (null == bb)
bb = rect;
else if (!bb.equals(rect))
bb.extend(rect);
}
}
public void close() throws IOException {
if (null != gds) gds.close();
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
/**
* Get the date of the ForecastModelRun
* @return the date of the ForecastModelRun
*/
public Date getRunDate() {
return runDate;
}
/**
* Get string representation of the date of the ForecastModelRun
* @return string representation of the date of the ForecastModelRun
*/
public String getRunDateString() {
return runTime;
}
/**
* Get a list of unique TimeCoords, which contain the list of variables that all use that TimeCoord.
*
* @return list of TimeCoord
*/
public List getTimeCoords() {
return times;
}
/**
* Get a list of unique VertCoords.
*
* @return list of VertCoord
*/
public List getVertCoords() {
return vaxes;
}
public LatLonRect getBB() {
return bb;
}
/**
* Release and close the dataset, and allow CG.
*
* @throws IOException on io error
*/
public void releaseDataset() throws IOException {
if (gds == null)
return;
gds.close();
for (TimeCoord tc : times) {
tc.axis = null; // allow GC
}
for (VertCoord vc : vaxes) {
vc.axis = null; // allow GC
}
}
public Grid findGrid(String name) {
for (TimeCoord tc : times) {
List grids = tc.getGrids();
for (Grid g : grids) {
if (g.name.equals(name))
return g;
}
}
return null;
}
//////////////////////////////////////////////////////////
// Grib files are collections of 2D horizontal arrays.
// LOOK: breaking encapsolation !!!
private void getIosp() {
NetcdfDataset ncd = (NetcdfDataset) gds.getNetcdfFile();
NetcdfFile ncfile = ncd.getReferencedFile();
while (ncfile instanceof NetcdfDataset) {
ncd = (NetcdfDataset) ncfile;
ncfile = ncd.getReferencedFile();
}
if (ncfile == null) return;
IOServiceProvider iosp = ncfile.getIosp();
if (iosp == null) return;
if (!(iosp instanceof GridServiceProvider)) return;
gribIosp = (GridServiceProvider) iosp;
}
private GridServiceProvider gribIosp;
private void addMissing(Variable v, GridCoordSystem gcs, Grid grid) {
if (gribIosp == null) return;
if (gcs.getVerticalAxis() == null && gcs.getEnsembleAxis() == null) return;
int ntimes = (int) gcs.getTimeAxis().getSize();
int nverts = 1;
if (gcs.getVerticalAxis() != null )
nverts = (int) gcs.getVerticalAxis().getSize();
int nens = 1;
if (gcs.getEnsembleAxis() != null )
nens = (int) gcs.getEnsembleAxis().getSize();
//int total = ntimes * nverts;
int total = ntimes * nens * nverts;
List missing = new ArrayList();
for (int timeIndex = 0; timeIndex < ntimes; timeIndex++) {
for (int ensIndex = 0; ensIndex < nens; ensIndex++) {
for (int vertIndex = 0; vertIndex < nverts; vertIndex++)
try {
if (gribIosp.isMissingXY(v, timeIndex, ensIndex, vertIndex))
missing.add(new Missing(timeIndex, ensIndex, vertIndex));
} catch (InvalidRangeException e) {
e.printStackTrace();
}
}
}
if (missing.size() > 0) {
grid.missing = missing;
if (debugMissing)
System.out.println("Missing " + gds.getTitle() + " " + v.getName() + " # =" + missing.size() + "/" + total);
} else if (debugMissing)
System.out.println(" None missing for " + gds.getTitle() + " " + v.getName() + " total = " + total);
}
private int[] getEnsInfo( Variable v ) {
//if (gribIosp == null) return null;
//int[] info = gribIosp.ensembleInfo(v);
return null;
}
/////////////////////////////////////////////////////////////////////////
private TimeCoord getTimeCoordinate(CoordinateAxis1D axis) {
for (TimeCoord tc : times) {
if ((tc.axis != null) && (tc.axis == axis))
return tc;
}
TimeCoord want = new TimeCoord(runDate, axis);
for (TimeCoord tc : times) {
if ((tc.equalsData(want)))
return tc;
}
// its a new one
times.add(want);
want.setId(Integer.toString(tc_seqno));
tc_seqno++;
return want;
}
private int tc_seqno = 0;
/**
* Represents a list of valid times.
* Tracks a list of variables that all have the same list of valid times.
*/
public static class TimeCoord implements FmrcCoordSys.TimeCoord, Comparable {
private CoordinateAxis1D axis; // is null when read from XML
private List vars = new ArrayList(); // list of Grid
private String id; // unique id
private double[] offset; // hours since runTime
TimeCoord() {
}
TimeCoord(int num, TimeCoord from) {
this.id = Integer.toString(num);
this.offset = from.offset;
}
TimeCoord(Date runDate, CoordinateAxis1D axis) {
this.axis = axis;
DateUnit unit = null;
try {
unit = new DateUnit(axis.getUnitsString());
} catch (Exception e) {
throw new IllegalArgumentException("Not a unit of time "+axis.getUnitsString());
}
int n = (int) axis.getSize();
offset = new double[n];
for (int i = 0; i < axis.getSize(); i++) {
Date d = unit.makeDate(axis.getCoordValue(i));
offset[i] = getOffsetInHours(runDate, d);
}
}
/**
* The list of Grid that use this TimeCoord
* @return list of Grid that use this TimeCoord
*/
public List getGrids() {
return vars;
}
/**
* A unique id for this TimeCoord
* @return unique id for this TimeCoord
*/
public String getId() {
return id;
}
/**
* Set the unique id for this TimeCoord
* @param id id for this TimeCoord
*/
public void setId(String id) {
this.id = id;
}
public String getName() {
return id.equals("0") ? "time" : "time" + id;
}
/**
* The list of valid times, in units of hours since the run time
*/
public double[] getOffsetHours() {
return offset;
}
public void setOffsetHours(double[] offset) {
this.offset = offset;
}
/**
* Instances that have the same offsetHours are equal
* @param tother compare this TomCoord's data
* @return true if data is equal
*/
public boolean equalsData(TimeCoord tother) {
if (offset.length != tother.offset.length)
return false;
for (int i = 0; i < offset.length; i++) {
if (!ucar.nc2.util.Misc.closeEnough(offset[i], tother.offset[i]))
return false;
}
return true;
}
int findIndex(double offsetHour) {
for (int i = 0; i < offset.length; i++)
if (offset[i] == offsetHour)
return i;
return -1;
}
/* Overrride hashcode to correspond to equals()
public int hashCode() {
if (hashcode != 0) return hashcode;
int result = 17;
for (int i = 0; i < offset.length ; i++) {
long temp = Double.doubleToLongBits( offset[i]);
result = 37*result + (int) (temp ^ (temp >>>32));
}
hashcode = result;
return hashcode;
}
private int hashcode = 0; */
public int compareTo(Object o) {
TimeCoord ot = (TimeCoord) o;
return id.compareTo(ot.id);
}
}
//////////////////////////////////////////////////////
/**
* A Grid variable has a name, timeCoord and optionally a Vertical Coordinate, and list of Missing.
* The inventory is represented as:
* 1) if 2D, the timeCoord represents the inventory
* 2) if 3D, inventory = timeCoord * vertCoord - Missing
*/
public static class Grid implements Comparable {
String name; // , sname;
TimeCoord parent = null;
EnsCoord ec = null; // optional
VertCoord vc = null; // optional
List missing;
Grid(String name) {
this.name = name;
}
public int compareTo(Object o) {
Grid other = (Grid) o;
return name.compareTo(other.name);
}
public int countInventory() {
return countTotal() - countMissing();
}
public int countTotal() {
int ntimes = parent.getOffsetHours().length;
return ntimes * getVertCoordLength();
}
public int countMissing() {
return (missing == null) ? 0 : missing.size();
}
int getVertCoordLength() {
return (vc == null) ? 1 : vc.getValues1().length;
}
public int countInventory(double hourOffset) {
int timeIndex = parent.findIndex(hourOffset);
if (timeIndex < 0)
return 0;
// otherwise, count the Missing with this time index
if (missing == null)
return getVertCoordLength();
int count = 0;
for (Missing m : missing) {
if (m.timeIndex == timeIndex)
count++;
}
return getVertCoordLength() - count;
}
/**
* Get inventory as an array of vert coords, at a particular time coord = hourOffset
*
* @param hourOffset : may or may not be in the list of time coords
* @return array of vert coords. NaN = missing; -0.0 = surface.
*/
public double[] getVertCoords(double hourOffset) {
int timeIndex = parent.findIndex(hourOffset);
if (timeIndex < 0)
return new double[0]; // if not in list of time coordinates, then entire inventory is missing
if (vc == null) {
double[] result = new double[1]; // if 2D return -0.0
result[0] = -0.0;
return result;
}
double[] result = vc.getValues1().clone();
if (null != missing) {
for (Missing m : missing) {
if (m.timeIndex == timeIndex)
result[m.vertIndex] = Double.NaN;
}
}
return result;
}
}
public static class Missing {
int timeIndex, ensIndex, vertIndex;
Missing(int timeIndex, int ensIndex, int vertIndex) {
this.timeIndex = timeIndex;
this.ensIndex = ensIndex;
this.vertIndex = vertIndex;
}
}
//////////////////////////////////////////////////////
private VertCoord getVertCoordinate(String vert_id) {
if (vert_id == null)
return null;
for (VertCoord vc : vaxes) {
if ((vc.id.equals(vert_id)))
return vc;
}
return null;
}
private VertCoord getVertCoordinate(CoordinateAxis1D axis) {
for (VertCoord vc : vaxes) {
if ((vc.axis != null) && (vc.axis == axis)) return vc;
}
VertCoord want = new VertCoord(axis);
for (VertCoord vc : vaxes) {
if ((vc.equalsData(want))) return vc;
}
// its a new one
vaxes.add(want);
want.setId(Integer.toString(vc_seqno));
vc_seqno++;
return want;
}
private int vc_seqno = 0;
/**
* Represents a vertical coordinate.
* Tracks a list of variables that all have the same list of valid times.
*/
public static class VertCoord implements FmrcCoordSys.VertCoord, Comparable {
CoordinateAxis1D axis; // is null when read from XML
private String name, units;
private String id; // unique id
double[] values1, values2;
VertCoord() {
}
VertCoord(CoordinateAxis1D axis) {
this.axis = axis;
this.name = axis.getName();
this.units = axis.getUnitsString();
int n = (int) axis.getSize();
if (axis.isInterval()) {
values1 = axis.getBound1();
values2 = axis.getBound2();
} else {
values1 = new double[n];
for (int i = 0; i < axis.getSize(); i++)
values1[i] = axis.getCoordValue(i);
}
}
// copy constructor
VertCoord(VertCoord vc) {
this.name = vc.getName();
this.units = vc.getUnits();
this.id = vc.getId();
this.values1 = vc.getValues1().clone();
this.values2 = (vc.getValues2() == null) ? null : vc.getValues2().clone();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUnits() {
return units;
}
public void setUnits(String units) {
this.units = units;
}
public double[] getValues1() {
return values1;
}
public void setValues1(double[] values) {
this.values1 = values;
}
public double[] getValues2() {
return values2;
}
public void setValues2(double[] values) {
this.values2 = values;
}
public int getSize() {
return values1.length;
}
public boolean equalsData(VertCoord other) {
if (values1.length != other.values1.length)
return false;
for (int i = 0; i < values1.length; i++) {
if (!ucar.nc2.util.Misc.closeEnough(values1[i], other.values1[i]))
return false;
}
if ((values2 == null) && (other.values2 == null))
return true;
if ((values2 == null) || (other.values2 == null))
return false;
if (values2.length != other.values2.length)
return false;
for (int i = 0; i < values2.length; i++) {
if (!ucar.nc2.util.Misc.closeEnough(values2[i], other.values2[i]))
return false;
}
return true;
}
public int compareTo(Object o) {
VertCoord other = (VertCoord) o;
return name.compareTo(other.name);
}
}
//////////////////////////////////////////////////////
private EnsCoord getEnsCoordinate(String ens_id) {
if (ens_id == null)
return null;
for (EnsCoord ec : eaxes) {
if ((ec.id.equals(ens_id)))
return ec;
}
return null;
}
private EnsCoord getEnsCoordinate(CoordinateAxis1D axis, int[] einfo ) {
for (EnsCoord ec : eaxes) {
if ((ec.axis != null) && (ec.axis == axis)) return ec;
}
EnsCoord want = new EnsCoord(axis, einfo );
for (EnsCoord ec : eaxes) {
if ((ec.equalsData(want))) return ec;
}
// its a new one
eaxes.add(want);
want.setId(Integer.toString(ec_seqno));
ec_seqno++;
return want;
}
private int ec_seqno = 0;
/**
* Represents a ensemble coordinate.
* Tracks a list of variables that all have the same list of ensembles.
*/
public static class EnsCoord implements FmrcCoordSys.EnsCoord, Comparable {
CoordinateAxis1D axis; // is null when read from XML
private String name; //, units;
private String id; // unique id
private int ensembles;
private int pdn;
private int[] ensTypes;
EnsCoord() {
}
EnsCoord(CoordinateAxis1D axis, int[] einfo) {
this.axis = axis;
this.name = axis.getName();
this.ensembles = einfo[ 0 ];
this.pdn = einfo[ 1 ];
this.ensTypes = new int[ this.ensembles ];
System.arraycopy( einfo, 2, ensTypes, 0, ensembles);
}
// copy constructor
EnsCoord(EnsCoord ec) {
this.name = ec.getName();
this.id = ec.getId();
this.ensembles = ec.getNEnsembles();
this.pdn = ec.getPDN();
this.ensTypes = ec.getEnsTypes().clone();
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getNEnsembles() {
return ensembles;
}
public void setNEnsembles(int ensembles) {
this.ensembles = ensembles;
}
public int getPDN() {
return pdn;
}
public void setPDN(int pdn ) {
this.pdn = pdn;
}
public int[] getEnsTypes() {
return ensTypes;
}
public void setEnsTypes(int[] ensTypes ) {
this.ensTypes = ensTypes;
}
public int getSize() {
return ensembles;
}
public boolean equalsData(EnsCoord other) {
if (ensembles != other.ensembles)
return false;
if (pdn != other.pdn)
return false;
for (int i = 0; i < ensTypes.length; i++) {
if ( ensTypes[i] != other.ensTypes[i])
return false;
}
return true;
}
public int compareTo(Object o) {
EnsCoord other = (EnsCoord) o;
return name.compareTo(other.name);
}
}
static public double getOffsetInHours(Date origin, Date date) {
double secs = date.getTime() / 1000;
double origin_secs = origin.getTime() / 1000;
double diff = secs - origin_secs;
return diff / 3600.0;
}
//////////////////////////////////////////////////////////////
/**
* Write the XML representation to a local file.
*
* @param filename wite to this local file
* @throws IOException on io error
*/
public void writeXML(String filename) throws IOException {
OutputStream out = new BufferedOutputStream(new FileOutputStream(filename));
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
fmt.output(writeDocument(), out);
out.close();
}
/**
* Write the XML representaion to an OutputStream.
*
* @param out write to this OutputStream
* @throws IOException on io error
*/
public void writeXML(OutputStream out) throws IOException {
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
fmt.output(writeDocument(), out);
}
/**
* Write the XML representation to a String.
* @return the XML representation to a String.
*/
public String writeXML() {
XMLOutputter fmt = new XMLOutputter(Format.getPrettyFormat());
return fmt.outputString(writeDocument());
}
/**
* Create the XML representation
* @return the XML representation as a Document
*/
public Document writeDocument() {
Element rootElem = new Element("forecastModelRun");
Document doc = new Document(rootElem);
rootElem.setAttribute("name", getName());
rootElem.setAttribute("runTime", runTime);
// list all the ensemble coords
Collections.sort(eaxes);
for (EnsCoord ec : eaxes) {
Element ecElem = new Element("ensCoord");
rootElem.addContent(ecElem);
ecElem.setAttribute("id", ec.id);
ecElem.setAttribute("name", ec.name);
ecElem.setAttribute("product_definition", Integer.toString(ec.pdn));
//if (ec.units != null)
// ecElem.setAttribute("units", ec.units);
StringBuilder sbuff = new StringBuilder();
for (int j = 0; j < ec.ensTypes.length; j++) {
if (j > 0) sbuff.append(" ");
sbuff.append(Integer.toString(ec.ensTypes[j]));
}
ecElem.addContent(sbuff.toString());
}
// list all the vertical coords
Collections.sort(vaxes);
for (VertCoord vc : vaxes) {
Element vcElem = new Element("vertCoord");
rootElem.addContent(vcElem);
vcElem.setAttribute("id", vc.id);
vcElem.setAttribute("name", vc.name);
if (vc.units != null)
vcElem.setAttribute("units", vc.units);
StringBuilder sbuff = new StringBuilder();
for (int j = 0; j < vc.values1.length; j++) {
if (j > 0) sbuff.append(" ");
sbuff.append(Double.toString(vc.values1[j]));
if (vc.values2 != null) {
sbuff.append(",");
sbuff.append(Double.toString(vc.values2[j]));
}
}
vcElem.addContent(sbuff.toString());
}
// list all the offset hours
for (TimeCoord tc : times) {
Element offsetElem = new Element("offsetHours");
rootElem.addContent(offsetElem);
offsetElem.setAttribute("id", tc.id);
StringBuilder sbuff = new StringBuilder();
for (int j = 0; j < tc.offset.length; j++) {
if (j > 0) sbuff.append(" ");
sbuff.append(Double.toString(tc.offset[j]));
}
offsetElem.addContent(sbuff.toString());
Collections.sort(tc.vars);
for (Grid grid : tc.vars) {
Element varElem = new Element("variable");
offsetElem.addContent(varElem);
varElem.setAttribute("name", grid.name);
if (grid.ec != null)
varElem.setAttribute("ens_id", grid.ec.id);
if (grid.vc != null)
varElem.setAttribute("vert_id", grid.vc.id);
if ((grid.missing != null) && (grid.missing.size() > 0)) {
Element missingElem = new Element("missing");
varElem.addContent(missingElem);
sbuff.setLength(0);
for (int k = 0; k < grid.missing.size(); k++) {
Missing m = grid.missing.get(k);
if (k > 0) sbuff.append(" ");
sbuff.append(m.timeIndex);
if ( grid.ec != null ) {
sbuff.append(",");
sbuff.append(m.ensIndex);
}
if ( grid.vc != null ) {
sbuff.append(",");
sbuff.append(m.vertIndex);
}
}
missingElem.addContent(sbuff.toString());
}
}
// add lat/lon bounding box
if (bb != null) {
Element bbElem = new Element("horizBB");
rootElem.addContent(bbElem);
LatLonPoint llpt = bb.getLowerLeftPoint();
LatLonPoint urpt = bb.getUpperRightPoint();
bbElem.setAttribute("west", ucar.unidata.util.Format.dfrac(llpt.getLongitude(), 3));
bbElem.setAttribute("east", ucar.unidata.util.Format.dfrac(urpt.getLongitude(), 3));
bbElem.setAttribute("south", ucar.unidata.util.Format.dfrac(llpt.getLatitude(), 3));
bbElem.setAttribute("north", ucar.unidata.util.Format.dfrac(urpt.getLatitude(), 3));
}
}
return doc;
}
/**
* Construct a ForecastModelRun from its XML representation
*
* @param xmlLocation location of xml - assumed to be a local file.
* @return ForecastModelRun
* @throws IOException on io error
*/
public static ForecastModelRunInventory readXML(String xmlLocation) throws IOException {
if (debug) System.out.println(" read from XML " + xmlLocation);
InputStream is = new BufferedInputStream(new FileInputStream(xmlLocation));
org.jdom.Document doc;
try {
SAXBuilder builder = new SAXBuilder();
doc = builder.build(is);
} catch (JDOMException e) {
throw new IOException(e.getMessage() + " reading from XML " + xmlLocation);
}
Element rootElem = doc.getRootElement();
ForecastModelRunInventory fmr = new ForecastModelRunInventory();
fmr.runTime = rootElem.getAttributeValue("runTime");
DateFormatter formatter = new DateFormatter();
fmr.runDate = formatter.getISODate(fmr.runTime);
java.util.List eList = rootElem.getChildren("ensCoord");
for (Element ensElem : eList) {
EnsCoord ec = new EnsCoord();
fmr.eaxes.add(ec);
ec.id = ensElem.getAttributeValue("id");
ec.name = ensElem.getAttributeValue("name");
ec.pdn = Integer.parseInt( ensElem.getAttributeValue("product_definition"));
// parse the values
String values = ensElem.getText();
StringTokenizer stoke = new StringTokenizer(values);
ec.ensembles = stoke.countTokens();
ec.ensTypes = new int[ ec.ensembles ];
int count = 0;
while (stoke.hasMoreTokens()) {
String toke = stoke.nextToken();
int pos = toke.indexOf(',');
if (pos < 0)
ec.ensTypes[count] = Integer.parseInt(toke);
// else {
// String val1 = toke.substring(0, pos);
// String val2 = toke.substring(pos + 1);
// vc.values1[count] = Double.parseDouble(val1);
// vc.values2[count] = Double.parseDouble(val2);
// }
count++;
}
}
java.util.List vList = rootElem.getChildren("vertCoord");
for (Element vertElem : vList) {
VertCoord vc = new VertCoord();
fmr.vaxes.add(vc);
vc.id = vertElem.getAttributeValue("id");
vc.name = vertElem.getAttributeValue("name");
vc.units = vertElem.getAttributeValue("units");
// parse the values
String values = vertElem.getText();
StringTokenizer stoke = new StringTokenizer(values);
int n = stoke.countTokens();
vc.values1 = new double[n];
int count = 0;
while (stoke.hasMoreTokens()) {
String toke = stoke.nextToken();
int pos = toke.indexOf(',');
if (pos < 0)
vc.values1[count] = Double.parseDouble(toke);
else {
if (vc.values2 == null)
vc.values2 = new double[n];
String val1 = toke.substring(0, pos);
String val2 = toke.substring(pos + 1);
vc.values1[count] = Double.parseDouble(val1);
vc.values2[count] = Double.parseDouble(val2);
}
count++;
}
}
java.util.List tList = rootElem.getChildren("offsetHours");
for (Element timeElem : tList) {
TimeCoord tc = new TimeCoord();
fmr.times.add(tc);
tc.id = timeElem.getAttributeValue("id");
// parse the values
String values = timeElem.getText();
StringTokenizer stoke = new StringTokenizer(values);
int n = stoke.countTokens();
tc.offset = new double[n];
int count = 0;
while (stoke.hasMoreTokens()) {
tc.offset[count++] = Double.parseDouble(stoke.nextToken());
}
//get the variable names
List varList = timeElem.getChildren("variable");
for (Element vElem : varList) {
Grid grid = new Grid(vElem.getAttributeValue("name"));
grid.ec = fmr.getEnsCoordinate(vElem.getAttributeValue("ens_id"));
grid.vc = fmr.getVertCoordinate(vElem.getAttributeValue("vert_id"));
tc.vars.add(grid);
grid.parent = tc;
List mList = vElem.getChildren("missing");
for (Element mElem : mList) {
grid.missing = new ArrayList();
// parse the values
values = mElem.getText();
stoke = new StringTokenizer(values, " ,");
while (stoke.hasMoreTokens()) {
int timeIdx = Integer.parseInt(stoke.nextToken());
int ensIdx = 0;
if (grid.ec != null )
ensIdx = Integer.parseInt(stoke.nextToken());
int vertIdx = 0;
if (grid.vc != null )
vertIdx = Integer.parseInt(stoke.nextToken());
grid.missing.add(new Missing(timeIdx, ensIdx, vertIdx));
}
}
}
}
// add lat/lon bounding box
Element bbElem = rootElem.getChild("horizBB");
if (bbElem != null) {
double west = Double.parseDouble(bbElem.getAttributeValue("west"));
double east = Double.parseDouble(bbElem.getAttributeValue("east"));
double north = Double.parseDouble(bbElem.getAttributeValue("north"));
double south = Double.parseDouble(bbElem.getAttributeValue("south"));
fmr.bb = new LatLonRect(new LatLonPointImpl(south, west), new LatLonPointImpl(north, east));
}
return fmr;
}
/**
* Open a GridDataset and construct a ForecastModelRun.
* The information is serialized into am XML file at ncfileLocation.fmrInv.xml, and used if it exists.
*
* @param cache use this cache to look for fmrInv.xml files (may be null)
* @param ncfileLocation location of the grid dataset.
* @param mode one of OPEN_NORMAL, OPEN_FORCE_NEW, OPEN_XML_ONLY constants
* @param isFile if its a file: new File( ncfileLocation) makes sense, so we can check if its changed
* @return ForecastModelRun
* @throws IOException on io error
*/
public static ForecastModelRunInventory open(ucar.nc2.util.DiskCache2 cache, String ncfileLocation, int mode, boolean isFile) throws IOException {
boolean force = (mode == OPEN_FORCE_NEW); // always write a new one
boolean xml_only = (mode == OPEN_XML_ONLY); // never write a new one
// do we already have a fmrInv file?
String summaryFileLocation = ncfileLocation + ".fmrInv.xml";
File summaryFile = new File(summaryFileLocation);
if (!summaryFile.exists()) {
if (null != cache) { // look for it in the cache
summaryFile = cache.getCacheFile(summaryFileLocation);
summaryFileLocation = summaryFile.getPath();
}
}
boolean haveOne = (summaryFile != null) && (summaryFile.exists());
if (xml_only && !haveOne) return null;
// use it if it exists
if (!force && haveOne) {
if (isFile) { // see if its changed
File ncdFile = new File(ncfileLocation);
if (!ncdFile.exists())
throw new IllegalArgumentException("Data File must exist = " + ncfileLocation);
if (xml_only || (summaryFile.lastModified() >= ncdFile.lastModified())) {
try { // hasnt changed - use it
return readXML(summaryFileLocation);
} catch (Exception ee) {
log.error("Failed to read FmrcInventory " + summaryFileLocation, ee);
// fall through to recreating it
}
}
// fall through to recreating it
} else { // not a file, just use it
try {
return readXML(summaryFileLocation);
} catch (Exception ee) {
log.error("Failed to read FmrcInventory " + summaryFileLocation, ee);
// fall through to recreating it
}
}
}
// otherwise, try to make it
/* try {
if (null != cache) {
summaryFile = cache.getCacheFile(summaryFileLocation);
summaryFileLocation = summaryFile.getPath();
} else {
summaryFile = new File(summaryFileLocation);
if (summaryFile.createNewFile()) {
summaryFile.delete();
} else {
summaryFile = null;
}
}
} catch (Throwable t) {
summaryFileLocation = null;
} */
if (debug) System.out.println(" read from dataset " + ncfileLocation + " write to XML " + summaryFileLocation);
ucar.nc2.dt.grid.GridDataset gds = null;
ForecastModelRunInventory fmr = null;
try {
gds = ucar.nc2.dt.grid.GridDataset.open(ncfileLocation);
fmr = new ForecastModelRunInventory(gds, null);
} catch (IOException ioe) {
if (debug)
ioe.printStackTrace();
return null;
} finally {
if (gds != null) gds.close();
}
// try to write it for future reference
if (summaryFileLocation != null) {
try {
fmr.writeXML(summaryFileLocation);
} catch (Throwable t) {
log.error("Failed to write FmrcInventory to " + summaryFileLocation, t);
}
}
if (showXML)
IO.copyFile(summaryFileLocation, System.out);
fmr.releaseDataset();
return fmr;
}
public static ForecastModelRunInventory open(ucar.nc2.dt.GridDataset gds, Date runDate) {
return new ForecastModelRunInventory(gds, runDate);
}
private static boolean debug = false, showXML = false;
public static void main2(String args[]) throws Exception {
//String def = "C:/data/grib/nam/c20s/NAM_CONUS_20km_surface_20060316_1800.grib1";
// String def = "C:/data/radarMosaic/RADAR_10km_mosaic_20060807_2220.grib1";
String def = "R:/testdata/motherlode/grid/NAM_CONUS_80km_20060728_1200.grib1";
String datasetName = (args.length < 1) ? def : args[0];
// ucar.nc2.util.DiskCache2 cache = new ucar.nc2.util.DiskCache2("C:/data/grib", false, -1, -1);
// cache.setCachePathPolicy(DiskCache2.CACHEPATH_POLICY_NESTED_TRUNCATE, "RUC");
ForecastModelRunInventory fmr = open(null, datasetName, OPEN_FORCE_NEW, true);
fmr.writeXML(System.out);
}
public static void main(String args[]) throws IOException {
if (args.length == 1) {
ForecastModelRunInventory.open(null, args[0], ForecastModelRunInventory.OPEN_FORCE_NEW, true);
ForecastModelRunInventory.readXML( args[0] +".fmrInv.xml" );
return;
}
DiskCache2 cache = new DiskCache2("fmrcInventory/", true, 5 * 24 * 3600, 3600);
String url = "http://motherlode.ucar.edu:9080/thredds/dodsC/fmrc/NCEP/NAM/CONUS_12km/files/NAM_CONUS_12km_20070419_1800.grib2";
ForecastModelRunInventory fmr = ForecastModelRunInventory.open(cache, url, ForecastModelRunInventory.OPEN_NORMAL, false);
fmr.writeXML(System.out);
}
}