001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2025 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; 033import java.util.Objects; 034 035import ucar.ma2.DataType; 036import visad.CoordinateSystem; 037import visad.Gridded2DDoubleSet; 038import visad.Gridded2DSet; 039import visad.Linear2DSet; 040import visad.RealTupleType; 041 042public class SwathNavigation implements Navigation { 043 044 public static SwathNavigation createNavigation(SwathAdapter swathAdapter) throws Exception { 045 String product_name = null; 046 SwathNavigation swathNav = null; 047 048 product_name = (String)swathAdapter.getMetadata().get(SwathAdapter.product_name); 049 050 if (product_name == null) { 051 swathNav = new SwathNavigation(swathAdapter); 052 } 053 else if (Objects.equals(product_name, "IASI_L1C_xxx")) { 054 swathNav = new IASI_L1C_LonLatNavigation(swathAdapter); 055 } 056 else if (Objects.equals(product_name, "CrIS_SDR")) { 057 swathNav = new CrIS_SDR_LonLatNavigation(swathAdapter); 058 } 059 else { 060 swathNav = new SwathNavigation(swathAdapter); 061 } 062 063 return swathNav; 064 } 065 066 int geo_track_idx; 067 int geo_xtrack_idx; 068 int geoTrackLen; 069 int geoXTrackLen; 070 071 SwathAdapter swathAdapter; 072 MultiDimensionReader reader; 073 String lon_array_name; 074 String lat_array_name; 075 int[] idx_order = new int[2]; 076 float ratio = 1; 077 float track_ratio = 1; 078 float xtrack_ratio = 1; 079 double track_offset = 0; 080 double xtrack_offset = 0; 081 int track_idx; 082 int xtrack_idx; 083 int[] geo_stride = new int[2]; 084 int[] geo_count = new int[2]; 085 int[] geo_start = new int[2]; 086 087 String scale_name = "SCALE_NAME"; 088 String offset_name = "OFFSET_NAME"; 089 String fillValue_name = "_FILLVALUE"; 090 091 int numDims = 2; 092 093 DataType type; 094 095 public SwathNavigation(SwathAdapter swathAdapter) throws Exception { 096 097 Map<String, Object> metadata = swathAdapter.getMetadata(); 098 reader = swathAdapter.getReader(); 099 this.swathAdapter = swathAdapter; 100 track_idx = swathAdapter.track_idx; 101 xtrack_idx = swathAdapter.xtrack_idx; 102 103 lon_array_name = (String)metadata.get(SwathAdapter.lon_array_name); 104 lat_array_name = (String)metadata.get(SwathAdapter.lat_array_name); 105 106 String[] lon_dim_names = null; 107 108 String[] lonDimNames = (String[])metadata.get(SwathAdapter.lon_array_dimension_names); 109 110 if (lonDimNames != null) { 111 lon_dim_names = lonDimNames; 112 } 113 else { 114 lon_dim_names = reader.getDimensionNames(lon_array_name); 115 } 116 117 int[] lon_dim_lengths = reader.getDimensionLengths(lon_array_name); 118 119 numDims = lon_dim_lengths.length; 120 geo_stride = new int[numDims]; 121 geo_count = new int[numDims]; 122 geo_start = new int[numDims]; 123 124 125 String geo_track_name = (String)metadata.get(SwathAdapter.geo_track_name); 126 String geo_xtrack_name = (String)metadata.get(SwathAdapter.geo_xtrack_name); 127 128 for (int k=0; k<numDims;k++) { 129 if ( geo_track_name.equals(lon_dim_names[k]) ) { 130 geo_track_idx = k; 131 } 132 if ( geo_xtrack_name.equals(lon_dim_names[k]) ) { 133 geo_xtrack_idx = k; 134 } 135 } 136 137 if (geo_track_idx < geo_xtrack_idx) { 138 idx_order[0] = geo_xtrack_idx; 139 idx_order[1] = geo_track_idx; 140 } 141 else { 142 idx_order[0] = geo_track_idx; 143 idx_order[1] = geo_xtrack_idx; 144 } 145 146 geoTrackLen = lon_dim_lengths[geo_track_idx]; 147 geoXTrackLen = lon_dim_lengths[geo_xtrack_idx]; 148 149 String str = (String)metadata.get(SwathAdapter.geo_track_skip_name); 150 151 if (str != null) { 152 track_ratio = (float) Double.parseDouble(str); 153 ratio = track_ratio; 154 } 155 str = (String)metadata.get(SwathAdapter.geo_xtrack_skip_name); 156 if (str != null) { 157 xtrack_ratio = (float) Double.parseDouble(str); 158 } 159 str = (String)metadata.get(SwathAdapter.geo_track_offset_name); 160 if (str != null) { 161 track_offset = Double.parseDouble(str); 162 } 163 str = (String)metadata.get(SwathAdapter.geo_xtrack_offset_name); 164 if (str != null) { 165 xtrack_offset = Double.parseDouble(str); 166 } 167 168 str = (String)metadata.get(SwathAdapter.geo_scale_name); 169 if (str != null) { 170 scale_name = str; 171 } 172 173 str = (String)metadata.get(SwathAdapter.geo_offset_name); 174 if (str != null) { 175 offset_name = str; 176 } 177 178 str = (String)metadata.get(SwathAdapter.geo_fillValue_name); 179 if (str != null) { 180 fillValue_name = str; 181 } 182 183 type = reader.getArrayType(lon_array_name); 184 } 185 186 public CoordinateSystem getVisADCoordinateSystem(Linear2DSet domainSet, Map<String, double[]> domainSubset) throws Exception 187 { 188 Subset select = swathAdapter.getIndexes(domainSubset); 189 190 double[] track_coords = domainSubset.get(SwathAdapter.track_name); 191 double[] xtrack_coords = domainSubset.get(SwathAdapter.xtrack_name); 192 193 int[] stride = new int[numDims]; 194 stride[geo_track_idx] = (int) track_coords[2]; 195 stride[geo_xtrack_idx] = (int) xtrack_coords[2]; 196 197 198 if (numDims > 2) { // initialize geo arrays, then recompute xtrack/track dimensions below 199 if (numDims == select.getRank()) { 200 int[] start = select.getStart(); 201 int[] count = select.getCount(); 202 stride = select.getStride(); 203 for (int i=0; i<numDims; i++) { 204 geo_start[i] = start[i]; 205 geo_count[i] = count[i]; 206 geo_stride[i] = stride[i]; 207 } 208 } 209 else { 210 geo_start[geo_track_idx] = (int) track_coords[0]; 211 geo_start[geo_xtrack_idx] = (int) xtrack_coords[0]; 212 geo_count[geo_track_idx] = (int) ((track_coords[1] - track_coords[0])/track_coords[2] + 1f); 213 geo_count[geo_xtrack_idx] = (int) ((xtrack_coords[1] - xtrack_coords[0])/xtrack_coords[2] + 1f); 214 } 215 } 216 217 218 if (ratio/(float)stride[0] <= 1) { 219 geo_stride[geo_track_idx] = Math.round((1f/(track_ratio/((float)stride[geo_track_idx])))); 220 geo_stride[geo_xtrack_idx] = Math.round((1f/(xtrack_ratio/((float)stride[geo_xtrack_idx])))); 221 } 222 else { 223 geo_stride[geo_track_idx] = 1; 224 geo_stride[geo_xtrack_idx] = 1; 225 } 226 227 int geo_track_start = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio); 228 int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio); 229 230 int geo_track_end = (int) ((track_coords[1] - track_offset)/((double)track_ratio)); 231 int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio)); 232 233 geo_count[geo_track_idx] = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1; 234 geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1; 235 236 geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx]; 237 geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx]; 238 239 geo_start[geo_track_idx] = geo_track_start; 240 geo_start[geo_xtrack_idx] = geo_xtrack_start; 241 242 //-- convert back track/xtrack coords: 243 int new_track_start = (int) (geo_track_start*track_ratio + (float)track_offset); 244 int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset); 245 int new_track_end = (int) (geo_track_end*track_ratio + (float)track_offset); 246 int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset); 247 248 249 //- these must be only 2D (Swath dimensions) 250 double[] first = new double[2]; 251 double[] last = new double[2]; 252 int[] length = new int[2]; 253 254 int track_idx; 255 int xtrack_idx; 256 if (geo_track_idx < geo_xtrack_idx) { 257 track_idx = 1; 258 xtrack_idx = 0; 259 } else { 260 track_idx = 0; 261 xtrack_idx = 1; 262 } 263 264 first[track_idx] = new_track_start; 265 first[xtrack_idx] = new_xtrack_start; 266 last[track_idx] = new_track_end; 267 last[xtrack_idx] = new_xtrack_end; 268 length[track_idx] = (int) ((last[track_idx] - first[track_idx])/stride[geo_track_idx] + 1); 269 length[xtrack_idx] = (int) ((last[xtrack_idx] - first[xtrack_idx])/stride[geo_xtrack_idx] + 1); 270 271 domainSet = new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]); 272 273 Gridded2DSet gset = null; 274 275 gset = createInterpSet(); 276 277 CoordinateSystem cs = new LongitudeLatitudeCoordinateSystem(domainSet, gset); 278 279 return cs; 280 } 281 282 Gridded2DSet createInterpSet() throws Exception { 283 Gridded2DSet gset = null; 284 if (type == DataType.FLOAT) { 285 float[] lonValues = reader.getFloatArray(lon_array_name, geo_start, geo_count, geo_stride); 286 float[] latValues = reader.getFloatArray(lat_array_name, geo_start, geo_count, geo_stride); 287 288 gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, 289 new float[][] {lonValues, latValues}, 290 geo_count[idx_order[0]], geo_count[idx_order[1]], 291 null, null, null, false, false); 292 } 293 else if (type == DataType.DOUBLE) { 294 double[] lonValues = reader.getDoubleArray(lon_array_name, geo_start, geo_count, geo_stride); 295 double[] latValues = reader.getDoubleArray(lat_array_name, geo_start, geo_count, geo_stride); 296 297 gset = new Gridded2DDoubleSet(RealTupleType.SpatialEarth2DTuple, 298 new double[][] {lonValues, latValues}, 299 geo_count[idx_order[0]], geo_count[idx_order[1]], 300 null, null, null, false); 301 } 302 else if (type == DataType.SHORT) { 303 short[] values = reader.getShortArray(lon_array_name, geo_start, geo_count, geo_stride); 304 Map<String, Object> metadata = new HashMap<>(); 305 metadata.put(SwathAdapter.array_name, lon_array_name); 306 metadata.put(SwathAdapter.scale_name, scale_name); 307 metadata.put(SwathAdapter.offset_name, offset_name); 308 metadata.put(SwathAdapter.fill_value_name, fillValue_name); 309 RangeProcessor rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata); 310 float[] lonValues = rangeProcessor.processRange(values, null); 311 312 values = reader.getShortArray(lat_array_name, geo_start, geo_count, geo_stride); 313 metadata = new HashMap<>(); 314 metadata.put(SwathAdapter.array_name, lat_array_name); 315 metadata.put(SwathAdapter.scale_name, scale_name); 316 metadata.put(SwathAdapter.offset_name, offset_name); 317 metadata.put(SwathAdapter.fill_value_name, fillValue_name); 318 rangeProcessor = RangeProcessor.createRangeProcessor(reader, metadata); 319 float[] latValues = rangeProcessor.processRange(values, null); 320 321 322 gset = new Gridded2DSet(RealTupleType.SpatialEarth2DTuple, 323 new float[][] {lonValues, latValues}, 324 geo_count[idx_order[0]], geo_count[idx_order[1]], 325 null, null, null, false, false); 326 327 } 328 return gset; 329 } 330 331 332 333 public static Linear2DSet getNavigationDomain(double data_x_start, double data_x_stop, double data_x_stride, 334 double data_y_start, double data_y_stop, double data_y_stride, 335 double ratio_x, double ratio_y, 336 double offset_x, double offset_y, 337 int[] geo_start, int[] geo_count, int[] geo_stride) 338 throws Exception { 339 340 int geo_track_idx = 1; 341 int geo_xtrack_idx = 0; 342 double track_ratio = ratio_y; 343 double xtrack_ratio = ratio_x; 344 double track_offset = offset_y; 345 double xtrack_offset = offset_x; 346 347 double[] track_coords = new double[3]; 348 double[] xtrack_coords = new double[3]; 349 350 xtrack_coords[0] = data_x_start; 351 xtrack_coords[1] = data_x_stop; 352 track_coords[0] = data_y_start; 353 track_coords[1] = data_y_stop; 354 355 double[] stride = new double[2]; 356 stride[geo_track_idx] = data_y_stride; 357 stride[geo_xtrack_idx] = data_x_stride; 358 359 if (track_ratio/(float)stride[0] <= 1) { 360 geo_stride[geo_track_idx] = (int) Math.round((1/(track_ratio/(stride[1])))); 361 geo_stride[geo_xtrack_idx] = (int) Math.round((1/(xtrack_ratio/(stride[0])))); 362 } 363 else { 364 geo_stride[0] = 1; 365 geo_stride[1] = 1; 366 } 367 368 int geo_track_start = (int) Math.ceil((track_coords[0] - track_offset)/track_ratio); 369 int geo_xtrack_start = (int) Math.ceil((xtrack_coords[0] - xtrack_offset)/xtrack_ratio); 370 371 int geo_track_end = (int) ((track_coords[1] - track_offset)/((double)track_ratio)); 372 int geo_xtrack_end = (int) ((xtrack_coords[1] - xtrack_offset)/((double)xtrack_ratio)); 373 374 geo_count[geo_track_idx] = (int) ((geo_track_end - geo_track_start)/geo_stride[geo_track_idx]) + 1; 375 geo_count[geo_xtrack_idx] = (int) ((geo_xtrack_end - geo_xtrack_start)/geo_stride[geo_xtrack_idx]) + 1; 376 377 geo_track_end = geo_track_start + (geo_count[geo_track_idx]-1)*geo_stride[geo_track_idx]; 378 geo_xtrack_end = geo_xtrack_start + (geo_count[geo_xtrack_idx]-1)*geo_stride[geo_xtrack_idx]; 379 380 geo_start[geo_track_idx] = geo_track_start; 381 geo_start[geo_xtrack_idx] = geo_xtrack_start; 382 383 //-- convert back track/xtrack coords: 384 int new_track_start = (int) (geo_track_start*track_ratio + (float)track_offset); 385 int new_xtrack_start = (int) (geo_xtrack_start*xtrack_ratio + (float)xtrack_offset); 386 int new_track_end = (int) (geo_track_end*track_ratio + (float)track_offset); 387 int new_xtrack_end = (int) (geo_xtrack_end*xtrack_ratio + (float)xtrack_offset); 388 389 390 double[] first = new double[2]; 391 double[] last = new double[2]; 392 int[] length = new int[2]; 393 first[geo_track_idx] = new_track_start; 394 first[geo_xtrack_idx] = new_xtrack_start; 395 last[geo_track_idx] = new_track_end; 396 last[geo_xtrack_idx] = new_xtrack_end; 397 length[geo_track_idx] = (int) ((last[geo_track_idx] - first[geo_track_idx])/stride[geo_track_idx] + 1); 398 length[geo_xtrack_idx] = (int) ((last[geo_xtrack_idx] - first[geo_xtrack_idx])/stride[geo_xtrack_idx] + 1); 399 400 return new Linear2DSet(first[0], last[0], length[0], first[1], last[1], length[1]); 401 402 } 403 404 405}