001/*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2024
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 https://www.gnu.org/licenses/.
027 */
028package edu.wisc.ssec.mcidasv.data.hydra;
029
030import visad.*;
031
032public class HistogramField {
033
034    Linear2DSet histSet;
035    Linear1DSet set0;
036    Linear1DSet set1;
037    int len0;
038    int len1;
039    int[] count;
040    int[][] indexes;
041    FlatField field_0;
042    FlatField field_1;
043    FlatField mask_field;
044    float[][] maskRange;
045    Class rangeType;
046    byte[][] mask = new byte[3][];
047    byte[] order = new byte[3];
048
049    public FlatField scatterDensityField;
050
051    public HistogramField(FlatField field_0, FlatField field_1,
052            FlatField mask_field,
053            int n_bins, int bin_size)
054            throws Exception {
055        this.field_0 = field_0;
056        this.field_1 = field_1;
057        this.mask_field = mask_field;
058        maskRange = mask_field.getFloats(false);
059
060        mask[0] = new byte[maskRange[0].length];
061        mask[1] = new byte[maskRange[0].length];
062        mask[2] = new byte[maskRange[0].length];
063        java.util.Arrays.fill(mask[0], Byte.MAX_VALUE);
064        java.util.Arrays.fill(mask[1], Byte.MAX_VALUE);
065        java.util.Arrays.fill(mask[2], Byte.MAX_VALUE);
066        java.util.Arrays.fill(order, Byte.MAX_VALUE);
067
068        Set[] rangeSets = field_0.getRangeSets();
069        Set rngSet = rangeSets[0];
070
071        if (rngSet instanceof FloatSet) {
072            rangeType = Float.TYPE;
073        } else if (rngSet instanceof DoubleSet) {
074            rangeType = Double.TYPE;
075        } else if (rngSet instanceof IntegerSet) {
076            rangeType = Integer.TYPE;
077        }
078
079        double[] minmax_0 = {Double.MAX_VALUE, -Double.MAX_VALUE};
080        double[] minmax_1 = {Double.MAX_VALUE, -Double.MAX_VALUE};
081
082
083        if (rangeType == Integer.TYPE) {
084          //Ghansham: Dont do any allocation here. Do based on the individual ranges of fieldX and fieldY respectively
085        } else {
086            indexes = new int[n_bins * n_bins][1];
087            count = new int[n_bins * n_bins];
088        }
089
090
091        double[][] val = new double[2][1];
092        int[] histIdx = null;
093
094        if (rangeType == Double.TYPE) {
095            double[][] vals_0 = field_0.getValues(false);
096            double[][] vals_1 = field_1.getValues(false);
097            int n_samples = vals_0[0].length;
098            for (int k = 0; k < n_samples; k++) {
099                double v0 = vals_0[0][k];
100                if (v0 < minmax_0[0]) {
101                    minmax_0[0] = v0;
102                } else if (v0 > minmax_0[1]) {
103                    minmax_0[1] = v0;
104                }
105
106                double v1 = vals_1[0][k];
107                if (v1 < minmax_1[0]) {
108                    minmax_1[0] = v1;
109                } else if (v1 > minmax_1[1]) {
110                    minmax_1[1] = v1;
111                }
112            }
113
114            histSet = new Linear2DSet(minmax_0[0], minmax_0[1], n_bins,
115                    minmax_1[0], minmax_1[1], n_bins);
116
117            for (int k = 0; k < n_samples; k++) {
118                val[0][0] = vals_0[0][k];
119                val[1][0] = vals_1[0][k];
120                histIdx = histSet.doubleToIndex(val);
121                if (histIdx[0] >= 0) {
122                    int len = indexes[histIdx[0]].length;
123                    if (count[histIdx[0]] > len - 1) { //-grow array
124                        int[] tmp = new int[len + bin_size];
125                        System.arraycopy(indexes[histIdx[0]], 0, tmp, 0, len);
126                        indexes[histIdx[0]] = tmp;
127                    }
128                    indexes[histIdx[0]][count[histIdx[0]]++] = k;
129                }
130            }
131        } else if (rangeType == Float.TYPE) {
132            float[][] vals_0 = field_0.getFloats(false);
133            float[][] vals_1 = field_1.getFloats(false);
134            int n_samples = vals_0[0].length;
135            for (int k = 0; k < n_samples; k++) {
136                double v0 = vals_0[0][k];
137                if (v0 < minmax_0[0]) {
138                    minmax_0[0] = v0;
139                } else if (v0 > minmax_0[1]) {
140                    minmax_0[1] = v0;
141                }
142                double v1 = vals_1[0][k];
143                if (v1 < minmax_1[0]) {
144                    minmax_1[0] = v1;
145                } else if (v1 > minmax_1[1]) {
146                    minmax_1[1] = v1;
147                }
148
149            }
150            histSet = new Linear2DSet(minmax_0[0], minmax_0[1], n_bins, minmax_1[0], minmax_1[1], n_bins);
151            for (int k = 0; k < n_samples; k++) {
152                val[0][0] = vals_0[0][k];
153                val[1][0] = vals_1[0][k];
154                histIdx = histSet.doubleToIndex(val);
155                if (histIdx[0] >= 0) {
156                    int len = indexes[histIdx[0]].length;
157                    if (count[histIdx[0]] > len - 1) { //-grow array
158
159                        int[] tmp = new int[len + bin_size];
160                        System.arraycopy(indexes[histIdx[0]], 0, tmp, 0, len);
161                        indexes[histIdx[0]] = tmp;
162                    }
163                    indexes[histIdx[0]][count[histIdx[0]]++] = k;
164                }
165            }
166        } else if (rangeType == Integer.TYPE) {
167            float[][] vals_0 = field_0.getFloats(false);
168            float[][] vals_1 = field_1.getFloats(false);
169            int n_samples = vals_0[0].length;
170            for (int k = 0; k < n_samples; k++) {
171                double v0 = vals_0[0][k];
172                if (v0 < minmax_0[0]) {
173                    minmax_0[0] = v0;
174                } else if (v0 > minmax_0[1]) {
175                    minmax_0[1] = v0;
176                }
177                double v1 = vals_1[0][k];
178                if (v1 < minmax_1[0]) {
179                    minmax_1[0] = v1;
180                } else if (v1 > minmax_1[1]) {
181                    minmax_1[1] = v1;
182                }
183            }
184
185
186            int startX = (int) minmax_0[0];
187            int endX = (int) minmax_0[1];
188            int startY = (int) minmax_1[0];
189            int endY = (int) minmax_1[1];
190            int lenX = endX - startX + 1;
191            int lenY = endY - startY + 1;
192            histSet = new Linear2DSet(startX, endX, lenX, startY, endY, lenY);
193
194
195
196            //Ghansham:Allocate here based on length of lenghts of XField and YField
197            indexes = new int[lenY * lenX][]; //Ghansham:Dont allocate here if not required.
198            count = new int[lenY * lenX];
199
200            //First calculate frequency of each grey count.
201            for (int k = 0; k < n_samples; k++) {
202                val[0][0] = vals_0[0][k];
203                val[1][0] = vals_1[0][k];
204                histIdx = histSet.doubleToIndex(val);
205                if (histIdx[0] >= 0) {
206                    count[histIdx[0]]++;
207                }
208            }
209
210            for (int k = 0; k < n_samples; k++) {
211                val[0][0] = vals_0[0][k];
212                val[1][0] = vals_1[0][k];
213                histIdx = histSet.doubleToIndex(val);
214                if (histIdx[0] >= 0) {
215                    if (indexes[histIdx[0]] == null) { //Tricky stuff is here: encountering a particular grey count first time.
216                        indexes[histIdx[0]] = new int[count[histIdx[0]]]; //Allocating the values based on the frequency of this grey count (calculate earlier). No extra allocation at all
217                        count[histIdx[0]] = 0;  //Resetting the frequency to 0.
218                    }
219                    indexes[histIdx[0]][count[histIdx[0]]++] = k;
220                }
221            }
222        }
223
224
225        set0 = histSet.getLinear1DComponent(0);
226        set1 = histSet.getLinear1DComponent(1);
227        len0 = set0.getLength();
228        len1 = set1.getLength();
229
230
231        Linear2DSet dSet = (Linear2DSet) histSet.changeMathType(new RealTupleType(RealType.XAxis, RealType.YAxis));
232        scatterDensityField = new FlatField(
233            new FunctionType(((SetType)dSet.getType()).getDomain(), RealType.getRealType("ScatterDensity")), dSet);
234        float[][] fltCount = new float[1][count.length];
235        for (int i=0; i<count.length; i++) { 
236            fltCount[0][i] = (float) count[i];
237            if (count[i] == 0) {
238               fltCount[0][i] = Float.NaN;
239            }
240            else {
241               fltCount[0][i] = (float) java.lang.Math.log((double)fltCount[0][i]);
242            }
243        }
244        scatterDensityField.setSamples(fltCount);
245    }
246
247    public FlatField getScatterDensityField() {
248        return scatterDensityField;
249    }
250
251    public void markMaskFieldByRange(double[] lowhi_0, double[] lowhi_1, float maskVal)
252            throws Exception {
253        reorder((byte)maskVal);
254
255        int[] hist0 = set0.doubleToIndex(new double[][] {{lowhi_0[0], lowhi_0[1]}});
256        int[] hist1 = set1.doubleToIndex(new double[][] {{lowhi_1[0], lowhi_1[1]}});
257
258        if (hist0[0] < 0) {
259            if (lowhi_0[0] < lowhi_0[1]) {
260                hist0[0] = 0;
261            } else {
262                hist0[0] = len0 - 1;
263            }
264        }
265        if (hist0[1] < 0) {
266            if (lowhi_0[0] < lowhi_0[1]) {
267                hist0[1] = len0 - 1;
268            } else {
269                hist0[1] = 0;
270            }
271        }
272
273        if (hist1[0] < 0) {
274            if (lowhi_1[0] < lowhi_1[1]) {
275                hist1[0] = 0;
276            } else {
277                hist1[0] = len1 - 1;
278            }
279        }
280        if (hist1[1] < 0) {
281            if (lowhi_1[0] < lowhi_1[1]) {
282                hist1[1] = len1 - 1;
283            } else {
284                hist1[1] = 0;
285            }
286        }
287
288        int h00, h01, h10, h11;
289
290
291        h10 = hist1[1];
292        h11 = hist1[0];
293        if (hist1[0] < hist1[1]) {
294            h10 = hist1[0];
295            h11 = hist1[1];
296        }
297
298        h00 = hist0[1];
299        h01 = hist0[0];
300        if (hist0[0] < hist0[1]) {
301            h00 = hist0[0];
302            h01 = hist0[1];
303        }
304
305        for (int k = 0; k < maskRange[0].length; k++) {
306            if (maskRange[0][k] == maskVal) {
307                maskRange[0][k] = Float.NaN;
308                mask[(byte)maskVal][k] = Byte.MAX_VALUE;
309            }
310        }
311
312        int lenX = set0.getLengthX();
313
314        for (int j = h10; j <= h11; j++) {
315            int col_factor = j * lenX;
316            for (int i = h00; i <= h01; i++) {
317                int idx = col_factor + i;
318                for (int k = 0; k < count[idx]; k++) {
319                    maskRange[0][indexes[idx][k]] = maskVal;
320                    mask[(byte)maskVal][indexes[idx][k]] = (byte)maskVal;
321                }
322            }
323        }
324
325        mask_field.setSamples(maskRange, false);
326    }
327
328    public void markMaskFieldByCurve(float[][] curve, float maskVal) throws Exception {
329        reorder((byte)maskVal);
330        float[][] samples0 = set0.getSamples();
331        float[][] samples1 = set1.getSamples();
332
333        boolean[][] checked = null;
334        boolean[][] inside = null;
335
336
337        if (rangeType == Integer.TYPE) { //Dealing with rangeSet constructed fields separately.
338            float[][] vals_0 = field_0.getFloats(false);
339            float[][] vals_1 = field_1.getFloats(false);
340            int lenX = set0.getLength();
341            int lenY = set1.getLength();
342            checked = new boolean[lenX][lenY];
343            inside = new boolean[lenX][lenY];
344            for (int jj = 0; jj < lenX; jj++) {
345                java.util.Arrays.fill(checked[jj], false);
346                java.util.Arrays.fill(inside[jj], false);
347            }
348            for (int jj = 0; jj < lenY - 1; jj++) {
349                for (int ii = 0; ii < lenX - 1; ii++) {
350                    int idx = jj * lenX + ii; //Calclualting the index value in the start only.
351                    if (count[idx] > 0) { //No need to do go further if the frequency of particular grey count is zero.
352                        int inside_cnt = 0;
353                        if (!checked[ii][jj]) {
354                            float x = samples0[0][ii];
355                            float y = samples1[0][jj];
356                            if (DelaunayCustom.inside(curve, x, y)) {
357                                inside_cnt++;
358                                inside[ii][jj] = true;
359                            }
360                            checked[ii][jj] = true;
361                        } else if (inside[ii][jj]) {
362                            inside_cnt++;
363                        }
364
365                        if (!checked[ii + 1][jj]) {
366                            float x = samples0[0][ii + 1];
367                            float y = samples1[0][jj];
368                            if (DelaunayCustom.inside(curve, x, y)) {
369                                inside_cnt++;
370                                inside[ii + 1][jj] = true;
371                            }
372                            checked[ii + 1][jj] = true;
373                        } else if (inside[ii + 1][jj]) {
374                            inside_cnt++;
375                        }
376
377                        if (!checked[ii][jj + 1]) {
378                            float x = samples0[0][ii];
379                            float y = samples1[0][jj + 1];
380                            if (DelaunayCustom.inside(curve, x, y)) {
381                                inside_cnt++;
382                                inside[ii][jj + 1] = true;
383                            }
384                            checked[ii][jj + 1] = true;
385                        } else if (inside[ii][jj + 1]) {
386                            inside_cnt++;
387                        }
388
389                        if (!checked[ii + 1][jj + 1]) {
390                            float x = samples0[0][ii + 1];
391                            float y = samples1[0][jj + 1];
392                            if (DelaunayCustom.inside(curve, x, y)) {
393                                inside_cnt++;
394                                inside[ii + 1][jj + 1] = true;
395                            }
396                            checked[ii + 1][jj + 1] = true;
397                        } else if (inside[ii + 1][jj + 1]) {
398                            inside_cnt++;
399                        }
400
401                        if (inside_cnt == 0) {
402                            continue;
403                        } else if (inside_cnt == 4) {
404                            for (int k = 0; k < count[idx]; k++) {
405                                maskRange[0][indexes[idx][k]] = maskVal;
406                            }
407                        } else if (inside_cnt > 0 && inside_cnt < 4) {
408                            for (int k = 0; k < count[idx]; k++) {
409                                float xx = vals_0[0][indexes[idx][k]];
410                                float yy = vals_1[0][indexes[idx][k]];
411                                if (DelaunayCustom.inside(curve, xx, yy)) {
412                                    maskRange[0][indexes[idx][k]] = maskVal;
413                                }
414                            }
415                        }
416                    }
417                }
418            }
419        } else {
420            int len = set0.getLength();
421            checked = new boolean[len][len];
422            inside = new boolean[len][len];
423            for (int jj = 0; jj < len; jj++) {
424                java.util.Arrays.fill(checked[jj], false);
425                java.util.Arrays.fill(inside[jj], false);
426            }
427            for (int jj = 0; jj < len - 1; jj++) {
428                for (int ii = 0; ii < len - 1; ii++) {
429                    int idx = jj * set0.getLengthX() + ii; //Calclualting the index value in the start only.
430                    if (count[idx] > 0) { //No need to do go further if the frequency of particular value is zero.
431                        int inside_cnt = 0;
432                        if (!checked[ii][jj]) {
433                            float x = samples0[0][ii];
434                            float y = samples1[0][jj];
435                            if (DelaunayCustom.inside(curve, x, y)) {
436                                inside_cnt++;
437                                inside[ii][jj] = true;
438                            }
439                            checked[ii][jj] = true;
440                        } else if (inside[ii][jj]) {
441                            inside_cnt++;
442                        }
443
444                        if (!checked[ii + 1][jj]) {
445                            float x = samples0[0][ii + 1];
446                            float y = samples1[0][jj];
447                            if (DelaunayCustom.inside(curve, x, y)) {
448                                inside_cnt++;
449                                inside[ii + 1][jj] = true;
450                            }
451                            checked[ii + 1][jj] = true;
452                        } else if (inside[ii + 1][jj]) {
453                            inside_cnt++;
454                        }
455
456                        if (!checked[ii][jj + 1]) {
457                            float x = samples0[0][ii];
458                            float y = samples1[0][jj + 1];
459                            if (DelaunayCustom.inside(curve, x, y)) {
460                                inside_cnt++;
461                                inside[ii][jj + 1] = true;
462                            }
463                            checked[ii][jj + 1] = true;
464                        } else if (inside[ii][jj + 1]) {
465                            inside_cnt++;
466                        }
467
468                        if (!checked[ii + 1][jj + 1]) {
469                            float x = samples0[0][ii + 1];
470                            float y = samples1[0][jj + 1];
471                            if (DelaunayCustom.inside(curve, x, y)) {
472                                inside_cnt++;
473                                inside[ii + 1][jj + 1] = true;
474                            }
475                            checked[ii + 1][jj + 1] = true;
476                        } else if (inside[ii + 1][jj + 1]) {
477                            inside_cnt++;
478                        }
479                        if (inside_cnt == 0) {
480                            continue;
481                        }
482
483                        if (rangeType == Float.TYPE) {
484                            float[][] vals_0 = field_0.getFloats(false);
485                            float[][] vals_1 = field_1.getFloats(false);
486                            if (inside_cnt == 4) {
487
488                                for (int k = 0; k < count[idx]; k++) {
489                                    maskRange[0][indexes[idx][k]] = maskVal;
490                                    mask[(byte)maskVal][indexes[idx][k]] = (byte)maskVal;
491                                }
492                            }
493                            if (inside_cnt > 0 && inside_cnt < 4) {
494
495                                for (int k = 0; k < count[idx]; k++) {
496                                    float xx = vals_0[0][indexes[idx][k]];
497                                    float yy = vals_1[0][indexes[idx][k]];
498                                    if (DelaunayCustom.inside(curve, xx, yy)) {
499                                        maskRange[0][indexes[idx][k]] = maskVal;
500                                        mask[(byte)maskVal][indexes[idx][k]] = (byte)maskVal;
501                                    }
502                                }
503                            }
504                        } else if (rangeType == Double.TYPE) {
505                            double[][] vals_0 = field_0.getValues(false);
506                            double[][] vals_1 = field_1.getValues(false);
507                            if (inside_cnt == 4) {
508                                for (int k = 0; k < count[idx]; k++) {
509                                    maskRange[0][indexes[idx][k]] = maskVal;
510                                    mask[(byte)maskVal][indexes[idx][k]] = (byte)maskVal;
511                                }
512                            }
513                            if (inside_cnt > 0 && inside_cnt < 4) {
514
515                                for (int k = 0; k < count[idx]; k++) {
516                                    double xx = vals_0[0][indexes[idx][k]];
517                                    double yy = vals_1[0][indexes[idx][k]];
518                                    if (DelaunayCustom.inside(curve, (float) xx, (float) yy)) {
519                                        maskRange[0][indexes[idx][k]] = maskVal;
520                                        mask[(byte)maskVal][indexes[idx][k]] = (byte)maskVal;
521                                    }
522                                }
523                            }
524                        }
525                    }
526                }
527            }
528        }
529
530        mask_field.setSamples(maskRange, false);
531    }
532
533    private void reorder(byte maskVal) {
534       order[2] = order[1];
535       order[1] = order[0];
536       order[0] = maskVal;
537    }
538
539    public void clearMaskField(float maskVal) {
540        for (int k = 0; k < maskRange[0].length; k++) {
541            maskRange[0][k] = Float.NaN;
542            mask[(byte)maskVal][k] = Byte.MAX_VALUE;
543        }
544
545        for (int t=0; t<order.length; t++) {
546           if (order[t] == (byte)maskVal) {
547               order[t] = Byte.MAX_VALUE;
548           }
549        }
550
551        for (int t=order.length-1; t >=0; t--) {
552            if (order[t] != Byte.MAX_VALUE) {
553               for (int k=0; k<maskRange[0].length; k++) {
554                   if (mask[order[t]][k] != Byte.MAX_VALUE) {
555                      maskRange[0][k] = (float) order[t];
556                   }
557               }
558            }
559        }
560    }
561
562    public void resetMaskField(float maskVal) throws Exception {
563        clearMaskField(maskVal);
564        mask_field.setSamples(maskRange, false);
565    }
566}