/*
* 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.controller;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import thredds.server.ncss.exception.OutOfBoundariesException;
import thredds.server.ncss.exception.TimeOutOfWindowException;
import thredds.server.ncss.exception.VariableNotContainedInDatasetException;
import thredds.server.ncss.params.NcssParamsBean;
import thredds.server.ncss.util.NcssRequestUtils;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dt.GridDatatype;
import ucar.nc2.dt.grid.GridAsPointDataset;
import ucar.nc2.dt.grid.GridDataset;
import ucar.nc2.time.Calendar;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateFormatter;
import ucar.nc2.time.CalendarDateRange;
import ucar.nc2.units.DateRange;
import ucar.nc2.units.DateType;
import ucar.nc2.units.TimeDuration;
/**
* @author mhermida
*
*/
public abstract class GridDatasetResponder {
public static CalendarDateRange getRequestedDateRange(NcssParamsBean params, Calendar cal) throws ParseException{
//CHECK --> must use Calendar!!!!
if(params.getTime()!=null){
CalendarDate date=null;
if( params.getTime().equalsIgnoreCase("present") ){
date =CalendarDate.of(new Date());
}else{
//date = CalendarDate.of( CalendarDateFormatter.isoStringToDate(params.getTime()) );
date = CalendarDateFormatter.isoStringToCalendarDate(cal, params.getTime());
}
return CalendarDateRange.of(date,date);
}
//We should have valid params here...
CalendarDateRange dates=null;
DateRange dr = new DateRange( new DateType(params.getTime_start() , null, null, cal), new DateType(params.getTime_end(), null, null, cal), new TimeDuration(params.getTime_duration()), null );
dates = CalendarDateRange.of(dr.getStart().getCalendarDate(), dr.getEnd().getCalendarDate() );
return dates;
}
public static String buildCacheUrl(String fileName){
return NcssRequestUtils.getTdsContext().getContextPath() + NcssController.getNCSSServletPath() + "/" + fileName;
}
// private GridDataset gds;
// private long maxFileDownloadSize;
// static private final short ESTIMATED_C0MPRESION_RATE = 5;
/**
*
* Returns true if all the variables have the same vertical axes.
* Throws exception if some of the variables in the request is not contained on the dataset
*/
protected boolean checkRequestedVars(GridDataset gds, NcssParamsBean params) throws VariableNotContainedInDatasetException{
//Check vars
if (params.getVar() == null || params.getVar().isEmpty()) return false; // err from VarParamsValidator
//if var = all--> all variables requested
if (params.getVar().get(0).equals("all")){
params.setVar(NcssRequestUtils.getAllVarsAsList(gds));
}
//Check not only all vars are contained in the grid, also they have the same vertical coords
Iterator<String> it = params.getVar().iterator();
String varName = it.next();
//GridDatatype grid = gds.findGridByShortName(varName);
GridDatatype grid = gds.findGridDatatype(varName);
if(grid == null)
throw new VariableNotContainedInDatasetException("Variable: "+varName+" is not contained in the requested dataset");
CoordinateAxis1D vertAxis = grid.getCoordinateSystem().getVerticalAxis();
CoordinateAxis1D newVertAxis = null;
boolean sameVertCoord = true;
while(sameVertCoord && it.hasNext()){
varName = it.next();
//grid = gds.findGridByShortName(varName);
grid = gds.findGridDatatype(varName);
if(grid == null)
throw new VariableNotContainedInDatasetException("Variable: "+varName+" is not contained in the requested dataset");
newVertAxis = grid.getCoordinateSystem().getVerticalAxis();
if( vertAxis != null ){
if( vertAxis.equals(newVertAxis)){
vertAxis = newVertAxis;
}else{
sameVertCoord = false;
}
}else{
if(newVertAxis != null) sameVertCoord = false;
}
}
return sameVertCoord;
}
protected Map<String, List<String>> groupVarsByVertLevels(GridDataset gds, NcssParamsBean params) throws VariableNotContainedInDatasetException{
String no_vert_levels ="no_vert_level";
List<String> vars = params.getVar();
Map<String, List<String>> varsGroupsByLevels = new HashMap<>();
for(String var :vars ){
GridDatatype grid =gds.findGridDatatype(var);
//Variables should have been checked before...
if(grid == null ){
throw new VariableNotContainedInDatasetException("Variable: "+var+" is not contained in the requested dataset");
}
CoordinateAxis1D axis = grid.getCoordinateSystem().getVerticalAxis();
String axisKey = null;
if(axis == null){
axisKey = no_vert_levels;
}else{
axisKey = axis.getShortName();
}
if( varsGroupsByLevels.containsKey(axisKey) ){
varsGroupsByLevels.get(axisKey).add(var);
}else{
List<String> varListForVerlLevel = new ArrayList<>();
varListForVerlLevel.add(var);
varsGroupsByLevels.put(axisKey, varListForVerlLevel);
}
}
return varsGroupsByLevels;
}
protected List<CalendarDate> getRequestedDates(GridDataset gds, NcssParamsBean params) throws OutOfBoundariesException, ParseException, TimeOutOfWindowException{
GridAsPointDataset gap = NcssRequestUtils.buildGridAsPointDataset(gds, params.getVar());
List<CalendarDate> dates = gap.getDates();
if(dates.isEmpty() ) return dates;
//Get the calendar
Calendar cal = dates.get(0).getCalendar();
long time_window = 0;
if( params.getTime_window() != null ){
TimeDuration dTW = new TimeDuration(params.getTime_window());
time_window = (long)dTW.getValueInSeconds()*1000;
}
//Check param temporal=all (ignore any other value) --> returns all dates
if(params.isAllTimes() ){
return dates;
}else{ //Check if some time param was provided, if not closest time to current
if(params.getTime()==null && params.getTime_start()==null && params.getTime_end()==null && params.getTime_duration()==null ){
//Closest to present
List<CalendarDate> closestToPresent = new ArrayList<>();
DateTime dt = new DateTime(new Date(), DateTimeZone.UTC) ;
CalendarDate now = CalendarDate.of(cal, dt.getMillis() );
CalendarDate start = dates.get(0);
CalendarDate end = dates.get(dates.size()-1);
if( now.isBefore(start) ){
//now = start;
if( time_window <= 0 || Math.abs(now.getDifferenceInMsecs(start)) < time_window ){
closestToPresent.add(start);
return closestToPresent;
}else{
throw new TimeOutOfWindowException("There is no time within the provided time window");
}
}
if( now.isAfter(end) ){
//now = end;
if( time_window <=0 || Math.abs(now.getDifferenceInMsecs(end)) < time_window ){
closestToPresent.add(end);
return closestToPresent;
}else{
throw new TimeOutOfWindowException("There is no time within the provided time window");
}
}
return NcssRequestUtils.wantedDates(gap, CalendarDateRange.of(now,now), time_window);
}
}
//We should have a time or a timeRange...
if(params.getTime_window()!=null && params.getTime()!=null){
DateRange dr = new DateRange( new DateType(params.getTime(), null, null, cal ), null, new TimeDuration(params.getTime_window()), null );
//time_window = CalendarDateRange.of(dr).getDurationInSecs()*1000;
time_window = CalendarDateRange.of(dr.getStart().getCalendarDate(), dr.getEnd().getCalendarDate()).getDurationInSecs()*1000;
}
CalendarDateRange dateRange = getRequestedDateRange(params, cal);
return NcssRequestUtils.wantedDates(gap, dateRange, time_window );
}
}