package ucar.nc2.grib.grib2;
import ucar.nc2.grib.GribData;
import ucar.nc2.grib.QuasiRegular;
import ucar.nc2.time.CalendarDate;
import ucar.unidata.io.RandomAccessFile;
import ucar.unidata.util.StringUtil2;
import java.io.IOException;
import java.util.Formatter;
/**
* Read one Record from a GRIB-2 files
*
* @author caron
* @since 3/28/11
*/
public class Grib2Record {
//////////////////////////////////////////////////////////////
private final Grib2SectionIndicator is;
private final Grib2SectionIdentification id;
private Grib2SectionLocalUse lus; // local use section
private Grib2SectionGridDefinition gdss;
private Grib2SectionProductDefinition pdss;
private Grib2SectionDataRepresentation drss;
private Grib2SectionBitMap bms;
private Grib2SectionData dataSection;
private Grib2Pds pds2 = null;
private Grib2Gds gds2 = null;
private final byte[] header; // anything in between the records - eg idd header
private int file; // for multiple files in same dataset
private boolean bmsReplaced;
private int scanMode;
public int repeat; // debug = see Grib2Report.doDrsSummary
/**
* Construction for Grib2Record.
*
* @param header Grib header
* @param is Grib2IndicatorSection
* @param id Grib2IdentificationSection
* @param lus raw bytes of local use section
* @param gdss Grib2GridDefinitionSection
* @param pdss Grib2ProductDefinitionSection
* @param drs Grib2SectionDataRepresentation
* @param bms Grib2SectionBitMap
* @param dataSection Grib2SectionData
* @param bmsReplaced Grib2SectionData
* @param scanMode from GDS of this record
*/
public Grib2Record(byte[] header, Grib2SectionIndicator is,
Grib2SectionIdentification id,
Grib2SectionLocalUse lus,
Grib2SectionGridDefinition gdss,
Grib2SectionProductDefinition pdss,
Grib2SectionDataRepresentation drs,
Grib2SectionBitMap bms,
Grib2SectionData dataSection,
boolean bmsReplaced,
int scanMode) {
this.header = header;
this.is = is;
this.id = id;
this.lus = lus;
this.gdss = gdss;
this.pdss = pdss;
this.drss = drs;
this.bms = bms;
this.dataSection = dataSection;
this.bmsReplaced = bmsReplaced;
// stored in index file after 4.5 2/6/2014, otherwise equals Grib2Index.ScanModeMissing, so get it from the GDS, which may have wrong one
this.scanMode = scanMode;
if (scanMode == Grib2Index.ScanModeMissing && gdss != null) {
this.scanMode = gdss.getGDS().getScanMode();
}
}
// copy constructor
Grib2Record(Grib2Record from) {
this.header = from.header;
this.is = from.is;
this.id = from.id;
this.lus = from.lus;
this.gdss = from.gdss;
this.pdss = from.pdss;
this.drss = from.drss;
this.bms = from.bms;
this.dataSection = from.dataSection;
this.repeat = from.repeat;
this.bmsReplaced = from.bmsReplaced;
this.scanMode = from.scanMode;
}
public byte[] getHeader() {
return header;
}
public Grib2SectionIndicator getIs() {
return is;
}
public Grib2SectionIdentification getId() {
return id;
}
public boolean hasLocalUseSection() {
return lus != null && lus.getRawBytes() != null;
}
public Grib2SectionLocalUse getLocalUseSection() {
return lus;
}
public Grib2SectionGridDefinition getGDSsection() {
return gdss;
}
public Grib2SectionProductDefinition getPDSsection() {
return pdss;
}
public Grib2SectionDataRepresentation getDataRepresentationSection() {
return drss;
}
public Grib2SectionBitMap getBitmapSection() {
return bms;
}
public Grib2SectionData getDataSection() {
return dataSection;
}
public int getDiscipline() {
return is.getDiscipline();
}
public CalendarDate getReferenceDate() {
return id.getReferenceDate();
}
public Grib2Pds getPDS() {
if (pds2 == null)
pds2 = pdss.getPDS();
return pds2;
}
public Grib2Gds getGDS() {
if (gds2 == null)
gds2 = gdss.getGDS();
return gds2;
}
public int getScanMode() {
return scanMode;
}
public void show(Formatter f) {
f.format("discipline=%d ", is.getDiscipline());
Grib2Pds pds = getPDS();
pds.show(f);
}
//////////////////////////////////////////
// setters used by repeating records
public void setLus(Grib2SectionLocalUse lus) {
this.lus = lus;
}
public void setGdss(Grib2SectionGridDefinition gdss) {
this.gdss = gdss;
}
public void setPdss(Grib2SectionProductDefinition pdss) {
this.pdss = pdss;
}
public void setDrs(Grib2SectionDataRepresentation drs) {
this.drss = drs;
}
public void setBms(Grib2SectionBitMap bms, boolean replaced) {
this.bms = bms;
this.bmsReplaced = replaced;
}
public void setDataSection(Grib2SectionData dataSection) {
this.dataSection = dataSection;
}
public int getFile() {
return file;
}
public void setFile(int file) {
this.file = file;
}
public boolean isBmsReplaced() {
return bmsReplaced;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Grib2Record{");
sb.append("file=").append(file);
sb.append(", ref=").append(getReferenceDate());
sb.append(", dataPos=").append(dataSection.getStartingPosition());
sb.append('}');
return sb.toString();
}
// isolate dependencies here - in case we have a "minimal I/O" mode where not all fields are available
public float[] readData(RandomAccessFile raf) throws IOException {
Grib2Gds gds = getGDS();
Grib2DataReader2 reader = new Grib2DataReader2(drss.getDataTemplate(), gdss.getNumberPoints(), drss.getDataPoints(),
getScanMode(), gds.getNxRaw(), dataSection.getStartingPosition(), dataSection.getMsgLength());
Grib2Drs gdrs = drss.getDrs(raf);
float[] data = reader.getData(raf, bms, gdrs);
if (gds.isThin())
data = QuasiRegular.convertQuasiGrid(data, gds.getNptsInLine(), gds.getNxRaw(), gds.getNyRaw(), GribData.getInterpolationMethod());
lastRecordRead = this;
return data;
}
// debugging - do not use
public int[] readRawData(RandomAccessFile raf) throws IOException {
Grib2Gds gds = getGDS();
Grib2DataReader2 reader = new Grib2DataReader2(drss.getDataTemplate(), gdss.getNumberPoints(), drss.getDataPoints(),
getScanMode(), gds.getNxRaw(), dataSection.getStartingPosition(), dataSection.getMsgLength());
Grib2Drs gdrs = drss.getDrs(raf);
return reader.getRawData(raf, bms, gdrs);
}
// debugging - do not use
public Grib2Drs.Type40 readDataTest(RandomAccessFile raf) throws IOException {
Grib2Gds gds = getGDS();
Grib2DataReader2 reader = new Grib2DataReader2(drss.getDataTemplate(), gdss.getNumberPoints(), drss.getDataPoints(),
getScanMode(), gds.getNxRaw(), dataSection.getStartingPosition(), dataSection.getMsgLength());
Grib2Drs gdrs = drss.getDrs(raf);
if (gdrs instanceof Grib2Drs.Type40) {
reader.getData(raf, bms, gdrs);
return (Grib2Drs.Type40) gdrs;
}
return null;
}
/**
* Read data array
*
* @param raf from this RandomAccessFile
* @param drsPos Grib2SectionDataRepresentation starts here
* @return data as float[] array
* @throws IOException on read error
*/
public float[] readData(RandomAccessFile raf, long drsPos) throws IOException {
raf.seek(drsPos);
Grib2SectionDataRepresentation drs = new Grib2SectionDataRepresentation(raf);
Grib2SectionBitMap bms = new Grib2SectionBitMap(raf);
Grib2SectionData dataSection = new Grib2SectionData(raf);
Grib2Gds gds = getGDS();
Grib2DataReader2 reader = new Grib2DataReader2(drs.getDataTemplate(), gdss.getNumberPoints(), drs.getDataPoints(),
getScanMode(), gds.getNxRaw(), dataSection.getStartingPosition(), dataSection.getMsgLength());
Grib2Drs gdrs = drs.getDrs(raf);
float[] data = reader.getData(raf, bms, gdrs);
if (gds.isThin())
data = QuasiRegular.convertQuasiGrid(data, gds.getNptsInLine(), gds.getNxRaw(), gds.getNyRaw(), GribData.getInterpolationMethod());
lastRecordRead = this;
return data;
}
// float[] data = Grib2Record.readData(rafData, dr.drsPos, vindex.group.hcs.gdsNumberPoints, vindex.group.hcs.scanMode, vindex.group.hcs.nx);
/**
* Read data array: use when you want to be independent of the GribRecord
*
* @param raf from this RandomAccessFile
* @param drsPos Grib2SectionDataRepresentation starts here
* @param bmsPos if non-zero, use the bms that starts here
* @param gdsNumberPoints gdss.getNumberPoints()
* @param scanMode gds.scanMode
* @param nx gds.nx
* @return data as float[] array
* @throws IOException on read error
*/
static public float[] readData(RandomAccessFile raf, long drsPos, long bmsPos, int gdsNumberPoints, int scanMode, int nx, int ny, int[] nptsInLine) throws IOException {
raf.seek(drsPos);
Grib2SectionDataRepresentation drs = new Grib2SectionDataRepresentation(raf);
Grib2SectionBitMap bms = new Grib2SectionBitMap(raf);
Grib2SectionData dataSection = new Grib2SectionData(raf);
if (bmsPos > 0)
bms = Grib2SectionBitMap.factory(raf, bmsPos);
Grib2DataReader2 reader = new Grib2DataReader2(drs.getDataTemplate(), gdsNumberPoints, drs.getDataPoints(),
scanMode, nx, dataSection.getStartingPosition(), dataSection.getMsgLength());
Grib2Drs gdrs = drs.getDrs(raf);
//return reader.getData(raf, bitmap, gdrs);
float[] data = reader.getData(raf, bms, gdrs);
if (nptsInLine != null)
data = QuasiRegular.convertQuasiGrid(data, nptsInLine, nx, ny, GribData.getInterpolationMethod());
if (getlastRecordRead)
lastRecordRead = Grib2RecordScanner.findRecordByDrspos(raf, drsPos);
return data;
}
public void check(RandomAccessFile raf, Formatter f) throws IOException {
long messLen = is.getMessageLength();
long startPos = is.getStartPos();
long endPos = is.getEndPos();
if (endPos > raf.length()) {
f.format("End of GRIB message (start=%d len=%d) end=%d > file.length=%d for %s%n", startPos, messLen , endPos, raf.length(), raf.getLocation());
return;
}
raf.seek(endPos-4);
for (int i = 0; i < 4; i++) {
if (raf.read() != 55) {
String clean = StringUtil2.cleanup(header);
if (clean.length() > 40) clean = clean.substring(0,40) + "...";
f.format("Missing End of GRIB message (start=%d len=%d) end=%d header= %s for %s (len=%d)%n", startPos, messLen, endPos, clean, raf.getLocation(), raf.length());
break;
}
}
long dataLen = dataSection.getMsgLength();
long dataStart = dataSection.getStartingPosition();
long dataEnd = dataStart + dataLen;
if (dataEnd > raf.length()) {
f.format("GRIB data section (start=%d len=%d) end=%d > file.length=%d for %s%n", dataStart, dataLen, dataEnd, raf.length(), raf.getLocation());
return;
}
if (dataEnd > endPos) {
f.format("GRIB data section (start=%d len=%d) end=%d > message end=%d for %s%n", dataStart, dataLen, dataEnd, endPos, raf.getLocation());
}
}
public GribData.Info getBinaryDataInfo(RandomAccessFile raf) throws IOException {
GribData.Info info = this.drss.getDrs(raf).getBinaryDataInfo(raf);
info.bitmapLength = (bms == null) ? 0 : bms.getLength(raf);
info.msgLength = is.getMessageLength();
info.dataLength = dataSection.getMsgLength();
info.ndataPoints = drss.getDataPoints();
Grib2Gds gds = getGDS();
info.nPoints = gds.getNx() * gds.getNy();
return info;
}
// debugging do not use
public static boolean getlastRecordRead;
public static Grib2Record lastRecordRead;
}