001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the COPYING file, which can be found * 009 * at the root of the source code distribution tree, * 010 * or in https://www.hdfgroup.org/licenses. * 011 * If you do not have access to either file, you may request a copy from * 012 * help@hdfgroup.org. * 013 ****************************************************************************/ 014 015package hdf.object.fits; 016 017import java.lang.reflect.Array; 018import java.util.Iterator; 019import java.util.List; 020import java.util.Vector; 021 022import hdf.object.Dataset; 023import hdf.object.Datatype; 024import hdf.object.FileFormat; 025import hdf.object.Group; 026import hdf.object.HObject; 027import hdf.object.MetaDataContainer; 028import hdf.object.ScalarDS; 029import hdf.object.fits.FitsAttribute; 030 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import nom.tam.fits.BasicHDU; 035import nom.tam.fits.Header; 036import nom.tam.fits.HeaderCard; 037 038/** 039 * FitsDataset describes an multi-dimension array of HDF5 scalar or atomic data types, such as byte, int, 040 * short, long, float, double and string, and operations performed on the scalar dataset 041 * 042 * The library predefines a modest number of datatypes. For details, read 043 * <a href="https://hdfgroup.github.io/hdf5/_h5_t__u_g.html#sec_datatype">HDF5 Datatypes in HDF5 User 044 * Guide</a> 045 * 046 * @version 1.1 9/4/2007 047 * @author Peter X. Cao 048 */ 049public class FitsDataset extends ScalarDS implements MetaDataContainer { 050 private static final long serialVersionUID = 3944770379558335171L; 051 052 private static final Logger log = LoggerFactory.getLogger(FitsDataset.class); 053 054 /** 055 * The list of attributes of this data object. Members of the list are 056 * instance of Attribute. 057 */ 058 private List attributeList; 059 060 /** the native dataset */ 061 private BasicHDU nativeDataset; 062 063 /** 064 * Constructs an FitsDataset object with specific netcdf variable. 065 * 066 * @param fileFormat the netcdf file. 067 * @param hdu the BasicHDU. 068 * @param dName the name for this dataset. 069 * @param oid the unique identifier for this dataset. 070 */ 071 public FitsDataset(FileFormat fileFormat, BasicHDU hdu, String dName, long[] oid) 072 { 073 super(fileFormat, dName, HObject.SEPARATOR, oid); 074 unsignedConverted = false; 075 nativeDataset = hdu; 076 } 077 078 /** 079 * Check if the object has any attributes attached. 080 * 081 * @return true if it has any attributes, false otherwise. 082 */ 083 @Override 084 public boolean hasAttribute() 085 { 086 return false; 087 } 088 089 // Implementing Dataset 090 @Override 091 public Dataset copy(Group pgroup, String dstName, long[] dims, Object buff) throws Exception 092 { 093 // not supported 094 throw new UnsupportedOperationException("copy operation unsupported for FITS."); 095 } 096 097 /* 098 * (non-Javadoc) 099 * @see hdf.object.Dataset#readBytes() 100 */ 101 @Override 102 public byte[] readBytes() throws Exception 103 { 104 // not supported 105 throw new UnsupportedOperationException("readBytes operation unsupported for FITS."); 106 } 107 108 /** 109 * Reads the data from file. 110 * 111 * read() reads the data from file to a memory buffer and returns the memory 112 * buffer. The dataset object does not hold the memory buffer. To store the 113 * memory buffer in the dataset object, one must call getData(). 114 * 115 * By default, the whole dataset is read into memory. Users can also select 116 * a subset to read. Subsetting is done in an implicit way. 117 * 118 * @return the data read from file. 119 * 120 * @see #getData() 121 * 122 * @throws Exception 123 * if object can not be read 124 * @throws OutOfMemoryError 125 * if memory is exhausted 126 */ 127 @Override 128 public Object read() throws Exception 129 { 130 Object theData = null; 131 Object fitsData = null; 132 133 if (nativeDataset == null) 134 return null; 135 136 try { 137 fitsData = nativeDataset.getData().getData(); 138 } 139 catch (Exception ex) { 140 throw new UnsupportedOperationException( 141 "This implementation only supports integer and float dataset. " 142 + "It may not work for other datatypes. \n" + ex); 143 } 144 145 int n = get1DLength(fitsData); 146 147 theData = FitsDatatype.allocateArray(nativeDataset.getBitPix(), n); 148 149 to1Darray(fitsData, theData, 0); 150 151 return theData; 152 } 153 154 /** 155 * Writes a memory buffer to the object in the file. 156 * 157 * @param buf 158 * the data to write 159 * 160 * @throws Exception 161 * if data can not be written 162 */ 163 @Override 164 public void write(Object buf) throws Exception 165 { 166 // not supported 167 throw new UnsupportedOperationException("write operation unsupported for FITS."); 168 } 169 170 /** 171 * Retrieves the object's metadata, such as attributes, from the file. 172 * 173 * Metadata, such as attributes, is stored in a List. 174 * 175 * @return the list of metadata objects. 176 * 177 * @throws Exception 178 * if the metadata can not be retrieved 179 */ 180 @SuppressWarnings("rawtypes") 181 public List getMetadata() throws Exception 182 { 183 if (attributeList != null) 184 return attributeList; 185 186 if (nativeDataset == null) 187 return null; 188 189 Header header = nativeDataset.getHeader(); 190 if (header == null) 191 return null; 192 193 attributeList = new Vector(); 194 HeaderCard hc = null; 195 Iterator it = header.iterator(); 196 FitsAttribute attr = null; 197 Datatype dtype = new FitsDatatype(Datatype.CLASS_STRING, 80, 0, 0); 198 long[] dims = {1}; 199 String value = null; 200 while (it.hasNext()) { 201 value = ""; 202 hc = (HeaderCard)it.next(); 203 attr = new FitsAttribute(this, hc.getKey(), dtype, dims); 204 String tvalue = hc.getValue(); 205 if (tvalue != null) 206 value += tvalue; 207 tvalue = hc.getComment(); 208 if (tvalue != null) 209 value += " / " + tvalue; 210 attr.setAttributeData(value); 211 attributeList.add(attr); 212 } 213 214 return attributeList; 215 } 216 217 /** 218 * Writes a specific piece of metadata (such as an attribute) into the file. 219 * 220 * If an HDF(4&5) attribute exists in the file, this method updates its 221 * value. If the attribute does not exist in the file, it creates the 222 * attribute in the file and attaches it to the object. It will fail to 223 * write a new attribute to the object where an attribute with the same name 224 * already exists. To update the value of an existing attribute in the file, 225 * one needs to get the instance of the attribute by getMetadata(), change 226 * its values, then use writeMetadata() to write the value. 227 * 228 * @param info 229 * the metadata to write. 230 * 231 * @throws Exception 232 * if the metadata can not be written 233 */ 234 public void writeMetadata(Object info) throws Exception 235 { 236 // not supported 237 throw new UnsupportedOperationException("writeMetadata operation unsupported for FITS."); 238 } 239 240 /** 241 * Deletes an existing piece of metadata from this object. 242 * 243 * @param info 244 * the metadata to delete. 245 * 246 * @throws Exception 247 * if the metadata can not be removed 248 */ 249 public void removeMetadata(Object info) throws Exception 250 { 251 // not supported 252 throw new UnsupportedOperationException("removeMetadata operation unsupported for FITS."); 253 } 254 255 /** 256 * Updates an existing piece of metadata attached to this object. 257 * 258 * @param info 259 * the metadata to update. 260 * 261 * @throws Exception 262 * if the metadata can not be updated 263 */ 264 public void updateMetadata(Object info) throws Exception 265 { 266 // not supported 267 throw new UnsupportedOperationException("updateMetadata operation unsupported for FITS."); 268 } 269 270 /* 271 * (non-Javadoc) 272 * @see hdf.object.HObject#open() 273 */ 274 @Override 275 public long open() 276 { 277 return -1; 278 } 279 280 /* 281 * (non-Javadoc) 282 * @see hdf.object.HObject#close(int) 283 */ 284 @Override 285 public void close(long did) 286 { 287 // Nothing to implement 288 } 289 290 /* 291 * (non-Javadoc) 292 * @see hdf.object.Dataset#init() 293 */ 294 @Override 295 public void init() 296 { 297 if (nativeDataset == null) 298 return; 299 300 if (inited) 301 return; // already called. Initialize only once 302 303 int[] axes = null; 304 try { 305 axes = nativeDataset.getAxes(); 306 } 307 catch (Exception ex) { 308 log.debug("nativeDataset.getAxes():", ex); 309 } 310 311 if (axes == null) 312 return; 313 314 rank = axes.length; 315 if (rank == 0) { 316 // a scalar data point 317 isScalar = true; 318 rank = 1; 319 dims = new long[] {1}; 320 } 321 else { 322 isScalar = false; 323 dims = new long[rank]; 324 for (int i = 0; i < rank; i++) 325 dims[i] = axes[i]; 326 } 327 328 startDims = new long[rank]; 329 selectedDims = new long[rank]; 330 for (int i = 0; i < rank; i++) { 331 startDims[i] = 0; 332 selectedDims[i] = 1; 333 } 334 335 if (rank == 1) { 336 selectedIndex[0] = 0; 337 selectedDims[0] = dims[0]; 338 } 339 else if (rank == 2) { 340 selectedIndex[0] = 0; 341 selectedIndex[1] = 1; 342 selectedDims[0] = dims[0]; 343 selectedDims[1] = dims[1]; 344 } 345 else if (rank > 2) { 346 selectedIndex[0] = 0; 347 selectedIndex[1] = 1; 348 selectedIndex[2] = 2; 349 selectedDims[0] = dims[0]; 350 selectedDims[1] = dims[1]; 351 } 352 353 if ((rank > 1) && isText) 354 selectedDims[1] = 1; 355 356 inited = true; 357 } 358 359 /* Implement abstart ScalarDS */ 360 361 /** 362 * Creates a new dataset. 363 * 364 * @param name the name of the dataset to create. 365 * @param pgroup the parent group of the new dataset. 366 * @param type the datatype of the dataset. 367 * @param dims the dimension size of the dataset. 368 * @param maxdims the max dimension size of the dataset. 369 * @param chunks the chunk size of the dataset. 370 * @param gzip the level of the gzip compression. 371 * @param data the array of data values. 372 * 373 * @return the new dataset if successful. Otherwise returns null. 374 * 375 * @throws Exception 376 * if there is an error 377 */ 378 public static FitsDataset create(String name, Group pgroup, Datatype type, long[] dims, long[] maxdims, 379 long[] chunks, int gzip, Object data) throws Exception 380 { 381 // not supported 382 throw new UnsupportedOperationException("Unsupported operation for FITS."); 383 } 384 385 /** 386 * Returns the datatype of the data object. 387 * 388 * @return the datatype of the data object. 389 */ 390 @Override 391 public Datatype getDatatype() 392 { 393 if (datatype == null) { 394 try { 395 datatype = new FitsDatatype(nativeDataset.getBitPix()); 396 } 397 catch (Exception ex) { 398 log.debug("getDatatype(): failed to create datatype: ", ex); 399 datatype = null; 400 } 401 } 402 403 return datatype; 404 } 405 406 /* 407 * (non-Javadoc) 408 * @see hdf.object.HObject#setName(java.lang.String) 409 */ 410 @Override 411 public void setName(String newName) throws Exception 412 { 413 // not supported 414 throw new UnsupportedOperationException("Unsupported operation for FITS."); 415 } 416 417 private int get1DLength(Object data) throws Exception 418 { 419 if (!data.getClass().isArray()) 420 return 1; 421 422 int len = Array.getLength(data); 423 424 int total = 0; 425 for (int i = 0; i < len; i++) 426 total += get1DLength(Array.get(data, i)); 427 428 return total; 429 } 430 431 /** copy multi-dimension array of fits data into 1D array */ 432 private int to1Darray(Object dataIn, Object dataOut, int offset) throws Exception 433 { 434 Class component = dataIn.getClass().getComponentType(); 435 if (component == null) 436 return offset; 437 438 int size = Array.getLength(dataIn); 439 if (!component.isArray()) { 440 System.arraycopy(dataIn, 0, dataOut, offset, size); 441 return offset + size; 442 } 443 444 for (int i = size - 1; i >= 0; i--) 445 offset = to1Darray(Array.get(dataIn, i), dataOut, offset); 446 447 return offset; 448 } 449 450 // Implementing DataFormat 451 /* FITS does not support metadata */ 452 /** 453 * Retrieves the object's metadata, such as attributes, from the file. 454 * 455 * Metadata, such as attributes, is stored in a List. 456 * 457 * @param attrPropList 458 * the list of properties to get 459 * 460 * @return the list of metadata objects. 461 * 462 * @throws Exception 463 * if the metadata can not be retrieved 464 */ 465 public List getMetadata(int... attrPropList) throws Exception 466 { 467 throw new UnsupportedOperationException("getMetadata(int... attrPropList) is not supported"); 468 } 469}