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