/** * Helper class for using the netcdf-3 record dimension. * * @author caron * @since Feb 29, 2008 */ public class RecordDatasetHelper { private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RecordDatasetHelper.class); protected NetcdfDataset ncfile; protected String obsTimeVName, nomTimeVName; protected String latVName, lonVName, zcoordVName, zcoordUnits; protected String stnIdVName, stnIndexVName, stnDescVName; protected StationHelper stationHelper; protected DataType stationIdType; protected StructureDS recordVar; protected Dimension obsDim; protected LatLonRect boundingBox; protected double minDate, maxDate; protected DateUnit timeUnit; protected double altScaleFactor = 1.0; protected Formatter errs = null; protected boolean showErrors = true; /** * Constructor. * * @param ncfile the netccdf file * @param typedDataVariables list of data variables; all record variables will be added to this * list, except . You can remove extra * @param obsTimeVName observation time variable name (required) * @param nomTimeVName nominal time variable name (may be null) * @throws IllegalArgumentException if ncfile has no unlimited dimension and recDimName is null. */ public RecordDatasetHelper( NetcdfDataset ncfile, String obsTimeVName, String nomTimeVName, List<VariableSimpleIF> typedDataVariables, String recDimName, Formatter errBuffer) { this.ncfile = ncfile; this.obsTimeVName = obsTimeVName; this.nomTimeVName = nomTimeVName; this.errs = errBuffer; // check if we already have a structure vs if we have to add it. if (this.ncfile.hasUnlimitedDimension()) { this.ncfile.sendIospMessage(NetcdfFile.IOSP_MESSAGE_ADD_RECORD_STRUCTURE); this.recordVar = (StructureDS) this.ncfile.getRootGroup().findVariable("record"); this.obsDim = ncfile.getUnlimitedDimension(); } else { if (recDimName == null) throw new IllegalArgumentException( "File <" + this.ncfile.getLocation() + "> has no unlimited dimension, specify psuedo record dimension with observationDimension global attribute."); this.obsDim = this.ncfile.getRootGroup().findDimension(recDimName); this.recordVar = new StructurePseudoDS(this.ncfile, null, "record", null, obsDim); } // create member variables List<Variable> recordMembers = ncfile.getVariables(); for (Variable v : recordMembers) { if (v == recordVar) continue; if (v.isScalar()) continue; if (v.getDimension(0) == this.obsDim) typedDataVariables.add(v); } // need the time units Variable timeVar = ncfile.findVariable(obsTimeVName); String timeUnitString = ncfile.findAttValueIgnoreCase(timeVar, CDM.UNITS, "seconds since 1970-01-01"); try { timeUnit = new DateUnit(timeUnitString); } catch (Exception e) { if (null != errs) errs.format("Error on string = %s == %s%n", timeUnitString, e.getMessage()); try { timeUnit = new DateUnit("seconds since 1970-01-01"); } catch (Exception e1) { // cant happen } } } /** * Set extra information used by station obs datasets. Use stnIdVName or stnIndexVName. * * @param stnIdVName the obs variable that is used to find the station in the stnHash; may be type * int or a String (char). * @param stnDescVName optional station var containing station description */ public void setStationInfo( String stnIdVName, String stnDescVName, String stnIndexVName, StationHelper stationHelper) { this.stnIdVName = stnIdVName; this.stnDescVName = stnDescVName; this.stnIndexVName = stnIndexVName; this.stationHelper = stationHelper; if (stnIdVName != null) { Variable stationVar = ncfile.findVariable(stnIdVName); stationIdType = stationVar.getDataType(); } } public void setLocationInfo(String latVName, String lonVName, String zcoordVName) { this.latVName = latVName; this.lonVName = lonVName; this.zcoordVName = zcoordVName; // check for meter conversion if (zcoordVName != null) { Variable v = ncfile.findVariable(zcoordVName); zcoordUnits = ncfile.findAttValueIgnoreCase(v, CDM.UNITS, null); if (zcoordUnits != null) try { altScaleFactor = getMetersConversionFactor(zcoordUnits); } catch (Exception e) { if (errs != null) errs.format("%s", e.getMessage()); } } } // make structure variable names to shortNames so StructureData sdata can // access it members public void setShortNames( String latVName, String lonVName, String altVName, String obsTimeVName, String nomTimeVName) { this.latVName = latVName; this.lonVName = lonVName; this.zcoordVName = altVName; this.obsTimeVName = obsTimeVName; this.nomTimeVName = nomTimeVName; } protected static double getMetersConversionFactor(String unitsString) throws Exception { SimpleUnit unit = SimpleUnit.factoryWithExceptions(unitsString); return unit.convertTo(1.0, SimpleUnit.meterUnit); } public Structure getRecordVar() { return (this.recordVar); } public int getRecordCount() { Dimension unlimitedDim = ncfile.getUnlimitedDimension(); return unlimitedDim.getLength(); } public void setTimeUnit(DateUnit timeUnit) { this.timeUnit = timeUnit; } public DateUnit getTimeUnit() { return this.timeUnit; } public LatLonPoint getLocation(StructureData sdata) { StructureMembers members = sdata.getStructureMembers(); double lat = sdata.convertScalarDouble(members.findMember(latVName)); double lon = sdata.convertScalarDouble(members.findMember(lonVName)); return new LatLonPointImpl(lat, lon); } public double getLatitude(StructureData sdata) { StructureMembers members = sdata.getStructureMembers(); return sdata.convertScalarDouble(members.findMember(latVName)); } public double getLongitude(StructureData sdata) { StructureMembers members = sdata.getStructureMembers(); return sdata.convertScalarDouble(members.findMember(lonVName)); } public double getZcoordinate(StructureData sdata) { StructureMembers members = sdata.getStructureMembers(); return (zcoordVName == null) ? Double.NaN : sdata.convertScalarDouble(members.findMember(zcoordVName)); } public String getZcoordUnits() { return zcoordUnits; } public Date getObservationTimeAsDate(StructureData sdata) { return timeUnit.makeDate(getObservationTime(sdata)); } public double getObservationTime(StructureData sdata) { return getTime(sdata.findMember(obsTimeVName), sdata); } private double getTime(StructureMembers.Member timeVar, StructureData sdata) { if (timeVar == null) return 0.0; if ((timeVar.getDataType() == DataType.CHAR) || (timeVar.getDataType() == DataType.STRING)) { String time = sdata.getScalarString(timeVar); CalendarDate date = CalendarDateFormatter.isoStringToCalendarDate(null, time); if (date == null) { log.error("Cant parse date - not ISO formatted, = " + time); return 0.0; } return date.getMillis() / 1000.0; } else { return sdata.convertScalarDouble(timeVar); } } /* * This reads through all the records in the dataset, and constructs a list of * RecordPointObs or RecordStationObs. It does not cache the data. * <p>If stnIdVName is not null, its a StationDataset, then construct a Station HashMap of StationImpl * objects. Add the RecordStationObs into the list of obs for that station. * * @param cancel allow user to cancel * @return List of RecordPointObs or RecordStationObs * @throws IOException on read error * public List<RecordPointObs> readAllCreateObs(CancelTask cancel) throws IOException { // see if its a station or point dataset boolean hasStations = stnIdVName != null; if (hasStations) stnHash = new HashMap<Object, Station>(); // get min and max date and lat,lon double minDate = Double.MAX_VALUE; double maxDate = -Double.MAX_VALUE; double minLat = Double.MAX_VALUE; double maxLat = -Double.MAX_VALUE; double minLon = Double.MAX_VALUE; double maxLon = -Double.MAX_VALUE; // read all the data, create a RecordObs StructureMembers members = null; List<RecordPointObs> records = new ArrayList<RecordPointObs>(); int recno = 0; Structure.Iterator ii = recordVar.getStructureIterator(); while (ii.hasNext()) { StructureData sdata = ii.next(); if (members == null) members = sdata.getStructureMembers(); Object stationId = null; if (hasStations) { if (stationIdType == DataType.INT) { stationId = sdata.getScalarInt(stnIdVName); } else stationId = sdata.getScalarString(stnIdVName).trim(); } String desc = (stnDescVName == null) ? null : sdata.getScalarString(stnDescVName); double lat = sdata.getScalarDouble(latVName); double lon = sdata.getScalarDouble(lonVName); double alt = (altVName == null) ? 0.0 : altScaleFactor * sdata.getScalarDouble(altVName); double obsTime = sdata.convertScalarDouble(members.findMember(obsTimeVName)); double nomTime = (nomTimeVName == null) ? obsTime : sdata.convertScalarDouble(members.findMember(nomTimeVName)); //double obsTime = sdata.convertScalarDouble( members.findMember( obsTimeVName) ); //double nomTime = (nomTimeVName == null) ? obsTime : sdata.convertScalarDouble( members.findMember( nomTimeVName)); if (hasStations) { Station stn = stnHash.get(stationId); if (stn == null) { stn = new Station(stationId.toString(), desc, lat, lon, alt); stnHash.put(stationId, stn); } RecordStationObs stnObs = new RecordStationObs(stn, obsTime, nomTime, timeUnit, recno); records.add(stnObs); //stn.addObs( stnObs); } else { records.add(new RecordPointObs(new EarthLocation(lat, lon, alt), obsTime, nomTime, timeUnit, recno)); } // track date range and bounding box minDate = Math.min(minDate, obsTime); maxDate = Math.max(maxDate, obsTime); minLat = Math.min(minLat, lat); maxLat = Math.max(maxLat, lat); minLon = Math.min(minLon, lon); maxLon = Math.max(maxLon, lon); recno++; if ((cancel != null) && cancel.isCancel()) return null; } boundingBox = new LatLonRect(new LatLonPointImpl(minLat, minLon), new LatLonPointImpl(maxLat, maxLon)); return records; } /* private boolean debugBB = false; public List getData(ArrayList records, LatLonRect boundingBox, CancelTask cancel) throws IOException { if (debugBB) System.out.println("Want bb= "+boundingBox); ArrayList result = new ArrayList(); for (int i = 0; i < records.size(); i++) { RecordDatasetHelper.RecordPointObs r = (RecordDatasetHelper.RecordPointObs) records.get(i); if (boundingBox.contains(r.getLatLon())) { if (debugBB) System.out.println(" ok latlon= "+r.getLatLon()); result.add( r); } if ((cancel != null) && cancel.isCancel()) return null; } return result; } // return List<PointObsDatatype> public List getData(ArrayList records, LatLonRect boundingBox, double startTime, double endTime, CancelTask cancel) throws IOException { if (debugBB) System.out.println("Want bb= "+boundingBox); ArrayList result = new ArrayList(); for (int i = 0; i < records.size(); i++) { RecordDatasetHelper.RecordPointObs r = (RecordDatasetHelper.RecordPointObs) records.get(i); if (boundingBox.contains(r.getLatLon())) { if (debugBB) System.out.println(" ok latlon= "+r.getLatLon()); double timeValue = r.getObservationTime(); if ((timeValue >= startTime) && (timeValue <= endTime)) result.add( r); } if ((cancel != null) && cancel.isCancel()) return null; } return result; } */ ////////////////////////////////////////////////////////////////////////////////////// public PointFeature factory(StationImpl s, StructureData sdata, int recno) { if (s == null) return new RecordPointObs(sdata, recno); else return new RecordStationObs(s, sdata, recno); } class RecordPointObs extends PointFeatureImpl { protected int recno; protected StructureData sdata; RecordPointObs(int recno) { super(RecordDatasetHelper.this.timeUnit); this.recno = recno; } // Constructor for the case where you keep track of the location, time of each record, but not // the data. protected RecordPointObs( EarthLocation location, double obsTime, double nomTime, DateUnit timeUnit, int recno) { super(location, obsTime, nomTime, timeUnit); this.recno = recno; } // Constructor for when you already have the StructureData and want to wrap it in a // StationObsDatatype protected RecordPointObs(StructureData sdata, int recno) { super(RecordDatasetHelper.this.timeUnit); this.sdata = sdata; this.recno = recno; StructureMembers members = sdata.getStructureMembers(); obsTime = getTime(members.findMember(obsTimeVName), sdata); nomTime = (nomTimeVName == null) ? obsTime : getTime(members.findMember(nomTimeVName), sdata); // this assumes the lat/lon/alt is stored in the obs record double lat = sdata.convertScalarDouble(members.findMember(latVName)); double lon = sdata.convertScalarDouble(members.findMember(lonVName)); double alt = (zcoordVName == null) ? 0.0 : altScaleFactor * sdata.convertScalarDouble(members.findMember(zcoordVName)); location = new EarthLocationImpl(lat, lon, alt); } public String getId() { return Integer.toString(recno); } public LatLonPoint getLatLon() { return new LatLonPointImpl(location.getLatitude(), location.getLongitude()); } public StructureData getFeatureData() throws IOException { if (null == sdata) { try { // deal with files that are updating // LOOK kludge? if (recno > getRecordCount()) { int n = getRecordCount(); ncfile.syncExtend(); log.info( "RecordPointObs.getData recno=" + recno + " > " + n + "; after sync= " + getRecordCount()); } sdata = recordVar.readStructure(recno); } catch (ucar.ma2.InvalidRangeException e) { e.printStackTrace(); throw new IOException(e.getMessage()); } } return sdata; } public ucar.ma2.StructureData getDataAll() throws java.io.IOException { return getFeatureData(); } } ////////////////////////////////////////////////////////////////////////////////////// // a PointObs with the location info stored as a Station class RecordStationObs extends RecordPointObs { private Station station; /** * Constructor for the case where you keep track of the station, time of each record, but the * data reading is deferred. * * @param station data is for this Station * @param obsTime observation time * @param nomTime nominal time (may be NaN) * @param recno data is at this record number */ protected RecordStationObs( Station station, double obsTime, double nomTime, DateUnit timeUnit, int recno) { super(station, obsTime, nomTime, timeUnit, recno); this.station = station; } // Constructor for when you have everything protected RecordStationObs( Station station, double obsTime, double nomTime, StructureData sdata, int recno) { super(recno); this.station = station; this.location = station; this.obsTime = obsTime; this.nomTime = nomTime; this.sdata = sdata; } // Constructor for when you already have the StructureData and Station, and calculate times protected RecordStationObs(Station station, StructureData sdata, int recno) { super(recno); this.station = station; this.location = station; this.sdata = sdata; StructureMembers members = sdata.getStructureMembers(); obsTime = getTime(members.findMember(obsTimeVName), sdata); nomTime = (nomTimeVName == null) ? obsTime : getTime(members.findMember(nomTimeVName), sdata); } // Constructor for when you already have the StructureData, and need to find Station and times protected RecordStationObs(StructureData sdata, int recno, boolean useId) { super(recno); this.recno = recno; this.sdata = sdata; this.timeUnit = RecordDatasetHelper.this.timeUnit; StructureMembers members = sdata.getStructureMembers(); obsTime = getTime(members.findMember(obsTimeVName), sdata); nomTime = (nomTimeVName == null) ? obsTime : getTime(members.findMember(nomTimeVName), sdata); if (useId) { // this assumes the station id/name is stored in the obs record String stationId; if (stationIdType == DataType.INT) { stationId = Integer.toString(sdata.getScalarInt(stnIdVName)); } else stationId = sdata.getScalarString(stnIdVName).trim(); station = stationHelper.getStation(stationId); if (null != errs) errs.format(" cant find station id = <%s> when reading record %d%n", stationId, recno); log.error(" cant find station id = <" + stationId + "> when reading record " + recno); } else { // use a station index List<Station> stations = stationHelper.getStations(); int stationIndex = sdata.getScalarInt(stnIndexVName); if (stationIndex < 0 || stationIndex >= stations.size()) { if (null != errs) errs.format( " cant find station at index =%d when reading record %d%n", stationIndex, recno); log.error( "cant find station at index = " + stationIndex + " when reading record " + recno); } else station = stations.get(stationIndex); } location = station; } } }
/** * _more_ * * @author edavis * @since 4.0 */ public class WcsCoverage { private static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(WcsCoverage.class); private GridDataset.Gridset coverage; private WcsDataset dataset; private String name; private String label; private String description; private GridCoordSystem coordSys; private String nativeCRS; private String defaultRequestCrs; private List<WcsRequest.Format> supportedCoverageFormatList; private HashMap<String, WcsRangeField> range; public WcsCoverage(GridDataset.Gridset coverage, WcsDataset dataset) { this.dataset = dataset; if (this.dataset == null) { log.error("WcsCoverage(): non-null dataset required."); throw new IllegalArgumentException("Non-null dataset required."); } this.coverage = coverage; if (this.coverage == null) { log.error("WcsCoverage(): non-null coverage required."); throw new IllegalArgumentException("Non-null coverage required."); } this.coordSys = coverage.getGeoCoordSystem(); if (this.coordSys == null) { log.error("WcsCoverage(): Coverage must have non-null coordinate system."); throw new IllegalArgumentException("Non-null coordinate system required."); } this.name = this.coordSys.getName(); this.label = this.coordSys.getName(); this.range = new HashMap<String, WcsRangeField>(); StringBuilder descripSB = new StringBuilder("All parameters on the \"") .append(this.name) .append("\" coordinate system: "); for (GridDatatype curField : this.coverage.getGrids()) { String stdName = curField.findAttValueIgnoreCase("standard_name", ""); descripSB.append(stdName.length() == 0 ? curField.getFullName() : stdName).append(","); WcsRangeField field = new WcsRangeField(curField); range.put(field.getName(), field); } descripSB.setCharAt(descripSB.length() - 1, '.'); this.description = descripSB.toString(); this.nativeCRS = EPSG_OGC_CF_Helper.getWcs1_0CrsId(this.coordSys.getProjection()); this.defaultRequestCrs = "OGC:CRS84"; this.supportedCoverageFormatList = new ArrayList<WcsRequest.Format>(); // this.supportedCoverageFormatList = "application/x-netcdf"; this.supportedCoverageFormatList.add(WcsRequest.Format.GeoTIFF); this.supportedCoverageFormatList.add(WcsRequest.Format.GeoTIFF_Float); this.supportedCoverageFormatList.add(WcsRequest.Format.NetCDF3); } GridDataset.Gridset getGridset() { return coverage; } public String getName() { return this.name; } public String getLabel() { return this.label; } public String getDescription() { return this.description; } public GridCoordSystem getCoordinateSystem() { return coordSys; } public String getDefaultRequestCrs() { return defaultRequestCrs; } public String getNativeCrs() { return nativeCRS; } public List<WcsRequest.Format> getSupportedCoverageFormatList() { return Collections.unmodifiableList(supportedCoverageFormatList); } public boolean isSupportedCoverageFormat(WcsRequest.Format covFormat) { return this.supportedCoverageFormatList.contains(covFormat); } public boolean isRangeFieldName(String fieldName) { return range.containsKey(fieldName); } public Set<String> getRangeFieldNames() { return range.keySet(); } public Collection<WcsRangeField> getRange() { return range.values(); } private static DiskCache2 diskCache = null; public static void setDiskCache(DiskCache2 _diskCache) { diskCache = _diskCache; } private static DiskCache2 getDiskCache() { if (diskCache == null) { log.error("getDiskCache(): Disk cache has not been set."); throw new IllegalStateException( "Disk cache must be set before calling GetCoverage.getDiskCache()."); } return diskCache; } public File writeCoverageDataToFile( WcsRequest.Format format, LatLonRect bboxLatLonRect, AxisSubset vertSubset, List<String> rangeSubset, CalendarDateRange timeRange) throws WcsException { boolean zRangeDone = false; boolean tRangeDone = false; try { // Get the height range. Range zRange = vertSubset != null ? vertSubset.getRange() : null; zRangeDone = true; // Get the time range. Range tRange = null; if (timeRange != null) { CoordinateAxis1DTime timeAxis = this.coordSys.getTimeAxis1D(); int startIndex = timeAxis.findTimeIndexFromCalendarDate(timeRange.getStart()); int endIndex = timeAxis.findTimeIndexFromCalendarDate(timeRange.getEnd()); tRange = new Range(startIndex, endIndex); tRangeDone = true; } if (format == WcsRequest.Format.GeoTIFF || format == WcsRequest.Format.GeoTIFF_Float) { if (rangeSubset.size() != 1) { String msg = "GeoTIFF response encoding only available for single range field selection [" + rangeSubset + "]."; log.error("writeCoverageDataToFile(): " + msg); throw new WcsException(WcsException.Code.InvalidParameterValue, "RangeSubset", msg); } String reqRangeFieldName = rangeSubset.get(0); File dir = new File(getDiskCache().getRootDirectory()); File tifFile = File.createTempFile("WCS", ".tif", dir); if (log.isDebugEnabled()) log.debug("writeCoverageDataToFile(): tifFile=" + tifFile.getPath()); WcsRangeField rangeField = this.range.get(reqRangeFieldName); GridDatatype subset = rangeField.getGridDatatype().makeSubset(tRange, zRange, bboxLatLonRect, 1, 1, 1); Array data = subset.readDataSlice(0, 0, -1, -1); GeotiffWriter writer = new GeotiffWriter(tifFile.getPath()); writer.writeGrid( this.dataset.getDataset(), subset, data, format == WcsRequest.Format.GeoTIFF); writer.close(); return tifFile; } else if (format == WcsRequest.Format.NetCDF3) { File dir = new File(getDiskCache().getRootDirectory()); File outFile = File.createTempFile("WCS", ".nc", dir); if (log.isDebugEnabled()) log.debug("writeCoverageDataToFile(): ncFile=" + outFile.getPath()); // WTF ?? this.coordSys.getVerticalAxis().isNumeric(); NetcdfFileWriter writer = NetcdfFileWriter.createNew(NetcdfFileWriter.Version.netcdf3, outFile.getAbsolutePath()); CFGridWriter2.writeFile( this.dataset.getDataset(), rangeSubset, bboxLatLonRect, null, 1, zRange, timeRange, 1, true, writer); return outFile; } else { log.error( "writeCoverageDataToFile(): Unsupported response encoding format [" + format + "]."); throw new WcsException( WcsException.Code.InvalidFormat, "Format", "Unsupported response encoding format [" + format + "]."); } } catch (InvalidRangeException e) { String msg = "Failed to subset coverage [" + this.getName(); if (!zRangeDone) msg += "] along vertical axis [" + vertSubset + "]. "; else if (!tRangeDone) msg += "] along time axis [" + timeRange + "]. "; else msg += "] in horizontal plane [" + bboxLatLonRect + "]. "; log.error("writeCoverageDataToFile(): " + msg + e.getMessage()); throw new WcsException(WcsException.Code.CoverageNotDefined, "", msg); } catch (IOException e) { log.error( "writeCoverageDataToFile(): Failed to write file for requested coverage <" + this.getName() + ">: " + e.getMessage()); throw new WcsException( WcsException.Code.UNKNOWN, "", "Problem creating coverage [" + this.getName() + "]."); } } }
/** * A georeferencing "gridded" VariableEnhanced, that has a GridCoordSys. In VisAD data model, it is * a sampled Field. The dimension are put into canonical order: (rt, e, t, z, y, x). * * <p> * * <p>Implementation note: If the Horizontal axes are 2D, the x and y dimensions are arbitrarily * chosen to be gcs.getXHorizAxis().getDimension(1), gcs.getXHorizAxis().getDimension(0) * respectively. * * <p> * * @author caron */ public class GeoGrid implements NamedObject, ucar.nc2.dt.GridDatatype { private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(GeoGrid.class); private static final boolean debugArrayShape = false; private final GridDataset dataset; private final GridCoordSys gcs; private final VariableDS vs; private int xDimOrgIndex = -1, yDimOrgIndex = -1, zDimOrgIndex = -1, tDimOrgIndex = -1, eDimOrgIndex = -1, rtDimOrgIndex = -1; private int xDimNewIndex = -1, yDimNewIndex = -1, zDimNewIndex = -1, tDimNewIndex = -1, eDimNewIndex = -1, rtDimNewIndex = -1; private final List<Dimension> mydims; /** * Constructor. * * @param dataset belongs to this dataset * @param dsvar wraps this Variable * @param gcs has this grid coordinate system */ public GeoGrid(GridDataset dataset, VariableDS dsvar, GridCoordSys gcs) { this.dataset = dataset; this.vs = dsvar; this.gcs = gcs; CoordinateAxis xaxis = gcs.getXHorizAxis(); if (xaxis instanceof CoordinateAxis1D) { xDimOrgIndex = findDimension(gcs.getXHorizAxis().getDimension(0)); yDimOrgIndex = findDimension(gcs.getYHorizAxis().getDimension(0)); } else { // 2D case yDimOrgIndex = findDimension(gcs.getXHorizAxis().getDimension(0)); xDimOrgIndex = findDimension(gcs.getXHorizAxis().getDimension(1)); } if (gcs.getVerticalAxis() != null) zDimOrgIndex = findDimension(gcs.getVerticalAxis().getDimension(0)); if (gcs.getTimeAxis() != null) { if (gcs.getTimeAxis1D() != null) tDimOrgIndex = findDimension(gcs.getTimeAxis1D().getDimension(0)); else tDimOrgIndex = findDimension(gcs.getTimeAxis().getDimension(1)); } if (gcs.getEnsembleAxis() != null) eDimOrgIndex = findDimension(gcs.getEnsembleAxis().getDimension(0)); if (gcs.getRunTimeAxis() != null) rtDimOrgIndex = findDimension(gcs.getRunTimeAxis().getDimension(0)); // construct canonical dimension list int count = 0; this.mydims = new ArrayList<Dimension>(); if ((rtDimOrgIndex >= 0) && (rtDimOrgIndex != tDimOrgIndex)) { mydims.add(dsvar.getDimension(rtDimOrgIndex)); rtDimNewIndex = count++; } if (eDimOrgIndex >= 0) { mydims.add(dsvar.getDimension(eDimOrgIndex)); eDimNewIndex = count++; } if (tDimOrgIndex >= 0) { mydims.add(dsvar.getDimension(tDimOrgIndex)); tDimNewIndex = count++; } if (zDimOrgIndex >= 0) { mydims.add(dsvar.getDimension(zDimOrgIndex)); zDimNewIndex = count++; } if (yDimOrgIndex >= 0) { mydims.add(dsvar.getDimension(yDimOrgIndex)); yDimNewIndex = count++; } if (xDimOrgIndex >= 0) { mydims.add(dsvar.getDimension(xDimOrgIndex)); xDimNewIndex = count; } } private int findDimension(Dimension want) { java.util.List dims = vs.getDimensions(); for (int i = 0; i < dims.size(); i++) { Dimension d = (Dimension) dims.get(i); if (d.equals(want)) return i; } return -1; } /** * Returns an ArrayList containing the dimensions used by this geoGrid. The dimension are put into * canonical order: (rt, e, t, z, y, x). Note that the z and t dimensions are optional. If the * Horizontal axes are 2D, the x and y dimensions are arbitrarily chosen to be * gcs.getXHorizAxis().getDimension(1), gcs.getXHorizAxis().getDimension(0), respectively. * * @return List with objects of type Dimension, in canonical order. */ public java.util.List<Dimension> getDimensions() { return new ArrayList<Dimension>(mydims); } /** * get the ith dimension * * @param i : which dimension * @return ith Dimension */ public Dimension getDimension(int i) { if ((i < 0) || (i >= mydims.size())) return null; return mydims.get(i); } /** get the time Dimension, if it exists */ public Dimension getTimeDimension() { return tDimNewIndex < 0 ? null : getDimension(tDimNewIndex); } /** get the z Dimension, if it exists */ public Dimension getZDimension() { return zDimNewIndex < 0 ? null : getDimension(zDimNewIndex); } /** get the y Dimension, if it exists */ public Dimension getYDimension() { return yDimNewIndex < 0 ? null : getDimension(yDimNewIndex); } /** get the x Dimension, if it exists */ public Dimension getXDimension() { return xDimNewIndex < 0 ? null : getDimension(xDimNewIndex); } /** get the ensemble Dimension, if it exists */ public Dimension getEnsembleDimension() { return eDimNewIndex < 0 ? null : getDimension(eDimNewIndex); } /** get the run time Dimension, if it exists */ public Dimension getRunTimeDimension() { return rtDimNewIndex < 0 ? null : getDimension(rtDimNewIndex); } /** get the time Dimension index in the geogrid (canonical order), or -1 if none */ public int getTimeDimensionIndex() { return tDimNewIndex; } /** get the z Dimension index in the geogrid (canonical order), or -1 if none */ public int getZDimensionIndex() { return zDimNewIndex; } /** get the y Dimension index in the geogrid (canonical order) */ public int getYDimensionIndex() { return yDimNewIndex; } /** get the x Dimension index in the geogrid (canonical order) */ public int getXDimensionIndex() { return xDimNewIndex; } /** get the ensemble Dimension index in the geogrid (canonical order) */ public int getEnsembleDimensionIndex() { return eDimNewIndex; } /** get the runtime Dimension index in the geogrid (canonical order) */ public int getRunTimeDimensionIndex() { return rtDimNewIndex; } /** * Convenience function; lookup Attribute by name. * * @param name the name of the attribute * @return the attribute, or null if not found */ public Attribute findAttributeIgnoreCase(String name) { return vs.findAttributeIgnoreCase(name); } /** * Convenience function; lookup Attribute value by name. Must be String valued * * @param attName name of the attribute * @param defaultValue if not found, use this as the default * @return Attribute string value, or default if not found. */ public String findAttValueIgnoreCase(String attName, String defaultValue) { return dataset.getNetcdfDataset().findAttValueIgnoreCase((Variable) vs, attName, defaultValue); } // implementation of GridDatatype interface /** get the rank */ public int getRank() { return mydims.size(); } /** get the shape */ public int[] getShape() { int[] shape = new int[mydims.size()]; for (int i = 0; i < mydims.size(); i++) { Dimension d = mydims.get(i); shape[i] = d.getLength(); } return shape; } /** get the data type */ public DataType getDataType() { return vs.getDataType(); } public List<Attribute> getAttributes() { return vs.getAttributes(); } public VariableDS getVariable() { return vs; } public String getFullName() { return vs.getFullName(); } public String getName() { return vs.getFullName(); } public String getShortName() { return vs.getShortName(); } /** get the GridCoordSys for this GeoGrid. */ public GridCoordSystem getCoordinateSystem() { return gcs; } /** get the Projection. */ public ProjectionImpl getProjection() { return gcs.getProjection(); } /** @return ArrayList of thredds.util.NamedObject, from the GridCoordSys. */ public List<NamedObject> getLevels() { return gcs.getLevels(); } /** @return ArrayList of thredds.util.NamedObject, from the GridCoordSys. */ public List<NamedObject> getTimes() { return gcs.getTimes(); } /** get the standardized description */ public String getDescription() { return vs.getDescription(); } /** get the unit as a string */ public String getUnitsString() { String units = vs.getUnitsString(); return (units == null) ? "" : units; } /** * @return getUnitsString() * @deprecated use getUnitsString() */ public java.lang.String getUnitString() { return getUnitsString(); } // public ucar.unidata.geoloc.ProjectionImpl getProjection() { return gcs.getProjection(); } /** true if there may be missing data, see VariableDS.hasMissing() */ public boolean hasMissingData() { return vs.hasMissing(); } /** if val is missing data, see VariableDS.isMissingData() */ public boolean isMissingData(double val) { return vs.isMissing(val); } /** * Convert (in place) all values in the given array that are considered as "missing" to Float.NaN, * according to isMissingData(val). * * @param values input array * @return input array, with missing values converted to NaNs. */ public float[] setMissingToNaN(float[] values) { if (!vs.hasMissing()) return values; final int length = values.length; for (int i = 0; i < length; i++) { double value = values[i]; if (vs.isMissing(value)) values[i] = Float.NaN; } return values; } /** * Get the minimum and the maximum data value of the previously read Array, skipping missing * values as defined by isMissingData(double val). * * @param a Array to get min/max values * @return both min and max value. */ public MAMath.MinMax getMinMaxSkipMissingData(Array a) { if (!hasMissingData()) return MAMath.getMinMax(a); IndexIterator iter = a.getIndexIterator(); double max = -Double.MAX_VALUE; double min = Double.MAX_VALUE; while (iter.hasNext()) { double val = iter.getDoubleNext(); if (isMissingData(val)) continue; if (val > max) max = val; if (val < min) min = val; } return new MAMath.MinMax(min, max); } /** * Reads in the data "volume" at the given time index. If its a product set, put into canonical * order (z-y-x). If not a product set, reorder to (z,i,j), where i, j are from the original * * @param t time index; ignored if no time axis. * @return data[z,y,x] or data[y,x] if no z axis. */ public Array readVolumeData(int t) throws java.io.IOException { // if (gcs.isProductSet()) return readDataSlice(t, -1, -1, -1); /* else { // 2D XY int rank = vs.getRank(); int[] shape = vs.getShape(); int [] start = new int[rank]; CoordinateAxis taxis = gcs.getTimeAxis(); if (taxis != null) { if ((t >= 0) && (t < taxis.getSize())) shape[ tDim] = 1; // fix t start[ tDim] = t; } if (debugArrayShape) { System.out.println("getDataVolume shape = "); for (int i=0; i<rank; i++) System.out.println(" start = "+start[i]+" shape = "+ shape[i]); } Array dataVolume; try { dataVolume = vs.read( start, shape); } catch (Exception e) { System.out.println("Exception: GeoGridImpl.getdataSlice() on dataset "+getName()); e.printStackTrace(); throw new java.io.IOException(e.getMessage()); } // no reordering FIX return dataVolume.reduce(); } */ } /** * Reads a Y-X "horizontal slice" at the given time and vertical index. If its a product set, put * into canonical order (y-x). * * @param t time index; ignored if no time axis. * @param z vertical index; ignored if no z axis. * @return data[y,x] * @throws java.io.IOException on read error */ public Array readYXData(int t, int z) throws java.io.IOException { return readDataSlice(t, z, -1, -1); } /** * Reads a Z-Y "vertical slice" at the given time and x index. If its a product set, put into * canonical order (z-y). * * @param t time index; ignored if no time axis. * @param x x index; ignored if no x axis. * @return data[z,y] * @throws java.io.IOException on read error */ public Array readZYData(int t, int x) throws java.io.IOException { return readDataSlice(t, -1, -1, x); } /** * @throws java.io.IOException on read error * @deprecated use readDataSlice */ public Array getDataSlice(int t, int z, int y, int x) throws java.io.IOException { return readDataSlice(t, z, y, x); } /** * This reads an arbitrary data slice, returning the data in canonical order (t-z-y-x). If any * dimension does not exist, ignore it. * * @param t if < 0, get all of time dim; if valid index, fix slice to that value. * @param z if < 0, get all of z dim; if valid index, fix slice to that value. * @param y if < 0, get all of y dim; if valid index, fix slice to that value. * @param x if < 0, get all of x dim; if valid index, fix slice to that value. * @return data[t,z,y,x], eliminating missing or fixed dimension. */ public Array readDataSlice(int t, int z, int y, int x) throws java.io.IOException { return readDataSlice(0, 0, t, z, y, x); } /** * This reads an arbitrary data slice, returning the data in canonical order (rt-e-t-z-y-x). If * any dimension does not exist, ignore it. * * @param rt if < 0, get all of runtime dim; if valid index, fix slice to that value. * @param e if < 0, get all of ensemble dim; if valid index, fix slice to that value. * @param t if < 0, get all of time dim; if valid index, fix slice to that value. * @param z if < 0, get all of z dim; if valid index, fix slice to that value. * @param y if < 0, get all of y dim; if valid index, fix slice to that value. * @param x if < 0, get all of x dim; if valid index, fix slice to that value. * @return data[rt,e,t,z,y,x], eliminating missing or fixed dimension. */ public Array readDataSlice(int rt, int e, int t, int z, int y, int x) throws java.io.IOException { int rank = vs.getRank(); int[] start = new int[rank]; int[] shape = new int[rank]; for (int i = 0; i < rank; i++) { start[i] = 0; shape[i] = 1; } Dimension xdim = getXDimension(); Dimension ydim = getYDimension(); Dimension zdim = getZDimension(); Dimension tdim = getTimeDimension(); Dimension edim = getEnsembleDimension(); Dimension rtdim = getRunTimeDimension(); // construct the shape of the data volume to be read if (rtdim != null) { if ((rt >= 0) && (rt < rtdim.getLength())) start[rtDimOrgIndex] = rt; // fix rt else { shape[rtDimOrgIndex] = rtdim.getLength(); // all of rt } } if (edim != null) { if ((e >= 0) && (e < edim.getLength())) start[eDimOrgIndex] = e; // fix e else { shape[eDimOrgIndex] = edim.getLength(); // all of e } } if (tdim != null) { if ((t >= 0) && (t < tdim.getLength())) start[tDimOrgIndex] = t; // fix t else { shape[tDimOrgIndex] = tdim.getLength(); // all of t } } if (zdim != null) { if ((z >= 0) && (z < zdim.getLength())) start[zDimOrgIndex] = z; // fix z else { shape[zDimOrgIndex] = zdim.getLength(); // all of z } } if (ydim != null) { if ((y >= 0) && (y < ydim.getLength())) start[yDimOrgIndex] = y; // fix y else { shape[yDimOrgIndex] = ydim.getLength(); // all of y } } if (xdim != null) { if ((x >= 0) && (x < xdim.getLength())) // all of x start[xDimOrgIndex] = x; // fix x else { shape[xDimOrgIndex] = xdim.getLength(); // all of x } } if (debugArrayShape) { System.out.println("read shape from org variable = "); for (int i = 0; i < rank; i++) System.out.println( " start = " + start[i] + " shape = " + shape[i] + " name = " + vs.getDimension(i).getName()); } // read it Array dataVolume; try { dataVolume = vs.read(start, shape); } catch (Exception ex) { log.error( "GeoGrid.getdataSlice() on dataset " + getFullName() + " " + dataset.getLocation(), ex); throw new java.io.IOException(ex.getMessage()); } // LOOK: the real problem is the lack of named dimensions in the Array object // figure out correct permutation for canonical ordering for permute List<Dimension> oldDims = new ArrayList<Dimension>(vs.getDimensions()); int[] permuteIndex = new int[dataVolume.getRank()]; int count = 0; if (oldDims.contains(rtdim)) permuteIndex[count++] = oldDims.indexOf(rtdim); if (oldDims.contains(edim)) permuteIndex[count++] = oldDims.indexOf(edim); if (oldDims.contains(tdim)) permuteIndex[count++] = oldDims.indexOf(tdim); if (oldDims.contains(zdim)) permuteIndex[count++] = oldDims.indexOf(zdim); if (oldDims.contains(ydim)) permuteIndex[count++] = oldDims.indexOf(ydim); if (oldDims.contains(xdim)) permuteIndex[count] = oldDims.indexOf(xdim); if (debugArrayShape) { System.out.println("oldDims = "); for (Dimension oldDim : oldDims) System.out.println(" oldDim = " + oldDim.getName()); System.out.println("permute dims = "); for (int aPermuteIndex : permuteIndex) System.out.println(" oldDim index = " + aPermuteIndex); } // check to see if we need to permute boolean needPermute = false; for (int i = 0; i < permuteIndex.length; i++) { if (i != permuteIndex[i]) needPermute = true; } // permute to the order rt,e,t,z,y,x if (needPermute) dataVolume = dataVolume.permute(permuteIndex); // eliminate fixed dimensions, but not all dimensions of length 1. count = 0; if (rtdim != null) { if (rt >= 0) dataVolume = dataVolume.reduce(count); else count++; } if (edim != null) { if (e >= 0) dataVolume = dataVolume.reduce(count); else count++; } if (tdim != null) { if (t >= 0) dataVolume = dataVolume.reduce(count); else count++; } if (zdim != null) { if (z >= 0) dataVolume = dataVolume.reduce(count); else count++; } if (ydim != null) { if (y >= 0) dataVolume = dataVolume.reduce(count); else count++; } if (xdim != null) { if (x >= 0) dataVolume = dataVolume.reduce(count); } return dataVolume; } ////////////////////////////////// /** * Create a new GeoGrid that is a logical subset of this GeoGrid. * * @param t_range subset the time dimension, or null if you want all of it * @param z_range subset the vertical dimension, or null if you want all of it * @param bbox a lat/lon bounding box, or null if you want all x,y * @param z_stride use only if z_range is null, then take all z with this stride (1 means all) * @param y_stride use this stride on the y coordinate (1 means all) * @param x_stride use this stride on the x coordinate (1 means all) * @return subsetted GeoGrid * @throws InvalidRangeException if bbox does not intersect GeoGrid */ public GeoGrid subset( Range t_range, Range z_range, LatLonRect bbox, int z_stride, int y_stride, int x_stride) throws InvalidRangeException { if ((z_range == null) && (z_stride > 1)) { Dimension zdim = getZDimension(); if (zdim != null) z_range = new Range(0, zdim.getLength() - 1, z_stride); } Range y_range = null, x_range = null; if (bbox != null) { List yx_ranges = gcs.getRangesFromLatLonRect(bbox); y_range = (Range) yx_ranges.get(0); x_range = (Range) yx_ranges.get(1); } if (y_stride > 1) { if (y_range == null) { Dimension ydim = getYDimension(); y_range = new Range(0, ydim.getLength() - 1, y_stride); } else { y_range = new Range(y_range.first(), y_range.last(), y_stride); } } if (x_stride > 1) { if (x_range == null) { Dimension xdim = getXDimension(); x_range = new Range(0, xdim.getLength() - 1, x_stride); } else { x_range = new Range(x_range.first(), x_range.last(), x_stride); } } return subset(t_range, z_range, y_range, x_range); } public GridDatatype makeSubset( Range t_range, Range z_range, LatLonRect bbox, int z_stride, int y_stride, int x_stride) throws InvalidRangeException { return subset(t_range, z_range, bbox, z_stride, y_stride, x_stride); } /** * Create a new GeoGrid that is a logical subset of this GeoGrid. * * @param t_range subset the time dimension, or null if you want all of it * @param z_range subset the vertical dimension, or null if you want all of it * @param y_range subset the y dimension, or null if you want all of it * @param x_range subset the x dimension, or null if you want all of it * @return subsetted GeoGrid * @throws InvalidRangeException if any of the ranges are invalid */ public GeoGrid subset(Range t_range, Range z_range, Range y_range, Range x_range) throws InvalidRangeException { return (GeoGrid) makeSubset(null, null, t_range, z_range, y_range, x_range); } public GridDatatype makeSubset( Range rt_range, Range e_range, Range t_range, Range z_range, Range y_range, Range x_range) throws InvalidRangeException { // get the ranges list int rank = getRank(); Range[] ranges = new Range[rank]; if (null != getXDimension()) ranges[xDimOrgIndex] = x_range; if (null != getYDimension()) ranges[yDimOrgIndex] = y_range; if (null != getZDimension()) ranges[zDimOrgIndex] = z_range; if (null != getTimeDimension()) ranges[tDimOrgIndex] = t_range; if (null != getRunTimeDimension()) ranges[rtDimOrgIndex] = rt_range; if (null != getEnsembleDimension()) ranges[eDimOrgIndex] = e_range; List<Range> rangesList = Arrays.asList(ranges); // subset the variable VariableDS v_section = (VariableDS) vs.section(new Section(rangesList)); List<Dimension> dims = v_section.getDimensions(); for (Dimension dim : dims) { dim.setShared(true); // make them shared (section will make them unshared) } // subset the axes in the GridCoordSys GridCoordSys gcs_section = new GridCoordSys(gcs, rt_range, e_range, t_range, z_range, y_range, x_range); // now we can make the geogrid return new GeoGrid(dataset, v_section, gcs_section); } ///////////////////////////////////////////////////////////////////////////////// /** Instances which have same name and coordinate system are equal. */ public boolean equals(Object oo) { if (this == oo) return true; if (!(oo instanceof GeoGrid)) return false; GeoGrid d = (GeoGrid) oo; if (!getFullName().equals(d.getFullName())) return false; if (!getCoordinateSystem().equals(d.getCoordinateSystem())) return false; return true; } /** Override Object.hashCode() to be consistent with equals. */ public int hashCode() { if (hashCode == 0) { int result = 17; // result = 37*result + dataset.getName().hashCode(); result = 37 * result + getFullName().hashCode(); result = 37 * result + getCoordinateSystem().hashCode(); hashCode = result; } return hashCode; } private int hashCode = 0; // Bloch, item 8 /** string representation */ public String toString() { return getFullName(); } /** nicely formatted information */ public String getInfo() { StringBuilder buf = new StringBuilder(200); buf.setLength(0); buf.append(getFullName()); Format.tab(buf, 30, true); buf.append(getUnitsString()); Format.tab(buf, 60, true); buf.append(hasMissingData()); Format.tab(buf, 66, true); buf.append(getDescription()); return buf.toString(); } public int compareTo(GridDatatype g) { return getFullName().compareTo(g.getFullName()); } }