001    /*
002     * $Id: GeoSubsetRubberBandBox.java,v 1.11 2012/02/19 17:35:45 davep Exp $
003     *
004     * This file is part of McIDAS-V
005     *
006     * Copyright 2007-2012
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    
031    package edu.wisc.ssec.mcidasv.data;
032    
033    import edu.wisc.ssec.mcidasv.data.hydra.MyRubberBandBoxRendererJ3D;
034    import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox;
035    //import edu.wisc.ssec.mcidasv.data.hydra.DataToDisplayCoordinateSystem;
036    
037    import ucar.unidata.view.geoloc.MapProjectionDisplay;
038    import ucar.visad.display.Displayable;
039    import ucar.visad.display.DisplayMaster;
040    import ucar.visad.display.LineDrawing;
041    
042    import visad.*;
043    import visad.bom.*;
044    import visad.georef.MapProjection;
045    
046    import java.rmi.RemoteException;
047    
048    import java.awt.event.InputEvent;
049    
050    
051    public 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    
217    class 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    }