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