/* * 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 java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.ServiceLoader; import java.util.logging.Logger; import org.opengis.coverage.grid.RectifiedGrid; import org.opengis.geometry.DirectPosition; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.util.FactoryException; import org.apache.sis.geometry.GeneralDirectPosition; import org.apache.sis.internal.referencing.GeodeticObjectBuilder; import org.apache.sis.util.logging.Logging; import org.geotoolkit.image.io.metadata.ReferencingBuilder; import org.geotoolkit.image.io.metadata.SpatialMetadata; import org.geotoolkit.image.io.plugin.TiffImageReader; import org.geotoolkit.internal.image.io.GridDomainAccessor; import org.geotoolkit.referencing.ReferencingUtilities; /** * Geotiff is a widely used format, many metadata formats may be associated with it. * Extensions may modify metadatas, samples or even grid geometry and crs. * * @author Remi Marechal (Geomatys) * @author Johann Sorel (Geomatys) */ public abstract class GeoTiffExtension { private static final GeoTiffExtension[] EXTENSIONS; static { final ServiceLoader<GeoTiffExtension> loader = ServiceLoader.load(GeoTiffExtension.class); final List<GeoTiffExtension> extensions = new ArrayList<>(); for(GeoTiffExtension extension : loader){ extensions.add(extension); } EXTENSIONS = extensions.toArray(new GeoTiffExtension[0]); } /** * Get all extensions. * * @return All geotiff extensions */ public static GeoTiffExtension[] getExtensions(){ return EXTENSIONS.clone(); } /** * Check if this extension is present for given input. * if isPresent return true and before using any method * a new instance of the extension should be created. * * @param input * @return true if extension exist */ public abstract boolean isPresent(Object input); /** * Create a new instance of this extension. * @return */ public GeoTiffExtension newInstance(){ try { return this.getClass().newInstance(); } catch (InstantiationException | IllegalAccessException ex) { throw new RuntimeException("Failed to create a new instance of "+this.getClass()); } } /** * Modify the spatial metadata of the geotiff coverage. * * @param reader * @param metadata to be modified * @return * @throws java.io.IOException */ public abstract SpatialMetadata fillSpatialMetaData(TiffImageReader reader, SpatialMetadata metadata) throws IOException; /** * Modify the given spatial metadata, adding a new crs axis dimension. * If the crs axis already exist it will not be added and only the value will be updated. * * This method should be used only to declare the geotiff on a new axis. * For example adding a temporal dimension and define it's value. * * @param metadata * @param axisCrs * @param value * @throws FactoryException */ public static void setOrCreateSliceDimension(SpatialMetadata metadata, CoordinateReferenceSystem axisCrs, double value) throws FactoryException{ //ensure no cache modify the values metadata.clearInstancesCache(); final ReferencingBuilder rb = new ReferencingBuilder(metadata); final GridDomainAccessor acc = new GridDomainAccessor(metadata); final RectifiedGrid rectifiedGrid = metadata.getInstanceForType(RectifiedGrid.class); //search for the coordinate reference system CoordinateReferenceSystem crs = rb.getCoordinateReferenceSystem(CoordinateReferenceSystem.class); if(crs==null){ //no crs defined, we can't add any slice axis value final Logger logger = Logging.getLogger("org.geotoolkit.metadata.geotiff"); logger.info("Tiff has no base CRS, slice dimension crs will not be added."); return; } final List<CoordinateReferenceSystem> crss = ReferencingUtilities.decompose(crs); int axisIndex = -1; int inc = 0; for(CoordinateReferenceSystem cs : crss){ if(cs.equals(axisCrs)){ axisIndex = inc; break; } inc += cs.getCoordinateSystem().getDimension(); } if (axisIndex < 0) { //this axis is not declared, add it crs = new GeodeticObjectBuilder().addName(crs.getName().getCode()+"/"+axisCrs.getName().getCode()) .createCompoundCRS(crs, axisCrs); rb.setCoordinateReferenceSystem(crs); //calculate new transform values final List<double[]> offsetVectors = new ArrayList(rectifiedGrid.getOffsetVectors()); for (int i = 0; i < offsetVectors.size(); i++) { double[] vector = offsetVectors.get(i); vector = Arrays.copyOf(vector, vector.length+1); offsetVectors.set(i, vector); } final double[] tempVector = new double[crs.getCoordinateSystem().getDimension()]; tempVector[tempVector.length-1] = 1; offsetVectors.add(tempVector); //new origin final DirectPosition oldOrigin = rectifiedGrid.getOrigin(); final GeneralDirectPosition newOrigin = new GeneralDirectPosition(crs); for (int i = 0, n = oldOrigin.getDimension(); i < n; i++) { newOrigin.setOrdinate(i, oldOrigin.getOrdinate(i)); } newOrigin.setOrdinate(oldOrigin.getDimension(), value); //new limits final int[][] limits = acc.getLimits(); limits[0] = Arrays.copyOf(limits[0], limits[0].length+1); limits[1] = Arrays.copyOf(limits[1], limits[1].length+1); // limits[1][limits[1].length-1] = 1; //set new values acc.setOrigin(newOrigin.getCoordinate()); acc.setLimits(limits[0], limits[1]); acc.clearOffsetVectors(); for (double[] ov : offsetVectors) { acc.addOffsetVector(ov); } } else { //axis already exist, update the value //new origin final DirectPosition oldOrigin = rectifiedGrid.getOrigin(); final GeneralDirectPosition newOrigin = new GeneralDirectPosition(oldOrigin); newOrigin.setOrdinate(axisIndex, value); //set new values acc.setOrigin(newOrigin.getCoordinate()); } //metadata keeps a cache of object likes crs, rectifiedgrid and so on ... //we must clear them since we modifyed the sub nodes metadata.clearInstancesCache(); } }