/*
* Copyright 1998-2015 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.nc2.iosp.gempak;
import ucar.ma2.*;
import ucar.nc2.*;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.unidata.io.RandomAccessFile;
import ucar.unidata.util.StringUtil2;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* An IOSP for Gempak Surface data.
*
* @author dmurray
*/
public class GempakSurfaceIOSP extends GempakStationFileIOSP {
/**
* Make the station reader
*
* @return GempakSurfaceFileReader
*/
protected AbstractGempakStationFileReader makeStationReader() {
return new GempakSurfaceFileReader();
}
/**
* Is this a valid file?
*
* @param raf RandomAccessFile to check
* @return true if a valid Gempak grid file
* @throws IOException problem reading file
*/
public boolean isValidFile(RandomAccessFile raf) throws IOException {
if (!super.isValidFile(raf)) {
return false;
}
// TODO: handle other types of surface files
return gemreader.getFileSubType()
.equals(GempakSurfaceFileReader.STANDARD) || gemreader
.getFileSubType().equals(GempakSurfaceFileReader.SHIP);
}
/**
* Get the file type id
*
* @return the file type id
*/
public String getFileTypeId() {
return "GempakSurface";
}
/**
* Get the file type description
*
* @return the file type description
*/
public String getFileTypeDescription() {
return "GEMPAK Surface Obs Data";
}
/**
* Get the CF feature type
*
* @return the feature type
*/
public String getCFFeatureType() {
if (gemreader.getFileSubType().equals(GempakSurfaceFileReader.SHIP)) {
return CF.FeatureType.point.toString();
}
return CF.FeatureType.timeSeries.toString();
}
/**
* Read the data for the variable
*
* @param v2 Variable to read
* @param section section infomation
* @return Array of data
* @throws IOException problem reading from file
* @throws InvalidRangeException invalid Range
*/
public Array readData(Variable v2, Section section)
throws IOException, InvalidRangeException {
if (gemreader == null) {
return null;
}
//System.out.println("looking for " + v2);
//System.out.println("Section = " + section);
//Trace.call1("GEMPAKSIOSP: readData");
Array array = null;
if (gemreader.getFileSubType().equals(GempakSurfaceFileReader.SHIP)) {
array = readShipData(v2, section);
} else if (gemreader.getFileSubType().equals(
GempakSurfaceFileReader.STANDARD)) {
array = readStandardData(v2, section);
} else { // climate data
//array = readClimateData(v2, section);
}
// long took = System.currentTimeMillis() - start;
// System.out.println(" read data took=" + took + " msec ");
//Trace.call2("GEMPAKSIOSP: readData");
return array;
}
/**
* Read in the data for the variable. In this case, it should be
* a Structure. The section should be rank 2 (station, time).
*
* @param v2 variable to read
* @param section section of the variable
* @return the array of data
* @throws IOException problem reading the file
*/
private Array readStandardData(Variable v2, Section section)
throws IOException {
Array array = null;
if (v2 instanceof Structure) {
List<GempakParameter> params =
gemreader.getParameters(GempakSurfaceFileReader.SFDT);
Structure pdata = (Structure) v2;
StructureMembers members = pdata.makeStructureMembers();
List<StructureMembers.Member> mbers =
members.getMembers();
int i = 0;
int numBytes = 0;
int totalNumBytes = 0;
for (StructureMembers.Member member : mbers) {
member.setDataParam(4 * i++);
numBytes = member.getDataType().getSize();
totalNumBytes += numBytes;
}
// one member is a byte
members.setStructureSize(totalNumBytes);
float[] missing = new float[mbers.size()];
int missnum = 0;
for (Variable v : pdata.getVariables()) {
Attribute att = v.findAttribute("missing_value");
missing[missnum++] = (att == null)
? GempakConstants.RMISSD
: att.getNumericValue().floatValue();
}
//int num = 0;
Range stationRange = section.getRange(0);
Range timeRange = section.getRange(1);
int size = stationRange.length() * timeRange.length();
// Create a ByteBuffer using a byte array
byte[] bytes = new byte[totalNumBytes * size];
ByteBuffer buf = ByteBuffer.wrap(bytes);
array = new ArrayStructureBB(members, new int[]{size}, buf, 0);
//Trace.call1("GEMPAKSIOSP: readStandardData", section.toString());
for (int y = stationRange.first(); y <= stationRange.last();
y += stationRange.stride()) {
for (int x = timeRange.first(); x <= timeRange.last();
x += timeRange.stride()) {
GempakFileReader.RData vals =
gemreader.DM_RDTR(x + 1, y + 1,
GempakSurfaceFileReader.SFDT);
if (vals == null) {
int k = 0;
for (StructureMembers.Member member : mbers) {
if (member.getDataType().equals(DataType.FLOAT)) {
buf.putFloat(missing[k]);
} else {
buf.put((byte) 1);
}
k++;
}
} else {
float[] reals = vals.data;
int var = 0;
for (GempakParameter param : params) {
if (members.findMember(param.getName()) != null) {
buf.putFloat(reals[var]);
}
var++;
}
// always add the missing flag
buf.put((byte) 0);
}
}
}
//Trace.call2("GEMPAKSIOSP: readStandardData");
}
return array;
}
/**
* Read in the data for the record variable. In this case, it should be
* a Structure of record dimension. We can handle a subset of the
* variables in a structure.
*
* @param v2 variable to read
* @param section section of the variable
* @return the array of data
* @throws IOException problem reading the file
*/
private Array readShipData(Variable v2, Section section)
throws IOException {
Array array = null;
if (v2 instanceof Structure) {
List<GempakParameter> params =
gemreader.getParameters(GempakSurfaceFileReader.SFDT);
Structure pdata = (Structure) v2;
StructureMembers members = pdata.makeStructureMembers();
List<StructureMembers.Member> mbers = members.getMembers();
int ssize = 0;
//int stnVarNum = 0;
List<String> stnKeyNames = gemreader.getStationKeyNames();
for (StructureMembers.Member member : mbers) {
if (stnKeyNames.contains(member.getName())) {
int varSize = getStnVarSize(member.getName());
member.setDataParam(ssize);
ssize += varSize;
} else if (member.getName().equals(TIME_VAR)) {
member.setDataParam(ssize);
ssize += 8;
} else if (member.getName().equals(MISSING_VAR)) {
member.setDataParam(ssize);
ssize += 1;
} else {
member.setDataParam(ssize);
ssize += 4;
}
}
members.setStructureSize(ssize);
// TODO: figure out how to get the missing value for data
//float[] missing = new float[mbers.size()];
//int missnum = 0;
//for (Variable v : pdata.getVariables()) {
// Attribute att = v.findAttribute("missing_value");
// missing[missnum++] = (att == null)
// ? GempakConstants.RMISSD
// : att.getNumericValue().floatValue();
//}
Range recordRange = section.getRange(0);
int size = recordRange.length();
// Create a ByteBuffer using a byte array
byte[] bytes = new byte[ssize * size];
ByteBuffer buf = ByteBuffer.wrap(bytes);
array = new ArrayStructureBB(members, new int[]{size}, buf, 0);
List<GempakStation> stationList = gemreader.getStations();
List<Date> dateList = gemreader.getDates();
boolean needToReadData = !pdata.isSubset();
if (!needToReadData) { // subset, see if we need some param data
for (GempakParameter param : params) {
if (members.findMember(param.getName()) != null) {
needToReadData = true;
break;
}
}
}
//boolean hasTime = (members.findMember(TIME_VAR) != null);
//Trace.call1("GEMPAKSIOSP: readShipData", section.toString());
// fill out the station information
for (int x = recordRange.first(); x <= recordRange.last();
x += recordRange.stride()) {
GempakStation stn = stationList.get(x);
for (String varname : stnKeyNames) {
if (members.findMember(varname) == null) {
continue;
}
String temp = null;
switch (varname) {
case GempakStation.STID:
temp = StringUtil2.padRight(stn.getName(), 8);
break;
case GempakStation.STNM:
buf.putInt(stn.getSTNM());
break;
case GempakStation.SLAT:
buf.putFloat((float) stn.getLatitude());
break;
case GempakStation.SLON:
buf.putFloat((float) stn.getLongitude());
break;
case GempakStation.SELV:
buf.putFloat((float) stn.getAltitude());
break;
case GempakStation.STAT:
temp = StringUtil2.padRight(stn.getSTAT(), 2);
break;
case GempakStation.COUN:
temp = StringUtil2.padRight(stn.getCOUN(), 2);
break;
case GempakStation.STD2:
temp = StringUtil2.padRight(stn.getSTD2(), 4);
break;
case GempakStation.SPRI:
buf.putInt(stn.getSPRI());
break;
case GempakStation.SWFO:
temp = StringUtil2.padRight(stn.getSWFO(), 4);
break;
case GempakStation.WFO2:
temp = StringUtil2.padRight(stn.getWFO2(), 4);
break;
}
if (temp != null) {
buf.put(temp.getBytes(CDM.utf8Charset));
}
}
if (members.findMember(TIME_VAR) != null) {
// put in the time
Date time = dateList.get(x);
buf.putDouble(time.getTime() / 1000.d);
}
if (needToReadData) {
int column = stn.getIndex();
GempakFileReader.RData vals =
gemreader.DM_RDTR(1, column,
GempakSurfaceFileReader.SFDT);
if (vals == null) {
for (GempakParameter param : params) {
if (members.findMember(param.getName()) != null) {
buf.putFloat(GempakConstants.RMISSD);
}
}
buf.put((byte) 1);
} else {
float[] reals = vals.data;
int var = 0;
for (GempakParameter param : params) {
if (members.findMember(param.getName()) != null) {
buf.putFloat(reals[var]);
}
var++;
}
buf.put((byte) 0);
}
}
}
//Trace.call2("GEMPAKSIOSP: readShipData");
}
return array;
}
/**
* Build the netCDF file
*
* @throws IOException problem reading the file
*/
protected void fillNCFile() throws IOException {
String fileType = gemreader.getFileSubType();
switch (fileType) {
case GempakSurfaceFileReader.STANDARD:
buildStandardFile();
break;
case GempakSurfaceFileReader.SHIP:
buildShipFile();
break;
default:
buildClimateFile();
break;
}
}
/**
* Build a standard station structure
*/
private void buildStandardFile() {
// Build station list
List<GempakStation> stations = gemreader.getStations();
//Trace.msg("GEMPAKSIOSP: now have " + stations.size() + " stations");
Dimension station = new Dimension("station", stations.size(), true);
ncfile.addDimension(null, station);
ncfile.addDimension(null, DIM_LEN8);
ncfile.addDimension(null, DIM_LEN4);
ncfile.addDimension(null, DIM_LEN2);
List<Variable> stationVars = makeStationVars(stations, station);
// loop through and add to ncfile
for (Variable stnVar : stationVars) {
ncfile.addVariable(null, stnVar);
}
// Build variable list (var(station,time))
// time
List<Date> timeList = gemreader.getDates();
int numTimes = timeList.size();
Dimension times = new Dimension(TIME_VAR, numTimes, true);
ncfile.addDimension(null, times);
Array varArray;
Variable timeVar = new Variable(ncfile, null, null, TIME_VAR, DataType.DOUBLE, TIME_VAR);
timeVar.addAttribute(new Attribute(CDM.UNITS, "seconds since 1970-01-01 00:00:00"));
timeVar.addAttribute(new Attribute("long_name", TIME_VAR));
varArray = new ArrayDouble.D1(numTimes);
int i = 0;
for (Date date : timeList) {
((ArrayDouble.D1) varArray).set(i, date.getTime() / 1000.d);
i++;
}
timeVar.setCachedData(varArray, false);
ncfile.addVariable(null, timeVar);
List<Dimension> stationTime = new ArrayList<>();
stationTime.add(station);
stationTime.add(times);
// TODO: handle other parts
Structure sfData = makeStructure(GempakSurfaceFileReader.SFDT,
stationTime, true);
if (sfData == null) {
return;
}
sfData.addAttribute(new Attribute(CF.COORDINATES, "time SLAT SLON SELV"));
ncfile.addVariable(null, sfData);
ncfile.addAttribute(
null,
new Attribute(
"CF:featureType",
CF.FeatureType.timeSeries.toString()));
}
/**
* Build a ship station structure. Here the columns are the
* stations/reports and the rows (1) are the reports.
*/
private void buildShipFile() {
// Build variable list (var(station,time))
List<GempakStation> stations = gemreader.getStations();
int numObs = stations.size();
//Trace.msg("GEMPAKSIOSP: now have " + numObs + " stations");
Dimension record = new Dimension("record", numObs, true,
(numObs == 0), false);
ncfile.addDimension(null, record);
List<Dimension> records = new ArrayList<>(1);
records.add(record);
// time
Variable timeVar = new Variable(ncfile, null, null, TIME_VAR,
DataType.DOUBLE, null);
timeVar.addAttribute(
new Attribute(CDM.UNITS, "seconds since 1970-01-01 00:00:00"));
timeVar.addAttribute(new Attribute("long_name", TIME_VAR));
ncfile.addDimension(null, DIM_LEN8);
ncfile.addDimension(null, DIM_LEN4);
ncfile.addDimension(null, DIM_LEN2);
List<Variable> stationVars = makeStationVars(stations, null);
List<GempakParameter> params =
gemreader.getParameters(GempakSurfaceFileReader.SFDT);
if (params == null) {
return;
}
Structure sVar = new Structure(ncfile, null, null, "Obs");
sVar.setDimensions(records);
// loop through and add to ncfile
boolean hasElevation = false;
for (Variable stnVar : stationVars) {
if (stnVar.getShortName().equals("SELV")) {
hasElevation = true;
}
sVar.addMemberVariable(stnVar);
}
sVar.addMemberVariable(timeVar);
for (GempakParameter param : params) {
Variable var = makeParamVariable(param, null);
sVar.addMemberVariable(var);
}
sVar.addMemberVariable(makeMissingVariable());
String coords = "Obs.time Obs.SLAT Obs.SLON";
if (hasElevation) {
coords = coords + " Obs.SELV";
}
sVar.addAttribute(new Attribute(CF.COORDINATES, coords));
ncfile.addVariable(null, sVar);
ncfile.addAttribute(null,
new Attribute("CF:featureType",
CF.FeatureType.point.toString()));
}
/**
* Build a ship station structure. Here the columns are the
* times and the rows are the stations.
*/
private void buildClimateFile() {
}
}