/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2007-2008, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.imageio;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import javax.measure.unit.UnitFormat;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.imageio.metadata.Axis;
import org.geotools.imageio.metadata.Band;
import org.geotools.imageio.metadata.BoundedBy;
import org.geotools.imageio.metadata.CoordinateReferenceSystem;
import org.geotools.imageio.metadata.DefinedByConversion;
import org.geotools.imageio.metadata.Identification;
import org.geotools.imageio.metadata.SpatioTemporalMetadata;
import org.geotools.imageio.metadata.SpatioTemporalMetadataFormat;
import org.geotools.imageio.metadata.BoundedBy.TemporalExtent;
import org.geotools.imageio.metadata.DefinedByConversion.ParameterValue;
import org.geotools.metadata.sql.MetadataException;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultCompoundCRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.referencing.crs.DefaultTemporalCRS;
import org.geotools.referencing.cs.DefaultCoordinateSystemAxis;
import org.geotools.referencing.cs.DefaultEllipsoidalCS;
import org.geotools.referencing.datum.DefaultEllipsoid;
import org.geotools.referencing.datum.DefaultGeodeticDatum;
import org.geotools.referencing.datum.DefaultPrimeMeridian;
import org.geotools.referencing.factory.ReferencingFactoryContainer;
import org.geotools.referencing.operation.DefiningConversion;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Errors;
import org.geotools.temporal.object.DefaultPosition;
import org.geotools.util.SimpleInternationalString;
import org.opengis.geometry.BoundingBox;
import org.opengis.parameter.ParameterNotFoundException;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.NoSuchIdentifierException;
import org.opengis.referencing.crs.CRSFactory;
import org.opengis.referencing.crs.CompoundCRS;
import org.opengis.referencing.crs.GeographicCRS;
import org.opengis.referencing.crs.ProjectedCRS;
import org.opengis.referencing.crs.TemporalCRS;
import org.opengis.referencing.crs.VerticalCRS;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CSFactory;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.cs.EllipsoidalCS;
import org.opengis.referencing.cs.TimeCS;
import org.opengis.referencing.cs.VerticalCS;
import org.opengis.referencing.datum.Datum;
import org.opengis.referencing.datum.DatumFactory;
import org.opengis.referencing.datum.Ellipsoid;
import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.datum.PrimeMeridian;
import org.opengis.referencing.datum.TemporalDatum;
import org.opengis.referencing.datum.VerticalDatum;
import org.opengis.referencing.datum.VerticalDatumType;
import org.opengis.referencing.operation.Conversion;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.MathTransformFactory;
import org.opengis.referencing.operation.TransformException;
import org.opengis.temporal.Instant;
import org.opengis.temporal.Period;
import org.opengis.temporal.Position;
import org.opengis.temporal.TemporalGeometricPrimitive;
/**
* Default implementation of {@link SliceDescriptor}.
*
* @author Alessio Fabiani, GeoSolutions
* @author Daniele Romagnoli, GeoSolutions
*
*
* @source $URL: http://svn.osgeo.org/geotools/trunk/modules/unsupported/coverage-experiment/coverage-core/src/main/java/org/geotools/imageio/DefaultSliceDescriptor.java $
*/
public class DefaultSliceDescriptor extends AbstractSliceDescriptor {
public DefaultSliceDescriptor(SpatioTemporalMetadata metadata) {
super(metadata, ReferencingFactoryContainer.instance(null));
}
private final static java.util.logging.Logger LOGGER = Logger
.getLogger("org.geotools.imageio");
public enum CRSType {
GEO3D, GEO2D, PROJECTED, DERIVED, PROJECTED3D, VERTICAL, TEMPORAL
}
/**
* This array allows to know how {@link #coordinateReferenceSystem} is
* composed. In case of a {@link CompoundCRS}, this field quickly allows to
* know what CoordinateReferenceSystem are composing the main
* CoordinateReferenceSystem.
*/
private CRSType[] CRSTypes;
/**
* The object to use for parsing and formatting units.
*/
private UnitFormat unitFormat;
private static org.opengis.referencing.crs.CoordinateReferenceSystem wgs84Crs;
static {
try {
wgs84Crs = CRS.decode("EPSG:4326", true);
} catch (NoSuchAuthorityCodeException e) {
wgs84Crs = DefaultGeographicCRS.WGS84;
} catch (FactoryException e) {
wgs84Crs = DefaultGeographicCRS.WGS84;
}
}
/**
* Set of commonly used symbols for "metres".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] METERS = { "meter", "meters", "metre",
"metres", "m" };
/**
* Set of commonly used symbols for "degrees".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] DEGREES = { "degree", "degrees", "deg", "°" };
/**
* Set of commonly used symbols for "seconds".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] SECONDS = { "second", "sec", "seconds since" };
/**
* Set of commonly used symbols for "seconds".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] MINUTES = { "minute", "min", "minutes since" };
/**
* Set of commonly used symbols for "seconds".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] HOURS = { "hour", "hh", "hours since" };
/**
* Set of commonly used symbols for "seconds".
*
* @todo Needs a more general way to set unit symbols once the Unit API is
* completed.
*/
private static final String[] DAYS = { "day", "dd", "days since" };
/**
* Set of {@linkplain DefaultEllipsoid ellipsoids} already defined.
*/
private static final DefaultEllipsoid[] ELLIPSOIDS = new DefaultEllipsoid[] {
DefaultEllipsoid.CLARKE_1866, DefaultEllipsoid.GRS80,
DefaultEllipsoid.INTERNATIONAL_1924, DefaultEllipsoid.SPHERE,
DefaultEllipsoid.WGS84 };
/**
*
* @param metadata
*/
protected void setCoordinateReferenceSystem(SpatioTemporalMetadata metadata) {
final ReferencingFactoryContainer factories = getFactoryContainer();
final CSFactory csFactory = factories.getCSFactory();
final DatumFactory datumFactory = factories.getDatumFactory();
final CRSFactory crsFactory = factories.getCRSFactory();
// ////
// Check if a TemporalCRS is defined
// ////
TemporalCRS temporalCRS = null;
// get the metadata for the temporal CRS
org.geotools.imageio.metadata.TemporalCRS metaTemporalCRS = metadata.getTemporalCRS();
try {
if (metaTemporalCRS != null) {
// Creating the Time Coordinate System
String csName = metaTemporalCRS.getCoordinateSystem().getName();
if (csName == null) {
csName = "Unknown";
}
final Map<String, String> csMap = Collections.singletonMap("name", csName);
final Axis timeAxis = metaTemporalCRS.getAxis(0);
final TimeCS timeCS = csFactory.createTimeCS(csMap, getAxis(timeAxis.getIdentification().getName(), getDirection(timeAxis.getDirection()), timeAxis.getUnits()));
// Creating the Temporal Datum
String datumName = metaTemporalCRS.getDatum().getName();
if (datumName == null) {
datumName = "Unknown";
}
final Map<String, String> datumMap = Collections.singletonMap("name", datumName);
final Position timeOrigin = new DefaultPosition(new SimpleInternationalString(metaTemporalCRS.getOrigin()));
final TemporalDatum temporalDatum = datumFactory.createTemporalDatum(datumMap, timeOrigin.getDate());
// Finally creating the Temporal CoordinateReferenceSystem
String crsName = metaTemporalCRS.getIdentification() != null ? metaTemporalCRS.getIdentification().getName(): null;
if (crsName == null) {
crsName = "Unknown";
}
final Map<String, String> crsMap = Collections.singletonMap("name", crsName);
temporalCRS = crsFactory.createTemporalCRS(crsMap,temporalDatum, timeCS);
}
} catch (FactoryException e) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Unable to parse temporal CRS", e);
temporalCRS = null;
} catch (ParseException e) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Unable to parse temporal CRS", e);
temporalCRS = null;
}
// ////
// Check if a VerticalCRS is defined
// ////
VerticalCRS verticalCRS = null;
org.geotools.imageio.metadata.VerticalCRS metaVerticalCRS = metadata.getVerticalCRS();
try {
if (metaVerticalCRS != null) {
// Creating the Vertical Coordinate System
String csName = metaVerticalCRS.getCoordinateSystem().getName();
if (csName == null) {
csName = "Unknown";
}
final Map<String, String> csMap = Collections.singletonMap(
"name", csName);
final Axis verticalAxis = metaVerticalCRS.getAxis(0);
VerticalCS verticalCS = csFactory.createVerticalCS(csMap,getAxis(verticalAxis.getIdentification().getName(),getDirection(verticalAxis.getDirection()),verticalAxis.getUnits()));
// Creating the Vertical Datum
String datumName = metaVerticalCRS.getDatum().getName();
if (datumName == null) {
datumName = "Unknown";
}
final Map<String, String> datumMap = Collections.singletonMap("name", datumName);
final VerticalDatum verticalDatum = datumFactory.createVerticalDatum( datumMap, VerticalDatumType.valueOf(metaVerticalCRS .getVerticalDatumType()));
// Finally creating the Vertical CoordinateReferenceSystem
String crsName = metaVerticalCRS.getIdentification() != null ? metaVerticalCRS.getIdentification().getName(): null;
if (crsName == null) {
crsName = "Unknown";
}
final Map<String, String> crsMap = Collections.singletonMap("name", crsName);
verticalCRS = crsFactory.createVerticalCRS(crsMap,verticalDatum, verticalCS);
}
} catch (FactoryException e) {
if (LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE, "Unable to parse vertical CRS", e);
verticalCRS = null;
}
// ////
// Creating the CoordinateReferenceSystem
// ////
org.opengis.referencing.crs.CoordinateReferenceSystem crs = null;
final CoordinateReferenceSystem metaCRS = metadata.getCRS();
String name = metaCRS.getCRS().getName();
if (name == null) {
name = "Unknown";
}
if (name.contains("WGS84") || name.contains("WGS 84")) {
crs = (name.contains("3D")) ? DefaultGeographicCRS.WGS84_3D: wgs84Crs;
}
if (crs == null) {
String type = metaCRS.getCrsType();
if (type == null) {
type = (metaCRS.getBaseCRS() == null) ? SpatioTemporalMetadataFormat.GEOGRAPHIC: SpatioTemporalMetadataFormat.PROJECTED;
}
final Map<String, String> map = Collections.singletonMap("name",
name);
try {
if (type.equalsIgnoreCase(SpatioTemporalMetadataFormat.GEOGRAPHIC) || type.equalsIgnoreCase(SpatioTemporalMetadataFormat.GEOGRAPHIC_3D)) {
crs = crsFactory.createGeographicCRS(map,(GeodeticDatum) getDatum(metaCRS),(EllipsoidalCS) getCoordinateSystem(metaCRS));
} else {
final Map<String, String> baseMap = Collections.singletonMap("name", metaCRS.getBaseCRS().getName());
final GeographicCRS baseCRS = crsFactory.createGeographicCRS(baseMap,(GeodeticDatum) getDatum(metaCRS), DefaultEllipsoidalCS.GEODETIC_2D);
crs = crsFactory.createProjectedCRS(map, baseCRS,getProjection(metaCRS),(CartesianCS) getCoordinateSystem(metaCRS));
}
}catch (Throwable e) {
crs = null;
}
}
// ////
// Finally setting up the Coordinate Reference System
// ////
if (temporalCRS != null && verticalCRS != null && crs != null) {
// build a compound crs
final CompoundCRS compoundCRS=
new DefaultCompoundCRS("CompoundCRS",
new org.opengis.referencing.crs.CoordinateReferenceSystem[] {temporalCRS, verticalCRS, crs});
setCoordinateReferenceSystem(compoundCRS);
//
// set the crs types
//
CRSTypes = new CRSType[3];
CRSTypes[0] = CRSType.TEMPORAL;
CRSTypes[1] = CRSType.VERTICAL;
// horizontal
if(metaCRS.getBaseCRS() != null ) // Do we have a prohect crs
if(metaCRS.getDimension() < 3)
CRSTypes[2] = CRSType.PROJECTED;
else
CRSTypes[2] = CRSType.PROJECTED3D;
else
if(metaCRS.getDimension() < 3)
CRSTypes[2] = CRSType.GEO2D;
else
CRSTypes[2] = CRSType.GEO3D;
} else if (temporalCRS != null && verticalCRS == null && crs != null) {
setCoordinateReferenceSystem(new DefaultCompoundCRS(
// metaCRS.getIdentification().getName(),
"CompoundCRS",
new org.opengis.referencing.crs.CoordinateReferenceSystem[] {temporalCRS,crs}));
CRSTypes = new CRSType[2];
CRSTypes[0] = CRSType.TEMPORAL;
// horizontal
if(metaCRS.getBaseCRS() != null ) // Do we have a prohect crs
if(metaCRS.getDimension() < 3)
CRSTypes[1] = CRSType.PROJECTED;
else
CRSTypes[1] = CRSType.PROJECTED3D;
else
if(metaCRS.getDimension() < 3)
CRSTypes[1] = CRSType.GEO2D;
else
CRSTypes[1] = CRSType.GEO3D;
} else if (temporalCRS == null && verticalCRS != null && crs != null) {
setCoordinateReferenceSystem(new DefaultCompoundCRS(
// metaCRS.getIdentification().getName(),
"CompoundCRS",
new org.opengis.referencing.crs.CoordinateReferenceSystem[] {verticalCRS ,crs}));
CRSTypes = new CRSType[2];
CRSTypes[0] = CRSType.VERTICAL;
// horizontal
if(metaCRS.getBaseCRS() != null ) // Do we have a prohect crs
if(metaCRS.getDimension() < 3)
CRSTypes[1] = CRSType.PROJECTED;
else
CRSTypes[1] = CRSType.PROJECTED3D;
else
if(metaCRS.getDimension() < 3)
CRSTypes[1] = CRSType.GEO2D;
else
CRSTypes[1] = CRSType.GEO3D;
} else if (temporalCRS != null && verticalCRS != null && crs == null) {
setCoordinateReferenceSystem(new DefaultCompoundCRS(
// metaCRS.getIdentification().getName(),
"CompoundCRS",
new org.opengis.referencing.crs.CoordinateReferenceSystem[] {temporalCRS,verticalCRS }));
CRSTypes = new CRSType[2];
CRSTypes[1] = CRSType.TEMPORAL;
CRSTypes[0] = CRSType.VERTICAL;
} else if (temporalCRS != null && verticalCRS == null && crs == null) {
setCoordinateReferenceSystem(temporalCRS);
CRSTypes = new CRSType[1];
CRSTypes[0] = CRSType.TEMPORAL;
} else if (temporalCRS == null && verticalCRS != null && crs == null) {
setCoordinateReferenceSystem(verticalCRS);
CRSTypes = new CRSType[1];
CRSTypes[0] = CRSType.VERTICAL;
} else if (temporalCRS == null && verticalCRS == null && crs != null) {
setCoordinateReferenceSystem(crs);
CRSTypes = new CRSType[1];
// horizontal
if(metaCRS.getBaseCRS() != null ) // Do we have a prohect crs
if(metaCRS.getDimension() < 3)
CRSTypes[0] = CRSType.PROJECTED;
else
CRSTypes[0] = CRSType.PROJECTED3D;
else
if(metaCRS.getDimension() < 3)
CRSTypes[0] = CRSType.GEO2D;
else
CRSTypes[0] = CRSType.GEO3D;
}
}
/**
* Get the {@link AxisDirection} object related to the specified direction
*
* @param direction
* @return
*/
private AxisDirection getDirection(final String direction) {
return AxisDirection.valueOf(direction);
}
/**
* Returns the {@linkplain CoordinateSystem coordinate system}. The default
* implementation builds a coordinate system using the
* {@linkplain #getAxis axes} defined in the metadata.
*
* @throws MetadataException
* if there is less than 2 axes defined in the metadata, or
* if the creation of the coordinate system failed.
*
* @see #getAxis
* @see CoordinateSystem
*/
private CoordinateSystem getCoordinateSystem(
final CoordinateReferenceSystem metaCRS) throws Exception {
String name = metaCRS.getCoordinateSystem().getName();
if (name == null) {
name = "Unknown";
}
final String type = (metaCRS.getBaseCRS() == null) ? SpatioTemporalMetadataFormat.ELLIPSOIDAL
: SpatioTemporalMetadataFormat.CARTESIAN;
final CSFactory factory = getFactoryContainer().getCSFactory();
final Map<String, String> map = Collections.singletonMap("name", name);
final int dimension = metaCRS.getDimension();
if (dimension < 2) {
throw new MetadataException("Number of dimension error : "
+ dimension);
}
try {
if (dimension < 3) {
CoordinateSystemAxis axis1 = getAxis(metaCRS.getAxis(0)
.getIdentification().getName(), getDirection(metaCRS
.getAxis(0).getDirection()), metaCRS.getAxis(0).getUnits());
CoordinateSystemAxis axis2 = getAxis(metaCRS.getAxis(1)
.getIdentification().getName(), getDirection(metaCRS
.getAxis(1).getDirection()), metaCRS.getAxis(1).getUnits());
if (type.equalsIgnoreCase(SpatioTemporalMetadataFormat.CARTESIAN)) {
return factory.createCartesianCS(map, axis1, axis2);
}
if (type.equalsIgnoreCase(SpatioTemporalMetadataFormat.ELLIPSOIDAL)) {
return factory.createEllipsoidalCS(map, axis1, axis2);
}
} else {
CoordinateSystemAxis axis1 = getAxis(metaCRS.getAxis(0)
.getIdentification().getName(), getDirection(metaCRS
.getAxis(0).getDirection()), metaCRS.getAxis(0).getUnits());
CoordinateSystemAxis axis2 = getAxis(metaCRS.getAxis(1)
.getIdentification().getName(), getDirection(metaCRS
.getAxis(1).getDirection()), metaCRS.getAxis(1).getUnits());
CoordinateSystemAxis axis3 = getAxis(metaCRS.getAxis(2)
.getIdentification().getName(), getDirection(metaCRS
.getAxis(2).getDirection()), metaCRS.getAxis(2).getUnits());
if (type.equalsIgnoreCase(SpatioTemporalMetadataFormat.CARTESIAN)) {
return factory.createCartesianCS(map, axis1, axis2, axis3);
}
if (type.equalsIgnoreCase(SpatioTemporalMetadataFormat.ELLIPSOIDAL)) {
return factory.createEllipsoidalCS(map, axis1, axis2, axis3);
}
}
/*
* Should not happened, since the type value should be contained in
* the {@link SpatioTemporalMetadataFormat#CS_TYPES} list.
*/
throw new Exception("Coordinate system type not known : " + type);
} catch (FactoryException e) {
throw new Exception(e.getLocalizedMessage());
}
}
/**
* Returns the datum. The default implementation performs the following
* steps:
* <p>
* <ul>
* <li>Verifies if the datum name contains {@code WGS84}, and returns a
* {@link DefaultGeodeticDatum#WGS84} geodetic datum if it is the case.
* </li>
* <li>Builds a {@linkplain PrimeMeridian prime meridian} using information
* stored into the metadata tree. </li>
* <li>Returns a {@linkplain DefaultGeodeticDatum geodetic datum} built on
* the prime meridian. </li>
* </ul>
* </p>
*
* @throws MetadataException
* if the datum is not defined, or if the
* {@link #getEllipsoid} method fails.
*
* @todo: The current implementation only returns a
* {@linkplain GeodeticDatum geodetic datum}, other kind of datum
* have to be generated too.
*
* @see #getEllipsoid
*/
private Datum getDatum(final CoordinateReferenceSystem metaCRS) throws Exception {
Identification id = metaCRS.getBaseCRS();
org.geotools.imageio.metadata.Identification identDatum = id != null ? id : metaCRS.getIdentification();
if (identDatum == null) {
throw new Exception("The datum is not defined.");
}
final String name = identDatum.getName();
if (name == null) {
throw new Exception("Datum name not defined.");
}
if (name.toUpperCase().contains("WGS84")) {
return DefaultGeodeticDatum.WGS84;
}
final String primeMeridianName = metaCRS.getPrimeMeridian().getName();
final PrimeMeridian primeMeridian;
/*
* By default, if the prime meridian name is not defined, or if it is
* defined with {@code Greenwich}, one chooses the {@code Greenwich}
* meridian as prime meridian. Otherwise one builds it, using the
* {@code greenwichLongitude} parameter.
*/
if ((primeMeridianName == null)
|| (primeMeridianName != null && primeMeridianName.toLowerCase().contains("greenwich"))) {
primeMeridian = DefaultPrimeMeridian.GREENWICH;
} else {
final double greenwichLon = Double.parseDouble(metaCRS.getGreenwichLongitude());
primeMeridian = (Double.isNaN(greenwichLon)) ? DefaultPrimeMeridian.GREENWICH
: new DefaultPrimeMeridian(primeMeridianName, greenwichLon);
}
return new DefaultGeodeticDatum(name, getEllipsoid(metaCRS), primeMeridian);
}
/**
* Returns the ellipsoid. Depending on whether
* {@link ImageReferencing#semiMinorAxis} or
* {@link ImageReferencing#inverseFlattening} has been defined, the default
* implementation will construct an ellispoid using
* {@link DatumFactory#createEllipsoid} or
* {@link DatumFactory#createFlattenedSphere} respectively.
*
* @throws MetadataException
* if the operation failed to create the
* {@linkplain Ellipsoid ellipsoid}.
*
* @see #getUnit(String)
*/
private Ellipsoid getEllipsoid(final CoordinateReferenceSystem metaCRS) throws Exception {
final String name = metaCRS.getEllipsoid().getName();
if (name != null) {
for (final DefaultEllipsoid ellipsoid : ELLIPSOIDS) {
if (ellipsoid.nameMatches(name)) {
return ellipsoid;
}
}
} else {
throw new Exception("Ellipsoid name not defined.");
}
// It has a name defined, but it is not present in the list of known
// ellipsoids.
final double semiMajorAxis = Double.parseDouble(metaCRS.getSemiMajorAxis());
if (Double.isNaN(semiMajorAxis)) {
throw new Exception("Ellipsoid semi major axis not defined.");
}
final String ellipsoidUnit = metaCRS.getEllipsoidUnit();
if (ellipsoidUnit == null) {
throw new Exception("Ellipsoid unit not defined.");
}
final Unit unit = getUnit(ellipsoidUnit);
final Map<String, String> map = Collections.singletonMap("name", name);
try {
final DatumFactory datumFactory = getFactoryContainer().getDatumFactory();
return (metaCRS.getSecondDefinigParameterType()
.equals(SpatioTemporalMetadataFormat.MD_DTM_GD_EL_SEMIMINORAXIS)) ? datumFactory
.createEllipsoid(map, semiMajorAxis, Double .parseDouble(metaCRS.getSecondDefinigParameterValue()), unit)
: datumFactory.createFlattenedSphere(map, semiMajorAxis,Double.parseDouble(metaCRS.getSecondDefinigParameterValue()), unit);
} catch (FactoryException e) {
throw new Exception(e.getLocalizedMessage());
}
}
/**
* Returns the unit which matches with the name given.
*
* @param unitName
* The name of the unit. Should not be {@code null}.
* @return The unit matching with the specified name.
* @throws MetadataException
* if the unit name does not match with the
* {@linkplain #unitFormat unit format}.
*/
private Unit<?> getUnit(final String unitName) throws FactoryException {
if (contains(unitName, METERS)) {
return SI.METER;
} else if (contains(unitName, DEGREES)) {
return NonSI.DEGREE_ANGLE;
} else if (contains(unitName, SECONDS)) {
return SI.SECOND;
} else if (contains(unitName, MINUTES)) {
return NonSI.MINUTE;
} else if (contains(unitName, HOURS)) {
return NonSI.HOUR;
} else if (contains(unitName, DAYS)) {
return NonSI.DAY;
} else {
if (unitFormat == null) {
unitFormat = UnitFormat.getInstance();
}
try {
return (Unit<?>) unitFormat.parseObject(unitName);
} catch (ParseException e) {
throw new FactoryException("Unit not known : " + unitName, e);
}
}
}
/**
* Build a proper {@link CoordinateSystemAxis} given the set composed of
* axisName, axisDirection and axis unit of measure.
*
* @param axisName
* the name of the axis to be built.
* @param direction
* the {@linkplain AxisDirection direction} of the axis.
* @param unitName
* the unit of measure string.
* @return a proper {@link CoordinateSystemAxis} instance or {@code null} if
* unable to build it.
* @throws FactoryException
*/
private CoordinateSystemAxis getAxis(final String axisName,
final AxisDirection direction, final String unitName)
throws FactoryException {
if (axisName == null) {
return null;
}
final DefaultCoordinateSystemAxis axisFound = DefaultCoordinateSystemAxis.getPredefined(axisName, direction);
if (axisFound != null) {
return axisFound;
}
/*
* The current axis defined in the metadata tree is not already known in
* the Geotools implementation, so one will build it using those
* information.
*/
final Unit<?> unit = getUnit(unitName);
final Map<String, String> map = Collections.singletonMap("name", axisName);
try {
return getFactoryContainer().getCSFactory().createCoordinateSystemAxis(map, axisName, direction, unit);
} catch (FactoryException e) {
throw new FactoryException(e.getLocalizedMessage());
}
}
/**
* Check if {@code toSearch} appears in the {@code list} array. Search is
* case-insensitive. This is a temporary patch (will be removed when the
* final API for JSR-108: Units specification will be available).
*/
private static boolean contains(final String toSearch, final String[] list) {
for (int i = list.length; --i >= 0;) {
if (toSearch.toLowerCase().contains(list[i].toLowerCase())) {
return true;
}
}
return false;
}
/**
* Returns the projection. The default implementation performs the following
* steps:
* <p>
* <ul>
* <li>Gets a {@linkplain ParameterValueGroup parameter value group} from a
* {@link MathTransformFactory}</li>
*
* <li>Gets the metadata values for each parameters in the above step. If a
* parameter is not defined in this {@code MetadataReader}, then it will be
* left to its (projection dependent) default value. Parameters are
* projection dependent, but will typically include
*
* {@code "semi_major"}, {@code "semi_minor"} (or
* {@code "inverse_flattening"}), {@code "central_meridian"},
* {@code "latitude_of_origin"}, {@code "false_easting"} and
* {@code "false_northing"}.
*
* <li>Computes and returns a {@linkplain DefiningConversion conversion}
* using the name of the projection and the parameter value group previously
* filled.</li>
* </ul>
* </p>
*
* @return The projection.
* @throws MetadataException
* if the operation failed for some other reason (for
* example if a parameter value can't be parsed as a
* {@code double}).
*
*/
private Conversion getProjection(final CoordinateReferenceSystem metaCRS) throws Exception {
final MathTransformFactory mathTransformFactory = getFactoryContainer().getMathTransformFactory();
DefinedByConversion conversionDefinition = metaCRS.getDefinedByConversion();
String projectionName = conversionDefinition.getIdentification().getName();
if (projectionName == null) {
throw new MetadataException("Projection name is not defined.");
}
final ParameterValueGroup paramValueGroup;
try {
paramValueGroup = mathTransformFactory.getDefaultParameters(projectionName);
} catch (NoSuchIdentifierException e) {
throw new MetadataException(e.getLocalizedMessage());
}
for (int i = 0; i < conversionDefinition.numParams(); i++) {
final ParameterValue parameter = conversionDefinition.getParameterValue(i);
final String name = parameter.getIdentification().getName();
if (name == null) {
continue;
}
final double value = parameter.getValue();
if (Double.isNaN(value)) {
continue;
}
try {
paramValueGroup.parameter(name).setValue(value);
} catch (ParameterNotFoundException e) {
// Should not happened. Continue with the next parameter, the
// current one will be ignored.
continue;
}
}
return new DefiningConversion(projectionName, paramValueGroup);
}
/**
* Set the {@link GeneralEnvelope} object referring to this object given a
* {@link BoundedBy} metadata element.
*
* @param boundedBy
* the metadata node to be used to set envelope.
*/
private void buildBoundingBox(BoundedBy boundedBy) {
if (boundedBy == null)
throw new NullPointerException("Specified boundedBy metadata node is null");
final List<Double> minDP = new ArrayList<Double>();
final List<Double> maxDP = new ArrayList<Double>();
final double[] lowers = boundedBy.getLowerCorner();
final double[] uppers = boundedBy.getUpperCorner();
// temporal extent
final TemporalGeometricPrimitive temporalExtent = getTemporalExtent();
final VerticalExtent verticalExtent = getVerticalExtent();
org.opengis.referencing.crs.CoordinateReferenceSystem coordinateReferenceSystem = getCoordinateReferenceSystem();
// build the various elements
int i=0;
for (CRSType crsType : CRSTypes) {
switch (crsType) {
case TEMPORAL:
if (temporalExtent != null) {
final TemporalCRS temporalCRS ;
if(CRSTypes.length == 1)
temporalCRS=(TemporalCRS) coordinateReferenceSystem;
else {
// get the compound crs
final CompoundCRS compoundCRS=(CompoundCRS) coordinateReferenceSystem;
temporalCRS=(TemporalCRS) compoundCRS.getCoordinateReferenceSystems().get(i);
}
if (temporalExtent instanceof Instant) {
double time = DefaultTemporalCRS.wrap(temporalCRS).toValue(((Instant) temporalExtent).getPosition().getDate());
minDP.add(time);
maxDP.add(time);
} else if (temporalExtent instanceof Period) {
double beginTime = DefaultTemporalCRS.wrap(temporalCRS).toValue(((Period) temporalExtent).getBeginning().getPosition().getDate());
double endTime = DefaultTemporalCRS.wrap(temporalCRS).toValue(((Period) temporalExtent).getEnding().getPosition().getDate());
minDP.add(beginTime);
maxDP.add(endTime);
}
}
break;
case VERTICAL:
if (verticalExtent != null) {
final VerticalExtent.VerticalLevelType verticalType = verticalExtent.getType();
if (verticalType == VerticalExtent.VerticalLevelType.SINGLE_NUMBER|| verticalType == VerticalExtent.VerticalLevelType.NUMBER_RANGE) {
minDP.add(verticalExtent.getMinimumValue());
maxDP.add(verticalExtent.getMaximumValue());
} // if UNKNOWN ???
}
break;
case GEO2D:case PROJECTED: case PROJECTED3D:
final org.opengis.referencing.crs.CoordinateReferenceSystem spatialCRS ;
if(CRSTypes.length == 1)
spatialCRS=coordinateReferenceSystem;
else {
// get the compound crs
final CompoundCRS compoundCRS=(CompoundCRS) coordinateReferenceSystem;
spatialCRS=compoundCRS.getCoordinateReferenceSystems().get(i);
}
if (spatialCRS.getCoordinateSystem().getDimension() == lowers.length&& lowers.length == uppers.length) {
for (int j = 0; j < lowers.length; j++) {
minDP.add(lowers[j]);
maxDP.add(uppers[j]);
}
} // not matching dimensions ???
break;
default:
throw new UnsupportedOperationException(Errors.format(ErrorKeys.UNSUPPORTED_COORDINATE_SYSTEM_$1,crsType.toString()));
}
i++;
}
final double[] ordinates = new double[minDP.size() + maxDP.size()];
int index = 0;
for (Double value : minDP) {
ordinates[index++] = value;
}
for (Double value : maxDP) {
ordinates[index++] = value;
}
//TODO Improve checks: A slice could be without an Horizontal Extent
final GeneralEnvelope generalEnvelope = new GeneralEnvelope(coordinateReferenceSystem);
generalEnvelope.setEnvelope(ordinates);
setGeneralEnvelope(generalEnvelope);
// try to get the spatial BBOX
try {
GeneralEnvelope spatialBBOX =null;
// we have a compound CRS
if (coordinateReferenceSystem instanceof CompoundCRS){
final org.opengis.referencing.crs.CoordinateReferenceSystem spatialCRS= CRS.getHorizontalCRS(coordinateReferenceSystem);
if(spatialCRS!=null){
// get the transform from the compound to the spatial crs
final MathTransform tr= CRS.findMathTransform(coordinateReferenceSystem, spatialCRS,true);
// extract the spatial envelope
spatialBBOX=CRS.transform(tr, generalEnvelope);
spatialBBOX.setCoordinateReferenceSystem(spatialCRS);
}
}
// we do not have a compound CRS
else if (coordinateReferenceSystem instanceof GeographicCRS || coordinateReferenceSystem instanceof ProjectedCRS)
{
spatialBBOX= new GeneralEnvelope(generalEnvelope);
spatialBBOX.setCoordinateReferenceSystem(coordinateReferenceSystem);
}
// at this point we might have not found a spatial CRS then the bbox should be empty
if(spatialBBOX!=null)
{
setHorizontalExtent((BoundingBox) new ReferencedEnvelope(spatialBBOX));
return;
}
} catch (FactoryException e) {
if(LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE,e.getLocalizedMessage(),e);
} catch (TransformException e) {
if(LOGGER.isLoggable(Level.FINE))
LOGGER.log(Level.FINE,e.getLocalizedMessage(),e);
}
// if we got here, either we got an error or we have no spatial crs
setHorizontalExtent(null);
}
/**
* Set the verticalExtent referring to this object given a {@link BoundedBy}
* metadata element.
*
* @param boundedBy
* the metadata node to be used to set envelope.
*/
private void buildVerticalExtent(BoundedBy boundedBy) {
if (boundedBy == null)
throw new NullPointerException("Specified boundedBy metadata node is null");
final org.geotools.imageio.metadata.BoundedBy.VerticalExtent metaVerticalExtent = boundedBy.getVerticalExtent();
if (metaVerticalExtent != null) {
Object value = metaVerticalExtent.getValue();
if (value != null) {
final VerticalExtent verticalExtent = new VerticalExtent();
verticalExtent.setValue(value);
setVerticalExtent(verticalExtent);
}
}
}
/**
* Set the temporal extent referring to this object given a
* {@link BoundedBy} metadata element.
*
* @param boundedBy
* the metadata node to be used to set envelope.
*/
private void buildTemporalExtent(BoundedBy boundedBy) {
if (boundedBy == null)
throw new NullPointerException("Specified boundedBy metadata node is null");
final TemporalExtent metaTemporalExtent = boundedBy.getTemporalExtent();
if (metaTemporalExtent != null) {
final TemporalGeometricPrimitive temporalExtent = (TemporalGeometricPrimitive) metaTemporalExtent.getValue();
setTemporalExtent(temporalExtent);
}
}
protected void setBoundedBy(final SpatioTemporalMetadata metadata) {
BoundedBy bb = metadata.getBoundedBy();
buildTemporalExtent(bb);
buildVerticalExtent(bb);
buildBoundingBox(bb);
}
@Override
protected void setName(final SpatioTemporalMetadata metadata) {
final Band band = metadata.getBand(0);
setElementName(band.getName());
}
}