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 @SuppressWarnings("javadoc")
157 public void prepareUncompressedData(ColumnTable<?> data) throws FitsException {
158 tiles = new ArrayList<>();
159
160 int nrows = data.getNRows();
161 int ncols = data.getNCols();
162
163 if (!isPrepped) {
164
165 for (int column = 0; column < ncols; column++) {
166 addColumn(BinaryTable.ColumnDesc.createForVariableSize(byte.class));
167 getDescriptor(column).name(null);
168 }
169
170
171 for (int rowStart = 0; rowStart < nrows; rowStart += getRowsPerTile()) {
172 addRow(new byte[ncols][0]);
173 }
174 }
175
176
177 for (int column = 0; column < ncols; column++) {
178 for (int tileIndex = 0, rowStart = 0; rowStart < nrows; tileIndex++, rowStart += getRowsPerTile()) {
179
180 BinaryTableTileDescription td = tile()
181 .rowStart(rowStart)
182 .rowEnd(Math.min(nrows, rowStart + getRowsPerTile()))
183 .column(column)
184 .tileIndex(tileIndex + 1)
185 .compressionAlgorithm(getAlgorithm(column));
186
187 BinaryTableTileCompressor tile = (orig == null) ? new BinaryTableTileCompressor(this, data, td) :
188 new BinaryTableTileCompressor(this, orig, td);
189
190 tiles.add(tile);
191 }
192 }
193
194 isPrepped = true;
195 }
196
197
198
199
200 @SuppressWarnings("javadoc")
201 protected BinaryTable asBinaryTable(BinaryTable toTable, Header compressedHeader, Header targetHeader)
202 throws FitsException {
203 return asBinaryTable(toTable, compressedHeader, targetHeader, 0);
204 }
205
206 BinaryTable asBinaryTable(BinaryTable toTable, Header compressedHeader, Header targetHeader, int fromTile)
207 throws FitsException {
208 int nrows = targetHeader.getIntValue(Standard.NAXIS2);
209 int ncols = compressedHeader.getIntValue(TFIELDS);
210 int tileSize = compressedHeader.getIntValue(Compression.ZTILELEN, nrows);
211
212 ensureData();
213 setColumnCompressionAlgorithms(compressedHeader);
214
215 BinaryTable.createColumnDataFor(toTable);
216
217 List<BinaryTableTile> tileList = new ArrayList<>();
218
219 for (int tileIndex = fromTile, rowStart = 0; rowStart < nrows; tileIndex++, rowStart += tileSize) {
220 for (int column = 0; column < ncols; column++) {
221 BinaryTableTileDecompressor tile = new BinaryTableTileDecompressor(this, toTable, tile()
222 .rowStart(rowStart)
223 .rowEnd(Math.min(nrows, rowStart + tileSize))
224 .column(column)
225 .tileIndex(tileIndex + 1)
226 .compressionAlgorithm(getAlgorithm(column)));
227 tileList.add(tile);
228
229 tile.execute(FitsFactory.threadPool());
230 }
231 }
232
233 for (BinaryTableTile tile : tileList) {
234 tile.waitForResult();
235 }
236
237 return toTable;
238 }
239
240 Object getColumnData(int col, int fromTile, int toTile, Header compressedHeader, Header targetHeader)
241 throws FitsException {
242
243 if (fromTile < 0 || fromTile >= getNRows()) {
244 throw new IllegalArgumentException("start tile " + fromTile + " is outof bounds for " + getNRows() + " tiles.");
245 }
246
247 if (toTile > getNRows()) {
248 throw new IllegalArgumentException("end tile " + toTile + " is outof bounds for " + getNRows() + " tiles.");
249 }
250
251 if (toTile <= fromTile) {
252 return null;
253 }
254
255 setColumnCompressionAlgorithms(compressedHeader);
256
257 int nr = targetHeader.getIntValue(Standard.NAXIS2);
258
259 int tileSize = compressedHeader.getIntValue(Compression.ZTILELEN, nr);
260 int nRows = (toTile - fromTile) * tileSize;
261
262 if (nRows > nr) {
263 nRows = nr;
264 }
265
266 ColumnDesc c = getDescriptor(targetHeader, col);
267 class UncompressedTable extends BinaryTable {
268 @Override
269 public void createTable(int nRows) throws FitsException {
270 super.createTable(nRows);
271 }
272 }
273
274 UncompressedTable data = new UncompressedTable();
275 data.addColumn(c);
276 data.createTable(nRows);
277
278 List<BinaryTableTile> tileList = new ArrayList<>();
279
280 String algorithm = compressedHeader.getStringValue(Compression.ZCTYPn.n(col + 1));
281
282 for (int tileIndex = fromTile, rowStart = 0; rowStart < nRows; tileIndex++, rowStart += tileSize) {
283 BinaryTableTileDecompressor tile = new BinaryTableTileDecompressor(this, data, tile()
284 .rowStart(rowStart)
285 .rowEnd(Math.min(nr, rowStart + tileSize))
286 .column(col)
287 .tileIndex(tileIndex + 1)
288 .compressionAlgorithm(algorithm));
289 tile.decompressToColumn(0);
290 tileList.add(tile);
291
292 tile.execute(FitsFactory.threadPool());
293 }
294
295 for (BinaryTableTile tile : tileList) {
296 tile.waitForResult();
297 }
298
299 return data.getColumn(0);
300 }
301
302
303
304
305
306
307
308 protected final synchronized int getRowsPerTile() {
309 return rowsPerTile;
310 }
311
312 private String getAlgorithm(int column) {
313 if (colAlgorithm != null && column < colAlgorithm.length && colAlgorithm[column] != null) {
314 return colAlgorithm[column];
315 }
316 return Compression.ZCMPTYPE_GZIP_2;
317 }
318
319
320
321
322
323 @SuppressWarnings("javadoc")
324 protected void setColumnCompressionAlgorithms(String[] columnCompressionAlgorithms) {
325 for (String algo : columnCompressionAlgorithms) {
326 if (!ALLOWED_ALGORITHMS.contains(algo.toUpperCase(Locale.US))) {
327 throw new IllegalArgumentException(algo + " cannot be used to compress tables.");
328 }
329 }
330
331 this.colAlgorithm = columnCompressionAlgorithms;
332 }
333
334 private void setColumnCompressionAlgorithms(Header header) {
335 int ncols = header.getIntValue(TFIELDS);
336
337
338 colAlgorithm = new String[ncols];
339
340
341 for (int column = 0; column < ncols; column++) {
342 colAlgorithm[column] = header.getStringValue(Compression.ZCTYPn.n(column + 1));
343 }
344 }
345
346
347
348
349
350 @SuppressWarnings("javadoc")
351 protected synchronized CompressedTableData setRowsPerTile(int value) {
352 rowsPerTile = value;
353 return this;
354 }
355 }