001/* 002 * LaWGSGeomReader -- A class that can read a LaWGS formatted geometry file. 003 * 004 * Copyright (C) 2009-2016, Joseph A. Huwaldt 005 * All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public License 018 * along with this program; if not, write to the Free Software 019 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 020 * Or visit: http://www.gnu.org/licenses/lgpl.html 021 */ 022package geomss.geom.reader; 023 024import geomss.geom.*; 025import static geomss.geom.reader.AbstractGeomReader.RESOURCES; 026import jahuwaldt.js.util.TextTokenizer; 027import java.io.File; 028import java.io.FileReader; 029import java.io.IOException; 030import java.io.LineNumberReader; 031import java.text.MessageFormat; 032import java.util.Locale; 033import static java.util.Objects.isNull; 034import static java.util.Objects.nonNull; 035import static java.util.Objects.requireNonNull; 036import javolution.text.Text; 037import javolution.text.TypeFormat; 038import javolution.util.FastTable; 039 040/** 041 * A {@link GeomReader} for reading vehicle geometry from a LaWGS (WGS) formatted geometry 042 * file. This is the Langley Wireframe Geometry Standard (LaWGS) defined in NASA-TM-85767. 043 * This implementation ignores the local symmetry and local transformation information! 044 * 045 * <p> Modified by: Joseph A. Huwaldt </p> 046 * 047 * @author Joseph A. Huwaldt, Date: April 27, 2009 048 * @version September 9, 2016 049 */ 050public class LaWGSGeomReader extends AbstractGeomReader { 051 052 // Debug output flag. 053 private static final boolean DEBUG = false; 054 055 // A brief description of the data read by this reaader. 056 private static final String DESCRIPTION = RESOURCES.getString("lawgsDescription"); 057 058 // The preferred file extension for files of this reader's type. 059 public static final String EXTENSION = "wgs"; 060 061 // Any of the delimiters that could be found in the file. 062 private static final String DELIMITERS = " ,/"; 063 064 /** 065 * Returns a string representation of the object. This will return a brief description 066 * of the format read by this reader. 067 * 068 * @return A brief description of the format read by this reader. 069 */ 070 @Override 071 public String toString() { 072 return DESCRIPTION; 073 } 074 075 /** 076 * Returns the preferred file extension (not including the ".") for files of this 077 * GeomReader's type. 078 * 079 * @return The preferred file extension for files of this readers type. 080 */ 081 @Override 082 public String getExtension() { 083 return EXTENSION; 084 } 085 086 /** 087 * Method that determines if this reader can read geometry from the specified input 088 * file. 089 * 090 * @param inputFile The input file containing the geometry to be read in. 091 * @return GeomReader.NO if the file format is not recognized by this reader. 092 * GeomReader.YES if the file format is definitely recognized by this reader. 093 * GeomReader.MAYBE if the file format might be readable by this reader, but 094 * that can't easily be determined without actually reading the file. 095 * @throws java.io.IOException If there is a problem reading from the specified 096 * file. 097 */ 098 @Override 099 public int canReadData(File inputFile) throws IOException { 100 101 int response = NO; 102 String name = inputFile.getName(); 103 name = name.toLowerCase().trim(); 104 if (name.endsWith(".wgs")) 105 response = MAYBE; 106 107 return response; 108 } 109 110 /** 111 * Reads in a LaWGS geometry file from the specified input file and returns a 112 * {@link PointVehicle} object that contains the geometry from the LaWGS file. 113 * <p> 114 * WARNING: This file format is not unit aware. You must set the units 115 * to be used by calling "setFileUnits()" before calling this method! 116 * </p> 117 * 118 * @param inputFile The input file containing the geometry to be read in. May not be 119 * null. 120 * @return A {@link PointVehicle} object containing the geometry read in from the 121 * file. If the file has no geometry in it, then this list will have no 122 * components in it (will have a size() of zero). 123 * @throws IOException If there is a problem reading the specified file. 124 * @see #setFileUnits(javax.measure.unit.Unit) 125 */ 126 @Override 127 public PointVehicle read(File inputFile) throws IOException { 128 requireNonNull(inputFile); 129 _warnings.clear(); 130 131 // Create an empty vehicle. 132 PointVehicle vehicle = PointVehicle.newInstance(); 133 134 // LaWGS files are required to be in ASCII with U.S. style number formatting. 135 // Get the default locale and save it. Then change the default locale to US. 136 Locale defLocale = Locale.getDefault(); 137 Locale.setDefault(Locale.US); 138 139 // Create a reader to access the ASCII file. 140 try (LineNumberReader reader = new LineNumberReader(new FileReader(inputFile))) { 141 142 143 // Skip any leading blank lines. 144 String aLine = readLine(reader).trim(); 145 while (aLine.length() == 0) 146 aLine = readLine(reader).trim(); 147 148 // Extract the configuration identification. 149 String idcomf = parseQuotedText(Text.valueOf(aLine), "'"); 150 if (isNull(idcomf)) 151 idcomf = inputFile.getName(); 152 153 // Set the name as the vehicle name. 154 vehicle.setName(idcomf); 155 156 // There is only a single component, so create it. 157 PointComponent comp = PointComponent.newInstance("Component"); 158 vehicle.add(comp); 159 160 // Loop over all the arrays stored in the file. 161 aLine = reader.readLine(); 162 while (nonNull(aLine)) { 163 164 String arrName = parseQuotedText(Text.valueOf(aLine), "'"); 165 PointArray array = readArray(reader, arrName); 166 comp.add(array); 167 168 // Begin searching for the next component. 169 aLine = reader.readLine(); 170 } 171 172 } finally { 173 // Restore the default locale. 174 Locale.setDefault(defLocale); 175 } 176 177 return vehicle; 178 } 179 180 /** 181 * This method always returns <code>false</code> as LaWGS files do not encode the 182 * units that are being used. You must call <code>setFileUnits</code> to set the units 183 * being used before reading or writing to a file of this format. 184 * 185 * @return this implementation always returns false 186 * @see #setFileUnits(javax.measure.unit.Unit) 187 */ 188 @Override 189 public boolean isUnitAware() { 190 return false; 191 } 192 193 /** 194 * Reads a single array or network from an input stream (pointing to the appropriate 195 * location in a LaWGS file). 196 * 197 * @param in Reader for the LaWGS file we are reading (positioned so that the 198 * next read will occur on the line following the line containing the 199 * name of the array). 200 * @param arrayName The name of the array. 201 * @return The network read in from the file. 202 * @throws java.io.IOException if there is a problem reading the array. 203 */ 204 private PointArray readArray(LineNumberReader in, String arrayName) throws IOException { 205 PointArray net = null; 206 207 // Create the needed tables. 208 FastTable<Point> pointList = FastTable.newInstance(); 209 FastTable<PointString<Point>> stringList = FastTable.newInstance(); 210 211 try { 212 // Read in the object information line. 213 String aLine = readLine(in); 214 TextTokenizer tokenizer = TextTokenizer.valueOf(aLine, DELIMITERS); 215 if (tokenizer.countTokens() != 14) 216 throw new IOException(MessageFormat.format( 217 RESOURCES.getString("parseErrMsg"), DESCRIPTION, in.getLineNumber())); 218 219 // Parse out the object ID number. 220 Text token = tokenizer.nextToken(); 221 int nObj = TypeFormat.parseInt(token); 222 223 // Parse out the number of contour lines. 224 token = tokenizer.nextToken(); 225 int nLine = TypeFormat.parseInt(token); 226 227 // Parse out the number of points in each contour line. 228 token = tokenizer.nextToken(); 229 int nPnt = TypeFormat.parseInt(token); 230 231 // Do a sanity check. 232 if (nLine < 0 || nLine > 1000000 || nPnt < 0 || nPnt > 1000000) 233 throw new IOException(MessageFormat.format( 234 RESOURCES.getString("incRowsColsErr"), DESCRIPTION, in.getLineNumber())); 235 236 // Parse out the local symmetry code. 237 token = tokenizer.nextToken(); 238 int iSymL = TypeFormat.parseInt(token); 239 if (iSymL < 0 || iSymL > 3) 240 throw new IOException(MessageFormat.format( 241 RESOURCES.getString("lawgsUnknownLocalSymm"), in.getLineNumber())); 242 243 // Parse out the rotation angles. 244 token = tokenizer.nextToken(); 245 double rx = TypeFormat.parseDouble(token); 246 247 token = tokenizer.nextToken(); 248 double ry = TypeFormat.parseDouble(token); 249 250 token = tokenizer.nextToken(); 251 double rz = TypeFormat.parseDouble(token); 252 253 // Parse out the translations. 254 token = tokenizer.nextToken(); 255 double tx = TypeFormat.parseDouble(token); 256 257 token = tokenizer.nextToken(); 258 double ty = TypeFormat.parseDouble(token); 259 260 token = tokenizer.nextToken(); 261 double tz = TypeFormat.parseDouble(token); 262 263 // Parse out the scales in each axis. 264 token = tokenizer.nextToken(); 265 double xScale = TypeFormat.parseDouble(token); 266 267 token = tokenizer.nextToken(); 268 double yScale = TypeFormat.parseDouble(token); 269 270 token = tokenizer.nextToken(); 271 double zScale = TypeFormat.parseDouble(token); 272 273 // Parse out the global symmetry code. 274 token = tokenizer.nextToken(); 275 int iSymG = TypeFormat.parseInt(token); 276 if (iSymG < 0 || iSymG > 3) 277 throw new IOException(MessageFormat.format( 278 RESOURCES.getString("lawgsUnknownGlobalSymm"), in.getLineNumber())); 279 280 if (DEBUG) { 281 System.out.println("arrayName = " + arrayName + ", nLine = " + nLine + ", nPnt = " + nPnt + ", iSymL = " + iSymL); 282 System.out.println(" rx,ry,rz = " + rx + "," + ry + "," + rz); 283 System.out.println(" tx,ty,tz = " + tx + "," + ty + "," + tz); 284 System.out.println(" sx,sy,sz = " + xScale + "," + yScale + "," + zScale); 285 System.out.println(" iSymG = " + iSymG); 286 } 287 288 // Loop over each line. 289 for (int i = 0; i < nLine; ++i) { 290 if (DEBUG) 291 System.out.println("i = " + i); 292 aLine = readLine(in); 293 tokenizer.setText(aLine); 294 if (tokenizer.countTokens() != 3 && tokenizer.countTokens() != 6) 295 throw new IOException(MessageFormat.format( 296 RESOURCES.getString("incPointCount"), in.getLineNumber())); 297 298 int pntsPerLine = tokenizer.countTokens() / 3; 299 300 // Loop over each point. 301 int count = 1; 302 for (int j = 0; j < nPnt; ++j) { 303 304 // There may be a limited number of points per line. 305 if (count > pntsPerLine) { 306 count = 1; 307 aLine = readLine(in); 308 tokenizer.setText(aLine); 309 if (tokenizer.countTokens() != 3 && tokenizer.countTokens() != 6) 310 throw new IOException(MessageFormat.format( 311 RESOURCES.getString("incPointCount"), in.getLineNumber())); 312 } 313 314 // Read in X coordinate. 315 token = tokenizer.nextToken(); 316 double xValue = TypeFormat.parseDouble(token); 317 318 // Read in Y coordinate. 319 token = tokenizer.nextToken(); 320 double yValue = TypeFormat.parseDouble(token); 321 322 // Read in Z coordinate. 323 token = tokenizer.nextToken(); 324 double zValue = TypeFormat.parseDouble(token); 325 326 if (DEBUG) 327 System.out.println("col = " + j + ", x,y,z = " + xValue + ", " + yValue + ", " + zValue); 328 329 // Create a Point object. 330 Point point = Point.valueOf(xValue, yValue, zValue, getFileUnits()); 331 pointList.add(point); 332 333 ++count; 334 } 335 336 // Create a PointString object from the points. 337 PointString<Point> string = PointString.valueOf(null, pointList); 338 stringList.add(string); 339 340 // Clear the point list for the next row. 341 pointList.clear(); 342 } 343 344 // Create a new network from the points just read in. 345 net = PointArray.valueOf(arrayName, stringList); 346 347 } catch (NumberFormatException e) { 348 e.printStackTrace(); 349 throw new IOException( 350 MessageFormat.format(RESOURCES.getString("parseErrMsg"), 351 DESCRIPTION, in.getLineNumber())); 352 353 } finally { 354 // Recycle the lists. 355 FastTable.recycle(pointList); 356 FastTable.recycle(stringList); 357 } 358 359 return net; 360 } 361 362 /** 363 * Method that parses out quoted text from a string of text and returns whatever is 364 * between the quotes. 365 * 366 * @param input The text containing a quote. 367 * @param quoteChar The character that delimits the quote. 368 * @return All the text between the 1st and last occurrence of quoteChar in the input 369 * Text. 370 */ 371 private static String parseQuotedText(Text input, CharSequence quoteChar) { 372 int idx1 = input.indexOf(quoteChar, 0) + 1; 373 int idx2 = input.lastIndexOf(quoteChar); 374 Text output; 375 if (idx1 < 0 || idx2 < 0) 376 output = input; 377 else 378 output = input.subtext(idx1, idx2); 379 380 if (isNull(output)) 381 return null; 382 383 output = output.trim(); 384 if (Text.EMPTY.equals(output)) 385 return null; 386 return output.toString(); 387 } 388 389}