1 package nom.tam.image.compression.hdu;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.List;
6 import java.util.Locale;
7
8 import nom.tam.fits.BinaryTable;
9 import nom.tam.fits.FitsException;
10 import nom.tam.fits.FitsFactory;
11 import nom.tam.fits.Header;
12 import nom.tam.fits.header.Compression;
13 import nom.tam.fits.header.Standard;
14 import nom.tam.image.compression.bintable.BinaryTableTile;
15 import nom.tam.image.compression.bintable.BinaryTableTileCompressor;
16 import nom.tam.image.compression.bintable.BinaryTableTileDecompressor;
17 import nom.tam.image.compression.bintable.BinaryTableTileDescription;
18 import nom.tam.util.ColumnTable;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 import static nom.tam.fits.header.Standard.TFIELDS;
52 import static nom.tam.image.compression.bintable.BinaryTableTileDescription.tile;
53
54
55
56
57
58
59
60 @SuppressWarnings("deprecation")
61 public class CompressedTableData extends BinaryTable {
62
63 private static final List<String> ALLOWED_ALGORITHMS = Arrays.asList(Compression.ZCMPTYPE_GZIP_1,
64 Compression.ZCMPTYPE_GZIP_2, Compression.ZCMPTYPE_RICE_1, Compression.ZCMPTYPE_NOCOMPRESS);
65
66 private int rowsPerTile;
67
68 private List<BinaryTableTile> tiles;
69
70 private BinaryTable orig;
71
72
73 private boolean isPrepped;
74
75 private String[] colAlgorithm;
76
77
78
79
80 public CompressedTableData() {
81 }
82
83
84
85
86
87
88
89
90 public CompressedTableData(Header header) throws FitsException {
91 super(header);
92 rowsPerTile = header.getIntValue(Compression.ZTILELEN, header.getIntValue(Standard.NAXIS2));
93 setColumnCompressionAlgorithms(header);
94 }
95
96
97
98
99
100
101
102
103
104 public void compress(Header header) throws FitsException {
105 discardVLAs();
106
107
108 for (BinaryTableTile tile : tiles) {
109 tile.execute(FitsFactory.threadPool());
110 }
111
112 for (BinaryTableTile tile : tiles) {
113 tile.waitForResult();
114 }
115 }
116
117 @Override
118 public synchronized long defragment() throws FitsException {
119 if (orig != null && orig.containsHeap()) {
120
121
122
123 return 0L;
124 }
125 return super.defragment();
126 }
127
128 @Override
129 public void fillHeader(Header h) throws FitsException {
130 super.fillHeader(h);
131
132 h.setNaxis(2, getData().getNRows());
133 h.addValue(Compression.ZTABLE, true);
134 h.addValue(Compression.ZTILELEN, getRowsPerTile());
135
136 for (int i = 0; i < getNCols(); i++) {
137 h.findCard(Compression.ZFORMn.n(i + 1));
138 h.addValue(Compression.ZCTYPn.n(i + 1), getAlgorithm(i));
139 }
140
141 h.deleteKey(Compression.ZIMAGE);
142 }
143
144 void prepareUncompressedData(BinaryTable fromTable) throws FitsException {
145 orig = fromTable;
146 prepareUncompressedData(orig.getData());
147 }
148
149
150
151
152
153
154
155
156 @Deprecated
157 @SuppressWarnings("javadoc")
158 public void prepareUncompressedData(ColumnTable<?> data) throws FitsException {
159 tiles = new ArrayList<>();
160
161 int nrows = data.getNRows();
162 int ncols = data.getNCols();
163
164 if (!isPrepped) {
165
166 for (int column = 0; column < ncols; column++) {
167 addColumn(BinaryTable.ColumnDesc.createForVariableSize(byte.class));
168 getDescriptor(column).name(null);
169 }
170
171
172 for (int rowStart = 0; rowStart < nrows; rowStart += getRowsPerTile()) {
173 addRow(new byte[ncols][0]);
174 }
175 }
176
177
178 for (int column = 0; column < ncols; column++) {
179 for (int tileIndex = 0, rowStart = 0; rowStart < nrows; tileIndex++, rowStart += getRowsPerTile()) {
180
181 BinaryTableTileDescription td = tile()
182 .rowStart(rowStart)
183 .rowEnd(Math.min(nrows, rowStart + getRowsPerTile()))
184 .column(column)
185 .tileIndex(tileIndex + 1)
186 .compressionAlgorithm(getAlgorithm(column));
187
188 BinaryTableTileCompressor tile = (orig == null) ? new BinaryTableTileCompressor(this, data, td) :
189 new BinaryTableTileCompressor(this, orig, td);
190
191 tiles.add(tile);
192 }
193 }
194
195 isPrepped = true;
196 }
197
198
199
200
201 @SuppressWarnings("javadoc")
202 protected BinaryTable asBinaryTable(BinaryTable toTable, Header compressedHeader, Header targetHeader)
203 throws FitsException {
204 return asBinaryTable(toTable, compressedHeader, targetHeader, 0);
205 }
206
207 BinaryTable asBinaryTable(BinaryTable toTable, Header compressedHeader, Header targetHeader, int fromTile)
208 throws FitsException {
209 int nrows = targetHeader.getIntValue(Standard.NAXIS2);
210 int ncols = compressedHeader.getIntValue(TFIELDS);
211 int tileSize = compressedHeader.getIntValue(Compression.ZTILELEN, nrows);
212
213 ensureData();
214 setColumnCompressionAlgorithms(compressedHeader);
215
216 BinaryTable.createColumnDataFor(toTable);
217
218 List<BinaryTableTile> tileList = new ArrayList<>();
219
220 for (int tileIndex = fromTile, rowStart = 0; rowStart < nrows; tileIndex++, rowStart += tileSize) {
221 for (int column = 0; column < ncols; column++) {
222 BinaryTableTileDecompressor tile = new BinaryTableTileDecompressor(this, toTable, tile()
223 .rowStart(rowStart)
224 .rowEnd(Math.min(nrows, rowStart + tileSize))
225 .column(column)
226 .tileIndex(tileIndex + 1)
227 .compressionAlgorithm(getAlgorithm(column)));
228 tileList.add(tile);
229
230 tile.execute(FitsFactory.threadPool());
231 }
232 }
233
234 for (BinaryTableTile tile : tileList) {
235 tile.waitForResult();
236 }
237
238 return toTable;
239 }
240
241 Object getColumnData(int col, int fromTile, int toTile, Header compressedHeader, Header targetHeader)
242 throws FitsException {
243
244 if (fromTile < 0 || fromTile >= getNRows()) {
245 throw new IllegalArgumentException("start tile " + fromTile + " is outof bounds for " + getNRows() + " tiles.");
246 }
247
248 if (toTile > getNRows()) {
249 throw new IllegalArgumentException("end tile " + toTile + " is outof bounds for " + getNRows() + " tiles.");
250 }
251
252 if (toTile <= fromTile) {
253 return null;
254 }
255
256 setColumnCompressionAlgorithms(compressedHeader);
257
258 int nr = targetHeader.getIntValue(Standard.NAXIS2);
259
260 int tileSize = compressedHeader.getIntValue(Compression.ZTILELEN, nr);
261 int nRows = (toTile - fromTile) * tileSize;
262
263 if (nRows > nr) {
264 nRows = nr;
265 }
266
267 ColumnDesc c = getDescriptor(targetHeader, col);
268 class UncompressedTable extends BinaryTable {
269 @Override
270 public void createTable(int nRows) throws FitsException {
271 super.createTable(nRows);
272 }
273 }
274
275 UncompressedTable data = new UncompressedTable();
276 data.addColumn(c);
277 data.createTable(nRows);
278
279 List<BinaryTableTile> tileList = new ArrayList<>();
280
281 String algorithm = compressedHeader.getStringValue(Compression.ZCTYPn.n(col + 1));
282
283 for (int tileIndex = fromTile, rowStart = 0; rowStart < nRows; tileIndex++, rowStart += tileSize) {
284 BinaryTableTileDecompressor tile = new BinaryTableTileDecompressor(this, data, tile()
285 .rowStart(rowStart)
286 .rowEnd(Math.min(nr, rowStart + tileSize))
287 .column(col)
288 .tileIndex(tileIndex + 1)
289 .compressionAlgorithm(algorithm));
290 tile.decompressToColumn(0);
291 tileList.add(tile);
292
293 tile.execute(FitsFactory.threadPool());
294 }
295
296 for (BinaryTableTile tile : tileList) {
297 tile.waitForResult();
298 }
299
300 return data.getColumn(0);
301 }
302
303
304
305
306
307
308
309 protected final synchronized int getRowsPerTile() {
310 return rowsPerTile;
311 }
312
313 private String getAlgorithm(int column) {
314 if (colAlgorithm != null && column < colAlgorithm.length && colAlgorithm[column] != null) {
315 return colAlgorithm[column];
316 }
317 return Compression.ZCMPTYPE_GZIP_2;
318 }
319
320
321
322
323
324 @SuppressWarnings("javadoc")
325 protected void setColumnCompressionAlgorithms(String[] columnCompressionAlgorithms) {
326 for (String algo : columnCompressionAlgorithms) {
327 if (!ALLOWED_ALGORITHMS.contains(algo.toUpperCase(Locale.US))) {
328 throw new IllegalArgumentException(algo + " cannot be used to compress tables.");
329 }
330 }
331
332 this.colAlgorithm = columnCompressionAlgorithms;
333 }
334
335 private void setColumnCompressionAlgorithms(Header header) {
336 int ncols = header.getIntValue(TFIELDS);
337
338
339 colAlgorithm = new String[ncols];
340
341
342 for (int column = 0; column < ncols; column++) {
343 colAlgorithm[column] = header.getStringValue(Compression.ZCTYPn.n(column + 1));
344 }
345 }
346
347
348
349
350
351 @SuppressWarnings("javadoc")
352 protected synchronized CompressedTableData setRowsPerTile(int value) {
353 rowsPerTile = value;
354 return this;
355 }
356 }