/* * Geotoolkit.org - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2016, Geomatys * * 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.geotoolkit.metadata.geotiff; import javax.measure.Unit; import java.io.IOException; import org.opengis.metadata.citation.Citation; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.OperationMethod; import org.opengis.referencing.operation.Projection; import org.opengis.referencing.IdentifiedObject; import org.opengis.referencing.datum.PrimeMeridian; import org.opengis.referencing.datum.Ellipsoid; import org.opengis.metadata.Identifier; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.crs.GeocentricCRS; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.ProjectedCRS; import org.opengis.referencing.datum.GeodeticDatum; import org.opengis.referencing.operation.TransformException; import org.opengis.util.FactoryException; import org.apache.sis.measure.Units; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.internal.referencing.provider.AlbersEqualArea; import org.apache.sis.internal.referencing.provider.LambertConformal2SP; import org.apache.sis.internal.referencing.provider.LambertConformal1SP; import org.apache.sis.internal.referencing.provider.Mercator1SP; import org.apache.sis.internal.referencing.provider.Mercator2SP; import org.apache.sis.internal.referencing.provider.PolarStereographicA; import org.apache.sis.internal.referencing.provider.PolarStereographicB; import org.apache.sis.internal.referencing.provider.TransverseMercator; import org.apache.sis.referencing.operation.transform.AbstractMathTransform; import org.apache.sis.internal.referencing.ReferencingUtilities; import org.geotoolkit.internal.referencing.CRSUtilities; import org.geotoolkit.metadata.Citations; import org.geotoolkit.referencing.operation.provider.Orthographic; import org.geotoolkit.referencing.operation.provider.ObliqueMercator; import org.geotoolkit.referencing.operation.provider.Stereographic; import org.geotoolkit.resources.Errors; import static org.geotoolkit.metadata.geotiff.GeoTiffConstants.*; import static org.apache.sis.util.ArgumentChecks.*; /** * Encode a CoordinateReferenceSystem as GeoTiff tags. * * note : with java 9 class will become only accessible by its module (no public signature on class header). * * @author Johann Sorel (Geomatys) * @module */ public final class GeoTiffCRSWriter { /** * Fill the Geotiff CRS metadatas with the given CRS. */ public void fillCRSMetaDatas(final GeoTiffMetaDataStack stack, CoordinateReferenceSystem crs) throws IOException, FactoryException { ensureNonNull("crs", crs); // extract 2D part from multidimensional CRS if (crs.getCoordinateSystem().getDimension() > 2) { try { crs = CRSUtilities.getCRS2D(crs); } catch (TransformException ex) { throw new IllegalStateException("Impossible to extract 2D part of coordinate referenceSystem", ex); } } final int crsType; if (crs instanceof ProjectedCRS) { crsType = ModelTypeProjected; fillProjectedCRSMetaDatas(stack, (ProjectedCRS) crs); } else if(crs instanceof GeocentricCRS) { crsType = ModelTypeGeocentric; fillGeocentricCRSMetaDatas(stack, (GeocentricCRS) crs); } else if(crs instanceof GeographicCRS) { crsType = ModelTypeGeographic; fillGeographicCRSMetaDatas(stack, (GeographicCRS) crs); } else { throw new IOException("GeoTiff only handle ProjectedCRS, GeocentricCRS or GeographicCRS. Can not support CRS : " + crs); } // add GTModelTypeGeoKey in GeoKeyDirectoryEntry stack.addShort(GTModelTypeGeoKey, crsType); } /** * Fill a projected CRS metadatas in geotiff tags. */ private static void fillProjectedCRSMetaDatas(final GeoTiffMetaDataStack stack, final ProjectedCRS crs) throws IOException, FactoryException { //first see if it is an EPSG CRS final Integer code = getEPSGCode(crs); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(ProjectedCSTypeGeoKey, code); return; } // user defined projected coordinate reference system. stack.addShort(ProjectedCSTypeGeoKey, GTUserDefinedGeoKey); // name of the user defined projected crs stack.addAscii(PCSCitationGeoKey, crs.getName().getCode()); // projection fillProjection(stack, crs); // geographic crs fillGeoGCS(stack, crs.getBaseCRS()); } /** * Fill a geographic CRS metadatas in geotiff tags. */ private static void fillGeographicCRSMetaDatas(final GeoTiffMetaDataStack stack, final GeographicCRS crs) throws IOException, FactoryException { //first see if it is an EPSG CRS final Integer code = getEPSGCode(crs); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(GeographicTypeGeoKey, code); return; } //it is a user defined CRS stack.addShort(GeographicTypeGeoKey, GTUserDefinedGeoKey); //use CRS code as a citation stack.addAscii(GeogCitationGeoKey, crs.getName().getCode()); // geodetic datum final GeodeticDatum datum = crs.getDatum(); fillDatum(stack, datum); // angular unit final Unit angularUnit = crs.getCoordinateSystem().getAxis(0).getUnit(); fillUnit(stack, angularUnit, 0); // prime meridian fillPrimeMeridian(stack, datum.getPrimeMeridian()); // linear unit final Unit linearUnit = datum.getEllipsoid().getAxisUnit(); fillUnit(stack,linearUnit, 1); } /** * Fill a geocentric CRS metadatas in geotiff tags. */ private static void fillGeocentricCRSMetaDatas(final GeoTiffMetaDataStack stack, final GeocentricCRS crs) throws IOException { throw new IOException("Not implemented."); } /** * Parsing ProjectionGeoKey 3074 for a <code>ProjectedCRS</code>. * * @param projectedCRS * The <code>ProjectedCRS</code> to parse. * @param metadata */ private static void fillProjection(final GeoTiffMetaDataStack stack, final ProjectedCRS projectedCRS) throws FactoryException, IOException { final Projection projection = projectedCRS.getConversionFromBase(); //first see if it is an EPSG Projection final Integer code = getEPSGCode(projection); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(ProjectionGeoKey, code); return; } // user defined projection stack.addShort(ProjectionGeoKey, GTUserDefinedGeoKey); stack.addAscii(PCSCitationGeoKey, projection.getName().getCode()); final OperationMethod method = projection.getMethod(); // looking for the parameters String name = method.getName().getCode(); name = name.trim(); name = name.replace(' ', '_'); final MathTransform trs = projection.getMathTransform(); if(trs instanceof AbstractMathTransform){ final AbstractMathTransform mt = (AbstractMathTransform) projection.getMathTransform(); final ParameterValueGroup parameters = mt.getParameterValues(); // key 3075 and parameters fillCoordinateProjectionTransform(stack, parameters, name); }else{ throw new IOException("Unsupported transform " + trs); } // parse linear unit fillLinearUnit(stack, projectedCRS); } /** * Parses a "GEOGCS" element. This element has the following pattern: * * <blockquote><code> * GEOGCS["<name>", <datum>, <prime meridian>, <angular unit> {,<twin axes>} {,<authority>}] * </code></blockquote> */ private static void fillGeoGCS(final GeoTiffMetaDataStack stack, final GeographicCRS geographicCRS) throws FactoryException { //first see if it is an EPSG geographic CRS final Integer code = getEPSGCode(geographicCRS); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(GeographicTypeGeoKey,code); return; } // ///////////////////////////////////////////////////////////////////// // User defined CRS // ///////////////////////////////////////////////////////////////////// // user defined geographic coordinate reference system. stack.addShort(GeographicTypeGeoKey, GTUserDefinedGeoKey); // get the name of the gcs which will become a citation for the user // define crs stack.addAscii(GeogCitationGeoKey, geographicCRS.getName().getCode()); // geodetic datum final GeodeticDatum datum = geographicCRS.getDatum(); fillDatum(stack, datum); // angular unit final Unit angularUnit = geographicCRS.getCoordinateSystem().getAxis(0).getUnit(); fillUnit(stack, angularUnit, 0); // prime meridian fillPrimeMeridian(stack, datum.getPrimeMeridian()); // linear unit final Unit linearUnit = datum.getEllipsoid().getAxisUnit(); fillUnit(stack, linearUnit, 1); } /** * Parses a "DATUM" element. This element has the following pattern: * * <blockquote><code> * DATUM["<name>", <spheroid> {,<to wgs84>} {,<authority>}] * </code></blockquote> */ private static void fillDatum(final GeoTiffMetaDataStack stack, final GeodeticDatum datum) throws FactoryException { //first see if it is an EPSG Datum final Integer code = getEPSGCode(datum); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(GeogGeodeticDatumGeoKey, code); return; } //it is a user defined CRS stack.addShort(GeogGeodeticDatumGeoKey, GTUserDefinedGeoKey); //set the name stack.addAscii(GeogCitationGeoKey, datum.getName().getCode()); fillEllipsoid(stack, datum.getEllipsoid()); } /** * Parses a "SPHEROID" element. This element has the following pattern: * * <blockquote><code> * SPHEROID["<name>", <semi-major axis>, <inverse flattening> {,<authority>}] * </code></blockquote> */ private static void fillEllipsoid(final GeoTiffMetaDataStack stack, final Ellipsoid ellipsoid) throws FactoryException { //first see if it is an EPSG Elipsoid final Integer code = getEPSGCode(ellipsoid); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(GeogEllipsoidGeoKey, code); return; } // user defined ellipsoid stack.addShort(GeogEllipsoidGeoKey, GTUserDefinedGeoKey); // setting the name stack.addAscii(GeogCitationGeoKey, ellipsoid.getName().getCode()); // setting semimajor axis stack.addDouble(GeogSemiMajorAxisGeoKey, ellipsoid.getSemiMajorAxis()); // setting inverse flattening stack.addDouble(GeogInvFlatteningGeoKey, ellipsoid.getInverseFlattening()); } /** * Parses a "PRIMEM" element. This element has the following pattern: * * <blockquote><code> * PRIMEM["<name>", <longitude> {,<authority>}] * </code></blockquote> */ private static void fillPrimeMeridian(final GeoTiffMetaDataStack stack, final PrimeMeridian pm) throws FactoryException { //first see if it is an EPSG Elipŝoid final Integer code = getEPSGCode(pm); if (code != null) { //it is a usual EPSG, store it's code and return stack.addShort(GeogPrimeMeridianGeoKey, code); return; } //user defined prime meridien stack.addShort(GeogPrimeMeridianGeoKey, GTUserDefinedGeoKey); //citation stack.addAscii(GeogCitationGeoKey, pm.getName().getCode()); //longitude stack.addDouble(GeogPrimeMeridianLongGeoKey, pm.getGreenwichLongitude()); } /** * Parses a along with coordinate transformation and its parameters. */ private static void fillCoordinateProjectionTransform(final GeoTiffMetaDataStack stack, final ParameterValueGroup parameters, final String name) throws IOException{ final String desc = parameters.getDescriptor().getName().getCode(); // ///////////////////////////////////////////////////////////////////// // Transverse Mercator // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new TransverseMercator().getParameters(), desc)) { // TODO: avoid creation of temporary object. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_TransverseMercator); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,TransverseMercator.LONGITUDE_OF_ORIGIN)); stack.addDouble(ProjNatOriginLatGeoKey, value(parameters,TransverseMercator.LATITUDE_OF_ORIGIN)); stack.addDouble(ProjScaleAtNatOriginGeoKey, value(parameters,TransverseMercator.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,TransverseMercator.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,TransverseMercator.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // Mercator_1SP // Mercator_2SP // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new Mercator2SP().getParameters(), desc) || // TODO: need an other way to check for match. IdentifiedObjects.isHeuristicMatchForName(new Mercator1SP().getParameters(), desc)) { // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_Mercator); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,Mercator1SP.LONGITUDE_OF_ORIGIN)); //stack.addDouble(ProjNatOriginLatGeoKey, value(parameters,Mercator1SP.LATITUDE_OF_ORIGIN)); stack.addDouble(ProjScaleAtNatOriginGeoKey, value(parameters,Mercator1SP.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,Mercator1SP.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,Mercator1SP.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // Lamber conformal 1sp // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new LambertConformal1SP().getParameters(), desc)) { // TODO: need an other way to check for match. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_LambertConfConic_Helmert); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,LambertConformal1SP.LONGITUDE_OF_ORIGIN)); stack.addDouble(ProjNatOriginLatGeoKey, value(parameters,LambertConformal1SP.LATITUDE_OF_ORIGIN)); stack.addDouble(ProjScaleAtNatOriginGeoKey, value(parameters,LambertConformal1SP.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,LambertConformal1SP.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,LambertConformal1SP.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // LAMBERT_CONFORMAL_CONIC_2SP // lambert_conformal_conic_2SP_Belgium // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new LambertConformal2SP().getParameters(), desc)) { // TODO: need an other way to check for match. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_LambertConfConic_2SP); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,LambertConformal2SP.LONGITUDE_OF_FALSE_ORIGIN)); stack.addDouble(ProjNatOriginLatGeoKey, value(parameters,LambertConformal2SP.LATITUDE_OF_FALSE_ORIGIN)); stack.addDouble(ProjStdParallel1GeoKey, value(parameters,LambertConformal2SP.STANDARD_PARALLEL_1)); stack.addDouble(ProjStdParallel2GeoKey, value(parameters,LambertConformal2SP.STANDARD_PARALLEL_2)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,LambertConformal2SP.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,LambertConformal2SP.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // stereographic // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(Stereographic.PARAMETERS, desc)) { // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_Stereographic); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,Stereographic.CENTRAL_MERIDIAN)); stack.addDouble(ProjNatOriginLatGeoKey, value(parameters,Stereographic.LATITUDE_OF_ORIGIN)); stack.addDouble(ProjScaleAtNatOriginGeoKey, value(parameters,Stereographic.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,Stereographic.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,Stereographic.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // polar_stereographic varian B // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new PolarStereographicB().getParameters(), desc)) { // TODO: need an other way to check for match. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_PolarStereographic); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjStraightVertPoleLongGeoKey, value(parameters,PolarStereographicB.LONGITUDE_OF_ORIGIN)); stack.addDouble(ProjStdParallel1GeoKey, value(parameters,PolarStereographicB.STANDARD_PARALLEL)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,PolarStereographicB.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,PolarStereographicB.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // polar_stereographic Varian A // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new PolarStereographicA().getParameters(), desc)) { // TODO: need an other way to check for match. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_PolarStereographic); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, value(parameters,PolarStereographicA.LONGITUDE_OF_ORIGIN)); stack.addDouble(ProjStraightVertPoleLongGeoKey, value(parameters,PolarStereographicA.LATITUDE_OF_ORIGIN)); stack.addDouble(ProjScaleAtNatOriginGeoKey, value(parameters,PolarStereographicA.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,PolarStereographicA.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,PolarStereographicA.FALSE_NORTHING)); return; } // ///////////////////////////////////////////////////////////////////// // Oblique Mercator // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(ObliqueMercator.PARAMETERS, desc)) { // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_ObliqueMercator); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjCenterLongGeoKey, value(parameters,ObliqueMercator.LONGITUDE_OF_CENTRE)); stack.addDouble(ProjCenterLatGeoKey, value(parameters,ObliqueMercator.LATITUDE_OF_CENTRE)); stack.addDouble(ProjScaleAtCenterGeoKey, value(parameters,ObliqueMercator.SCALE_FACTOR)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,ObliqueMercator.FALSE_EASTING)); stack.addDouble(ProjAzimuthAngleGeoKey, value(parameters,ObliqueMercator.AZIMUTH)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,ObliqueMercator.FALSE_NORTHING)); // rectified grid angle??? return; } // ///////////////////////////////////////////////////////////////////// // albers_Conic_Equal_Area // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(new AlbersEqualArea().getParameters(), desc)) { // TODO: need an other way to check for match. // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_AlbersEqualArea); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjNatOriginLongGeoKey, parameters.parameter("longitude_of_center").doubleValue()); //TODO no direct match found ? stack.addDouble(ProjNatOriginLatGeoKey, parameters.parameter("latitude_of_center").doubleValue()); //TODO no direct match found ? stack.addDouble(ProjFalseEastingGeoKey, value(parameters,AlbersEqualArea.EASTING_AT_FALSE_ORIGIN)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,AlbersEqualArea.NORTHING_AT_FALSE_ORIGIN)); stack.addDouble(ProjStdParallel1GeoKey, value(parameters,AlbersEqualArea.STANDARD_PARALLEL_1)); stack.addDouble(ProjStdParallel2GeoKey, value(parameters,AlbersEqualArea.STANDARD_PARALLEL_2)); // rectified grid angle??? return; } // ///////////////////////////////////////////////////////////////////// // Orthographic // ///////////////////////////////////////////////////////////////////// if (IdentifiedObjects.isHeuristicMatchForName(Orthographic.PARAMETERS, desc)) { // key 3075 stack.addShort(ProjCoordTransGeoKey, CT_Orthographic); stack.addAscii(PCSCitationGeoKey, name); // params stack.addDouble(ProjCenterLongGeoKey, value(parameters,Orthographic.LONGITUDE_OF_CENTRE)); stack.addDouble(ProjCenterLatGeoKey, value(parameters,Orthographic.LATITUDE_OF_CENTRE)); stack.addDouble(ProjFalseEastingGeoKey, value(parameters,Orthographic.FALSE_EASTING)); stack.addDouble(ProjFalseNorthingGeoKey, value(parameters,Orthographic.FALSE_NORTHING)); return; } throw new IOException("Could not find match for projection " + name); } /** * Parses a linear unit for a <code>ProjectedCRS</code>. * * @todo complete the list of linear unit of measures and clean the * exception * @param projectedCRS * @param metadata */ private static void fillLinearUnit(final GeoTiffMetaDataStack stack, final ProjectedCRS projectedCRS) { // getting the linear unit final Unit linearUnit = ReferencingUtilities.getUnit(projectedCRS.getCoordinateSystem()); if (linearUnit != null && !Units.METRE.isCompatible(linearUnit)) { throw new IllegalArgumentException(Errors.format( Errors.Keys.NonLinearUnit_1, linearUnit)); } if (Units.METRE.isCompatible(linearUnit)) { if (Units.METRE.equals(linearUnit)) { stack.addShort(ProjLinearUnitsGeoKey,Linear_Meter); stack.addDouble(ProjLinearUnitSizeGeoKey, 1.0); } if (Units.NAUTICAL_MILE.equals(linearUnit)) { stack.addShort(ProjLinearUnitsGeoKey,Linear_Mile_International_Nautical); stack.addDouble(ProjLinearUnitSizeGeoKey, linearUnit.getConverterTo(Units.METRE).convert(1)); } if (Units.FOOT.equals(linearUnit)) { stack.addShort(ProjLinearUnitsGeoKey, Linear_Foot); stack.addDouble(ProjLinearUnitSizeGeoKey, linearUnit.getConverterTo(Units.METRE).convert(1)); } // if (Units.YARD.equals(linearUnit)) { // TODO: pending completion of migration of JSR-275 to JSR-363. // stack.addShort(ProjLinearUnitsGeoKey,Linear_Yard_Sears);// ?? // stack.addDouble(ProjLinearUnitSizeGeoKey, linearUnit.getConverterTo(Units.METRE).convert(1)); // } } } /** * Parses an "UNIT" element. This element has the following pattern: * * <blockquote><code> * UNIT["<name>", <conversion factor> {,<authority>}] * </code></blockquote> */ private static void fillUnit(final GeoTiffMetaDataStack stack, final Unit unit, final int model) { // user defined unit stack.addShort( (model == 0) ? GeogAngularUnitsGeoKey : ProjLinearUnitsGeoKey, GTUserDefinedGeoKey); try { // citation stack.addAscii(GeogCitationGeoKey, unit.toString()); } catch(Exception ex) { // do nothing unit is unamed. Adding citation is useless. } final Unit base; if (Units.METRE.isCompatible(unit)) { base = Units.METRE; } else if (Units.SECOND.isCompatible(unit)) { base = Units.SECOND; } else if (Units.RADIAN.isCompatible(unit) && !Units.UNITY.equals(unit)) { base = Units.RADIAN; } else { base = null; } if (base != null) { stack.addDouble( model == 0 ? GeogAngularUnitSizeGeoKey : GeogLinearUnitSizeGeoKey, unit.getConverterTo(base).convert(1)); } else { stack.addDouble( model == 0 ? GeogAngularUnitSizeGeoKey : GeogLinearUnitSizeGeoKey, 1); } } /** * Searches for an EPSG code inside this <code>IdentifiedObject</code>. * If it can not be found, this method will search in the EPSG authority * for an equivalant object and return it's identifier * * @return EPSG numeric code, if one is found, null otherwise. */ private static Integer getEPSGCode(final IdentifiedObject candidate) throws FactoryException { // looking for an EPSG code for(final Identifier rid : candidate.getIdentifiers()){ final Citation citation = rid.getAuthority(); if(org.apache.sis.metadata.iso.citation.Citations.identifierMatches(citation, Citations.EPSG)){ return Integer.parseInt(rid.getCode()); } } //search for an IdentifiedObject with the same definition return org.apache.sis.referencing.IdentifiedObjects.lookupEPSG(candidate); } private static double value(final ParameterValueGroup parameters, final ParameterDescriptor desc){ return parameters.parameter(desc.getName().getCode()).doubleValue(); } }