package ucar.coord; import net.jcip.annotations.Immutable; import ucar.nc2.grib.GribUtils; import ucar.nc2.grib.TimeCoord; import ucar.nc2.grib.grib1.Grib1ParamTime; import ucar.nc2.grib.grib1.Grib1Record; import ucar.nc2.grib.grib1.Grib1SectionProductDefinition; import ucar.nc2.grib.grib1.tables.Grib1Customizer; import ucar.nc2.grib.grib2.Grib2Utils; import ucar.nc2.time.CalendarDate; import ucar.nc2.time.CalendarDateRange; import ucar.nc2.time.CalendarDateUnit; import ucar.nc2.grib.grib2.Grib2Pds; import ucar.nc2.grib.grib2.Grib2Record; import ucar.nc2.time.CalendarPeriod; import ucar.nc2.util.Indent; import ucar.nc2.util.Misc; import java.util.*; /** * Time coordinates that are offsets from the reference date (not intervals). * * @author caron * @since 11/24/13 */ @Immutable public class CoordinateTime extends CoordinateTimeAbstract implements Coordinate { private final List<Integer> offsetSorted; public CoordinateTime(int code, CalendarPeriod timeUnit, CalendarDate refDate, List<Integer> offsetSorted, int[] time2runtime) { super(code, timeUnit, refDate, time2runtime); this.offsetSorted = Collections.unmodifiableList(offsetSorted); } CoordinateTime(CoordinateTime org, CalendarDate refDate) { super(org.code, org.timeUnit, refDate, null); this.offsetSorted = org.getOffsetSorted(); } public List<Integer> getOffsetSorted() { return offsetSorted; } @Override public List<? extends Object> getValues() { return offsetSorted; } @Override public int getIndex(Object val) { return Collections.binarySearch(offsetSorted, (Integer) val); } @Override public Object getValue(int idx) { if (idx < 0 || idx >= offsetSorted.size()) return null; return offsetSorted.get(idx); } @Override public int getSize() { return offsetSorted.size(); } @Override public Type getType() { return Type.time; } @Override public int estMemorySize() { return 320 + getSize() * (16); } @Override public CalendarDateRange makeCalendarDateRange(ucar.nc2.time.Calendar cal) { CalendarDateUnit cdu = CalendarDateUnit.withCalendar(cal, periodName + " since " + refDate.toString()); CalendarDate start = cdu.makeCalendarDate(offsetSorted.get(0)); CalendarDate end = cdu.makeCalendarDate(offsetSorted.get(getSize()-1)); return CalendarDateRange.of(start, end); } @Override public void showInfo(Formatter info, Indent indent) { info.format("%s%s:", indent, getType()); for (Integer cd : offsetSorted) info.format(" %3d,", cd); info.format(" (%d) %n", offsetSorted.size()); if (time2runtime != null) info.format("%stime2runtime: %s", indent, Misc.showInts(time2runtime)); } @Override public void showCoords(Formatter info) { info.format("Time offsets: (%s) ref=%s %n", getUnit(), getRefDate()); for (Integer cd : offsetSorted) info.format(" %3d%n", cd); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CoordinateTime that = (CoordinateTime) o; if (code != that.code) return false; if (!offsetSorted.equals(that.offsetSorted)) return false; return true; } @Override public int hashCode() { int result = offsetSorted.hashCode(); result = 31 * result + code; return result; } //////////////////////////////////////////////// protected CoordinateTimeAbstract makeBestFromComplete(int[] best, int n) { List<Integer> offsetSortedBest = new ArrayList<>(offsetSorted.size()); int[] time2runtimeBest = new int[n]; int count = 0; for (int i=0; i<best.length; i++) { int time = best[i]; if (time >= 0) { time2runtimeBest[count] = time; offsetSortedBest.add(offsetSorted.get(i)); count++; } } return new CoordinateTime(code, timeUnit, refDate, offsetSortedBest, time2runtimeBest); } /* public CoordinateTime makeBestTimeCoordinate(List<Double> runOffsets) { Set<Integer> values = new HashSet<>(); for (double runOffset : runOffsets) { for (Integer val : getOffsetSorted()) values.add((int) (runOffset + val)); // LOOK possible roundoff } List<Integer> offsetSorted = new ArrayList<>(values.size()); for (Object val : values) offsetSorted.add( (Integer) val); Collections.sort(offsetSorted); return new CoordinateTime(getCode(), getTimeUnit(), getRefDate(), offsetSorted); } /* * calculate which runtime to use, based on missing * @param runOffsets for each runtime, the offset from base time * @param coordBest best time coordinate, from convertBestTimeCoordinate * @param twot variable missing array * @return for each time in coordBest, which runtime to use, as 1-based index into runtime runOffsets (0 = missing) * public int[] makeTime2RuntimeMap(List<Double> runOffsets, CoordinateTime coordBest, TwoDTimeInventory twot) { int[] result = new int[ coordBest.getSize()]; Map<Integer, Integer> map = new HashMap<>(); // lookup coord val to index int count = 0; for (Integer val : coordBest.getOffsetSorted()) map.put(val, count++); int runIdx = 0; for (double runOffset : runOffsets) { int timeIdx = 0; for (Integer val : getOffsetSorted()) { if (twot == null || twot.getCount(runIdx, timeIdx) > 0) { // skip missing Integer bestVal = (int) (runOffset + val); Integer bestValIdx = map.get(bestVal); if (bestValIdx == null) throw new IllegalStateException(); result[bestValIdx] = runIdx+1; // use this partition; later ones override; 1-based so 0 = missing } timeIdx++; } runIdx++; } return result; } */ //////////////////////////////////////////// /* @Override public CoordinateBuilder makeBuilder() { return new Builder(code); } */ static public class Builder2 extends CoordinateBuilderImpl<Grib2Record> { private final int code; // pdsFirst.getTimeUnit() private final CalendarPeriod timeUnit; private final CalendarDate refDate; public Builder2(int code, CalendarPeriod timeUnit, CalendarDate refDate) { this.code = code; this.timeUnit = timeUnit; this.refDate = refDate; } @Override public Object extract(Grib2Record gr) { Grib2Pds pds = gr.getPDS(); int offset = pds.getForecastTime(); int tuInRecord = pds.getTimeUnit(); if (tuInRecord == code) { return offset; } else { CalendarPeriod period = Grib2Utils.getCalendarPeriod(tuInRecord); CalendarDate validDate = refDate.add(period.multiply(offset)); int newOffset = TimeCoord.getOffset(refDate, validDate, timeUnit); // offset in correct time unit return newOffset; } } @Override public Coordinate makeCoordinate(List<Object> values) { List<Integer> offsetSorted = new ArrayList<>(values.size()); for (Object val : values) offsetSorted.add( (Integer) val); Collections.sort(offsetSorted); return new CoordinateTime(code, timeUnit, refDate, offsetSorted, null); } } static public class Builder1 extends CoordinateBuilderImpl<Grib1Record> { final Grib1Customizer cust; final int code; // pdsFirst.getTimeUnit() final CalendarPeriod timeUnit; final CalendarDate refDate; public Builder1(Grib1Customizer cust, int code, CalendarPeriod timeUnit, CalendarDate refDate) { this.cust = cust; this.code = code; this.timeUnit = timeUnit; this.refDate = refDate; } @Override public Object extract(Grib1Record gr) { Grib1SectionProductDefinition pds = gr.getPDSsection(); Grib1ParamTime ptime = gr.getParamTime(cust); int offset = ptime.getForecastTime(); int tuInRecord = pds.getTimeUnit(); if (tuInRecord == code) { return offset; } else { CalendarDate validDate = GribUtils.getValidTime(refDate, tuInRecord, offset); int newOffset = TimeCoord.getOffset(refDate, validDate, timeUnit); return newOffset; } } @Override public Coordinate makeCoordinate(List<Object> values) { List<Integer> offsetSorted = new ArrayList<>(values.size()); for (Object val : values) offsetSorted.add( (Integer) val); Collections.sort(offsetSorted); return new CoordinateTime(code, timeUnit, refDate, offsetSorted, null); } } }