in endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/esri/RawRasterStore.java [340:514]
private void readHeader() throws URISyntaxException, IOException, DataStoreException {
assert Thread.holdsLock(this);
@SuppressWarnings("LocalVariableHidesMemberVariable")
final ChannelDataInput input = this.input;
if (input == null) {
throw new DataStoreClosedException(canNotRead());
}
int nrows = 0;
int ncols = 0;
int nbands = 1;
int nbits = Byte.SIZE;
boolean signed = false;
long skipBytes = 0;
int bandRowBytes = 0;
int totalRowBytes = 0;
int bandGapBytes = 0;
double ulxmap = 0;
double ulymap = 0;
double xdim = 1;
double ydim = 1;
int geomask = 0; // Mask telling whether ulxmap, ulymap, xdim, ydim were specified (in that order).
RawRasterLayout layout = RawRasterLayout.BIL;
ByteOrder byteOrder = ByteOrder.nativeOrder();
final AuxiliaryContent header = readAuxiliaryFile(RawRasterStoreProvider.HDR, false);
if (header == null) {
throw new DataStoreException(cannotReadAuxiliaryFile(RawRasterStoreProvider.HDR));
}
for (CharSequence line : CharSequences.splitOnEOL(header)) {
final int length = line.length();
final int keyStart = CharSequences.skipLeadingWhitespaces(line, 0, length);
final int keyEnd = CharSequences.indexOf(line, ' ', keyStart, length);
if (keyStart >= 0) {
// Note: text after value is considered comment according ESRI specification.
int valStart = CharSequences.skipLeadingWhitespaces(line, keyEnd, length);
int valEnd = CharSequences.indexOf(line, ' ', valStart, length);
if (valEnd < 0) {
valEnd = CharSequences.skipTrailingWhitespaces(line, valStart, length);
if (valEnd <= valStart) continue;
}
final String keyword = line.subSequence(keyStart, keyEnd).toString();
final String value = line.subSequence(valStart, valEnd).toString();
try {
switch (keyword.toUpperCase(Locale.US)) {
case NROWS: nrows = parseStrictlyPositive(keyword, value); break;
case NCOLS: ncols = parseStrictlyPositive(keyword, value); break;
case NBANDS: nbands = parseStrictlyPositive(keyword, value); break;
case NBITS: nbits = parseStrictlyPositive(keyword, value); break;
case BANDROWBYTES: bandRowBytes = parseStrictlyPositive(keyword, value); break;
case TOTALROWBYTES: totalRowBytes = parseStrictlyPositive(keyword, value); break;
case BANDGAPBYTES: bandGapBytes = Integer.parseInt(value); break;
case SKIPBYTES: skipBytes = Long.parseLong(value); break;
case ULXMAP: ulxmap = Double.parseDouble(value); geomask |= 1; break;
case ULYMAP: ulymap = Double.parseDouble(value); geomask |= 2; break;
case XDIM: xdim = Double.parseDouble(value); geomask |= 4; break;
case YDIM: ydim = Double.parseDouble(value); geomask |= 8; break;
case NODATA: nodataValue = Double.parseDouble(value); break;
case PIXELTYPE: signed = indexOf(keyword, value, "SIGNED", "SIGNEDINT") >= 0; break;
case LAYOUT: layout = RawRasterLayout.valueOf(value.toUpperCase(Locale.US)); break;
case BYTEORDER: {
switch (indexOf(keyword, value, "I", "M")) {
case 0: byteOrder = ByteOrder.LITTLE_ENDIAN; break;
case 1: byteOrder = ByteOrder.BIG_ENDIAN; break;
default: throw new DataStoreContentException(errors().getString(
Errors.Keys.IllegalPropertyValue_2, keyword, value));
}
}
/*
* No default. The specification said that any line in the file that
* does not begin with a keyword is treated as a comment and ignored.
*/
}
} catch (IllegalArgumentException e) { // Include NumberFormatException.
throw new DataStoreContentException(errors().getString(
Errors.Keys.IllegalPropertyValue_2, keyword, value), e);
}
}
}
input.buffer.order(byteOrder);
/*
* Validate parameters, compute default values then create the grid geometry.
* If one of ULXMAP or ULYMAP is specified, then both of them shall be specified.
* If one of XDIM or YDIM is specified, then all of ULXMAP, ULYMAP, XDIM and YDIM shall be specified.
*/
if (nrows == 0 || ncols == 0) {
throw missingProperty(header, (nrows == 0) ? NROWS : NCOLS);
}
// Invoke following method now because it does argument validation.
final var dataType = DataType.forNumberOfBits(nbits, false, signed);
final int bytesPerSample = dataType.bytes();
switch (geomask) {
case 0: ulymap = ncols - 1; break; // No property specified.
case 3: break; // ULXMAP and ULYMAP specified.
case 15: break; // ULXMAP, ULYMAP, XDIM and YDIM specified.
default: {
final String keyword;
switch (Integer.lowestOneBit(~geomask)) {
case 1: keyword = ULXMAP; break;
case 2: keyword = ULYMAP; break;
case 4: keyword = XDIM; break;
case 8: keyword = YDIM; break;
default: keyword = "?"; break; // Should not happen.
}
throw missingProperty(header, keyword);
}
}
readPRJ(RawRasterStore.class, "getGridGeometry");
final GridGeometry gg = new GridGeometry(new GridExtent(ncols, nrows), CELL_ANCHOR,
new AffineTransform2D(xdim, 0, 0, -ydim, ulxmap, ulymap), crs);
/*
* Create a sample model for the data layout. This block encapsulates all layout information
* except `skipBytes` and `bandGapBytes`, which need to be taken in account at reading time.
* Note that there is many ways to create a sample model. For example, a `BandedSampleModel`
* could store 3 bands in the same array or in 3 different arrays. The choices made in this
* block must be consistent with the expectations of `read(…)` method implementation.
*/
SampleModel sampleModel = null;
final int bt = dataType.toDataBufferType();
switch (layout) {
case BIL: {
ignoredProperty(BANDGAPBYTES, bandGapBytes);
if (bandRowBytes == 0) bandRowBytes = ceilDiv(multiplyExact(ncols, nbits), Byte.SIZE);
if (totalRowBytes == 0) totalRowBytes = multiplyExact(nbands, bandRowBytes);
if (bytesPerSample != 0) {
final int bandStride = wholeDiv(bandRowBytes, bytesPerSample);
final int scanlineStride = wholeDiv(totalRowBytes, bytesPerSample);
final int[] bankIndices = new int[nbands];
final int[] bandOffsets = new int[nbands];
for (int i=1; i<nbands; i++) {
bandOffsets[i] = multiplyExact(bandStride, i);
}
sampleModel = new ComponentSampleModel(bt, ncols, nrows, 1, scanlineStride, bankIndices, bandOffsets);
}
break;
}
case BIP: {
ignoredProperty(BANDGAPBYTES, bandGapBytes);
ignoredProperty(BANDROWBYTES, bandRowBytes);
if (totalRowBytes == 0) {
totalRowBytes = ceilDiv(multiplyExact(multiplyExact(ncols, nbands), nbits), Byte.SIZE);
}
if (bytesPerSample != 0) {
final int scanlineStride = wholeDiv(totalRowBytes, bytesPerSample);
final int[] bandOffsets = ArraysExt.range(0, nbands);
sampleModel = new PixelInterleavedSampleModel(bt, ncols, nrows, nbands, scanlineStride, bandOffsets);
}
break;
}
case BSQ: {
ignoredProperty(BANDROWBYTES, bandRowBytes);
if (totalRowBytes == 0) {
totalRowBytes = ncols;
}
if (bytesPerSample != 0) {
final int scanlineStride = wholeDiv(totalRowBytes, bytesPerSample);
final int[] bankIndices = ArraysExt.range(0, nbands);
final int[] bandOffsets = new int[nbands];
sampleModel = new BandedSampleModel(bt, ncols, nrows, scanlineStride, bankIndices, bandOffsets);
}
break;
}
default: throw new AssertionError(layout);
}
if (bytesPerSample == 0) {
if (nbands != 1) {
throw new DataStoreContentException(errors().getString(Errors.Keys.InconsistentAttribute_2, nbits, NBITS));
}
sampleModel = new MultiPixelPackedSampleModel(bt, ncols, nrows, nbits, totalRowBytes, 0);
}
/*
* Prepare the reader as the last step because non-null `reader` field is used
* as a sentinel value meaning that the initialization has been completed.
*/
reader = new RawRasterReader(gg, dataType, sampleModel, bandGapBytes, input);
reader.setOrigin(skipBytes);
}