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