001/*
002 * Copyright (c) 2016 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-03-22 16:28:13 -0700 (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 org.kepler.gis.data.GISToken;
032import org.kepler.gis.data.RasterToken;
033
034import ptolemy.actor.lib.Transformer;
035import ptolemy.kernel.CompositeEntity;
036import ptolemy.kernel.util.IllegalActionException;
037import ptolemy.kernel.util.NameDuplicationException;
038
039import java.io.BufferedReader;
040import java.io.File;
041import java.io.IOException;
042import java.io.InputStream;
043import java.io.InputStreamReader;
044
045import org.geotools.geometry.jts.ReferencedEnvelope;
046import org.kepler.gis.data.BoundingBoxToken;
047
048import ptolemy.actor.TypedIOPort;
049import ptolemy.actor.parameters.PortParameter;
050import ptolemy.data.StringToken;
051import ptolemy.data.Token;
052import ptolemy.data.type.BaseType;
053import ptolemy.kernel.util.Attribute;
054
055/** Crop a GIS data set.
056 * 
057 *  @author Daniel Crawl
058 *  @version $Id$
059 */
060public class Crop extends Transformer {
061
062    public Crop(CompositeEntity container, String name) throws NameDuplicationException, IllegalActionException {
063        super(container, name);
064        
065        input.setTypeAtMost(GISToken.GIS);
066
067        output.setTypeSameAs(input);
068        
069        bbox = new TypedIOPort(this, "bbox", true, false);
070        bbox.setTypeEquals(BoundingBoxToken.BOUNDING_BOX);
071        new Attribute(bbox, "_showName");
072        
073        outputName = new PortParameter(this, "outputName");
074        outputName.setTypeEquals(BaseType.STRING);
075        outputName.getPort().setTypeEquals(BaseType.STRING);
076        outputName.setStringMode(true);
077        new Attribute(outputName.getPort(), "_showName");
078        
079    }
080    
081    @Override
082    public void fire() throws IllegalActionException {
083        
084        super.fire();
085
086        ReferencedEnvelope envelope = ((BoundingBoxToken)bbox.get(0)).boundingBoxValue();
087        
088        outputName.update();
089        Token token = outputName.getToken();
090        String outputNameStr = null;
091        if(token != null) {
092            outputNameStr = ((StringToken)token).stringValue();
093        }
094        
095        if(outputNameStr == null || outputNameStr.trim().isEmpty()) {
096            throw new IllegalActionException("Must specify output name.");
097        }
098
099        token = input.get(0);
100        
101        if(token instanceof RasterToken) {
102            File rasterFile = ((RasterToken)token).rasterFile();
103            if(rasterFile != null) {
104                ProcessBuilder builder = new ProcessBuilder("gdal_translate",
105                    "-of",
106                    "AAIGrid",
107                    "-a_nodata",
108                    "-9999",
109                    "-projwin",
110                    String.format("%.8f", envelope.getMinX()),
111                    String.format("%.8f", envelope.getMaxY()),
112                    String.format("%.8f", envelope.getMaxX()),
113                    String.format("%.8f", envelope.getMinY()),
114                    rasterFile.getAbsolutePath(),
115                    outputNameStr);
116                
117                builder.redirectErrorStream(true);
118                try {
119                    Process process = builder.start();
120                    try {
121                        if(_debugging) {
122                            _debug("gdal_translate " +
123                                    "-of " +
124                                    "AAIGrid " +
125                                    "-a_nodata" +
126                                    "-9999" +
127                                    "-projwin " +
128                                    String.format("%.8f", envelope.getMinX()) + " " +
129                                    String.format("%.8f", envelope.getMaxY()) + " " +
130                                    String.format("%.8f", envelope.getMaxX()) + " " +
131                                    String.format("%.8f", envelope.getMinY()) + " " +
132                                    rasterFile.getAbsolutePath() + " " +
133                                    outputNameStr);
134                            try(InputStream stdout = process.getInputStream();
135                                BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));) {
136                                String line;
137                                while((line = reader.readLine()) != null) {
138                                    _debug(line);
139                                }
140                            }
141                        }
142                    } finally {
143                        if(process.waitFor() != 0) {
144                            throw new IllegalActionException(this, "gdal_translate did not return 0.");
145                        } else {
146                            output.broadcast(new RasterToken(new File(outputNameStr)));
147                        }
148                    }
149                } catch(IOException | InterruptedException e) {
150                    throw new IllegalActionException(this, "Error running gdal_translate: " +
151                        e.getMessage());
152                }
153            } else {
154                throw new IllegalActionException(this, "Unsupported type of GIS data set.");
155            }
156        } else {
157            throw new IllegalActionException(this, "Unsupported type of GIS data set.");
158        }        
159    }
160
161    /** Crop to this bounding box. */
162    public TypedIOPort bbox;
163    
164    /** Name of output file. */
165    public PortParameter outputName;
166    
167}