001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2015 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 */ 028package edu.wisc.ssec.mcidasv.data; 029 030import java.io.BufferedReader; 031import java.io.File; 032import java.io.FileNotFoundException; 033import java.io.FileReader; 034import java.io.IOException; 035import java.util.ArrayList; 036import java.util.List; 037 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040 041import ucar.unidata.geoloc.projection.UtmProjection; 042 043public class EnviInfo extends HeaderInfo { 044 045 /** The url */ 046 private String dataFile = ""; 047 private boolean isEnvi = false; 048 private boolean hasBounds = false; 049 private static final Logger logger = LoggerFactory.getLogger(EnviInfo.class); 050 051 // Map Info header field indices 052 // See www.exelisvis.com/docs/ENVIHeaderFiles.html 053 // for a description of this information 054 enum MapInfoIndex { 055 056 MAP_INFO_IDX_PROJ_NAME(0), 057 MAP_INFO_IDX_X_REF(1), 058 MAP_INFO_IDX_Y_REF(2), 059 MAP_INFO_IDX_EASTING(3), 060 MAP_INFO_IDX_NORTHING(4), 061 MAP_INFO_IDX_X_SIZE(5), 062 MAP_INFO_IDX_Y_SIZE(6), 063 MAP_INFO_IDX_ZONE(7), 064 MAP_INFO_IDX_N_OR_S(8), 065 MAP_INFO_IDX_DATUM(9), 066 MAP_INFO_IDX_UNITS(10); 067 068 private final int index; 069 070 private MapInfoIndex(int idx) { 071 index = idx; 072 } 073 074 public int getIndex() { 075 return index; 076 } 077 } 078 079 /** 080 * Ctor for xml encoding 081 */ 082 public EnviInfo() {} 083 084 /** 085 * CTOR 086 * 087 * @param thisFile File to use. 088 */ 089 public EnviInfo(File thisFile) { 090 this(thisFile.getAbsolutePath()); 091 } 092 093 /** 094 * CTOR 095 * 096 * @param filename The filename 097 */ 098 public EnviInfo(String filename) { 099 super(filename); 100 this.dataFile = filename.replace(".hdr", ".img"); 101 } 102 103 /** 104 * Is the file an ENVI header file? 105 */ 106 public boolean isEnviHeader() { 107 parseHeader(); 108 return isEnvi; 109 } 110 111 /** 112 * Can we find a matching ENVI data file? 113 */ 114 public boolean hasEnviData() { 115 File testFile = new File(dataFile); 116 if (testFile.exists()) return true; 117 else return false; 118 } 119 120 /** 121 * Is this a navigation header file? 122 */ 123 public boolean isNavHeader() { 124 parseHeader(); 125 List bandNames = (List) getParameter(BANDNAMES, new ArrayList()); 126 if (bandNames == null) return false; 127 if (bandNames.contains("Latitude") && bandNames.contains("Longitude")) return true; 128 return false; 129 } 130 131 /** 132 * Which band/file is latitude? 133 */ 134 public int getLatBandNum() { 135 parseHeader(); 136 List bandNames = (List) getParameter(BANDNAMES, new ArrayList()); 137 for (int i=0; i<bandNames.size(); i++) { 138 if (bandNames.get(i).equals("Latitude")) return i+1; 139 } 140 return -1; 141 } 142 143 public String getLatBandFile() { 144 parseHeader(); 145 List bandFiles = (List) getParameter(BANDFILES, new ArrayList()); 146 int bandNum = getLatBandNum(); 147 if (bandNum < 0) return ""; 148 return (String)(bandFiles.get(bandNum-1)); 149 } 150 151 /** 152 * Which band/file is longitude? 153 */ 154 public int getLonBandNum() { 155 parseHeader(); 156 List bandNames = (List) getParameter(BANDNAMES, new ArrayList()); 157 for (int i=0; i<bandNames.size(); i++) { 158 if (bandNames.get(i).equals("Longitude")) return i+1; 159 } 160 return -1; 161 } 162 163 public String getLonBandFile() { 164 parseHeader(); 165 List bandFiles = (List) getParameter(BANDFILES, new ArrayList()); 166 int bandNum = getLonBandNum(); 167 if (bandNum < 0) return ""; 168 return (String)(bandFiles.get(bandNum-1)); 169 } 170 171 /** 172 * Return a FlatField representing the data 173 */ 174// public FlatField getDataField() { 175// 176// } 177 178 /** 179 * Return a Gridded2DSet representing navigation 180 */ 181// public Gridded2DSet getNavField() { 182// 183// } 184 185 /** 186 * @return the hasBounds 187 */ 188 public boolean isHasBounds() { 189 return hasBounds; 190 } 191 192 /** 193 * @param hasBounds the hasBounds to set 194 */ 195 public void setHasBounds(boolean hasBounds) { 196 this.hasBounds = hasBounds; 197 } 198 199 /** 200 * Parse a potential ENVI header file 201 */ 202 203 protected void parseHeader() { 204 if (haveParsed()) return; 205 if (!doesExist()) { 206 isEnvi = false; 207 return; 208 } 209 210 try { 211 BufferedReader br = new BufferedReader(new FileReader(getFilename())); 212 String line; 213 String parameter = ""; 214 String value = ""; 215 boolean inValue = false; 216 217 List<String> bandNames = new ArrayList<String>(); 218 List bandFiles = new ArrayList(); 219 220 while ((line = br.readLine()) != null) { 221 if (line.trim().equals("ENVI")) { 222 isEnvi = true; 223 continue; 224 } 225 if (!isEnvi) break; 226 227 int indexOfEquals = line.indexOf("="); 228 int indexOfOpen = line.indexOf("{"); 229 int indexOfClose = line.indexOf("}"); 230 if (indexOfEquals >= 0) { 231 parameter = line.substring(0, indexOfEquals).trim(); 232 value = ""; 233 inValue = false; 234 } 235 if (indexOfOpen >= 0) { 236 if (indexOfClose >= 0) { 237 value += line.substring(indexOfOpen+1, indexOfClose).trim(); 238 inValue = false; 239 } 240 else { 241 value += line.substring(indexOfOpen+1).trim(); 242 inValue = true; 243 continue; 244 } 245 } 246 else if (inValue) { 247 if (indexOfClose >= 0) { 248 value += line.substring(0, indexOfClose).trim(); 249 inValue = false; 250 } 251 else { 252 value += line.trim(); 253 continue; 254 } 255 } 256 else { 257 value += line.substring(indexOfEquals+1).trim(); 258 } 259 260 if (parameter.equals("")) continue; 261 262 if (parameter.equals("description")) { 263 setParameter(DESCRIPTION, value); 264 } 265 266 // TJJ Apr 2014 267 // NOTE: method signatures in parent class should be modified or extended 268 // I had to pass in an Integer object here in order to be able to retrieve 269 // anything other than default values later (both "lines" and "samples") 270 271 else if (parameter.equals("samples")) { 272 setParameter(ELEMENTS, new Integer(value)); 273 } 274 else if (parameter.equals("lines")) { 275 setParameter(LINES, new Integer(value)); 276 } 277 278 else if (parameter.equals("header offset")) { 279 setParameter(OFFSET, Integer.parseInt(value)); 280 } 281 else if (parameter.equals("data type")) { 282 setParameter(DATATYPE, Integer.parseInt(value)); 283 } 284 else if (parameter.equals("data ignore value") || 285 parameter.equals("bad value")) { 286 setParameter(MISSINGVALUE, Float.parseFloat(value)); 287 } 288 else if (parameter.equals("interleave")) { 289 setParameter(INTERLEAVE, value.toUpperCase()); 290 } 291 else if (parameter.equals("map info")) { 292 logger.debug("Parsing Map Info, value: " + value); 293 294 ArrayList<String> mapInfo = new ArrayList<String>(); 295 String[] mapInfoSplit = value.split(","); 296 for (int i = 0; i < mapInfoSplit.length; i++) { 297 mapInfo.add(mapInfoSplit[i].trim()); 298 } 299 300 // See www.exelisvis.com/docs/ENVIHeaderFiles.html 301 // for a description of this information 302 // this code handles UTM files 303 304 String projName = mapInfo.get(MapInfoIndex.MAP_INFO_IDX_PROJ_NAME.getIndex()); 305 306 if (projName.equals("UTM")) { 307 308 // zone and hemisphere 309 int utmZone = Integer.parseInt(mapInfo.get(MapInfoIndex.MAP_INFO_IDX_ZONE.getIndex())); 310 boolean utmN = false; 311 if (mapInfo.get(MapInfoIndex.MAP_INFO_IDX_N_OR_S.getIndex()).equals("North")) utmN = true; 312 UtmProjection utmp = new UtmProjection(utmZone, utmN); 313 314 // Java UTM class default units km, adjust if necessary 315 float distFactor = 1.0f; 316 if (mapInfo.get(MapInfoIndex.MAP_INFO_IDX_UNITS.getIndex()).contains("Meters")) distFactor = 1000.0f; 317 318 // figure out Lat/Lon bounding box from Northing/Easting, 319 // resolution, and grid size 320 float upperLeftX = Float.parseFloat(mapInfo.get(MapInfoIndex.MAP_INFO_IDX_EASTING.getIndex())) / distFactor; 321 float upperLeftY = Float.parseFloat(mapInfo.get(MapInfoIndex.MAP_INFO_IDX_NORTHING.getIndex())) / distFactor; 322 323 // lines and samples were already seen 324 int numLines = getParameter(LINES, 0); 325 int numSamples = getParameter(ELEMENTS, 0); 326 327 float xMag = Float.parseFloat(mapInfo.get(MapInfoIndex.MAP_INFO_IDX_X_SIZE.getIndex())); 328 float yMag = Float.parseFloat(mapInfo.get(MapInfoIndex.MAP_INFO_IDX_Y_SIZE.getIndex())); 329 330 float lowerRightX = upperLeftX + ((numSamples * xMag) / distFactor); 331 float lowerRightY = upperLeftY + ((numLines * yMag) / distFactor); 332 333 float [][] from = new float[2][2]; 334 from [0][0] = upperLeftX; 335 from [1][0] = upperLeftY; 336 from [0][1] = lowerRightX; 337 from [1][1] = lowerRightY; 338 float [][] to = new float[2][2]; 339 to = utmp.projToLatLon(from, to); 340 341 // Need to check and see if we are correct in assuming which one is upper left 342 if (to[0][0] > to[0][1]) { 343 setParameter("BOUNDS.ULLAT", "" + to[0][0]); 344 setParameter("BOUNDS.ULLON", "" + to[1][0]); 345 setParameter("BOUNDS.LRLAT", "" + to[0][1]); 346 setParameter("BOUNDS.LRLON", "" + to[1][1]); 347 } else { 348 from [0][0] = upperLeftX; 349 from [1][0] = upperLeftY - ((numLines * yMag) / distFactor); 350 from [0][1] = lowerRightX; 351 from [1][1] = lowerRightY - ((numLines * yMag) / distFactor); 352 to = utmp.projToLatLon(from, to); 353 setParameter("BOUNDS.ULLAT", "" + to[0][1]); 354 setParameter("BOUNDS.ULLON", "" + to[1][0]); 355 setParameter("BOUNDS.LRLAT", "" + to[0][0]); 356 setParameter("BOUNDS.LRLON", "" + to[1][1]); 357 } 358 hasBounds = true; 359 } 360 } 361 else if (parameter.equals("byte order")) { 362 boolean bigEndian = false; 363 if (value.equals("1")) bigEndian = true; 364 setParameter(BIGENDIAN, bigEndian); 365 } 366 else if (parameter.equals("bands")) { 367 if (bandNames.size() <= 0 && bandFiles.size() <= 0) { 368 int bandCount = Integer.parseInt(value); 369 for (int i=0; i<bandCount; i++) { 370 bandNames.add("Band " + i+1); 371 bandFiles.add(dataFile); 372 } 373 setParameter(BANDNAMES, bandNames); 374 setParameter(BANDFILES, bandFiles); 375 } 376 } 377 else if (parameter.equals("band names")) { 378 bandNames = new ArrayList<String>(); 379 bandFiles = new ArrayList<String>(); 380 String[] bandNamesSplit = value.split(","); 381 for (int i=0; i<bandNamesSplit.length; i++) { 382 bandNames.add(bandNamesSplit[i].trim()); 383 bandFiles.add(dataFile); 384 } 385 setParameter(BANDNAMES, bandNames); 386 setParameter(BANDFILES, bandFiles); 387 } 388 389 } 390 br.close(); 391 } catch (FileNotFoundException fnfe) { 392 fnfe.printStackTrace(); 393 } catch (IOException ioe) { 394 ioe.printStackTrace(); 395 } 396 397 } 398 399}