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'));
    }
}
  • No labels