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}