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; 033 034import ptolemy.actor.TypedAtomicActor; 035import ptolemy.actor.TypedIOPort; 036import ptolemy.data.BooleanToken; 037import ptolemy.data.StringToken; 038import ptolemy.data.expr.Parameter; 039import ptolemy.data.expr.StringParameter; 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 * <p> 048 * <b>Name:</b> MergeGrids.java<br/> 049 * </p> 050 * <p> 051 * <b>Purpose:</b> The purpose of this actor is to 'merge' two ASC grids. The 052 * precise meaning of 'merge' will depend on the 'merge' operator. One example 053 * is the combination of 2 grids into a new grid whose extent is a rectangle 054 * that includes both input bounding box retangles, averageing values from both 055 * inputs. Simple math operations (add, subtract) are other examples.<br/> 056 * 057 * Order of the input grids may be significant( e.g for subtraction). Extent of 058 * the output will always include the combined extent of the inputs, but the 059 * cell size will match that of the first grid. 060 * </p> 061 * 062 * @author : Dan Higgins NCEAS UC Santa Barbara 063 * 064 */ 065 066public class MergeGrids extends TypedAtomicActor { 067 /** 068 * This parameter describes the type of merge to be executed. Choices 069 * include" AVERAGE, ADD, SUBTRACT, MASK, NOT_MASK<br/> 070 * MASK - grid2 missing values will mask correponding points in grid1<br/> 071 * NOT_MASK - grid2 NOT-missing values will mask correponding points in 072 * grid1 073 */ 074 public StringParameter mergeOperation; 075 int mergeOp = 0; 076 // input ports 077 /** 078 * The first grid file (*.asc format) to be merged 079 */ 080 public TypedIOPort grid1FileName = new TypedIOPort(this, "grid1FileName", 081 true, false); 082 /** 083 * The second grid file (*.asc format) to be merged 084 */ 085 public TypedIOPort grid2FileName = new TypedIOPort(this, "grid2FileName", 086 true, false); 087 /** 088 * The file name to be given to the result 089 */ 090 public TypedIOPort mergedGridFileName = new TypedIOPort(this, 091 "mergedGridFileName", true, false); 092 093 /** 094 * The resulting merged grid filename. 095 */ 096 public TypedIOPort mergedGridFileResult = new TypedIOPort(this, 097 "mergedGridFileResult", false, true); 098 099 /** 100 * Boolean setting to determine whether or not to use disk for storing grid 101 * data rather than putting all data in RAM arrays 102 */ 103 public Parameter useDisk; 104 105 private Grid grid1; 106 private Grid grid2; 107 private Grid mergedGrid; 108 109 private boolean useDiskValue = true; 110 111 private static final int NEAREST_NEIGHBOR = 0; 112 private static final int INVERSE_DISTANCE = 1; 113 114 // merge operations 115 private static final int AVERAGE = 0; 116 private static final int ADD = 1; 117 private static final int SUBTRACT = 2; 118 private static final int MASK = 3; // grid2 missing values will mask 119 // correponding points in grid1 120 private static final int NOT_MASK = 4; // grid2 NOT-missing values will mask 121 // correponding points in grid1 122 123 public MergeGrids(CompositeEntity container, String name) 124 throws NameDuplicationException, IllegalActionException { 125 super(container, name); 126 127 mergeOperation = new StringParameter(this, "mergeOperation"); 128 mergeOperation.setExpression("Average"); 129 mergeOperation.addChoice("Average"); 130 mergeOperation.addChoice("Add"); 131 mergeOperation.addChoice("Subtract"); 132 mergeOperation.addChoice("Mask"); 133 mergeOperation.addChoice("(NOT)Mask"); 134 135 grid1FileName.setTypeEquals(BaseType.STRING); 136 grid2FileName.setTypeEquals(BaseType.STRING); 137 mergedGridFileResult.setTypeEquals(BaseType.STRING); 138 mergedGridFileResult.setTypeEquals(BaseType.STRING); 139 140 useDisk = new Parameter(this, "useDisk"); 141 useDisk.setDisplayName("Use disk storage (for large grids)"); 142 useDisk.setTypeEquals(BaseType.BOOLEAN); 143 useDisk.setToken(BooleanToken.TRUE); 144 } 145 146 /** 147 * 148 */ 149 public void initialize() throws IllegalActionException { 150 151 } 152 153 /** 154 * 155 */ 156 public boolean prefire() throws IllegalActionException { 157 return super.prefire(); 158 } 159 160 /** 161 * 162 */ 163 public void fire() throws IllegalActionException { 164 super.fire(); 165 useDiskValue = ((BooleanToken) useDisk.getToken()).booleanValue(); 166 167 String temp = mergeOperation.stringValue(); 168 if (temp.equals("Average")) { 169 mergeOp = AVERAGE; 170 } else if (temp.equals("Add")) { 171 mergeOp = ADD; 172 } else if (temp.equals("Subtract")) { 173 mergeOp = SUBTRACT; 174 } else if (temp.equals("Mask")) { 175 mergeOp = MASK; 176 } else if (temp.equals("(NOT)Mask")) { 177 mergeOp = NOT_MASK; 178 } 179 180 StringToken grid1FileToken = (StringToken) grid1FileName.get(0); 181 String grid1FileNameStr = grid1FileToken.stringValue(); 182 StringToken grid2FileToken = (StringToken) grid2FileName.get(0); 183 String grid2FileNameStr = grid2FileToken.stringValue(); 184 185 File grid1File = new File(grid1FileNameStr); 186 File grid2File = new File(grid2FileNameStr); 187 grid1 = new Grid(grid1File, !useDiskValue); 188 grid2 = new Grid(grid2File, !useDiskValue); 189 190 double minx = grid1.xllcorner; 191 if (grid2.xllcorner < minx) 192 minx = grid2.xllcorner; 193 double miny = grid1.yllcorner; 194 if (grid2.yllcorner < miny) 195 miny = grid2.yllcorner; 196 double maxx = grid1.xllcorner + grid1.ncols * grid1.delx; 197 if ((grid2.xllcorner + grid2.ncols * grid2.delx) > maxx) 198 maxx = grid2.xllcorner + grid2.ncols * grid2.delx; 199 double maxy = grid1.yllcorner + grid1.nrows * grid1.dely; 200 if ((grid2.yllcorner + grid2.nrows * grid2.dely) > maxy) 201 maxy = grid2.yllcorner + grid2.nrows * grid2.dely; 202 ; 203 double new_cs = grid1.delx; // remember, delx and dely are equal! 204 int new_ncols = (int) ((maxx - minx) / new_cs); 205 int new_nrows = (int) ((maxy - miny) / new_cs); 206 207 mergedGrid = new Grid(new_ncols, new_nrows, new_cs, new_cs, minx, miny); 208 // new merged grid has now been created but no storage or values alloted 209 merge(NEAREST_NEIGHBOR, mergeOp); 210 211 StringToken outputFileToken = (StringToken) mergedGridFileName.get(0); 212 String outFileStr = outputFileToken.stringValue(); 213 mergedGrid.createAsc(outFileStr); 214 mergedGridFileResult.broadcast(new StringToken(outFileStr)); 215 } 216 217 /** 218 * Post fire the actor. Return false to indicate that the process has 219 * finished. If it returns true, the process will continue indefinitely. 220 * 221 * */ 222 public boolean postfire() throws IllegalActionException { 223 grid1.delete(); // remove potentially large data storage associated with 224 // grid1 225 grid2.delete(); // remove potentially large data storage associated with 226 // grid2 227 mergedGrid.delete(); // remove potentially large data storage associated 228 // with mergedGrid 229 return super.postfire(); 230 } 231 232 private void merge(int scalingAlgorithm, int mergeOperation) { 233 int nr = mergedGrid.nrows; 234 int nc = mergedGrid.ncols; 235 double ymin = mergedGrid.yllcorner; 236 double xmin = mergedGrid.xllcorner; 237 double dx = mergedGrid.delx; 238 double dy = mergedGrid.dely; 239 if (!useDiskValue) { 240 double[][] newDataArray = new double[nc][nr]; 241 mergedGrid.dataArray = newDataArray; 242 for (int j = 0; j < nr; j++) { 243 double yloc = ymin + nr * dy - j * dy; 244 for (int i = 0; i < nc; i++) { 245 double xloc = xmin + i * dx; 246 double val1 = grid1.interpValue(xloc, yloc, 247 scalingAlgorithm); 248 double val2 = grid2.interpValue(xloc, yloc, 249 scalingAlgorithm); 250 double val = getMergedValue(val1, val2, mergeOperation); 251 newDataArray[i][j] = val; 252 } 253 } 254 } else { // using PersistentVector for data storage 255 mergedGrid.pv = new PersistentVector(); 256 mergedGrid.pv.setFirstRow(6); 257 mergedGrid.pv.setFieldDelimiter("#x20"); 258 String[] rowvals = new String[nc]; 259 for (int j = 0; j < nr; j++) { 260 double yloc = ymin + nr * dy - j * dy; 261 for (int i = 0; i < nc; i++) { 262 double xloc = xmin + i * dx; 263 double val1 = grid1.interpValue(xloc, yloc, 264 scalingAlgorithm); 265 double val2 = grid2.interpValue(xloc, yloc, 266 scalingAlgorithm); 267 double val = getMergedValue(val1, val2, mergeOperation); 268 269 String valStr; 270 if (val > 1.0E100) { 271 valStr = Grid.NODATA_value_String; 272 } else { 273 valStr = (new Double(val)).toString(); 274 } 275 276 rowvals[i] = valStr; 277 } 278 mergedGrid.pv.addElement(rowvals); 279 rowvals = new String[nc]; // needed to make sure new object 280 // added to pv 281 } 282 } 283 } 284 285 private double getMergedValue(double val1, double val2, int mergeOperation) { 286 287 if (mergeOperation == ADD) { 288 // if either grid point is a missing value, return a missing value 289 if ((val1 > 1.0e100) || (val2 > 1.0e100)) { 290 return 1.0e101; 291 } else { 292 return (val1 + val2); 293 } 294 } else if (mergeOperation == SUBTRACT) { 295 // if either grid point is a missing value, return a missing value 296 if ((val1 > 1.0e100) || (val2 > 1.0e100)) { 297 return 1.0e101; 298 } else { 299 return (val1 - val2); 300 } 301 } else if (mergeOperation == MASK) { 302 if (val2 > 1.0e100) { 303 return 1.0e101; 304 } else { 305 return val1; 306 } 307 } else if (mergeOperation == NOT_MASK) { 308 if (val2 < 1.0e100) { 309 return 1.0e101; 310 } else { 311 return val1; 312 } 313 } else { // 'AVERAGE' operation 314 if ((val1 > 1.0e100) && (val2 > 1.0e100)) { 315 return 1.0e101; 316 } else if (val1 > 1.0e100) { 317 return val2; 318 } else if (val2 > 1.0e100) { 319 return val1; 320 } else { 321 return ((val1 + val2) / 2.0); 322 } 323 } 324 } 325}