001/*
002 * Copyright (c) 2015 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-03-22 23:28:13 +0000 (Tue, 22 Mar 2016) $' 
007 * '$Revision: 34456 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029package org.kepler.gis.actor;
030
031import java.io.File;
032
033import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
034import org.geotools.geometry.jts.ReferencedEnvelope;
035import org.kepler.gis.data.BoundingBoxToken;
036import org.kepler.gis.data.GISToken;
037import org.kepler.gis.data.RasterToken;
038import org.kepler.gis.data.VectorToken;
039import org.kepler.gis.util.RasterUtilities;
040import org.kepler.gis.util.RasterUtilities.RasterInfo;
041import org.opengis.referencing.FactoryException;
042import org.opengis.referencing.crs.CoordinateReferenceSystem;
043import org.opengis.referencing.operation.TransformException;
044
045import ptolemy.actor.TypedIOPort;
046import ptolemy.data.Token;
047import ptolemy.kernel.CompositeEntity;
048import ptolemy.kernel.util.IllegalActionException;
049import ptolemy.kernel.util.NameDuplicationException;
050
051/** Get the bounding box for a GIS data set.
052 *  <p>
053 *  If the coordinate reference system is specified in <i>crs</i>
054 *  and is different than the crs in the input data set, then the
055 *  bounding box will be reprojected.
056 * 
057 *  @author Daniel Crawl
058 *  @version $Id: GetBoundingBox.java 34456 2016-03-22 23:28:13Z crawl $ 
059 */
060public class GetBoundingBox extends CRSActor {
061
062    /** Create a new GetBoundingBox with a given container and name. */
063    public GetBoundingBox(CompositeEntity container, String name)
064            throws NameDuplicationException, IllegalActionException {
065        super(container, name);
066        
067        input = new TypedIOPort(this, "input", true, false);
068        input.setTypeAtMost(GISToken.GIS);
069        
070        output = new TypedIOPort(this, "output", false, true);
071        output.setTypeEquals(BoundingBoxToken.BOUNDING_BOX);
072    }
073    
074    /** Output the bounding box for the data. */
075    @Override
076    public void fire() throws IllegalActionException {
077        super.fire();
078        Token token = input.get(0);
079        ReferencedEnvelope envelope = getBoundingBox(token, _crs);
080        output.broadcast(new BoundingBoxToken(envelope));
081    }
082    
083    /** Get the bounding box for a GIS dataset in a token. */
084    public static ReferencedEnvelope getBoundingBox(Token token) throws IllegalActionException {
085        return getBoundingBox(token, null);
086    }
087
088    /** Get the bounding box for a GIS dataset in a token with a specific crs. */
089    public static ReferencedEnvelope getBoundingBox(Token token, CoordinateReferenceSystem crs) throws IllegalActionException {
090
091        ReferencedEnvelope envelope = null;
092        
093        if(token instanceof VectorToken) {
094            envelope = ((VectorToken)token).getVectors().getBounds();
095        } else if(token instanceof RasterToken) {
096            AbstractGridCoverage2DReader reader = ((RasterToken)token).reader();
097            if(reader != null) {
098                envelope = ReferencedEnvelope.create(reader.getOriginalEnvelope(),
099                    reader.getCoordinateReferenceSystem());
100            } else {
101                File file = ((RasterToken)token).rasterFile();
102                RasterInfo rasterInfo = RasterUtilities.getInfo(file);
103                if(rasterInfo != null) {
104                    envelope = rasterInfo.getEnvelope();
105                }
106            }
107        } else if(token instanceof BoundingBoxToken) {
108            envelope = ((BoundingBoxToken)token).boundingBoxValue();
109        } else {
110            throw new IllegalActionException("Unknown GIS input type: " + token.getClass());
111        }
112        
113        if(envelope == null) {
114            throw new IllegalActionException("Unable to get bounding box for input.");
115        }
116        
117        ReferencedEnvelope newEnvelope;
118        if(crs == null) {
119            newEnvelope = envelope;
120        } else {
121            try {
122                newEnvelope = envelope.transform(crs, true);
123            } catch (TransformException | FactoryException e) {
124                throw new IllegalActionException("Error reprojecting bounding box: " + e.getMessage());
125            }
126        }
127        
128        return newEnvelope;        
129    }
130    
131    /** The input GIS data set. */
132    public TypedIOPort input;
133    
134    /** The bounding box of the data set. */
135    public TypedIOPort output;
136
137}