/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package thredds.server.ncss.view.gridaspoint.netcdf; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import thredds.server.ncss.dataservice.StructureDataFactory; import thredds.server.ncss.util.NcssRequestUtils; import ucar.ma2.InvalidRangeException; import ucar.ma2.StructureData; import ucar.nc2.Attribute; import ucar.nc2.NetcdfFileWriter; import ucar.nc2.constants.CDM; import ucar.nc2.dataset.CoordinateAxis1D; import ucar.nc2.dt.GridDataset; import ucar.nc2.dt.GridDatatype; import ucar.nc2.dt.grid.GridAsPointDataset; import ucar.nc2.ft.point.writer.CFPointWriterUtils; import ucar.nc2.time.CalendarDate; import ucar.nc2.units.DateUnit; import ucar.unidata.geoloc.EarthLocation; import ucar.unidata.geoloc.EarthLocationImpl; import ucar.unidata.geoloc.LatLonPoint; import ucar.unidata.geoloc.Station; import ucar.unidata.geoloc.StationImpl; /** * * @author mhermida * */ final class CFTimeSeriesProfileCollectionWriterWrapper implements CFPointWriterWrapper { static private Logger log = LoggerFactory.getLogger(CFTimeSeriesProfileCollectionWriterWrapper.class); private WriterCFTimeSeriesProfileCollection writerCFTimeSeriesProfileCollection; private CalendarDate timeOrigin; private CFTimeSeriesProfileCollectionWriterWrapper(NetcdfFileWriter.Version version, String filePath, List<Attribute> atts ) throws IOException{ writerCFTimeSeriesProfileCollection = new WriterCFTimeSeriesProfileCollection(version, filePath, atts); } @Override public boolean header(Map<String, List<String>> groupedVars, GridDataset gds, List<CalendarDate> wDates, List<Attribute> timeDimAtts, LatLonPoint point, Double vertCoord){ boolean headerDone = false; //timeOrigin = dateUnit.makeCalendarDate(0); Attribute unitsAtt = CFPointWriterUtils.findCDMAtt(timeDimAtts, CDM.UNITS); DateUnit dateUnit; try { dateUnit = new DateUnit(unitsAtt.getStringValue()); timeOrigin = dateUnit.makeCalendarDate(0); } catch (Exception e) { log.error("Error creating time units for: "+unitsAtt.getStringValue()); return headerDone; } List<Attribute> atts = new ArrayList<>(); atts.add(new Attribute( CDM.TITLE, "Extract time series profiles data from Grid file "+ gds.getLocationURI()) ); //Create the list of stations (only one) String stnName="Grid Point"; String desc = "Grid Point at lat/lon="+point.getLatitude()+","+point.getLongitude(); Station s = new StationImpl( stnName, desc, "", point.getLatitude(), point.getLongitude(), Double.NaN); List<Station> stnList = new ArrayList<>(); stnList.add(s); try { writerCFTimeSeriesProfileCollection.writeHeader(stnList, groupedVars, gds, timeDimAtts, wDates.size(), vertCoord ); headerDone = true; } catch (IOException ioe) { log.error("Error writing header", ioe); } return headerDone; } @Override public boolean write(Map<String, List<String>> groupedVars, GridDataset gridDataset, CalendarDate date, LatLonPoint point, Double targetLevel) throws InvalidRangeException { boolean allDone = false; List<String> keys =new ArrayList<>(groupedVars.keySet()); try{ for(String key : keys){ List<String> varsGroup = groupedVars.get(key); //Ensemble... CoordinateAxis1D ensAxis = gridDataset.findGridDatatype(varsGroup.get(0)).getCoordinateSystem().getEnsembleAxis(); double[] ensCoords = new double[]{-1}; if(ensAxis != null){ ensCoords = ensAxis.getCoordValues(); } int ensIdx =0; for(double ensCoord : ensCoords){ if(ensCoord >=0){ writerCFTimeSeriesProfileCollection.writeEnsCoord(ensIdx, ensCoord); ensIdx++; } CoordinateAxis1D zAxis = gridDataset.findGridDatatype(varsGroup.get(0)).getCoordinateSystem().getVerticalAxis(); //String profileName = NO_VERT_LEVEL; EarthLocation earthLocation=null; GridAsPointDataset gap = NcssRequestUtils.buildGridAsPointDataset(gridDataset, varsGroup); GridDatatype timeGrid = NcssRequestUtils.getTimeGrid(groupedVars, gridDataset); if(timeGrid == null){ throw new IllegalArgumentException("Variables do not have time dimension"); } Double timeCoordValue = NcssRequestUtils.getTimeCoordValue(timeGrid, date, timeOrigin); if(zAxis == null){ //Variables without vertical levels //Write no vert levels StructureData sdata = StructureDataFactory.getFactory().createSingleStructureData(gridDataset, point, varsGroup,true ); sdata.findMember("time").getDataArray().setDouble(0, timeCoordValue); //sdata.findMember("time").getDataArray().setObject(0, date.toString()); //gap = NcssRequestUtils.buildGridAsPointDataset(gridDataset, varsGroup); Iterator<String> itVars = varsGroup.iterator(); int cont =0; while (itVars.hasNext()) { String varName = itVars.next(); GridDatatype grid = gridDataset.findGridDatatype(varName); if (gap.hasTime(grid, date) ) { GridAsPointDataset.Point p = gap.readData(grid, date, ensCoord, -1, point.getLatitude(), point.getLongitude()); sdata.findMember(varName).getDataArray().setDouble(0, p.dataValue ); if(cont ==0){ earthLocation = new EarthLocationImpl(p.lat, p.lon, Double.NaN ); } }else{ //Set missing value sdata.findMember(varName).getDataArray().setDouble(0, gap.getMissingValue(grid) ); earthLocation = new EarthLocationImpl(point.getLatitude(), point.getLongitude(), Double.NaN ); } } if(ensCoord >=0) writerCFTimeSeriesProfileCollection.writeRecord( timeCoordValue, ensCoord, date, earthLocation , sdata); else writerCFTimeSeriesProfileCollection.writeRecord( timeCoordValue, date, earthLocation , sdata); }else{ String profileName =zAxis.getShortName(); //Loop over vertical levels double[] vertCoords= new double[]{0.0}; int vertCoordsIndex = 0; if(targetLevel != null){ vertCoords[0] = targetLevel; vertCoordsIndex = zAxis.findCoordElementBounded(targetLevel); }else{ if(zAxis.getCoordValues().length > 1) vertCoords = zAxis.getCoordValues(); } int vertCoordsIndexForFile =0; for(double vertLevel : vertCoords){ //The zAxis was added to the variables and we need a structure data that contains z-levels StructureData sdata = StructureDataFactory.getFactory().createSingleStructureData(gridDataset, point, varsGroup, zAxis, true); //sdata.findMember("date").getDataArray().setObject(0, date.toString()); sdata.findMember("time").getDataArray().setDouble(0, timeCoordValue); sdata.findMember(zAxis.getShortName()).getDataArray().setDouble(0, zAxis.getCoordValues()[vertCoordsIndex] ); vertCoordsIndex++; vertCoordsIndexForFile++; int cont =0; // Iterating vars Iterator<String> itVars = varsGroup.iterator(); while (itVars.hasNext()) { String varName = itVars.next(); GridDatatype grid = gridDataset.findGridDatatype(varName); if( grid.getCoordinateSystem().getVerticalTransform() != null ){ double actualLevel = NcssRequestUtils.getActualVertLevel(grid, date, point, vertLevel); sdata.findMember( grid.getCoordinateSystem().getVerticalCT().getName() ).getDataArray().setDouble(0, actualLevel ); } if (gap.hasTime(grid, date) && gap.hasVert(grid, vertLevel)) { GridAsPointDataset.Point p = gap.readData(grid, date, ensCoord, vertLevel, point.getLatitude(), point.getLongitude() ); sdata.findMember(varName).getDataArray().setDouble(0, p.dataValue ); if(cont ==0){ earthLocation = new EarthLocationImpl(p.lat, p.lon, p.z); } }else{ //Set missing value sdata.findMember(varName).getDataArray().setDouble(0, gap.getMissingValue(grid) ); earthLocation = new EarthLocationImpl(point.getLatitude(), point.getLongitude() , vertLevel); } cont++; } if(ensCoord >=0){ writerCFTimeSeriesProfileCollection.writeRecord(profileName, timeCoordValue, ensCoord, date, earthLocation , sdata, vertCoordsIndexForFile-1); }else{ writerCFTimeSeriesProfileCollection.writeRecord(profileName, timeCoordValue, date, earthLocation , sdata, vertCoordsIndexForFile-1); } allDone = true; } } } } }catch(IOException ioe){ log.error("Error writing data", ioe); } return allDone; } @Override public boolean trailer(){ boolean allDone =false; try{ writerCFTimeSeriesProfileCollection.finish(); allDone =true; }catch(IOException ioe){ log.error("Error finishing WriterCFTimeSeriesProfileCollection: "+ioe); } return allDone; } public static CFTimeSeriesProfileCollectionWriterWrapper createWrapper(NetcdfFileWriter.Version version, String filePath, List<Attribute> atts ) throws IOException{ return new CFTimeSeriesProfileCollectionWriterWrapper(version, filePath, atts); } }