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