001 /* 002 * $Id: RangeProcessor.java,v 1.28 2012/04/10 15:32:42 rink Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2012 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030 031 package edu.wisc.ssec.mcidasv.data.hydra; 032 033 import java.util.HashMap; 034 import java.util.ArrayList; 035 036 import org.slf4j.Logger; 037 import org.slf4j.LoggerFactory; 038 039 import visad.util.Util; 040 041 public class RangeProcessor { 042 043 private static final Logger logger = LoggerFactory.getLogger(RangeProcessor.class); 044 045 static RangeProcessor createRangeProcessor(MultiDimensionReader reader, HashMap metadata) throws Exception { 046 if (reader instanceof GranuleAggregation) { 047 return new AggregationRangeProcessor((GranuleAggregation)reader, metadata); 048 } 049 050 if (metadata.get("scale_name") == null) { 051 String product_name = (String) metadata.get(SwathAdapter.product_name); 052 if (product_name == "IASI_L1C_xxx") { 053 return new IASI_RangeProcessor(); 054 } 055 return null; 056 } 057 else { 058 String product_name = (String) metadata.get(ProfileAlongTrack.product_name); 059 if (product_name == "2B-GEOPROF") { 060 return new CloudSat_2B_GEOPROF_RangeProcessor(reader, metadata); 061 } 062 else { 063 return new RangeProcessor(reader, metadata); 064 } 065 } 066 } 067 068 MultiDimensionReader reader; 069 HashMap metadata; 070 071 float[] scale = null; 072 float[] offset = null; 073 float[] missing = null; 074 float[] valid_range = null; 075 float valid_low = -Float.MAX_VALUE; 076 float valid_high = Float.MAX_VALUE; 077 float[] low = new float[] {-Float.MAX_VALUE}; 078 float[] high = new float[] {Float.MAX_VALUE}; 079 080 boolean unpack = false; 081 boolean unsigned = false; 082 boolean rangeCheckBeforeScaling = true; 083 084 int scaleOffsetLen = 1; 085 086 String multiScaleDimName = SpectrumAdapter.channelIndex_name; 087 boolean hasMultiDimensionScale = false; 088 089 int multiScaleDimensionIndex = 0; 090 091 int soIndex = 0; 092 093 public RangeProcessor() { 094 } 095 096 public RangeProcessor(float scale, float offset, float valid_low, float valid_high, float missing) { 097 this.scale = new float[] {scale}; 098 this.offset = new float[] {offset}; 099 this.missing = new float[] {missing}; 100 this.valid_low = valid_low; 101 this.valid_high = valid_high; 102 } 103 104 105 public RangeProcessor(MultiDimensionReader reader, HashMap metadata, String multiScaleDimName) throws Exception { 106 this(reader, metadata); 107 this.multiScaleDimName = multiScaleDimName; 108 } 109 110 public RangeProcessor(MultiDimensionReader reader, HashMap metadata) throws Exception { 111 this.reader = reader; 112 this.metadata = metadata; 113 114 if (metadata.get("unpack") != null) { 115 unpack = true; 116 } 117 118 if (metadata.get("unsigned") != null) { 119 unsigned = true; 120 } 121 122 if (metadata.get("range_check_after_scaling") != null) { 123 String s = (String) metadata.get("range_check_after_scaling"); 124 logger.debug("range_check_after_scaling: " + s); 125 rangeCheckBeforeScaling = false; 126 } 127 128 String array_name = (String) metadata.get("array_name"); 129 130 scale = getAttributeAsFloatArray(array_name, (String) metadata.get("scale_name")); 131 132 offset = getAttributeAsFloatArray(array_name, (String) metadata.get("offset_name")); 133 134 if (scale != null) { 135 scaleOffsetLen = scale.length; 136 137 if (offset != null) { 138 if (scale.length != offset.length) { 139 throw new Exception("RangeProcessor: scale and offset array lengths must be equal"); 140 } 141 } 142 else { 143 offset = new float[scaleOffsetLen]; 144 for (int i=0; i<offset.length; i++) offset[i] = 0f; 145 } 146 147 } 148 149 missing = getAttributeAsFloatArray(array_name, (String) metadata.get("fill_value_name")); 150 151 String metaStr = (String)metadata.get("valid_range"); 152 // attr name not supplied, so try the convention default 153 if (metaStr == null) { 154 metaStr = "valid_range"; 155 } 156 157 valid_range = getAttributeAsFloatArray(array_name, metaStr); 158 if (valid_range != null) { 159 160 valid_low = valid_range[0]; 161 valid_high = valid_range[1]; 162 163 if (valid_range[0] > valid_range[1]) { 164 valid_low = valid_range[1]; 165 valid_high = valid_range[0]; 166 } 167 } 168 169 String str = (String)metadata.get("multiScaleDimensionIndex"); 170 hasMultiDimensionScale = (str != null); 171 multiScaleDimensionIndex = (str != null) ? Integer.parseInt(str) : 0; 172 } 173 174 public float[] getAttributeAsFloatArray(String arrayName, String attrName) 175 throws Exception 176 { 177 float[] fltArray = null; 178 HDFArray arrayAttr = reader.getArrayAttribute(arrayName, attrName); 179 180 if (arrayAttr != null) { 181 182 if (arrayAttr.getType().equals(Float.TYPE)) { 183 float[] attr = (float[]) arrayAttr.getArray(); 184 fltArray = new float[attr.length]; 185 for (int k=0; k<attr.length; k++) fltArray[k] = attr[k]; 186 } 187 else if (arrayAttr.getType().equals(Short.TYPE)) { 188 short[] attr = (short[]) arrayAttr.getArray(); 189 fltArray = new float[attr.length]; 190 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 191 } 192 else if (arrayAttr.getType().equals(Integer.TYPE)) { 193 int[] attr = (int[]) arrayAttr.getArray(); 194 fltArray = new float[attr.length]; 195 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 196 } 197 else if (arrayAttr.getType().equals(Double.TYPE)) { 198 double[] attr = (double[]) arrayAttr.getArray(); 199 fltArray = new float[attr.length]; 200 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 201 } 202 203 } 204 205 return fltArray; 206 } 207 208 /** 209 * Process a range of data from a byte array 210 * @param values 211 * @param subset 212 * @return 213 */ 214 215 public float[] processRange(byte[] values, HashMap subset) { 216 217 if (subset != null) { 218 if (subset.get(multiScaleDimName) != null) { 219 soIndex = (int) ((double[])subset.get(multiScaleDimName))[0]; 220 } 221 } 222 223 float[] new_values = new float[values.length]; 224 225 // if we are working with unsigned data, need to convert missing vals to unsigned too 226 if (unsigned) { 227 if (missing != null) { 228 for (int i = 0; i < missing.length; i++) { 229 missing[i] = (float) Util.unsignedByteToInt((byte) missing[i]); 230 } 231 } 232 } 233 234 float val = 0f; 235 int i = 0; 236 boolean isMissing = false; 237 238 for (int k = 0; k < values.length; k++) { 239 240 val = (float) values[k]; 241 if (unsigned) { 242 i = Util.unsignedByteToInt(values[k]); 243 val = (float) i; 244 } 245 246 // first, check the (possibly multiple) missing values 247 isMissing = false; 248 if (missing != null) { 249 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 250 if (val == missing[mvIdx]) { 251 isMissing = true; 252 break; 253 } 254 } 255 } 256 257 if (isMissing) { 258 new_values[k] = Float.NaN; 259 continue; 260 } 261 262 if (rangeCheckBeforeScaling) { 263 if ((val < valid_low) || (val > valid_high)) { 264 new_values[k] = Float.NaN; 265 continue; 266 } 267 } 268 269 if (scale != null) { 270 if (unpack) { 271 new_values[k] = scale[soIndex] * (val) + offset[soIndex]; 272 } else { 273 new_values[k] = scale[soIndex] * (val - offset[soIndex]); 274 } 275 } 276 else { 277 new_values[k] = val; 278 } 279 280 // do valid range check AFTER scaling? 281 if (! rangeCheckBeforeScaling) { 282 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 283 new_values[k] = Float.NaN; 284 } 285 } 286 } 287 return new_values; 288 } 289 290 /** 291 * Process a range of data from a short array 292 * @param values 293 * @param subset 294 * @return 295 */ 296 297 public float[] processRange(short[] values, HashMap subset) { 298 299 300 if (subset != null) { 301 if (subset.get(multiScaleDimName) != null) { 302 soIndex = (int) ((double[])subset.get(multiScaleDimName))[0]; 303 } 304 } 305 306 float[] new_values = new float[values.length]; 307 308 // if we are working with unsigned data, need to convert missing vals to unsigned too 309 if (unsigned) { 310 if (missing != null) { 311 for (int i = 0; i < missing.length; i++) { 312 missing[i] = (float) Util.unsignedShortToInt((short) missing[i]); 313 } 314 } 315 } 316 317 float val = 0f; 318 int i = 0; 319 boolean isMissing = false; 320 321 for (int k = 0; k < values.length; k++) { 322 323 val = (float) values[k]; 324 if (unsigned) { 325 i = Util.unsignedShortToInt(values[k]); 326 val = (float) i; 327 } 328 329 // first, check the (possibly multiple) missing values 330 isMissing = false; 331 if (missing != null) { 332 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 333 if (val == missing[mvIdx]) { 334 isMissing = true; 335 break; 336 } 337 } 338 } 339 340 if (isMissing) { 341 new_values[k] = Float.NaN; 342 continue; 343 } 344 345 if (rangeCheckBeforeScaling) { 346 if ((val < valid_low) || (val > valid_high)) { 347 new_values[k] = Float.NaN; 348 continue; 349 } 350 } 351 352 if (scale != null) { 353 if (unpack) { 354 new_values[k] = (scale[soIndex] * val) + offset[soIndex]; 355 } else { 356 new_values[k] = scale[soIndex] * (val - offset[soIndex]); 357 } 358 } else { 359 new_values[k] = val; 360 } 361 362 // do valid range check AFTER scaling? 363 if (! rangeCheckBeforeScaling) { 364 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 365 new_values[k] = Float.NaN; 366 } 367 } 368 369 } 370 return new_values; 371 } 372 373 /** 374 * Process a range of data from a float array 375 * @param values 376 * @param subset 377 * @return 378 */ 379 380 public float[] processRange(float[] values, HashMap subset) { 381 382 float[] new_values = null; 383 384 if ((missing != null) || (valid_range != null)) { 385 new_values = new float[values.length]; 386 } 387 else { 388 return values; 389 } 390 391 float val; 392 393 for (int k = 0; k < values.length; k++) { 394 val = values[k]; 395 new_values[k] = val; 396 397 // first, check the (possibly multiple) missing values 398 if (missing != null) { 399 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 400 if (val == missing[mvIdx]) { 401 new_values[k] = Float.NaN; 402 break; 403 } 404 } 405 } 406 407 if ((valid_range != null) && ((val < valid_low) || (val > valid_high))) { 408 new_values[k] = Float.NaN; 409 } 410 411 } 412 413 return new_values; 414 } 415 416 /** 417 * Process a range of data from a double array 418 * @param values 419 * @param subset 420 * @return 421 */ 422 423 public double[] processRange(double[] values, HashMap subset) { 424 425 double[] new_values = null; 426 427 if ((missing != null) || (valid_range != null)) { 428 new_values = new double[values.length]; 429 } 430 else { 431 return values; 432 } 433 434 double val; 435 436 for (int k = 0; k < values.length; k++) { 437 val = values[k]; 438 new_values[k] = val; 439 440 // first, check the (possibly multiple) missing values 441 if (missing != null) { 442 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 443 if (val == missing[mvIdx]) { 444 new_values[k] = Float.NaN; 445 break; 446 } 447 } 448 } 449 450 if ((valid_range != null) && ((val < valid_low) || (val > valid_high))) { 451 new_values[k] = Double.NaN; 452 } 453 } 454 455 return new_values; 456 } 457 458 /** 459 * Process a range of data from a byte array 460 * @param values 461 * @return 462 */ 463 464 public float[] processAlongMultiScaleDim(byte[] values) { 465 466 float[] new_values = new float[values.length]; 467 468 // if we are working with unsigned data, need to convert missing vals to unsigned too 469 if (unsigned) { 470 if (missing != null) { 471 for (int i = 0; i < missing.length; i++) { 472 missing[i] = (float) Util.unsignedByteToInt((byte) missing[i]); 473 } 474 } 475 } 476 477 float val = 0f; 478 int i = 0; 479 boolean isMissing = false; 480 481 for (int k = 0; k < values.length; k++) { 482 483 val = (float) values[k]; 484 if (unsigned) { 485 i = Util.unsignedByteToInt(values[k]); 486 val = (float) i; 487 } 488 489 // first, check the (possibly multiple) missing values 490 isMissing = false; 491 if (missing != null) { 492 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 493 if (val == missing[mvIdx]) { 494 isMissing = true; 495 break; 496 } 497 } 498 } 499 500 if (isMissing) { 501 new_values[k] = Float.NaN; 502 continue; 503 } 504 505 if (rangeCheckBeforeScaling) { 506 if ((val < valid_low) || (val > valid_high)) { 507 new_values[k] = Float.NaN; 508 continue; 509 } 510 } 511 512 if (unpack) { 513 new_values[k] = scale[k] * val + offset[k]; 514 } else { 515 new_values[k] = scale[k] * (val - offset[k]); 516 } 517 518 // do valid range check AFTER scaling? 519 if (! rangeCheckBeforeScaling) { 520 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 521 new_values[k] = Float.NaN; 522 } 523 } 524 } 525 return new_values; 526 } 527 528 /** 529 * Process a range of data from a short array 530 * @param values 531 * @return 532 */ 533 534 public float[] processAlongMultiScaleDim(short[] values) { 535 536 float[] new_values = new float[values.length]; 537 538 // if we are working with unsigned data, need to convert missing vals to unsigned too 539 if (unsigned) { 540 if (missing != null) { 541 for (int i = 0; i < missing.length; i++) { 542 missing[i] = (float) Util.unsignedShortToInt((short) missing[i]); 543 } 544 } 545 } 546 547 float val = 0f; 548 int i = 0; 549 boolean isMissing = false; 550 551 for (int k = 0; k < values.length; k++) { 552 553 val = (float) values[k]; 554 if (unsigned) { 555 i = Util.unsignedShortToInt(values[k]); 556 val = (float) i; 557 } 558 559 // first, check the (possibly multiple) missing values 560 isMissing = false; 561 if (missing != null) { 562 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 563 if (val == missing[mvIdx]) { 564 isMissing = true; 565 break; 566 } 567 } 568 } 569 570 if (isMissing) { 571 new_values[k] = Float.NaN; 572 continue; 573 } 574 575 if (rangeCheckBeforeScaling) { 576 if ((val < valid_low) || (val > valid_high)) { 577 new_values[k] = Float.NaN; 578 continue; 579 } 580 } 581 582 if (unpack) { 583 new_values[k] = scale[k] * val + offset[k]; 584 } else { 585 new_values[k] = scale[k] * (val - offset[k]); 586 } 587 588 // do valid range check AFTER scaling? 589 if (! rangeCheckBeforeScaling) { 590 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 591 new_values[k] = Float.NaN; 592 } 593 } 594 } 595 return new_values; 596 } 597 598 public void setMultiScaleDimName(String multiScaleDimName) { 599 this.multiScaleDimName = multiScaleDimName; 600 } 601 602 public int getMultiScaleDimensionIndex() { 603 return multiScaleDimensionIndex; 604 } 605 606 public boolean hasMultiDimensionScale() { 607 return hasMultiDimensionScale; 608 } 609 610 public void setHasMultiDimensionScale(boolean yesno) { 611 hasMultiDimensionScale = yesno; 612 } 613 614 public void setMultiScaleIndex(int idx) { 615 this.soIndex = idx; 616 } 617 618 } 619 620 class IASI_RangeProcessor extends RangeProcessor { 621 622 public IASI_RangeProcessor() throws Exception { 623 super(); 624 } 625 626 public float[] processRange(short[] values, HashMap subset) { 627 int channelIndex = (int) ((double[]) subset.get(SpectrumAdapter.channelIndex_name))[0]; 628 629 float[] new_values = IASI_L1C_Utility.getDecodedIASIImage(values, null, channelIndex); 630 631 double[] track_coords = (double[]) subset.get(SwathAdapter.track_name); 632 double[] xtrack_coords = (double[]) subset.get(SwathAdapter.xtrack_name); 633 634 int numElems = ((int)(xtrack_coords[1] - xtrack_coords[0]) + 1); 635 int numLines = ((int)(track_coords[1] - track_coords[0]) + 1); 636 637 new_values = IASI_L1C_Utility.psuedoScanReorder2(new_values, 60, numLines*2); 638 639 //- subset here, if necessary 640 641 return new_values; 642 } 643 644 } 645 646 class CrIS_RangeProcessor extends RangeProcessor { 647 648 public CrIS_RangeProcessor() throws Exception { 649 super(); 650 } 651 652 public float[] processRange(float[] values, HashMap subset) { 653 654 double[] track_coords = (double[]) subset.get(SwathAdapter.track_name); 655 double[] xtrack_coords = (double[]) subset.get(SwathAdapter.xtrack_name); 656 657 int numElems = ((int)(xtrack_coords[1] - xtrack_coords[0]) + 1); 658 int numLines = ((int)(track_coords[1] - track_coords[0]) + 1); 659 660 values = CrIS_SDR_Utility.psuedoScanReorder(values, 90, numLines*3); 661 662 //- subset here, if necessary 663 664 return values; 665 } 666 667 } 668 669 670 class CloudSat_2B_GEOPROF_RangeProcessor extends RangeProcessor { 671 672 public CloudSat_2B_GEOPROF_RangeProcessor(MultiDimensionReader reader, HashMap metadata) throws Exception { 673 super(reader, metadata); 674 } 675 676 public float[] processRange(short[] values, HashMap subset) { 677 float[] new_values = new float[values.length]; 678 for (int k=0; k<values.length;k++) { 679 float val = (float) values[k]; 680 if (val == missing[0]) { 681 new_values[k] = Float.NaN; 682 } 683 else if ((val < valid_low) || (val > valid_high)) { 684 new_values[k] = -40f; 685 } 686 else { 687 new_values[k] = val/scale[0] + offset[0]; 688 } 689 } 690 return new_values; 691 } 692 693 } 694 695 class AggregationRangeProcessor extends RangeProcessor { 696 697 ArrayList<RangeProcessor> rangeProcessors = new ArrayList<RangeProcessor>(); 698 699 int rngIdx = 0; 700 701 public AggregationRangeProcessor(GranuleAggregation aggrReader, HashMap metadata) throws Exception { 702 super(); 703 704 ArrayList readers = aggrReader.getReaders(); 705 706 int num = 0; 707 708 for (int rdrIdx = 0; rdrIdx < readers.size(); rdrIdx++) { 709 RangeProcessor rngProcessor = 710 RangeProcessor.createRangeProcessor((MultiDimensionReader)readers.get(rdrIdx), metadata); 711 712 if (rngProcessor.hasMultiDimensionScale()) { 713 num++; 714 } 715 716 rangeProcessors.add(rngProcessor); 717 } 718 719 if (num > 0 && num != readers.size()) { 720 throw new Exception("AggregationRangeProcessor: all or none can define a multiDimensionScale"); 721 } 722 else if (num == readers.size()) { 723 setHasMultiDimensionScale(true); 724 } 725 726 aggrReader.addRangeProcessor((String)metadata.get(SwathAdapter.array_name), this); 727 } 728 729 public synchronized void setWhichRangeProcessor(int index) { 730 rngIdx = index; 731 } 732 733 public synchronized void setMultiScaleIndex(int idx) { 734 rangeProcessors.get(rngIdx).setMultiScaleIndex(idx); 735 } 736 737 738 public synchronized float[] processRange(byte[] values, HashMap subset) { 739 return rangeProcessors.get(rngIdx).processRange(values, subset); 740 } 741 742 public synchronized float[] processRange(short[] values, HashMap subset) { 743 return rangeProcessors.get(rngIdx).processRange(values, subset); 744 } 745 746 public synchronized float[] processRange(float[] values, HashMap subset) { 747 return rangeProcessors.get(rngIdx).processRange(values, subset); 748 } 749 750 public synchronized double[] processRange(double[] values, HashMap subset) { 751 return rangeProcessors.get(rngIdx).processRange(values, subset); 752 } 753 754 public synchronized float[] processAlongMultiScaleDim(short[] values) { 755 return rangeProcessors.get(rngIdx).processAlongMultiScaleDim(values); 756 } 757 758 public synchronized float[] processAlongMultiScaleDim(byte[] values) { 759 return rangeProcessors.get(rngIdx).processAlongMultiScaleDim(values); 760 } 761 }