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.hydra;
030
031import java.util.HashMap;
032import java.util.Map;
033
034import org.slf4j.Logger;
035import org.slf4j.LoggerFactory;
036
037import visad.CoordinateSystem;
038import visad.FunctionType;
039import visad.Linear2DSet;
040import visad.RealTupleType;
041import visad.RealType;
042import visad.Set;
043import visad.Unit;
044
045public class SwathAdapter extends MultiDimensionAdapter {
046
047          private static final Logger logger = LoggerFactory.getLogger(SwathAdapter.class);
048      String nav_type = "Interp";
049      boolean lon_lat_trusted = true;
050
051      private int TrackLen;
052      private int XTrackLen;
053
054      static String longitude_name = "Longitude";
055      static String latitude_name  = "Latitude";
056      static String track_name  = "Track";
057      static String xtrack_name = "XTrack";
058      static String geo_track_name = "geo_Track";
059      static String geo_xtrack_name  = "geo_XTrack";
060      static String array_name = "array_name";
061      static String array_dimension_names = "array_dimension_names";
062      static String lon_array_name = "lon_array_name";
063      static String lat_array_name = "lat_array_name";
064      static String lon_array_dimension_names = "lon_array_dimension_names";
065      static String lat_array_dimension_names = "lat_array_dimension_names";
066      static String range_name = "range_name";
067      static String product_name = "product_name";
068      static String scale_name = "scale_name";
069      static String offset_name = "offset_name";
070      static String fill_value_name = "fill_value_name";
071      static String geo_track_offset_name  = "geoTrack_offset";
072      static String geo_xtrack_offset_name = "geoXTrack_offset";
073      static String geo_track_skip_name  = "geoTrack_skip";
074      static String geo_xtrack_skip_name = "geoXTrack_skip";
075      static String geo_scale_name = "geo_scale_name";
076      static String geo_offset_name = "geo_scale_name";
077      static String geo_fillValue_name = "geo_fillValue_name";
078      static String multiScaleDimensionIndex = "multiScaleDimensionIndex";
079
080      String[] rangeName_s  = null;
081      Class[] arrayType_s = null;
082      Unit[] rangeUnit_s  = new Unit[] {null};
083
084      String rangeName = null;
085
086      RealType track  = RealType.getRealType(track_name);
087      RealType xtrack = RealType.getRealType(xtrack_name);
088      RealType[] domainRealTypes = new RealType[2];
089
090      int track_idx      = -1;
091      int xtrack_idx     = -1;
092      int lon_track_idx  = -1;
093      int lon_xtrack_idx = -1;
094      int lat_track_idx  = -1;
095      int lat_xtrack_idx = -1;
096      int range_rank     = -1;
097
098      int geo_track_offset = 0;
099      int geo_track_skip = 1;
100      int geo_xtrack_offset = 0;
101      int geo_xtrack_skip = 1;
102
103      int track_tup_idx;
104      int xtrack_tup_idx;
105
106      private SwathNavigation navigation;
107
108      private Linear2DSet swathDomain;
109      private Linear2DSet domainSet_save;
110
111      private Map<String, double[]> last_subset;
112
113      int default_stride = 1;
114
115      public static Map<String, double[]> getEmptySubset() {
116        Map<String, double[]> subset = new HashMap<>();
117        subset.put(track_name, new double[3]);
118        subset.put(xtrack_name, new double[3]);
119        return subset;
120      }
121
122      public static Map<String, Object> getEmptyMetadataTable() {
123          Map<String, Object> metadata = new HashMap<>();
124          metadata.put(array_name, null);
125          metadata.put(array_dimension_names, null);
126          metadata.put(track_name, null);
127          metadata.put(xtrack_name, null);
128          metadata.put(geo_track_name, null);
129          metadata.put(geo_xtrack_name, null);
130          metadata.put(lon_array_name, null);
131          metadata.put(lat_array_name, null);
132          metadata.put(lon_array_dimension_names, null);
133          metadata.put(lat_array_dimension_names, null);
134          metadata.put(scale_name, null);
135          metadata.put(offset_name, null);
136          metadata.put(fill_value_name, null);
137          metadata.put(range_name, null);
138          metadata.put(product_name, null);
139          metadata.put(geo_track_offset_name, null);
140          metadata.put(geo_xtrack_offset_name, null);
141          metadata.put(geo_track_skip_name, null);
142          metadata.put(geo_xtrack_skip_name, null);
143          metadata.put(multiScaleDimensionIndex, null);
144          return metadata;
145      }
146
147      public SwathAdapter() {
148
149      }
150
151      public SwathAdapter(MultiDimensionReader reader, Map<String, Object> metadata) {
152        super(reader, metadata);
153        this.init();
154      }
155
156      private void init() {
157        for (int k=0; k<array_rank;k++) {
158          if ( ((String)metadata.get(track_name)).equals(array_dim_names[k]) ) {
159            track_idx = k;
160          }
161          if ( ((String)metadata.get(xtrack_name)).equals(array_dim_names[k]) ) {
162            xtrack_idx = k;
163          }
164        }
165
166        int[] lengths = new int[2];
167
168        if (track_idx < xtrack_idx) {
169          domainRealTypes[0] = xtrack;
170          domainRealTypes[1] = track;
171          lengths[0] = array_dim_lengths[xtrack_idx];
172          lengths[1] = array_dim_lengths[track_idx];
173          track_tup_idx = 1;
174          xtrack_tup_idx = 0;
175        }
176        else {
177          domainRealTypes[0] = track;
178          domainRealTypes[1] = xtrack;
179          lengths[0] = array_dim_lengths[track_idx];
180          lengths[1] = array_dim_lengths[xtrack_idx];
181          track_tup_idx = 0;
182          xtrack_tup_idx = 1;
183        }
184
185        TrackLen  = array_dim_lengths[track_idx];
186        XTrackLen = array_dim_lengths[xtrack_idx];
187        
188        setLengths();
189
190        lengths[track_tup_idx]  = TrackLen;
191        lengths[xtrack_tup_idx] = XTrackLen;
192
193        if (metadata.get(range_name) != null) {
194          rangeName = (String)metadata.get(range_name);
195        } 
196        else {
197          rangeName = (String)metadata.get(array_name);
198        }
199      
200        rangeType = RealType.getRealType(rangeName, rangeUnit_s[0]);
201
202        /** TODO could be a mis-match between supplied unit, and default
203            unit of an existing RealType with same name. */
204        if (rangeType == null) {
205          rangeType = RealType.getRealType(rangeName);
206        }
207
208        try {
209          RangeProcessor rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata);
210          if ( !(reader instanceof GranuleAggregation) ) {
211            setRangeProcessor(rangeProcessor);
212          }
213        } catch (Exception e) {
214          logger.error("RangeProcessor failed to create", e);
215        }
216
217        try {
218          navigation = SwathNavigation.createNavigation(this);
219          RealTupleType domainTupType = new RealTupleType(domainRealTypes[0], domainRealTypes[1]);
220          swathDomain = new Linear2DSet(domainTupType, 0, lengths[0]-1, lengths[0], 0, lengths[1]-1, lengths[1]);
221        } catch (Exception e) {
222          logger.error("Navigation failed to create", e);
223        }
224
225                if (XTrackLen <= 256) {
226                        default_stride = 1;
227                } else {
228                        default_stride = Math.round((float) XTrackLen / 256.0f);
229                }
230        
231        /* force default stride even */
232        if (default_stride > 1) {
233          default_stride = (default_stride/2)*2;
234        }
235
236      }
237
238      protected void setLengths() {
239      }
240
241      public int getTrackLength() {
242        return TrackLen;
243      }
244
245      public int getXTrackLength() {
246        return XTrackLen;
247      }
248
249      public SwathNavigation getNavigation() {
250        return navigation;
251      }
252
253      protected void setTrackLength(int len) {
254        TrackLen = len;
255      }
256
257      protected void setXTrackLength(int len) {
258        XTrackLen = len;
259      }
260
261      public Set makeDomain(Map<String, double[]> subset) throws Exception {
262        if (last_subset != null) {
263          if (spatialEquals(last_subset, subset)) return domainSet_save;
264        }
265
266        double[] first = new double[2];
267        double[] last = new double[2];
268        int[] length = new int[2];
269
270        Map<String, double[]> domainSubset = new HashMap<>();
271        domainSubset.put(track_name, subset.get(track_name));
272        domainSubset.put(xtrack_name, subset.get(xtrack_name));
273
274        domainSubset.put(track_name, new double[] {0,0,0});
275        domainSubset.put(xtrack_name, new double[] {0,0,0});
276
277        // compute coordinates for the Linear2D domainSet
278        for (int kk=0; kk<2; kk++) {
279          RealType rtype = domainRealTypes[kk];
280          String name = rtype.getName();
281          double[] coords = subset.get(name);
282          coords[0] = Math.ceil(coords[0]);
283          coords[1] = Math.floor(coords[1]);
284          first[kk] = coords[0];
285          last[kk] = coords[1];
286          length[kk] = (int) ((last[kk] - first[kk])/coords[2] + 1);
287          last[kk] = first[kk] + (length[kk]-1)*coords[2];
288
289          double[] new_coords = domainSubset.get(name);
290          new_coords[0] = first[kk];
291          new_coords[1] = last[kk];
292          new_coords[2] = coords[2];
293        }
294        last_subset = subset;
295
296        Linear2DSet domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]);
297        //CoordinateSystem cs = navigation.getVisADCoordinateSystem(domainSet, domainSubset);
298        CoordinateSystem cs = navigation.getVisADCoordinateSystem(domainSet, subset);
299
300        RealTupleType domainTupType = new RealTupleType(domainRealTypes[0], domainRealTypes[1], cs, null);
301        domainSet_save = new Linear2DSet(domainTupType, first[0], last[0], length[0], first[1], last[1], length[1]);
302
303        return domainSet_save;
304      }
305
306      public String getArrayName() {
307        return rangeName;
308      }
309
310      public FunctionType getMathType() {
311        return null;
312      }
313
314      public RealType[] getDomainRealTypes() {
315        return domainRealTypes;
316      }
317
318      public Linear2DSet getSwathDomain() {
319        return swathDomain;
320      }
321      
322      public boolean spatialEquals(Map<String, double[]> last_subset, Map<String, double[]> subset) {
323        double[] last_coords = last_subset.get(track_name);
324        double[] coords = subset.get(track_name);
325
326        for (int k=0; k<coords.length; k++) {
327          if (coords[k] != last_coords[k]) {
328            return false;
329          }
330        }
331
332        last_coords = last_subset.get(xtrack_name);
333        coords = subset.get(xtrack_name);
334
335        for (int k=0; k<coords.length; k++) {
336          if (coords[k] != last_coords[k]) { 
337             return false;
338          }
339        }
340      
341        return true;
342      }
343
344      public void setDefaultStride(int stride) {
345        default_stride = stride;
346      }
347
348      public Map<String, double[]> getDefaultSubset() {
349        Map<String, double[]> subset = SwathAdapter.getEmptySubset();
350
351        double[] coords = subset.get("Track");
352        coords[0] = 0.0;
353        coords[1] = TrackLen - 1;
354        coords[2] = (double)default_stride;
355        subset.put("Track", coords);
356
357        coords = subset.get("XTrack");
358        coords[0] = 0.0;
359        coords[1] = XTrackLen - 1 ;
360        coords[2] = (double)default_stride;
361        subset.put("XTrack", coords);
362        return subset;
363      }
364}