/*
* 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.params;
import org.springframework.format.annotation.DateTimeFormat;
import thredds.server.ncss.exception.NcssException;
import thredds.server.ncss.validation.NcssRequestConstraint;
import thredds.server.ncss.validation.TimeParamsConstraint;
import thredds.server.ncss.validation.VarParamConstraint;
import ucar.nc2.ft.FeatureDataset;
import ucar.nc2.ft.point.collection.UpdateableCollection;
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;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonRect;
import java.io.IOException;
import java.text.ParseException;
import java.util.Date;
import java.util.Formatter;
import java.util.List;
/**
* Ncss Parameters
*
* @author caron
* @since 10/5/13
*/
@TimeParamsConstraint
@NcssRequestConstraint
public class NcssParamsBean {
static private org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger("featureCollectionScan");
private String accept;
@VarParamConstraint
private List<String> var;
@DateTimeFormat
private String time_start;
@DateTimeFormat
private String time_end;
@DateTimeFormat
private String time_duration;
@DateTimeFormat
private String time_window; // time_window is meant to be used with time=present. When time=present it returns the closest time to current in the dataset
// but if the dataset does not have up to date data that could be really far from the current time and most
// likely useless (in particular for observation data).
// time_window tells the server give me the data if it's within this period otherwise don't bother.
// time_window must be a valid W3C time duration.
@DateTimeFormat
private String time;
private String temporal; // == all
private Double north;
private Double south;
private Double east;
private Double west;
private Double latitude;
private Double longitude;
//// grid only
private Double minx;
private Double maxx;
private Double miny;
private Double maxy;
private boolean addLatLon;
private Integer horizStride = 1;
private Integer timeStride = 1;
private Double vertCoord;
private Integer vertStride=1;
//// station only
private List<String> stns;
/////////////////////////////////////////////////////
public String getAccept() {
return accept;
}
public void setAccept(String accept) {
this.accept = accept;
}
public List<String> getVar() {
return var;
}
public void setVar(List<String> var) {
this.var = var;
}
public String getTime_start() {
return time_start;
}
public void setTime_start(String time_start) {
this.time_start = time_start;
}
public String getTime_end() {
return time_end;
}
public void setTime_end(String time_end) {
this.time_end = time_end;
}
public String getTime_duration() {
return time_duration;
}
public void setTime_duration(String time_duration) {
this.time_duration = time_duration;
}
public String getTime_window() {
return time_window;
}
public void setTime_window(String time_window) {
this.time_window = time_window;
}
public String getTime() {
return time;
}
public void setTime(String time) {
this.time = time;
}
public Double getVertCoord() {
return vertCoord;
}
public void setVertCoord(Double vertCoord) {
this.vertCoord = vertCoord;
}
public Double getNorth() {
return north;
}
public void setNorth(Double north) {
this.north = north;
}
public Double getSouth() {
return south;
}
public void setSouth(Double south) {
this.south = south;
}
public Double getEast() {
return east;
}
public void setEast(Double east) {
this.east = east;
}
public Double getWest() {
return west;
}
public void setWest(Double west) {
this.west = west;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getMinx() {
return minx;
}
public void setMinx(Double minx) {
this.minx = minx;
}
public Double getMaxx() {
return maxx;
}
public void setMaxx(Double maxx) {
this.maxx = maxx;
}
public Double getMiny() {
return miny;
}
public void setMiny(Double miny) {
this.miny = miny;
}
public Double getMaxy() {
return maxy;
}
public void setMaxy(Double maxy) {
this.maxy = maxy;
}
public boolean isAddLatLon() {
return addLatLon;
}
public void setAddLatLon(boolean addLatLon) {
this.addLatLon = addLatLon;
}
public Integer getHorizStride() {
return horizStride;
}
public void setHorizStride(Integer horizStride) {
this.horizStride = horizStride;
}
public Integer getTimeStride() {
return timeStride;
}
public void setTimeStride(Integer timeStride) {
this.timeStride = timeStride;
}
public Integer getVertStride() {
return vertStride;
}
public void setVertStride(Integer vertStride) {
this.vertStride = vertStride;
}
public List<String> getStns() {
return stns;
}
public void setStns(List<String> stns) {
this.stns = stns;
}
public boolean hasLatLonPoint() {
return latitude != null && longitude != null;
}
public boolean hasLatLonBB() { // need to validate
return east != null && west != null && north != null && south != null;
}
public boolean hasProjectionBB() { // need to validate
return minx != null && miny != null && maxx != null && maxy != null;
}
public boolean hasStations() {
return stns != null && !stns.isEmpty();
}
public String getTemporal() {
return temporal;
}
public void setTemporal(String temporal) {
this.temporal = temporal;
}
public boolean isAllTimes() {
return temporal != null && temporal.equalsIgnoreCase("all");
}
public TimeDuration parseTimeDuration() throws NcssException {
if (getTime_duration() == null) return null;
try {
return TimeDuration.parseW3CDuration(getTime_duration());
} catch (ParseException e) {
throw new NcssException("invalid time duration");
}
}
public LatLonRect getBoundingBox() {
if (!hasLatLonBB()) {
return null;
} else {
double width = getEast() - getWest();
double height = getNorth() - getSouth();
return new LatLonRect(new LatLonPointImpl(getSouth(), getWest()), height, width);
}
}
private boolean hasValidTime;
private boolean hasValidDateRange;
public void setHasValidTime(boolean hasValidTime) {
this.hasValidTime = hasValidTime;
}
public void setHasValidDateRange(boolean hasValidDateRange) {
this.hasValidDateRange = hasValidDateRange;
}
public CalendarDateRange getCalendarDateRange(Calendar cal) throws ParseException {
if (!hasValidDateRange) return null;
DateRange dr;
if (time == null)
dr = new DateRange( new DateType(time_start, null, null, cal), new DateType(time_end, null, null, cal), new TimeDuration(time_duration), null );
else{
//DateType dtDate = new DateType(time, null, null, cal);
dr = new DateRange( new DateType(time, null, null, cal), new DateType(time, null, null, cal), new TimeDuration(time_duration), null );
}
//return CalendarDateRange.of(dr );
return CalendarDateRange.of(dr.getStart().getCalendarDate(), dr.getEnd().getCalendarDate() );
}
public CalendarDate getRequestedTime( Calendar cal ) throws ParseException {
if (!hasValidTime) return null;
CalendarDate date=null;
if( getTime().equalsIgnoreCase("present") ){
java.util.Calendar c = java.util.Calendar.getInstance();
c.setTime( new Date() );
return CalendarDate.of( cal, c.getTimeInMillis() );
}
// default calendar (!)
return CalendarDateFormatter.isoStringToCalendarDate(cal, getTime());
}
public boolean isValidGridRequest() {
return true;
}
public boolean intersectsTime(FeatureDataset fd, Formatter errs) throws ParseException {
CalendarDateRange have = fd.getCalendarDateRange();
try {
//
// if this is an updatable collection, check for a new CalendarDateRange (i.e. new
// data available).
// The issue is that fd does not get updated with thredds.catalog.InvDatasetFcPoint
//
// This allows requests to be fulfilled, even if the correct CalendarDateRange is
// not available in the catalog. Ideally these should be synced.
//
UpdateableCollection uc = (UpdateableCollection) fd;
have = uc.update();
} catch (IOException ioe) {
fd.getLocation();
log.error("NCSS I/O Error for location %s", fd.getLocation());
log.error(ioe.getLocalizedMessage());
} catch (ClassCastException cce) {
// not an updateable collection...just keep going.
}
if (have == null) return true;
Calendar dataCal = have.getStart().getCalendar(); // use the same calendar as the dataset
CalendarDateRange want = getCalendarDateRange(dataCal);
if (want != null) {
if (have.intersects(want)) {
return true;
} else {
errs.format("Requested time range %s does not intersect actual time range %s", want, have);
return false;
}
}
CalendarDate wantTime = getRequestedTime(dataCal);
if (wantTime == null) return true;
if (!have.includes(wantTime)) {
errs.format("Requested time %s does not intersect actual time range %s", wantTime, have);
return false;
}
return true;
}
}