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 */
028
029package edu.wisc.ssec.mcidasv.data;
030
031import edu.wisc.ssec.mcidasv.data.hydra.MyRubberBandBoxRendererJ3D;
032import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox;
033//import edu.wisc.ssec.mcidasv.data.hydra.DataToDisplayCoordinateSystem;
034
035import ucar.unidata.view.geoloc.MapProjectionDisplay;
036import ucar.visad.display.Displayable;
037import ucar.visad.display.DisplayMaster;
038import ucar.visad.display.LineDrawing;
039
040import visad.*;
041import visad.bom.*;
042import visad.georef.MapProjection;
043
044import java.rmi.RemoteException;
045
046import java.awt.event.InputEvent;
047
048
049public class GeoSubsetRubberBandBox extends SubsetRubberBandBox {
050
051    /** x type for the box */
052    private RealType xType;
053
054    /** y type for the box */
055    private RealType yType;
056
057    /** renderer */
058    private MyRubberBandBoxRendererJ3D rubberBandBox;
059
060    /** bounds defined by the rubber band box */
061    private Gridded2DSet bounds;
062
063    /** mouse event mask */
064    private int mask;
065
066    private FlatField data;
067    private boolean isLL;
068    private boolean lastBoxOn;
069
070    private CoordinateSystem dataCS;
071
072    private CoordinateSystem displayCS;
073    private DisplayMaster dispMaster;
074
075    private GeoDataToDisplayCoordinateSystem new_cs;
076
077    private static int count = 0;
078
079    /**
080     * Construct a RubberBandBox using xType as the X coordinate and
081     * yType as the Y coordinate of the box.
082     *
083     * @param data
084     * @param displayCS
085     *
086     * @throws VisADException VisAD error
087     * @throws RemoteException Remote error
088     */
089    public GeoSubsetRubberBandBox(FlatField data, CoordinateSystem displayCS)
090            throws VisADException, RemoteException {
091        this(false, data, displayCS, 0);
092    }
093
094    public GeoSubsetRubberBandBox(FlatField data, CoordinateSystem displayCS, int mask)
095            throws VisADException, RemoteException {
096        this(false, data, displayCS, mask);
097    }
098
099    public GeoSubsetRubberBandBox(boolean isLL, FlatField data, CoordinateSystem displayCS, int mask)
100            throws VisADException, RemoteException {
101        this(isLL, data, displayCS, mask, true);
102    }
103
104    public GeoSubsetRubberBandBox(FlatField data, CoordinateSystem displayCS, int mask, boolean lastBoxOn)
105            throws VisADException, RemoteException {
106        this(false, data, displayCS, mask, lastBoxOn);
107    }
108
109    /**
110     * Construct a RubberBandBox using xType as the X coordinate and
111     * yType as the Y coordinate of the box.
112     *
113     * @param isLL
114     * @param data
115     * @param displayCS
116     * @param mask Key mask to use for rubberbanding
117     * @param lastBoxOn
118     *
119     * @throws VisADException   VisAD error
120     * @throws RemoteException   Remote error
121     */
122    public GeoSubsetRubberBandBox(boolean isLL, FlatField data, CoordinateSystem displayCS, int mask, boolean lastBoxOn)
123            throws VisADException, RemoteException {
124        super(isLL, data, displayCS, mask, lastBoxOn);
125
126        this.data = data;
127        this.displayCS = displayCS;
128        this.isLL = isLL;
129        this.lastBoxOn =  lastBoxOn;
130
131        RealTupleType rtype = ((FunctionType)data.getType()).getDomain();
132        dataCS = rtype.getCoordinateSystem();
133        if (dataCS == null) {
134          dataCS = new GridCoordinateSystem((GriddedSet)data.getDomainSet());
135        }
136
137        IdentityCoordinateSystem iCS =
138             new IdentityCoordinateSystem(
139                   new RealTupleType(new RealType[] {RealType.getRealType("ZZtop")}));
140
141        CoordinateSystem cs =
142             new CartesianProductCoordinateSystem(new CoordinateSystem[] {dataCS, iCS});
143
144        new_cs = new GeoDataToDisplayCoordinateSystem(isLL, cs, displayCS);
145        resetExtremes();
146
147        DisplayRealType displayLineType =
148           new DisplayRealType("displayLine_"+count, true, 0.0, 10000.0, 0.0, null);
149        DisplayRealType displayElemType =
150           new DisplayRealType("displayElem_"+count, true, 0.0, 10000.0, 0.0, null);
151        DisplayRealType displayAltType =
152           new DisplayRealType("displayAlt_"+count, true, -1.0, 1.0, 0.0, null);
153        DisplayTupleType dtt =
154           new DisplayTupleType(new DisplayRealType[] {displayLineType, displayElemType, displayAltType}, new_cs);
155
156        RealType elemType = RealType.getRealType("elem_"+count);
157        RealType lineType = RealType.getRealType("line_"+count);
158        this.xType = lineType;
159        this.yType = elemType;
160        this.mask  = mask;
161        bounds = new Gridded2DSet(new RealTupleType(xType, yType), null, 1);
162
163        ScalarMap elemMap = new ScalarMap(elemType, displayElemType);
164        ScalarMap lineMap = new ScalarMap(lineType, displayLineType);
165
166        GriddedSet domainSet = (GriddedSet) data.getDomainSet();
167        float[] low = domainSet.getLow();
168        float[] hi  = domainSet.getHi();
169
170        elemMap.setRange(low[1], hi[1]);
171        lineMap.setRange(low[0], hi[0]);
172
173        addScalarMap(elemMap);
174        addScalarMap(lineMap);
175
176        setData(bounds);
177        count += 1;
178    }
179
180    /**
181     * Constructor for creating a RubberBandBox from another instance
182     *
183     * @param that  other instance
184     *
185     * @throws VisADException   VisAD error
186     * @throws RemoteException   Remote error
187     */
188    protected GeoSubsetRubberBandBox(GeoSubsetRubberBandBox that)
189            throws VisADException, RemoteException {
190
191        super(that);
192    }
193
194    protected void setDisplayMaster(DisplayMaster dspMaster) {
195        dispMaster = dspMaster;
196        new_cs.setDisplayMaster(dispMaster);
197    }
198
199    public float[] getRanges() {
200        float[] extrms = new_cs.getExtremes();
201        resetExtremes();
202        return extrms;
203    }
204
205    protected void resetExtremes() {
206        new_cs.resetExtremes();
207    }
208
209    protected GeoDataToDisplayCoordinateSystem getDisplayCoordSystem() {
210        return new_cs;
211    }
212}
213
214class GeoDataToDisplayCoordinateSystem extends CoordinateSystem {
215  private CoordinateSystem dataCS;
216  private CoordinateSystem displayCS;
217  private boolean isLL;
218  private MapProjectionDisplay mapProjDisp;
219  private double scaleX;
220  private double scaleY;
221  private double offsetX;
222  private double offsetY;
223
224  private float lineLo;
225  private float lineHi;
226  private float eleLo;
227  private float eleHi;
228
229  GeoDataToDisplayCoordinateSystem(boolean isLL, CoordinateSystem dataCS, CoordinateSystem displayCS) throws VisADException {
230    super(displayCS.getReference(), null);
231    try {
232        this.dataCS = dataCS;
233        this.displayCS = displayCS;
234        this.isLL = isLL;
235    } catch (Exception e) {
236        System.out.println("e=" + e);
237    }
238  }
239
240  protected void setDisplayMaster(DisplayMaster dspMaster) {
241      if (dspMaster instanceof MapProjectionDisplay) {
242          mapProjDisp = (MapProjectionDisplay)dspMaster;
243          this.mapProjDisp = mapProjDisp;
244          MapProjection mapProj = mapProjDisp.getMapProjection();
245          java.awt.geom.Rectangle2D bounds =
246             mapProj.getDefaultMapArea();
247          scaleX  = bounds.getWidth() / 2.0;
248          scaleY  = bounds.getHeight() / 2.0;
249          offsetX = bounds.getX() + scaleX;
250          offsetY = bounds.getY() + scaleY;
251      }
252  }
253
254  public float[] getExtremes() {
255    float[] extremes = new float[4];
256    extremes[0] = eleLo;
257    extremes[1] = lineLo;
258    extremes[2] = eleHi;
259    extremes[3] = lineHi;
260    return extremes;
261  }
262
263  public void resetExtremes() {
264    lineLo = (float)99999.0;
265    lineHi = (float)0.0;
266    eleLo = (float)99999.0;
267    eleHi = (float)0.0;
268  }
269
270  public float[][] toReference(float[][] values) throws VisADException {
271
272    if (values[0][0] < eleLo) eleLo = values[0][0];
273    if (values[0][0] > eleHi) eleHi = values[0][0];
274    if (values[1][0] < lineLo) lineLo = values[1][0];
275    if (values[1][0] > lineHi) lineHi = values[1][0];
276
277    float[][] new_values = bypassToReference(values);
278    return new_values;
279  }
280
281  private float[][] bypassToReference(float[][] xyz) {
282    if ((xyz == null) || (xyz[0].length < 1)) {
283        return xyz;
284    }
285    int numpoints = xyz[0].length;
286    float x, y;
287    float[]t2ax = xyz[0];
288    float[]t2ay = xyz[1];
289    for (int i = 0; i < numpoints; i++) {
290        float t2x = t2ax[i];
291        float t2y = t2ay[i];
292        if (t2x!=t2x || t2y!=t2y) {
293            x = Float.NaN;
294            y = Float.NaN;
295        } else {
296            x = (float) ((t2x - offsetX) / scaleX);
297            y = (float) ((t2y - offsetY) / scaleY);
298        }
299        xyz[0][i] = x;
300        xyz[1][i] = y;
301    }
302    return xyz;
303  }
304
305  public float[][] fromReference(float[][] values) throws VisADException {
306    float[][] new_values = bypassFromReference(values);
307    return new_values;
308  }
309
310  /**
311   * Transform display XYZ values to latitude/longitude/altitude
312   *
313   * @param  xyz  array of Display.DisplaySpatialCartesianTuple XYZ values
314   * @return array of display lat/lon/alt values.
315   *
316   * @throws VisADException  can't create the necessary VisAD object
317   */
318  private float[][] bypassFromReference(float[][] xyz) throws VisADException {
319      if ((xyz == null) || (xyz[0].length < 1)) {
320          return xyz;
321      }
322      int numpoints = xyz[0].length;
323      for (int i = 0; i < numpoints; i++) {
324          if (Float.isNaN(xyz[0][i]) || Float.isNaN(xyz[0][i])) {
325              continue;
326          }
327          xyz[0][i] = (float) (xyz[0][i] * scaleX + offsetX);
328          xyz[1][i] = (float) (xyz[1][i] * scaleY + offsetY);
329      }
330      return xyz;
331  }
332
333  public double[][] toReference(double[][] values) throws VisADException {
334    //- if (isLL) values = reverseArrayOrder(values);
335    double[][] new_values = dataCS.toReference(values);
336    if (isLL) new_values = reverseArrayOrder(new_values);
337    new_values = displayCS.toReference(new double[][] {new_values[1], new_values[0], new_values[2]});
338    return new_values;
339  }
340
341
342  public double[][] fromReference(double[][] values) throws VisADException {
343    //- if (isLL) values = reverseArrayOrder(values);
344    double[][] new_values = displayCS.fromReference(values);
345    if (isLL) new_values = reverseArrayOrder(new_values);
346    new_values = dataCS.fromReference(new double[][] {new_values[1], new_values[0], new_values[2]});
347    return new_values;
348  }
349
350  public boolean equals(Object obj) {
351    return true;
352  }
353
354    private double[][] reverseArrayOrder(double[][] in) {
355        if (in.length < 2) return in;
356        int len1 = 2;
357        int len2 = in[0].length;
358        double[][] out = new double[in.length][len2];;
359        for (int i=0; i<len1; i++) {
360            for (int j=0; j<len2; j++) {
361                out[len1-i-1][j] = in[i][j];
362            }
363        }
364        if (in.length > 2) {
365            for (int i=2; i<in.length; i++) {
366                for (int j=0; j<len2; j++) {
367                    out[i][j] = in[i][j];
368                }
369            }
370        }
371        return out;
372    }
373
374
375    private float[][] reverseArrayOrder(float[][] in) {
376        if (in.length < 2) return in;
377        int len1 = 2;
378        int len2 = in[0].length;
379        float[][] out = new float[in.length][len2];;
380        for (int i=0; i<len1; i++) {
381            for (int j=0; j<len2; j++) {
382                out[len1-i-1][j] = in[i][j];
383            }
384        }
385        if (in.length > 2) {
386            for (int i=2; i<in.length; i++) {
387                for (int j=0; j<len2; j++) {
388                    out[i][j] = in[i][j];
389                }
390            }
391        }
392        return out;
393    }
394}