/** * Retrieves velocities as a vector [u,v,w] based on given positions * * @param time - time coordinate in milliseconds * @param z - depth coordinate * @param lon - longitude (decimal degrees) * @param lat - latitude (decimal degrees) */ @Override public synchronized double[] getVelocities(long time, double z, double lon, double lat) { if (Double.isNaN(lon) || Double.isNaN(lat)) { throw new IllegalArgumentException("Latitude or Longitude value is NaN"); } // Completely outside the time bounds if (time < bounds[0][0] || time > bounds[0][1]) { this.notifyAll(); return null; } // Completely outside the vertical bounds if (z < bounds[1][0] || z > bounds[1][1]) { this.notifyAll(); return null; } // Completely outside the north-south bounds - return null as opposed // to NODATA if (lat < bounds[2][0] || lat > bounds[2][1]) { this.notifyAll(); return null; } // Completely outside the east-west bounds if (lon < bounds[3][0] || lon > bounds[3][1]) { this.notifyAll(); return null; } checkTime(time); try { int js, is, ks, ts; // Searching for the cell indices nearest to the given location is = yloc.lookup(lat); js = xloc.lookup(lon); ks = zloc.lookup(z); ts = tloc.lookup(TimeConvert.millisToHYCOM(time)); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // ATTENTION!!!! THE ORDER IS VERY IMPORTANT HERE!!!! Latitude (i/y) // and then Longitude (j/x). That's the way it is set up in the // NetCDF File. The best way would be to have automatic order // detection // somehow.... // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // Handling data edges int i_lhs = halfKernel; int i_rhs = halfKernel; int j_lhs = halfKernel; int j_rhs = halfKernel; int k_lhs = zHalfKernel; int k_rhs = zHalfKernel; if (is < halfKernel) { i_lhs = is; } if (is + halfKernel >= yloc.arraySize()) { i_rhs = yloc.arraySize() - is - 1; } if (js < halfKernel) { j_lhs = js; } if (js + halfKernel >= xloc.arraySize()) { j_rhs = xloc.arraySize() - js - 1; } if (ks < zHalfKernel) { k_lhs = ks; } if (ks + zHalfKernel >= zloc.arraySize()) { k_rhs = zloc.arraySize() - ks - 1; } int istart = is - i_lhs; int jstart = js - j_lhs; int kstart = ks - k_lhs; // LHS + RHS + middle int idim = i_lhs + i_rhs + 1; int jdim = j_lhs + j_rhs + 1; int kdim = k_lhs + k_rhs + 1; // Splines cannot be used with only 2 points. If we are using a // kernel size of 3 and it is reduced due to edge effects, then // slide the window. if (kdim == 2) { if (kstart != 0) { kstart -= (k_rhs + 1); } kdim = zKernelSize; } int blocksize = idim * jdim * kdim; // int semiblock = (idim * (jdim + 1) * kdim) / 3; int[] origin = new int[] {ts, kstart, istart, jstart}; int[] shape = new int[] {1, kdim, idim, jdim}; try { uArr = uVar.read(origin, shape); uArr = uArr.reduce(); } catch (InvalidRangeException e) { // Should not occur. Checking done above. e.printStackTrace(); } try { vArr = vVar.read(origin, shape); vArr = vArr.reduce(); } catch (InvalidRangeException e) { // Should not occur. Checking done above. e.printStackTrace(); } if (zloc.isIn_Bounds() >= 0) { try { wArr = wVar.read(origin, shape); wArr = wArr.reduce(); } catch (InvalidRangeException e) { // Should not occur. Checking done above. e.printStackTrace(); } } float[][][] autmp = (float[][][]) uArr.copyToNDJavaArray(); float[][][] avtmp = (float[][][]) vArr.copyToNDJavaArray(); float[][][] awtmp = (float[][][]) wArr.copyToNDJavaArray(); // Mitigate NODATA values int uct = 0; double usum = 0; double uavg = 0; double ussq = 0; double uvar = 0; int vct = 0; double vsum = 0; double vavg = 0; double vssq = 0; double vvar = 0; int wct = 0; double wsum = 0; double wavg = 0; double wssq = 0; double wvar = 0; for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (autmp[k][i][j] < cutoff && !Double.isNaN(autmp[k][i][j])) { uct++; usum += autmp[k][i][j]; uavg = usum / uct; ussq += autmp[k][i][j] * autmp[k][i][j]; uvar = ussq / uct - uavg * uavg; // } else { // if ((k > 0) && Double.isNaN(autmp[k - 1][i][j])) // { // autmp[k][i][j] = 0; // } } } } } for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (avtmp[k][i][j] < cutoff && !Double.isNaN(avtmp[k][i][j])) { vct++; vsum += avtmp[k][i][j]; vavg = vsum / vct; vssq += avtmp[k][i][j] * avtmp[k][i][j]; vvar = vssq / vct - vavg * vavg; // } else { // if ((k > 0) // && Double.isNaN(avtmp[k - 1][i][j])) { // avtmp[k][i][j] = 0; // } } } } } if (zloc.isIn_Bounds() >= 0) { for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (awtmp[k][i][j] < cutoff && !Double.isNaN(awtmp[k][i][j])) { wct++; wsum += awtmp[k][i][j]; wavg = wsum / wct; wssq += awtmp[k][i][j] * awtmp[k][i][j]; wvar = wssq / wct - wavg * wavg; // } else { // if ((k > 0) // && Double.isNaN(awtmp[k - 1][i][j])) { // awtmp[k][i][j] = 0; // } } } } } } nearNoData = false; // If there are null values... if (uct < blocksize) { nearNoData = true; // If there are more than (io-1)^2, return null /* * if (uct < semiblock) { velocities = NODATA; averages = * NODATA; variances = NODATA; return NODATA; } */ // Otherwise mitigate by replacing using the average value. for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (autmp[k][i][j] > cutoff || Double.isNaN(autmp[k][i][j])) { if (ks == 0) { autmp[k][i][j] = 0; } autmp[k][i][j] = (float) uavg; } } } } } if (vct < blocksize) { nearNoData = true; // If there are more than halfKernel/2, return null /* * if (vct < semiblock) { this.notifyAll(); velocities = NODATA; * averages = NODATA; variances = NODATA; return NODATA; } */ // Otherwise mitigate by replacing using the average value. for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (avtmp[k][i][j] > cutoff || Double.isNaN(avtmp[k][i][j])) { avtmp[k][i][j] = (float) vavg; } } } } } if (zloc.isIn_Bounds() >= 0) { if (wct < blocksize) { nearNoData = true; // If there are more than (halfKernel-1)^2, return null /* * if (wct < semiblock) { this.notifyAll(); velocities = * NODATA; averages = NODATA; variances = NODATA; return * NODATA; } */ // Otherwise mitigate by replacing using the average value. for (int i = 0; i < idim; i++) { for (int j = 0; j < jdim; j++) { for (int k = 0; k < kdim; k++) { if (awtmp[k][i][j] > cutoff || Double.isNaN(awtmp[k][i][j])) { awtmp[k][i][j] = (float) wavg; } } } } } } /* * Convert the Arrays into Java arrays - because latitude and z * should be consistent among files, we subset from a constant * array. */ double[] latja = Arrays.copyOfRange(yloc.getJavaArray(), istart, istart + idim); double[] lonja = Arrays.copyOfRange(xloc.getJavaArray(), jstart, jstart + jdim); double[] zja = Arrays.copyOfRange(zloc.getJavaArray(), kstart, kstart + kdim); // Obtain the interpolated values TricubicSplineInterpolator tci = new TricubicSplineInterpolator(); TricubicSplineInterpolatingFunction tsf = tci.interpolate(zja, latja, lonja, autmp); // u = tcs.interpolate(z, lat, lon); u = tsf.value(z, lat, lon); // tcs.setValues(avtmp); // v = tcs.interpolate(z, lat, lon); tsf = tci.interpolate(zja, latja, lonja, avtmp); v = tsf.value(z, lat, lon); if (zloc.isIn_Bounds() >= 0) { // tcs.setValues(awtmp); // w = tcs.interpolate(z, lat, lon); tsf = tci.interpolate(zja, latja, lonja, awtmp); w = tsf.value(z, lat, lon); } else { w = 0; } // If there is something strange with the values, return NODATA. if (Math.abs(u) > cutoff) { u = 0.0f; v = 0.0f; w = 0.0f; this.notifyAll(); velocities = NODATA; averages = NODATA; variances = NODATA; return NODATA; } else if (Math.abs(v) > cutoff) { u = 0.0f; v = 0.0f; w = 0.0f; this.notifyAll(); velocities = NODATA; averages = NODATA; variances = NODATA; return NODATA; } else if (Math.abs(w) > cutoff) { u = 0.0f; v = 0.0f; w = 0.0f; this.notifyAll(); velocities = NODATA; averages = NODATA; variances = NODATA; return NODATA; } if (Double.isNaN(u) || Double.isNaN(v) || Double.isNaN(w)) { u = 0.0f; v = 0.0f; w = 0.0f; this.notifyAll(); velocities = NODATA; averages = NODATA; variances = NODATA; return NODATA; } // Otherwise return the interpolated values. this.notifyAll(); velocities = new double[] {u, v, w}; averages = new double[] {uavg, vavg, wavg}; // Correct running population variance to be sample variance. double ucorr = (double) uct / (double) (uct - 1); double vcorr = (double) vct / (double) (vct - 1); double wcorr = (double) wct / (double) (wct - 1); variances = new double[] {uvar * ucorr, vvar * vcorr, wvar * wcorr}; return velocities; // If for some reason there was an error reading from the file, // return null. } catch (IOException e) { System.out.println("WARNING: Error reading from velocity files.\n\n"); e.printStackTrace(); } this.notifyAll(); return null; }
public void initialize(String dir) throws IOException { // Set Time Zone to UTC formatUTC.setTimeZone(TimeZone.getTimeZone("UTC")); this.dir = dir; File f = new File(dir); // Ensure the path is a directory if (!f.isDirectory()) { throw new IllegalArgumentException(f.getName() + " is not a directory."); } // Filter the list of files File[] fa = f.listFiles(new FilenamePatternFilter(".*_[uvw].*\\.nc")); if (fa == null) { throw new IOException("File list is empty."); } for (File fil : fa) { String name = fil.getName(); NetcdfFile ncf = NetcdfFile.open(fil.getPath()); tVar = ncf.findVariable(tName); if (tVar == null) { System.out.println( "WARNING: Time variable " + tVar + " was not found in " + fil.getPath() + " when initializing the VelocityReader. This file will be skipped."); continue; } Array arr = tVar.read(); // Convert into a java array double[] ja = (double[]) arr.copyTo1DJavaArray(); // Determine the minimum and maximum times long[] minmax = new long[2]; minmax[0] = TimeConvert.HYCOMToMillis((long) ja[0]); minmax[1] = TimeConvert.HYCOMToMillis((long) ja[ja.length - 1]); // Put into an index linking start time with the associated file if (name.lastIndexOf("_u") > 0) { if (uFiles.containsKey(minmax[0])) { System.out.print( "WARNING: Velocity files have duplicate time keys. " + name + "/" + uFiles.get(minmax[0]) + " at " + new Date(minmax[0])); System.out.println(" Skipping latter file."); } else { uFiles.put(minmax[0], ncf); } } if (name.lastIndexOf("_v") > 0) { if (vFiles.containsKey(minmax[0])) { System.out.print( "WARNING: Velocity files have duplicate time keys. " + name + "/" + vFiles.get(minmax[0]) + " at " + new Date(minmax[0])); System.out.println(" Skipping latter file."); } else { vFiles.put(minmax[0], ncf); } } if (name.lastIndexOf("_w") > 0) { if (wFiles.containsKey(minmax[0])) { System.out.print( "WARNING: Velocity files have duplicate time keys. " + name + "/" + wFiles.get(minmax[0]) + " at " + new Date(minmax[0])); System.out.println(" Skipping latter file."); } else { wFiles.put(minmax[0], ncf); } } } // If there are no files in one of the index collections, then exit. if (uFiles.size() == 0 || vFiles.size() == 0 || wFiles.size() == 0) { System.out.println( "Velocity directory is empty, or files/variables are not named properly." + "Files must be named as *_u*, *_v*, and *_w*."); System.exit(0); } uKeys = new ArrayList<Long>(uFiles.keySet()); vKeys = new ArrayList<Long>(vFiles.keySet()); wKeys = new ArrayList<Long>(wFiles.keySet()); // Populate uFile, vFile and wFile with the first entry so that they // are not null uFile = uFiles.get(uKeys.get(0)); vFile = vFiles.get(vKeys.get(0)); wFile = wFiles.get(wKeys.get(0)); uVar = uFile.findVariable(uName); vVar = vFile.findVariable(vName); wVar = wFile.findVariable(wName); // Latitude and depth are read here because they should not change // and therefore can be input once only. latVar = uFile.findVariable(latName); lonVar = uFile.findVariable(lonName); zVar = uFile.findVariable(zName); setXLookup(lonName); setYLookup(latName); setZLookup(zName); if (positiveDown) { zloc.setNegate(true); } bounds[0][0] = uKeys.get(0); NetcdfFile lastfile = uFiles.get(uKeys.get(uKeys.size() - 1)); Variable t = lastfile.findVariable(tName); double last; try { last = t.read(new int[] {t.getShape(0) - 1}, new int[] {1}).getDouble(0); bounds[0][1] = TimeConvert.HYCOMToMillis((long) last); } catch (InvalidRangeException e) { e.printStackTrace(); } }