001/*
002 * Copyright (c) 2018 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2018-07-24 17:31:17 +0000 (Tue, 24 Jul 2018) $' 
007 * '$Revision: 34707 $'
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.calc;
030
031import java.io.IOException;
032import java.util.ArrayList;
033
034import org.geotools.coverage.grid.GridCoverage2D;
035import org.geotools.coverage.grid.io.AbstractGridCoverage2DReader;
036import org.kepler.gis.data.RasterToken;
037
038import it.geosolutions.jaiext.range.Range.DataType;
039import ptolemy.actor.TypedAtomicActor;
040import ptolemy.actor.TypedIOPort;
041import ptolemy.actor.parameters.PortParameter;
042import ptolemy.data.StringToken;
043import ptolemy.data.expr.StringParameter;
044import ptolemy.data.type.BaseType;
045import ptolemy.kernel.CompositeEntity;
046import ptolemy.kernel.util.Attribute;
047import ptolemy.kernel.util.IllegalActionException;
048import ptolemy.kernel.util.NameDuplicationException;
049import ptolemy.kernel.util.SingletonAttribute;
050
051/** Raster calculator using Jiffle. Add an input port for each input
052 * raster, and refer to the raster in the jiffle script using the
053 * corresponding name of the port. The name of the output raster
054 * in the script is "dest".
055 * 
056 * Jiffle language reference:
057 * 
058 * https://github.com/geosolutions-it/jai-ext/wiki/Jiffle---language-summary
059 * 
060 * @author Daniel Crawl
061 * @version $Id: JiffleRasterCalculator.java 34707 2018-07-24 17:31:17Z crawl $
062 */
063public class JiffleRasterCalculator extends TypedAtomicActor {
064
065    public JiffleRasterCalculator(CompositeEntity container, String name)
066            throws IllegalActionException, NameDuplicationException {
067        super(container, name);
068        
069        script = new PortParameter(this, "script");
070        script.setStringMode(true);
071        script.setTypeEquals(BaseType.STRING);
072        script.getPort().setTypeEquals(BaseType.STRING);
073        new SingletonAttribute(script.getPort(), "_showName");
074        
075        outType = new StringParameter(this, "outType");
076        outType.addChoice("byte");
077        outType.addChoice("double");
078        outType.addChoice("float");
079        outType.addChoice("integer");
080        outType.addChoice("long");
081        outType.addChoice("short");
082        outType.addChoice("unsigned short");
083        outType.setToken("double");
084        
085        output = new TypedIOPort(this, "output", false, true);
086        output.setTypeEquals(RasterToken.RASTER);
087    }
088    
089    @Override
090    public void attributeChanged(Attribute attribute) throws IllegalActionException {
091        if(attribute == outType) {
092            String val = outType.stringValue();
093            if(val == null || val.isEmpty()) {
094                throw new IllegalActionException(this, "Must specify output type.");
095            }
096            DataType type = DataType.valueOf(val.toUpperCase());
097            if(type == null) {
098                throw new IllegalActionException(this, "Invalid output type.");
099            }
100            _outputType = type;
101        } else {
102            super.attributeChanged(attribute);
103        }
104    }
105    
106    @Override
107    public void fire() throws IllegalActionException {
108        
109        super.fire();
110        
111        script.update();
112        String scriptStr = ((StringToken)script.getToken()).stringValue();
113        if(scriptStr == null || scriptStr.trim().isEmpty()) {
114            throw new IllegalActionException(this, "Must specify jiffle script.");
115        }
116        
117        JiffleProcess process = new JiffleProcess();
118        
119        ArrayList<String> names = new ArrayList<String>();
120        ArrayList<GridCoverage2D> coverages = new ArrayList<GridCoverage2D>();
121        
122        for(TypedIOPort input: this.inputPortList()) {
123            if(input.numberOfSources() > 0 && input != script.getPort()) {
124                names.add(input.getName());
125                RasterToken token = (RasterToken) input.get(0);
126                GridCoverage2D coverage = (GridCoverage2D) token.coverageValue();
127                if(coverage == null) {
128                    AbstractGridCoverage2DReader reader = token.reader();
129                    if(reader == null) {
130                        throw new IllegalActionException(this, "File rasters not supported. Use RasterReader first.");
131                    }
132                    try {
133                        coverage = reader.read(null);
134                    } catch (IllegalArgumentException | IOException e) {
135                        throw new IllegalActionException(this, e,
136                                "Error getting reading coverage for raster.");
137                    }
138                }
139                coverages.add(coverage);
140            }
141        }
142        
143        GridCoverage2D outputCoverage = null;
144        try {
145            outputCoverage = process.execute(
146                coverages.toArray(new GridCoverage2D[coverages.size()]),
147                scriptStr,
148                null,
149                names.toArray(new String[names.size()]),
150                _outputType,
151                null);
152        } catch (Exception e) {
153            throw new IllegalActionException(this, e,
154                    "Error running jiffle process.");
155        }
156        
157        output.broadcast(new RasterToken(outputCoverage));
158        
159    }
160    
161    @Override
162    public void preinitialize() throws IllegalActionException {
163        
164        super.preinitialize();
165        
166        for(TypedIOPort input: this.inputPortList()) {
167            if(input != script.getPort()) {
168                input.setTypeEquals(RasterToken.RASTER);
169            }
170        }
171        
172    }
173
174    /** The jiffle script. */
175    public PortParameter script;
176    
177    /** Raster output data type. */
178    public StringParameter outType;
179    
180    /** Raster output. */
181    public TypedIOPort output;
182
183    private DataType _outputType;
184    
185}