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 }