001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2018 005 * Space Science and Engineering Center (SSEC) 006 * University of Wisconsin - Madison 007 * 1225 W. Dayton Street, Madison, WI 53706, USA 008 * https://www.ssec.wisc.edu/mcidas 009 * 010 * All Rights Reserved 011 * 012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 013 * some McIDAS-V source code is based on IDV and VisAD source code. 014 * 015 * McIDAS-V is free software; you can redistribute it and/or modify 016 * it under the terms of the GNU Lesser Public License as published by 017 * the Free Software Foundation; either version 3 of the License, or 018 * (at your option) any later version. 019 * 020 * McIDAS-V is distributed in the hope that it will be useful, 021 * but WITHOUT ANY WARRANTY; without even the implied warranty of 022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 023 * GNU Lesser Public License for more details. 024 * 025 * You should have received a copy of the GNU Lesser Public License 026 * along with this program. If not, see http://www.gnu.org/licenses. 027 */ 028 029package edu.wisc.ssec.mcidasv.data.hydra; 030 031import java.rmi.RemoteException; 032import visad.Set; 033import visad.Gridded1DSet; 034import visad.CoordinateSystem; 035import visad.RealType; 036import visad.RealTupleType; 037import visad.Linear1DSet; 038import visad.Linear2DSet; 039import visad.Gridded2DSet; 040import visad.SampledSet; 041import visad.Unit; 042import visad.FunctionType; 043import visad.VisADException; 044import visad.QuickSort; 045import visad.FlatField; 046import visad.FieldImpl; 047import java.util.HashMap; 048import java.util.Map; 049import visad.GriddedSet; 050 051 052public abstract class ProfileAlongTrack extends MultiDimensionAdapter { 053 054 private FunctionType mathtype; 055 056 int TrackLen; 057 int VertLen; 058 059 private float[] vertLocs = null; 060 private float[] trackTimes = null; 061 private float[] trackLongitude = null; 062 private float[] trackLatitude = null; 063 064 public static String longitude_name = "Longitude"; 065 public static String latitude_name = "Latitude"; 066 public static String trackDim_name = "TrackDim"; 067 public static String vertDim_name = "VertDim"; 068 public static String array_name = "array_name"; 069 public static String profileTime_name = "ProfileTime"; 070 public static String profileTime_unit = "ProfileTime_unit"; 071 public static String altitude_unit = "altitude_unit"; 072 public static String sfcElev_name = "SurfaceElev"; 073 public static String range_name = "range_name"; 074 public static String scale_name = "scale_name"; 075 public static String offset_name = "offset_name"; 076 public static String fill_value_name = "fill_value_name"; 077 public static String valid_range = "valid_range"; 078 public static String ancillary_file_name = "ancillary_file"; 079 static String product_name = "product_name"; 080 081 String[] rangeName_s = null; 082 Class[] arrayType_s = null; 083 Unit[] rangeUnit_s = new Unit[] {null}; 084 085 RealType track = RealType.getRealType(trackDim_name); 086 RealType vert = RealType.getRealType(vertDim_name); 087 RealType[] domainRealTypes = new RealType[2]; 088 089 RealType vertLocType; 090 RealType trackTimeType; 091 092 int track_idx = -1; 093 int vert_idx = -1; 094 int range_rank = -1; 095 096 int track_tup_idx; 097 int vert_tup_idx; 098 099 boolean isVertDimAlt = true; 100 101 CoordinateSystem cs = null; 102 103 int medianFilterTrackWidth = 10; 104 int medianFilterVertWidth = 10; 105 106 public static Map<String, double[]> getEmptySubset() { 107 Map<String, double[]> subset = new HashMap<>(); 108 subset.put(trackDim_name, new double[3]); 109 subset.put(vertDim_name, new double[3]); 110 return subset; 111 } 112 113 public static Map<String, Object> getEmptyMetadataTable() { 114 Map<String, Object> metadata = new HashMap<>(); 115 metadata.put(array_name, null); 116 metadata.put(trackDim_name, null); 117 metadata.put(vertDim_name, null); 118 metadata.put(longitude_name, null); 119 metadata.put(latitude_name, null); 120 metadata.put(profileTime_name, null); 121 metadata.put(profileTime_unit, null); 122 metadata.put(altitude_unit, null); 123 metadata.put(sfcElev_name, null); 124 metadata.put(scale_name, null); 125 metadata.put(offset_name, null); 126 metadata.put(fill_value_name, null); 127 /* 128 metadata.put(range_name, null); 129 metadata.put(range_unit, null); 130 metadata.put(valid_range, null); 131 */ 132 return metadata; 133 } 134 135 public ProfileAlongTrack() { 136 } 137 138 public ProfileAlongTrack(MultiDimensionReader reader, Map<String, Object> metadata) { 139 this(reader, metadata, true); 140 } 141 142 public ProfileAlongTrack(MultiDimensionReader reader, Map<String, Object> metadata, boolean isVertDimAlt) { 143 super(reader, metadata); 144 this.isVertDimAlt = isVertDimAlt; 145 this.init(); 146 } 147 148 149 private void init() { 150 for (int k=0; k<array_rank;k++) { 151 if ( ((String)metadata.get(trackDim_name)).equals(array_dim_names[k]) ) { 152 track_idx = k; 153 } 154 if ( ((String)metadata.get(vertDim_name)).equals(array_dim_names[k]) ) { 155 vert_idx = k; 156 } 157 } 158 159 int[] lengths = new int[2]; 160 161 if (track_idx < vert_idx) { 162 domainRealTypes[0] = vert; 163 domainRealTypes[1] = track; 164 track_tup_idx = 1; 165 vert_tup_idx = 0; 166 lengths[0] = array_dim_lengths[vert_idx]; 167 lengths[1] = array_dim_lengths[track_idx]; 168 } 169 else { 170 domainRealTypes[0] = track; 171 domainRealTypes[1] = vert; 172 track_tup_idx = 0; 173 vert_tup_idx = 1; 174 lengths[0] = array_dim_lengths[track_idx]; 175 lengths[1] = array_dim_lengths[vert_idx]; 176 } 177 178 TrackLen = array_dim_lengths[track_idx]; 179 VertLen = array_dim_lengths[vert_idx]; 180 181 String rangeName = null; 182 if (metadata.get("range_name") != null) { 183 rangeName = (String)metadata.get("range_name"); 184 } 185 else { 186 rangeName = (String)metadata.get("array_name"); 187 } 188 rangeType = RealType.getRealType(rangeName, rangeUnit_s[0]); 189 190 try { 191 rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata); 192 } 193 catch (Exception e) { 194 System.out.println("RangeProcessor failed to create"); 195 e.printStackTrace(); 196 } 197 198 try { 199 if (isVertDimAlt) { 200 vertLocs = getVertBinAltitude(); 201 } 202 vertLocType = makeVertLocType(); 203 trackTimes = getTrackTimes(); 204 trackTimeType = makeTrackTimeType(); 205 trackLongitude = getTrackLongitude(); 206 trackLatitude = getTrackLatitude(); 207 } 208 catch (Exception e) { 209 System.out.println(e); 210 } 211 212 } 213 214 public int getTrackLength() { 215 return TrackLen; 216 } 217 218 public int getVertLength() { 219 return VertLen; 220 } 221 222 public int getVertIdx() { 223 return vert_idx; 224 } 225 226 public int getTrackIdx() { 227 return track_idx; 228 } 229 230 public int getVertTupIdx() { 231 return vert_tup_idx; 232 } 233 234 public int getTrackTupIdx() { 235 return track_tup_idx; 236 } 237 238 public int getMedianFilterWindowWidth() { 239 return medianFilterTrackWidth; 240 } 241 242 public int getMedianFilterWindowHeight() { 243 return medianFilterVertWidth; 244 } 245 246 public Set makeDomain(Map<String, double[]> subset) throws Exception { 247 double[] first = new double[2]; 248 double[] last = new double[2]; 249 int[] length = new int[2]; 250 251 Map<String, double[]> domainSubset = new HashMap<>(); 252 domainSubset.put(trackDim_name, subset.get(trackDim_name)); 253 domainSubset.put(vertDim_name, subset.get(vertDim_name)); 254 255 for (int kk=0; kk<2; kk++) { 256 RealType rtype = domainRealTypes[kk]; 257 double[] coords = subset.get(rtype.getName()); 258 first[kk] = coords[0]; 259 last[kk] = coords[1]; 260 length[kk] = (int) ((last[kk] - first[kk])/coords[2] + 1); 261 last[kk] = first[kk]+coords[2]*(length[kk]-1); 262 } 263 Linear2DSet domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]); 264 final Linear1DSet[] lin1DSet_s = new Linear1DSet[] {domainSet.getLinear1DComponent(0), 265 domainSet.getLinear1DComponent(1)}; 266 267 float[] new_altitudes = new float[length[vert_tup_idx]]; 268 float[] new_times = new float[length[track_tup_idx]]; 269 270 int track_start = (int) first[track_tup_idx]; 271 int vert_start = (int) first[vert_tup_idx]; 272 int vert_skip = (int) (subset.get(vertDim_name))[2]; 273 int track_skip = (int) (subset.get(trackDim_name))[2]; 274 for (int k=0; k<new_altitudes.length; k++) { 275 new_altitudes[k] = vertLocs[vert_start+(k*vert_skip)]; 276 } 277 278 for (int k=0; k<new_times.length; k++) { 279 new_times[k] = trackTimes[track_start+(k*track_skip)]; 280 } 281 282 final Gridded1DSet alt_set = new Gridded1DSet(vertLocType, new float[][] {new_altitudes}, new_altitudes.length); 283 final Gridded1DSet time_set = new Gridded1DSet(trackTimeType, new float[][] {new_times}, new_times.length); 284 final float vert_offset = (float) first[vert_tup_idx]; 285 final float track_offset = (float) first[track_tup_idx]; 286 287 RealTupleType reference = new RealTupleType(vertLocType, trackTimeType); 288 289 CoordinateSystem cs = null; 290 291 try { 292 cs = new CoordinateSystem(reference, null) { 293 public float[][] toReference(float[][] vals) throws VisADException { 294 int[] indexes = lin1DSet_s[0].valueToIndex(new float[][] {vals[0]}); 295 /* ? 296 for (int k=0; k<vals[0].length;k++) { 297 indexes[k] = (int) (vals[vert_tup_idx][k] - vert_offset); 298 } 299 */ 300 float[][] alts = alt_set.indexToValue(indexes); 301 302 indexes = lin1DSet_s[1].valueToIndex(new float[][] {vals[1]}); 303 /* ? 304 for (int k=0; k<vals[0].length;k++) { 305 indexes[k] = (int) (vals[track_tup_idx][k] - track_offset); 306 } 307 */ 308 float[][] times = time_set.indexToValue(indexes); 309 310 return new float[][] {alts[0], times[0]}; 311 } 312 public float[][] fromReference(float[][] vals) throws VisADException { 313 int[] indexes = alt_set.valueToIndex(new float[][] {vals[vert_tup_idx]}); 314 float[][] vert_coords = lin1DSet_s[vert_tup_idx].indexToValue(indexes); 315 indexes = time_set.valueToIndex(new float[][] {vals[track_tup_idx]}); 316 float[][] track_coords = lin1DSet_s[track_tup_idx].indexToValue(indexes); 317 return new float[][] {vert_coords[0], track_coords[0]}; 318 } 319 public double[][] toReference(double[][] vals) throws VisADException { 320 return Set.floatToDouble(toReference(Set.doubleToFloat(vals))); 321 } 322 public double[][] fromReference(double[][] vals) throws VisADException { 323 return Set.floatToDouble(fromReference(Set.doubleToFloat(vals))); 324 } 325 public boolean equals(Object obj) { 326 return true; 327 } 328 }; 329 } 330 catch (Exception e) { 331 } 332 333 RealTupleType domainTupType = new RealTupleType(domainRealTypes[0], domainRealTypes[1], cs, null); 334 domainSet = new Linear2DSet(domainTupType, first[0], last[0], length[0], first[1], last[1], length[1]); 335 336 return domainSet; 337 } 338 339 public FunctionType getMathType() { 340 return null; 341 } 342 343 public RealType[] getDomainRealTypes() { 344 return domainRealTypes; 345 } 346 347 public Map<String, double[]> getDefaultSubset() { 348 Map<String, double[]> subset = ProfileAlongTrack.getEmptySubset(); 349 350 double[] coords = subset.get("TrackDim"); 351 coords[0] = 20000.0; 352 coords[1] = (TrackLen - 15000.0) - 1; 353 //-coords[2] = 30.0; 354 coords[2] = 5.0; 355 subset.put("TrackDim", coords); 356 357 coords = subset.get("VertDim"); 358 coords[0] = 98.0; 359 coords[1] = (VertLen) - 1; 360 coords[2] = 2.0; 361 subset.put("VertDim", coords); 362 return subset; 363 } 364 365 public int[] getTrackRangeInsideLonLatRect(double minLat, double maxLat, double minLon, double maxLon) { 366 int nn = 100; 367 int skip = TrackLen/nn; 368 double lon; 369 double lat; 370 371 int idx = 0; 372 while (idx < TrackLen) { 373 lon = (double)trackLongitude[idx]; 374 lat = (double)trackLatitude[idx]; 375 if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) break; 376 idx += skip; 377 } 378 if (idx > TrackLen-1) idx = TrackLen-1; 379 if (idx == TrackLen-1) return new int[] {-1,-1}; 380 381 int low_idx = idx; 382 while (low_idx > 0) { 383 lon = (double)trackLongitude[low_idx]; 384 lat = (double)trackLatitude[low_idx]; 385 if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) { 386 low_idx -= 1; 387 continue; 388 } 389 else { 390 break; 391 } 392 } 393 394 int hi_idx = idx; 395 while (hi_idx < TrackLen-1) { 396 lon = (double)trackLongitude[hi_idx]; 397 lat = (double)trackLatitude[hi_idx]; 398 if (((lon > minLon) && (lon < maxLon)) && ((lat > minLat)&&(lat < maxLat))) { 399 hi_idx += 1; 400 continue; 401 } 402 else { 403 break; 404 } 405 } 406 return new int[] {low_idx, hi_idx}; 407 } 408 409 public Map<String, double[]> getSubsetFromLonLatRect(Map<String, double[]> subset, double minLat, double maxLat, double minLon, double maxLon) { 410 double[] coords = subset.get("TrackDim"); 411 int[] idxs = getTrackRangeInsideLonLatRect(minLat, maxLat, minLon, maxLon); 412 coords[0] = (double) idxs[0]; 413 coords[1] = (double) idxs[1]; 414 if ((coords[0] == -1) || (coords[1] == -1)) return null; 415 return subset; 416 } 417 418 public Map<String, double[]> getSubsetFromLonLatRect(Map<String, double[]> subset, double minLat, double maxLat, double minLon, double maxLon, 419 int xStride, int yStride, int zStride) { 420 421 double[] coords = subset.get("TrackDim"); 422 int[] idxs = getTrackRangeInsideLonLatRect(minLat, maxLat, minLon, maxLon); 423 coords[0] = (double) idxs[0]; 424 coords[1] = (double) idxs[1]; 425 if ((coords[0] == -1) || (coords[1] == -1)) return null; 426 if (xStride > 0) { 427 coords[2] = xStride; 428 } 429 430 coords = subset.get("VertDim"); 431 if (yStride > 0) { 432 coords[2] = yStride; 433 } 434 return subset; 435 } 436 437 public Map<String, double[]> getSubsetFromLonLatRect(double minLat, double maxLat, double minLon, double maxLon) { 438 return getSubsetFromLonLatRect(getDefaultSubset(), minLat, maxLat, minLon, maxLon); 439 } 440 441 public Map<String, double[]> getSubsetFromLonLatRect(double minLat, double maxLat, double minLon, double maxLon, 442 int xStride, int yStride, int zStride) { 443 444 return getSubsetFromLonLatRect(getDefaultSubset(), minLat, maxLat, minLon, maxLon, xStride, yStride, zStride); 445 } 446 447 public abstract float[] getVertBinAltitude() throws Exception; 448 public abstract float[] getTrackTimes() throws Exception; 449 public abstract RealType makeVertLocType() throws Exception; 450 public abstract RealType makeTrackTimeType() throws Exception; 451 public abstract float[] getTrackLongitude() throws Exception; 452 public abstract float[] getTrackLatitude() throws Exception; 453 454 public static FieldImpl medianFilter(FieldImpl field, int window_lenx, int window_leny) throws VisADException, RemoteException, CloneNotSupportedException { 455 Set dSet = field.getDomainSet(); 456 if (dSet.getManifoldDimension() != 1) { 457 throw new VisADException("medianFilter: outer field domain must have manifoldDimension = 1"); 458 } 459 int outerLen = dSet.getLength(); 460 461 FieldImpl filtField = (FieldImpl)field.clone(); 462 463 for (int t=0; t<outerLen; t++) { 464 FlatField ff = (FlatField) filtField.getSample(t, false); 465 medianFilter(ff, window_lenx, window_leny); 466 } 467 468 return filtField; 469 } 470 471 /** 472 * Apply a median filter to FlatField range If domain dimension == 3, range is filtered as succession of 473 * 2D filtered layers along the slowest varying dimension. 474 * @param fltFld incoming FlatField to be filtered 475 * @param window_lenx filter window (kernel) dimensions; x: fastest varying dimensions 476 * @param window_leny 477 * @return FlatField with filtered (copied) range 478 * @throws VisADException 479 * @throws RemoteException 480 */ 481 public static FlatField medianFilter(FlatField fltFld, int window_lenx, int window_leny) throws VisADException, RemoteException { 482 GriddedSet domSet = (GriddedSet) fltFld.getDomainSet(); 483 FlatField filtFld = new FlatField((FunctionType)fltFld.getType(), domSet); 484 485 int[] lens = domSet.getLengths(); 486 int manifoldDimension = domSet.getManifoldDimension(); 487 488 float[][] rngVals = fltFld.getFloats(false); 489 int rngTupleDim = rngVals.length; 490 float[][] filtVals = new float[rngTupleDim][]; 491 492 if (manifoldDimension == 2) { 493 for (int t=0; t<rngTupleDim; t++) { 494 filtVals[t] = medianFilter(rngVals[t], lens[0], lens[1], window_lenx, window_leny); 495 } 496 } 497 else if (manifoldDimension == 3) { 498 int outerDimLen = lens[0]; 499 filtVals = new float[rngTupleDim][lens[0]*lens[1]*lens[2]]; 500 float[] rngVals2D = new float[lens[1]*lens[2]]; 501 502 for (int k = 0; k<outerDimLen; k++) { 503 int idx = k*lens[1]*lens[2]; 504 for (int t=0; t<rngTupleDim; t++) { 505 System.arraycopy(rngVals[t], idx, rngVals2D, 0, lens[1]*lens[2]); 506 507 float[] fltA = medianFilter(rngVals2D, lens[1], lens[2], window_lenx, window_leny); 508 509 System.arraycopy(fltA, 0, filtVals[t], idx, lens[1]*lens[2]); 510 } 511 } 512 } 513 514 filtFld.setSamples(filtVals, false); 515 516 return filtFld; 517 } 518 519 /** 520 * Apply median filter to 2D array of values 521 * @param A 2D array to filter 522 * @param lenx Dimensions of A, x varies fastest 523 * @param leny 524 * @param window_lenx Dimensions of window (kernel) x fastest varying 525 * @param window_leny 526 * @return 527 * @throws VisADException 528 */ 529 public static float[] medianFilter(float[] A, int lenx, int leny, int window_lenx, int window_leny) 530 throws VisADException { 531 float[] result = new float[A.length]; 532 float[] window = new float[window_lenx*window_leny]; 533 float[] sortedWindow = new float[window_lenx*window_leny]; 534 int[] sort_indexes = new int[window_lenx*window_leny]; 535 int[] indexes = new int[window_lenx*window_leny]; 536 int[] indexesB = new int[window_lenx*window_leny]; 537 538 int[] numToInsertAt = new int[window_lenx*window_leny]; 539 float[][] valsToInsert = new float[window_lenx*window_leny][window_lenx*window_leny]; 540 int[][] idxsToInsert = new int[window_lenx*window_leny][window_lenx*window_leny]; 541 542 int[] numBefore = new int[window_lenx*window_leny]; 543 float[][] valsBefore = new float[window_lenx*window_leny][window_lenx*window_leny]; 544 int[][] idxsBefore = new int[window_lenx*window_leny][window_lenx*window_leny]; 545 546 int[] numAfter = new int[window_lenx*window_leny]; 547 float[][] valsAfter = new float[window_lenx*window_leny][window_lenx*window_leny]; 548 int[][] idxsAfter = new int[window_lenx*window_leny][window_lenx*window_leny]; 549 550 float[] sortedArray = new float[window_lenx*window_leny]; 551 552 int a_idx; 553 int w_idx; 554 555 int w_lenx = window_lenx/2; 556 int w_leny = window_leny/2; 557 558 int lo; 559 int hi; 560 int ww_jj; 561 int ww_ii; 562 int cnt=0; 563 int ncnt; 564 int midx; 565 float median; 566 567 int lenA = A.length; 568 569 for (int i=0; i<lenx; i++) { // zig-zag better? Maybe, but more complicated 570 for (int j=0; j<leny; j++) { 571 a_idx = j*lenx + i; 572 573 if (j > 0) { 574 ncnt = 0; 575 for (int t=0; t<cnt; t++) { 576 // last window index in data coords: A[lenx,leny] 577 int k = indexes[sort_indexes[t]]; 578 ww_jj = k/lenx; 579 ww_ii = k % lenx; 580 581 // current window bounds in data coords 582 int ww_jj_lo = j - w_leny; 583 int ww_jj_hi = j + w_leny; 584 int ww_ii_lo = i - w_lenx; 585 int ww_ii_hi = i + w_lenx; 586 587 if (ww_jj_lo < 0) ww_jj_lo = 0; 588 if (ww_ii_lo < 0) ww_ii_lo = 0; 589 if (ww_jj_hi > leny-1) ww_jj_hi = leny-1; 590 if (ww_ii_hi > lenx-1) ww_ii_hi = lenx-1; 591 592 593 // These are the points which overlap between the last and current window 594 if ((ww_jj >= ww_jj_lo && ww_jj < ww_jj_hi) && (ww_ii >= ww_ii_lo && ww_ii < ww_ii_hi)) { 595 window[ncnt] = sortedWindow[t]; 596 indexesB[ncnt] = k; 597 ncnt++; 598 } 599 } 600 601 602 // Add the new points from sliding the window to the overlap points above 603 java.util.Arrays.fill(numToInsertAt, 0); 604 java.util.Arrays.fill(numBefore, 0); 605 java.util.Arrays.fill(numAfter, 0); 606 607 ww_jj = w_leny-1 + j; 608 for (int w_i=-w_lenx; w_i<w_lenx; w_i++) { 609 ww_ii = w_i + i; 610 int k = ww_jj*lenx+ww_ii; 611 if (k >= 0 && k < lenA) { 612 float val = A[k]; 613 if (ncnt > 0) { 614 int t = 0; 615 if (val < window[t]) { 616 valsBefore[0][numBefore[0]] = val; 617 idxsBefore[0][numBefore[0]] = k; 618 numBefore[0]++; 619 continue; 620 } 621 t = ncnt-1; 622 if (val >= window[t]) { 623 valsAfter[0][numAfter[0]] = val; 624 idxsAfter[0][numAfter[0]] = k; 625 numAfter[0]++; 626 continue; 627 } 628 629 for (t=0; t<ncnt-1; t++) { 630 if (val >= window[t] && val < window[t+1]) { 631 valsToInsert[t][numToInsertAt[t]] = val; 632 idxsToInsert[t][numToInsertAt[t]] = k; 633 numToInsertAt[t]++; 634 break; 635 } 636 } 637 } 638 else if (!Float.isNaN(val)) { 639 valsBefore[0][numBefore[0]] = val; 640 idxsBefore[0][numBefore[0]] = k; 641 numBefore[0]++; 642 continue; 643 } 644 } 645 } 646 647 // insert new unsorted values into the already sorted overlap window region 648 int tcnt = 0; 649 650 for (int it=0; it<numBefore[0]; it++) { 651 sortedArray[tcnt] = valsBefore[0][it]; 652 indexes[tcnt] = idxsBefore[0][it]; 653 tcnt++; 654 } 655 656 for (int t=0; t<ncnt; t++) { 657 sortedArray[tcnt] = window[t]; 658 indexes[tcnt] = indexesB[t]; 659 tcnt++; 660 if (numToInsertAt[t] > 0) { 661 if (numToInsertAt[t] == 2) { // two item sort here to save work for QuickSort 662 float val0 = valsToInsert[t][0]; 663 float val1 = valsToInsert[t][1]; 664 665 if (val0 <= val1) { 666 sortedArray[tcnt] = val0; 667 indexes[tcnt] = idxsToInsert[t][0]; 668 tcnt++; 669 670 sortedArray[tcnt] = val1; 671 indexes[tcnt] = idxsToInsert[t][1]; 672 tcnt++; 673 } 674 else { 675 sortedArray[tcnt] = val1; 676 indexes[tcnt] = idxsToInsert[t][1]; 677 tcnt++; 678 679 sortedArray[tcnt] = val0; 680 indexes[tcnt] = idxsToInsert[t][0]; 681 tcnt++; 682 } 683 } 684 else if (numToInsertAt[t] == 1) { 685 sortedArray[tcnt] = valsToInsert[t][0]; 686 indexes[tcnt] = idxsToInsert[t][0]; 687 tcnt++; 688 } 689 else { 690 for (int it=0; it<numToInsertAt[t]; it++) { 691 sortedArray[tcnt] = valsToInsert[t][it]; 692 indexes[tcnt] = idxsToInsert[t][it]; 693 tcnt++; 694 } 695 } 696 } 697 } 698 699 for (int it=0; it<numAfter[0]; it++) { 700 sortedArray[tcnt] = valsAfter[0][it]; 701 indexes[tcnt] = idxsAfter[0][it]; 702 tcnt++; 703 } 704 705 // Now sort the new unsorted and overlap sorted points together to get the new window median 706 707 System.arraycopy(sortedArray, 0, sortedWindow, 0, tcnt); 708 if (tcnt > 0) { 709 sort_indexes = QuickSort.sort(sortedWindow, 0, tcnt-1); 710 median = sortedWindow[tcnt/2]; 711 } 712 else { 713 median = Float.NaN; 714 } 715 cnt = tcnt; 716 717 } 718 else { // full sort done once for each row (see note on zigzag above) 719 720 cnt = 0; 721 for (int w_j=-w_leny; w_j<w_leny; w_j++) { 722 for (int w_i=-w_lenx; w_i<w_lenx; w_i++) { 723 ww_jj = w_j + j; 724 ww_ii = w_i + i; 725 w_idx = (w_j+w_leny)*window_lenx + (w_i+w_lenx); 726 if ((ww_jj >= 0) && (ww_ii >=0) && (ww_jj < leny) && (ww_ii < lenx)) { 727 int k = ww_jj*lenx+ww_ii; 728 float val = A[k]; 729 if (!Float.isNaN(val)) { 730 window[cnt] = val; 731 indexes[cnt] = k; 732 cnt++; 733 } 734 } 735 } 736 } 737 738 739 System.arraycopy(window, 0, sortedWindow, 0, cnt); 740 if (cnt > 0) { 741 sort_indexes = QuickSort.sort(sortedWindow, 0, cnt-1); 742 midx = cnt/2; 743 median = sortedWindow[midx]; 744 } 745 else { 746 median = Float.NaN; 747 } 748 749 } 750 751 if (Float.isNaN(A[a_idx])) { 752 result[a_idx] = Float.NaN; 753 } 754 else { 755 result[a_idx] = median; 756 } 757 758 } 759 } 760 761 return result; 762 } 763 764}