/*
* 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.grid;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.dt.fmr.FmrcCoordSys;
import ucar.nc2.iosp.AbstractIOServiceProvider;
import ucar.nc2.util.CancelTask;
import ucar.unidata.io.RandomAccessFile;
import ucar.grid.GridIndex;
import ucar.grid.GridRecord;
import java.io.IOException;
/**
* Superclass for Gempak grid, MciDAS grid, and GRIB IOSPs
*
* @author IDV Development Team
*/
public abstract class GridServiceProvider extends AbstractIOServiceProvider {
public enum IndexExtendMode {
/**
* if data file changes, completely rewrite the index
*/
rewrite,
/**
* if data file changes, assume its been extended, so extend the index
*/
extendwrite,
/**
* if index file exists, use it as is
*/
readonly
}
// these defaults are for clients, TDS sets these explicitly
static protected IndexExtendMode indexFileModeOnOpen = IndexExtendMode.rewrite; // default is to rewrite
static protected IndexExtendMode indexFileModeOnSync = IndexExtendMode.extendwrite; // default is to extend
static protected boolean addLatLon = false; // add lat/lon coordinates for strict CF compliance LOOK should not be static !
static protected boolean useMaximalCoordSys = false;
static protected boolean forceNewIndex = false; // force that a new index file is written - for debugging
static protected boolean alwaysInCache = false;
/**
* debug flags
*/
public static boolean debugOpen = false,
debugMissing = false,
debugMissingDetails = false,
debugProj = false,
debugTiming = false,
debugVert = false;
/**
* Set whether to use the maximal coordinate system or not
*
* @param b true to use
*/
static public void useMaximalCoordSys(boolean b) {
useMaximalCoordSys = b;
}
/**
* Set whether to force new index or not
*
* @param b true to use
*/
static public void forceNewIndex(boolean b) {
forceNewIndex = b;
}
/**
* Set the debug flags
*
* @param debugFlag debug flags
*/
static public void setDebugFlags(ucar.nc2.util.DebugFlags debugFlag) {
debugOpen = debugFlag.isSet("Grid/open");
debugMissing = debugFlag.isSet("Grid/missing");
debugMissingDetails = debugFlag.isSet("Grid/missingDetails");
debugProj = debugFlag.isSet("Grid/projection");
debugVert = debugFlag.isSet("Grid/vertical");
debugTiming = debugFlag.isSet("Grid/timing");
}
/**
* This controls what happens when a GRIB file is opened, and the data file has changed since the index was written.
* <ol>
* <li>IndexExtendMode.extendwrite: when GRIB file length increases, extend the index. This is the case when the file
* is being appended to, as new data arrives.
* <li>IndexExtendMode.rewrite: when GRIB file length changes, rewrite the index. This is the safest thing to do,
* at the expense of performance.
* <li>IndexExtendMode.readonly: never modify an existing index, just use it. However, if there is no index, created one
* </ol>
*
* @param mode IndexExtendMode when file is opened
*/
static public void setIndexFileModeOnOpen(IndexExtendMode mode) {
indexFileModeOnOpen = mode;
}
/**
* This controls what happens when sync() is called on a GRIB file. The main use of sync() is when you are using
* NetcdfFile object caching. Before NetcdfFile is returned from a cache hit, sync() is called on it.
* Default is IndexExtendMode.extend.
*
* @param mode IndexExtendMode when sync() is called. Same meaning as setIndexExtendMode(IndexExtendMode mode)
*/
/**
* This controls what happens when a GRIB file is synced (usually from FileCache), and the data or index file has changed
* since the file was placed in the cache.
* <ol>
* <li>IndexExtendMode.extendwrite: when GRIB file or index length increases, extend the index. If file or index length
* decreases, rewrite it.
* <li>IndexExtendMode.rewrite: when GRIB file length changes, rewrite the index.
* <li>IndexExtendMode.readonly: never modify an existing index, just use it. However, if there is no index, created one
* </ol>
*
* @param mode IndexExtendMode when file is opened
*/
static public void setIndexFileModeOnSync(IndexExtendMode mode) {
indexFileModeOnSync = mode;
}
// backwards compatible with old API
/**
* Set how indexes are used for both open and sync
*
* @param b if true, set modes to IndexExtendMode.extendwrite, else IndexExtendMode.readonly
* @deprecated use setIndexFileModeOnSync and setIndexFileModeOnOpen
*/
static public void setExtendIndex(boolean b) {
indexFileModeOnOpen = b ? IndexExtendMode.extendwrite : IndexExtendMode.readonly;
indexFileModeOnSync = b ? IndexExtendMode.extendwrite : IndexExtendMode.readonly;
}
/**
* Set disk cache policy for index files.
* Default = false, meaning try to write index files in same directory as grib file.
* True means always use the DiskCache area. TDS sets this to true, so it wont interfere with external indexer.
*
* @param b set to this value
*/
static public void setIndexAlwaysInCache(boolean b) {
alwaysInCache = b;
}
////////////////////////////////////////////////////////////////////////////////////////////////
/**
* set by the FMRC from the inventory definition, otherwise null
*/
protected FmrcCoordSys fmrcCoordSys;
/**
* The netCDF file that the iosp is part of
*/
protected NetcdfFile ncfile;
/**
* the RandomAccessFile we are reading from
*/
// protected RandomAccessFile raf;
/*
* place to store debug stuff
*/
//protected StringBuilder parseInfo = new StringBuilder();
/**
* Use the given index to fill the NetcdfFile object with attributes and variables.
*
* @param index GridIndex to use
* @param cancelTask cancel task
* @throws IOException problem reading the file
*/
protected abstract void open(GridIndex index, CancelTask cancelTask) throws IOException;
/**
* Open the service provider for reading.
*
* @param raf file to read from
* @param ncfile netCDF file we are writing to (memory)
* @param cancelTask task for cancelling
* @throws IOException problem reading file
*/
@Override
public void open(RandomAccessFile raf, NetcdfFile ncfile, CancelTask cancelTask) throws IOException {
this.raf = raf;
this.ncfile = ncfile;
}
/**
* Get the detail information
*
* @return the detail info
*/
@Override
public String getDetailInfo() {
return ""; // parseInfo.toString();
}
/**
* Send an IOSP message
*
* @param special isn't that special?
*/
@Override
public Object sendIospMessage(Object special) {
if (special instanceof FmrcCoordSys) {
fmrcCoordSys = (FmrcCoordSys) special;
}
return super.sendIospMessage(special);
}
/**
* Read the data for the variable
*
* @param v2 Variable to read
* @param section section infomation
* @return Array of data
* @throws IOException problem reading from file
* @throws InvalidRangeException invalid Range
*/
@Override
public Array readData(Variable v2, Section section) throws IOException, InvalidRangeException {
long start = System.currentTimeMillis();
Array dataArray = Array.factory(DataType.FLOAT, section.getShape());
GridVariable pv = (GridVariable) v2.getSPobject();
// Canonical ordering is ens, time, level, lat, lon
int rangeIdx = 0;
Range ensRange = pv.hasEnsemble() ? section.getRange(rangeIdx++) : new Range( 0, 0 );
Range timeRange = (section.getRank() > 2) ? section.getRange(rangeIdx++) : new Range( 0, 0 );
Range levRange = pv.hasVert() ? section.getRange(rangeIdx++) : new Range( 0, 0 );
Range yRange = section.getRange(rangeIdx++);
Range xRange = section.getRange(rangeIdx);
IndexIterator ii = dataArray.getIndexIterator();
// loop over ens
for (int ensIdx = ensRange.first(); ensIdx <= ensRange.last(); ensIdx += ensRange.stride()) {
//loop over time
for (int timeIdx = timeRange.first(); timeIdx <= timeRange.last(); timeIdx += timeRange.stride()) {
//loop over level
for (int levelIdx = levRange.first(); levelIdx <= levRange.last(); levelIdx += levRange.stride()) {
readXY(v2, ensIdx, timeIdx, levelIdx, yRange, xRange, ii);
}
}
}
if (debugTiming) {
long took = System.currentTimeMillis() - start;
System.out.println(" read data took=" + took + " msec ");
}
return dataArray;
}
/**
* read one YX array
*
* @param v2 variable to put the data into
* @param ensIdx ensemble index
* @param timeIdx time index
* @param levIdx level index
* @param yRange x range
* @param xRange y range
* @param ii index iterator
* @throws IOException problem reading the file
* @throws InvalidRangeException invalid range
*/
private void readXY(Variable v2, int ensIdx, int timeIdx, int levIdx, Range yRange, Range xRange, IndexIterator ii)
throws IOException, InvalidRangeException {
GridVariable pv = (GridVariable) v2.getSPobject();
GridHorizCoordSys hsys = pv.getHorizCoordSys();
int nx = hsys.getNx();
GridRecord record = pv.findRecord(ensIdx, timeIdx, levIdx);
if (record == null) {
Attribute att = v2.findAttribute("missing_value");
float missing_value = (att == null) ? -9999.0f : att.getNumericValue().floatValue();
int xyCount = yRange.length() * xRange.length();
for (int j = 0; j < xyCount; j++) {
ii.setFloatNext(missing_value);
}
return;
}
// otherwise read it
float[] data = _readData(record);
// LOOK can improve with System.copy ??
for (int y = yRange.first(); y <= yRange.last(); y += yRange.stride()) {
for (int x = xRange.first(); x <= xRange.last(); x += xRange.stride()) {
int index = y * nx + x;
ii.setFloatNext(data[index]);
}
}
}
/**
* Is this XY level missing?
*
* @param v2 Variable
* @param timeIdx time index
* @param ensIdx ensemble index
* @param levIdx level index
* @return true if missing
* @throws InvalidRangeException invalid range
*/
public boolean isMissingXY(Variable v2, int timeIdx, int ensIdx, int levIdx) throws InvalidRangeException {
GridVariable pv = (GridVariable) v2.getSPobject();
if ((timeIdx < 0) || (timeIdx >= pv.getNTimes())) {
throw new InvalidRangeException("timeIdx=" + timeIdx);
}
if ((levIdx < 0) || (levIdx >= pv.getVertNlevels())) {
throw new InvalidRangeException("levIdx=" + levIdx);
}
if ((ensIdx < 0) || (ensIdx >= pv.getNEnsembles())) {
throw new InvalidRangeException("ensIdx=" + ensIdx);
}
return (null == pv.findRecord(ensIdx, timeIdx, levIdx));
}
/*
* Ensemble information for this Variable:
* ensembles - number of ensembles
* pdn - productType of Ensemble
* ensTypes[] - type of Ensemble
*
* Both pdn and ensTypes are needed for ensemble type that is either
* perturbed or derived :
* Grib2Tables.getEnsembleType( int productType, int type)
*
*
*
* @param v2 Variable
* @return ensInfo int[]
*
public int[] ensembleInfo(Variable v2 ) {
GridVariable pv = (GridVariable) v2.getSPobject();
int ensembles = pv.getNEnsembles();
// pack ensembles, pdn, ensTypes into int array ensInfo
int[] ensInfo = new int[ ensembles + 2];
ensInfo[ 0 ] = ensembles;
ensInfo[ 1 ] = pv.getPDN();
System.arraycopy( pv.getEnsTypes(), 0, ensInfo, 2, ensembles);
return ensInfo;
} */
/**
* Read the data for this GridRecord
*
* @param gr grid identifier
* @return the data (or null)
* @throws IOException problem reading the data
*/
protected abstract float[] _readData(GridRecord gr) throws IOException;
}