thredds.wcs.v1_0_0_Plus.DescribeCoverage Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of netcdf Show documentation
Show all versions of netcdf Show documentation
The NetCDF-Java Library is a Java interface to NetCDF files,
as well as to many other types of scientific data formats.
The newest version!
/*
* 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 org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Attribute;
import org.jdom2.output.XMLOutputter;
import java.util.List;
import java.util.Date;
import java.io.PrintWriter;
import java.io.IOException;
import ucar.nc2.dt.GridCoordSystem;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.dataset.CoordinateAxis1DTime;
import ucar.nc2.dataset.CoordinateAxis;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.units.DateFormatter;
import ucar.unidata.geoloc.LatLonRect;
import ucar.unidata.geoloc.LatLonPoint;
/**
* _more_
*
* @author edavis
* @since 4.0
*/
public class DescribeCoverage extends WcsRequest
{
// private static org.slf4j.Logger log =
// org.slf4j.LoggerFactory.getLogger( DescribeCoverage.class );
private List coverages;
private Document describeCoverageDoc;
public DescribeCoverage( Operation operation, String version, WcsDataset dataset,
List coverages )
{
super( operation, version, dataset );
this.coverages = coverages;
if ( this.coverages == null )
throw new IllegalArgumentException( "Non-null coverage list required." );
if ( this.coverages.size() < 1 )
throw new IllegalArgumentException( "Coverage list must contain at least one ID <" + this.coverages.size() + ">." );
String badCovIds = "";
for ( String curCov : coverages )
{
if ( ! this.getDataset().isAvailableCoverageName( curCov))
badCovIds += (badCovIds.length() > 0 ? ", " : "") + curCov;
}
if ( badCovIds.length() > 0 )
throw new IllegalArgumentException("Coverage ID list contains one or more unknown IDs <" + badCovIds + ">." );
}
public Document getDescribeCoverageDoc()
{
if ( this.describeCoverageDoc == null )
describeCoverageDoc = generateDescribeCoverageDoc();
return describeCoverageDoc;
}
public void writeDescribeCoverageDoc( PrintWriter pw )
throws IOException
{
XMLOutputter xmlOutputter = new XMLOutputter( org.jdom2.output.Format.getPrettyFormat() );
xmlOutputter.output( getDescribeCoverageDoc(), pw );
}
public Document generateDescribeCoverageDoc()
{
// CoverageDescription (wcs) [1]
Element coverageDescriptionsElem = new Element( "CoverageDescription", wcsNS );
coverageDescriptionsElem.addNamespaceDeclaration( gmlNS );
coverageDescriptionsElem.addNamespaceDeclaration( xlinkNS );
coverageDescriptionsElem.setAttribute( "version", this.getVersion() );
// ToDo Consider dealing with "updateSequence"
// coverageDescriptionsElem.setAttribute( "updateSequence", this.getCurrentUpdateSequence() );
for ( String curCoverageId : this.coverages )
coverageDescriptionsElem.addContent( genCoverageOfferingElem( curCoverageId ) );
return new Document( coverageDescriptionsElem );
}
public Element genCoverageOfferingElem( String covId )
{
WcsCoverage coverage = this.getDataset().getAvailableCoverage( covId );
GridCoordSystem gridCoordSystem = coverage.getCoordinateSystem();
// CoverageDescription/CoverageOffering (wcs) [1..*]
Element covDescripElem = genCoverageOfferingBriefElem( "CoverageOffering", covId,
coverage.getLabel(), coverage.getDescription(),
gridCoordSystem );
// CoverageDescription/CoverageOffering/domainSet [1]
covDescripElem.addContent( genDomainSetElem( coverage));
// CoverageDescription/CoverageOffering/rangeSet [1]
covDescripElem.addContent( genRangeSetElem( coverage));
// CoverageDescription/CoverageOffering/supportedCRSs [1]
covDescripElem.addContent( genSupportedCRSsElem( coverage));
// CoverageDescription/CoverageOffering/supportedFormats [1]
covDescripElem.addContent( genSupportedFormatsElem( coverage) );
// CoverageDescription/CoverageOffering/supportedInterpolations [0..1]
covDescripElem.addContent( genSupportedInterpolationsElem() );
return covDescripElem;
}
private Element genDomainSetElem( WcsCoverage coverage)
{
// ../domainSet
Element domainSetElem = new Element( "domainSet", wcsNS);
// ../domainSet/spatialDomain [0..1] AND/OR temporalDomain [0..1]
domainSetElem.addContent( genSpatialDomainElem( coverage) );
if ( coverage.getCoordinateSystem().hasTimeAxis() )
{
domainSetElem.addContent( genTemporalDomainElem( coverage.getCoordinateSystem().getTimeAxis1D() ) );
}
return domainSetElem;
}
private Element genSpatialDomainElem( WcsCoverage coverage )
{
// ../domainSet/spatialDomain
Element spatialDomainElem = new Element( "spatialDomain", wcsNS );
// ../domainSet/spatialDomain/gml:Envelope [1..*]
spatialDomainElem.addContent( this.genEnvelopeElem( coverage.getCoordinateSystem()));
// ../domainSet/spatialDomain/gml:RectifiedGrid [0..*]
spatialDomainElem.addContent( this.genRectifiedGridElem( coverage));
// ../domainSet/spatialDomain/gml:Polygon [0..*]
return spatialDomainElem;
}
private Element genRectifiedGridElem( WcsCoverage coverage )
{
// ../spatialDomain/gml:RectifiedGrid
Element rectifiedGridElem = new Element( "RectifiedGrid", gmlNS);
CoordinateAxis1D xaxis = (CoordinateAxis1D) coverage.getCoordinateSystem().getXHorizAxis();
CoordinateAxis1D yaxis = (CoordinateAxis1D) coverage.getCoordinateSystem().getYHorizAxis();
CoordinateAxis1D zaxis = coverage.getCoordinateSystem().getVerticalAxis();
// ../spatialDomain/gml:RectifiedGrid@srsName [0..1] (URI)
rectifiedGridElem.setAttribute( "srsName", coverage.getNativeCrs() );
// ../spatialDomain/gml:RectifiedGrid@dimension [1] (positive integer)
int ndim = ( zaxis != null ) ? 3 : 2;
rectifiedGridElem.setAttribute( "dimension", Integer.toString( ndim ) );
// ../spatialDomain/gml:RectifiedGrid/gml:limits [1]
int[] minValues = new int[ndim];
int[] maxValues = new int[ndim];
maxValues[0] = (int) ( xaxis.getSize() - 1 );
maxValues[1] = (int) ( yaxis.getSize() - 1 );
if ( zaxis != null )
maxValues[2] = (int) ( zaxis.getSize() - 1 );
Element limitsElem = new Element( "limits", gmlNS);
// ../spatialDomain/gml:RectifiedGrid/gml:limits/gml:GridEnvelope [1]
// ../spatialDomain/gml:RectifiedGrid/gml:limits/gml:GridEnvelope/gml:low [1] (integer list)
// ../spatialDomain/gml:RectifiedGrid/gml:limits/gml:GridEnvelope/gml:high [1] (integer list)
limitsElem.addContent(
new Element( "GridEnvelope", gmlNS)
.addContent( new Element( "low", gmlNS ).addContent( genIntegerListString( minValues)))
.addContent( new Element( "high", gmlNS).addContent( genIntegerListString( maxValues))) );
rectifiedGridElem.addContent( limitsElem);
// ../spatialDomain/gml:RectifiedGrid/gml:axisName [1..*] (string)
rectifiedGridElem.addContent( new Element( "axisName", gmlNS).addContent( "x"));
rectifiedGridElem.addContent( new Element( "axisName", gmlNS).addContent( "y"));
if ( zaxis != null )
rectifiedGridElem.addContent( new Element( "axisName", gmlNS ).addContent( "z" ) );
// ../spatialDomain/gml:RectifiedGrid/gml:origin [1]
// ../spatialDomain/gml:RectifiedGrid/gml:origin/gml:pos [1] (space seperated list of double values)
// ../spatialDomain/gml:RectifiedGrid/gml:origin/gml:pos@dimension [0..1] (number of entries in list)
double[] origin = new double[ndim];
origin[0] = xaxis.getStart();
origin[1] = yaxis.getStart();
if ( zaxis != null )
origin[2] = zaxis.getStart();
rectifiedGridElem.addContent(
new Element( "origin", gmlNS).addContent(
new Element( "pos", gmlNS).addContent( genDoubleListString( origin))));
// ../spatialDomain/gml:RectifiedGrid/gml:offsetVector [1..*] (space seperated list of double values)
// ../spatialDomain/gml:RectifiedGrid/gml:offsetVector@dimension [0..1] (number of entries in list)
double[] xoffset = new double[ndim];
xoffset[0] = xaxis.getIncrement();
rectifiedGridElem.addContent(
new Element( "offsetVector", gmlNS)
.addContent( genDoubleListString( xoffset)));
double[] yoffset = new double[ndim];
yoffset[1] = yaxis.getIncrement();
rectifiedGridElem.addContent(
new Element( "offsetVector", gmlNS )
.addContent( genDoubleListString( yoffset ) ) );
if ( zaxis != null )
{
double[] zoffset = new double[ndim];
zoffset[2] = zaxis.getIncrement();
rectifiedGridElem.addContent(
new Element( "offsetVector", gmlNS )
.addContent( genDoubleListString( zoffset ) ) );
}
return rectifiedGridElem;
}
private String genIntegerListString( int[] values)
{
StringBuilder buf = new StringBuilder();
for ( int intValue : values )
{
if ( buf.length() > 0 )
buf.append( " ");
buf.append( intValue);
}
return buf.toString();
}
private String genDoubleListString( double[] values)
{
StringBuffer buf = new StringBuffer();
for ( double doubleValue : values )
{
if ( buf.length() > 0 )
buf.append( " ");
buf.append( doubleValue);
}
return buf.toString();
}
private Element genEnvelopeElem( GridCoordSystem gcs )
{
// spatialDomain/Envelope
Element envelopeElem;
if ( gcs.hasTimeAxis() )
envelopeElem = new Element( "EnvelopeWithTimePeriod", wcsNS );
else
envelopeElem = new Element( "Envelope", wcsNS );
// spatialDomain/Envelope@srsName [0..1] (URI)
envelopeElem.setAttribute( "srsName", "urn:ogc:def:crs:OGC:1.3:CRS84" );
LatLonRect llbb = gcs.getLatLonBoundingBox();
LatLonPoint llpt = llbb.getLowerLeftPoint();
LatLonPoint urpt = llbb.getUpperRightPoint();
double lon = llpt.getLongitude() + llbb.getWidth();
int posDim = 2;
String firstPosition = llpt.getLongitude() + " " + llpt.getLatitude();
String secondPosition = lon + " " + urpt.getLatitude();
// ToDo WCS 1.0Plus - Deal with conversion to meters. (Yikes!!)
CoordinateAxis1D vertAxis = gcs.getVerticalAxis();
if ( vertAxis != null )
{
posDim++;
// See verAxis.getUnitsString()
double zeroIndexValue = vertAxis.getCoordValue( 0 );
double sizeIndexValue = vertAxis.getCoordValue( ( (int) vertAxis.getSize() ) - 1 );
if ( vertAxis.getPositive().equals( ucar.nc2.constants.CF.POSITIVE_UP ) )
{
firstPosition += " " + zeroIndexValue;
secondPosition += " " + sizeIndexValue;
}
else
{
firstPosition += " " + sizeIndexValue;
secondPosition += " " + zeroIndexValue;
}
}
String posDimString = Integer.toString( posDim );
// spatialDomain/Envelope/gml:pos [2] (space seperated list of double values)
// spatialDomain/Envelope/gml:pos@dimension [0..1] (number of entries in list)
envelopeElem.addContent(
new Element( "pos", gmlNS )
.addContent( firstPosition )
.setAttribute( new Attribute( "dimension", posDimString) ) );
envelopeElem.addContent(
new Element( "pos", gmlNS )
.addContent( secondPosition )
.setAttribute( new Attribute( "dimension", posDimString ) ) );
// spatialDomain/Envelope/gml:timePostion [2]
if ( gcs.hasTimeAxis() )
{
envelopeElem.addContent(
new Element( "timePosition", gmlNS).addContent(
gcs.getCalendarDateRange().getStart().toString()));
envelopeElem.addContent(
new Element( "timePosition", gmlNS).addContent(
gcs.getCalendarDateRange().getEnd().toString()));
}
return envelopeElem;
}
private Element genTemporalDomainElem(CoordinateAxis1DTime timeAxis) {
// temporalDomain
Element temporalDomainElem = new Element("temporalDomain", wcsNS);
List dates = timeAxis.getCalendarDates();
// temporalDomain/timePosition [1..*]
for (CalendarDate curDate : dates) {
temporalDomainElem.addContent(
new Element("timePosition", gmlNS).addContent(curDate.toString()));
}
return temporalDomainElem;
}
private Element genRangeSetElem( WcsCoverage coverage )
{
// rangeSet
Element rangeSetElem = new Element( "rangeSet", wcsNS);
// rangeSet/RangeSet
// rangeSet/RangeSet@semantic
// rangeSet/RangeSet@refSys
// rangeSet/RangeSet@refSysLabel
//Element innerRangeSetElem = new Element( "RangeSet", wcsNS);
// ToDo How deal with range fields and thier axes?
for ( WcsRangeField curField: coverage.getRange() )
{
Element fieldElem = new Element( "Field", wcsNS);
// rangeSet/RangeSet/description [0..1]
if ( curField.getDescription() != null )
fieldElem.addContent(
new Element( "description")
.addContent( curField.getDescription() ) );
// rangeSet/RangeSet/name [1]
fieldElem.addContent(
new Element( "name", wcsNS).addContent( curField.getName()));
// rangeSet/RangeSet/label [1]
fieldElem.addContent(
new Element( "label", wcsNS ).addContent( curField.getLabel() ) );
fieldElem.addContent(
new Element( "dataType", wcsNS ).addContent( curField.getDatatypeString() ) );
fieldElem.addContent(
new Element( "units", wcsNS ).addContent( curField.getUnitsString() ) );
// .../values/interval
// .../values/interval/min [0..1]
// .../values/interval/max [0..1]
// .../values/interval/res [0..1]
Element valuesElem = new Element( "AllowedValues", wcsNS );
Element minElem = new Element( "MinimumValue", wcsNS )
.addContent( Double.toString(
curField.getValidMin()));
Element maxElem = new Element( "MaximumValue", wcsNS )
.addContent( Double.toString(
curField.getValidMin()));
valuesElem.addContent( new Element( "Range", wcsNS)
.addContent( minElem )
.addContent( maxElem ));
List axes = curField.getAxes();
for ( WcsRangeField.Axis curAxis: axes)
{
if ( curAxis != null )
{
// rangeSet/RangeSet/axisDescription [0..*]
//Element axisDescElem = new Element( "axisDescription", wcsNS );
Element axisDescElem = new Element( "Axis", wcsNS );
// rangeSet/RangeSet/axisDescription/AxisDescription [1]
//Element innerAxisDescElem = new Element( "AxisDescription", wcsNS);
// rangeSet/RangeSet/axisDescription/AxisDescription/name [1]
// rangeSet/RangeSet/axisDescription/AxisDescription/label [1]
axisDescElem.addContent( new Element( "name", wcsNS).addContent( curAxis.getName() ));
axisDescElem.addContent( new Element( "label", wcsNS).addContent( curAxis.getLabel()));
// rangeSet/RangeSet/axisDescription/AxisDescription/values [1]
Element axisValuesElem = new Element( "values", wcsNS);
// rangeSet/RangeSet/axisDescription/AxisDescription/values/singleValue [1..*]
// ----- interval is alternate for singleValue
// rangeSet/RangeSet/axisDescription/AxisDescription/values/interval
// rangeSet/RangeSet/axisDescription/AxisDescription/values/interval/min [0..1]
// rangeSet/RangeSet/axisDescription/AxisDescription/values/interval/max [0..1]
// rangeSet/RangeSet/axisDescription/AxisDescription/values/interval/res [0..1]
// -----
for ( String curVal : curAxis.getValues())
axisValuesElem.addContent(
new Element( "singleValue", wcsNS)
.addContent( curVal));
// rangeSet/RangeSet/axisDescription/AxisDescription/values/default [0..1]
axisDescElem.addContent( axisValuesElem);
fieldElem.addContent( axisDescElem);
}
}
// rangeSet/RangeSet/nullValues [0..1]
// rangeSet/RangeSet/nullValues/{interval|singleValue} [1..*]
if ( curField.hasMissingData() )
{
fieldElem.addContent(
new Element( "nullValues", wcsNS).addContent(
new Element( "singleValue", wcsNS)
// ToDo Is missing always NaN?
.addContent( "NaN" ) ) );
}
rangeSetElem.addContent( fieldElem);
}
return rangeSetElem;
}
private Element genSupportedCRSsElem( WcsCoverage coverage )
{
// supportedCRSs
Element supportedCRSsElem = new Element( "supportedCRSs", wcsNS);
// supportedCRSs/requestCRSs [1..*] (wcs) (space seperated list of strings)
// supportedCRSs/requestCRSs@codeSpace [0..1] (URI)
supportedCRSsElem.addContent(
new Element( "requestCRSs", wcsNS)
.addContent( coverage.getDefaultRequestCrs()));
// supportedCRSs/responseCRSs [1..*] (wcs) (space seperated list of strings)
// supportedCRSs/responseCRSs@codeSpace [0..1] (URI)
supportedCRSsElem.addContent(
new Element( "responseCRSs", wcsNS)
.addContent( coverage.getNativeCrs()));
return supportedCRSsElem;
}
private Element genSupportedFormatsElem( WcsCoverage coverage)
{
// supportedFormats
// supportedFormats@nativeFormat [0..1] (string)
Element supportedFormatsElem = new Element( "supportedFormats", wcsNS );
// supportedFormats/formats [1..*] (wcs) (space seperated list of strings)
// supportedFormats/formats@codeSpace [0..1] (URI)
for ( WcsRequest.Format curFormat : coverage.getSupportedCoverageFormatList() )
{
supportedFormatsElem.addContent(
new Element( "formats", wcsNS )
.addContent( curFormat.toString() ) );
}
return supportedFormatsElem;
}
private Element genSupportedInterpolationsElem()
{
// supportedInterpolations
// supportedInterpolations@default [0..1] ???
Element supportedInterpolationsElem = new Element( "supportedInterpolations", wcsNS);
// supportedInterpolations/interpolationMethod [1..*]
supportedInterpolationsElem.addContent(
new Element( "interpolationMethod", wcsNS)
.addContent( "none"));
return supportedInterpolationsElem;
}
}