/* * Copyright 1998-2009 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.wcs.v1_0_0_Plus; import ucar.nc2.NetcdfFileWriter; import ucar.nc2.dt.GridDatatype; import ucar.nc2.dt.GridCoordSystem; import ucar.nc2.dt.GridDataset; import ucar.nc2.dt.grid.CFGridWriter2; import ucar.nc2.time.CalendarDateRange; import ucar.nc2.util.DiskCache2; import ucar.unidata.geoloc.*; import ucar.unidata.geoloc.ogc.EPSG_OGC_CF_Helper; import ucar.ma2.Range; import ucar.ma2.InvalidRangeException; import ucar.ma2.Array; import java.util.*; import java.io.File; import java.io.IOException; import ucar.nc2.dataset.CoordinateAxis1DTime; import ucar.nc2.geotiff.GeotiffWriter; /** * _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(); } static private DiskCache2 diskCache = null; static public void setDiskCache( DiskCache2 _diskCache ) { diskCache = _diskCache; } static private 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() + "]." ); } } }