package nl.wldelft.util.io; import nl.wldelft.util.BinaryUtils; import nl.wldelft.util.ByteArrayUtils; import nl.wldelft.util.DateUtils; import nl.wldelft.util.FloatArrayUtils; import nl.wldelft.util.IOUtils; import nl.wldelft.util.NumberType; import nl.wldelft.util.ShortArrayUtils; import nl.wldelft.util.TextUtils; import nl.wldelft.util.coverage.Geometry; import nl.wldelft.util.coverage.GridUtils; import nl.wldelft.util.coverage.RegularGridGeometry; import nl.wldelft.util.geodatum.GeoDatum; import nl.wldelft.util.geodatum.GeoPoint; import nl.wldelft.util.geodatum.Wgs1984Point; import nl.wldelft.util.timeseries.DefaultTimeSeriesHeader; import nl.wldelft.util.timeseries.TimeSeriesContentHandler; import org.apache.log4j.Logger; import java.io.BufferedInputStream; import java.io.IOException; import java.nio.ByteOrder; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; public class MosaicRadarTimeSeriesParser implements BinaryParser<TimeSeriesContentHandler> { private static final Logger log = Logger.getLogger(MosaicRadarTimeSeriesParser.class); private Geometry geometry = null; private int nx1 = 0; private int ny1 = 0; private int nz1 = 0; private String projection = null; private int scale = 0; private int trulat1 = 0; private int trulat2 = 0; private int trulon = 0; private float nwLon = 0.0F; private float nwLat = 0.0F; private float xyScale = 0.0F; private float dx1 = 0.0F; private float dy2 = 0.0F; private int iBbMode = 0; private String varName = null; private String varUnit = null; private int varScale = 0; private int imissing = 0; private int nradars = 0; private String[] radarNames = null; private float dxyScale = 0.0F; private int size = 0; private long time = Long.MIN_VALUE; private float[] values = FloatArrayUtils.EMPTY_ARRAY; private byte[] byteBuffer = ByteArrayUtils.EMPTY_ARRAY; private short[] shortBuffer = ShortArrayUtils.EMPTY_ARRAY; private LittleEndianDataInputStream is = null; private TimeSeriesContentHandler contentHandler = null; @Override public void parse(BufferedInputStream inputStream, String virtualFileName, TimeSeriesContentHandler contentHandler) throws Exception { this.contentHandler = contentHandler; this.is = new LittleEndianDataInputStream(inputStream); parseHeader(); if (contentHandler.isCurrentTimeSeriesHeaderForCurrentTimeRejected()) return; if (values.length != geometry.size()) { values = new float[geometry.size()]; byteBuffer = new byte[geometry.size() * NumberType.INT16_SIZE]; shortBuffer = new short[geometry.size()]; } if (values.length != geometry.size()) { values = new float[geometry.size()]; byteBuffer = new byte[geometry.size() * NumberType.INT16_SIZE]; shortBuffer = new short[geometry.size()]; } // read all the complete grid at once for optimal performance is.readFully(byteBuffer); BinaryUtils.copy(byteBuffer, 0, size * NumberType.INT16_SIZE, shortBuffer, 0, size, ByteOrder.LITTLE_ENDIAN); if (is.available() > 0) log.error("Too many bytes available in " + virtualFileName); for (int i = 0; i < values.length; i++) { short shortValue = shortBuffer[i]; values[i] = shortValue == imissing ? Float.NaN : shortValue / varScale; } // content handle expect the rows from top to bottom GridUtils.reverseOrderRows(values, geometry.getCols(), geometry.getRows()); contentHandler.setCoverageValues(values); contentHandler.applyCurrentFields(); } @SuppressWarnings({"OverlyLongMethod"}) private void parseHeader() throws IOException { DefaultTimeSeriesHeader header = new DefaultTimeSeriesHeader(); int year = is.readInt(); int month = is.readInt(); int day = is.readInt(); int hour = is.readInt(); int minute = is.readInt(); int second = is.readInt(); Calendar gmtCalendar = new GregorianCalendar(DateUtils.GMT); gmtCalendar.set(year, month - 1, day, hour, minute, second); contentHandler.setTime(gmtCalendar.getTimeInMillis()); nx1 = is.readInt(); ny1 = is.readInt(); nz1 = is.readInt(); projection = IOUtils.readText(is, 4).trim(); scale = is.readInt(); trulat1 = is.readInt(); trulat2 = is.readInt(); trulon = is.readInt(); nwLon = (float) is.readInt() / (float) scale; nwLat = (float) is.readInt() / (float) scale; xyScale = is.readInt(); int dx1int = is.readInt(); int dy2int = is.readInt(); dxyScale = (float) is.readInt(); dx1 = (float) dx1int / dxyScale; dy2 = (float) dy2int / dxyScale; IOUtils.skipFully(is, 4 * nz1); iBbMode = is.readInt(); IOUtils.skipFully(is, 4 * 9); IOUtils.skipFully(is, 4); varName = IOUtils.readText(is, 20); varUnit = IOUtils.readText(is, 6); varScale = is.readInt(); imissing = is.readInt(); nradars = is.readInt(); if (nradars > 0) { radarNames = new String[nradars]; for (int i = 0; i < nradars; i++) { radarNames[i] = IOUtils.readText(is, 4).trim(); } } size = nx1 * ny1 * nz1; GeoPoint firstCellCenter = new Wgs1984Point(nwLat - dy2 / 2, nwLon + dx1 / 2); geometry = RegularGridGeometry.create(GeoDatum.WGS_1984, firstCellCenter, dx1, dy2, ny1, nx1); contentHandler.setGeometry(geometry); contentHandler.setValueResolution(1.0f / varScale); header.setParameterId(varName); header.setUnit(varUnit); contentHandler.setTimeSeriesHeader(header); debugHeader(); } private void debugHeader() { if (!log.isDebugEnabled()) return; SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); dateFormat.setTimeZone(DateUtils.GMT); List<String> header = new ArrayList<String>(10); header.add(dateFormat.format(new Date(time))); header.add("nx1 = " + nx1); header.add("ny1 = " + ny1); header.add("nz1 = " + nz1); header.add("size = " + size); header.add("projection = " + projection); header.add("scale = " + scale); header.add("trulat1 = " + trulat1); header.add("trulat2 = " + trulat2); header.add("trulon = " + trulon); header.add("nw_lon = " + nwLon); header.add("nw_lat = " + nwLat); header.add("xy_scale = " + xyScale); header.add("dx1 = " + dx1); header.add("dy2 = " + dy2); header.add("i_bb_mode = " + iBbMode); header.add("varName = " + varName); header.add("varUnit = " + varUnit); header.add("var_scale = " + varScale); header.add("imissing = " + imissing); header.add("nradars = " + nradars); if (radarNames != null) { for (int i = 0; i < radarNames.length; i++) { String radarName = radarNames[i]; header.add("radarName = " + radarName); } } header.add("dxyScale = " + dxyScale); log.debug(TextUtils.join(header, '\n')); } }