001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
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 */
029
030package org.ecoinformatics.seek.gis.java_gis;
031
032import java.io.File;
033import java.util.Vector;
034
035import ptolemy.actor.TypedAtomicActor;
036import ptolemy.actor.TypedIOPort;
037import ptolemy.data.ArrayToken;
038import ptolemy.data.StringToken;
039import ptolemy.data.type.ArrayType;
040import ptolemy.data.type.BaseType;
041import ptolemy.kernel.CompositeEntity;
042import ptolemy.kernel.util.IllegalActionException;
043import ptolemy.kernel.util.NameDuplicationException;
044import util.PersistentVector;
045
046/**
047 * <b>Name:</b> AddGrids.java<br/>
048 * <b>Purpose:</b> The purpose of this actor is to 'merge' multiple ASC grids.
049 * This differs from MergeGrid in that more than 2 grids can be merged.
050 * <p>
051 * Extent and cell size will match that of the first grid. The primary purpose
052 * is to combine stochastic grids to give a spatial distribution where more
053 * probable cells have larger values. Thus, cell values are added for all pixels
054 * in the input grid file list.
055 * </p>
056 * 
057 * @author : Dan Higgins NCEAS UC Santa Barbara
058 * 
059 */
060
061public class AddGrids extends TypedAtomicActor {
062        int mergeOp = 1; // ADD
063        // input ports
064        /**
065         * A string array of filenames of grid files to be added
066         */
067        public TypedIOPort gridFilenameArrayPort = new TypedIOPort(this,
068                        "gridFilenameArrayPort", true, false);
069
070        /**
071         * The name to be given to the resulting output file
072         */
073        public TypedIOPort mergedGridFileName = new TypedIOPort(this,
074                        "mergedGridFileName", true, false);
075
076        /**
077         * The file name of the resulting grid (acts as a trigger when addition is
078         * complete)
079         */
080        public TypedIOPort mergedGridFileResult = new TypedIOPort(this,
081                        "mergedGridFileResult", false, true);
082
083        private Grid grid1;
084        private Grid grid2;
085        private Grid mergedGrid;
086
087        private static final int NEAREST_NEIGHBOR = 0;
088        private static final int INVERSE_DISTANCE = 1;
089
090        private Vector gridsVector;
091
092        public AddGrids(CompositeEntity container, String name)
093                        throws NameDuplicationException, IllegalActionException {
094                super(container, name);
095
096                gridFilenameArrayPort.setTypeEquals(new ArrayType(BaseType.STRING));
097                mergedGridFileName.setTypeEquals(BaseType.STRING);
098                mergedGridFileResult.setTypeEquals(BaseType.STRING);
099
100                gridsVector = new Vector();
101        }
102
103        /**
104   *
105   */
106        public void initialize() throws IllegalActionException {
107
108        }
109
110        /**
111   *
112   */
113        public boolean prefire() throws IllegalActionException {
114                return super.prefire();
115        }
116
117        /**
118   *
119   */
120        public void fire() throws IllegalActionException {
121                super.fire();
122
123                gridsVector = new Vector();
124                if (gridFilenameArrayPort.getWidth() > 0) {
125                        ArrayToken token = (ArrayToken) gridFilenameArrayPort.get(0);
126                        // now iterate over all the filenames in the array
127                        for (int i = 0; i < token.length(); i++) {
128                                StringToken s_token = (StringToken) token.getElement(i);
129                                String ascfilename = s_token.stringValue();
130                                gridsVector.addElement(ascfilename);
131                        }
132                }
133                System.out.println("Array size: " + gridsVector.size());
134
135                // assume at least 2 grids in the Vector
136                File grid1File = new File((String) gridsVector.elementAt(0));
137                File grid2File = new File((String) gridsVector.elementAt(1));
138                grid1 = new Grid(grid1File);
139                grid2 = new Grid(grid2File);
140
141                double minx = grid1.xllcorner;
142                double miny = grid1.yllcorner;
143                double maxx = grid1.xllcorner + grid1.ncols * grid1.delx;
144                double maxy = grid1.yllcorner + grid1.nrows * grid1.dely;
145                double new_cs = grid1.delx; // remember, delx and dely are equal!
146                int new_ncols = (int) ((maxx - minx) / new_cs);
147                int new_nrows = (int) ((maxy - miny) / new_cs);
148
149                mergedGrid = new Grid(new_ncols, new_nrows, new_cs, new_cs, minx, miny);
150                // new merged grid has now been created but no storage or values alloted
151                merge(NEAREST_NEIGHBOR, mergeOp);
152                // we have now added the first two grids
153                grid1.delete(); // get rid of unneeded data
154                grid2.delete();
155                for (int k = 2; k < gridsVector.size(); k++) {
156                        grid1 = mergedGrid;
157                        mergedGrid = new Grid(new_ncols, new_nrows, new_cs, new_cs, minx,
158                                        miny);
159                        grid2File = new File((String) gridsVector.elementAt(k));
160                        grid2 = new Grid(grid2File);
161                        merge(NEAREST_NEIGHBOR, mergeOp);
162                        grid1.delete(); // get rid of unneeded data
163                        grid2.delete();
164                }
165
166                StringToken outputFileToken = (StringToken) mergedGridFileName.get(0);
167                String outFileStr = outputFileToken.stringValue();
168                mergedGrid.createAsc(outFileStr);
169                mergedGridFileResult.broadcast(new StringToken(outFileStr));
170        }
171
172        /**
173         * Post fire the actor. Return false to indicate that the process has
174         * finished. If it returns true, the process will continue indefinitely.
175         * 
176         *       */
177        public boolean postfire() throws IllegalActionException {
178                grid1.delete(); // remove potentially large data storage associated with
179                                                // grid1
180                grid2.delete(); // remove potentially large data storage associated with
181                                                // grid2
182                mergedGrid.delete(); // remove potentially large data storage associated
183                                                                // with mergedGrid
184                return super.postfire();
185        }
186
187        private void merge(int scalingAlgorithm, int mergeOperation) {
188                int nr = mergedGrid.nrows;
189                int nc = mergedGrid.ncols;
190                double ymin = mergedGrid.yllcorner;
191                double xmin = mergedGrid.xllcorner;
192                double dx = mergedGrid.delx;
193                double dy = mergedGrid.dely;
194                if (mergedGrid.inMemFlag && (mergedGrid.dataArray != null)) {
195                        double[][] newDataArray = new double[nc][nr];
196                        mergedGrid.dataArray = newDataArray;
197                        for (int j = 0; j < nr; j++) {
198                                double yloc = ymin + nr * dy - j * dy;
199                                for (int i = 0; i < nc; i++) {
200                                        double xloc = xmin + i * dx;
201                                        double val1 = grid1.interpValue(xloc, yloc,
202                                                        scalingAlgorithm);
203                                        double val2 = grid2.interpValue(xloc, yloc,
204                                                        scalingAlgorithm);
205                                        double val = getMergedValue(val1, val2, mergeOperation);
206                                        newDataArray[i][j] = val;
207                                }
208                        }
209                } else { // using PersistentVector for data storage
210                        mergedGrid.pv = new PersistentVector();
211                        mergedGrid.pv.setFirstRow(6);
212                        mergedGrid.pv.setFieldDelimiter("#x20");
213                        String[] rowvals = new String[nc];
214                        for (int j = 0; j < nr; j++) {
215                                double yloc = ymin + nr * dy - j * dy;
216                                for (int i = 0; i < nc; i++) {
217                                        double xloc = xmin + i * dx;
218                                        double val1 = grid1.interpValue(xloc, yloc,
219                                                        scalingAlgorithm);
220                                        double val2 = grid2.interpValue(xloc, yloc,
221                                                        scalingAlgorithm);
222                                        double val = getMergedValue(val1, val2, mergeOperation);
223
224                                        String valStr;
225                                        if (val > 1.0E100) {
226                                                valStr = Grid.NODATA_value_String;
227                                        } else {
228                                                valStr = (new Double(val)).toString();
229                                        }
230
231                                        rowvals[i] = valStr;
232                                }
233                                mergedGrid.pv.addElement(rowvals);
234                                rowvals = new String[nc]; // needed to make sure new object
235                                                                                        // added to pv
236                        }
237                }
238        }
239
240        private double getMergedValue(double val1, double val2, int mergeOperation) {
241
242                // if either grid point is a missing value, return a missing value
243                if ((val1 > 1.0e100) || (val2 > 1.0e100)) {
244                        return 1.0e101;
245                } else {
246                        return (val1 + val2);
247                }
248        }
249}