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 java.rmi.RemoteException;
031import java.util.Arrays;
032
033import visad.CoordinateSystem;
034import visad.FlatField;
035import visad.FunctionType;
036import visad.Linear2DSet;
037import visad.RealTupleType;
038import visad.RealType;
039import visad.SetType;
040import visad.VisADException;
041import visad.util.ThreadManager;
042
043public class ReprojectSwath {
044  private static int count = 0;
045
046  Linear2DSet grid;
047  Linear2DSet swathDomain;
048  FunctionType ftype;
049  float[][] swathRange;
050  CoordinateSystem swathCoordSys;
051  CoordinateSystem gridCoordSys;
052
053  float[][] allSwathGridCoords;
054  int[] allSwathGridIndexs;
055  float[][] swathGridCoord;
056  int[] swathIndexAtGrid;
057
058  int trackLen;
059  int xtrackLen;
060
061  int gridXLen;
062  int gridYLen;
063  int gridLen;
064
065  float[][] gridRange;
066  int rngTupDim;
067  FlatField grdFF;
068
069  int[][][] quads;
070  int mode;
071  
072  public static final int NEAREST = 1;
073  public static final int BILINEAR_VISAD = 0;
074  
075  int numProc = Runtime.getRuntime().availableProcessors();
076  private static boolean doParallel = false;
077
078  private static ReprojectSwath lastReproject = null;
079
080  public static void setDoParallel(boolean enable) {
081     doParallel = enable;
082  }
083  
084  public static FlatField swathToGrid(Linear2DSet grid, FlatField[] swaths, int mode) throws Exception {
085     return swathToGrid(grid, swaths, mode, true);
086  }
087  
088  public static FlatField swathToGrid(Linear2DSet grid, FlatField[] swaths, int mode, boolean filter) throws Exception {
089    FunctionType ftype = (FunctionType) swaths[0].getType();
090    visad.Set domSet = swaths[0].getDomainSet();
091
092    FlatField swath = new FlatField(new FunctionType(ftype.getDomain(),
093        new RealTupleType(new RealType[] 
094           {RealType.getRealType("redimage_"+count), RealType.getRealType("greenimage_"+count), RealType.getRealType("blueimage_"+count)})), domSet);
095
096    swath.setSamples(new float[][]
097        {swaths[0].getFloats(false)[0], swaths[1].getFloats(false)[0], swaths[2].getFloats(false)[0]});
098
099    count++;
100
101    return swathToGrid(grid, swath, mode);
102  }
103  
104  public static FlatField swathToGrid(Linear2DSet grid, FlatField swath, int mode) throws Exception {
105      return swathToGrid(grid, swath, mode, true);
106  }
107  
108  public static FlatField swathToGrid(Linear2DSet grid, FlatField swath, int mode, boolean filter) throws Exception {
109    ReprojectSwath obj = null;
110    FlatField ff = null;
111
112    if (lastReproject != null) {
113       if (grid.equals(lastReproject.grid) && (swath.getDomainSet()).equals(lastReproject.swathDomain)) {
114         obj = lastReproject;
115         ff = obj.reproject(swath, mode, filter);
116       }
117       else {
118         obj = new ReprojectSwath(grid, swath);
119         ff = obj.reproject(mode, filter);
120       }
121    }
122    else {
123      obj = new ReprojectSwath(grid, swath);
124      ff = obj.reproject(mode, filter);
125    }
126    lastReproject = obj;
127
128    return ff;
129  }
130  
131  public ReprojectSwath() {
132  }
133  
134  public ReprojectSwath(Linear2DSet grid, FlatField swath) throws Exception {
135      
136    init(grid, swath);
137    
138    if (trackLen < 200 || doParallel == false) {
139        numProc = 1;
140    }
141
142    projectSwathToGrid();
143  }
144  
145  private void init(Linear2DSet grid, FlatField swath) throws VisADException {
146     this.grid = grid;
147     gridLen = grid.getLength();
148     int[] lens = grid.getLengths();
149     gridXLen = lens[0];
150     gridYLen = lens[1];
151     gridCoordSys = grid.getCoordinateSystem();
152     
153     swathDomain = (Linear2DSet) swath.getDomainSet();
154     lens = swathDomain.getLengths();
155     trackLen = lens[1];
156     xtrackLen = lens[0];
157     int swathLen = trackLen*xtrackLen;
158     swathCoordSys = swathDomain.getCoordinateSystem();
159     swathRange = swath.getFloats(false);
160     ftype = (FunctionType) swath.getType();
161    
162     allSwathGridCoords = new float[2][swathLen];
163     allSwathGridIndexs = new int[swathLen];
164     swathGridCoord = new float[2][gridLen];
165     swathIndexAtGrid = new int[gridLen];
166    
167     quads = new int[gridYLen][gridXLen][4];
168   }
169
170  /*
171  public FlatField reproject(Linear2DSet grid, FlatField swath, int mode, boolean filter) throws Exception {
172    init();
173    
174    if (trackLen < 200 || doParallel == false) {
175        numProc = 1;
176    }
177  
178    return reproject(mode, filter);
179  }
180  */
181    
182  public FlatField reproject(int mode, boolean filter) throws Exception {
183    this.mode = mode;
184    
185    initGrid();
186
187    getBoundingQuadAtGridPts();
188
189    interpolateToGrid();
190    
191    if (filter) {
192       grdFF.setSamples(filter(), false);
193    }
194    else {
195       grdFF.setSamples(gridRange, false);
196    }
197    
198    return grdFF;
199  }
200
201  private FlatField reproject(FlatField swath, int mode, boolean filter) throws Exception {
202     this.mode = mode;
203     swathRange = swath.getFloats(false);
204     ftype = (FunctionType) swath.getType();
205     
206     initGrid();
207     
208     getBoundingQuadAtGridPts();
209     
210     interpolateToGrid();
211     
212     if (filter) {
213       grdFF.setSamples(filter(), false);
214     }
215     else {
216       grdFF.setSamples(gridRange, false);
217     }
218     
219     return grdFF;
220  }
221  
222   private void getBoundingQuadAtGridPts() throws VisADException, RemoteException {
223    int ystart = 3;
224    int ystop = gridYLen-4;
225    int subLen = ((ystop - ystart)+1)/numProc;
226    int rem = ((ystop - ystart)+1) % numProc;
227    
228    
229    ThreadManager threadManager = new ThreadManager("getBoundingQuadAtGridPts");
230    for (int i=0; i<numProc; i++) {
231        final int start = i*subLen + ystart;
232        final int stop = (i != numProc-1 ) ? (start + subLen - 1): (start + subLen + rem - 1);
233          threadManager.addRunnable(new ThreadManager.MyRunnable() {
234                  public void run()  throws Exception {
235                     getBoundingQuadAtGridPts(start, stop);
236                  }
237              });
238    }
239    
240    if (numProc == 1 || !doParallel) {
241       threadManager.runSequentially();
242    }
243    else {
244       threadManager.runAllParallel();
245    }
246  }
247
248  // start to stop inclusive
249  private void getBoundingQuadAtGridPts(int grdYstart, int grdYstop) {
250
251    for (int j=grdYstart; j<=grdYstop; j++) {
252       for (int i=3; i<gridXLen-3; i++) {
253          int grdIdx = i + j*gridXLen;
254
255          int ll = findSwathGridLoc(grdIdx, swathGridCoord, gridYLen, gridXLen, "LL");
256          quads[j][i][0] = ll;
257
258          int lr = findSwathGridLoc(grdIdx, swathGridCoord, gridYLen, gridXLen, "LR");
259          quads[j][i][1] = lr;
260
261          int ul = findSwathGridLoc(grdIdx, swathGridCoord, gridYLen, gridXLen, "UL");
262          quads[j][i][2] = ul;
263
264          int ur = findSwathGridLoc(grdIdx, swathGridCoord, gridYLen, gridXLen, "UR");
265          quads[j][i][3] = ur;
266       }
267    }
268  }
269  
270  public void interpolateToGrid() throws VisADException, RemoteException {
271    int ystart = 3;
272    int ystop = gridYLen-4;
273    int subLen = ((ystop - ystart)+1)/numProc;
274    int rem = ((ystop - ystart)+1) % numProc;
275    
276    ThreadManager threadManager = new ThreadManager("interpolateToGrid");
277    for (int i=0; i<numProc; i++) {
278        final int start = i*subLen + ystart;
279        final int stop = (i != numProc-1 ) ? (start + subLen - 1): (start + subLen + rem - 1);
280          threadManager.addRunnable(new ThreadManager.MyRunnable() {
281                  public void run()  throws Exception {
282                     interpolateToGrid(start, stop);
283                  }
284              });
285    }
286    
287    if (numProc == 1 || !doParallel) {
288       threadManager.runSequentially();
289    }
290    else {
291       threadManager.runAllParallel();
292    }
293  }
294
295  // start to stop inclusive
296  public void interpolateToGrid(int grdYstart, int grdYstop) throws VisADException, RemoteException {
297
298    float[][] corners = new float[2][4];
299    float[][] rngVals = new float[rngTupDim][4];
300    float[] values = new float[4];
301    float gx;
302    float gy;
303
304    for (int j=grdYstart; j<=grdYstop; j++) {
305       for (int i=3; i<gridXLen-3; i++) {
306          int grdIdx = i + j*gridXLen;
307          gx = (float) (grdIdx % gridXLen);
308          gy = (float) (grdIdx / gridXLen);
309
310          java.util.Arrays.fill(corners[0], Float.NaN);
311          java.util.Arrays.fill(corners[1], Float.NaN);
312        
313          int ll = quads[j][i][0];
314          int lr = quads[j][i][1];
315          int ul = quads[j][i][2];
316          int ur = quads[j][i][3];
317
318          if (ll >= 0) {
319             corners[0][0] = swathGridCoord[0][ll] - gx;
320             corners[1][0] = swathGridCoord[1][ll] - gy;
321          }
322          if (lr >= 0) {
323             corners[0][1] = swathGridCoord[0][lr] - gx;
324             corners[1][1] = swathGridCoord[1][lr] - gy;
325          }
326          if (ul >= 0) {
327             corners[0][2] = swathGridCoord[0][ul] - gx;
328             corners[1][2] = swathGridCoord[1][ul] - gy;
329          }
330          if (ur >= 0) {
331             corners[0][3] = swathGridCoord[0][ur] - gx;
332             corners[1][3] = swathGridCoord[1][ur] - gy;
333          }
334
335          if (mode == NEAREST) { // Nearest neighbor
336             for (int t=0; t<rngTupDim; t++) {
337                java.util.Arrays.fill(values, Float.NaN);
338                if (ll >=0) {
339                   values[0] = swathRange[t][swathIndexAtGrid[ll]];
340                }
341                if (lr >= 0) {
342                   values[1] = swathRange[t][swathIndexAtGrid[lr]];
343                }
344                if (ul >= 0) {
345                   values[2] = swathRange[t][swathIndexAtGrid[ul]];
346                }
347                if (ur >= 0) {
348                   values[3] = swathRange[t][swathIndexAtGrid[ur]];
349                }
350                float val = nearest(0f, 0f, corners, values);
351                gridRange[t][grdIdx] = val;
352             }
353          }
354          else if (mode == BILINEAR_VISAD) {  //from VisAD
355             if (!(ll >= 0 && lr >= 0 && ul >= 0 && ur >= 0)) {
356                continue;
357             }
358             corners[0][0] = swathGridCoord[0][ll];
359             corners[1][0] = swathGridCoord[1][ll];
360             corners[0][1] = swathGridCoord[0][lr];
361             corners[1][1] = swathGridCoord[1][lr];
362             corners[0][2] = swathGridCoord[0][ul];
363             corners[1][2] = swathGridCoord[1][ul];
364             corners[0][3] = swathGridCoord[0][ur];
365             corners[1][3] = swathGridCoord[1][ur];
366             for (int t=0; t<rngTupDim; t++) {
367                java.util.Arrays.fill(values, Float.NaN);
368                values[0] = swathRange[t][swathIndexAtGrid[ll]];
369                values[1] = swathRange[t][swathIndexAtGrid[lr]];
370                values[2] = swathRange[t][swathIndexAtGrid[ul]];
371                values[3] = swathRange[t][swathIndexAtGrid[ur]];
372                float val = visad2D(gy, gx, corners, values);
373                gridRange[t][grdIdx] = val;
374             }
375          }
376          else if (mode == 1) { //TODO: not working yet
377             if (!(ll >= 0 && lr >= 0 && ul >= 0 && ur >= 0)) {
378                continue;
379             }
380             gx -= swathGridCoord[0][ll];
381             gy -= swathGridCoord[1][ll];
382             corners[0][0] = 0f;
383             corners[1][0] = 0f;
384             corners[0][1] = swathGridCoord[0][ul] - swathGridCoord[0][ll];
385             corners[1][1] = swathGridCoord[1][ul] - swathGridCoord[1][ll];
386             corners[0][2] = swathGridCoord[0][ur] - swathGridCoord[0][ll];
387             corners[1][2] = swathGridCoord[1][ur] - swathGridCoord[1][ll];
388             corners[0][3] = swathGridCoord[0][lr] - swathGridCoord[0][ll];
389             corners[1][3] = swathGridCoord[1][lr] - swathGridCoord[1][ll];
390             for (int t=0; t<rngTupDim; t++) {
391                java.util.Arrays.fill(values, Float.NaN);
392                values[0] = swathRange[t][swathIndexAtGrid[ll]];
393                values[1] = swathRange[t][swathIndexAtGrid[ul]];
394                values[2] = swathRange[t][swathIndexAtGrid[ur]];
395                values[3] = swathRange[t][swathIndexAtGrid[lr]];
396                float val = biLinearIntrp(gy, gx, corners, values);
397                gridRange[t][grdIdx] = val;
398             }
399
400          }
401       }
402    }
403
404  }
405
406 public void projectSwathToGrid() throws VisADException, RemoteException {
407    int subLen = trackLen/numProc;
408    int rem = trackLen % numProc;
409    
410    ThreadManager threadManager = new ThreadManager("projectSwathToGrid");
411    for (int i=0; i<numProc; i++) {
412        final int start = i*subLen;
413        final int stop = (i != numProc-1 ) ? (start + subLen - 1): (start + subLen + rem - 1);
414          threadManager.addRunnable(new ThreadManager.MyRunnable() {
415                  public void run()  throws Exception {
416                     projectSwathToGrid(start, stop);
417                  }
418              });
419    }
420    
421    if (numProc == 1 || !doParallel) {
422       threadManager.runSequentially();
423    }
424    else {
425       threadManager.runAllParallel();
426    }
427 }
428 
429 public void projectSwathToGrid(int trackStart, int trackStop) throws VisADException, RemoteException {
430
431    for (int j=trackStart; j <= trackStop; j++) {
432       for (int i=0; i < xtrackLen; i++) {
433         int swathIdx = j*xtrackLen + i;
434
435         float[][] swathCoord = swathDomain.indexToValue(new int[] {swathIdx});
436         float[][] swathEarthCoord = swathCoordSys.toReference(swathCoord);
437
438         float[][] gridValue = gridCoordSys.fromReference(swathEarthCoord);
439         float[][] gridCoord = grid.valueToGrid(gridValue);
440         float g0 = gridCoord[0][0];
441         float g1 = gridCoord[1][0];
442         int grdIdx = (g0 != g0 || g1 != g1) ? -1 : ((int) g0) + gridXLen * ((int) g1);
443         int m=0;
444         int n=0;
445         int k = grdIdx + (m + n*gridXLen);
446         
447         allSwathGridCoords[0][swathIdx] = g0;
448         allSwathGridCoords[1][swathIdx] = g1;
449         allSwathGridIndexs[swathIdx] = k;
450             
451       }
452    }
453 }
454 
455 public void initGrid() throws VisADException {
456    Arrays.fill(swathGridCoord[0], -999.9f);
457    Arrays.fill(swathGridCoord[1], -999.9f);
458    Arrays.fill(swathIndexAtGrid, -1);
459
460    for (int j=0; j < trackLen; j++) {
461       for (int i=0; i < xtrackLen; i++) {
462         int swathIdx = j*xtrackLen + i;
463         float val = swathRange[0][swathIdx];
464         int k = allSwathGridIndexs[swathIdx];
465         
466         if ( !(Float.isNaN(val)) && ((k >=0) && (k < gridLen)) ) { // val or val[rngTupDim] ?
467            if (swathIndexAtGrid[k] == -1) {
468               swathGridCoord[0][k] = allSwathGridCoords[0][swathIdx];
469               swathGridCoord[1][k] = allSwathGridCoords[1][swathIdx];
470               swathIndexAtGrid[k] = swathIdx;
471            }
472         }
473       }
474    }
475    
476    for (int j=0; j<gridYLen; j++) {
477       for (int i=0; i<gridXLen; i++) {
478          java.util.Arrays.fill(quads[j][i], -1);
479       }
480    }
481    
482    RealTupleType rtt = ((SetType)grid.getType()).getDomain();
483    grdFF = new FlatField(new FunctionType(rtt, ftype.getRange()), grid);
484    gridRange = grdFF.getFloats(false);
485    rngTupDim = gridRange.length;
486    for (int t=0; t<rngTupDim; t++) {
487       java.util.Arrays.fill(gridRange[t], Float.NaN);
488    }
489 }
490
491 private float[][] filter() throws VisADException, RemoteException {
492
493    double mag = 3.0;
494    double sigma = 0.4;
495
496    float[][] weights = new float[3][3];
497
498    float sumWeights = 0f;
499    for (int n=-1; n<=1; n++) {
500       for (int m=-1; m<=1; m++) {
501          double del_0 = m;
502          double del_1 = n;
503          double dst = Math.sqrt(del_0*del_0 + del_1*del_1);
504
505          weights[n+1][m+1] = (float) (mag/Math.exp(dst/sigma));
506
507          sumWeights += weights[n+1][m+1];
508       }
509    }
510
511    for (int n=-1; n<=1; n++) {
512       for (int m=-1; m<=1; m++) {
513          weights[n+1][m+1] /= sumWeights;
514        }
515    }
516
517    float[][] newRange = new float[rngTupDim][gridLen];
518    for (int t=0; t<rngTupDim; t++) {
519       java.util.Arrays.fill(newRange[t], Float.NaN);
520    }
521    float[] sum = new float[rngTupDim];
522
523    for (int j=2; j<gridYLen-2; j++) {
524       for (int i=2; i<gridXLen-2; i++) {
525         int grdIdx = i + j*gridXLen;
526
527         java.util.Arrays.fill(sum, 0f);
528         for (int n=-1; n<=1; n++) {
529            for (int m=-1; m<=1; m++) {
530               int k = grdIdx + (m + n*gridXLen);
531
532               for (int t=0; t<rngTupDim; t++) {
533                  sum[t] += weights[n+1][m+1]*gridRange[t][k];
534               }
535            }
536         }
537
538         for (int t=0; t<rngTupDim; t++) {
539            newRange[t][grdIdx] = sum[t];
540         }
541       }
542    }
543
544    return newRange;
545 }
546 private static int findSwathGridLoc(int grdIdx, float[][] swathGridCoord, int gridYLen, int gridXLen, String which) {
547  
548    int idx = -1;
549
550    int gy = grdIdx/gridXLen;
551    int gx = grdIdx % gridXLen;
552    
553    int yu1 = (gy+1)*gridXLen;
554    int yu2 = (gy+2)*gridXLen;
555    int yu3 = (gy+3)*gridXLen;    
556    
557    int yd1 = (gy-1)*gridXLen;
558    int yd2 = (gy-2)*gridXLen;
559    int yd3 = (gy-3)*gridXLen;
560
561    switch (which) {
562       case "LL":
563
564          idx = yd1 + (gx-1);
565          if (swathGridCoord[0][idx] != -999.9f) {
566             break;
567          }
568          idx = yd2 + (gx-1);
569          if (swathGridCoord[0][idx] != -999.9f) {
570             break;
571          }
572          idx = yd1 + (gx-2);
573          if (swathGridCoord[0][idx] != -999.9f) {
574             break;
575          }
576          idx = yd2 + (gx-2);
577          if (swathGridCoord[0][idx] != -999.9f) {
578             break;
579          }
580
581
582          idx = yd2 + (gx-3);
583          if (swathGridCoord[0][idx] != -999.9f) {
584             break;
585          }
586          idx = yd3 + (gx-2);
587          if (swathGridCoord[0][idx] != -999.9f) {
588             break;
589          }
590          idx = yd3 + (gx-1);
591          if (swathGridCoord[0][idx] != -999.9f) {
592             break;
593          }
594          idx = yd1 + (gx-3);
595          if (swathGridCoord[0][idx] != -999.9f) {
596             break;
597          }
598          idx = yd3 + (gx-3);
599          if (swathGridCoord[0][idx] != -999.9f) {
600             break;
601          }
602
603          idx = -1;
604          break;
605       case "UL":
606          idx = (gy)*gridXLen + (gx-1);
607          if (swathGridCoord[0][idx] != -999.9f) {
608             break;
609          }
610          idx = (gy)*gridXLen + (gx-2);
611          if (swathGridCoord[0][idx] != -999.9f) {
612             break;
613          }
614          idx = yu1 + (gx-1);
615          if (swathGridCoord[0][idx] != -999.9f) {
616             break;
617          }
618          idx = yu1 + (gx-2);
619          if (swathGridCoord[0][idx] != -999.9f) {
620             break;
621          }
622
623          idx = yu1 + (gx-3);
624          if (swathGridCoord[0][idx] != -999.9f) {
625             break;
626          }
627          idx = yu2 + (gx-3);
628          if (swathGridCoord[0][idx] != -999.9f) {
629             break;
630          }
631          idx = yu2 + (gx-2);
632          if (swathGridCoord[0][idx] != -999.9f) {
633             break;
634          }
635          idx = (gy)*gridXLen + (gx-3);
636          if (swathGridCoord[0][idx] != -999.9f) {
637             break;
638          }
639          idx = yd1 + (gx-3);
640          if (swathGridCoord[0][idx] != -999.9f) {
641             break;
642          }
643
644          idx = -1;
645          break;
646       case "UR":
647          idx = (gy)*gridXLen + (gx);
648          if (swathGridCoord[0][idx] != -999.9f) {
649             break;
650          }
651          idx = yu1 + (gx);
652          if (swathGridCoord[0][idx] != -999.9f) {
653             break;
654          }
655          idx = (gy)*gridXLen + (gx+1);
656          if (swathGridCoord[0][idx] != -999.9f) {
657             break;
658          }
659          idx = yu1 + (gx+1);
660          if (swathGridCoord[0][idx] != -999.9f) {
661             break;
662          }
663
664          idx = yu2 + (gx+1);
665          if (swathGridCoord[0][idx] != -999.9f) {
666             break;
667          }
668          idx = yu1 + (gx+2);
669          if (swathGridCoord[0][idx] != -999.9f) {
670             break;
671          }
672          idx = yu2 + (gx+2);
673          if (swathGridCoord[0][idx] != -999.9f) {
674             break;
675          }
676          idx = (gy)*gridXLen + (gx+2);
677          if (swathGridCoord[0][idx] != -999.9f) {
678             break;
679          }
680          idx = yd1 + (gx+2);
681          if (swathGridCoord[0][idx] != -999.9f) {
682             break;
683          }
684
685          idx = -1;
686          break;
687       case "LR":
688          idx = yd1 + (gx);
689          if (swathGridCoord[0][idx] != -999.9f) {
690             break;
691          }
692          idx = yd1 + (gx+1);
693          if (swathGridCoord[0][idx] != -999.9f) {
694             break;
695          }
696          idx = yd2 + (gx);
697          if (swathGridCoord[0][idx] != -999.9f) {
698             break;
699          }
700          idx = yd2 + (gx+1);
701          if (swathGridCoord[0][idx] != -999.9f) {
702             break;
703          }
704
705          idx = yd1 + (gx+2);
706          if (swathGridCoord[0][idx] != -999.9f) {
707             break;
708          }
709          idx = yd2 + (gx+2);
710          if (swathGridCoord[0][idx] != -999.9f) {
711             break;
712          }
713          idx = yd3 + (gx);
714          if (swathGridCoord[0][idx] != -999.9f) {
715             break;
716          }
717          idx = yd3 + (gx+1);
718          if (swathGridCoord[0][idx] != -999.9f) {
719             break;
720          }
721          idx = yd3 + (gx+2);
722          if (swathGridCoord[0][idx] != -999.9f) {
723             break;
724          }
725 
726          idx = -1;
727          break;
728    }
729
730    return idx;
731 }
732
733 /* Reference: David W. Zingg, University of Toronto, Downsview, Ontario, Canada
734               Maurice Yarrow, Sterling Software, Arnes Research Center, Moffett Field, California. 
735               NASA Technical Memorandum 102213
736
737     y -> q, x -> p; first point (x=0, y=0) and clockwise around
738  */
739 public static float biLinearIntrp(float gy, float gx, float[][] corners, float[] values) {
740    // transform physical space (gy, gx) to unit square (q, p)
741    // bilinear mapping coefficients
742/*
743    float a = corners[0][3];
744    float b = corners[0][1];
745    float c = (corners[0][2] - corners[0][3] - corners[0][1]);
746
747    float d = corners[1][3];
748    float e = corners[1][1];
749    float f = (corners[1][2] - corners[1][3] - corners[1][1]);
750*/
751
752    float a = corners[0][1];
753    float b = corners[0][3];
754    float c = (corners[0][2] - corners[0][1] - corners[0][3]);
755
756    float d = corners[1][1];
757    float e = corners[1][3];
758    float f = (corners[1][2] - corners[1][1] - corners[1][3]);
759
760
761    // quadratic terms to determine p
762    // p = (-coef1 +/- sqrt(coef1^2 - 4*coef2*coef0))/2*coef2
763
764    float coef2 = (c*d - a*f);  // A 
765    float coef1 = (-c*gy + b*d + gx*f - a*e);  // B
766    float coef0 = (-gy*b + gx*e);  // C
767
768    // two p vals from quadratic:
769    float p0 =  (-coef1 + ((float)Math.sqrt(coef1*coef1 - 4*coef2*coef0)) )/2f*coef2;
770    float p1 =  (-coef1 - ((float)Math.sqrt(coef1*coef1 - 4*coef2*coef0)) )/2f*coef2;
771
772    // the corresponding q values for both p solutions:
773    float q0 = (gx - a*p0)/(b + c*p0);
774    float q1 = (gx - a*p1)/(b + c*p1);
775
776    // test  which point to use. One will be outside unit square:
777    float p = Float.NaN;
778    float q = Float.NaN;
779    if ((p0 >= 0f && p0 <= 1f) && (q0 >= 0f && q0 <= 1f)) {
780       p = p0;
781       q = q0;
782    }
783    else if ((p1 >= 0f && p1 <= 1f) && (q1 >= 0f && q1 <= 1f)) {
784       p = p1;
785       q = q1;
786    }
787
788    // bilinear interpolation within the unit square:
789
790    float intrp = values[0]*(1f-p)*(1f-q) + values[1]*(1f-p)*q + values[2]*p*q + values[3]*p*(1f-q);
791
792    return intrp;
793 }
794
795 private static float nearest(float gy, float gx, float[][] corners, float[] values) {
796   float minDist = Float.MAX_VALUE;
797
798   float delx;
799   float dely;
800   int closest = 0;
801   for (int k=0; k<4; k++) {
802      delx = corners[0][k] - gx;
803      dely = corners[1][k] - gy;
804      float dst_sqrd = delx*delx + dely*dely;
805
806      if (dst_sqrd < minDist) {
807         minDist = dst_sqrd;
808         closest = k;
809      }
810   }
811
812   return values[closest];
813 }
814 public static float visad2D(float gy, float gx, float[][] corners, float[] values) {
815    boolean Pos = true;
816
817    // A:0, B:1, C:2, D:3
818    float v0x = corners[0][0];
819    float v0y = corners[1][0];
820    float v1x = corners[0][1];
821    float v1y = corners[1][1];
822    float v2x = corners[0][2];
823    float v2y = corners[1][2];
824    float v3x = corners[0][3];
825    float v3y = corners[1][3];
826
827    float bdx = v2x-v1x;
828    float bdy = v2y-v1y;
829
830    float bpx = gx-v1x;
831    float bpy = gy-v1y;
832
833    float dpx = gx-v2x;
834    float dpy = gy-v2y;
835
836    // lower triangle first
837
838    boolean insideTri = true;
839
840    float abx = v1x-v0x;
841    float aby = v1y-v0y;
842    float dax = v0x-v2x;
843    float day = v0y-v2y;
844    float apx = gx-v0x;
845    float apy = gy-v0y;
846    
847    float tval1 = abx*apy-aby*apx;
848    float tval2 = bdx*bpy-bdy*bpx;
849    float tval3 = dax*dpy-day*dpx;
850    boolean test1 = (tval1 == 0) || ((tval1 > 0) == Pos);
851    boolean test2 = (tval2 == 0) || ((tval2 > 0) == Pos);
852    boolean test3 = (tval3 == 0) || ((tval3 > 0) == Pos);
853
854    if (!test1 && !test2) {      // Go UP & RIGHT
855       insideTri = false;
856    }
857    else if (!test2 && !test3) { // Go DOWN & LEFT
858       insideTri = false;
859    }
860    else if (!test1 && !test3) { // Go UP & LEFT
861       insideTri = false;
862    }
863    else if (!test1) {           // Go UP
864       insideTri = false;
865    }
866    else if (!test3) {           // Go LEFT
867       insideTri = false;
868    }
869
870    insideTri = (insideTri && test2);
871 
872    float gxx = Float.NaN;
873    float gyy = Float.NaN;
874
875    if (insideTri) {
876       gxx = ((gx-v0x)*(v2y-v0y)
877                        + (v0y-gy)*(v2x-v0x))
878                       / ((v1x-v0x)*(v2y-v0y)
879                        + (v0y-v1y)*(v2x-v0x));
880
881       gyy =  ((gx-v0x)*(v1y-v0y)
882                        + (v0y-gy)*(v1x-v0x))
883                       / ((v2x-v0x)*(v1y-v0y)
884                        + (v0y-v2y)*(v1x-v0x)); 
885    }
886    else {
887       insideTri = true;
888
889       float bcx = v3x-v1x;
890       float bcy = v3y-v1y;
891       float cdx = v2x-v3x;
892       float cdy = v2y-v3y;
893       float cpx = gx-v3x;
894       float cpy = gy-v3y;
895
896       tval1 = bcx*bpy-bcy*bpx;
897       tval2 = cdx*cpy-cdy*cpx;
898       tval3 = bdx*dpy-bdy*dpx;
899       test1 = (tval1 == 0) || ((tval1 > 0) == Pos);
900       test2 = (tval2 == 0) || ((tval2 > 0) == Pos);
901       test3 = (tval3 == 0) || ((tval3 < 0) == Pos);
902       if (!test1 && !test3) {      // Go UP & RIGHT
903          insideTri = false;
904       }
905       else if (!test2 && !test3) { // Go DOWN & LEFT
906          insideTri = false;
907       }
908       else if (!test1 && !test2) { // Go DOWN & RIGHT
909          insideTri = false;
910       }
911       else if (!test1) {           // Go RIGHT
912          insideTri = false;
913       }
914       else if (!test2) {           // Go DOWN
915          insideTri = false;
916       }
917       insideTri = (insideTri && test3);
918
919       if (insideTri) {
920            // Found correct grid triangle
921            // Solve the point with the reverse interpolation
922            gxx = ((v3x-gx)*(v1y-v3y)
923                        + (gy-v3y)*(v1x-v3x))
924                       / ((v2x-v3x)*(v1y-v3y)
925                        - (v2y-v3y)*(v1x-v3x)) + 1;
926            gyy = ((v2y-v3y)*(v3x-gx)
927                        + (v2x-v3x)*(gy-v3y))
928                       / ((v1x-v3x)*(v2y-v3y)
929                        - (v2x-v3x)*(v1y-v3y)) + 1;
930       }
931
932    }
933
934    // bilinear interpolation within the unit square:
935
936    float intrp = values[0]*(1f-gxx)*(1f-gyy) + values[2]*(1f-gxx)*gyy + values[3]*gxx*gyy + values[1]*gxx*(1f-gyy);
937
938
939    return intrp;
940 }
941
942}