org.geoserver.wcs.responses.GeoTiffWriterHelper.java Source code

Java tutorial

Introduction

Here is the source code for org.geoserver.wcs.responses.GeoTiffWriterHelper.java

Source

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

import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;

import javax.media.jai.OpImage;
import javax.media.jai.RenderedOp;

import org.apache.commons.io.FileUtils;
import org.geoserver.platform.ServiceException;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
import org.geotools.coverage.grid.io.AbstractGridCoverageWriter;
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.GeoTiffReader;
import org.geotools.gce.geotiff.GeoTiffWriteParams;
import org.geotools.geometry.GeneralEnvelope;
import org.geotools.image.ImageWorker;
import org.geotools.resources.image.ImageUtilities;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.geometry.Envelope;
import org.opengis.parameter.GeneralParameterValue;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.crs.EngineeringCRS;

/**
 * Support class setting up reasonable defaults on the write parameters and centralizing the write code and associated optimizations
 *
 * @author Andrea Aime - GeoSolutions
 */
public class GeoTiffWriterHelper {

    private final static GeoTiffFormat TIFF_FORMAT = new GeoTiffFormat();

    private GridCoverage2D coverage;

    private File sourceFile;

    private GeoTiffWriteParams imageIoWriteParams;

    private ParameterValueGroup geotoolsWriteParams;

    public GeoTiffWriterHelper(GridCoverage2D coverage) throws IOException {
        this.coverage = coverage;

        // did we get lucky and all we need to do is to copy the original file file over?
        if (isUnprocessed(coverage)) {
            this.sourceFile = getSourceFile(coverage);
        }

        // setup default writing params, respect by default the original tiling structure
        // for optimal extraction performance
        this.imageIoWriteParams = buildWriteParams(coverage);
        this.geotoolsWriteParams = buildGeoToolsWriteParams(imageIoWriteParams);
    }

    /**
     * Returns the original source file, is present in the metadata, and if the coverage
     */
    private File getSourceFile(GridCoverage2D coverage) {
        final Object fileSource = coverage.getProperty(AbstractGridCoverage2DReader.FILE_SOURCE_PROPERTY);
        if (fileSource != null && fileSource instanceof String) {
            File file = new File((String) fileSource);
            if (file.exists()) {
                GeoTiffReader reader = null;
                try {
                    reader = new GeoTiffReader(file);
                    GeneralEnvelope originalEnvelope = reader.getOriginalEnvelope();
                    Envelope envelope = coverage.getEnvelope();
                    if (originalEnvelope.equals(envelope, 1e-9, false)) {
                        GridCoverage2D test = reader.read(null);
                        ImageUtilities.disposeImage(test.getRenderedImage());
                        return file;
                    }
                } catch (Exception e) {
                    // ok, not a geotiff!
                } finally {
                    if (reader != null) {
                        reader.dispose();
                    }
                }
            }
        }

        return null;
    }

    private GeoTiffWriteParams buildWriteParams(GridCoverage2D coverage) {
        final RenderedImage renderedImage = coverage.getRenderedImage();
        int tileWidth = renderedImage.getTileWidth();
        int tileHeight = renderedImage.getTileHeight();

        // avoid tiles bigger than the image
        final GridEnvelope gr = coverage.getGridGeometry().getGridRange();
        if (gr.getSpan(0) < tileWidth) {
            tileWidth = gr.getSpan(0);
        }
        if (gr.getSpan(1) < tileHeight) {
            tileHeight = gr.getSpan(1);
        }

        GeoTiffWriteParams writeParams = new GeoTiffWriteParams();
        writeParams.setTilingMode(GeoToolsWriteParams.MODE_EXPLICIT);
        writeParams.setTiling(tileWidth, tileHeight);
        return writeParams;
    }

    private ParameterValueGroup buildGeoToolsWriteParams(GeoTiffWriteParams writeParams) {
        final ParameterValueGroup wparams = TIFF_FORMAT.getWriteParameters();
        wparams.parameter(AbstractGridFormat.GEOTOOLS_WRITE_PARAMS.getName().toString()).setValue(writeParams);
        return wparams;

    }

    /**
     * Returns the write parameters, allowing their customization
     * 
     * @return
     */
    public GeoTiffWriteParams getImageIoWriteParams() {
        return imageIoWriteParams;
    }

    /**
     * Returns the GeoTools grid writer params, allowing their customization
     * @return
     */
    public ParameterValueGroup getGeotoolsWriteParams() {
        return geotoolsWriteParams;
    }

    /**
     * The code can figure out if it's really just copying over a GeoTiff source file and run a straight file copy, call this method if the source
     * copy should be turned off
     */
    public void disableSourceCopyOptimization() {
        this.sourceFile = null;
    }

    public void write(OutputStream stream) throws IOException {
        if (sourceFile != null) {
            FileUtils.copyFile(sourceFile, stream);
        } else {
            CoordinateReferenceSystem crs = coverage.getCoordinateReferenceSystem();
            boolean unreferenced = crs == null || crs instanceof EngineeringCRS;

            if (unreferenced) {
                RenderedImage ri = coverage.getRenderedImage();
                int tileWidth, tileHeight;
                if (imageIoWriteParams.getTilingMode() == GeoToolsWriteParams.MODE_EXPLICIT) {
                    tileWidth = imageIoWriteParams.getTileWidth();
                    tileHeight = imageIoWriteParams.getTileHeight();
                } else {
                    tileWidth = ri.getTileWidth();
                    tileHeight = ri.getTileHeight();
                }

                new ImageWorker(ri).writeTIFF(stream, null, 0.75f, tileWidth, tileHeight);
            } else {
                final GeneralParameterValue[] wps = (GeneralParameterValue[]) geotoolsWriteParams.values()
                        .toArray(new GeneralParameterValue[geotoolsWriteParams.values().size()]);

                // write out the coverage
                AbstractGridCoverageWriter writer = (AbstractGridCoverageWriter) TIFF_FORMAT.getWriter(stream);
                if (writer == null)
                    throw new ServiceException(
                            "Could not find the GeoTIFF writer, please check it's in the classpath");
                try {
                    writer.write(coverage, wps);
                } finally {
                    try {
                        writer.dispose();
                    } catch (Exception e) {
                        // swallow, silent close
                    }
                }
            }
        }
    }

    /**
     * Returns true if the coverage has not been processed in any way since it has been read
     * 
     * @param coverage
     *
     */
    private boolean isUnprocessed(GridCoverage2D coverage) {
        RenderedImage ri = coverage.getRenderedImage();
        if (ri instanceof RenderedOp) {
            RenderedOp op = (RenderedOp) ri;
            return op.getOperationName().startsWith("ImageRead");
        } else if (ri instanceof OpImage) {
            return ri.getClass().getSimpleName().startsWith("ImageRead");
        } else {
            return true;
        }
    }
}