All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.integratedmodelling.engine.geospace.coverage.raster.WCSCoverage Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (C) 2007, 2015:
 * 
 * - Ferdinando Villa  - integratedmodelling.org - any
 * other authors listed in @author annotations
 *
 * All rights reserved. This file is part of the k.LAB software suite, meant to enable
 * modular, collaborative, integrated development of interoperable data and model
 * components. For details, see http://integratedmodelling.org.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms
 * of the Affero General Public License Version 3 or any later version.
 *
 * This program 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 Affero General Public License for more details.
 * 
 * You should have received a copy of the Affero General Public License along with this
 * program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
 * 330, Boston, MA 02111-1307, USA. The license is also available at:
 * https://www.gnu.org/licenses/agpl.html
 *******************************************************************************/
package org.integratedmodelling.engine.geospace.coverage.raster;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Map;
import java.util.Properties;

import javax.media.jai.iterator.RandomIterFactory;

import org.apache.jcs.access.exception.CacheException;
import org.geotools.coverage.grid.GeneralGridEnvelope;
import org.geotools.coverage.grid.GridGeometry2D;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.integratedmodelling.api.configuration.IConfiguration;
import org.integratedmodelling.api.modelling.IObserver;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.monitoring.Messages;
import org.integratedmodelling.api.network.API;
import org.integratedmodelling.common.configuration.KLAB;
import org.integratedmodelling.common.utils.FileUtils;
import org.integratedmodelling.engine.geospace.Geospace;
import org.integratedmodelling.engine.geospace.coverage.ICoverage;
import org.integratedmodelling.engine.geospace.extents.Area;
import org.integratedmodelling.engine.geospace.extents.Grid;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabIOException;
import org.integratedmodelling.exceptions.KlabInternalErrorException;
import org.integratedmodelling.exceptions.KlabValidationException;

public class WCSCoverage extends AbstractRasterCoverage {

    public static final String WCS_SERVICE_PROPERTY = "wcs.service.url";
    public static final String WCS_FORMAT_PROPERTY  = "wcs.service.format";

    String                     wcsService           = "http://127.0.0.1:8080/geoserver/wcs";
    String                     wcsFormat            = "geotiff";

    String                     description          = null;
    // read from kvp in coverage keywords
    Properties                 properties           = new Properties();
    IMonitor                   monitor;
    String                     authentication;

    static class CachedDescriptor implements Serializable {

        private static final long serialVersionUID = 1453574401114688492L;

        double                    x1, x2, y1, y2;
        int                       sx1, sx2, sy1, sy2;
        String                    srs;
    }

    private boolean readFromCache() throws KlabException {

        String key = wcsService + "#" + layerName;

        CachedDescriptor cd = (CachedDescriptor) Geospace.get().getWCSCache().get(key);
        if (cd != null) {

            try {
                this.crs = Geospace.getCRSFromID(cd.srs);
            } catch (Exception e) {
                KLAB.error(layerName + ": " + e.getMessage());
            }

            this.xCellSize = (cd.x2 - cd.x1) / (cd.sx2 - cd.sx1);
            this.yCellSize = (cd.y2 - cd.y1) / (cd.sy2 - cd.sy1);
            this.boundingBox = new ReferencedEnvelope(cd.x1, cd.x2, cd.y1, cd.y2, crs);

            this.gridGeometry = new GridGeometry2D(new GeneralGridEnvelope(new int[] {
                    cd.sx1,
                    cd.sy1 }, new int[] { cd.sx2, cd.sy2 }, false), boundingBox);

            return true;
        }
        return false;
    }

    /**
     * This constructor reads the WCS coverage descriptor and initializes all fields from
     * it. Data are not loaded until loadData(), so the coverage is null.
     * 
     * @param coverageID
     * 
     * @param properties
     *            should contain the URL of the WCS service; if null, geoserver on
     *            localhost:8080 is used (not elegant, but OK for now).
     * @param monitor
     * @throws KlabException
     */
    public WCSCoverage(String coverageID, Properties properties, IMonitor monitor, String authentication)
            throws KlabException {

        this.authentication = authentication;

        if (properties != null) {
            wcsService = properties.getProperty(WCS_SERVICE_PROPERTY, "http://127.0.0.1:8080/geoserver/wcs");
            wcsFormat = properties.getProperty(WCS_FORMAT_PROPERTY, wcsFormat);
            if (properties.containsKey(NODATA_PROPERTY)) {
                String[] zz = properties.getProperty(NODATA_PROPERTY).split(",");
                this.noData = new double[zz.length];
                for (int i = 0; i < zz.length; i++)
                    this.noData[i] = Double.parseDouble(zz[i]);
            }
        }

        layerName = coverageID;

        if (!readFromCache()) {

            WCS wcs = new WCS(wcsService);
            if (!wcs.responds()) {
                monitor.warn("connection to WCS failed: " + coverageID);
                throw new KlabIOException("connection to WCS host failed for layer " + coverageID
                        + "; service URL is " + wcsService);
            }

            try {
                CachedDescriptor cd = parseDescriptor(wcs);
                saveToCache(cd);
            } catch (KlabException e) {

                /*
                 * pass any exceptions through the monitor before throwing it.
                 */
                if (monitor != null) {
                    monitor.error(e);
                }
                throw e;
            }
        }
    }

    private void saveToCache(CachedDescriptor cd) throws KlabException {

        String key = wcsService + "#" + layerName;
        try {
            Geospace.get().getWCSCache().put(key, cd);
        } catch (CacheException e) {
            throw new KlabIOException(e);
        }
    }

    /**
     * This constructor creates the coverage by reading the WCS coverage passed from the
     * associated WCS service, reading data only for the specified extent.
     * 
     * @param coverage
     * @param extent
     */
    public WCSCoverage(WCSCoverage coverage, Area extent) {
        layerName = coverage.layerName;
        authentication = coverage.authentication;
        monitor = coverage.monitor;
    }

    private CachedDescriptor parseDescriptor(WCS wcs) throws KlabException {

        Map map = wcs.describeCoverage(this.layerName, authentication, monitor);

        try {
            this.crs = Geospace.getCRSFromID(map.get(WCS.CRS).toString());
        } catch (Exception e) {
            // KLAB.error(layerName + ": " + e.getMessage());
            throw new KlabInternalErrorException(e);
        }

        double x1 = (double) map.get(WCS.MINX);
        double x2 = (double) map.get(WCS.MAXX);
        double y1 = (double) map.get(WCS.MINY);
        double y2 = (double) map.get(WCS.MAXY);
        int xcells = (int) map.get(WCS.XCELLS);
        int ycells = (int) map.get(WCS.YCELLS);
        this.xCellSize = (x2 - x1) / xcells;
        this.yCellSize = (y2 - y1) / ycells;

        this.boundingBox = new ReferencedEnvelope(x1, x2, y1, y2, crs);

        this.gridGeometry = new GridGeometry2D(new GeneralGridEnvelope(new int[] { 0, 0 }, new int[] {
                xcells,
                ycells }, false), boundingBox);

        /*
         * create and return descriptor for cache
         */
        CachedDescriptor ret = new CachedDescriptor();

        ret.x1 = x1;
        ret.x2 = x2;
        ret.y1 = y1;
        ret.y2 = y2;
        ret.sx1 = 0;
        ret.sx2 = xcells;
        ret.sy1 = 0;
        ret.sy2 = ycells;
        ret.srs = map.get(WCS.CRS).toString();

        return ret;
    }

    private URL buildRetrieveUrl(Grid extent) throws KlabException {

        URL url = null;
        String rcrs = Geospace.getCRSIdentifier(extent.getCRS(), false);

        int xc = extent.getXCells();
        int yc = extent.getYCells();

        if (extent.getXCells() == 0 && extent.getYCells() == 0) {

            xc = (int) Math.ceil((extent.getEast() - extent.getWest()) / this.xCellSize);
            yc = (int) Math.ceil((extent.getNorth() - extent.getSouth()) / this.yCellSize);

            // System.out.println("computed raster size is " + xc + " x " + yc);
            extent.setResolution(xc, yc);
        }

        String s = wcsService + "?service=WCS&version=1.0.0&request=GetCoverage&coverage=" + layerName
                + "&bbox=" + extent.getWest() + "," + extent.getSouth() + "," + extent.getEast() + ","
                + extent.getNorth() + "&crs=" + rcrs + "&responseCRS=" + rcrs + "&width=" + xc + "&height="
                + yc + "&format=" + wcsFormat;

        try {
            url = new URL(s);
        } catch (MalformedURLException e) {
            throw new KlabInternalErrorException(e);
        }

        KLAB.info("retrieving " + url);

        return url;
    }

    // @Override
    public void loadData() throws KlabException {

        checkCoverage();

        /*
         * get rid of old image if we had one
         */
        if (image != null) {
            image = null;
        }

        image = coverage.getRenderedImage();
        itera = RandomIterFactory.create(image, null);
        _loaded = true;
    }

    private void checkCoverage() throws KlabException {

        if (coverage == null) {
            /*
             * TODO we should read it as is, but typically we don't care for downloading a
             * gigabyte from the net, so the case with coverage == null is normally an
             * error - no specification of a subrange has been asked. For now let's just
             * throw an error.
             */
            throw new KlabValidationException("WCS coverage " + layerName
                    + " being read in its entirety without subsetting: "
                    + "this is most likely an error causing extremely long download times.");
        }
    }

    @Override
    public ICoverage requireMatch(Area arealExtent, IObserver observer, IMonitor monitor, boolean allowClassChange)
            throws KlabException {

        if (!(arealExtent instanceof Grid))
            throw new KlabValidationException("coverage can only be reprojected on a grid extent for now");

        URL getCov = buildRetrieveUrl((Grid) arealExtent);
        String savUrl = getCov.toString();

        ClassLoader clsl = null;

        try {
            if (monitor != null) {
                monitor.info("requesting " + layerName + " as WCS", Messages.INFOCLASS_MODEL);
            }

            // clsl = KLABEngine.get().swapClassloader();
            URLConnection connection = getCov.openConnection();
            /*
             * set configured timeout
             */
            if (KLAB.CONFIG.getProperties().containsKey(IConfiguration.KLAB_CONNECTION_TIMEOUT)) {
                int timeout = 1000 * Integer.parseInt(KLAB.CONFIG.getProperties()
                        .getProperty(IConfiguration.KLAB_CONNECTION_TIMEOUT, "10"));
                connection.setConnectTimeout(timeout);
                connection.setReadTimeout(timeout);
            }

            File f = File.createTempFile("geo", ".tiff");
            FileUtils.copyInputStreamToFile(connection.getInputStream(), f);
            getCov = f.toURI().toURL();
            GeoTiffReader reader = new GeoTiffReader(getCov, Geospace.get().getGeotoolsHints());
            this.coverage = reader.read(null);

            /*
             * if the bounding box was read from a wcs coverage, keep it in the
             * originalBoundingBox field so that we can check coverage inside thinklab
             */
            if (this.originalBoundingBox == null) {
                this.originalBoundingBox = this.boundingBox;
            }

            /*
             * we obviously want it in the same CRS as the new one.
             */
            if (this.originalBoundingBox != null) {
                try {
                    this.originalBoundingBox = this.originalBoundingBox.transform(arealExtent.getCRS(), true);
                } catch (Exception e) {
                    throw new KlabValidationException(e);
                }
            }

        } catch (IOException e) {
            throw new KlabIOException(layerName + ": " + e.getMessage() + ": url = " + savUrl);
        } finally {
            // KLABEngine.get().resetClassLoader(clsl);
        }

        setExtent((Grid) arealExtent);

        /*
         * recreate iterator - fix
         */
        loadData();

        return this;
    }

    private void setExtent(Grid e) {

        this.xCellSize = e.getEWResolution();
        this.yCellSize = e.getNSResolution();

        this.boundingBox = e.getEnvelope();

        this.gridGeometry = new GridGeometry2D(new GeneralGridEnvelope(new int[] {
                e.getXMinCell(),
                e.getYMinCell() }, new int[] { e.getXMaxCell(), e.getYMaxCell() }, false), boundingBox);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy