All Downloads are FREE. Search and download functionalities are using the official Maven repository.

uk.ac.starlink.fits.AbstractFitsTableWriter Maven / Gradle / Ivy

package uk.ac.starlink.fits;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Date;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.logging.Logger;
import uk.ac.starlink.table.MultiStarTableWriter;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.table.StarTableOutput;
import uk.ac.starlink.table.StreamStarTableWriter;
import uk.ac.starlink.table.TableSequence;
import uk.ac.starlink.table.Tables;
import uk.ac.starlink.util.ConfigMethod;
import uk.ac.starlink.util.DataBufferedOutputStream;
import uk.ac.starlink.util.IOUtils;

/**
 * Abstract table writer superclass designed for writing FITS tables.
 *
 * 

A couple of Auxiliary metadata items of the ColumnInfo metadata * from written tables are respected: *

    *
  • {@link uk.ac.starlink.table.Tables#NULL_VALUE_INFO}: * sets the value of TNULLn "magic" blank value for * integer columns
  • *
  • {@link uk.ac.starlink.table.Tables#UBYTE_FLAG_INFO}: * if set to Boolean.TRUE and if the column has content class * Short or short[], the data will be written * as unsigned bytes (TFORMn='B') * not 16-bit signed integers (TFORMn='I').
  • *
  • {@link BintableStarTable#LONGOFF_INFO}: * if this is set to a string representation of an integer value, * and the column has content class String or String[], * then the data will be written as long integers (TFORMn='K') * with the given offset (TZEROn=...). * This option supports round-tripping of offset long values * (typically representing unsigned longs) which are converted to * strings on read.
  • *
* * @author Mark Taylor * @since 27 Jun 2006 */ public abstract class AbstractFitsTableWriter extends StreamStarTableWriter implements MultiStarTableWriter { private String formatName_; private boolean writeDate_; private boolean allowSignedByte_; private boolean allowZeroLengthString_; private WideFits wide_; private byte padChar_; private static final Logger logger_ = Logger.getLogger( "uk.ac.starlink.fits" ); /** * Constructor. * * @param formatName format name */ protected AbstractFitsTableWriter( String formatName ) { setFormatName( formatName ); allowSignedByte_ = true; allowZeroLengthString_ = true; wide_ = WideFits.DEFAULT; padChar_ = (byte) '\0'; writeDate_ = true; } public String getFormatName() { return formatName_; } /** * Sets the declared format name. * * @param formatName format name */ public void setFormatName( String formatName ) { formatName_ = formatName; } /** * Returns "application/fits". * * @return MIME type */ public String getMimeType() { return "application/fits"; } /** * Writes a single table. * Invokes {@link #writeStarTables}. */ public void writeStarTable( StarTable table, OutputStream out ) throws IOException { writeStarTables( Tables.singleTableSequence( table ), out ); } /** * Writes tables. Calls {@link #writePrimaryHDU(java.io.OutputStream)} * to write the primary HDU. * Subclasses which want to put something related to the input tables * into the primary HDU will need to override this method * (writeStarTables). */ public void writeStarTables( TableSequence tableSeq, OutputStream out ) throws IOException { writePrimaryHDU( out ); for ( StarTable table; ( table = tableSeq.nextTable() ) != null; ) { writeTableHDU( table, createSerializer( table ), out ); } out.flush(); } /** * Invokes {@link #writeStarTables(uk.ac.starlink.table.TableSequence, * java.io.OutputStream)}. */ public void writeStarTables( TableSequence tableSeq, String location, StarTableOutput sto ) throws IOException { OutputStream out = sto.getOutputStream( location ); try { out = new BufferedOutputStream( out ); writeStarTables( tableSeq, out ); out.flush(); } finally { out.close(); } } /** * Writes the primary HDU. This cannot contain a table since BINTABLE * HDUs can only be extensions. * The AbstractFitsTableWriter implementation writes a minimal, data-less * HDU. * * @param out destination stream */ public void writePrimaryHDU( OutputStream out ) throws IOException { FitsUtil.writeEmptyPrimary( out ); } /** * Writes a data HDU. * * @param table the table to be written into the HDU * @param fitser fits serializer initalised from table * @param out destination stream */ public void writeTableHDU( StarTable table, FitsTableSerializer fitser, OutputStream out ) throws IOException { List cards = new ArrayList( Arrays.asList( fitser.getHeader() ) ); cards.addAll( getMetadataCards() ); cards.add( CardFactory.END_CARD ); FitsUtil.writeHeader( cards.toArray( new CardImage[ 0 ] ), out ); DataBufferedOutputStream dout = new DataBufferedOutputStream( out ); fitser.writeData( dout ); dout.flush(); } /** * Returns the configuration details for writing FITS files. * Its content can be controlled using single-config-item * mutator methods * (which may also be labelled as * {@link uk.ac.starlink.util.ConfigMethod}s) * on this class. * This covers things that are generally orthogonal to the type * of serialization, so may be set for any kind of FITS output, * which is why it makes sense to manage them in the * AbstractFitsTableWriter abstract superclass. * * @return object representing the FITS serialization options * currently configured for this writer */ public FitsTableSerializerConfig getConfig() { final boolean allowSignedByte = allowSignedByte_; final boolean allowZeroLengthString = allowZeroLengthString_; final WideFits wide = wide_; final byte padChar = padChar_; return new FitsTableSerializerConfig() { public boolean allowSignedByte() { return allowSignedByte; } public boolean allowZeroLengthString() { return allowZeroLengthString; } public WideFits getWide() { return wide; } public byte getPadCharacter() { return padChar; } }; } /** * Provides a suitable serializer for a given table. * Note this should throw an IOException if it can be determined that * the submitted table cannot be written by this writer, for instance * if it has too many columns. * * @param table table to serialize * @return FITS serializer * @throws IOException if the table can't be written */ protected abstract FitsTableSerializer createSerializer( StarTable table ) throws IOException; /** * Adds some standard metadata header cards to a FITS table header. * This includes date stamp, STIL version, etc. * * @return list of cards giving write-specific metadata */ protected List getMetadataCards() { List cards = new ArrayList<>(); CardFactory cfact = CardFactory.DEFAULT; if ( getWriteDate() ) { cards.add( cfact.createStringCard( "DATE-HDU", getCurrentDate(), "Date of HDU creation (UTC)" ) ); } String stilVers = IOUtils.getResourceContents( StarTable.class, "stil.version", null ); cards.add( cfact.createStringCard( "STILVERS", stilVers, "Version of STIL software" ) ); cards.add( cfact.createStringCard( "STILCLAS", getClass().getName(), "STIL Author class" ) ); return cards; } /** * Configures whether a datestamp is written to output FITS files. * * @param writeDate true to include DATE-HDU, false to omit it */ @ConfigMethod( property = "date", doc = "

If true, the DATE-HDU header is filled in with the current " + "date; otherwise it is not included.

" ) public void setWriteDate( boolean writeDate ) { writeDate_ = writeDate; } /** * Indicates whether a datestamp is written to output FITS files. * * @return true to include DATE-HDU, false to omit it */ public boolean getWriteDate() { return writeDate_; } /** * Configures how Byte-valued columns are written. * This is a bit fiddly, since java bytes are signed, * but FITS 8-bit integers are unsigned. * If true, they are written as FITS unsigned 8-bit integers * with an offset, as discussed in the FITS standard * (TFORMn='B', TZERO=-128). * If false, they are written as FITS signed 16-bit integers. * * @param allowSignedByte true to write offset bytes, * false to write shorts */ public void setAllowSignedByte( boolean allowSignedByte ) { allowSignedByte_ = allowSignedByte; } /** * Returns a flag indicating how Byte-valued columns are written. * * @return true to write offset bytes, false to write shorts */ public boolean getAllowSignedByte() { return allowSignedByte_; } /** * Sets whether zero-length string columns may be written. * Such columns (TFORMn='0A') are explicitly permitted * by the FITS standard, but they can cause crashes because of * divide-by-zero errors when encountered by some versions of CFITSIO * (v3.50 and earlier), so FITS writing code may wish to avoid them. * If this is set false, then A repeat values will always be >=1. * * @param allowZeroLengthString false to prevent zero-length * string columns */ public void setAllowZeroLengthString( boolean allowZeroLengthString ) { allowZeroLengthString_ = allowZeroLengthString; } /** * Indicates whether zero-length string columns may be output. * * @return false if zero-length string columns are avoided */ public boolean getAllowZeroLengthString() { return allowZeroLengthString_; } /** * Sets the convention for representing over-wide (>999 column) tables. * * @param wide wide-table representation policy, * null to avoid wide tables */ public void setWide( WideFits wide ) { wide_ = wide; } /** * Indicates the convention in use for representing over-wide tables. * * @return wide-table representation policy, null for no wide tables */ public WideFits getWide() { return wide_; } /** * Sets the byte value with which under-length string (character array) * values should be padded. * This should normally be one of 0x00 (ASCII NUL) or 0x20 (space). * The function of an ASCII NUL is to terminate the string early, * as described in Section 7.3.3.1 of the FITS 4.0 standard; * space characters pad it to its declared length with whitespace. * Other values in the range 0x21-0x7E are permitted but probably * not sensible. * * @param padChar character data padding byte */ public void setPadCharacter( byte padChar ) { padChar_ = padChar; } /** * Returns the byte value with which under-length string (character array) * values will be padded. * This will normally be one of 0x00 (ASCII NUL) or 0x20 (space). * * @return character data padding byte */ public byte getPadCharacter() { return padChar_; } /** * Returns an ISO-8601 data string representing the time at which this * method is called. * * @return date string */ public static String getCurrentDate() { DateFormat fmt = new SimpleDateFormat( "yyyy-MM-dd'T'HH:mm:ss" ); TimeZone utc = TimeZone.getTimeZone( "UTC" ); fmt.setTimeZone( utc ); fmt.setCalendar( new GregorianCalendar( utc, Locale.UK ) ); return fmt.format( new Date() ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy