thredds.client.catalog.ThreddsMetadata Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 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.client.catalog;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import thredds.client.catalog.builder.CatalogBuilder;
import thredds.client.catalog.builder.DatasetBuilder;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants.CF;
import ucar.nc2.dataset.CoordinateAxis1D;
import ucar.nc2.units.SimpleUnit;
import ucar.unidata.geoloc.LatLonPointImpl;
import ucar.unidata.geoloc.LatLonRect;
import javax.annotation.concurrent.Immutable;
import java.net.URI;
import java.util.*;
/**
* Thredds Metadata. Immutable after calling finish.
*
* @author John
* @since 1/10/2015
*/
// @Immutable
public class ThreddsMetadata implements ThreddsMetadataContainer {
static private final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(ThreddsMetadata.class);
private final Map flds;
private boolean immutable;
public ThreddsMetadata() {
this.flds = new HashMap<>();
}
// make mutable copy
public ThreddsMetadata(ThreddsMetadata from) {
this.flds = new HashMap<>();
for (Map.Entry entry : from.flds.entrySet()) // bypass immutable
this.flds.put(entry.getKey(), entry.getValue());
}
// do not use after building
public Map getFlds() {
if (immutable) throw new UnsupportedOperationException();
return flds;
}
public Iterable> getFldIterator() {
return flds.entrySet();
}
public void set(String fldName, Object fldValue) {
if (immutable)
throw new UnsupportedOperationException();
if (fldValue != null)
flds.put( fldName, fldValue);
else
flds.remove(fldName);
}
public void addToList(String fldName, Object fldValue) {
if (immutable) throw new UnsupportedOperationException();
if (fldValue != null) DatasetBuilder.addToList(flds, fldName, fldValue);
}
public void finish() {
immutable = true;
}
public boolean isImmutable() {
return immutable;
}
@Override
public Object getLocalField(String fldName) {
return flds.get(fldName);
}
@Override
public List getLocalFieldAsList(String fldName) {
Object value = flds.get(fldName);
if (value != null) {
if (value instanceof List) return (List) value;
List result = new ArrayList(1);
result.add(value);
return result;
}
return new ArrayList(0);
}
//////////////////////////////////////////////////////////////////////////////////////////////
@Immutable
static public class Contributor {
public final String name;
public final String role;
public Contributor(String name, String role) {
this.name = name;
this.role = role;
}
public String getName() {
return name;
}
public String getRole() {
return role;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* Implements Source type, used by publisher and creator elements.
*/
@Immutable
static public class Source {
public final Vocab name;
public final String url, email;
public Source(Vocab name, String url, String email) {
this.name = name;
this.url = url;
this.email = email;
}
public Vocab getNameVocab() {
return this.name;
}
public String getName() {
return this.name.getText();
}
public String getUrl() {
return url;
}
public String getEmail() {
return email;
}
public String getVocabulary() {
return this.name.getVocabulary();
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* Implements Vocab type, just text with an optional "vocabulary" attribute.
*/
@Immutable
static public class Vocab {
public final String text;
public final String vocabulary;
public Vocab(String text, String vocabulary) {
this.text = text;
this.vocabulary = vocabulary;
}
public String getText() {
return text;
}
public String getVocabulary() {
return vocabulary;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* Implements GeospatialCoverage type.
*/
@Immutable
static public class GeospatialCoverage {
/* static private GeospatialRange defaultEastwest = new GeospatialRange(0.0, 0.0, Double.NaN, CDM.LON_UNITS);
static private GeospatialRange defaultNorthsouth = new GeospatialRange(0.0, 0.0, Double.NaN, CDM.LAT_UNITS);
static private GeospatialRange defaultUpdown = new GeospatialRange(0.0, 0.0, Double.NaN, "km");
static private GeospatialCoverage empty = new GeospatialCoverage(); */
public final GeospatialRange eastwest, northsouth, updown;
public final boolean isGlobal;
public final String zpositive;
public final List names;
// constructor from catalog
public GeospatialCoverage(GeospatialRange eastwest, GeospatialRange northsouth, GeospatialRange updown, List names, String zpositive) {
this.eastwest = eastwest; // : new Range(defaultEastwest);
this.northsouth = northsouth; // : new Range(defaultNorthsouth);
this.updown = updown; // : new Range(defaultUpdown);
this.zpositive = (zpositive != null) ? zpositive : "up";
this.names = names;
boolean isGlobalCheck = false;
if (names != null) {
for (Vocab name : names) {
String elem = name.getText();
if (elem.equalsIgnoreCase("global")) isGlobalCheck = true;
}
}
this.isGlobal = isGlobalCheck;
}
// constructor from dataset
public GeospatialCoverage(LatLonRect bb, CoordinateAxis1D vaxis, double dX, double dY) {
if (bb == null) {
this.eastwest = null;
this.northsouth = null;
this.isGlobal = false;
this.names = null;
} else {
LatLonPointImpl llpt = bb.getLowerLeftPoint();
LatLonPointImpl urpt = bb.getUpperRightPoint();
double height = urpt.getLatitude() - llpt.getLatitude();
this.eastwest = new GeospatialRange(llpt.getLongitude(), bb.getWidth(), dX, CDM.LON_UNITS);
this.northsouth = new GeospatialRange(llpt.getLatitude(), height, dY, CDM.LAT_UNITS);
if ((bb.getWidth() >= (360-dX)) && (height >= (180-dY))) {
this.isGlobal = true;
// serialize isGlobal
this.names = new ArrayList<>();
this.names.add(new Vocab("global", null));
} else {
this.isGlobal = false;
this.names = null;
}
}
if (vaxis == null) {
this.updown = null;
this.zpositive = null;
} else {
int n = (int) vaxis.getSize();
double size = vaxis.getCoordValue(n - 1) - vaxis.getCoordValue(0);
double resolution = vaxis.getIncrement();
String units = vaxis.getUnitsString();
this.updown = new GeospatialRange(vaxis.getCoordValue(0), size, resolution, units);
if (units != null) {
boolean isPositive = SimpleUnit.isCompatible("m", units);
this.zpositive = isPositive ? CF.POSITIVE_UP : CF.POSITIVE_DOWN;
} else {
this.zpositive = CF.POSITIVE_UP;
}
}
}
/* public boolean isEmpty() {
return this.equals(empty);
} */
public GeospatialRange getEastWestRange() {
return eastwest;
}
public GeospatialRange getNorthSouthRange() {
return northsouth;
}
public GeospatialRange getUpDownRange() {
return updown;
}
public List getNames() {
return names != null ? names : new ArrayList();
}
/**
* @return "up" or "down"
*/
public String getZPositive() {
return zpositive;
}
public boolean isZPositiveUp() {
return zpositive.equalsIgnoreCase("up");
}
public boolean isValid() {
return isGlobal || ((eastwest != null) && (northsouth != null));
}
public boolean isGlobal() {
return isGlobal;
}
public double getLatStart() {
return (northsouth == null) ? Double.NaN : northsouth.start;
}
/**
* @return latitude extent - may be positive or negetive
*/
public double getLatExtent() {
return (northsouth == null) ? Double.NaN : northsouth.size;
}
/**
* @return latitude resolution: 0.0 or NaN means not set.
*/
public double getLatResolution() {
return (northsouth == null) ? Double.NaN : northsouth.resolution;
}
/**
* @return latitude units
*/
public String getLatUnits() {
return (northsouth == null) ? null : northsouth.units;
}
// LOOK not dealing with units degrees_south
public double getLatNorth() {
return Math.max(northsouth.start, northsouth.start + northsouth.size);
}
public double getLatSouth() {
return Math.min(northsouth.start, northsouth.start + northsouth.size);
}
/**
* @return starting longitude
*/
public double getLonStart() {
return (eastwest == null) ? Double.NaN : eastwest.start;
}
/**
* @return longitude extent - may be positive or negetive
*/
public double getLonExtent() {
return (eastwest == null) ? Double.NaN : eastwest.size;
}
/**
* @return longitude resolution: 0.0 or NaN means not set.
*/
public double getLonResolution() {
return (eastwest == null) ? Double.NaN : eastwest.resolution;
}
/**
* @return longitude units
*/
public String getLonUnits() {
return (eastwest == null) ? null : eastwest.units;
}
// LOOK not dealing with units degrees_west
public double getLonEast() {
if (eastwest == null) return Double.NaN;
return Math.max(eastwest.start, eastwest.start + eastwest.size);
}
public double getLonWest() {
if (eastwest == null) return Double.NaN;
return Math.min(eastwest.start, eastwest.start + eastwest.size);
}
/**
* @return starting height
*/
public double getHeightStart() {
return updown == null ? 0.0 : updown.start;
}
/**
* @return height extent - may be positive or negetive
*/
public double getHeightExtent() {
return updown == null ? 0.0 : updown.size;
}
/**
* @return height resolution: 0.0 or NaN means not set.
*/
public double getHeightResolution() {
return updown == null ? 0.0 : updown.resolution;
}
/**
* @return height units
*/
public String getHeightUnits() {
return updown == null ? null : updown.units;
}
public LatLonRect getBoundingBox() {
return isGlobal ? new LatLonRect() :
new LatLonRect(new LatLonPointImpl(getLatStart(), getLonStart()), getLatExtent(), getLonExtent());
}
}
/**
* Implements spatialRange type.
*/
@Immutable
static public class GeospatialRange {
public final double start, size, resolution;
public final String units;
/**
* Constructor
*
* @param start starting value
* @param size ending = start + size
* @param resolution data resolution, or NaN if unknown
* @param units what units are start, size in?
*/
public GeospatialRange(double start, double size, double resolution, String units) {
this.start = start;
this.size = size;
this.resolution = resolution;
this.units = units;
}
/**
* Copy constructor
*
* @param from copy this
*/
public GeospatialRange(GeospatialRange from) {
this.start = from.start;
this.size = from.size;
this.resolution = from.resolution;
this.units = from.units;
}
public double getStart() {
return start;
}
public double getSize() {
return size;
}
public double getResolution() {
return resolution;
}
public String getUnits() {
return units;
}
public boolean hasResolution() {
return (resolution != 0.0) && !Double.isNaN(resolution);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////
/**
* Implements Variable type.
*/
@Immutable
static public class Variable implements Comparable {
public final String name, desc, vocabulary_name, units, id;
public Variable(String name, String desc, String vocabulary_name, String units, String id) {
this.name = name;
this.desc = desc;
this.vocabulary_name = vocabulary_name;
this.units = units;
this.id = id;
}
public String getName() {
return name;
}
public String getDescription() {
return desc;
}
public String getVocabularyName() {
return vocabulary_name;
}
// need unique id, cant count on name, eg because of variant GRIB tables
public String getVocabularyId() {
return id;
}
public String getUnits() {
return units;
}
@Override
public int compareTo(Variable o) {
return name.compareTo(o.name);
}
}
@Immutable
public static class UriResolved {
public final String href;
public final URI resolved;
public UriResolved(String href, URI resolved) {
this.href = href;
this.resolved = resolved;
}
}
@Immutable
static public class VariableGroup {
public final String vocabulary;
public final UriResolved vocabUri;
public final UriResolved variableMap;
public final List variables;
public VariableGroup(String vocab, UriResolved vocabUri, UriResolved variableMap, List variables) {
this.vocabulary = vocab;
this.vocabUri = vocabUri;
this.variableMap = variableMap;
Collections.sort(variables);
this.variables = Collections.unmodifiableList(variables);
}
public String getVocabulary() {
return vocabulary;
}
public UriResolved getVocabUri() {
return vocabUri;
}
public UriResolved getVariableMap() {
return variableMap;
}
public List getVariableList() {
if (variables.size() > 0) return variables;
if (variableMap != null) return getVariablesFromMap();
return variables;
}
private List getVariablesFromMap() {
try {
SAXBuilder saxBuilder = new SAXBuilder();
org.jdom2.Document jdomDoc = saxBuilder.build(variableMap.resolved.toURL());
Element varsElem = jdomDoc.getRootElement();
java.util.List vlist = varsElem.getChildren("variable", Catalog.defNS);
List variables = new ArrayList<>();
for (Element e : vlist) {
variables.add(CatalogBuilder.readVariable(e));
}
return variables;
} catch (Exception e) {
logger.error("failed to read VariablesFromMap at {}", variableMap.resolved.toString(), e);
return new ArrayList<>(0);
}
}
}
@Immutable
public static class MetadataOther {
public final String title, type;
public final String xlinkHref;
public final String namespaceURI, prefix;
public final boolean isInherited;
public final Object contentObject;
public MetadataOther(String xlinkHref, String title, String type, String namespaceURI, String prefix, boolean inherited) {
this.xlinkHref = xlinkHref;
this.title = title;
this.type = type;
this.namespaceURI = namespaceURI;
this.prefix = prefix;
this.isInherited = inherited;
this.contentObject = null;
}
public MetadataOther(String mtype, String namespaceURI, String namespacePrefix, boolean inherited, Object contentObject) {
this.xlinkHref = null;
this.title = null;
this.type = mtype;
this.namespaceURI = namespaceURI;
this.prefix = namespacePrefix;
this.isInherited = inherited;
this.contentObject = contentObject;
}
public boolean isInherited() {
return isInherited;
}
public String getTitle() {
return title;
}
public String getType() {
return type;
}
public String getXlinkHref() {
return xlinkHref;
}
public String getNamespaceURI() {
return namespaceURI;
}
public String getPrefix() {
return prefix;
}
public Object getContentObject() {
return contentObject;
}
}
}