001/* 002 * $Id: RangeProcessor.java,v 1.24 2011/03/24 16:06:33 davep Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 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 031package edu.wisc.ssec.mcidasv.data.hydra; 032 033import java.util.HashMap; 034import java.util.ArrayList; 035 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039import visad.util.Util; 040 041public 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 088 public RangeProcessor() { 089 } 090 091 public RangeProcessor(float scale, float offset, float valid_low, float valid_high, float missing) { 092 this.scale = new float[] {scale}; 093 this.offset = new float[] {offset}; 094 this.missing = new float[] {missing}; 095 this.valid_low = valid_low; 096 this.valid_high = valid_high; 097 } 098 099 100 public RangeProcessor(MultiDimensionReader reader, HashMap metadata, String multiScaleDimName) throws Exception { 101 this(reader, metadata); 102 this.multiScaleDimName = multiScaleDimName; 103 } 104 105 public RangeProcessor(MultiDimensionReader reader, HashMap metadata) throws Exception { 106 this.reader = reader; 107 this.metadata = metadata; 108 109 if (metadata.get("unpack") != null) { 110 unpack = true; 111 } 112 113 if (metadata.get("unsigned") != null) { 114 unsigned = true; 115 } 116 117 if (metadata.get("range_check_after_scaling") != null) { 118 String s = (String) metadata.get("range_check_after_scaling"); 119 logger.debug("range_check_after_scaling: " + s); 120 rangeCheckBeforeScaling = false; 121 } 122 123 String array_name = (String) metadata.get("array_name"); 124 125 scale = getAttributeAsFloatArray(array_name, (String) metadata.get("scale_name")); 126 127 offset = getAttributeAsFloatArray(array_name, (String) metadata.get("offset_name")); 128 129 if (scale != null) { 130 scaleOffsetLen = scale.length; 131 132 if (offset != null) { 133 if (scale.length != offset.length) { 134 throw new Exception("RangeProcessor: scale and offset array lengths must be equal"); 135 } 136 } 137 else { 138 offset = new float[scaleOffsetLen]; 139 for (int i=0; i<offset.length; i++) offset[i] = 0f; 140 } 141 142 } 143 144 missing = getAttributeAsFloatArray(array_name, (String) metadata.get("fill_value_name")); 145 146 String metaStr = (String)metadata.get("valid_range"); 147 // attr name not supplied, so try the convention default 148 if (metaStr == null) { 149 metaStr = "valid_range"; 150 } 151 152 valid_range = getAttributeAsFloatArray(array_name, metaStr); 153 if (valid_range != null) { 154 155 valid_low = valid_range[0]; 156 valid_high = valid_range[1]; 157 158 if (valid_range[0] > valid_range[1]) { 159 valid_low = valid_range[1]; 160 valid_high = valid_range[0]; 161 } 162 } 163 164 } 165 166 public float[] getAttributeAsFloatArray(String arrayName, String attrName) 167 throws Exception 168 { 169 float[] fltArray = null; 170 HDFArray arrayAttr = reader.getArrayAttribute(arrayName, attrName); 171 172 if (arrayAttr != null) { 173 174 if (arrayAttr.getType().equals(Float.TYPE)) { 175 float[] attr = (float[]) arrayAttr.getArray(); 176 fltArray = new float[attr.length]; 177 for (int k=0; k<attr.length; k++) fltArray[k] = attr[k]; 178 } 179 else if (arrayAttr.getType().equals(Short.TYPE)) { 180 short[] attr = (short[]) arrayAttr.getArray(); 181 fltArray = new float[attr.length]; 182 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 183 } 184 else if (arrayAttr.getType().equals(Integer.TYPE)) { 185 int[] attr = (int[]) arrayAttr.getArray(); 186 fltArray = new float[attr.length]; 187 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 188 } 189 else if (arrayAttr.getType().equals(Double.TYPE)) { 190 double[] attr = (double[]) arrayAttr.getArray(); 191 fltArray = new float[attr.length]; 192 for (int k=0; k<attr.length; k++) fltArray[k] = (float) attr[k]; 193 } 194 195 } 196 197 return fltArray; 198 } 199 200 /** 201 * Process a range of data from a byte array 202 * @param values 203 * @param subset 204 * @return 205 */ 206 207 public float[] processRange(byte[] values, HashMap subset) { 208 209 int soIndex = 0; // scale/offset index 210 211 if (subset != null) { 212 if (subset.get(multiScaleDimName) != null) { 213 soIndex = (int) ((double[])subset.get(multiScaleDimName))[0]; 214 } 215 } 216 217 float[] new_values = new float[values.length]; 218 219 // if we are working with unsigned data, need to convert missing vals to unsigned too 220 if (unsigned) { 221 if (missing != null) { 222 for (int i = 0; i < missing.length; i++) { 223 missing[i] = (float) Util.unsignedByteToInt((byte) missing[i]); 224 } 225 } 226 } 227 228 float val = 0f; 229 int i = 0; 230 boolean isMissing = false; 231 232 for (int k = 0; k < values.length; k++) { 233 234 val = (float) values[k]; 235 if (unsigned) { 236 i = Util.unsignedByteToInt(values[k]); 237 val = (float) i; 238 } 239 240 // first, check the (possibly multiple) missing values 241 isMissing = false; 242 if (missing != null) { 243 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 244 if (val == missing[mvIdx]) { 245 isMissing = true; 246 break; 247 } 248 } 249 } 250 251 if (isMissing) { 252 new_values[k] = Float.NaN; 253 continue; 254 } 255 256 if (rangeCheckBeforeScaling) { 257 if ((val < valid_low) || (val > valid_high)) { 258 new_values[k] = Float.NaN; 259 continue; 260 } 261 } 262 263 if (unpack) { 264 new_values[k] = scale[soIndex] * (val) + offset[soIndex]; 265 } else { 266 new_values[k] = scale[soIndex] * (val - offset[soIndex]); 267 } 268 269 // do valid range check AFTER scaling? 270 if (! rangeCheckBeforeScaling) { 271 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 272 new_values[k] = Float.NaN; 273 } 274 } 275 } 276 return new_values; 277 } 278 279 /** 280 * Process a range of data from a short array 281 * @param values 282 * @param subset 283 * @return 284 */ 285 286 public float[] processRange(short[] values, HashMap subset) { 287 288 int soIndex = 0; // scale/offset index 289 290 if (subset != null) { 291 if (subset.get(multiScaleDimName) != null) { 292 soIndex = (int) ((double[])subset.get(multiScaleDimName))[0]; 293 } 294 } 295 296 float[] new_values = new float[values.length]; 297 298 // if we are working with unsigned data, need to convert missing vals to unsigned too 299 if (unsigned) { 300 if (missing != null) { 301 for (int i = 0; i < missing.length; i++) { 302 missing[i] = (float) Util.unsignedShortToInt((short) missing[i]); 303 } 304 } 305 } 306 307 float val = 0f; 308 int i = 0; 309 boolean isMissing = false; 310 311 for (int k = 0; k < values.length; k++) { 312 313 val = (float) values[k]; 314 if (unsigned) { 315 i = Util.unsignedShortToInt(values[k]); 316 val = (float) i; 317 } 318 319 // first, check the (possibly multiple) missing values 320 isMissing = false; 321 if (missing != null) { 322 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 323 if (val == missing[mvIdx]) { 324 isMissing = true; 325 break; 326 } 327 } 328 } 329 330 if (isMissing) { 331 new_values[k] = Float.NaN; 332 continue; 333 } 334 335 if (rangeCheckBeforeScaling) { 336 if ((val < valid_low) || (val > valid_high)) { 337 new_values[k] = Float.NaN; 338 continue; 339 } 340 } 341 342 if (unpack) { 343 new_values[k] = (scale[soIndex] * val) + offset[soIndex]; 344 } else { 345 new_values[k] = scale[soIndex] * (val - offset[soIndex]); 346 } 347 348 // do valid range check AFTER scaling? 349 if (! rangeCheckBeforeScaling) { 350 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 351 new_values[k] = Float.NaN; 352 } 353 } 354 355 } 356 return new_values; 357 } 358 359 /** 360 * Process a range of data from a float array 361 * @param values 362 * @param subset 363 * @return 364 */ 365 366 public float[] processRange(float[] values, HashMap subset) { 367 368 float[] new_values = null; 369 370 if ((missing != null) || (valid_range != null)) { 371 new_values = new float[values.length]; 372 } 373 else { 374 return values; 375 } 376 377 float val; 378 379 for (int k = 0; k < values.length; k++) { 380 val = values[k]; 381 new_values[k] = val; 382 383 // first, check the (possibly multiple) missing values 384 if (missing != null) { 385 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 386 if (val == missing[mvIdx]) { 387 new_values[k] = Float.NaN; 388 break; 389 } 390 } 391 } 392 393 if ((valid_range != null) && ((val < valid_low) || (val > valid_high))) { 394 new_values[k] = Float.NaN; 395 } 396 397 } 398 399 return new_values; 400 } 401 402 /** 403 * Process a range of data from a double array 404 * @param values 405 * @param subset 406 * @return 407 */ 408 409 public double[] processRange(double[] values, HashMap subset) { 410 411 double[] new_values = null; 412 413 if ((missing != null) || (valid_range != null)) { 414 new_values = new double[values.length]; 415 } 416 else { 417 return values; 418 } 419 420 double val; 421 422 for (int k = 0; k < values.length; k++) { 423 val = values[k]; 424 new_values[k] = val; 425 426 // first, check the (possibly multiple) missing values 427 if (missing != null) { 428 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 429 if (val == missing[mvIdx]) { 430 new_values[k] = Float.NaN; 431 break; 432 } 433 } 434 } 435 436 if ((valid_range != null) && ((val < valid_low) || (val > valid_high))) { 437 new_values[k] = Double.NaN; 438 } 439 } 440 441 return new_values; 442 } 443 444 /** 445 * Process a range of data from a byte array 446 * @param values 447 * @return 448 */ 449 450 public float[] processAlongBandDim(byte[] values) { 451 452 float[] new_values = new float[values.length]; 453 454 // if we are working with unsigned data, need to convert missing vals to unsigned too 455 if (unsigned) { 456 if (missing != null) { 457 for (int i = 0; i < missing.length; i++) { 458 missing[i] = (float) Util.unsignedByteToInt((byte) missing[i]); 459 } 460 } 461 } 462 463 float val = 0f; 464 int i = 0; 465 boolean isMissing = false; 466 467 for (int k = 0; k < values.length; k++) { 468 469 val = (float) values[k]; 470 if (unsigned) { 471 i = Util.unsignedByteToInt(values[k]); 472 val = (float) i; 473 } 474 475 // first, check the (possibly multiple) missing values 476 isMissing = false; 477 if (missing != null) { 478 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 479 if (val == missing[mvIdx]) { 480 isMissing = true; 481 break; 482 } 483 } 484 } 485 486 if (isMissing) { 487 new_values[k] = Float.NaN; 488 continue; 489 } 490 491 if (rangeCheckBeforeScaling) { 492 if ((val < valid_low) || (val > valid_high)) { 493 new_values[k] = Float.NaN; 494 continue; 495 } 496 } 497 498 if (unpack) { 499 new_values[k] = scale[k] * val + offset[k]; 500 } else { 501 new_values[k] = scale[k] * (val - offset[k]); 502 } 503 504 // do valid range check AFTER scaling? 505 if (! rangeCheckBeforeScaling) { 506 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 507 new_values[k] = Float.NaN; 508 } 509 } 510 } 511 return new_values; 512 } 513 514 /** 515 * Process a range of data from a short array 516 * @param values 517 * @return 518 */ 519 520 public float[] processAlongBandDim(short[] values) { 521 522 float[] new_values = new float[values.length]; 523 524 // if we are working with unsigned data, need to convert missing vals to unsigned too 525 if (unsigned) { 526 if (missing != null) { 527 for (int i = 0; i < missing.length; i++) { 528 missing[i] = (float) Util.unsignedShortToInt((short) missing[i]); 529 } 530 } 531 } 532 533 float val = 0f; 534 int i = 0; 535 boolean isMissing = false; 536 537 for (int k = 0; k < values.length; k++) { 538 539 val = (float) values[k]; 540 if (unsigned) { 541 i = Util.unsignedShortToInt(values[k]); 542 val = (float) i; 543 } 544 545 // first, check the (possibly multiple) missing values 546 isMissing = false; 547 if (missing != null) { 548 for (int mvIdx = 0; mvIdx < missing.length; mvIdx++) { 549 if (val == missing[mvIdx]) { 550 isMissing = true; 551 break; 552 } 553 } 554 } 555 556 if (isMissing) { 557 new_values[k] = Float.NaN; 558 continue; 559 } 560 561 if (rangeCheckBeforeScaling) { 562 if ((val < valid_low) || (val > valid_high)) { 563 new_values[k] = Float.NaN; 564 continue; 565 } 566 } 567 568 if (unpack) { 569 new_values[k] = scale[k] * val + offset[k]; 570 } else { 571 new_values[k] = scale[k] * (val - offset[k]); 572 } 573 574 // do valid range check AFTER scaling? 575 if (! rangeCheckBeforeScaling) { 576 if ((new_values[k] < valid_low) || (new_values[k] > valid_high)) { 577 new_values[k] = Float.NaN; 578 } 579 } 580 } 581 return new_values; 582 } 583 584 public void setMultiScaleDimName(String multiScaleDimName) { 585 this.multiScaleDimName = multiScaleDimName; 586 } 587 588} 589 590class IASI_RangeProcessor extends RangeProcessor { 591 592 public IASI_RangeProcessor() throws Exception { 593 super(); 594 } 595 596 public float[] processRange(short[] values, HashMap subset) { 597 int channelIndex = (int) ((double[]) subset.get(SpectrumAdapter.channelIndex_name))[0]; 598 599 float[] new_values = IASI_L1C_Utility.getDecodedIASIImage(values, null, channelIndex); 600 601 double[] track_coords = (double[]) subset.get(SwathAdapter.track_name); 602 double[] xtrack_coords = (double[]) subset.get(SwathAdapter.xtrack_name); 603 604 int numElems = ((int)(xtrack_coords[1] - xtrack_coords[0]) + 1); 605 int numLines = ((int)(track_coords[1] - track_coords[0]) + 1); 606 607 new_values = IASI_L1C_Utility.psuedoScanReorder2(new_values, 60, numLines*2); 608 609 //- subset here, if necessary 610 611 return new_values; 612 } 613 614} 615 616class CrIS_RangeProcessor extends RangeProcessor { 617 618 public CrIS_RangeProcessor() throws Exception { 619 super(); 620 } 621 622 public float[] processRange(float[] values, HashMap subset) { 623 624 double[] track_coords = (double[]) subset.get(SwathAdapter.track_name); 625 double[] xtrack_coords = (double[]) subset.get(SwathAdapter.xtrack_name); 626 627 int numElems = ((int)(xtrack_coords[1] - xtrack_coords[0]) + 1); 628 int numLines = ((int)(track_coords[1] - track_coords[0]) + 1); 629 630 values = CrIS_SDR_Utility.psuedoScanReorder(values, 90, numLines*3); 631 632 //- subset here, if necessary 633 634 return values; 635 } 636 637} 638 639 640class CloudSat_2B_GEOPROF_RangeProcessor extends RangeProcessor { 641 642 public CloudSat_2B_GEOPROF_RangeProcessor(MultiDimensionReader reader, HashMap metadata) throws Exception { 643 super(reader, metadata); 644 } 645 646 public float[] processRange(short[] values, HashMap subset) { 647 float[] new_values = new float[values.length]; 648 for (int k=0; k<values.length;k++) { 649 float val = (float) values[k]; 650 if (val == missing[0]) { 651 new_values[k] = Float.NaN; 652 } 653 else if ((val < valid_low) || (val > valid_high)) { 654 new_values[k] = -40f; 655 } 656 else { 657 new_values[k] = val/scale[0] + offset[0]; 658 } 659 } 660 return new_values; 661 } 662 663} 664 665class AggregationRangeProcessor extends RangeProcessor { 666 667 ArrayList<RangeProcessor> rangeProcessors = new ArrayList<RangeProcessor>(); 668 669 int rngIdx = 0; 670 671 public AggregationRangeProcessor(GranuleAggregation aggrReader, HashMap metadata) throws Exception { 672 super(); 673 674 ArrayList readers = aggrReader.getReaders(); 675 676 for (int rdrIdx = 0; rdrIdx < readers.size(); rdrIdx++) { 677 rangeProcessors.add( 678 RangeProcessor.createRangeProcessor( 679 (MultiDimensionReader)readers.get(rdrIdx), metadata)); 680 } 681 682 aggrReader.addRangeProcessor((String)metadata.get(SwathAdapter.array_name), this); 683 } 684 685 public synchronized void setIndex(int index) { 686 rngIdx = index; 687 } 688 689 public synchronized float[] processRange(byte[] values, HashMap subset) { 690 return rangeProcessors.get(rngIdx).processRange(values, subset); 691 } 692 693 public synchronized float[] processRange(short[] values, HashMap subset) { 694 return rangeProcessors.get(rngIdx).processRange(values, subset); 695 } 696 697 public synchronized float[] processRange(float[] values, HashMap subset) { 698 return rangeProcessors.get(rngIdx).processRange(values, subset); 699 } 700 701 public synchronized double[] processRange(double[] values, HashMap subset) { 702 return rangeProcessors.get(rngIdx).processRange(values, subset); 703 } 704}