package org.geotools.arcsde.raster.io; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_S; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_16BIT_U; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_1BIT; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_S; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_32BIT_U; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_4BIT; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_64BIT_REAL; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_8BIT_S; import static org.geotools.arcsde.raster.info.RasterCellType.TYPE_8BIT_U; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import org.geotools.arcsde.raster.info.RasterCellType; import com.esri.sde.sdk.client.SeRasterTile; /** * Fills the raw tile data in a {@link TileInfo} out of a {@link SeRasterTile}, potentially * promoting the original tile data to a higher precision sample detph, and filling out the TileInfo * pixels with the no-data value where appropriate (determined by the * {@link SeRasterTile#getBitMaskData() bitmask data}). * * @author Gabriel Roldan (OpenGeo) * @version $Id$ * @since 2.5.9 * @source $URL$ * @see NativeTileReader */ abstract class TileDataFetcher { private static Map<RasterCellType, TileDataFetcher> tileDataSetters = new HashMap<RasterCellType, TileDataFetcher>(); static { final ByteTileSetter byteTileSetter = new ByteTileSetter(); tileDataSetters.put(TYPE_1BIT, new OneBitTileSetter()); tileDataSetters.put(TYPE_4BIT, byteTileSetter); tileDataSetters.put(TYPE_8BIT_S, byteTileSetter); tileDataSetters.put(TYPE_8BIT_U, byteTileSetter); tileDataSetters.put(RasterCellType.TYPE_16BIT_U, new UShortTileSetter()); tileDataSetters.put(RasterCellType.TYPE_16BIT_S, new ShortTileSetter()); tileDataSetters.put(RasterCellType.TYPE_32BIT_S, new IntegerTileSetter()); tileDataSetters.put(RasterCellType.TYPE_32BIT_U, new UnsignedIntegerTileSetter()); tileDataSetters.put(RasterCellType.TYPE_32BIT_REAL, new FloatTileSetter()); tileDataSetters.put(RasterCellType.TYPE_64BIT_REAL, new DoubleTileSetter()); } /** * Returns a {@code TileDataFetcher} that knows how to translate the raw pixel data from an * {@link SeRasterTile} with pixel type {@code nativeType} to a {@link TileInfo}'s internal * array with a pixel type determined by {@code targetCellType}. * * @param nativeType * the arcsde raster's native pixel type * @param targetCellType * the TileInfo's target pixel type * @return */ public static TileDataFetcher getTileDataFetcher(final RasterCellType nativeType, final RasterCellType targetCellType) { final TileDataFetcher tileDataFetcher; if (nativeType == targetCellType) { tileDataFetcher = tileDataSetters.get(nativeType); } else { if (nativeType == TYPE_1BIT && targetCellType == RasterCellType.TYPE_8BIT_U) { return new OneBitTileSetter(); } else if (nativeType == TYPE_8BIT_U && targetCellType == TYPE_16BIT_U) { return new UcharToUshort(); } else if (nativeType == TYPE_16BIT_S && targetCellType == TYPE_32BIT_S) { return new ShortToInt(); } else if (nativeType == TYPE_8BIT_S && targetCellType == TYPE_16BIT_S) { return new ByteToShort(); } else if (nativeType == TYPE_16BIT_U && targetCellType == TYPE_32BIT_U) { return new UShortToUInt(); } else if (nativeType == TYPE_32BIT_U && targetCellType == TYPE_64BIT_REAL) { return new UIntToDouble(); } else if (nativeType == TYPE_32BIT_S && targetCellType == TYPE_64BIT_REAL) { return new IntToDouble(); } throw new IllegalArgumentException("No registered TileDataFetcher for pixel type " + nativeType + " and target type " + targetCellType); } if (tileDataFetcher == null) { throw new IllegalArgumentException("No registered TileDataFetcher for pixel type " + nativeType); } return tileDataFetcher; } /** * Grabs the native pixel data out of {@code tile} * * @param tile * @param tileInfo */ public abstract void setTileData(SeRasterTile tile, TileInfo tileInfo); /** * Returns whether the sample N in the bitmask byte array is marked as a no-data pixel */ protected final boolean isNoData(final int sampleN, final byte[] bitmaskData) { boolean isNoData = ((bitmaskData[sampleN / 8] >> (7 - (sampleN % 8))) & 0x01) == 0x00; return isNoData; } /** * */ private static final class OneBitTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { byte[] tileData = tileInfo.getTileDataAsBytes(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final byte nodata = tileInfo.getNoDataValue().byteValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { try { tile.getPixels(tileData); } catch (Exception e) { throw new RuntimeException(e); } // getPixels(byte[]) for a 1-bit raster sets set bits to 255 instead of 1, we want // them to be 1 final byte bitSet = (byte) 0xFF; final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } else { if (bitSet == tileData[pn]) { tileData[pn] = 1; } } } } } } /** * */ private static final class ByteTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final byte nodata = tileInfo.getNoDataValue().byteValue(); byte[] tileData = tileInfo.getTileDataAsBytes(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { byte[] pixelData = tile.getPixelData(); System.arraycopy(pixelData, 0, tileData, 0, numPixelsRead); if (tileInfo.hasNoDataPixels()) { final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } private static final class UShortTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { short[] tileData = tileInfo.getTileDataAsUnsignedShorts(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final int numPixels = tileInfo.getNumPixels(); final short nodata = (short) (tileInfo.getNoDataValue().intValue() & 0xFFFF); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { int[] ints = new int[numPixels]; try { tile.getPixels(ints); } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixels; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } else { tileData[pn] = (short) ints[pn]; } } } } } private static final class ShortTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { short[] tileData = tileInfo.getTileDataAsShorts(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final int numPixels = tileInfo.getNumPixels(); final short nodata = tileInfo.getNoDataValue().shortValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { int[] ints = new int[numPixels]; try { tile.getPixels(ints); } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixels; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } else { tileData[pn] = (short) ints[pn]; } } } } } private static final class IntegerTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { int[] tileData = tileInfo.getTileDataAsIntegers(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final int nodata = tileInfo.getNoDataValue().intValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { try { tile.getPixels(tileData); } catch (Exception e) { throw new RuntimeException(e); } if (tileInfo.hasNoDataPixels()) { byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } private static final class UnsignedIntegerTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { double[] tileData = tileInfo.getTileDataAsDoubles(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final double nodata = tileInfo.getNoDataValue().doubleValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { try { tile.getPixels(tileData); } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); if (hasNoDataPixels) { final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } private static final class FloatTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { float[] tileData = tileInfo.getTileDataAsFloats(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final float nodata = tileInfo.getNoDataValue().floatValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { try { tile.getPixels(tileData); } catch (Exception e) { throw new RuntimeException(e); } if (tileInfo.hasNoDataPixels()) { byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } private static final class DoubleTileSetter extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, TileInfo tileInfo) { double[] tileData = tileInfo.getTileDataAsDoubles(); final int numPixelsRead = tileInfo.getNumPixelsRead(); final double nodata = tileInfo.getNoDataValue().doubleValue(); if (numPixelsRead == 0) { Arrays.fill(tileData, nodata); } else { try { tile.getPixels(tileData); } catch (Exception e) { throw new RuntimeException(e); } if (tileInfo.hasNoDataPixels()) { byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } /** * Converts native unsigned byte tile data to unsigned short data */ private static final class UcharToUshort extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final short nodata = (short) (tileInfo.getNoDataValue().intValue() & 0xFFFF); final short[] tileDataUShorts = tileInfo.getTileDataAsUnsignedShorts(); //Arrays.fill(tileDataUShorts, nodata); if (numPixelsRead == 0) { Arrays.fill(tileDataUShorts, nodata); } else { /* * getPixelData returns the SeRasterTile internal buffer with no extra copy. It may * contain extra elements for the bitmask array in case there are no-data pixels */ final byte[] pixelData = tile.getPixelData(); final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileDataUShorts[pn] = nodata; } else { tileDataUShorts[pn] = (short) (pixelData[pn] & 0xFF); } } } } } /** * Converts native signed byte tile data to signed short data */ private static final class ByteToShort extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final short nodata = tileInfo.getNoDataValue().shortValue(); if (numPixelsRead == 0) { Arrays.fill(tileInfo.getTileDataAsShorts(), nodata); } else { /* * getPixelData returns the SeRasterTile internal buffer with no extra copy. It may * contain extra elements for the bitmask array in case there are no-data pixels */ final byte[] pixelData = tile.getPixelData(); final short[] tileDataShorts = tileInfo.getTileDataAsShorts(); final byte[] bitmaskData = tileInfo.getBitmaskData(); final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); for (int pn = 0; pn < numPixelsRead; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileDataShorts[pn] = nodata; } else { tileDataShorts[pn] = (short) pixelData[pn]; } } } } } /** * Converts native signed short tile data to signed int data */ private static final class ShortToInt extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final int nodata = tileInfo.getNoDataValue().intValue(); if (numPixelsRead == 0) { Arrays.fill(tileInfo.getTileDataAsIntegers(), nodata); } else { int[] cache = tileInfo.getTileDataAsIntegers(); try { tile.getPixels(cache); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); if (hasNoDataPixels) { final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { cache[pn] = nodata; } } } } } } /** * Converts native integer tile data to double data */ private static final class IntToDouble extends TileDataFetcher { private int[] cache = {}; @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final int numPixels = tileInfo.getNumPixels(); final double nodata = tileInfo.getNoDataValue().doubleValue(); if (numPixelsRead == 0) { Arrays.fill(tileInfo.getTileDataAsDoubles(), nodata); } else { if (cache.length < numPixels) { cache = new int[numPixels]; } try { tile.getPixels(cache); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } double[] tileData = tileInfo.getTileDataAsDoubles(); final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (hasNoDataPixels && isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } else { tileData[pn] = cache[pn]; } } } } } /** * Converts native unsigned integer tile data to double data */ private static final class UIntToDouble extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final double nodata = tileInfo.getNoDataValue().doubleValue(); if (numPixelsRead == 0) { Arrays.fill(tileInfo.getTileDataAsDoubles(), nodata); } else { double[] tileData = tileInfo.getTileDataAsDoubles(); try { tile.getPixels(tileData); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); if (hasNoDataPixels) { final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } /** * Converts native unsigned integer tile data to double data */ private static final class UShortToUInt extends TileDataFetcher { @Override public void setTileData(final SeRasterTile tile, final TileInfo tileInfo) { final int numPixelsRead = tileInfo.getNumPixelsRead(); final int nodata = tileInfo.getNoDataValue().intValue(); if (numPixelsRead == 0) { Arrays.fill(tileInfo.getTileDataAsIntegers(), nodata); } else { int[] tileData = tileInfo.getTileDataAsIntegers(); try { tile.getPixels(tileData); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } final boolean hasNoDataPixels = tileInfo.hasNoDataPixels(); if (hasNoDataPixels) { final byte[] bitmaskData = tileInfo.getBitmaskData(); for (int pn = 0; pn < numPixelsRead; pn++) { if (isNoData(pn, bitmaskData)) { tileData[pn] = nodata; } } } } } } }