001    /*
002     * This file is part of McIDAS-V
003     *
004     * Copyright 2007-2013
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     */
028    
029    package edu.wisc.ssec.mcidasv.data;
030    
031    import edu.wisc.ssec.mcidasv.data.hydra.MyRubberBandBoxRendererJ3D;
032    import edu.wisc.ssec.mcidasv.data.hydra.SubsetRubberBandBox;
033    //import edu.wisc.ssec.mcidasv.data.hydra.DataToDisplayCoordinateSystem;
034    
035    import ucar.unidata.view.geoloc.MapProjectionDisplay;
036    import ucar.visad.display.Displayable;
037    import ucar.visad.display.DisplayMaster;
038    import ucar.visad.display.LineDrawing;
039    
040    import visad.*;
041    import visad.bom.*;
042    import visad.georef.MapProjection;
043    
044    import java.rmi.RemoteException;
045    
046    import java.awt.event.InputEvent;
047    
048    
049    public 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  xType   RealType of the X coordinate of the box
084         * @param  yType   RealType of the Y coordinate of the box
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    
111        /**
112         * Construct a RubberBandBox using xType as the X coordinate and
113         * yType as the Y coordinate of the box.
114         *
115         * @param xType   RealType of the X coordinate of the box
116         * @param yType   RealType of the Y coordinate of the box
117         * @param mask    key mask to use for rubberbanding
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    
148            DisplayRealType displayLineType =
149               new DisplayRealType("displayLine_"+count, true, 0.0, 10000.0, 0.0, null);
150            DisplayRealType displayElemType =
151               new DisplayRealType("displayElem_"+count, true, 0.0, 10000.0, 0.0, null);
152            DisplayRealType displayAltType =
153               new DisplayRealType("displayAlt_"+count, true, -1.0, 1.0, 0.0, null);
154            DisplayTupleType dtt =
155               new DisplayTupleType(new DisplayRealType[] {displayLineType, displayElemType, displayAltType}, new_cs);
156    
157            RealType elemType = RealType.getRealType("elem_"+count);
158            RealType lineType = RealType.getRealType("line_"+count);
159            this.xType = lineType;
160            this.yType = elemType;
161            this.mask  = mask;
162            bounds = new Gridded2DSet(new RealTupleType(xType, yType), null, 1);
163    
164            ScalarMap elemMap = new ScalarMap(elemType, displayElemType);
165            ScalarMap lineMap = new ScalarMap(lineType, displayLineType);
166    
167            GriddedSet domainSet = (GriddedSet) data.getDomainSet();
168            float[] low = domainSet.getLow();
169            float[] hi  = domainSet.getHi();
170    
171            elemMap.setRange(low[1], hi[1]);
172            lineMap.setRange(low[0], hi[0]);
173    
174            addScalarMap(elemMap);
175            addScalarMap(lineMap);
176    
177            setData(bounds);
178            count += 1;
179        }
180    
181        /**
182         * Constructor for creating a RubberBandBox from another instance
183         *
184         * @param that  other instance
185         *
186         * @throws VisADException   VisAD error
187         * @throws RemoteException   Remote error
188         */
189        protected GeoSubsetRubberBandBox(GeoSubsetRubberBandBox that)
190                throws VisADException, RemoteException {
191    
192            super(that);
193        }
194    
195        protected void setDisplayMaster(DisplayMaster dspMaster) {
196            dispMaster = dspMaster;
197            new_cs.setDisplayMaster(dispMaster);
198        }
199    
200        public float[] getRanges() {
201            float[] extrms = new_cs.getExtremes();
202            resetExtremes();
203            return extrms;
204        }
205    
206        protected void resetExtremes() {
207            new_cs.resetExtremes();
208        }
209    
210        protected GeoDataToDisplayCoordinateSystem getDisplayCoordSystem() {
211            return new_cs;
212        }
213    }
214    
215    class GeoDataToDisplayCoordinateSystem extends CoordinateSystem {
216      private CoordinateSystem dataCS;
217      private CoordinateSystem displayCS;
218      private boolean isLL;
219      private MapProjectionDisplay mapProjDisp;
220      private double scaleX;
221      private double scaleY;
222      private double offsetX;
223      private double offsetY;
224    
225      private float lineLo;
226      private float lineHi;
227      private float eleLo;
228      private float eleHi;
229    
230      GeoDataToDisplayCoordinateSystem(boolean isLL, CoordinateSystem dataCS, CoordinateSystem displayCS) throws VisADException {
231        super(displayCS.getReference(), null);
232        try {
233            this.dataCS = dataCS;
234            this.displayCS = displayCS;
235            this.isLL = isLL;
236        } catch (Exception e) {
237            System.out.println("e=" + e);
238        }
239      }
240    
241      protected void setDisplayMaster(DisplayMaster dspMaster) {
242          if (dspMaster instanceof MapProjectionDisplay) {
243              mapProjDisp = (MapProjectionDisplay)dspMaster;
244              this.mapProjDisp = mapProjDisp;
245              MapProjection mapProj = mapProjDisp.getMapProjection();
246              java.awt.geom.Rectangle2D bounds =
247                 mapProj.getDefaultMapArea();
248              scaleX  = bounds.getWidth() / 2.0;
249              scaleY  = bounds.getHeight() / 2.0;
250              offsetX = bounds.getX() + scaleX;
251              offsetY = bounds.getY() + scaleY;
252          }
253      }
254    
255      public float[] getExtremes() {
256        float[] extremes = new float[4];
257        extremes[0] = eleLo;
258        extremes[1] = lineLo;
259        extremes[2] = eleHi;
260        extremes[3] = lineHi;
261        return extremes;
262      }
263    
264      public void resetExtremes() {
265        lineLo = (float)99999.0;
266        lineHi = (float)0.0;
267        eleLo = (float)99999.0;
268        eleHi = (float)0.0;
269      }
270    
271      public float[][] toReference(float[][] values) throws VisADException {
272    
273        if (values[0][0] < eleLo) eleLo = values[0][0];
274        if (values[0][0] > eleHi) eleHi = values[0][0];
275        if (values[1][0] < lineLo) lineLo = values[1][0];
276        if (values[1][0] > lineHi) lineHi = values[1][0];
277    
278        float[][] new_values = bypassToReference(values);
279        return new_values;
280      }
281    
282      private float[][] bypassToReference(float[][] xyz) {
283        if ((xyz == null) || (xyz[0].length < 1)) {
284            return xyz;
285        }
286        int numpoints = xyz[0].length;
287        float x, y;
288        float[]t2ax = xyz[0];
289        float[]t2ay = xyz[1];
290        for (int i = 0; i < numpoints; i++) {
291            float t2x = t2ax[i];
292            float t2y = t2ay[i];
293            if (t2x!=t2x || t2y!=t2y) {
294                x = Float.NaN;
295                y = Float.NaN;
296            } else {
297                x = (float) ((t2x - offsetX) / scaleX);
298                y = (float) ((t2y - offsetY) / scaleY);
299            }
300            xyz[0][i] = x;
301            xyz[1][i] = y;
302        }
303        return xyz;
304      }
305    
306      public float[][] fromReference(float[][] values) throws VisADException {
307        float[][] new_values = bypassFromReference(values);
308        return new_values;
309      }
310    
311      /**
312       * Transform display XYZ values to latitude/longitude/altitude
313       *
314       * @param  xyz  array of Display.DisplaySpatialCartesianTuple XYZ values
315       * @return array of display lat/lon/alt values.
316       *
317       * @throws VisADException  can't create the necessary VisAD object
318       */
319      private float[][] bypassFromReference(float[][] xyz) throws VisADException {
320          if ((xyz == null) || (xyz[0].length < 1)) {
321              return xyz;
322          }
323          int numpoints = xyz[0].length;
324          for (int i = 0; i < numpoints; i++) {
325              if (Float.isNaN(xyz[0][i]) || Float.isNaN(xyz[0][i])) {
326                  continue;
327              }
328              xyz[0][i] = (float) (xyz[0][i] * scaleX + offsetX);
329              xyz[1][i] = (float) (xyz[1][i] * scaleY + offsetY);
330          }
331          return xyz;
332      }
333    
334      public double[][] toReference(double[][] values) throws VisADException {
335        //- if (isLL) values = reverseArrayOrder(values);
336        double[][] new_values = dataCS.toReference(values);
337        if (isLL) new_values = reverseArrayOrder(new_values);
338        new_values = displayCS.toReference(new double[][] {new_values[1], new_values[0], new_values[2]});
339        return new_values;
340      }
341    
342    
343      public double[][] fromReference(double[][] values) throws VisADException {
344        //- if (isLL) values = reverseArrayOrder(values);
345        double[][] new_values = displayCS.fromReference(values);
346        if (isLL) new_values = reverseArrayOrder(new_values);
347        new_values = dataCS.fromReference(new double[][] {new_values[1], new_values[0], new_values[2]});
348        return new_values;
349      }
350    
351      public boolean equals(Object obj) {
352        return true;
353      }
354    
355        private double[][] reverseArrayOrder(double[][] in) {
356            if (in.length < 2) return in;
357            int len1 = 2;
358            int len2 = in[0].length;
359            double[][] out = new double[in.length][len2];;
360            for (int i=0; i<len1; i++) {
361                for (int j=0; j<len2; j++) {
362                    out[len1-i-1][j] = in[i][j];
363                }
364            }
365            if (in.length > 2) {
366                for (int i=2; i<in.length; i++) {
367                    for (int j=0; j<len2; j++) {
368                        out[i][j] = in[i][j];
369                    }
370                }
371            }
372            return out;
373        }
374    
375    
376        private float[][] reverseArrayOrder(float[][] in) {
377            if (in.length < 2) return in;
378            int len1 = 2;
379            int len2 = in[0].length;
380            float[][] out = new float[in.length][len2];;
381            for (int i=0; i<len1; i++) {
382                for (int j=0; j<len2; j++) {
383                    out[len1-i-1][j] = in[i][j];
384                }
385            }
386            if (in.length > 2) {
387                for (int i=2; i<in.length; i++) {
388                    for (int j=0; j<len2; j++) {
389                        out[i][j] = in[i][j];
390                    }
391                }
392            }
393            return out;
394        }
395    }