public double makeOffsetFromRefDate( CalendarDate date) {
    if (isCalendarField) {
      if (date.equals(baseDate)) return 0.0;
      return date.getDifference(baseDate, periodField);
    } else {
      long msecs = date.getDifferenceInMsecs(baseDate);
      return msecs / period.getValueInMillisecs();
    }
  }

public double makeOffsetFromRefDate ( CalendarDate date ) { if ( isCalendarField ) { if ( date . equals ( baseDate ) ) return 0.0 ; return date . getDifference ( baseDate , periodField ) ; } else { long msecs = date . getDifferenceInMsecs ( baseDate ) ; return msecs / period . getValueInMillisecs ( ) ; } }


public CalendarDate makeCalendarDate(double value) {
    if (isCalendarField)
      return baseDate.add(CalendarPeriod.of( (int) value, periodField));  // LOOK int vs double
    else
      return baseDate.add( value, periodField);
  }

public
CalendarDate
makeCalendarDate
(
double
value
)
{
if
(
isCalendarField
)
return
baseDate
.
add
(
CalendarPeriod
.
of
(
(
int
)
value
,
periodField
)
)
;
// LOOK int vs double
else
return
baseDate
.
add
(
value
,
periodField
)
;
}


public QuantityDimension
    getQuantityDimension()
    {
	Factor[]	factors = getFactors();
	for (int i = factors.length; --i >= 0; )
	{
	    Factor	factor = factors[i];
	    factors[i] =
		new Factor(
		    ((BaseUnit)factor.getBase()).getBaseQuantity(),
		    factor.getExponent());
	}
	return new QuantityDimension(factors);
    }

public QuantityDimension getQuantityDimension ( ) { Factor [ ] factors = getFactors ( ) ; for ( int i = factors . length ; -- i >= 0 ; ) { Factor factor = factors [ i ] ; factors [ i ] = new Factor ( ( ( BaseUnit ) factor . getBase ( ) ) . getBaseQuantity ( ) , factor . getExponent ( ) ) ; } return new QuantityDimension ( factors ) ; }

Returns the corresponding quantity dimension.
@return			The quantity dimension corresponding to this
unit dimension.

Returns the corresponding quantity dimension .


public boolean isValidFile(ucar.unidata.io.RandomAccessFile raf) {
    try {
      raf.order(RandomAccessFile.LITTLE_ENDIAN);
      // The first struct in the file is the product_hdr, which will have the
      // standard structure_header, followed by other embedded structures.
      // Each of these structures also have a structure header. To validate
      // the file we check for a product_hdr (by looking for type 27 in the
      // structure_header), then a product_configuration structure (by looking
      // for type 26 in its structure_header), then checking that that
      // the product_configuration does indicate a type of RAW data (type 15)
      raf.seek(0);
      short[] data = new short[13];
      raf.readShort(data, 0, 13);
      return (data[0] == (short) 27 &&
              data[6] == (short) 26 &&
              data[12] ==(short) 15);
    } catch (IOException ioe) {
      System.out.println("In isValidFile(): " + ioe.toString());
      return false;
    }
  }

Check if this is a valid SIGMET-IRIS file for this IOServiceProvider.

Check if this is a valid SIGMET - IRIS file for this IOServiceProvider .


public java.util.Map<String, String> readStnNames(ucar.unidata.io.RandomAccessFile raf) {
    java.util.Map<String, String> hdrNames = new java.util.HashMap<String, String>();
    try {
      raf.seek(6288);
      String stnName = raf.readString(16);  //System.out.println(" stnName="+stnName.trim());
      raf.seek(6306);
      String stnName_util = raf.readString(16);
      hdrNames.put("StationName", stnName.trim());
      hdrNames.put("StationName_SetupUtility", stnName_util.trim());
    } catch (Exception e) {
      System.out.println(e.toString());
      e.printStackTrace();
    }
    return hdrNames;
  }


public Array readData1(ucar.nc2.Variable v2, Section section)
          throws IOException, InvalidRangeException {
    //doData(raf, ncfile, varList);
    int[] sh = section.getShape();
    Array temp = Array.factory(v2.getDataType(), sh);
    long pos0 = 0;
    // Suppose that the data has LayoutRegular
    LayoutRegular index = new LayoutRegular(pos0, v2.getElementSize(), v2.getShape(), section);
    if (v2.getShortName().startsWith("time") | v2.getShortName().startsWith("numGates")) {
      temp = readIntData(index, v2);
    } else {
      temp = readFloatData(index, v2);
    }
    return temp;
  }

Read data from a top level Variable and return a memory resident Array.

@param v2      Variable. It may have FLOAT/INTEGER data type.
@param section wanted section of data of Variable. The section list is a list
of ucar.ma2.Range which define the requested data subset.
@return Array of data which will be read from Variable through this call.

Read data from a top level Variable and return a memory resident Array .


public Array readIntData(LayoutRegular index, Variable v2) throws IOException {
    int[] var = (int[]) (v2.read().get1DJavaArray(v2.getDataType()));
    int[] data = new int[(int) index.getTotalNelems()];
    while (index.hasNext()) {
      Layout.Chunk chunk = index.next();
      System.arraycopy(var, (int) chunk.getSrcPos() / 4, data, (int) chunk.getDestElem(), chunk.getNelems());
    }
    return Array.factory(v2.getDataType(), new int[] {(int) index.getTotalNelems()}, data);
  }

public Array readIntData ( LayoutRegular index , Variable v2 ) throws IOException { int [ ] var = ( int [ ] ) ( v2 . read ( ) . get1DJavaArray ( v2 . getDataType ( ) ) ) ; int [ ] data = new int [ ( int ) index . getTotalNelems ( ) ] ; while ( index . hasNext ( ) ) { Layout . Chunk chunk = index . next ( ) ; System . arraycopy ( var , ( int ) chunk . getSrcPos ( ) / 4 , data , ( int ) chunk . getDestElem ( ) , chunk . getNelems ( ) ) ; } return Array . factory ( v2 . getDataType ( ) , new int [ ] { ( int ) index . getTotalNelems ( ) } , data ) ; }

Read data from a top level Variable of INTEGER data type and return a memory resident Array.

@param index LayoutRegular object
@param v2    Variable has INTEGER data type.
@return Array of data which will be read from Variable through this call.

Read data from a top level Variable of INTEGER data type and return a memory resident Array .


public long readToByteChannel11(ucar.nc2.Variable v2, Section section, WritableByteChannel channel)
          throws java.io.IOException, ucar.ma2.InvalidRangeException {
    Array data = readData(v2, section);
    float[] ftdata = new float[(int) data.getSize()];
    byte[] bytedata = new byte[(int) data.getSize() * 4];
    IndexIterator iter = data.getIndexIterator();
    int i = 0;
    ByteBuffer buffer = ByteBuffer.allocateDirect(bytedata.length);
    while (iter.hasNext()) {
      ftdata[i] = iter.getFloatNext();
      bytedata[i] = new Float(ftdata[i]).byteValue();
      buffer.put(bytedata[i]);
      i++;
    }
    buffer = ByteBuffer.wrap(bytedata);
    // write the bytes to the channel
    int count = channel.write(buffer);
    System.out.println("COUNT=" + count);
    // check if all bytes where written
    if (buffer.hasRemaining()) {
      // if not all bytes were written, move the unwritten bytes to the beginning and
      // set position just after the last unwritten byte
      buffer.compact();
    } else {
      buffer.clear();
    }
    return (long) count;
  }

Read data from a top level Variable and send data to a WritableByteChannel.

@param v2      Variable
@param section wanted section of data of Variable. The section list is a list
of ucar.ma2.Range which define the requested data subset.
@param channel WritableByteChannel object - channel that can write bytes.
@return the number of bytes written, possibly zero.

Read data from a top level Variable and send data to a WritableByteChannel .


static float calcElev(short angle) {
    final double maxval = 65536.0;
    double ang = (double) angle;
    if (angle < 0) ang = (~angle) + 1;
    double temp = (ang / maxval) * 360.0;
    BigDecimal bd = new BigDecimal(temp);
    BigDecimal result = bd.setScale(2, RoundingMode.HALF_DOWN);
    return result.floatValue();
  }

static float calcElev ( short angle ) { final double maxval = 65536.0 ; double ang = ( double ) angle ; if ( angle < 0 ) ang = ( ~ angle ) + 1 ; double temp = ( ang / maxval ) * 360.0 ; BigDecimal bd = new BigDecimal ( temp ) ; BigDecimal result = bd . setScale ( 2 , RoundingMode . HALF_DOWN ) ; return result . floatValue ( ) ; }

Calculate radial elevation of each ray

@param angle two bytes binary angle
@return float value of elevation in degrees with precision of two decimal


static float calcStep(float range_first, float range_last, short num_bins) {
    float step = (range_last - range_first) / (num_bins - 1);
    BigDecimal bd = new BigDecimal(step);
    BigDecimal result = bd.setScale(2, RoundingMode.HALF_DOWN);
    return result.floatValue();
  }

static float calcStep ( float range_first , float range_last , short num_bins ) { float step = ( range_last - range_first ) / ( num_bins - 1 ) ; BigDecimal bd = new BigDecimal ( step ) ; BigDecimal result = bd . setScale ( 2 , RoundingMode . HALF_DOWN ) ; return result . floatValue ( ) ; }

Calculate distance between sequential bins in a ray

@param range_first range of first bin in centimeters
@param range_last  range of last bin in centimeters
@param num_bins    number of bins
@return float distance in centimeters with precision of two decimal


static float calcAz(short az0, short az1) {
    // output in deg
    float azim0 = calcAngle(az0);
    float azim1 = calcAngle(az1);
    float d = 0.0f;
    d = Math.abs(azim0 - azim1);
    if ((az0 < 0) & (az1 > 0)) {
      d = Math.abs(360.0f - azim0) + Math.abs(azim1);
    }
    double temp = azim0 + d * 0.5;
    if (temp > 360.0) {
      temp -= 360.0;
    }
    BigDecimal bd = new BigDecimal(temp);
    BigDecimal result = bd.setScale(2, RoundingMode.HALF_DOWN);
    return result.floatValue();
  }

static
float
calcAz
(
short
az0
,
short
az1
)
{
// output in deg
float
azim0
=
calcAngle
(
az0
)
;
float
azim1
=
calcAngle
(
az1
)
;
float
d
=
0.0f
;
d
=
Math
.
abs
(
azim0
azim1
)
;
if
(
(
az0
<
0
)
&
(
az1
>
0
)
)
{
d
=
Math
.
abs
(
360.0f
azim0
)
+
Math
.
abs
(
azim1
)
;
}
double
temp
=
azim0
+
d
*
0.5
;
if
(
temp
>
360.0
)
{
temp
-=
360.0
;
}
BigDecimal
bd
=
new
BigDecimal
(
temp
)
;
BigDecimal
result
=
bd
.
setScale
(
2
,
RoundingMode
.
HALF_DOWN
)
;
return
result
.
floatValue
(
)
;
}

Calculate azimuth of a ray

@param az0 azimuth at beginning of ray (binary angle)
@param az1 azimuth at end of ray (binary angle)
@return float azimuth in degrees with precision of two decimal


static float calcData(Map<String, Number> recHdr, short dty, byte data) {
    short[] coef = {1, 2, 3, 4}; // MultiPRF modes
    short multiprf = recHdr.get("multiprf").shortValue();
    float vNyq = recHdr.get("vNyq").floatValue();
    double temp = -999.99;
    switch (dty) {
      default:        // dty=1,2 -total_power, reflectivity (dBZ)
        if (data != 0) {
          temp = (((int) data & 0xFF) - 64) * 0.5;
        }
        break;
      case 3:        // dty=3 - mean velocity (m/sec)
        if (data != 0) {
          temp = ((((int) data & 0xFF) - 128) / 127.0) * vNyq * coef[multiprf];
        }
        break;
      case 4:        // dty=4 - spectrum width (m/sec)
        if (data != 0) {
          double v = ((((int) data & 0xFF) - 128) / 127.0) * vNyq * coef[multiprf];
          temp = (((int) data & 0xFF) / 256.0) * v;
        }
        break;
      case 5:        // dty=5 - differential reflectivity (dB)
        if (data != 0) {
          temp = ((((int) data & 0xFF) - 128) / 16.0);
        }
        break;
    }
    BigDecimal bd = new BigDecimal(temp);
    BigDecimal result = bd.setScale(2, RoundingMode.HALF_DOWN);
    return result.floatValue();
  }

Calculate data values from raw ingest data

@param recHdr java.util.Map object with values for calculation
@param dty    type of data ( "Total_Power", "Reflectivity", "Velocity",
"Width", "Differential_Reflectivity")
@param data   1-byte input value
@return float value with precision of two decimal


static float calcNyquist(int prf, int wave) {
    double tmp = (prf * wave * 0.01) * 0.25;
    tmp = tmp * 0.01;                    //Make it m/sec
    BigDecimal bd = new BigDecimal(tmp);
    BigDecimal result = bd.setScale(2, RoundingMode.HALF_DOWN);
    return result.floatValue();
  }

static
float
calcNyquist
(
int
prf
,
int
wave
)
{
double
tmp
=
(
prf
*
wave
*
0.01
)
*
0.25
;
tmp
=
tmp
*
0.01
;
//Make it m/sec
BigDecimal
bd
=
new
BigDecimal
(
tmp
)
;
BigDecimal
result
=
bd
.
setScale
(
2
,
RoundingMode
.
HALF_DOWN
)
;
return
result
.
floatValue
(
)
;
}

Calculate of Nyquist velocity

@param prf  PRF in Hertz
@param wave wavelength in 1/100 of centimeters
@return float value of Nyquist velocity in m/sec with precision of two decimal


public VerticalTransform makeVerticalTransform(NetcdfDataset ds, Dimension timeDim) {
    return builder.makeMathTransform(ds, timeDim, this);
  }

public VerticalTransform makeVerticalTransform ( NetcdfDataset ds , Dimension timeDim ) { return builder . makeMathTransform ( ds , timeDim , this ) ; }

Use the builder to make the Vertical Transform function

@param ds      containing dataset
@param timeDim time Dimension
@return VerticalTransform
@see VertTransformBuilderIF#makeMathTransform


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");
  }

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" ) ; }

Set the debug flags

@param debugFlag debug flags


static public void setExtendIndex(boolean b) {
    indexFileModeOnOpen = b ? IndexExtendMode.extendwrite : IndexExtendMode.readonly;
    indexFileModeOnSync = b ? IndexExtendMode.extendwrite : IndexExtendMode.readonly;
  }

static public void setExtendIndex ( boolean b ) { indexFileModeOnOpen = b ? IndexExtendMode . extendwrite : IndexExtendMode . readonly ; indexFileModeOnSync = b ? IndexExtendMode . extendwrite : IndexExtendMode . readonly ; }

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


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);
    if (data == null) {
      _readData(record); // debug
      return;
    }

    // LOOK can improve with System.copy ??
    for (int y : yRange) {
      for (int x : xRange) {
        int index = y * nx + x;
        ii.setFloatNext(data[index]);
      }
    }
  }

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
==
)
{
Attribute
att
=
v2
.
findAttribute
(
"missing_value"
)
;
float
missing_value
=
(
att
==
)
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
)
;
if
(
data
==
)
{
_readData
(
record
)
;
// debug
return
;
}
// LOOK can improve with System.copy ??
for
(
int
y
:
yRange
)
{
for
(
int
x
:
xRange
)
{
int
index
=
y
*
nx
+
x
;
ii
.
setFloatNext
(
data
[
index
]
)
;
}
}
}

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


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));
  }

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 ) ) ; }

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 LatLonRect getLatLonBoundingBox() {

    if (llbb == null) {

      if ((getXHorizAxis() instanceof CoordinateAxis2D) && (getYHorizAxis() instanceof CoordinateAxis2D)) {
        return null;
      }

      CoordinateAxis horizXaxis = getXHorizAxis();
      CoordinateAxis horizYaxis = getYHorizAxis();
      if (isLatLon()) {
        double startLat = horizYaxis.getMinValue();
        double startLon = horizXaxis.getMinValue();

        double deltaLat = horizYaxis.getMaxValue() - startLat;
        double deltaLon = horizXaxis.getMaxValue() - startLon;

        LatLonPoint llpt = new LatLonPointImpl(startLat, startLon);
        llbb = new LatLonRect(llpt, deltaLat, deltaLon);

      } else {
        ProjectionImpl dataProjection = getProjection();
        ProjectionRect bb = getBoundingBox();
        if (bb != null)
          llbb = dataProjection.projToLatLonBB(bb);
      }
    }

    return llbb;

      /*  // look at all 4 corners of the bounding box
        LatLonPointImpl llpt = (LatLonPointImpl) dataProjection.projToLatLon(bb.getLowerLeftPoint(), new LatLonPointImpl());
        LatLonPointImpl lrpt = (LatLonPointImpl) dataProjection.projToLatLon(bb.getLowerRightPoint(), new LatLonPointImpl());
        LatLonPointImpl urpt = (LatLonPointImpl) dataProjection.projToLatLon(bb.getUpperRightPoint(), new LatLonPointImpl());
        LatLonPointImpl ulpt = (LatLonPointImpl) dataProjection.projToLatLon(bb.getUpperLeftPoint(), new LatLonPointImpl());

        // Check if grid contains poles.
        boolean includesNorthPole = false;
        int[] resultNP;
        resultNP = findXYindexFromLatLon(90.0, 0, null);
        if (resultNP[0] != -1 && resultNP[1] != -1)
          includesNorthPole = true;
        boolean includesSouthPole = false;
        int[] resultSP;
        resultSP = findXYindexFromLatLon(-90.0, 0, null);
        if (resultSP[0] != -1 && resultSP[1] != -1)
          includesSouthPole = true;

        if (includesNorthPole && !includesSouthPole) {
          llbb = new LatLonRect(llpt, new LatLonPointImpl(90.0, 0.0)); // ??? lon=???
          llbb.extend(lrpt);
          llbb.extend(urpt);
          llbb.extend(ulpt);
          // OR
          //llbb.extend( new LatLonRect( llpt, lrpt ));
          //llbb.extend( new LatLonRect( lrpt, urpt ) );
          //llbb.extend( new LatLonRect( urpt, ulpt ) );
          //llbb.extend( new LatLonRect( ulpt, llpt ) );
        } else if (includesSouthPole && !includesNorthPole) {
          llbb = new LatLonRect(llpt, new LatLonPointImpl(-90.0, -180.0)); // ??? lon=???
          llbb.extend(lrpt);
          llbb.extend(urpt);
          llbb.extend(ulpt);
        } else {
          double latMin = Math.min(llpt.getLatitude(), lrpt.getLatitude());
          double latMax = Math.max(ulpt.getLatitude(), urpt.getLatitude());

          // longitude is a bit tricky as usual
          double lonMin = getMinOrMaxLon(llpt.getLongitude(), ulpt.getLongitude(), true);
          double lonMax = getMinOrMaxLon(lrpt.getLongitude(), urpt.getLongitude(), false);

          llpt.set(latMin, lonMin);
          urpt.set(latMax, lonMax);

          llbb = new LatLonRect(llpt, urpt);
        }
      }
    }  */

  }

Get horizontal bounding box in lat, lon coordinates.

@return lat, lon bounding box.

Get horizontal bounding box in lat lon coordinates .


public void finish() {
    sequenceOffset = new int[nelems];

    total = 0;
    for (int i=0; i<nelems; i++) {
      sequenceOffset[i] = total;
      total += sequenceLen[i];
    }

    sdata = new StructureData[nelems];
    for (int i=0; i<nelems; i++)
      sdata[i] = new StructureDataA( this, sequenceOffset[i]);

    // make the member arrays
    for (StructureMembers.Member m : members.getMembers()) {
      int[] mShape = m.getShape();
      int[] shape = new int[mShape.length + 1];
      shape[0] = total;
      System.arraycopy(mShape, 0, shape, 1, mShape.length);

      // LOOK not doing nested structures
      Array data = Array.factory(m.getDataType(), shape);
      m.setDataArray(data);
    }
  }

public
void
finish
(
)
{
sequenceOffset
=
new
int
[
nelems
]
;
total
=
0
;
for
(
int
i
=
0
;
i
<
nelems
;
i
++
)
{
sequenceOffset
[
i
]
=
total
;
total
+=
sequenceLen
[
i
]
;
}
sdata
=
new
StructureData
[
nelems
]
;
for
(
int
i
=
0
;
i
<
nelems
;
i
++
)
sdata
[
i
]
=
new
StructureDataA
(
this
,
sequenceOffset
[
i
]
)
;
// make the member arrays
for
(
StructureMembers
.
Member
m
:
members
.
getMembers
(
)
)
{
int
[
]
mShape
=
m
.
getShape
(
)
;
int
[
]
shape
=
new
int
[
mShape
.
length
+
1
]
;
shape
[
0
]
=
total
;
System
.
arraycopy
(
mShape
,
0
,
shape
,
1
,
mShape
.
length
)
;
// LOOK not doing nested structures
Array
data
=
Array
.
factory
(
m
.
getDataType
(
)
,
shape
)
;
m
.
setDataArray
(
data
)
;
}
}

Call this when you have set all the sequence lengths.

Call this when you have set all the sequence lengths .


public static void tab(StringBuffer sbuff, int tabStop, boolean alwaysOne) {
    int len = sbuff.length();
    if (tabStop > len) {
      sbuff.setLength(tabStop);
      for (int i = len; i < tabStop; i++) {
        sbuff.setCharAt(i, ' ');
      }
    } else if (alwaysOne) {
      sbuff.setLength(len + 1);
      sbuff.setCharAt(len, ' ');
    }
  }

public static void tab ( StringBuffer sbuff , int tabStop , boolean alwaysOne ) { int len = sbuff . length ( ) ; if ( tabStop > len ) { sbuff . setLength ( tabStop ) ; for ( int i = len ; i < tabStop ; i ++ ) { sbuff . setCharAt ( i , ' ' ) ; } } else if ( alwaysOne ) { sbuff . setLength ( len + 1 ) ; sbuff . setCharAt ( len , ' ' ) ; } }

Blank fill sbuff with blanks, until position tabStop.

@param sbuff     StringBuffer to manipulate
@param tabStop   pad out to here
@param alwaysOne true if you want to guarentee at least one space.

Blank fill sbuff with blanks until position tabStop .


public static String pad(String s, int width, boolean rightJustify) {
    if (s.length() >= width) {
      return s;
    }
    StringBuilder sbuff = new StringBuilder(width);
    int need = width - s.length();
    sbuff.setLength(need);
    for (int i = 0; i < need; i++) {
      sbuff.setCharAt(i, ' ');
    }

    if (rightJustify) {
      sbuff.append(s);
    } else {
      sbuff.insert(0, s);
    }

    return sbuff.toString();
  }

public static String pad ( String s , int width , boolean rightJustify ) { if ( s . length ( ) >= width ) { return s ; } StringBuilder sbuff = new StringBuilder ( width ) ; int need = width - s . length ( ) ; sbuff . setLength ( need ) ; for ( int i = 0 ; i < need ; i ++ ) { sbuff . setCharAt ( i , ' ' ) ; } if ( rightJustify ) { sbuff . append ( s ) ; } else { sbuff . insert ( 0 , s ) ; } return sbuff . toString ( ) ; }

Create a new string by padding the existing one with blanks to specified width.
Do nothing if length is already greater or equal to width.

@param s            string to pad
@param width        length of return string
@param rightJustify if true, right justify, else left justify
@return padded string

Create a new string by padding the existing one with blanks to specified width . Do nothing if length is already greater or equal to width .


public static String i(int v, int width) {
    return pad(Integer.toString(v), width, true);
  }

public static String i ( int v , int width ) { return pad ( Integer . toString ( v ) , width , true ) ; }

Format an integer value.

@param v     : value
@param width pad to this width
@return formatted string


public static String l(long v, int width) {
    return pad(Long.toString(v), width, true);
  }

public static String l ( long v , int width ) { return pad ( Long . toString ( v ) , width , true ) ; }

Format a long value.

@param v     : value
@param width pad to this width
@return formatted string


public static String formatByteSize(double size) {
    String unit = null;
    if (size > 1.0e15) {
      unit = "Pbytes";
      size *= 1.0e-15;
    } else if (size > 1.0e12) {
      unit = "Tbytes";
      size *= 1.0e-12;
    } else if (size > 1.0e9) {
      unit = "Gbytes";
      size *= 1.0e-9;
    } else if (size > 1.0e6) {
      unit = "Mbytes";
      size *= 1.0e-6;
    } else if (size > 1.0e3) {
      unit = "Kbytes";
      size *= 1.0e-3;
    } else {
      unit = "bytes";
    }

    return Format.d(size, 4) + " " + unit;
  }

public
static
String
formatByteSize
(
double
size
)
{
String
unit
=
;
if
(
size
>
1.0e15
)
{
unit
=
"Pbytes"
;
size
*=
1.0e-15
;
}
else
if
(
size
>
1.0e12
)
{
unit
=
"Tbytes"
;
size
*=
1.0e-12
;
}
else
if
(
size
>
1.0e9
)
{
unit
=
"Gbytes"
;
size
*=
1.0e-9
;
}
else
if
(
size
>
1.0e6
)
{
unit
=
"Mbytes"
;
size
*=
1.0e-6
;
}
else
if
(
size
>
1.0e3
)
{
unit
=
"Kbytes"
;
size
*=
1.0e-3
;
}
else
{
unit
=
"bytes"
;
}
return
Format
.
d
(
size
,
4
)
+
" "
+
unit
;
}

Nicely formatted representation of bytes, eg turn 5.636E7 into

@param size the size in bytes
@return formatted string

Nicely formatted representation of bytes eg turn 5 . 636E7 into


private static void show(double d, int sigfig) {
    System.out.println("Format.d(" + d + "," + sigfig + ") == "
            + Format.d(d, sigfig));
  }

private
static
void
show
(
double
d
,
int
sigfig
)
{
System
.
out
.
println
(
"Format.d("
+
d
+
","
+
sigfig
+
") == "
+
Format
.
d
(
d
,
sigfig
)
)
;
}

Show the value of a double to the significant figures

@param d      double value
@param sigfig number of significant figures


private static void show2(double d, int dec_places) {
    System.out.println("Format.dfrac(" + d + "," + dec_places + ") == "
            + Format.dfrac(d, dec_places));
  }

private
static
void
show2
(
double
d
,
int
dec_places
)
{
System
.
out
.
println
(
"Format.dfrac("
+
d
+
","
+
dec_places
+
") == "
+
Format
.
dfrac
(
d
,
dec_places
)
)
;
}

Show the value of a double with specified number of decimal places

@param d          double value
@param dec_places number of decimal places


private String getShapeString(int[] shape) {
		StringBuilder buf = new StringBuilder();
		for (int i = 0; i < shape.length; i++) {
			if (i != 0) buf.append(" ");
			buf.append(shape[i]);
		}
		return buf.toString();
	}

private
String
getShapeString
(
int
[
]
shape
)
{
StringBuilder
buf
=
new
StringBuilder
(
)
;
for
(
int
i
=
0
;
i
<
shape
.
length
;
i
++
)
{
if
(
i
!=
0
)
buf
.
append
(
" "
)
;
buf
.
append
(
shape
[
i
]
)
;
}
return
buf
.
toString
(
)
;
}


public Slice
    finish()
            throws DapException
    {
        // Attempt to repair undefined values
        if(this.first == UNDEFINED) this.first = 0;   // default
        if(this.stride == UNDEFINED) this.stride = 1; // default
        if(this.stop == UNDEFINED && this.maxsize != UNDEFINED)
            this.stop = this.maxsize;
        if(this.stop == UNDEFINED && this.maxsize == UNDEFINED)
            this.stop = this.first + 1;
        if(this.maxsize == UNDEFINED && this.stop != UNDEFINED)
            this.maxsize = this.stop;
        // else (this.stop != UNDEFINED && this.maxsize != UNDEFINED)
        assert (this.first != UNDEFINED);
        assert (this.stride != UNDEFINED);
        assert (this.stop != UNDEFINED);
        // sanity checks
        if(this.first > this.maxsize)
            throw new DapException("Slice: first index > max size");
        if(this.stop > (this.maxsize+1))
            throw new DapException("Slice: stop > max size");
        if(this.first < 0)
            throw new DapException("Slice: first index < 0");
        if(this.stop < 0)
            throw new DapException("Slice: stop index < 0");
        if(this.stride <= 0)
            throw new DapException("Slice: stride index <= 0");
        if(this.first > this.stop)
            throw new DapException("Slice: first index > last");
        return this; // fluent interface
    }

Perform sanity checks on a slice and repair where possible.

@return this (fluent interface)
@throws dap4.core.util.DapException if slice is malformed

Perform sanity checks on a slice and repair where possible .


public String toConstraintString()
            throws DapException
    {
        assert this.first != UNDEFINED && this.stride != UNDEFINED && this.stop != UNDEFINED;
        if((this.stop - this.first) == 0) {
            return String.format("[0]");
        } else if(this.stride == 1) {
            if((this.stop - this.first) == 1)
                return String.format("[%d]", this.first);
            else
                return String.format("[%d:%d]", this.first, this.stop - 1);
        } else
            return String.format("[%d:%d:%d]", this.first, this.stride, this.stop - 1);
    }

public String toConstraintString ( ) throws DapException { assert this . first != UNDEFINED && this . stride != UNDEFINED && this . stop != UNDEFINED ; if ( ( this . stop - this . first ) == 0 ) { return String . format ( "[0]" ) ; } else if ( this . stride == 1 ) { if ( ( this . stop - this . first ) == 1 ) return String . format ( "[%d]" , this . first ) ; else return String . format ( "[%d:%d]" , this . first , this . stop - 1 ) ; } else return String . format ( "[%d:%d:%d]" , this . first , this . stride , this . stop - 1 ) ; }

Convert this slice to a string
suitable for use in a constraint

@return constraint usable string
@throws DapException


static public Slice
    compose(Slice target, Slice src)
            throws DapException
    {
        long sr_stride = target.getStride() * src.getStride();
        long sr_first = MAP(target, src.getFirst());
        long lastx = MAP(target, src.getLast());
        long sr_last = (target.getLast() < lastx ? target.getLast() : lastx); //min(last(),lastx)
        return new Slice(sr_first, sr_last + 1, sr_stride, sr_last + 1).finish();
    }

static public Slice compose ( Slice target , Slice src ) throws DapException { long sr_stride = target . getStride ( ) * src . getStride ( ) ; long sr_first = MAP ( target , src . getFirst ( ) ) ; long lastx = MAP ( target , src . getLast ( ) ) ; long sr_last = ( target . getLast ( ) < lastx ? target . getLast ( ) : lastx ) ; //min(last(),lastx) return new Slice ( sr_first , sr_last + 1 , sr_stride , sr_last + 1 ) . finish ( ) ; }

Take two slices and compose src wrt target
Assume neither argument is null. This code
should match ucar.ma2.Section in thredds
and dceconstraint.c in the netcdf-c library.

@param target
@param src
@return new, composed Range
@throws DapException

Take two slices and compose src wrt target Assume neither argument is null . This code should match ucar . ma2 . Section in thredds and dceconstraint . c in the netcdf - c library .


static long
    MAP(Slice target, long i)
            throws DapException
    {
        if(i < 0)
            throw new DapException("Slice.compose: i must be >= 0");
        if(i > target.getStop())
            throw new DapException("i must be < stop");
        return target.getFirst() + i * target.getStride();
    }

static
long
MAP
(
Slice
target
,
long
i
)
throws
DapException
{
if
(
i
<
0
)
throw
new
DapException
(
"Slice.compose: i must be >= 0"
)
;
if
(
i
>
target
.
getStop
(
)
)
throw
new
DapException
(
"i must be < stop"
)
;
return
target
.
getFirst
(
)
+
i
*
target
.
getStride
(
)
;
}

Map ith element of one range wrt a target range

@param target the target range
@param i      index of the element
@return the i-th element of a range
@throws DapException if index is invalid


static public void
    dumpbytes(ByteBuffer buf0, boolean skipdmr)
    {
        int savepos = buf0.position();
        int limit0 = buf0.limit();
        int skipcount = 0;
        if(limit0 > MAXLIMIT) limit0 = MAXLIMIT;
        if(limit0 >= buf0.limit()) limit0 = buf0.limit();
        if(skipdmr) {
            ByteOrder saveorder = buf0.order();
            buf0.order(ByteOrder.BIG_ENDIAN); // must read in network order
            skipcount = buf0.getInt(); //dmr count
            buf0.order(saveorder);
            skipcount &= 0xFFFFFF; // mask off the flags to get true count
            skipcount += 4; // skip the count also
        }
        byte[] bytes = new byte[(limit0 + 8) - skipcount];
        Arrays.fill(bytes, (byte) 0);
        buf0.position(savepos + skipcount);
        buf0.get(bytes, 0, limit0 - skipcount);
        buf0.position(savepos);

        System.err.println("order="+buf0.order());

        ByteBuffer buf = ByteBuffer.wrap(bytes).order(buf0.order());
        dumpbytes(buf);
    }

static
public
void
dumpbytes
(
ByteBuffer
buf0
,
boolean
skipdmr
)
{
int
savepos
=
buf0
.
position
(
)
;
int
limit0
=
buf0
.
limit
(
)
;
int
skipcount
=
0
;
if
(
limit0
>
MAXLIMIT
)
limit0
=
MAXLIMIT
;
if
(
limit0
>=
buf0
.
limit
(
)
)
limit0
=
buf0
.
limit
(
)
;
if
(
skipdmr
)
{
ByteOrder
saveorder
=
buf0
.
order
(
)
;
buf0
.
order
(
ByteOrder
.
BIG_ENDIAN
)
;
// must read in network order
skipcount
=
buf0
.
getInt
(
)
;
//dmr count
buf0
.
order
(
saveorder
)
;
skipcount
&=
0xFFFFFF
;
// mask off the flags to get true count
skipcount
+=
4
;
// skip the count also
}
byte
[
]
bytes
=
new
byte
[
(
limit0
+
8
)
skipcount
]
;
Arrays
.
fill
(
bytes
,
(
byte
)
0
)
;
buf0
.
position
(
savepos
+
skipcount
)
;
buf0
.
get
(
bytes
,
0
,
limit0
skipcount
)
;
buf0
.
position
(
savepos
)
;
System
.
err
.
println
(
"order="
+
buf0
.
order
(
)
)
;
ByteBuffer
buf
=
ByteBuffer
.
wrap
(
bytes
)
.
order
(
buf0
.
order
(
)
)
;
dumpbytes
(
buf
)
;
}


static public void
    dumpbytes(ByteBuffer buf0)
    {
        int stop = buf0.limit();
        int size = stop + 8;
        int savepos = buf0.position();
        assert savepos == 0;
        byte[] bytes = new byte[size];
        Arrays.fill(bytes, (byte) 0);
        buf0.get(bytes,0,stop);
        buf0.position(savepos);
        ByteBuffer buf = ByteBuffer.wrap(bytes).order(buf0.order());
        buf.position(0);
        buf.limit(size);
        int i = 0;
        try {
            for(i = 0; buf.position() < stop; i++) {
                savepos = buf.position();
                int iv = buf.getInt();
                buf.position(savepos);
                long lv = buf.getLong();
                buf.position(savepos);
                short sv = buf.getShort();
                buf.position(savepos);
                byte b = buf.get();
                int ub = ((int)b) & 0x000000FF;
                long uiv = ((long) iv) & 0xFFFFFFFFL;
                int usv = ((int) sv) & 0xFFFF;
                int ib = (int) b;
                char c = (char) ub;
                String s = Character.toString(c);
                if(c == '\r') s = "\\r";
                else if(c == '\n') s = "\\n";
                else if(c < ' ' || c >= 0x7f) s = "?";
                System.err.printf("[%03d] %02x %03d %4d '%s'", i, ub, ub, ib, s);
                System.err.printf("\t%12d 0x%08x", iv, uiv);
                System.err.printf("\t%5d\t0x%04x", sv, usv);
                System.err.println();
                System.err.flush();
            }

        } catch (Exception e) {
            System.err.println("failure:" + e);
        } finally {
            System.err.flush();
            //new Exception().printStackTrace(System.err);
	        System.err.flush();
        }
    }

Dump the contents of a buffer from 0 to position

@param buf0 byte buffer to dump


public static EarthEllipsoid getType(String name) {
    if (name == null)
      return null;
    return hash.get(name);
  }

public static EarthEllipsoid getType ( String name ) { if ( name == null ) return null ; return hash . get ( name ) ; }

Find the EarthEllipsoid that matches this name.

@param name : name to match
@return EarthEllipsoid or null if no match.

Find the EarthEllipsoid that matches this name .


public static EarthEllipsoid getType(int epsgId) {
    Collection<EarthEllipsoid> all = getAll();
    for (EarthEllipsoid ellipsoid : all) {
      if (ellipsoid.epsgId == epsgId) {
        return ellipsoid;
      }
    }
    return null;
  }

public static EarthEllipsoid getType ( int epsgId ) { Collection < EarthEllipsoid > all = getAll ( ) ; for ( EarthEllipsoid ellipsoid : all ) { if ( ellipsoid . epsgId == epsgId ) { return ellipsoid ; } } return null ; }

Find the EarthEllipsoid that matches this EPSG Id.

@param epsgId : epsg Id to match
@return EarthEllipsoid or null if no match.

Find the EarthEllipsoid that matches this EPSG Id .


public Array decodeVlenData(NcStreamProto.DataCol dproto) throws IOException {
    DataType dataType = NcStream.convertDataType(dproto.getDataType());
    ByteBuffer bb = dproto.getPrimdata().asReadOnlyByteBuffer();
    ByteOrder bo = dproto.getBigend() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
    bb.order(bo);
    Array alldata = Array.factory(dataType, new int[]{dproto.getNelems()}, bb); // flat array
    IndexIterator all = alldata.getIndexIterator();

    Section section = NcStream.decodeSection(dproto.getSection());
    Array[] data = new Array[(int) section.computeSize()];

    // divide the primitive data into variable length arrays
    int count = 0;
    for (int len : dproto.getVlensList()) {
      Array primdata = Array.factory(dataType, new int[]{len});
      IndexIterator prim = primdata.getIndexIterator();
      for (int i=0; i<len; i++) {
        prim.setObjectNext( all.getObjectNext()); // generic
      }
      data[count++] = primdata;
    }

    // return Array.makeObjectArray(dataType, data[0].getClass(), section.getShape(), data);
    return Array.makeVlenArray(section.getShape(), data);
  }


private Array decodeVlenData(NcStreamProto.DataCol dproto, Section parentSection) throws IOException {
    DataType dataType = NcStream.convertDataType(dproto.getDataType());
    ByteBuffer bb = dproto.getPrimdata().asReadOnlyByteBuffer();
    ByteOrder bo = dproto.getBigend() ? ByteOrder.BIG_ENDIAN : ByteOrder.LITTLE_ENDIAN;
    bb.order(bo);
    Array alldata = Array.factory(dataType, new int[]{dproto.getNelems()}, bb); // 1D array
    IndexIterator all = alldata.getIndexIterator();

    int psize = (int) parentSection.computeSize();

    Section section = NcStream.decodeSection(dproto.getSection());
    Section vsection = section.removeFirst(parentSection);
    int vsectionSize = (int) vsection.computeSize(); // the # of varlen Arrays at the inner structure
    // LOOK check for scalar

    // divide the primitive data into variable length arrays
    int countInner = 0;
    Array[] pdata = new Array[psize];
    for (int pCount=0; pCount<psize; pCount++) {
      Array[] vdata = new Array[vsectionSize];
      for (int vCount=0; vCount<vsectionSize; vCount++) {
        int vlen = dproto.getVlens(countInner++);
        Array primdata = Array.factory(dataType, new int[]{vlen});
        IndexIterator prim = primdata.getIndexIterator();
        for (int i = 0; i < vlen; i++) {
          prim.setObjectNext(all.getObjectNext()); // generic
        }
        vdata[vCount] = primdata;
      }
      pdata[pCount] = Array.makeVlenArray(vsection.getShape(), vdata);
    }

    // ArrayObject(parentShape)
    return Array.makeVlenArray(parentSection.getShape(), pdata);
  }


public ArrayList extract(String url) throws IOException {
    if (debug) System.out.println(" URLextract=" + url);

    baseURL = new URL(url);
    InputStream in = baseURL.openStream();
    InputStreamReader r = new InputStreamReader(filterTag(in), CDM.UTF8);
    HTMLEditorKit.ParserCallback callback = new CallerBacker();

    urlList = new ArrayList();
    wantURLS = true;
    wantText = false;
    parser.parse(r, callback, false);

    return urlList;
  }

public
ArrayList
extract
(
String
url
)
throws
IOException
{
if
(
debug
)
System
.
out
.
println
(
" URLextract="
+
url
)
;
baseURL
=
new
URL
(
url
)
;
InputStream
in
=
baseURL
.
openStream
(
)
;
InputStreamReader
r
=
new
InputStreamReader
(
filterTag
(
in
)
,
CDM
.
UTF8
)
;
HTMLEditorKit
.
ParserCallback
callback
=
new
CallerBacker
(
)
;
urlList
=
new
ArrayList
(
)
;
wantURLS
=
true
;
wantText
=
false
;
parser
.
parse
(
r
,
callback
,
false
)
;
return
urlList
;
}

Extract all A-HREF contained URLS from the given URL and return in List

Extract all A - HREF contained URLS from the given URL and return in List


public String getTextContent(String url) throws IOException {
    if (debug) System.out.println(" URL.getTextContent=" + url);

    baseURL = new URL(url);
    InputStream in = baseURL.openStream();
    InputStreamReader r = new InputStreamReader(filterTag(in), CDM.UTF8);
    HTMLEditorKit.ParserCallback callback = new CallerBacker();

    textBuffer = new StringBuffer(3000);
    wantURLS = false;
    wantText = true;
    parser.parse(r, callback, false);

    return textBuffer.toString();
  }

public
String
getTextContent
(
String
url
)
throws
IOException
{
if
(
debug
)
System
.
out
.
println
(
" URL.getTextContent="
+
url
)
;
baseURL
=
new
URL
(
url
)
;
InputStream
in
=
baseURL
.
openStream
(
)
;
InputStreamReader
r
=
new
InputStreamReader
(
filterTag
(
in
)
,
CDM
.
UTF8
)
;
HTMLEditorKit
.
ParserCallback
callback
=
new
CallerBacker
(
)
;
textBuffer
=
new
StringBuffer
(
3000
)
;
wantURLS
=
false
;
wantText
=
true
;
parser
.
parse
(
r
,
callback
,
false
)
;
return
textBuffer
.
toString
(
)
;
}

Extract text content from the given URL and return in String


private InputStream filterTag(InputStream in) throws IOException {
    BufferedReader buffIn = new BufferedReader(new InputStreamReader(in, CDM.UTF8));
    ByteArrayOutputStream bos = new ByteArrayOutputStream(10000);

    String line = buffIn.readLine();
    while (line != null) {
      String lline = line.toLowerCase();
      if (lline.contains("<meta "))  // skip meta tags
        continue;
      //System.out.println("--"+line);
      bos.write(line.getBytes(CDM.utf8Charset));
      line = buffIn.readLine();
    }
    buffIn.close();

    return new ByteArrayInputStream(bos.toByteArray());
  }

private
InputStream
filterTag
(
InputStream
in
)
throws
IOException
{
BufferedReader
buffIn
=
new
BufferedReader
(
new
InputStreamReader
(
in
,
CDM
.
UTF8
)
)
;
ByteArrayOutputStream
bos
=
new
ByteArrayOutputStream
(
10000
)
;
String
line
=
buffIn
.
readLine
(
)
;
while
(
line
!=
)
{
String
lline
=
line
.
toLowerCase
(
)
;
if
(
lline
.
contains
(
"<meta "
)
)
// skip meta tags
continue
;
//System.out.println("--"+line);
bos
.
write
(
line
.
getBytes
(
CDM
.
utf8Charset
)
)
;
line
=
buffIn
.
readLine
(
)
;
}
buffIn
.
close
(
)
;
return
new
ByteArrayInputStream
(
bos
.
toByteArray
(
)
)
;
}

workaround for HTMLEditorKit.Parser, cant deal with "content-encoding"

workaround for HTMLEditorKit . Parser cant deal with content - encoding


@Override
  public long readToByteChannel(ucar.nc2.Variable v2, Section section, WritableByteChannel channel)
          throws java.io.IOException, ucar.ma2.InvalidRangeException {

    Array data = readData(v2, section);
    return IospHelper.copyToByteChannel(data,  channel);
  }

@ Override public long readToByteChannel ( ucar . nc2 . Variable v2 , Section section , WritableByteChannel channel ) throws java . io . IOException , ucar . ma2 . InvalidRangeException { Array data = readData ( v2 , section ) ; return IospHelper . copyToByteChannel ( data , channel ) ; }


@Override
  public void addAll(Iterable<Attribute> atts) {
    for (Attribute att : atts) addAttribute(att);
  }

@ Override public void addAll ( Iterable < Attribute > atts ) { for ( Attribute att : atts ) addAttribute ( att ) ; }


@Override
  public boolean removeAttribute(String attName) {
    Attribute att = findAttribute(attName);
    return att != null && atts.remove(att);
  }

@ Override public boolean removeAttribute ( String attName ) { Attribute att = findAttribute ( attName ) ; return att != null && atts . remove ( att ) ; }

Remove an Attribute by name.

@param attName if exists, remove this attribute
@return true if was found and removed


@Override
  public boolean removeAttributeIgnoreCase(String attName) {
    Attribute att = findAttributeIgnoreCase(attName);
    return att != null && atts.remove(att);
  }

@ Override public boolean removeAttributeIgnoreCase ( String attName ) { Attribute att = findAttributeIgnoreCase ( attName ) ; return att != null && atts . remove ( att ) ; }

Remove an Attribute by name, ignoring case

@param attName if exists, remove this attribute
@return true if was found and removed

Remove an Attribute by name ignoring case


public List<Double> getOffsetsInTimeUnits() {
    double start = firstDate.getMillis();

    List<Double> result = new ArrayList<>(runtimes.length);
    for (int idx=0; idx<runtimes.length; idx++) {
      double runtime = (double) getRuntime(idx);
      double msecs = (runtime - start);
      result.add(msecs / timeUnit.getValueInMillisecs());
    }
    return result;
  }

public List < Double > getOffsetsInTimeUnits ( ) { double start = firstDate . getMillis ( ) ; List < Double > result = new ArrayList <> ( runtimes . length ) ; for ( int idx = 0 ; idx < runtimes . length ; idx ++ ) { double runtime = ( double ) getRuntime ( idx ) ; double msecs = ( runtime - start ) ; result . add ( msecs / timeUnit . getValueInMillisecs ( ) ) ; } return result ; }

Get offsets from firstDate, in units of timeUnit
@return for each runtime, a list of values from firstdate

Get offsets from firstDate in units of timeUnit


public void addActionSourceListener( ActionSourceListener l) {
    if (!eventType.equals(l.getEventTypeName()))
      throw new IllegalArgumentException("ActionCoordinator: tried to add ActionSourceListener for wrong kind of Action "+
         eventType+ " != "+ l.getEventTypeName());

    lm.addListener(l);
    l.addActionValueListener(this);
  }


static public Date getStandardDate(String text) {
    double value;
    String udunitString;

    text = text.trim();
    StringTokenizer stoker = new StringTokenizer(text);
    String firstToke = stoker.nextToken();
    try {
      value = Double.parseDouble(firstToke);
      udunitString = text.substring(firstToke.length());
    } catch (NumberFormatException e) { // stupid way to test if it starts with a number
      value = 0.0;
      udunitString = text;
    }

    DateUnit du;
    try {
      du = new DateUnit(udunitString);
    } catch (Exception e) {
      return null;
    }

    return du.makeDate(value);
  }

Create a java.util.Date from this udunits String.

@param text a udunit string.
<pre>[number] (units) since [-]Y[Y[Y[Y]]]-MM-DD[(T| )hh[:mm[:ss[.sss*]]][ [+|-]hh[[:]mm]]]</pre>
@return Date or null if not date unit.

Create a java . util . Date from this udunits String .


static public Date getStandardOrISO(String text) {
    Date result = getStandardDate(text);
    if (result == null) {
      DateFormatter formatter = new DateFormatter();
      result = formatter.getISODate(text);
    }
    return result;
  }

static public Date getStandardOrISO ( String text ) { Date result = getStandardDate ( text ) ; if ( result == null ) { DateFormatter formatter = new DateFormatter ( ) ; result = formatter . getISODate ( text ) ; } return result ; }

Create a java.util.Date from a udunit or ISO String.

@param text a udunit or ISO string.
@return Date or null if not date unit.
@see #getStandardDate
@see DateFormatter#getISODate

Create a java . util . Date from a udunit or ISO String .


public Date getDateOrigin() {
    if (!(uu instanceof TimeScaleUnit)) return null;
    TimeScaleUnit tu = (TimeScaleUnit) uu;
    return tu.getOrigin();
  }

public Date getDateOrigin ( ) { if ( ! ( uu instanceof TimeScaleUnit ) ) return null ; TimeScaleUnit tu = ( TimeScaleUnit ) uu ; return tu . getOrigin ( ) ; }

Get the origin Date.

@return Date or null if not a time unit.


public Date getDate() {
    double secs = timeUnit.getValueInSeconds(value);
    return new Date(getDateOrigin().getTime() + (long) (1000 * secs));
  }

public Date getDate ( ) { double secs = timeUnit . getValueInSeconds ( value ) ; return new Date ( getDateOrigin ( ) . getTime ( ) + ( long ) ( 1000 * secs ) ) ; }

Get the equivalent java.util.Date.

@return Date or null if failure


public Date makeDate(double val) {
    if (Double.isNaN(val)) return null;
    double secs = timeUnit.getValueInSeconds(val); //
    return new Date(getDateOrigin().getTime() + (long) (1000 * secs));
  }

public Date makeDate ( double val ) { if ( Double . isNaN ( val ) ) return null ; double secs = timeUnit . getValueInSeconds ( val ) ; // return new Date ( getDateOrigin ( ) . getTime ( ) + ( long ) ( 1000 * secs ) ) ; }

Create a Date from this base unit and the given value.

@param val value in the units of this base unit, eg sec since base date
@return Date .

Create a Date from this base unit and the given value .


public double makeValue(Date date) {
    double secs = date.getTime() / 1000.0;
    double origin_secs = getDateOrigin().getTime() / 1000.0;
    double diff = secs - origin_secs;

    try {
      timeUnit.setValueInSeconds(diff);
    } catch (Exception e) {
      throw new RuntimeException(e.getMessage());
    }
    return timeUnit.getValue();
  }

public double makeValue ( Date date ) { double secs = date . getTime ( ) / 1000.0 ; double origin_secs = getDateOrigin ( ) . getTime ( ) / 1000.0 ; double diff = secs - origin_secs ; try { timeUnit . setValueInSeconds ( diff ) ; } catch ( Exception e ) { throw new RuntimeException ( e . getMessage ( ) ) ; } return timeUnit . getValue ( ) ; }

Create the equivalent value from this base unit and the given Date.
Inverse of makeDate.

@param date to convert.
@return value in units of this base unit.

Create the equivalent value from this base unit and the given Date . Inverse of makeDate .


public String makeStandardDateString(double value) {
    Date date = makeDate(value);
    if (date == null) return null;
    DateFormatter formatter = new DateFormatter();
    return formatter.toDateTimeStringISO(date);
  }

public String makeStandardDateString ( double value ) { Date date = makeDate ( value ) ; if ( date == null ) return null ; DateFormatter formatter = new DateFormatter ( ) ; return formatter . toDateTimeStringISO ( date ) ; }

Make a standard GMT string representation from this unit and given value.

@param value of time in these units.
@return String or null if not time unit.

Make a standard GMT string representation from this unit and given value .


private double getGridSpacingInKm(String type) {
    double value = gds.getDouble(type);
    if (Double.isNaN(value)) return value;
    String gridUnit = gds.getParam(GridDefRecord.GRID_UNITS);
    SimpleUnit unit;

    if (gridUnit == null || gridUnit.length() == 0) {
      unit = SimpleUnit.meterUnit;
    } else {
      unit = SimpleUnit.factory(gridUnit);
    }
    if (unit != null && SimpleUnit.isCompatible(unit.getUnitString(), "km")) {
      value = unit.convertTo(value, SimpleUnit.kmUnit);
    }
    return value;

  }

private double getGridSpacingInKm ( String type ) { double value = gds . getDouble ( type ) ; if ( Double . isNaN ( value ) ) return value ; String gridUnit = gds . getParam ( GridDefRecord . GRID_UNITS ) ; SimpleUnit unit ; if ( gridUnit == null || gridUnit . length ( ) == 0 ) { unit = SimpleUnit . meterUnit ; } else { unit = SimpleUnit . factory ( gridUnit ) ; } if ( unit != null && SimpleUnit . isCompatible ( unit . getUnitString ( ) , "km" ) ) { value = unit . convertTo ( value , SimpleUnit . kmUnit ) ; } return value ; }

Get the grid spacing in kilometers
@param type param name
@return the grid spacing in kilometers


void addDimensionsToNetcdfFile(NetcdfFile ncfile) {

    if (isLatLon) {
      ncfile.addDimension(g, new Dimension("lat", gds.getInt(GridDefRecord.NY), true));
      ncfile.addDimension(g, new Dimension("lon", gds.getInt(GridDefRecord.NX), true));
    } else {
      ncfile.addDimension(g, new Dimension("y", gds.getInt(GridDefRecord.NY), true));
      ncfile.addDimension(g, new Dimension("x", gds.getInt(GridDefRecord.NX), true));
    }
  }

void addDimensionsToNetcdfFile ( NetcdfFile ncfile ) { if ( isLatLon ) { ncfile . addDimension ( g , new Dimension ( "lat" , gds . getInt ( GridDefRecord . NY ) , true ) ) ; ncfile . addDimension ( g , new Dimension ( "lon" , gds . getInt ( GridDefRecord . NX ) , true ) ) ; } else { ncfile . addDimension ( g , new Dimension ( "y" , gds . getInt ( GridDefRecord . NY ) , true ) ) ; ncfile . addDimension ( g , new Dimension ( "x" , gds . getInt ( GridDefRecord . NX ) , true ) ) ; } }

Add the dimensions associated with this coord sys to the netCDF file

@param ncfile netCDF file to add to


private double[] addCoordAxis(NetcdfFile ncfile, String name, int n,
                                double start, double incr, String units,
                                String desc, String standard_name,
                                AxisType axis) {

    // ncfile.addDimension(g, new Dimension(name, n, true));

    Variable v = new Variable(ncfile, g, null, name);
    v.setDataType(DataType.DOUBLE);
    v.setDimensions(name);

    // create the data
    double[] data = new double[n];
    for (int i = 0; i < n; i++) {
      data[i] = start + incr * i;
    }
    Array dataArray = Array.factory(DataType.DOUBLE, new int[]{n}, data);
    v.setCachedData(dataArray, false);

    v.addAttribute(new Attribute("units", units));
    v.addAttribute(new Attribute("long_name", desc));
    v.addAttribute(new Attribute("standard_name", standard_name));
    v.addAttribute(new Attribute("grid_spacing", incr + " " + units));
    v.addAttribute(new Attribute(_Coordinate.AxisType, axis.toString()));

    ncfile.addVariable(g, v);
    return data;
  }

Add a coordinate axis

@param ncfile        the netCDF file to add to
@param name          name of the axis
@param n             number of points
@param start         starting value
@param incr          increment between points
@param units         units
@param desc          description
@param standard_name standard name
@param axis          axis type
@return the coordinate values


private boolean makeProjection(NetcdfFile ncfile, int projType) {
    switch (projType) {

      case GridTableLookup.RotatedLatLon:
        makeRotatedLatLon(ncfile);
        break;

      case GridTableLookup.PolarStereographic:
        makePS();
        break;

      case GridTableLookup.LambertConformal:
        makeLC();
        break;

      case GridTableLookup.Mercator:
        makeMercator();
        break;

      case GridTableLookup.Orthographic:
        //makeSpaceViewOrOthographic();
        makeMSGgeostationary();
        break;

      case GridTableLookup.Curvilinear:
        makeCurvilinearAxis( ncfile);
        break;

      default:
        throw new UnsupportedOperationException("unknown projection = "
            + gds.getInt(GridDefRecord.GRID_TYPE));
    }

    // dummy coordsys variable
    Variable v = new Variable(ncfile, g, null, grid_name);
    v.setDataType(DataType.CHAR);
    v.setDimensions(""); // scalar
    char[] data = new char[]{'d'};
    Array dataArray = Array.factory(DataType.CHAR, new int[0], data);
    v.setCachedData(dataArray, false);

    for (Attribute att : attributes)
      v.addAttribute(att);

    // add CF Conventions attributes
    v.addAttribute(new Attribute(GridCF.EARTH_SHAPE, shape_name));

    // LOOK - spherical earth ??
    double radius_spherical_earth = gds.getDouble(GridDefRecord.RADIUS_SPHERICAL_EARTH);
    // have to check both because Grib1 and Grib2 used different names
    if (Double.isNaN(radius_spherical_earth))
      radius_spherical_earth = gds.getDouble("radius_spherical_earth");

    if( ! Double.isNaN(radius_spherical_earth) ) {
      //inconsistent - sometimes in km, sometimes in m.
      if (radius_spherical_earth < 10000.00) // then its in km
        radius_spherical_earth *= 1000.0;    // convert to meters
      v.addAttribute(new Attribute(GridCF.EARTH_RADIUS, radius_spherical_earth)); //this attribute needs to be meters

    } else { // oblate earth
      double major_axis = gds.getDouble( GridDefRecord.MAJOR_AXIS_EARTH );
      if (Double.isNaN( major_axis ))
        major_axis = gds.getDouble("major_axis_earth");
      double minor_axis = gds.getDouble( GridDefRecord.MINOR_AXIS_EARTH );
      if (Double.isNaN(minor_axis))
        minor_axis = gds.getDouble("minor_axis_earth");
      if ( ! Double.isNaN ( major_axis ) && ! Double.isNaN ( minor_axis )) {
        v.addAttribute(new Attribute(GridCF.SEMI_MAJOR_AXIS, major_axis));
        v.addAttribute(new Attribute(GridCF.SEMI_MINOR_AXIS, minor_axis));
      }
    }
    addGDSparams(v);
    ncfile.addVariable(g, v);
    return true;
  }

Make a projection and add it to the netCDF file

@param ncfile netCDF file
@return true if projection was added and coordinates need to be added, false means do nothing


private void addGDSparams(Variable v) {
    // add all the gds parameters
    List<String> keyList = new ArrayList<>(gds.getKeys());
    Collections.sort(keyList);
    String pre = getGDSprefix();

    for (String key : keyList) {
      String name = pre + "_param_" + key;

      String vals = gds.getParam(key);
      try {
        int vali = Integer.parseInt(vals);
        if (key.equals(GridDefRecord.VECTOR_COMPONENT_FLAG)) {
          String cf = GridCF.VectorComponentFlag.of(vali);
          v.addAttribute(new Attribute(name, cf));
        } else {
          v.addAttribute(new Attribute(name, vali));
        }
      } catch (Exception e) {
        try {
          double vald = Double.parseDouble(vals);
          v.addAttribute(new Attribute(name, vald));
        } catch (Exception e2) {
          v.addAttribute(new Attribute(name, vals));
        }
      }
    }
  }

private
void
addGDSparams
(
Variable
v
)
{
// add all the gds parameters
List
<
String
>
keyList
=
new
ArrayList
<>
(
gds
.
getKeys
(
)
)
;
Collections
.
sort
(
keyList
)
;
String
pre
=
getGDSprefix
(
)
;
for
(
String
key
:
keyList
)
{
String
name
=
pre
+
"_param_"
+
key
;
String
vals
=
gds
.
getParam
(
key
)
;
try
{
int
vali
=
Integer
.
parseInt
(
vals
)
;
if
(
key
.
equals
(
GridDefRecord
.
VECTOR_COMPONENT_FLAG
)
)
{
String
cf
=
GridCF
.
VectorComponentFlag
.
of
(
vali
)
;
v
.
addAttribute
(
new
Attribute
(
name
,
cf
)
)
;
}
else
{
v
.
addAttribute
(
new
Attribute
(
name
,
vali
)
)
;
}
}
catch
(
Exception
e
)
{
try
{
double
vald
=
Double
.
parseDouble
(
vals
)
;
v
.
addAttribute
(
new
Attribute
(
name
,
vald
)
)
;
}
catch
(
Exception
e2
)
{
v
.
addAttribute
(
new
Attribute
(
name
,
vals
)
)
;
}
}
}
}

Add the GDS params to the variable as attributes

@param v the GDS params.


private void addCoordSystemVariable(NetcdfFile ncfile, String name, String dims) {
    Variable v = new Variable(ncfile, g, null, name);
    v.setDataType(DataType.CHAR);
    v.setDimensions(""); // scalar
    Array dataArray = Array.factory(DataType.CHAR, new int[0], new char[]{'0'});
    v.setCachedData(dataArray, false);
    v.addAttribute(new Attribute(_Coordinate.Axes, dims));
    if (isLatLon())
      v.addAttribute(new Attribute(_Coordinate.Transforms, "")); // to make sure its identified as a Coordinate System Variable
    else
      v.addAttribute(new Attribute(_Coordinate.Transforms, getGridName()));
    addGDSparams(v);
    ncfile.addVariable(g, v);
  }

Add coordinate system variable

@param ncfile netCDF file
@param name   name of the variable
@param dims   dimensions


private void makeLC() {
    // we have to project in order to find the origin
    proj = new LambertConformal(
        gds.getDouble(GridDefRecord.LATIN1), gds.getDouble(GridDefRecord.LOV),
        gds.getDouble(GridDefRecord.LATIN1), gds.getDouble(GridDefRecord.LATIN2));
    LatLonPointImpl startLL =
        new LatLonPointImpl(gds.getDouble(GridDefRecord.LA1), gds.getDouble(GridDefRecord.LO1));
    ProjectionPointImpl start = (ProjectionPointImpl) proj.latLonToProj(startLL);
    startx = start.getX();
    starty = start.getY();

    if (Double.isNaN(getDxInKm())) {
      setDxDy(startx, starty, proj);
    }

    if (GridServiceProvider.debugProj) {
      System.out.println("GridHorizCoordSys.makeLC start at latlon " + startLL);

      double Lo2 = gds.getDouble(GridDefRecord.LO2);
      double La2 = gds.getDouble(GridDefRecord.LA2);
      LatLonPointImpl endLL = new LatLonPointImpl(La2, Lo2);
      System.out.println("GridHorizCoordSys.makeLC end at latlon " + endLL);

      ProjectionPointImpl endPP = (ProjectionPointImpl) proj.latLonToProj(endLL);
      System.out.println("   end at proj coord " + endPP);

      double endx = startx + getNx() * getDxInKm();
      double endy = starty + getNy() * getDyInKm();
      System.out.println("   should be x=" + endx + " y=" + endy);
    }

    attributes.add(new Attribute(GridCF.GRID_MAPPING_NAME, "lambert_conformal_conic"));
    if (gds.getDouble(GridDefRecord.LATIN1) == gds.getDouble(GridDefRecord.LATIN2)) {
      attributes.add(new Attribute(GridCF.STANDARD_PARALLEL, gds.getDouble(GridDefRecord.LATIN1)));
    } else {
      double[] data = new double[]{gds.getDouble(GridDefRecord.LATIN1), gds.getDouble(GridDefRecord.LATIN2)};
      attributes.add(new Attribute(GridCF.STANDARD_PARALLEL, Array.factory(DataType.DOUBLE, new int[]{2}, data)));
    }
    //attributes.add(new Attribute("longitude_of_central_meridian",
    attributes.add(new Attribute(GridCF.LONGITUDE_OF_CENTRAL_MERIDIAN, gds.getDouble(GridDefRecord.LOV)));
    //attributes.add(new Attribute("latitude_of_projection_origin",
    attributes.add(new Attribute(GridCF.LATITUDE_OF_PROJECTION_ORIGIN, gds.getDouble(GridDefRecord.LATIN1)));
  }


private void makePS() {
    String nproj = gds.getParam(GridDefRecord.NPPROJ);
    double latOrigin = (nproj == null || nproj.equalsIgnoreCase("true")) ? 90.0 : -90.0;

    // Why the scale factor?. according to GRIB docs:
    // "Grid lengths are in units of meters, at the 60 degree latitude circle nearest to the pole"
    // since the scale factor at 60 degrees = k = 2*k0/(1+sin(60))  [Snyder,Working Manual p157]
    // then to make scale = 1 at 60 degrees, k0 = (1+sin(60))/2 = .933
    double scale;
    double lad = gds.getDouble(GridDefRecord.LAD);
    if (Double.isNaN(lad)) {
      scale = .933;
    } else {
      scale = (1.0+Math.sin( Math.toRadians( Math.abs(lad)) ))/2;
    }

    proj = new Stereographic(latOrigin, gds.getDouble(GridDefRecord.LOV), scale);

    // we have to project in order to find the origin
    ProjectionPointImpl start = (ProjectionPointImpl) proj.latLonToProj(
        new LatLonPointImpl( gds.getDouble(GridDefRecord.LA1), gds.getDouble(GridDefRecord.LO1)));
    startx = start.getX();
    starty = start.getY();

    if (Double.isNaN(getDxInKm()))
      setDxDy(startx, starty, proj);

    if (GridServiceProvider.debugProj) {
      System.out.printf("starting proj coord %s lat/lon %s%n", start, proj.projToLatLon(start));
      System.out.println("   should be LA1=" + gds.getDouble(GridDefRecord.LA1) + " l)1=" + gds.getDouble(GridDefRecord.LO1));
    }

    attributes.add(new Attribute(GridCF.GRID_MAPPING_NAME, "polar_stereographic"));
    //attributes.add(new Attribute("longitude_of_projection_origin",
    attributes.add(new Attribute(GridCF.LONGITUDE_OF_PROJECTION_ORIGIN, gds.getDouble(GridDefRecord.LOV)));
    //attributes.add(new Attribute("straight_vertical_longitude_from_pole",
    attributes.add(new Attribute( GridCF.STRAIGHT_VERTICAL_LONGITUDE_FROM_POLE, gds.getDouble(GridDefRecord.LOV)));
    //attributes.add(new Attribute("scale_factor_at_projection_origin",
    attributes.add(new Attribute(GridCF.SCALE_FACTOR_AT_PROJECTION_ORIGIN, scale));
    attributes.add(new Attribute(GridCF.LATITUDE_OF_PROJECTION_ORIGIN, latOrigin));
  }


private void makeMercator() {
    /**
     * Construct a Mercator Projection.
     * @param lon0 longitude of origin (degrees)
     * @param par standard parallel (degrees). cylinder cuts earth at this latitude.
     */
    double Latin = gds.getDouble(GridDefRecord.LAD);
    // name depends on Grib version 1 or 2
    if (Double.isNaN(Latin))
      Latin = gds.getDouble(GridDefRecord.LATIN);
    double Lo1 = gds.getDouble(GridDefRecord.LO1); //gds.Lo1;
    double La1 = gds.getDouble(GridDefRecord.LA1); //gds.La1;

    // put longitude origin at first point - doesnt actually matter
    proj = new Mercator(Lo1, Latin);

    // find out where
    ProjectionPoint startP = proj.latLonToProj(
        new LatLonPointImpl(La1, Lo1));
    startx = startP.getX();
    starty = startP.getY();

    if (Double.isNaN(getDxInKm())) {
      setDxDy(startx, starty, proj);
    }

    attributes.add(new Attribute(GridCF.GRID_MAPPING_NAME, "mercator"));
    attributes.add(new Attribute(GridCF.STANDARD_PARALLEL, Latin));
    attributes.add(new Attribute(GridCF.LONGITUDE_OF_PROJECTION_ORIGIN, Lo1));

    if (GridServiceProvider.debugProj) {
      double Lo2 = gds.getDouble(GridDefRecord.LO2);
      if (Lo2 < Lo1) Lo2 += 360;
      double La2 = gds.getDouble(GridDefRecord.LA2);
      LatLonPointImpl endLL = new LatLonPointImpl(La2, Lo2);
      System.out.println("GridHorizCoordSys.makeMercator: end at latlon= " +
          endLL);

      ProjectionPointImpl endPP = (ProjectionPointImpl) proj.latLonToProj(endLL);
      System.out.println("   start at proj coord " +
          new ProjectionPointImpl(startx, starty));
      System.out.println("   end at proj coord " + endPP);

      double endx = startx + (getNx() - 1) * getDxInKm();
      double endy = starty + (getNy() - 1) * getDyInKm();
      System.out.println("   should be x=" + endx + " y=" + endy);
    }
  }


private void makeMSGgeostationary() {
    double Lat0 = gds.getDouble(GridDefRecord.LAP);  // sub-satellite point lat
    double Lon0 = gds.getDouble(GridDefRecord.LOP);  // sub-satellite point lon

    //int nx = gds.getInt(GridDefRecord.NX);
    int ny = gds.getInt(GridDefRecord.NY);
    int x_off = gds.getInt(GridDefRecord.XP);  // sub-satellite point in grid lengths
    int y_off = gds.getInt(GridDefRecord.YP);

    double dx; // = gds.getDouble(GridDefRecord.DX);  // apparent diameter of earth in units of grid lengths
    double dy = gds.getDouble(GridDefRecord.DY);

    // per Simon Eliot 1/18/2010, there is a bug in Eumetsat grib files,
    // we need to "correct for ellipsoidal earth"
    // (Note we should check who the originating center is
    // "Originating_center" = "EUMETSAT Operation Centre" in the GRIB id (section 1))
    // although AFAIK, eumetsat is only one using this projection.
    if (dy < 2100) {
      dx = 1207;
      dy = 1203;
    } else {
      dx = 3622;
      dy = 3610;
    }

    // have to check both names because Grib1 and Grib2 used different names
    double major_axis = gds.getDouble(GridDefRecord.MAJOR_AXIS_EARTH);  // m
    if (Double.isNaN(major_axis))
      major_axis = gds.getDouble("major_axis_earth");

    double minor_axis = gds.getDouble(GridDefRecord.MINOR_AXIS_EARTH);  // m
    if (Double.isNaN(minor_axis))
      minor_axis = gds.getDouble("minor_axis_earth");
    // Nr = altitude of camera from center, in units of radius
    double nr = gds.getDouble(GridDefRecord.NR) * 1e-6; // altitude of the camera from the Earths centre, measured in units of the Earth (equatorial) radius

          // CFAC = 2^16 / {[2 * arcsine (10^6 / Nr)] / dx }
    double as = 2 * Math.asin(1.0/nr);
    double cfac = dx / as;
    double lfac = dy / as;

    // use km, so scale by the earth radius
    double scale_factor = (nr - 1) * major_axis / 1000; // this sets the units of the projection x,y coords in km
    double scale_x = scale_factor; // LOOK fake neg need scan value
    double scale_y = -scale_factor; // LOOK fake neg need scan value
    startx = scale_factor * (1 - x_off) / cfac;
    starty = scale_factor * (y_off - ny) / lfac;
    incrx = scale_factor/cfac;
    incry = scale_factor/lfac;

    attributes.add(new Attribute(GridCF.GRID_MAPPING_NAME, "MSGnavigation"));
    attributes.add(new Attribute(GridCF.LONGITUDE_OF_PROJECTION_ORIGIN, Lon0));
    attributes.add(new Attribute(GridCF.LATITUDE_OF_PROJECTION_ORIGIN, Lat0));
    //attributes.add(new Attribute("semi_major_axis", new Double(major_axis)));
    //attributes.add(new Attribute("semi_minor_axis", new Double(minor_axis)));
    attributes.add(new Attribute("height_from_earth_center", nr * major_axis));
    attributes.add(new Attribute("scale_x", scale_x));
    attributes.add(new Attribute("scale_y", scale_y));

    proj = new MSGnavigation(Lat0, Lon0, major_axis, minor_axis, nr * major_axis, scale_x, scale_y);

    if (GridServiceProvider.debugProj) {

      double Lo2 = gds.getDouble(GridDefRecord.LO2) + 360.0;
      double La2 = gds.getDouble(GridDefRecord.LA2);
      LatLonPointImpl endLL = new LatLonPointImpl(La2, Lo2);
      System.out.println("GridHorizCoordSys.makeMSGgeostationary end at latlon " + endLL);

      ProjectionPointImpl endPP =(ProjectionPointImpl) proj.latLonToProj(endLL);
      System.out.println("   end at proj coord " + endPP);

      double endx = 1 + getNx();
      double endy = 1 + getNy();
      System.out.println("   should be x=" + endx + " y=" + endy);
    }
  }

Make a Eumetsat MSG "Normalized Geostationary Projection" projection.
Fake coordinates for now, then see if this can be generalized.

from  FM 92 GRIB-2 doc:

Grid Definition Template 3.90: Space view perspective or orthographic
Octet Number(s) Contents
15 Shape of the earth (see Code Table 3.2)
16 Scale factor of radius of spherical earth
17-20 Scaled value of radius of spherical earth
21 Scale factor of major axis of oblate spheroid earth
22-25 Scaled value of major axis of oblate spheroid earth
26 Scale factor of minor axis of oblate spheroid earth
27-30 Scaled value of minor axis of oblate spheroid earth
31-34 Nx - number of points along X-axis (columns)
35-38 Ny - number of points along Y-axis (rows or lines)
39-42 Lap - latitude of sub-satellite point
43-46 Lop - longitude of sub-satellite point
47 Resolution and component flags (see Flag Table 3.3)
48-51 dx - apparent diameter of Earth in grid lengths, in X-direction
52-55 dy - apparent diameter of Earth in grid lengths, in Y-direction
56-59 Xp - X-coordinate of sub-satellite point (in units of 10-3 grid length expressed as an integer)
60-63 Yp - Y-coordinate of sub-satellite point (in units of 10-3 grid length expressed as an integer)
64 Scanning mode (flags - see Flag Table 3.4)
65-68 Orientation of the grid; i.e., the angle between the increasing Y-axis and the meridian of the sub-satellite point in the direction of increasing latitude (see Note 3)
69-72 Nr - altitude of the camera from the Earths centre, measured in units of the Earth (equatorial) radius multiplied by a scale factor of 10 6 (see Notes 4 and 5)
73-76 Xo - X-coordinate of origin of sector image
77-80 Yo - Y-coordinate of origin of sector image

Notes:
(1) It is assumed that the satellite is at its nominal position, i.e., it is looking directly at its sub-satellite point.
(2) Octets 69-72 shall be set to all ones (missing) to indicate the orthographic view (from infinite distance)
(3) It is the angle between the increasing Y-axis and the meridian 180E if the sub-satellite point is the North Pole; or the meridian 0 if the sub-satellite point is the South Pole.
(4) The apparent angular size of the Earth will be given by 2 * Arcsin (10^6 )/Nr).
(5) For orthographic view from infinite distance, the value of Nr should be encoded as missing (all bits set to 1).
(6) The horizontal and vertical angular resolutions of the sensor (Rx and Ry), needed for navigation equation, can be calculated from the following:
Rx = 2 * Arcsin (106 )/Nr)/ dx
Ry = 2 * Arcsin (106 )/Nr)/ dy

=======

from  simon.elliott@eumetsat.int

For products on a single pixel resolution grid, the scan angle is 83.84333 E-6 rad.
So dx = 2 * arcsin(10e6/Nr) / 83.84333 E-6 = 3622.30, which encoded to the nearest integer is 3622.
This is correctly encoded in our products.

For products on a 3x3 pixel resolution grid, the scan angle is 3 * 83.84333 E-6 rad = 251.52999 E-6 rad.
So dx = 2 * arcsin(10e6/Nr) / 251.52999 E-6 = 1207.43, which encoded to the nearest integer is 1207.
This is correctly encoded in our products.

Due to the elliptical shape of the earth, the calculation is a bit different in the y direction (Nr is in multiples of
the equatorial radius, but the tangent point is much closer to the polar radius from the earth's centre.
Approximating that the tangent point is actually at the polar radius from the earth's centre:
The sine of the angle subtended by the Earths centre and the tangent point on the equator as seen from the spacecraft
= Rp / (( Nr * Re) / 10^6) = (Rp * 10^6) / (Re * Nr)

The angle subtended by the Earth equator as seen by the spacecraft is, by symmetry twice the inverse sine above,
= 2 * arcsine ((Rp * 10^6) / (Re * Nr))

For products on a single pixel resolution grid, the scan angle is 83.84333 E-6 rad.
So dy = 2 * arcsine ((Rp * 10^6) / (Re * Nr)) / 83.84333 E-6 = 3610.06, which encoded to the nearest integer is 3610.
This is currently encoded in our products as 3568.

For products on a 3x3 pixel resolution grid, the scan angle is 3 * 83.84333 E-6 rad = 251.52999 E-6 rad.
So dy = 2 * arcsine ((Rp * 10^6) / (Re * Nr)) / 251.52999 E-6 = 1203.35, which encoded to the nearest integer is 1203.
This is currently encoded in our products as 1189.

As you can see the dx and dy values we are using will lead to an error of around 1% in the y direction.
I will ensure that the values are corrected to those explained here (3610 and 1203) as soon as possible.

Make a Eumetsat MSG Normalized Geostationary Projection projection . Fake coordinates for now then see if this can be generalized .


private void setDxDy(double startx, double starty, ProjectionImpl proj) {
    double Lo2 = gds.getDouble(GridDefRecord.LO2);
    double La2 = gds.getDouble(GridDefRecord.LA2);
    if (Double.isNaN(Lo2) || Double.isNaN(La2)) {
      return;
    }
    LatLonPointImpl endLL = new LatLonPointImpl(La2, Lo2);
    ProjectionPointImpl end =
        (ProjectionPointImpl) proj.latLonToProj(endLL);
    double dx = Math.abs(end.getX() - startx)
        / (gds.getInt(GridDefRecord.NX) - 1);
    double dy = Math.abs(end.getY() - starty)
        / (gds.getInt(GridDefRecord.NY) - 1);
    gds.addParam(GridDefRecord.DX, String.valueOf(dx));
    gds.addParam(GridDefRecord.DY, String.valueOf(dy));
    gds.addParam(GridDefRecord.GRID_UNITS, "km");
  }

private void setDxDy ( double startx , double starty , ProjectionImpl proj ) { double Lo2 = gds . getDouble ( GridDefRecord . LO2 ) ; double La2 = gds . getDouble ( GridDefRecord . LA2 ) ; if ( Double . isNaN ( Lo2 ) || Double . isNaN ( La2 ) ) { return ; } LatLonPointImpl endLL = new LatLonPointImpl ( La2 , Lo2 ) ; ProjectionPointImpl end = ( ProjectionPointImpl ) proj . latLonToProj ( endLL ) ; double dx = Math . abs ( end . getX ( ) - startx ) / ( gds . getInt ( GridDefRecord . NX ) - 1 ) ; double dy = Math . abs ( end . getY ( ) - starty ) / ( gds . getInt ( GridDefRecord . NY ) - 1 ) ; gds . addParam ( GridDefRecord . DX , String . valueOf ( dx ) ) ; gds . addParam ( GridDefRecord . DY , String . valueOf ( dy ) ) ; gds . addParam ( GridDefRecord . GRID_UNITS , "km" ) ; }

Calculate the dx and dy from startx, starty and projection.

@param startx starting x projection point
@param starty starting y projection point
@param proj   projection for transform

Calculate the dx and dy from startx starty and projection .


protected InvDatasetImpl readDataset(InvCatalogImpl catalog, InvDatasetImpl parent, Element dsElem, URI base) {

    // deal with aliases
    String name = dsElem.getAttributeValue("name");
    String alias = dsElem.getAttributeValue("alias");
    if (alias != null) {
      InvDatasetImpl ds = (InvDatasetImpl) catalog.findDatasetByID(alias);
      if (ds == null) {
        factory.appendErr(" ** Parse error: dataset named " + name + " has illegal alias = " + alias + "\n");
        return null;
      }
      return new InvDatasetImplProxy(name, ds);
    }

    InvDatasetImpl dataset = new InvDatasetImpl(parent, name);
    readDatasetInfo(catalog, dataset, dsElem, base);

    if (InvCatalogFactory.debugXML) System.out.println(" Dataset added: " + dataset.dump());
    return dataset;
  }

protected
InvDatasetImpl
readDataset
(
InvCatalogImpl
catalog
,
InvDatasetImpl
parent
,
Element
dsElem
,
URI
base
)
{
// deal with aliases
String
name
=
dsElem
.
getAttributeValue
(
"name"
)
;
String
alias
=
dsElem
.
getAttributeValue
(
"alias"
)
;
if
(
alias
!=
)
{
InvDatasetImpl
ds
=
(
InvDatasetImpl
)
catalog
.
findDatasetByID
(
alias
)
;
if
(
ds
==
)
{
factory
.
appendErr
(
" ** Parse error: dataset named "
+
name
+
" has illegal alias = "
+
alias
+
"\n"
)
;
return
;
}
return
new
InvDatasetImplProxy
(
name
,
ds
)
;
}
InvDatasetImpl
dataset
=
new
InvDatasetImpl
(
parent
,
name
)
;
readDatasetInfo
(
catalog
,
dataset
,
dsElem
,
base
)
;
if
(
InvCatalogFactory
.
debugXML
)
System
.
out
.
println
(
" Dataset added: "
+
dataset
.
dump
(
)
)
;
return
dataset
;
}


protected InvDatasetScan readDatasetScan(InvCatalogImpl catalog, InvDatasetImpl parent, Element dsElem, URI base) {
    InvDatasetScan datasetScan;

    if (dsElem.getAttributeValue("dirLocation") == null) {
      if (dsElem.getAttributeValue("location") == null) {
        logger.error("readDatasetScan(): datasetScan has neither a \"location\" nor a \"dirLocation\" attribute.");
        datasetScan = null;
      } else {
        return readDatasetScanNew(catalog, parent, dsElem, base);
      }
    } else {
      String name = dsElem.getAttributeValue("name");
      factory.appendWarning("**Warning: Dataset " + name + " using old form of DatasetScan (dirLocation instead of location)\n");

      String path = dsElem.getAttributeValue("path");

      String scanDir = expandAliasForPath(dsElem.getAttributeValue("dirLocation"));
      String filter = dsElem.getAttributeValue("filter");
      String addDatasetSizeString = dsElem.getAttributeValue("addDatasetSize");
      String addLatest = dsElem.getAttributeValue("addLatest");
      String sortOrderIncreasingString = dsElem.getAttributeValue("sortOrderIncreasing");
      boolean sortOrderIncreasing = false;
      if (sortOrderIncreasingString != null)
        if (sortOrderIncreasingString.equalsIgnoreCase("true"))
          sortOrderIncreasing = true;
      boolean addDatasetSize = true;
      if (addDatasetSizeString != null)
        if (addDatasetSizeString.equalsIgnoreCase("false"))
          addDatasetSize = false;

      if (path != null) {
        if (path.charAt(0) == '/') path = path.substring(1);
        int last = path.length() - 1;
        if (path.charAt(last) == '/') path = path.substring(0, last);
      }

      if (scanDir != null) {
        int last = scanDir.length() - 1;
        if (scanDir.charAt(last) != '/') scanDir = scanDir + '/';
      }

      Element atcElem = dsElem.getChild("addTimeCoverage", defNS);
      String dsNameMatchPattern = null;
      String startTimeSubstitutionPattern = null;
      String duration = null;
      if (atcElem != null) {
        dsNameMatchPattern = atcElem.getAttributeValue("datasetNameMatchPattern");
        startTimeSubstitutionPattern = atcElem.getAttributeValue("startTimeSubstitutionPattern");
        duration = atcElem.getAttributeValue("duration");
      }

      try {
        datasetScan = new InvDatasetScan(catalog, parent, name, path, scanDir, filter, addDatasetSize, addLatest, sortOrderIncreasing,
                dsNameMatchPattern, startTimeSubstitutionPattern, duration);
        readDatasetInfo(catalog, datasetScan, dsElem, base);
        if (InvCatalogFactory.debugXML) System.out.println(" Dataset added: " + datasetScan.dump());

      } catch (Exception e) {
        logger.error("Reading DatasetScan", e);
        datasetScan = null;
      }
    }

    return datasetScan;
  }


public Object readMetadataContentFromURL(InvDataset dataset, java.net.URI uri) throws java.io.IOException {
    Element elem = readContentFromURL(uri);
    Object contentObject = readMetadataContent(dataset, elem);
    if (debugMetadataRead) System.out.println(" convert to " + contentObject.getClass().getName());
    return contentObject;
  }

public
Object
readMetadataContentFromURL
(
InvDataset
dataset
,
java
.
net
.
URI
uri
)
throws
java
.
io
.
IOException
{
Element
elem
=
readContentFromURL
(
uri
)
;
Object
contentObject
=
readMetadataContent
(
dataset
,
elem
)
;
if
(
debugMetadataRead
)
System
.
out
.
println
(
" convert to "
+
contentObject
.
getClass
(
)
.
getName
(
)
)
;
return
contentObject
;
}

this is only called for ThredddsMetadata


public int getLevelType1() {
    // TODO:  flush this out
    int gribLevel = getDirBlock()[51];
    int levelType = 0;
    if (!((gribLevel == McIDASUtil.MCMISSING) || (gribLevel == 0))) {
      levelType = gribLevel;
    } else {
      levelType = 1;
    }
    return levelType;
  }

public
int
getLevelType1
(
)
{
// TODO:  flush this out
int
gribLevel
=
getDirBlock
(
)
[
51
]
;
int
levelType
=
0
;
if
(
!
(
(
gribLevel
==
McIDASUtil
.
MCMISSING
)
||
(
gribLevel
==
0
)
)
)
{
levelType
=
gribLevel
;
}
else
{
levelType
=
1
;
}
return
levelType
;
}

Get the type for the first level of this GridRecord

@return level type


public boolean isValidFile(RandomAccessFile raf) throws IOException {
    raf.seek(0);
    String test = raf.readString(MAGIC.length());
    return test.equals(MAGIC);
  }

public boolean isValidFile ( RandomAccessFile raf ) throws IOException { raf . seek ( 0 ) ; String test = raf . readString ( MAGIC . length ( ) ) ; return test . equals ( MAGIC ) ; }

Check if this is a valid file for this IOServiceProvider.
You must make this method thread safe, ie dont keep any state.

@param raf RandomAccessFile
@return true if valid.
@throws IOException if read error

Check if this is a valid file for this IOServiceProvider . You must make this method thread safe ie dont keep any state .


public void write(byte[] b, int off, int len) throws IOException {

        count += len;
        super.write(b, off, len);

    }

public void write ( byte [ ] b , int off , int len ) throws IOException { count += len ; super . write ( b , off , len ) ; }

Writes an array of bytes to the compressed output stream. This
method will block until all the bytes are written.

@param off the start offset of the data
@param len the length of the data
@throws IOException if an I/O error has occurred

Writes an array of bytes to the compressed output stream . This method will block until all the bytes are written .


@Override
    public void setClearName(String clearname)
    {
	    super.setClearName(clearname);
        if(_attr != null) _attr.setClearName(clearname);
        if(_attrTbl !=  null) _attrTbl.setClearName(clearname);
    }

@ Override public void setClearName ( String clearname ) { super . setClearName ( clearname ) ; if ( _attr != null ) _attr . setClearName ( clearname ) ; if ( _attrTbl != null ) _attrTbl . setClearName ( clearname ) ; }

Sets the unencoded name of the class instance.

@param clearname the unencoded name of the class instance.

Sets the unencoded name of the class instance .


static public String getCoordinateName(NetcdfDataset ds, AxisType a) {
    List<Variable> varList = ds.getVariables();
    for (Variable v : varList) {
      if (v instanceof Structure) {
        List<Variable> vars = ((Structure) v).getVariables();
        for (Variable vs : vars) {
          String axisType = ds.findAttValueIgnoreCase(vs, _Coordinate.AxisType, null);
          if ((axisType != null) && axisType.equals(a.toString()))
            return vs.getShortName();
        }
      } else {
        String axisType = ds.findAttValueIgnoreCase(v, _Coordinate.AxisType, null);
        if ((axisType != null) && axisType.equals(a.toString()))
          return v.getShortName();
      }
    }

    if (a == AxisType.Lat)
      return findVariableName( ds, "latitude");

    if (a == AxisType.Lon)
      return findVariableName( ds, "longitude");

    if (a == AxisType.Time)
      return findVariableName( ds, "time");

    if (a == AxisType.Height) {
      Variable v = findVariable( ds, "altitude");
      if (null == v) v = findVariable( ds, "depth");
      if (v != null) return v.getShortName();
    }

    // I think the CF part is done by the CoordSysBuilder adding the _CoordinateAxisType attrinutes.
    return null;
  }

Tries to find the coordinate variable of the specified type.
@param ds search in this dataset
@param a AxisType.LAT, LON, HEIGHT, or TIME
@return coordinate variable, or null if not found.

Tries to find the coordinate variable of the specified type .


static public String getCoordinateName(NetcdfDataset ds, AxisType a, Dimension dim) {
    String name = getCoordinateName(ds, a);
    if (name == null) return null;

    Variable v = ds.findVariable(name);
    if (v == null) return null;

    if (v.isScalar()) return null;
    if (!v.getDimension(0).equals(dim)) return null;

    return name;
  }

static public String getCoordinateName ( NetcdfDataset ds , AxisType a , Dimension dim ) { String name = getCoordinateName ( ds , a ) ; if ( name == null ) return null ; Variable v = ds . findVariable ( name ) ; if ( v == null ) return null ; if ( v . isScalar ( ) ) return null ; if ( ! v . getDimension ( 0 ) . equals ( dim ) ) return null ; return name ; }

Tries to find the coordinate variable of the specified type, which has the specified dimension as its firsst dimension
@param ds search in this dataset
@param a AxisType.LAT, LON, HEIGHT, or TIME
@param dim must use this dimension
@return coordinate variable, or null if not found.

Tries to find the coordinate variable of the specified type which has the specified dimension as its firsst dimension


public String buildXML()
    {
        StringBuilder response = new StringBuilder();
        response.append("<Error");
        if(code > 0)
            response.append(String.format(" httpcode=\"%d\"", code));
        response.append(">\n");
        if(message != null)
            response.append("<Message>" + getMessage() + "</Message>\n");
        if(context != null)
            response.append("<Context>" + getContext() + "</Context>\n");
        if(otherinfo != null)
            response.append("<OtherInformation>" + getOtherInfo() + "</OtherInformation>\n");
        return response.toString();
    }

public
String
buildXML
(
)
{
StringBuilder
response
=
new
StringBuilder
(
)
;
response
.
append
(
"<Error"
)
;
if
(
code
>
0
)
response
.
append
(
String
.
format
(
" httpcode=\"%d\""
,
code
)
)
;
response
.
append
(
">\n"
)
;
if
(
message
!=
)
response
.
append
(
"<Message>"
+
getMessage
(
)
+
"</Message>\n"
)
;
if
(
context
!=
)
response
.
append
(
"<Context>"
+
getContext
(
)
+
"</Context>\n"
)
;
if
(
otherinfo
!=
)
response
.
append
(
"<OtherInformation>"
+
getOtherInfo
(
)
+
"</OtherInformation>\n"
)
;
return
response
.
toString
(
)
;
}

Convert an ErrorResponse to the equivalent XML
@return  XML representation of the ErrorResponse


public DapException buildException()
    {
        String XML = buildXML();
        DapException dapex = new DapException(XML).setCode(code);
        return dapex;
    }

public DapException buildException ( ) { String XML = buildXML ( ) ; DapException dapex = new DapException ( XML ) . setCode ( code ) ; return dapex ; }

Convert an ErrorResponse to the equivalent DapException.
@return  DapException representation of the ErrorResponse

Convert an ErrorResponse to the equivalent DapException .


private List<Dimension> breakupLevels(NetcdfDataset ds, Variable levelVar) throws IOException {
    if (debugBreakup) parseInfo.format("breakupLevels = %s%n", levelVar.getShortName());
    List<Dimension> dimList = new ArrayList<>();

    ArrayChar levelVarData;
    try {
      levelVarData = (ArrayChar) levelVar.read();
    } catch (IOException ioe) {
      return dimList;
    }

    List<String> values = null;
    String currentUnits = null;
    ArrayChar.StringIterator iter = levelVarData.getStringIterator();
    while (iter.hasNext()) {
      String s = iter.next();
      if (debugBreakup) parseInfo.format("   %s%n", s);
      StringTokenizer stoke = new StringTokenizer(s);

/* problem with blank string:
   char pvvLevels(levels_35=35, charsPerLevel=10);
"MB 1000   ", "MB 975    ", "MB 950    ", "MB 925    ", "MB 900    ", "MB 875    ", "MB 850    ", "MB 825    ", "MB 800    ", "MB 775    ", "MB 750    ",
"MB 725    ", "MB 700    ", "MB 675    ", "MB 650    ", "MB 625    ", "MB 600    ", "MB 575    ", "MB 550    ", "MB 525    ", "MB 500    ", "MB 450    ",
"MB 400    ", "MB 350    ", "MB 300    ", "MB 250    ", "MB 200    ", "MB 150    ", "MB 100    ", "BL 0 30   ", "BL 60 90  ", "BL 90 120 ", "BL 120 150",
"BL 150 180", ""
*/
      if (!stoke.hasMoreTokens())
        continue; // skip it

      // first token is the unit
      String units = stoke.nextToken().trim();
      if (!units.equals(currentUnits)) {
        if (values != null)
          dimList.add(makeZCoordAxis(ds, values, currentUnits));
        values = new ArrayList<>();
        currentUnits = units;
      }

      // next token is the value
      if (stoke.hasMoreTokens())
        values.add(stoke.nextToken());
      else
        values.add("0");
    }
    if (values != null)
      dimList.add(makeZCoordAxis(ds, values, currentUnits));

    if (debugBreakup) parseInfo.format("  done breakup%n");

    return dimList;
  }

return the list of Dimensions that were created


private Dimension makeZCoordAxis(NetcdfDataset ds, List<String> values, String units) throws IOException {
    int len = values.size();
    String name = makeZCoordName(units);
    if (len > 1)
      name = name + Integer.toString(len);
    else
      name = name + values.get(0);
    StringUtil2.replace(name, ' ', "-");

    Dimension dim;
    if (null != (dim = ds.getRootGroup().findDimension(name))) {
      if (dim.getLength() == len) {
        // check against actual values
        Variable coord = ds.getRootGroup().findVariable(name);
        Array coordData = coord.read();
        Array newData = Array.makeArray(coord.getDataType(), values);
        if (MAMath.nearlyEquals(coordData, newData)) {
          if (debugBreakup) parseInfo.format("  use existing coord %s%n", dim);
          return dim;
        }
      }
    }

    String orgName = name;
    int count = 1;
    while (ds.getRootGroup().findDimension(name) != null) {
      name = orgName + "-" + count;
      count++;
    }

    // create new one
    dim = new Dimension(name, len);
    ds.addDimension(null, dim);
    if (debugBreakup) parseInfo.format("  make Dimension = %s length = %d%n", name, len);

    // if (len < 2) return dim; // skip 1D

    if (debugBreakup) {
      parseInfo.format("  make ZCoordAxis = = %s length = %d%n", name, len);
    }

    CoordinateAxis v = new CoordinateAxis1D(ds, null, name, DataType.DOUBLE, name,
            makeUnitsName(units), makeLongName(name));
    String positive = getZisPositive(ds, v);
    if (null != positive)
      v.addAttribute(new Attribute(_Coordinate.ZisPositive, positive));

    v.setValues(values);
    ds.addCoordinateAxis(v);

    parseInfo.format("Created Z Coordinate Axis = ");
    v.getNameAndDimensions(parseInfo, true, false);
    parseInfo.format("%n");

    return dim;
  }

"  make ZCoordAxis = = %s length = %d%n"

make a new variable out of the list in "values"

make a new variable out of the list in values


private void createNewVariables(NetcdfDataset ds, Variable ncVar, List<Dimension> newDims,
                                  Dimension levelDim) throws InvalidRangeException {

    List<Dimension> dims = ncVar.getDimensions();
    int newDimIndex = dims.indexOf(levelDim);
    //String shapeS = ncVar.getShapeS();

    int[] origin = new int[ncVar.getRank()];
    int[] shape = ncVar.getShape();
    int count = 0;
    for (Dimension dim : newDims) {
      String name = ncVar.getShortName() + "-" + dim.getShortName();

      origin[newDimIndex] = count;
      shape[newDimIndex] = dim.getLength();

      Variable varNew = ncVar.section(new Section(origin, shape));
      varNew.setName(name);
      varNew.setDimension(newDimIndex, dim);

      // synthesize long name
      String long_name = ds.findAttValueIgnoreCase(ncVar, CDM.LONG_NAME, ncVar.getShortName());
      long_name = long_name + "-" + dim.getShortName();
      ds.addVariableAttribute(varNew, new Attribute(CDM.LONG_NAME, long_name));

      ds.addVariable(null, varNew);

      parseInfo.format("Created New Variable as section = ");
      varNew.getNameAndDimensions(parseInfo, true, false);
      parseInfo.format("%n");

      count += dim.getLength();
    }
  }

private
void
createNewVariables
(
NetcdfDataset
ds
,
Variable
ncVar
,
List
<
Dimension
>
newDims
,
Dimension
levelDim
)
throws
InvalidRangeException
{
List
<
Dimension
>
dims
=
ncVar
.
getDimensions
(
)
;
int
newDimIndex
=
dims
.
indexOf
(
levelDim
)
;
//String shapeS = ncVar.getShapeS();
int
[
]
origin
=
new
int
[
ncVar
.
getRank
(
)
]
;
int
[
]
shape
=
ncVar
.
getShape
(
)
;
int
count
=
0
;
for
(
Dimension
dim
:
newDims
)
{
String
name
=
ncVar
.
getShortName
(
)
+
"-"
+
dim
.
getShortName
(
)
;
origin
[
newDimIndex
]
=
count
;
shape
[
newDimIndex
]
=
dim
.
getLength
(
)
;
Variable
varNew
=
ncVar
.
section
(
new
Section
(
origin
,
shape
)
)
;
varNew
.
setName
(
name
)
;
varNew
.
setDimension
(
newDimIndex
,
dim
)
;
// synthesize long name
String
long_name
=
ds
.
findAttValueIgnoreCase
(
ncVar
,
CDM
.
LONG_NAME
,
ncVar
.
getShortName
(
)
)
;
long_name
=
long_name
+
"-"
+
dim
.
getShortName
(
)
;
ds
.
addVariableAttribute
(
varNew
,
new
Attribute
(
CDM
.
LONG_NAME
,
long_name
)
)
;
ds
.
addVariable
(
,
varNew
)
;
parseInfo
.
format
(
"Created New Variable as section = "
)
;
varNew
.
getNameAndDimensions
(
parseInfo
,
true
,
false
)
;
parseInfo
.
format
(
"%n"
)
;
count
+=
dim
.
getLength
(
)
;
}
}

create new variables as sections of ncVar


private CoordinateAxis makeTimeCoordAxisFromReference(NetcdfDataset ds, Variable timeVar, Array vals) {
    Variable refVar = ds.findVariable("reftime");
    if (refVar == null) return null;
    double refValue;
    try {
      Array refArray = refVar.read();
      refValue = refArray.getDouble(refArray.getIndex()); // get the first value
    } catch (IOException ioe) {
      return null;
    }
    if (refValue == N3iosp.NC_FILL_DOUBLE)
      return null;

    // construct the values array - make it a double to be safe
    Array dvals = Array.factory(DataType.DOUBLE, vals.getShape());
    IndexIterator diter = dvals.getIndexIterator();
    IndexIterator iiter = vals.getIndexIterator();
    while (iiter.hasNext())
      diter.setDoubleNext(iiter.getDoubleNext() + refValue); // add reftime to each of the values

    String units = ds.findAttValueIgnoreCase(refVar, CDM.UNITS, "seconds since 1970-1-1 00:00:00");
    units = normalize(units);
    String desc = "synthesized time coordinate from reftime, valtimeMINUSreftime";
    CoordinateAxis1D timeCoord = new CoordinateAxis1D(ds, null, "timeCoord", DataType.DOUBLE, "record", units, desc);

    timeCoord.setCachedData(dvals, true);

    parseInfo.format("Created Time Coordinate Axis From Reference = ");
    timeCoord.getNameAndDimensions(parseInfo, true, false);
    parseInfo.format("%n");

    return timeCoord;
  }

construct time coordinate from reftime variable


public static float bitShave(float value, int bitMask) {
    if (Float.isNaN(value)) return value;   // ??

    int bits = Float.floatToRawIntBits(value);
    int shave = bits & bitMask;
    return Float.intBitsToFloat(shave);
  }

public
static
float
bitShave
(
float
value
,
int
bitMask
)
{
if
(
Float
.
isNaN
(
value
)
)
return
value
;
// ??
int
bits
=
Float
.
floatToRawIntBits
(
value
)
;
int
shave
=
bits
&
bitMask
;
return
Float
.
intBitsToFloat
(
shave
)
;
}

Shave n bits off the float
@param value    original floating point
@param bitMask  bitMask from getBitMask()
@return modified float


public static void main(String[] args) {
    String fileIn = (args.length > 0) ? args[0] : "Q:/cdmUnitTest/formats/grib2/LMPEF_CLM_050518_1200.grb";
    String fileOut = (args.length > 1) ? args[1] : "C:/tmp/ds.mint.bi";

    try (GribToNetcdfWriter writer = new GribToNetcdfWriter(fileIn, fileOut)) {
      writer.write();

    } catch (IOException e) {
      e.printStackTrace();
    }

  }

Write Grib file to a netcdf4 file. Experimental.

Write Grib file to a netcdf4 file . Experimental .


static public NetcdfDataset wrap(NetcdfFile ncfile, Set<Enhance> mode) throws IOException {
    if (ncfile instanceof NetcdfDataset) {
      NetcdfDataset ncd = (NetcdfDataset) ncfile;
      if (!ncd.enhanceNeeded(mode))
        return (NetcdfDataset) ncfile;
    }

    // enhancement requires wrappping, to not modify underlying dataset, eg if cached
    // perhaps need a method variant that allows the ncfile to be modified
    return new NetcdfDataset(ncfile, mode);
  }

Make NetcdfFile into NetcdfDataset with given enhance mode

@param ncfile wrap this
@param mode   using this enhance mode (may be null, meaning no enhance)
@return NetcdfDataset wrapping the given ncfile
@throws IOException on io error