Package org.geoserver.wcs.responses

Source Code of org.geoserver.wcs.responses.GeoTIFFCoverageResponseDelegate

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2001 - 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.wcs.responses;

import it.geosolutions.imageioimpl.plugins.tiff.TIFFLZWCompressor;

import java.awt.Dimension;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import javax.media.jai.JAI;

import org.geoserver.config.GeoServer;
import org.geoserver.platform.OWS20Exception;
import org.geoserver.wcs.WCSInfo;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridFormat;
import org.geotools.coverage.grid.io.imageio.GeoToolsWriteParams;
import org.geotools.gce.geotiff.GeoTiffFormat;
import org.geotools.gce.geotiff.GeoTiffWriteParams;
import org.geotools.gce.geotiff.GeoTiffWriter;
import org.geotools.util.Utilities;
import org.geotools.util.logging.Logging;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.vfny.geoserver.wcs.WcsException;
import org.vfny.geoserver.wcs.WcsException.WcsExceptionCode;

import com.sun.media.imageio.plugins.tiff.BaselineTIFFTagSet;

/**
* Coverage writer for the geotiff format.
*
* @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ (last modification)
* @author Simone Giannecchini, GeoSolutions SAS
*/
public class GeoTIFFCoverageResponseDelegate extends BaseCoverageResponseDelegate implements CoverageResponseDelegate {

    private final static Logger LOGGER= Logging.getLogger(GeoTIFFCoverageResponseDelegate.class.toString());
   
    /** DEFAULT_JPEG_COMPRESSION_QUALITY */
    private static final float DEFAULT_JPEG_COMPRESSION_QUALITY = 0.75f;

  private static final GeoTiffFormat GEOTIF_FORMAT = new GeoTiffFormat();
 
        public static final String GEOTIFF_CONTENT_TYPE = "image/tiff";


    @SuppressWarnings("serial")
    public GeoTIFFCoverageResponseDelegate(GeoServer geoserver) {
        super(
                geoserver,
                Arrays.asList("tif","tiff","geotiff","TIFF", "GEOTIFF", "GeoTIFF","image/geotiff"), //output formats
                new HashMap<String, String>(){ // file extensions
                    {
                        put("tiff", "tif");
                        put("tiff", "tif");
                        put("geotiff", "tif");
                        put("TIFF", "tif");
                        put("GEOTIFF", "tif");
                        put("GeoTIFF", "tif");
                        put("image/geotiff", "tif");   
                        put("image/tiff", "tif");
                    }
                },
                new HashMap<String, String>(){ //mime types
                    {
                        put("tiff", "image/tiff");
                        put("tif", "image/tiff");
                        put("geotiff", "image/tiff");
                        put("TIFF", "image/tiff");
                        put("GEOTIFF", "image/tiff");
                        put("GeoTIFF", "image/tiff");
                        put("image/geotiff", "image/tiff");                       
                    }
                });       
    }

    public void encode(GridCoverage2D sourceCoverage, String outputFormat, Map<String,String> econdingParameters, OutputStream output) throws IOException {
        Utilities.ensureNonNull("sourceCoverage", sourceCoverage);
        Utilities.ensureNonNull("econdingParameters", econdingParameters);

       
        // imposing encoding parameters
        final GeoTiffWriteParams wp = new GeoTiffWriteParams();
       
        // compression
        handleCompression(econdingParameters, wp);
       
        // tiling
        handleTiling(econdingParameters, wp, sourceCoverage);
       
        // interleaving
        handleInterleaving(econdingParameters, wp, sourceCoverage);

        final ParameterValueGroup writerParams = GEOTIF_FORMAT.getWriteParameters();
        writerParams.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(wp);
       
        if(geoserver.getService(WCSInfo.class).isLatLon()){
            writerParams.parameter(GeoTiffFormat.RETAIN_AXES_ORDER.getName().toString()).setValue(true);
        }

        // write down
        GeoTiffWriter writer = (GeoTiffWriter) GEOTIF_FORMAT.getWriter(output);
        try {
            if (writer != null)
                writer.write(sourceCoverage, (GeneralParameterValue[]) writerParams.values()
                        .toArray(new GeneralParameterValue[1]));
        } finally {
            try {
                if (writer != null)
                    writer.dispose();
            } catch (Throwable e) {
                // eating exception
            }
            sourceCoverage.dispose(false);
        }
    }

    /**
     * Handle interleaving encoding parameters for WCS.
     *
     * <p>
     * Notice that the Tiff ImageWriter supports only pixel interleaving.
     *
     * @param econdingParameters a {@link Map} of {@link String} keys with {@link String} values to hold the encoding parameters.
     * @param wp an instance of {@link GeoTiffWriteParams} to be massaged as per the provided encoding parameters.
     *
     * @throws WcsException in case there are invalid or unsupported options.
     */
    private void handleInterleaving(Map<String, String> encondingParameters, GeoTiffWriteParams wp, GridCoverage2D sourceCoverage) throws WcsException{

        // interleaving is optional
        if(encondingParameters.containsKey("interleave")){
           
            // ok, the interleaving has been specified, let's see what we got
            final String interleavingS= encondingParameters.get("interleave");
            if(interleavingS.equals("pixel")){
                // ok we want pixel interleaving, TIFF ImageWriter always writes
                // with pixel interleaving hence, we are good!
            } else if(interleavingS.equals("band")){
                // TODO implement this in TIFF Writer, as it is not supported right now
                throw new OWS20Exception("Banded Interleaving not supported", ows20Code(WcsExceptionCode.InterleavingNotSupported), interleavingS);
            } else {
                throw new OWS20Exception("Invalid Interleaving type provided", ows20Code(WcsExceptionCode.InterleavingInvalid), interleavingS);
            }
        }
       
    }

    /**
     * All OWS 2.0 exceptions for the geotiff extension come with a 404 error code
     * @param code
     * @return
     */
    private OWS20Exception.OWSExceptionCode ows20Code(WcsExceptionCode code) {
        return new OWS20Exception.OWSExceptionCode(code.toString(), 404);
    }

    /**
     * Handle tiling encoding parameters for WCS.
     *
     * <p>
     * Notice that tile width and height must be positive and multiple of 16.
     *
     *
     * @param econdingParameters a {@link Map} of {@link String} keys with {@link String} values to hold the encoding parameters.
     * @param wp an instance of {@link GeoTiffWriteParams} to be massaged as per the provided encoding parameters.
     * @param sourceCoverage the source {@link GridCoverage2D} to encode.
     *
     * @throws WcsException in case there are invalid or unsupported options.
     */
    private void handleTiling(Map<String, String> econdingParameters, final GeoTiffWriteParams wp, GridCoverage2D sourceCoverage)
            throws WcsException {

        // start with default dimension, since tileW and tileH are optional
        final RenderedImage sourceImage=sourceCoverage.getRenderedImage();
        final SampleModel sampleModel = sourceImage.getSampleModel();
        final int sourceTileW=sampleModel.getWidth();
        final int sourceTileH=sampleModel.getHeight();       
        final Dimension tileDimensions= new Dimension(sourceTileW,sourceTileH);
        LOGGER.fine("Source tiling:"+tileDimensions.width+"x"+tileDimensions.height);
        // if the tile size exceeds the image dimension, let's retile to save space on output image
        final GridEnvelope gr = sourceCoverage.getGridGeometry().getGridRange();
        if(gr.getSpan(0) < tileDimensions.width) {
            tileDimensions.width = gr.getSpan(0);
        }
        if(gr.getSpan(1) < tileDimensions.height) {
            tileDimensions.height = gr.getSpan(1);
        }
        LOGGER.fine("Source tiling reviewed to save space:"+tileDimensions.width+"x"+tileDimensions.height);       

        //
        // tiling
        //
        if(econdingParameters.containsKey("tiling")){
           
            final String tilingS= econdingParameters.get("tiling");
            if(tilingS!=null&&Boolean.valueOf(tilingS)){ 
               
               
                // tileW
                if(econdingParameters.containsKey("tilewidth")){
                    final String tileW_= econdingParameters.get("tilewidth");
                    if(tileW_!=null){ 
                        try{
                            final int tileW=Integer.valueOf(tileW_);
                            if(tileW>0&& (tileW%16==0)){
                                tileDimensions.width=tileW;
                            } else {
                                // tile width not supported
                                throw new OWS20Exception(
                                        "Provided tile width is invalid",
                                        ows20Code(WcsExceptionCode.TilingInvalid),
                                        Integer.toString(tileW));                           
                            }
                        }catch (Exception e) {
                            // tile width not supported
                            throw new OWS20Exception(
                                    "Provided tile width is invalid",
                                    ows20Code(WcsExceptionCode.TilingInvalid),
                                    tileW_);   
                        }

                    }
                   
                }
                // tileH   
                if(econdingParameters.containsKey("tileheight")){
                    final String tileH_= econdingParameters.get("tileheight");
                    if(tileH_!=null){ 
                        try{
                            final int tileH=Integer.valueOf(tileH_);
                            if(tileH>0&& (tileH%16==0)){
                                tileDimensions.height=tileH;
                            } else {
                                // tile height not supported
                                throw new OWS20Exception(
                                        "Provided tile height is invalid",
                                        ows20Code(WcsExceptionCode.TilingInvalid),
                                        Integer.toString(tileH));
                            }
                        }catch (Exception e) {
                            // tile height not supported
                            throw new OWS20Exception(
                                    "Provided tile height is invalid",
                                    ows20Code(WcsExceptionCode.TilingInvalid),
                                    tileH_);
                        }
                    }
                }
            }
        }

        // set tile dimensions
        if(tileDimensions.width!=sourceTileW||tileDimensions.height!=sourceTileH){
            LOGGER.fine("Final tiling:"+tileDimensions.width+"x"+tileDimensions.height);
            wp.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT);
            wp.setTiling(tileDimensions.width, tileDimensions.height);
        } else {
            LOGGER.fine("Mantaining original tiling");
        }
    }

    /**
     * Handle compression encoding parameters for WCS.
     *
     * <p>
     * Notice that not all the encoding params are supported by the underlying Tiff ImageWriter
     * <ol>
     * <li>Floating Point predictor is not supported  for LZW</li>
     * <li>Huffman is supported only for 1 bit images</li>
     * </ol>
     *
     * @param econdingParameters a {@link Map} of {@link String} keys with {@link String} values to hold the encoding parameters.
     * @param wp an instance of {@link GeoTiffWriteParams} to be massaged as per the provided encoding parameters.
     *
     * @throws WcsException in case there are invalid or unsupported options.
     */
    private void handleCompression(Map<String, String> econdingParameters,
            final GeoTiffWriteParams wp) throws WcsException {
        // compression
        if(econdingParameters.containsKey("compression")){
            String compressionS= econdingParameters.get("compression");
            if(compressionS!=null&&!compressionS.equalsIgnoreCase("none")){
                if(compressionS.equals("LZW")){
                    wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
                    wp.setCompressionType("LZW");
                   
                    // look for a predictor
                    String predictorS= econdingParameters.get("predictor");
                    if(predictorS!=null){
                        if(predictorS.equals("None")){
                           
                        } else if(predictorS.equals("Horizontal")){
                            wp.setTIFFCompressor(new TIFFLZWCompressor(BaselineTIFFTagSet.PREDICTOR_HORIZONTAL_DIFFERENCING));
                        } else if(predictorS.equals("Floatingpoint")){
                            // NOT SUPPORTED YET
                            throw new OWS20Exception(
                                    "Floating Point predictor is not supported",
                                    ows20Code(WcsExceptionCode.PredictorNotSupported),
                                    predictorS);
                        } else {
                            // invalid predictor
                            throw new OWS20Exception(
                                    "Invalid Predictor provided",
                                    ows20Code(WcsExceptionCode.PredictorInvalid),
                                    predictorS);
                        }
                    }
                } else if(compressionS.equals("JPEG")){
                    wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
                    wp.setCompressionType("JPEG");
                    // start with the default one, visually lossless
                    wp.setCompressionQuality(DEFAULT_JPEG_COMPRESSION_QUALITY);
                   
                    // check quality
                    if(econdingParameters.containsKey("jpeg_quality")){
                        final String quality_= econdingParameters.get("jpeg_quality");
                        if(quality_!=null){ 
                            try{
                                final int quality=Integer.valueOf(quality_);
                                if(quality>0&&quality<=100){
                                    wp.setCompressionQuality(quality/100.f);
                                } else {
                                    // invalid quality
                                    throw new OWS20Exception(
                                            "Provided quality value for the jpeg compression in invalid",
                                            ows20Code(WcsExceptionCode.JpegQualityInvalid),
                                            quality_);
                               
                            } catch (Exception e) {
                                // invalid quality
                                throw new OWS20Exception(
                                        "Provided quality value for the jpeg compression in invalid",
                                        ows20Code(WcsExceptionCode.JpegQualityInvalid),
                                        quality_);
                            }
                        } 
                    }
                } else if(compressionS.equals("PackBits")){
                    wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
                    wp.setCompressionType("PackBits");     
                } else if(compressionS.equals("DEFLATE")){
                    wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
                    wp.setCompressionType("Deflate");     
                } else if(compressionS.equals("Huffman")){
                    wp.setCompressionMode(GeoTiffWriteParams.MODE_EXPLICIT);
                    wp.setCompressionType("CCITT RLE")
                } else {
                    // compression not supported
                    throw new OWS20Exception("Provided compression does not seem supported", ows20Code(WcsExceptionCode.CompressionInvalid), compressionS);
                }
            }
        }
    }

   
    @Override
    public String getConformanceClass(String format) {
        return "http://www.opengis.net/spec/GMLCOV_geotiff-coverages/1.0/conf/geotiff-coverage";
    }
}
TOP

Related Classes of org.geoserver.wcs.responses.GeoTIFFCoverageResponseDelegate

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.