/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2014, 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.coverage.xmlstore; import java.awt.Color; import java.awt.image.DataBuffer; import org.apache.sis.util.Numbers; import org.opengis.metadata.content.TransferFunctionType; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.apache.sis.measure.NumberRange; import org.geotoolkit.coverage.Category; import org.apache.sis.util.ObjectConverters; import org.opengis.referencing.operation.MathTransform1D; import org.apache.sis.referencing.operation.transform.TransferFunction; /** * * @author Johann Sorel (Geomatys) */ @XmlRootElement(name="Category") @XmlAccessorType(XmlAccessType.FIELD) public class XMLCategory { /** y=C0+C1*x */ public static final String FUNCTION_LINEAR = "linear"; /** y=10^(C0+C1*x) */ public static final String FUNCTION_EXPONENTIAL = "exponential"; @XmlElement(name="name") public String name; @XmlElement(name="lower") public double lower; @XmlElement(name="upper") public double upper; @XmlElement(name="c0") public double c0; @XmlElement(name="c1") public double c1; @XmlElement(name="function") public String function; @XmlElement(name="colors") public String[] colors; public String getName() { return name; } public void setName(String name) { this.name = name; } public double getLower() { return lower; } public void setLower(double lower) { this.lower = lower; } public double getUpper() { return upper; } public void setUpper(double upper) { this.upper = upper; } public double getC0() { return c0; } public void setC0(double c0) { this.c0 = c0; } public double getC1() { return c1; } public void setC1(double c1) { this.c1 = c1; } public String getFunction() { return function; } public void setFunction(String function) { this.function = function; } /** * Build Category from its internal unmarshalling attributs. * * @return Expected {@link Category} build from its unmarshalled attributs. * @throws IllegalArgumentException if internal transfer function is not known. * @param dataType */ public Category buildCategory(int dataType) { MathTransform1D sampleToGeophysics = null; //-- if function != null means categories own a quantitative unit. //-- For example it is not NOData category but exits more other reason. if (function != null) { final TransferFunction f = new TransferFunction(); if (FUNCTION_LINEAR.equals(function)) { f.setType(TransferFunctionType.LINEAR); } else if (FUNCTION_EXPONENTIAL.equals(function)) { f.setType(TransferFunctionType.EXPONENTIAL); } else { throw new IllegalArgumentException("Unsupported transform : "+function); } f.setScale(c1); f.setOffset(c0); sampleToGeophysics = f.getTransform(); } Class typeClass = null; switch (dataType) { case DataBuffer.TYPE_BYTE : //fall through case DataBuffer.TYPE_USHORT : //fall through case DataBuffer.TYPE_SHORT : //fall through case DataBuffer.TYPE_INT : typeClass = Integer.class ; break; case DataBuffer.TYPE_DOUBLE : typeClass= Double.class ; break; case DataBuffer.TYPE_FLOAT : typeClass = Float.class ; break; case DataBuffer.TYPE_UNDEFINED : //fall through default : throw new IllegalArgumentException("Undefined SampleDimension DataType."); } final NumberRange range = getTypedRangeNumber(typeClass, lower, true, upper, true); final Color[] cols = new Color[colors.length]; for(int i = 0; i < cols.length; i++) { cols[i] = ObjectConverters.convert(colors[i], Color.class); } final Category cat; if (Double.isNaN(lower) || lower == upper) { cat = new Category(name, cols[0], range); } else { cat = new Category(name, cols, range, sampleToGeophysics);//-- sampletogeophysic may be null. } return cat; } /** * Copy informations from given category. * @param category */ public void fill(Category category){ //we store the category in packed type category = category.geophysics(false); final Color[] cols = category.getColors(); colors = new String[cols.length]; for(int i=0;i<cols.length;i++){ colors[i] = toString(cols[i]); } name = category.getName().toString(); final NumberRange range = category.getRange(); lower = range.getMinDouble(true); upper = range.getMaxDouble(true); final MathTransform1D trs = category.getSampleToGeophysics(); if (trs != null) { //-- if category != NODATA. trs == null for nodata is expected comportement final TransferFunction f = new TransferFunction(); f.setTransform(trs); if (TransferFunctionType.LINEAR.equals(f.getType())) { function = FUNCTION_LINEAR; c0 = f.getOffset(); c1 = f.getScale(); } else if (TransferFunctionType.EXPONENTIAL.equals(f.getType())) { function = FUNCTION_EXPONENTIAL; c0 = f.getOffset(); c1 = f.getScale(); }else{ throw new IllegalArgumentException("Unsupported 1D transform : "+trs); } } } /** * Color to hexadecimal. * * @param color * @return color in hexadecimal form */ private static String toString(final Color color) { if (color == null) { return null; } String redCode = Integer.toHexString(color.getRed()); String greenCode = Integer.toHexString(color.getGreen()); String blueCode = Integer.toHexString(color.getBlue()); if (redCode.length() == 1) redCode = "0" + redCode; if (greenCode.length() == 1) greenCode = "0" + greenCode; if (blueCode.length() == 1) blueCode = "0" + blueCode; final String colorCode; int alpha = color.getAlpha(); if(alpha != 255){ String alphaCode = Integer.toHexString(alpha); if (alphaCode.length() == 1) alphaCode = "0" + alphaCode; colorCode = "#" + alphaCode + redCode + greenCode + blueCode; }else{ colorCode = "#" + redCode + greenCode + blueCode; } return colorCode.toUpperCase(); } /** * Returns an appropriate {@link NumberRange} from given parameters. * * @param <T> type of internal data. * @param type type of internal data. * @param min minimum range value. * @param isMinIncluded {@code true} if minimum value is considered as include into range interval else false (exclusive). * @param max maximum range value. * @param isMaxIncluded {@code true} if maximum value is considered as include into range interval else false (exclusive). * @return appropriate range value casted in expected type. */ private static <T extends Number & Comparable<T>> NumberRange<T> getTypedRangeNumber(final Class<T> type, final double min, final boolean isMinIncluded, final double max, final boolean isMaxIncluded) { return new NumberRange(type, Numbers.cast(min, type), isMinIncluded, Numbers.cast(max, type), isMaxIncluded); } }