Return-Path: Limitations:
Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java?rev=1771707&r1=1771706&r2=1771707&view=diff
==============================================================================
--- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java [UTF-8] (original)
+++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java [UTF-8] Mon Nov 28 10:52:22 2016
@@ -16,14 +16,20 @@
*/
package org.apache.sis.storage.geotiff;
-import java.io.IOException;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+import java.util.StringJoiner;
import java.util.logging.Level;
import java.util.logging.LogRecord;
+import java.util.NoSuchElementException;
+import java.lang.reflect.Array;
import javax.measure.Unit;
+import javax.measure.Quantity;
+import javax.measure.quantity.Angle;
+import javax.measure.quantity.Length;
+import org.opengis.metadata.Identifier;
import org.opengis.metadata.spatial.CellGeometry;
import org.opengis.metadata.spatial.PixelOrientation;
import org.opengis.parameter.ParameterValueGroup;
@@ -48,9 +54,11 @@ import org.opengis.util.FactoryException
import org.opengis.util.NoSuchIdentifierException;
import org.apache.sis.internal.geotiff.Resources;
+import org.apache.sis.internal.referencing.NilReferencingObject;
import org.apache.sis.internal.storage.MetadataBuilder;
import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.util.Constants;
+import org.apache.sis.internal.util.Utilities;
import org.apache.sis.math.Vector;
import org.apache.sis.measure.Units;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
@@ -61,6 +69,9 @@ import org.apache.sis.referencing.cs.Coo
import org.apache.sis.referencing.factory.GeodeticAuthorityFactory;
import org.apache.sis.referencing.factory.GeodeticObjectFactory;
import org.apache.sis.storage.DataStoreContentException;
+import org.apache.sis.util.Characters;
+
+import static org.apache.sis.util.Utilities.equalsIgnoreMetadata;
/**
@@ -169,6 +180,13 @@ final class CRSBuilder {
private CoordinateOperationFactory operationFactory;
/**
+ * Name of the last object created. This is used by {@link #properties(String)} for reusing existing instance
+ * if possible. This is useful in GeoTIFF file since they do not use different names for geographic CRS,
+ * the datum and the ellipsoid.
+ */
+ private Identifier lastName;
+
+ /**
* Creates a new builder of coordinate reference systems.
*
* @param reader where to report warnings if any.
@@ -233,27 +251,55 @@ final class CRSBuilder {
* Returns a map with the given name associated to the {@value org.opengis.referencing.IdentifiedObject#NAME_KEY} key.
* This is an helper method for creating geodetic objects with {@link #objectFactory}.
*/
- private static Map
ProjLinearUnits
and the
- * ProjLinearUnitSize
. The unit may either be
- * specified as a standard EPSG recognized unit, or may be user defined.
- *
- * @param key
- * @param userDefinedKey
- * @param base
- * @param def
- * @return Unit
object representative of the tags in the file.
- * @throws IOException
- * if theProjLinearUnits
is not specified
- * or if unit is user defined and
- * ProjLinearUnitSize
is either not defined
- * or does not contain a number.
+ * Creates units of measurement for projected or geographic coordinate reference systems.
+ * The units may either be specified as a standard EPSG recognized unit, or may be user defined.
+ * If the first case (EPSG code), the {@code keyUser} is ignored. In the second case (user-defined),
+ * the unit of measurement is defined by a conversion factor from metre or radian base unit.
+ *
+ * @param keyEPSG the {@link GeoKeys} for a unit of measurement defined by an EPSG code.
+ * @param keyUser the {@link GeoKeys} for a unit of measurement defined by a scale applied on a base unit.
+ * @param quantity {@link Length} for a linear unit, or {@link Angle} for an angular unit.
+ * @param defaultValue the unit of measurement to return if no value is found in the GeoTIFF file.
+ * @return the unit of measurement associated to the given {@link GeoKeys}, or the default value.
+ *
+ * @throws NoSuchElementException if {@code keyEPSG} value is {@link GeoCodes#userDefined} and no value is associated to {@code keyUser}.
+ * @throws NumberFormatException if a numeric value was stored as a string and can not be parsed.
+ * @throws ClassCastException if the unit of measurement identified by the EPSG code is not of the expected quantity.
*/
- private Unit> createUnit(final short key, final short userDefinedKey, final Unit> base, final Unit> def)
- throws FactoryException, DataStoreContentException
+ private > UnitcreateUnit(final short keyEPSG, final short keyUser, + final Classquantity, final UnitdefaultValue) throws FactoryException { - String unitCode = getAsString(key, false); - - // If not defined, return the default unit of measure. - if (unitCode == null) { - return def; - } - /* - * If specified, retrieve the appropriate unit code. There is two cases to take into account: - * First case is when the unit of measure has an EPSG code, second case is when it can be - * instantiated as a conversion from meter. - */ - if (unitCode.equals(GeoKeys.GTUserDefined_String)) { - return base.multiply(getAsDouble(userDefinedKey, true)); + final int epsg = getAsInteger(keyEPSG); + switch (epsg) { + case GeoCodes.undefined: { + return defaultValue; + } + case GeoCodes.userDefined: { + return defaultValue.getSystemUnit().multiply(getMandatoryDouble(keyUser)); + } + default: { + return epsgFactory().createUnit(String.valueOf(epsg)).asType(quantity); + } } - - //-- using epsg code for this unit - return epsgFactory().createUnit(unitCode); } /** - * Creating a Geodetic Datum for the {@link #createUserDefinedGCRS(javax.measure.Unit, javax.measure.Unit) } method - * we are creating at an higher level.
- * As usual this method tries to follow the geotiff specification
- * Needed tags are : + * Creates a geodetic datum from an EPSG code or from user-defined parameters. + * The GeoTIFF values used by this method are: + * *- *
* - * @param unit to use for building this {@link GeodeticDatum}. - * @return a {@link GeodeticDatum}. - * @throws DataStoreContentException if datum code from relative tiff tag is missing ({@code null}). - * @throws FactoryException if factory problem during Datum creation. - * @see #createPrimeMeridian(javax.measure.Unit) - * @see #createEllipsoid(javax.measure.Unit) + * @param name the name to use if the geodetic datum is user-defined, or {@code null} if unnamed. + * @param angularUnit the angular unit of the longitude value relative to Greenwich. + * @param linearUnit the linear unit of the ellipsoid semi-axis lengths. + * + * @throws NoSuchElementException if a mandatory value is missing. + * @throws NumberFormatException if a numeric value was stored as a string and can not be parsed. + * @throws ClassCastException if an object defined by an EPSG code is not of the expected type. + * @throws FactoryException if an error occurred during objects creation with the factories. + * + * @see #createPrimeMeridian(Unit) + * @see #createEllipsoid(Unit) */ - private GeodeticDatum createGeodeticDatum(final Unit unit) - throws DataStoreContentException, FactoryException { - - // lookup the datum (w/o PrimeMeridian). - String datumCode = getAsString(GeoKeys.GeogGeodeticDatum, true); - - //-- Geodetic Datum define as an EPSG code. - if (!datumCode.equals(GeoKeys.GTUserDefined_String)) - return epsgFactory().createGeodeticDatum(String.valueOf(datumCode)); - - //-- USER DEFINE Geodetic Datum creation - { - //-- Datum name - assert datumCode.equals(GeoKeys.GTUserDefined_String); - String datumName = getAsString(GeoKeys.GeogCitation, false); - if (datumName == null) { - datumName = "Unamed User Defined Geodetic Datum"; + private GeodeticDatum createGeodeticDatum(String name, final Unit- a code definition given by {@link GeoKeys.GeogGeodeticDatumGeoKey} tag
- *- a name given by {@link GeoKeys.GeogCitationGeoKey}
- *- required prime meridian tiff tags
- *- required ellipsoid tiff tags
+ *- a code given by {@link GeoKeys#GeogGeodeticDatum}
+ *- a name given by {@link GeoKeys#GeogCitation}
+ *- all values required by {@link #createPrimeMeridian(Unit)}
+ *- all values required by {@link #createEllipsoid(Unit)}
*angularUnit, final Unit linearUnit) + throws FactoryException + { + final int epsg = getAsInteger(GeoKeys.GeogGeodeticDatum); + switch (epsg) { + case GeoCodes.undefined: { + throw new NoSuchElementException(missingValue(GeoKeys.GeogGeodeticDatum)); + } + default: { + // Geodetic Datum defined by an EPSG code. + return epsgFactory().createGeodeticDatum(String.valueOf(epsg)); + } + case GeoCodes.userDefined: { + /* + * Create the ellipsoid and the prime meridian, then assemble those components into a datum. + * The datum name however may not be appropriate, since GeoTIFF provides only a global name + * for the whole CRS. The name does not matter so much, except for datums where the name is + * taken in account when determining if two datums are equal. In order to get better names, + * after datum construction we will compare the datum with a few well-known cases defined in + * CommonCRS. We use the CRS name given in the GeoTIFF file for that purpose, exploiting the + * fact that it is often a name that can be mapped to a CommonCRS name like "WGS84". + */ + final Ellipsoid ellipsoid = createEllipsoid(name, linearUnit); + final PrimeMeridian meridian = createPrimeMeridian(angularUnit); + final GeodeticDatum datum = objectFactory().createGeodeticDatum(properties(name), ellipsoid, meridian); + name = Utilities.toUpperCase(name, Characters.Filter.LETTERS_AND_DIGITS); + lastName = datum.getName(); + try { + final GeodeticDatum predefined = CommonCRS.valueOf(name).datum(); + if (equalsIgnoreMetadata(predefined.getEllipsoid(), ellipsoid) && + equalsIgnoreMetadata(predefined.getPrimeMeridian(), meridian)) + { + return predefined; + } + } catch (IllegalArgumentException e) { + // Not a name that can be mapped to CommonCRS. Ignore. + } + return datum; } - - //-- particularity case - if (datumName.equalsIgnoreCase("WGS84")) return CommonCRS.WGS84.datum(); - - //-- ELLIPSOID creation - final Ellipsoid ellipsoid = createEllipsoid(unit); - - //-- PRIME MERIDIAN - final PrimeMeridian primeMeridian = createPrimeMeridian(unit); - - //-- factory Datum creation - return objectFactory().createGeodeticDatum(name(datumName), ellipsoid, primeMeridian); } } /** - * Creating a prime meridian for the {@link #createGeodeticDatum(javax.measure.Unit) } method - * we are creating at an higher level.
- * As usual this method tries to follow the geotiff specification
- * Needed tags are : + * Creates the prime meridian from an EPSG code or from user-defined parameters. + * The GeoTIFF values used by this method are: + * *- *
* - * @param linearUnit use for building this {@link PrimeMeridian}. - * @return a {@link PrimeMeridian} built using the provided {@link Unit} and - * the provided metadata. - * @throws FactoryException if problem during factory Prime Meridian creation. + * If no prime-meridian is defined, then the default is Greenwich as per GeoTIFF specification. + * + * @param unit the angular unit of the longitude value relative to Greenwich. + * @return a prime meridian created from the given {@link Unit} and the above-cited GeoTIFF keys. + * + * @throws NumberFormatException if a numeric value was stored as a string and can not be parsed. + * @throws FactoryException if an error occurred during objects creation with the factories. */ - private PrimeMeridian createPrimeMeridian(final Unit linearUnit) throws DataStoreContentException, FactoryException { - //-- prime meridian : - //-- could be an EPSG code - //-- or could be user defined - //-- or not defined = greenwich - String pmCode = getAsString(GeoKeys.GeogPrimeMeridian, false); - - //-- if Prime Meridian code not define, assume WGS84 - if (pmCode == null) return CommonCRS.WGS84.primeMeridian(); - - //-- if Prime Meridian define as an EPSG code. - if (!pmCode.equals(GeoKeys.GTUserDefined_String)) { - return epsgFactory().createPrimeMeridian(String.valueOf(pmCode)); - } - //-- user define Prime Meridian creation - { - assert pmCode.equals(GeoKeys.GTUserDefined_String); - - double pmValue = getAsDouble(GeoKeys.GeogPrimeMeridianLong, false); - if (Double.isNaN(pmValue)) { - missingValue(GeoKeys.GeogPrimeMeridianLong); - pmValue = 0; + private PrimeMeridian createPrimeMeridian(final Unit- a code definition given by {@link GeoKeys.GeogPrimeMeridianGeoKey} tag
- *- a name given by {@link GeoKeys.GeogCitationGeoKey}
- *- a prime meridian value given by {@link GeoKeys.GeogPrimeMeridianLongGeoKey}
+ *- a code given by {@link GeoKeys#GeogPrimeMeridian}
+ *- a prime meridian value given by {@link GeoKeys#GeogPrimeMeridianLong}
*unit) throws FactoryException { + final int epsg = getAsInteger(GeoKeys.GeogPrimeMeridian); + switch (epsg) { + case GeoCodes.undefined: break; // If not specified, default to Greenwich. + default: { // Prime Meridian defined by an EPSG code. + return epsgFactory().createPrimeMeridian(String.valueOf(epsg)); + } + case GeoCodes.userDefined: { + final double longitude = getAsDouble(GeoKeys.GeogPrimeMeridianLong); + if (Double.isNaN(longitude)) { + missingValue(GeoKeys.GeogPrimeMeridianLong); + } else if (longitude != 0) { + /* + * If the prime meridian is not Greenwich, create that meridian without name. + * We do not use the name given by GeoKeys.GeogCitation because that name is + * for the CRS (e.g. "WGS84") while the prime meridian names are very different + * (e.g. "Paris", "Madrid", etc). + */ + return objectFactory().createPrimeMeridian(properties(null), longitude, unit); + } } - - //-- if user define prime meridian is not define, assume WGS84 - if (pmValue == 0) return CommonCRS.WGS84.primeMeridian(); - - final String name = getAsString(GeoKeys.GeogCitation, false); - return objectFactory().createPrimeMeridian( - name((name == null) ? "User Defined GEOTIFF Prime Meridian" : name), pmValue, linearUnit); } + return CommonCRS.WGS84.primeMeridian(); } /** - * Creating an {@link Ellipsoid} following the GeoTiff spec.
- * Creating a Ellipsoid for the {@link #createGeodeticDatum(javax.measure.Unit) } method - * we are creating at an higher level.
- * As usual this method tries to follow the geotiff specification
- * Needed tags are : + * Creates the ellipsoid from an EPSG code or from user-defined parameters. + * The GeoTIFF values used by this method are: + * *- *
* - * @param unit use for building this {@link Ellipsoid}. - * @return a {@link Ellipsoid} built using the provided {@link Unit} and - * the provided metadata. - * @throws FactoryException if problem during factory Prime Meridian creation. - * @throws DataStoreContentException if missing needed geokeys. + * @param name the name to use if the ellipsoid is user-defined, or {@code null} if unnamed. + * @param unit the linear unit of the semi-axis lengths. + * @return an ellipsoid created from the given {@link Unit} and the above-cited GeoTIFF keys. + * @throws NoSuchElementException if a mandatory value is missing. + * @throws NumberFormatException if a numeric value was stored as a string and can not be parsed. + * @throws FactoryException if an error occurred during objects creation with the factories. */ - private Ellipsoid createEllipsoid(final Unit unit) - throws FactoryException, DataStoreContentException { - - //-- ellipsoid key - final String ellipsoidKey = getAsString(GeoKeys.GeogEllipsoid, false); - - //-- if ellipsoid key NOT "user define" decode EPSG code. - if (ellipsoidKey != null && !ellipsoidKey.equalsIgnoreCase(GeoKeys.GTUserDefined_String)) - return epsgFactory().createEllipsoid(ellipsoidKey); - - //-- User define Ellipsoid creation - { - String nameEllipsoid = getAsString(GeoKeys.GeogCitation, false); - if (nameEllipsoid == null) { - nameEllipsoid = "User define unamed Ellipsoid"; - } - //-- particularity case - if (nameEllipsoid.equalsIgnoreCase("WGS84")) - return CommonCRS.WGS84.ellipsoid(); - - //-- try to build ellipsoid from others parameters. - //-- get semi Major axis and, semi minor or invertflattening - - //-- semi Major - final double semiMajorAxis = getAsDouble(GeoKeys.GeogSemiMajorAxis, true); - - //-- try to get inverseFlattening - double inverseFlattening = getAsDouble(GeoKeys.GeogInvFlattening, false); - if (Double.isNaN(inverseFlattening)) { - //-- get semi minor axis to build missing inverseFlattening - final double semiMinSTR = getAsDouble(GeoKeys.GeogSemiMinorAxis, true); - inverseFlattening = semiMajorAxis / (semiMajorAxis - semiMinSTR); + private Ellipsoid createEllipsoid(final String name, final Unit- a code definition given by {@link GeoKeys.GeogEllipsoidGeoKey} tag
- *- a name given by {@link GeoKeys.GeogCitationGeoKey}
- *- a semi major axis value given by {@link GeoKeys.GeogSemiMajorAxisGeoKey}
- *- a semi major axis value given by {@link GeoKeys.GeogInvFlatteningGeoKey}
- *- a semi major axis value given by {@link GeoKeys.GeogSemiMinorAxisGeoKey}
+ *- a code given by {@link GeoKeys#GeogEllipsoid} tag
+ *- a name given by {@link GeoKeys#GeogCitation}
+ *- a semi major axis value given by {@link GeoKeys#GeogSemiMajorAxis}
+ *- a semi major axis value given by {@link GeoKeys#GeogInvFlattening}
+ *- a semi major axis value given by {@link GeoKeys#GeogSemiMinorAxis}
*unit) throws FactoryException { + final int epsg = getAsInteger(GeoKeys.GeogEllipsoid); + switch (epsg) { + case GeoCodes.undefined: { + throw new NoSuchElementException(missingValue(GeoKeys.GeogGeodeticDatum)); + } + default: { + // Ellipsoid defined by an EPSG code. + return epsgFactory().createEllipsoid(String.valueOf(epsg)); + } + case GeoCodes.userDefined: { + /* + * Try to build ellipsoid from others parameters. Those parameters are the + * semi-major axis and either semi-minor axis or inverse flattening factor. + */ + final Map properties = properties(name); + final double semiMajor = getMandatoryDouble(GeoKeys.GeogSemiMajorAxis); + double inverseFlattening = getAsDouble(GeoKeys.GeogInvFlattening); + if (!Double.isNaN(inverseFlattening)) { + return objectFactory().createFlattenedSphere(properties, semiMajor, inverseFlattening, unit); + } + /* + * If the inverse flattening factory was not defined, fallback on semi-major axis length. + * This is a less common way to define ellipsoid (the most common way uses flattening). + */ + final double semiMinor = getMandatoryDouble(GeoKeys.GeogSemiMinorAxis); + return objectFactory().createEllipsoid(properties, semiMajor, semiMinor, unit); } - - //-- ellipsoid creation - return objectFactory().createFlattenedSphere(name(nameEllipsoid), semiMajorAxis, inverseFlattening, unit); } } @@ -744,7 +831,7 @@ final class CRSBuilder { * @param fallBackUnit * @return */ - private CartesianCS retrieveCartesianCS(final short unitKey, final CartesianCS baseCS, final Unit fallBackUnit) + private CartesianCS retrieveCartesianCS(final short unitKey, final CartesianCS baseCS, final Unit fallBackUnit) throws DataStoreContentException, FactoryException { assert baseCS.getDimension() == 2; @@ -779,7 +866,7 @@ final class CRSBuilder { } //-- get the Unit epsg code if exist - String unitCode = getAsString(unitKey, false); + String unitCode = getAsString(unitKey); if (unitCode == null || unitCode.equalsIgnoreCase(GeoKeys.GTUserDefined_String)) { return (CartesianCS) CoordinateSystems.replaceLinearUnit(baseCS, fallBackUnit); } @@ -926,7 +1013,7 @@ final class CRSBuilder { * @param fallBackUnit * @return */ - private EllipsoidalCS retrieveEllipsoidalCS(final short unitKey, final EllipsoidalCS baseCS, final Unit fallBackUnit) + private EllipsoidalCS retrieveEllipsoidalCS(final short unitKey, final EllipsoidalCS baseCS, final Unit fallBackUnit) throws DataStoreContentException, FactoryException { assert baseCS.getDimension() == 2; @@ -961,7 +1048,7 @@ final class CRSBuilder { } //-- get the Unit epsg code if exist - String unitCode = getAsString(unitKey, false); + String unitCode = getAsString(unitKey); if (unitCode == null || unitCode.equalsIgnoreCase(GeoKeys.GTUserDefined_String)) { return (EllipsoidalCS) CoordinateSystems.replaceAngularUnit(baseCS, fallBackUnit); } @@ -1065,11 +1152,10 @@ final class CRSBuilder { private CoordinateReferenceSystem createProjectedCRS() throws FactoryException, DataStoreContentException { - final String projCode = getAsString(GeoKeys.ProjectedCSType, false); + final String projCode = getAsString(GeoKeys.ProjectedCSType); //-- getting the linear unit used by this coordinate reference system. - final Unit linearUnit = createUnit(GeoKeys.ProjLinearUnits, - GeoKeys.ProjLinearUnitSize, Units.METRE, Units.METRE); + final Unit linearUnit = createUnit(GeoKeys.ProjLinearUnits, GeoKeys.ProjLinearUnitSize, Length.class, Units.METRE); //--------------------------- USER DEFINE -----------------------------// //-- if it's user defined, we have to parse many informations and @@ -1091,10 +1177,11 @@ final class CRSBuilder { //-- if 'tiff defined unit' does not match with decoded Projected CRS, build another converted projected CRS. if (linearUnit != null && !linearUnit.equals(pcrs.getCoordinateSystem().getAxis(0).getUnit())) { //-- Creating a new projected CRS - pcrs = objectFactory().createProjectedCRS(name(IdentifiedObjects.getName(pcrs, new DefaultCitation("EPSG"))), - (GeographicCRS) pcrs.getBaseCRS(), - pcrs.getConversionFromBase(), - retrieveCartesianCS(GeoKeys.ProjLinearUnits, pcrs.getCoordinateSystem(), linearUnit)); + pcrs = objectFactory().createProjectedCRS(properties(IdentifiedObjects.getName(pcrs, new DefaultCitation("EPSG"))), + pcrs.getBaseCRS(), + pcrs.getConversionFromBase(), + retrieveCartesianCS(GeoKeys.ProjLinearUnits, pcrs.getCoordinateSystem(), linearUnit)); + lastName = pcrs.getName(); } return pcrs; } @@ -1115,10 +1202,10 @@ final class CRSBuilder { * @throws DataStoreContentException if missing needed geoKey. * @throws FactoryException if problem during projected CRS factory build. */ - private ProjectedCRS createUserDefinedProjectedCRS(final Unit linearUnit) + private ProjectedCRS createUserDefinedProjectedCRS(final Unit linearUnit) throws FactoryException, DataStoreContentException { //-- get projected CRS Name - String projectedCrsName = getAsString(GeoKeys.PCSCitation, false); + String projectedCrsName = getAsString(GeoKeys.PCSCitation); if (projectedCrsName == null) { projectedCrsName = "User Defined unnamed ProjectedCRS"; } @@ -1128,17 +1215,18 @@ final class CRSBuilder { final GeographicCRS gcs = createGeographicCRS(); //-- get the projection code if exist - final String projCode = getAsString(GeoKeys.Projection, false); + final String projCode = getAsString(GeoKeys.Projection); //-- is it user defined? final Conversion projection; if (projCode == null || projCode.equals(GeoKeys.GTUserDefined_String)) { //-- get Operation Method from proj key - final String coordTrans = getAsString(GeoKeys.ProjCoordTrans, true); + final String coordTrans = getMandatoryString(GeoKeys.ProjCoordTrans); final OperationMethod operationMethod = operationFactory().getOperationMethod(coordTrans); final ParameterValueGroup parameters = operationMethod.getParameters().createValue(); - projection = operationFactory().createDefiningConversion(name(projectedCrsName), operationMethod, parameters); + projection = operationFactory().createDefiningConversion(properties(projectedCrsName), operationMethod, parameters); + lastName = projection.getName(); } else { projection = (Conversion) epsgFactory().createCoordinateOperation(String.valueOf(projCode)); } @@ -1148,7 +1236,7 @@ final class CRSBuilder { if (linearUnit != null && !linearUnit.equals(Units.METRE)) predefineCartesianCS = retrieveCartesianCS(GeoKeys.ProjLinearUnits, predefineCartesianCS, linearUnit); - return objectFactory().createProjectedCRS(name(projectedCrsName), gcs, projection, predefineCartesianCS); + return objectFactory().createProjectedCRS(properties(projectedCrsName), gcs, projection, predefineCartesianCS); } @@ -1182,30 +1270,24 @@ final class CRSBuilder { throws FactoryException, DataStoreContentException { //-- Get the crs code - final String tempCode = getAsString(GeoKeys.GeographicType, false); + final String tempCode = getAsString(GeoKeys.GeographicType); //-- Angular units used in this geotiff image - Unit angularUnit = createUnit(GeoKeys.GeogAngularUnits, - GeoKeys.GeogAngularUnitSize, Units.RADIAN, - Units.DEGREE); + Unit angularUnit = createUnit(GeoKeys.GeogAngularUnits, GeoKeys.GeogAngularUnitSize, Angle.class, Units.DEGREE); //-- Geographic CRS is "UserDefine", we have to parse many informations from other geokeys. if (tempCode == null || tempCode.equals(GeoKeys.GTUserDefined_String)) { - //-- linear unit - final Unit linearUnit = createUnit(GeoKeys.GeogLinearUnits, - GeoKeys.GeogLinearUnitSize, Units.METRE, - Units.METRE); + final Unit linearUnit = createUnit(GeoKeys.GeogLinearUnits, GeoKeys.GeogLinearUnitSize, Length.class, Units.METRE); ///-- Geographic CRS given name from tiff tag (GeogCitation) - String name = getAsString(GeoKeys.GeogCitation, false); - if (name == null) name = "User Define Geographic CRS"; + String name = getAsString(GeoKeys.GeogCitation); + if (name == null) name = "User-defined Geographic CRS"; - final GeodeticDatum datum = createGeodeticDatum(linearUnit); + final GeodeticDatum datum = createGeodeticDatum(name, angularUnit, linearUnit); //-- make the user defined GCS from all the components... - return objectFactory().createGeographicCRS(name(name), - datum, - retrieveEllipsoidalCS(GeoKeys.GeogAngularUnits, - CommonCRS.defaultGeographic().getCoordinateSystem(), - angularUnit)); + return objectFactory().createGeographicCRS(properties(name), datum, + retrieveEllipsoidalCS(GeoKeys.GeogAngularUnits, + CommonCRS.defaultGeographic().getCoordinateSystem(), + angularUnit)); // (EllipsoidalCS) CoordinateSystems.replaceAngularUnit(CommonCRS.defaultGeographic().getCoordinateSystem(), // angularUnit)); } @@ -1227,19 +1309,21 @@ final class CRSBuilder { if (!(geoCRS instanceof GeographicCRS)) { warning(Resources.Keys.UnexpectedGeoCRS_1, reader.input.filename); - geoCRS = objectFactory().createGeographicCRS(name(IdentifiedObjects.getName(geoCRS, new DefaultCitation("EPSG"))), + geoCRS = objectFactory().createGeographicCRS(properties(IdentifiedObjects.getName(geoCRS, new DefaultCitation("EPSG"))), ((GeodeticCRS)geoCRS).getDatum(), CommonCRS.defaultGeographic().getCoordinateSystem()); + lastName = geoCRS.getName(); } //-- in case where tiff define unit does not match if (angularUnit != null && !angularUnit.equals(geoCRS.getCoordinateSystem().getAxis(0).getUnit())) { - geoCRS = objectFactory().createGeographicCRS(name(IdentifiedObjects.getName(geoCRS, new DefaultCitation("EPSG"))), - (GeodeticDatum) ((GeographicCRS)geoCRS).getDatum(), + geoCRS = objectFactory().createGeographicCRS(properties(IdentifiedObjects.getName(geoCRS, new DefaultCitation("EPSG"))), + ((GeographicCRS)geoCRS).getDatum(), retrieveEllipsoidalCS(GeoKeys.GeogAngularUnits, CommonCRS.defaultGeographic().getCoordinateSystem(), angularUnit)); // (EllipsoidalCS) CoordinateSystems.replaceAngularUnit(CommonCRS.defaultGeographic().getCoordinateSystem(), angularUnit)); + lastName = geoCRS.getName(); } return (GeographicCRS) geoCRS; } Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java?rev=1771707&r1=1771706&r2=1771707&view=diff ============================================================================== --- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java [UTF-8] (original) +++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoCodes.java [UTF-8] Mon Nov 28 10:52:22 2016 @@ -39,6 +39,11 @@ final class GeoCodes { */ public static final short undefined = 0; + /** + * The code value for a property defined by the user instead than by an EPSG code. + */ + public static final short userDefined = 32767; + /* * 6.3.1.1 Model Type Codes * Modified: sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java?rev=1771707&r1=1771706&r2=1771707&view=diff ============================================================================== --- sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java [UTF-8] (original) +++ sis/branches/JDK8/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoKeys.java [UTF-8] Mon Nov 28 10:52:22 2016 @@ -44,8 +44,8 @@ final class GeoKeys { /** Section 6.3.1.2 Codes */ public static final short RasterType = 1025; /** Documentation */ public static final short Citation = 1026; - public static final short UserDefined = 32767; - static final String GTUserDefined_String = "32767"; + @Deprecated public static final short UserDefined = 32767; + @Deprecated static final String GTUserDefined_String = "32767"; // 6.2.2 Geographic CS Parameter Keys public static final short GeographicType = 2048; /* Section 6.3.2.1 Codes */ Modified: sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java URL: http://svn.apache.org/viewvc/sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java?rev=1771707&r1=1771706&r2=1771707&view=diff ============================================================================== --- sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] (original) +++ sis/branches/JDK8/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/MetadataBuilder.java [UTF-8] Mon Nov 28 10:52:22 2016 @@ -855,6 +855,19 @@ public class MetadataBuilder { } /** + * Returns {@code true} if a title has been specified for the current identification information. + * This method is provided because titles are mandatory in ISO 19115 metadata, so data stores may + * want to provide a fallback if no title has been found. Titles are specified by calls to + * {@link #addTitle(CharSequence)} and needs to be specified again if {@link #newIdentification()} + * has been invoked. + * + * @return whether a title exists for the current identification information. + */ + public final boolean hasTitle() { + return (citation != null) && citation.getTitle() != null; + } + + /** * Adds a title or alternate title of the resource. * Storage location is: * @@ -1449,6 +1462,21 @@ parse: for (int i = 0; i < length;) } } + /** + * Sets a general description of the transformation form grid coordinates to "real world" coordinates. + * Storage location is: + * + * metadata/spatialRepresentationInfo/transformationDimensionDescription+ * + * @param value a general description of the "grid to CRS" transformation, or {@code null} if unknown. + */ + public final void setGridToCRS(final CharSequence value) { + final InternationalString i18n = trim(value); + if (i18n != null) { + gridRepresentation().setTransformationDimensionDescription(i18n); + } + } + /** * Returns the axis at the given dimension index. All previous dimensions are created if needed. *