All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ucar.nc2.dataset.CoordinateAxis1DTime Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2020 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */

package ucar.nc2.dataset;

import java.util.ArrayList;
import java.util.Date;
import java.util.Formatter;
import java.util.List;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ucar.ma2.Array;
import ucar.ma2.ArrayChar;
import ucar.ma2.ArrayObject;
import ucar.ma2.DataType;
import ucar.ma2.Index;
import ucar.ma2.IndexIterator;
import ucar.ma2.InvalidRangeException;
import ucar.ma2.Range;
import ucar.nc2.Group;
import ucar.nc2.constants.AxisType;
import ucar.nc2.constants._Coordinate;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateFormatter;
import ucar.nc2.time.CalendarDateRange;
import ucar.nc2.units.TimeUnit;
import ucar.nc2.Dimension;
import ucar.nc2.Attribute;
import ucar.nc2.util.NamedAnything;
import ucar.nc2.util.NamedObject;
import java.io.IOException;
import ucar.nc2.units.DateRange;

/**
 * A 1-dimensional Coordinate Axis representing Calendar time.
 * Its coordinate values can be represented as Dates.
 * 

* May use udunit dates, or ISO Strings. * * @author caron */ public class CoordinateAxis1DTime extends CoordinateAxis1D { private static final Logger logger = LoggerFactory.getLogger(CoordinateAxis1DTime.class); public static CoordinateAxis1DTime factory(NetcdfDataset ncd, VariableDS org, Formatter errMessages) throws IOException { if (org instanceof CoordinateAxis1DTime) return (CoordinateAxis1DTime) org; if (org.getDataType() == DataType.CHAR) return new CoordinateAxis1DTime(ncd, org, errMessages, org.getDimension(0).getShortName()); else if (org.getDataType() == DataType.STRING) return new CoordinateAxis1DTime(ncd, org, errMessages, org.getDimensionsString()); else return new CoordinateAxis1DTime(ncd, org, errMessages); } //////////////////////////////////////////////////////////////// // for section and slice /** @deprecated Use {@link #toBuilder()} */ @Deprecated @Override protected CoordinateAxis1DTime copy() { return new CoordinateAxis1DTime(this.ncd, this); } /** @deprecated Use CoordinateAxis1DTime.toBuilder() */ @Deprecated // copy constructor private CoordinateAxis1DTime(NetcdfDataset ncd, CoordinateAxis1DTime org) { super(ncd, org); helper = org.helper; this.cdates = org.cdates; } @Override public CoordinateAxis1DTime section(Range r) throws InvalidRangeException { CoordinateAxis1DTime s = (CoordinateAxis1DTime) super.section(r); List cdates = getCalendarDates(); List cdateSection = new ArrayList<>(cdates.size()); for (int idx : r) cdateSection.add(cdates.get(idx)); s.cdates = cdateSection; return s; } /** * Get the the ith CalendarDate. * * @param idx index * @return the ith CalendarDate */ public CalendarDate getCalendarDate(int idx) { List cdates = getCalendarDates(); // in case we want to lazily evaluate return cdates.get(idx); } /** * Get calendar date range * * @return calendar date range */ public CalendarDateRange getCalendarDateRange() { List cd = getCalendarDates(); int last = cd.size(); return (last > 0) ? CalendarDateRange.of(cd.get(0), cd.get(last - 1)) : null; } @Override public List getNames() { List cdates = getCalendarDates(); List names = new ArrayList<>(cdates.size()); for (CalendarDate cd : cdates) names.add(new NamedAnything(CalendarDateFormatter.toDateTimeStringISO(cd), getShortName())); // "calendar date")); return names; } /** * only if isRegular() LOOK REDO * * @return time unit * @throws Exception on bad unit string */ public TimeUnit getTimeResolution() throws Exception { String tUnits = getUnitsString(); StringTokenizer stoker = new StringTokenizer(tUnits); double tResolution = getIncrement(); return new TimeUnit(tResolution, stoker.nextToken()); } /** * Given a Date, find the corresponding time index on the time coordinate axis. * Can only call this is hasDate() is true. * This will return *

    *
  • i, if time(i) <= d < time(i+1). *
  • 0, if d < time(0) *
  • n-1, if d > time(n-1), where n is length of time coordinates *
* * @param d date to look for * @return corresponding time index on the time coordinate axis * @throws UnsupportedOperationException is no time axis or isDate() false */ public int findTimeIndexFromCalendarDate(CalendarDate d) { List cdates = getCalendarDates(); // LOOK linear search, switch to binary int index = 0; while (index < cdates.size()) { if (d.compareTo(cdates.get(index)) < 0) break; index++; } return Math.max(0, index - 1); } /** * See if the given CalendarDate appears as a coordinate * * @param date test this * @return true if equals a coordinate */ public boolean hasCalendarDate(CalendarDate date) { List cdates = getCalendarDates(); for (CalendarDate cd : cdates) { // LOOK linear search, switch to binary if (date.equals(cd)) return true; } return false; } /** * Get the list of datetimes in this coordinate as CalendarDate objects. * * @return list of CalendarDates. */ public List getCalendarDates() { return cdates; } public CalendarDate[] getCoordBoundsDate(int i) { double[] intv = getCoordBounds(i); CalendarDate[] e = new CalendarDate[2]; e[0] = helper.makeCalendarDateFromOffset(intv[0]); e[1] = helper.makeCalendarDateFromOffset(intv[1]); return e; } public CalendarDate getCoordBoundsMidpointDate(int i) { double[] intv = getCoordBounds(i); double midpoint = (intv[0] + intv[1]) / 2; return helper.makeCalendarDateFromOffset(midpoint); } @Override protected void readValues() { // if orgVar DataType is not numeric (e.g. Char or String), read from the cdates array that was created when // the axis was created by this classes factory. if (this.orgDataType != null && !this.orgDataType.isNumeric()) { this.coords = cdates.stream().mapToDouble(cdate -> (double) cdate.getDifferenceInMsecs(cdates.get(0))).toArray(); // make sure parent methods do not try to read from the orgVar again this.wasRead = true; } else { super.readValues(); } } //////////////////////////////////////////////////////////////////////// /** * Constructor for CHAR or STRING variables. * Must be ISO dates. * * @param ncd the containing dataset * @param org the underlying Variable * @param errMessages put error messages here; may be null * @param dims list of dimensions * @throws IOException on read error * @throws IllegalArgumentException if cant convert coordinate values to a Date * @deprecated Use CoordinateAxis1DTime.builder() */ @Deprecated private CoordinateAxis1DTime(NetcdfDataset ncd, VariableDS org, Formatter errMessages, String dims) throws IOException { // Although we are dealing with a string or char variable, we're going to make a numeric valued time // coordinate axis, as long as we can transform the string values into a UDUNITS time value (e.g. // transform an ISO date/time string into something like "seconds since 1970-01-01 00:00:00 UTC) super(ncd, org.getParentGroupOrRoot(), org.getShortName(), DataType.DOUBLE, dims, org.getUnitsString(), org.getDescription()); // Need to set the original var its DataType, that way we can treat this coordinate axis as numeric properly // when we override the readValues() method and encounter a 1D time coordinate axis backing variable that is // of type String. this.orgVar = org; this.orgDataType = org.getDataType(); this.orgName = org.orgName; this.helper = new CoordinateAxisTimeHelper(getCalendarFromAttribute(), null); if (org.getDataType() == DataType.CHAR) cdates = makeTimesFromChar(org, errMessages); else cdates = makeTimesFromStrings(org, errMessages); for (Attribute att : org.attributes()) { addAttribute(att); } // look for _CoordinateAxisType attribute and use it if it is time or runtime Attribute coordAxisTypeAttr = org.attributes().findAttributeIgnoreCase(_Coordinate.AxisType); String attributeTypeName = coordAxisTypeAttr != null ? coordAxisTypeAttr.getStringValue() : null; if (attributeTypeName != null) { if (attributeTypeName.equalsIgnoreCase(AxisType.Time.name()) || attributeTypeName.equalsIgnoreCase(AxisType.RunTime.name())) { this.axisType = AxisType.getType(attributeTypeName); } else { logger.info("Attribute {} on variable {} is not a recognized time axis type.", _Coordinate.AxisType, org.getFullName()); } } this.setUnitsString("milliseconds since " + cdates.get(0).toString()); } private List makeTimesFromChar(VariableDS org, Formatter errMessages) throws IOException { int ncoords = (int) org.getSize(); int rank = org.getRank(); int strlen = org.getShape(rank - 1); ncoords /= strlen; List result = new ArrayList<>(ncoords); ArrayChar data = (ArrayChar) org.read(); ArrayChar.StringIterator ii = data.getStringIterator(); ArrayObject.D1 sdata = (ArrayObject.D1) Array.factory(DataType.STRING, new int[] {ncoords}); for (int i = 0; i < ncoords; i++) { String coordValue = ii.next(); CalendarDate cd = makeCalendarDateFromStringCoord(coordValue, org, errMessages); sdata.set(i, coordValue); result.add(cd); } setCachedData(sdata, true); return result; } private List makeTimesFromStrings(VariableDS org, Formatter errMessages) throws IOException { int ncoords = (int) org.getSize(); List result = new ArrayList<>(ncoords); ArrayObject data = (ArrayObject) org.read(); IndexIterator ii = data.getIndexIterator(); for (int i = 0; i < ncoords; i++) { String coordValue = (String) ii.getObjectNext(); CalendarDate cd = makeCalendarDateFromStringCoord(coordValue, org, errMessages); result.add(cd); } return result; } private CalendarDate makeCalendarDateFromStringCoord(String coordValue, VariableDS org, Formatter errMessages) { CalendarDate cd = helper.makeCalendarDateFromOffset(coordValue); if (cd == null) { if (errMessages != null) { errMessages.format("String time coordinate must be ISO formatted= %s%n", coordValue); logger.info("Char time coordinate must be ISO formatted= {} file = {}", coordValue, org.getDatasetLocation()); } throw new IllegalArgumentException(); } return cd; } /** * Constructor for numeric values - must have units * * @param ncd the containing dataset * @param org the underlying Variable * @throws IOException on read error * @deprecated Use CoordinateAxis1DTime.builder() */ @Deprecated private CoordinateAxis1DTime(NetcdfDataset ncd, VariableDS org, Formatter errMessages) throws IOException { super(ncd, org); this.helper = new CoordinateAxisTimeHelper(getCalendarFromAttribute(), getUnitsString()); // make the coordinates int ncoords = (int) org.getSize(); List result = new ArrayList<>(ncoords); if (org instanceof CoordinateAxis1D) { coords = ((CoordinateAxis1D) org).getCoordValues(); } else { Array data = org.read(); coords = (double[]) data.get1DJavaArray(DataType.DOUBLE); } this.wasRead = true; int count = 0; for (int i = 0; i < ncoords; i++) { double val = coords[i]; if (Double.isNaN(val)) continue; // WTF ?? result.add(helper.makeCalendarDateFromOffset(val)); count++; } // if we encountered NaNs, shorten it up if (count != ncoords) { Dimension localDim = Dimension.builder(getShortName(), count).setIsShared(false).build(); setDimension(0, localDim); // set the shortened values Array shortData = Array.factory(org.getDataType(), new int[] {count}); Index ima = shortData.getIndex(); int count2 = 0; for (int i = 0; i < ncoords; i++) { double val = coords[i]; if (Double.isNaN(val)) continue; shortData.setDouble(ima.set0(count2), val); count2++; } // we have to decouple from the original variable cache.reset(); setCachedData(shortData, true); } cdates = result; } /////////////////////////////////////////////////////// /** * Does not handle non-standard Calendars * * @deprecated use getCalendarDates() to correctly interpret calendars */ public java.util.Date[] getTimeDates() { List cdates = getCalendarDates(); Date[] timeDates = new Date[cdates.size()]; int index = 0; for (CalendarDate cd : cdates) timeDates[index++] = cd.toDate(); return timeDates; } /** * Does not handle non-standard Calendars * * @deprecated use getCalendarDate() */ public java.util.Date getTimeDate(int idx) { return getCalendarDate(idx).toDate(); } /** * Does not handle non-standard Calendars * * @deprecated use getCalendarDateRange() */ public DateRange getDateRange() { CalendarDateRange cdr = getCalendarDateRange(); return cdr.toDateRange(); } /** * Does not handle non-standard Calendars * * @deprecated use findTimeIndexFromCalendarDate */ public int findTimeIndexFromDate(java.util.Date d) { return findTimeIndexFromCalendarDate(CalendarDate.of(d)); } /** * Does not handle non-standard Calendars * * @deprecated use hasCalendarDate */ public boolean hasTime(Date date) { List cdates = getCalendarDates(); for (CalendarDate cd : cdates) { if (date.equals(cd.toDate())) return true; } return false; } //////////////////////////////////////////////////////////////////////////////////////////// private CoordinateAxisTimeHelper helper; private List cdates; protected CoordinateAxis1DTime(Builder builder, Group parentGroup) { super(builder, parentGroup); } public Builder toBuilder() { return addLocalFieldsToBuilder(builder()); } // Add local fields to the passed - in builder. protected Builder addLocalFieldsToBuilder(Builder> b) { return (Builder) super.addLocalFieldsToBuilder(b); } /** * Get Builder for this class that allows subclassing. * * @see "https://community.oracle.com/blogs/emcmanus/2010/10/24/using-builder-pattern-subclasses" */ public static Builder builder() { return new Builder2(); } private static class Builder2 extends Builder { @Override protected Builder2 self() { return this; } } public static abstract class Builder> extends CoordinateAxis1D.Builder { private boolean built; protected abstract T self(); public CoordinateAxis1DTime build(Group parentGroup) { if (built) throw new IllegalStateException("already built"); built = true; return new CoordinateAxis1DTime(this, parentGroup); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy