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