This is an automated email from the ASF dual-hosted git repository.

jsorel pushed a commit to branch feat/postgis-raster
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 9a72e1075545eb58e7acb1343ee9cde6897f41c2
Author: jsorel <johann.so...@geomatys.com>
AuthorDate: Wed Nov 24 16:49:59 2021 +0100

    feat(PostGIS): add WKB Raster reader/writer
---
 .../internal/sql/postgis/LEDataInputStream.java    | 360 +++++++++++++++++++++
 .../internal/sql/postgis/LEDataOutputStream.java   | 231 +++++++++++++
 .../sis/internal/sql/postgis/WKBRasterBand.java    | 117 +++++++
 .../internal/sql/postgis/WKBRasterConstants.java   | 133 ++++++++
 .../sis/internal/sql/postgis/WKBRasterReader.java  | 331 +++++++++++++++++++
 .../sis/internal/sql/postgis/WKBRasterWriter.java  | 330 +++++++++++++++++++
 .../sis/internal/sql/postgis/LEStreamTest.java     | 130 ++++++++
 7 files changed, 1632 insertions(+)

diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataInputStream.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataInputStream.java
new file mode 100644
index 0000000..fce9903
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataInputStream.java
@@ -0,0 +1,360 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Little endian Data input stream.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class LEDataInputStream extends InputStream implements DataInput {
+
+    private final DataInputStream ds;
+    private final InputStream in;
+    private final byte[] buffer = new byte[8];
+    private long position;
+
+    public LEDataInputStream(final InputStream in) {
+        this.in = in;
+        this.ds = new DataInputStream(in);
+    }
+
+    /**
+     * Get current stream position from the first written byte.
+     * @return stream position
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    @Override
+    public int available() throws IOException {
+        return ds.available();
+    }
+
+    @Override
+    public final short readShort() throws IOException {
+        position += 2;
+        ds.readFully(buffer, 0, 2);
+        return (short) ((buffer[1] & 0xff) << 8
+                      | (buffer[0] & 0xff));
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public short[] readShorts(int nbValues) throws IOException {
+        final short[] array = new short[nbValues];
+        for (int i = 0; i < nbValues; i++) {
+            array[i] = readShort();
+        }
+        return array;
+    }
+
+    @Override
+    public final int readUnsignedShort() throws IOException {
+        position += 2;
+        ds.readFully(buffer, 0, 2);
+        return ((buffer[1] & 0xff) << 8
+              | (buffer[0] & 0xff));
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public int[] readUnsignedShorts(int nbValues) throws IOException {
+        final int[] array = new int[nbValues];
+        for (int i = 0; i<nbValues; i++) {
+            array[i] = readUnsignedShort();
+        }
+        return array;
+    }
+
+    @Override
+    public final char readChar() throws IOException {
+        position += 2;
+        ds.readFully(buffer, 0, 2);
+        return (char) ((buffer[1] & 0xff) << 8
+                     | (buffer[0] & 0xff));
+    }
+
+    @Override
+    public final int readInt() throws IOException {
+        position += 4;
+        ds.readFully(buffer, 0, 4);
+        return (buffer[3]) << 24
+             | (buffer[2] & 0xff) << 16
+             | (buffer[1] & 0xff) << 8
+             | (buffer[0] & 0xff);
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public int[] readInts(int nbValues) throws IOException {
+        final int[] array = new int[nbValues];
+        for (int i = 0; i < nbValues; i++) {
+            array[i] = readInt();
+        }
+        return array;
+    }
+
+    @Override
+    public final long readLong() throws IOException {
+        position += 8;
+        ds.readFully(buffer, 0, 8);
+        return (long) (buffer[7]) << 56
+             | (long) (buffer[6] & 0xff) << 48
+             | (long) (buffer[5] & 0xff) << 40
+             | (long) (buffer[4] & 0xff) << 32
+             | (long) (buffer[3] & 0xff) << 24
+             | (long) (buffer[2] & 0xff) << 16
+             | (long) (buffer[1] & 0xff) << 8
+             | (long) (buffer[0] & 0xff);
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public long[] readLongs(int nbValues) throws IOException {
+        final long[] array = new long[nbValues];
+        for (int i = 0; i < nbValues; i++) {
+            array[i] = readLong();
+        }
+        return array;
+    }
+
+    @Override
+    public final float readFloat() throws IOException {
+        return Float.intBitsToFloat(readInt());
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public float[] readFloats(int nbValues) throws IOException {
+        final float[] array = new float[nbValues];
+        for (int i = 0; i < nbValues; i++) {
+            array[i] = readFloat();
+        }
+        return array;
+    }
+
+    @Override
+    public final double readDouble() throws IOException {
+        return Double.longBitsToDouble(readLong());
+    }
+
+    /**
+     * Read multiple values in one call.
+     *
+     * @param nbValues number of valeus to read
+     * @return array of values
+     * @throws java.io.IOException
+     */
+    public double[] readDoubles(int nbValues) throws IOException {
+        final double[] array = new double[nbValues];
+        for (int i = 0; i < nbValues; i++) {
+            array[i] = readDouble();
+        }
+        return array;
+    }
+
+    @Override
+    public final int read(byte[] b, int off, int len) throws IOException {
+        int nb = in.read(b, off, len);
+        position += nb;
+        return nb;
+    }
+
+    @Override
+    public final void readFully(byte[] b) throws IOException {
+        ds.readFully(b, 0, b.length);
+        position += b.length;
+    }
+
+    @Override
+    public final void readFully(byte[] b, int off, int len) throws IOException 
{
+        ds.readFully(b, off, len);
+        position += len;
+    }
+
+    @Override
+    public final int skipBytes(int n) throws IOException {
+        int nb = ds.skipBytes(n);
+        if (nb <= 0) return nb;
+        position += nb;
+        return nb;
+    }
+
+    public final void skipFully(int n) throws IOException {
+        while (n > 0) {
+            int nb = skipBytes(n);
+            if (nb == -1) {
+                throw new EOFException();
+            } else if (nb == 0) {
+                nb = read();
+                if (nb == -1) {
+                    throw new EOFException();
+                }
+                nb = 1;
+            }
+            n -= nb;
+        }
+    }
+
+    @Override
+    public final boolean readBoolean() throws IOException {
+        position++;
+        return ds.readBoolean();
+    }
+
+    @Override
+    public final byte readByte() throws IOException {
+        position++;
+        return ds.readByte();
+    }
+
+    @Override
+    public int read() throws IOException {
+        position++;
+        return in.read();
+    }
+
+    @Override
+    public final int readUnsignedByte() throws IOException {
+        position++;
+        return ds.readUnsignedByte();
+    }
+
+    @Override
+    public final String readLine() throws IOException {
+        return ds.readLine();
+    }
+
+    @Override
+    public final String readUTF() throws IOException {
+        return ds.readUTF();
+    }
+
+    /**
+     * Ralign stream position, skipping any remaining byte to match given 
block size.
+     * Older formats or javascript buffer requiere to be aligned on 2, 4 or 8 
bytes
+     * (short,int,float,double) to read datas.
+     *
+     * @param blockSize
+     * @return number of bytes skipped
+     */
+    public int realign(int blockSize) throws IOException {
+        final long position = getPosition();
+        final long res = position % blockSize;
+        if (res == 0) return 0;
+        try {
+            skipFully((int) (blockSize-res));
+        } catch (EOFException ex) {
+            return -1;
+        }
+        return (int) res;
+    }
+
+    @Override
+    public final void close() throws IOException {
+        ds.close();
+    }
+
+
+    public static short readUnsignedByte(final byte[] buffer, final int 
offset){
+        return (short) (buffer[offset] & 0xff);
+    }
+
+    public static short readShort(final byte[] buffer, final int offset){
+        return (short) ((buffer[offset+1] & 0xff) << 8
+                | (buffer[offset+0] & 0xff));
+    }
+
+    public static int readUnsignedShort(final byte[] buffer, final int offset){
+        return ((buffer[offset+1] & 0xff) << 8 | (buffer[offset+0] & 0xff));
+    }
+
+    public static char readChar(final byte[] buffer, final int offset){
+        return (char) ((buffer[offset+1] & 0xff) << 8
+                | (buffer[offset+0] & 0xff));
+    }
+
+    public static int readInt(final byte[] buffer, final int offset){
+        return    (buffer[offset+3]) << 24
+                | (buffer[offset+2] & 0xff) << 16
+                | (buffer[offset+1] & 0xff) << 8
+                | (buffer[offset+0] & 0xff);
+    }
+
+    public static long readUnsignedInt(final byte[] buffer, final int offset){
+        return    (long) (buffer[offset+3] & 0xff) << 24
+                | (long) (buffer[offset+2] & 0xff) << 16
+                | (long) (buffer[offset+1] & 0xff) << 8
+                | (long) (buffer[offset+0] & 0xff);
+    }
+
+    public static long readLong(final byte[] buffer, final int offset){
+        return    (long) (buffer[offset+7]) << 56
+                | (long) (buffer[offset+6] & 0xff) << 48
+                | (long) (buffer[offset+5] & 0xff) << 40
+                | (long) (buffer[offset+4] & 0xff) << 32
+                | (long) (buffer[offset+3] & 0xff) << 24
+                | (long) (buffer[offset+2] & 0xff) << 16
+                | (long) (buffer[offset+1] & 0xff) << 8
+                | (long) (buffer[offset+0] & 0xff);
+    }
+
+    public static float readFloat(final byte[] buffer, final int offset){
+        return Float.intBitsToFloat(readInt(buffer,offset));
+    }
+
+    public static double readDouble(final byte[] buffer, final int offset){
+        return Double.longBitsToDouble(readLong(buffer,offset));
+    }
+
+}
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataOutputStream.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataOutputStream.java
new file mode 100644
index 0000000..a26b560
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/LEDataOutputStream.java
@@ -0,0 +1,231 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.io.DataOutput;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Little endian Data output stream.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class LEDataOutputStream extends FilterOutputStream implements 
DataOutput {
+
+    private long position;
+
+    public LEDataOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /**
+     * Get current stream position from the first written byte.
+     * @return stream position
+     */
+    public long getPosition() {
+        return position;
+    }
+
+    @Override
+    public void write(int b) throws IOException {
+        out.write(b);
+        position++;
+    }
+
+    @Override
+    public void write(byte[] data, int offset, int length) throws IOException {
+        out.write(data, offset, length);
+        position += length;
+    }
+
+    @Override
+    public void writeBoolean(boolean b) throws IOException {
+        this.write( b ? 1 : 0 );
+    }
+
+    @Override
+    public void writeByte(int b) throws IOException {
+        out.write(b);
+        position++;
+    }
+
+    @Override
+    public void writeShort(int s) throws IOException {
+        out.write((s      ) & 0xFF);
+        out.write((s >>> 8) & 0xFF);
+        position += 2;
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeShorts(short[] values) throws IOException {
+        for (short value : values) {
+            writeShort(value);
+        }
+    }
+
+    public void writeUnsignedShort(int s) throws IOException {
+        writeShort(s);
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeUnsignedShorts(int[] values) throws IOException {
+        for (int value : values) {
+            writeUnsignedShort(value);
+        }
+    }
+
+    @Override
+    public void writeChar(int c) throws IOException {
+        out.write((c      ) & 0xFF);
+        out.write((c >>> 8) & 0xFF);
+        position += 2;
+    }
+
+    @Override
+    public void writeInt(int i) throws IOException {
+        out.write((i       ) & 0xFF);
+        out.write((i >>>  8) & 0xFF);
+        out.write((i >>> 16) & 0xFF);
+        out.write((i >>> 24) & 0xFF);
+        position += 4;
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeInts(int[] values) throws IOException {
+        for (int value : values) {
+            writeInt(value);
+        }
+    }
+
+    @Override
+    public void writeLong(long l) throws IOException {
+        out.write((int) (l       ) & 0xFF);
+        out.write((int) (l >>> 8 ) & 0xFF);
+        out.write((int) (l >>> 16) & 0xFF);
+        out.write((int) (l >>> 24) & 0xFF);
+        out.write((int) (l >>> 32) & 0xFF);
+        out.write((int) (l >>> 40) & 0xFF);
+        out.write((int) (l >>> 48) & 0xFF);
+        out.write((int) (l >>> 56) & 0xFF);
+        position += 8;
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeLongs(long[] values) throws IOException {
+        for (long value : values) {
+            writeLong(value);
+        }
+    }
+
+    @Override
+    public final void writeFloat(float f) throws IOException {
+        this.writeInt(Float.floatToIntBits(f));
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeFloats(float[] values) throws IOException {
+        for (float value : values) {
+            writeFloat(value);
+        }
+    }
+
+    @Override
+    public final void writeDouble(double d) throws IOException {
+        this.writeLong(Double.doubleToLongBits(d));
+    }
+
+    /**
+     * Write multiple values in one call.
+     *
+     * @param values values to write
+     * @throws IOException
+     */
+    public final void writeDoubles(double[] values) throws IOException {
+        for (double value : values) {
+            writeDouble(value);
+        }
+    }
+
+    @Override
+    public void writeBytes(String s) throws IOException {
+        for (int i = 0, n = s.length(); i < n; i++) {
+            out.write((byte) s.charAt(i));
+        }
+        position += s.length();
+    }
+
+    @Override
+    public void writeChars(String s) throws IOException {
+        for (int i = 0, n = s.length(); i < n; i++) {
+            final char c = s.charAt(i);
+            out.write((c      ) & 0xFF);
+            out.write((c >>> 8) & 0xFF);
+        }
+        position += s.length()*2;
+    }
+
+    @Override
+    public void writeUTF(String s) throws IOException {
+        throw new IOException("Not supported");
+    }
+
+    /**
+     * Align byte stream to a padding value, 0 bytes will be added until 
padding
+     * value is reached.
+     * Padding values are usually very small, 2, 4 or 8 bytes to ensure 
primitive
+     * types such as Short, Integer, Float are aligned in memory.
+     *
+     * @param padding value to realign.
+     * @throws IOException
+     */
+    public void realign(int padding) throws IOException{
+        while ((position % padding) != 0) {
+            write(0);
+        }
+    }
+
+}
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterBand.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterBand.java
new file mode 100644
index 0000000..4e916c4
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterBand.java
@@ -0,0 +1,117 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+/**
+ * WKB Raster band, used in postGIS 2.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class WKBRasterBand {
+
+    private int pixelType;
+    private boolean offdatabase;
+    private boolean hasnodata;
+    private boolean isnodata;
+    private boolean reserved;
+    private Number noDataValue;
+    private byte[] datas;
+
+    public WKBRasterBand() {
+    }
+
+    public int getPixelType() {
+        return pixelType;
+    }
+
+    public void setPixelType(int pixelType) {
+        this.pixelType = pixelType;
+    }
+
+    public boolean isOffDatabase() {
+        return offdatabase;
+    }
+
+    public void setOffDatabase(boolean offdatabase) {
+        this.offdatabase = offdatabase;
+    }
+
+    public boolean hasNodata() {
+        return hasnodata;
+    }
+
+    public void setHasNodata(boolean hasnodata) {
+        this.hasnodata = hasnodata;
+    }
+
+    public boolean isNodata() {
+        return isnodata;
+    }
+
+    public void setIsNodata(boolean isnodata) {
+        this.isnodata = isnodata;
+    }
+
+    public boolean getReserved() {
+        return reserved;
+    }
+
+    public void setReserved(boolean reserved) {
+        this.reserved = reserved;
+    }
+
+    public void setNoDataValue(Number noDataValue) {
+        this.noDataValue = noDataValue;
+    }
+
+    public Number getNoDataValue() {
+        return noDataValue;
+    }
+
+    public void setDatas(byte[] datas) {
+        this.datas = datas;
+    }
+
+    public byte[] getDatas() {
+        return datas;
+    }
+
+    public int getNbBytePerPixel() {
+        return WKBRasterConstants.getNbBytePerPixel(pixelType);
+    }
+
+    public int getDataBufferType() {
+        return WKBRasterConstants.getDataBufferType(pixelType);
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder("WKB Raster Band :");
+        sb.append("\n- pixel type : ").append(pixelType);
+        sb.append("\n- offdatabase : ").append(offdatabase);
+        sb.append("\n- hasnodata : ").append(hasnodata);
+        sb.append("\n- is no data : ").append(isnodata);
+        sb.append("\n- reserved : ").append(reserved);
+        sb.append("\n- no data value : ").append(noDataValue);
+        return sb.toString();
+    }
+
+
+}
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterConstants.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterConstants.java
new file mode 100644
index 0000000..bccc29d
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterConstants.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.awt.image.DataBuffer;
+
+/**
+ * WKB raster constants, used in postGIS 2.
+ *
+ * Specification :
+ * https://postgis.net/docs/RT_reference.html
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class WKBRasterConstants {
+
+    /** 1-bit boolean            */
+    public static final int PT_1BB = 0;
+    /** 2-bit unsigned integer   */
+    public static final int PT_2BUI = 1;
+    /** 4-bit unsigned integer   */
+    public static final int PT_4BUI = 2;
+    /** 8-bit signed integer     */
+    public static final int PT_8BSI = 3;
+    /** 8-bit unsigned integer   */
+    public static final int PT_8BUI = 4;
+    /** 16-bit signed integer    */
+    public static final int PT_16BSI = 5;
+    /** 16-bit unsigned integer  */
+    public static final int PT_16BUI = 6;
+    /** 32-bit signed integer    */
+    public static final int PT_32BSI = 7;
+    /** 32-bit unsigned integer  */
+    public static final int PT_32BUI = 8;
+    /** 32-bit float             */
+    public static final int PT_32BF = 10;
+    /** 64-bit float             */
+    public static final int PT_64BF = 11;
+    public static final int PT_END = 13;
+
+    public static final int BANDTYPE_FLAGS_MASK = 0xF0;
+    public static final int BANDTYPE_PIXTYPE_MASK = 0x0F;
+    public static final int BANDTYPE_FLAG_OFFDB = 1 << 7;
+    public static final int BANDTYPE_FLAG_HASNODATA = 1 << 6;
+    public static final int BANDTYPE_FLAG_ISNODATA = 1 << 5;
+    public static final int BANDTYPE_FLAG_RESERVED3 =1 << 4;
+
+    private WKBRasterConstants() {}
+
+    public static int getNbBytePerPixel(int pixelType) {
+        switch (pixelType) {
+            case PT_1BB:
+            case PT_2BUI:
+            case PT_4BUI:
+            case PT_8BUI:
+            case PT_8BSI:
+                return 1;
+            case PT_16BSI:
+            case PT_16BUI:
+                return 2;
+            case PT_32BSI:
+            case PT_32BUI:
+            case PT_32BF:
+                return 4;
+            case PT_64BF:
+                return 8;
+            default:
+                throw new IllegalArgumentException("unknowned pixel type : " + 
pixelType);
+        }
+    }
+
+    public static int getDataBufferType(int pixelType) {
+        switch (pixelType) {
+            case PT_1BB:
+            case PT_2BUI:
+            case PT_4BUI:
+            case PT_8BUI:
+            case PT_8BSI:
+                return DataBuffer.TYPE_BYTE;
+            case PT_16BSI:
+                return DataBuffer.TYPE_SHORT;
+            case PT_16BUI:
+                return DataBuffer.TYPE_USHORT;
+            case PT_32BSI:
+                return DataBuffer.TYPE_INT;
+            case PT_32BUI:
+                return DataBuffer.TYPE_INT;
+            case PT_32BF:
+                return DataBuffer.TYPE_FLOAT;
+            case PT_64BF:
+                return DataBuffer.TYPE_DOUBLE;
+            default:
+                throw new IllegalArgumentException("unknowned pixel type : " + 
pixelType);
+        }
+    }
+
+    public static int getPixelType(int dataBufferType) {
+        switch (dataBufferType) {
+            case DataBuffer.TYPE_BYTE:
+                return PT_8BUI;
+            case DataBuffer.TYPE_SHORT:
+                return PT_16BSI;
+            case DataBuffer.TYPE_USHORT:
+                return PT_16BUI;
+            case DataBuffer.TYPE_INT:
+                return PT_32BSI;
+            case DataBuffer.TYPE_FLOAT:
+                return PT_32BF;
+            case DataBuffer.TYPE_DOUBLE:
+                return PT_64BF;
+            default:
+                throw new IllegalArgumentException("unknowned data buffer type 
: " + dataBufferType);
+        }
+    }
+
+}
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterReader.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterReader.java
new file mode 100644
index 0000000..c0c970a
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterReader.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.awt.image.ColorModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.io.ByteArrayInputStream;
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridCoverageBuilder;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.internal.coverage.j2d.ColorModelFactory;
+import org.apache.sis.internal.coverage.j2d.RasterFactory;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
+import org.apache.sis.referencing.CRS;
+import static org.apache.sis.internal.sql.postgis.WKBRasterConstants.*;
+import org.opengis.referencing.NoSuchAuthorityCodeException;
+import org.opengis.referencing.crs.CRSAuthorityFactory;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.util.FactoryException;
+
+/**
+ * WKB Raster Reader, used in postGIS 2 but can be used elsewhere.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class WKBRasterReader {
+
+    private AffineTransform2D gridToCRS;
+    private int srid;
+
+    public WKBRasterReader(){
+    }
+
+    /**
+     * Reset values before new read call.
+     */
+    public void reset(){
+        gridToCRS = null;
+        srid = 0;
+    }
+
+    /**
+     * Get the Grid to CRS transform, can be called after read only.
+     * @return AffineTransform2D
+     */
+    public AffineTransform2D getGridToCRS() {
+        return gridToCRS;
+    }
+
+    /**
+     * Get the postgis srid, can be called after read only.
+     * @return int, postgid srid
+     */
+    public int getSRID(){
+        return srid;
+    }
+
+    /**
+     * Parse given byte[] and rebuild a GridCoverage2D.
+     *
+     * @param data
+     * @return
+     * @throws IOException
+     */
+    public GridCoverage readCoverage(byte[] data, CRSAuthorityFactory 
authorityFactory)
+            throws IOException, NoSuchAuthorityCodeException, FactoryException{
+        final InputStream stream = new ByteArrayInputStream(data);
+        return readCoverage(stream, authorityFactory);
+    }
+
+    /**
+     * Parse given InputStream and rebuild a GridCoverage2D.
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    public GridCoverage readCoverage(final InputStream stream, 
CRSAuthorityFactory authorityFactory)
+            throws IOException, NoSuchAuthorityCodeException, FactoryException{
+        final BufferedImage image = read(stream);
+        final GridCoverageBuilder gcb = new GridCoverageBuilder();
+        final String epsgCode = "EPSG:" + srid;
+        final CoordinateReferenceSystem crs;
+        if (authorityFactory != null) {
+            crs = authorityFactory.createCoordinateReferenceSystem(epsgCode);
+        } else {
+            crs = CRS.forCode(epsgCode);
+        }
+        gcb.setDomain(new GridGeometry(null, PixelInCell.CELL_CENTER, 
getGridToCRS(), crs));
+        gcb.setValues(image);
+        return gcb.build();
+    }
+
+    /**
+     * Parse given byte[] and rebuild RenderedImage.
+     *
+     * @param data
+     * @return
+     * @throws IOException
+     */
+    public BufferedImage read(byte[] data) throws IOException{
+        final InputStream stream = new ByteArrayInputStream(data);
+        return read(stream);
+    }
+
+    /**
+     * Parse given InputStream and rebuild RenderedImage.
+     *
+     * @param stream
+     * @return
+     * @throws IOException
+     */
+    public BufferedImage read(final InputStream stream) throws IOException{
+
+        final DataInput ds;
+        final boolean littleEndian = stream.read() == 1;
+        if (littleEndian) {
+            //big endian
+            ds = new LEDataInputStream(stream);
+        } else {
+            //little endian
+            ds = new DataInputStream(stream);
+        }
+
+        final int version = ds.readUnsignedShort();
+        final int nbBand = ds.readUnsignedShort();
+        //grid to crs
+        final double scaleX = ds.readDouble();
+        final double scaleY = ds.readDouble();
+        final double ipX = ds.readDouble();
+        final double ipY = ds.readDouble();
+        final double skewX = ds.readDouble();
+        final double skewY = ds.readDouble();
+        gridToCRS = new AffineTransform2D(scaleX, skewY, skewX, scaleY, ipX, 
ipY);
+
+
+        srid = ds.readInt();
+        final int width = ds.readUnsignedShort();
+        final int height = ds.readUnsignedShort();
+
+        if (nbBand == 0) {
+            //possible for empty raster
+            return null;
+        }
+
+        final WKBRasterBand[] bands = new WKBRasterBand[nbBand];
+
+        for (int i = 0; i < nbBand; i++) {
+            final WKBRasterBand band = new WKBRasterBand();
+
+            final byte b = ds.readByte();
+            band.setPixelType(b & BANDTYPE_PIXTYPE_MASK);
+            band.setOffDatabase( (b & BANDTYPE_FLAG_OFFDB) != 0);
+            band.setHasNodata( (b & BANDTYPE_FLAG_HASNODATA) != 0);
+            band.setIsNodata( (b & BANDTYPE_FLAG_ISNODATA) != 0);
+            band.setReserved( (b & BANDTYPE_FLAG_RESERVED3) != 0);
+
+            /* read nodata value */
+            switch (band.getPixelType()) {
+                case PT_1BB:
+                case PT_2BUI:
+                case PT_4BUI:
+                case PT_8BUI:
+                    band.setNoDataValue(ds.readUnsignedByte());
+                    break;
+                case PT_8BSI:
+                    band.setNoDataValue(ds.readByte());
+                    break;
+                case PT_16BSI:
+                    band.setNoDataValue(ds.readShort());
+                    break;
+                case PT_16BUI:
+                    band.setNoDataValue(ds.readUnsignedShort());
+                    break;
+                case PT_32BSI:
+                    band.setNoDataValue(ds.readInt());
+                    break;
+                case PT_32BUI:
+                    band.setNoDataValue(ds.readInt() & 0x00000000ffffffffL);
+                    break;
+                case PT_32BF:
+                    band.setNoDataValue(ds.readFloat());
+                    break;
+                case PT_64BF:
+                    band.setNoDataValue(ds.readDouble());
+                    break;
+                default:
+                    throw new IOException("unknowned pixel type : " + 
band.getPixelType());
+            }
+
+            if (band.isOffDatabase()) {
+                throw new IOException("can not access data which are off 
database");
+            } else {
+                //read values
+                final int nbBytePerPixel = band.getNbBytePerPixel();
+                final byte[] datas = new byte[width * height * 
band.getNbBytePerPixel()];
+                ds.readFully(datas);
+                if (littleEndian && nbBytePerPixel > 1) {
+                    //image databank expect values in big endian so we must 
flip bytes
+                    byte temp;
+                    for (int k = 0; k < datas.length; k += nbBytePerPixel) {
+                        for (int p = 0, n = nbBytePerPixel / 2; p < n; p++) {
+                            final int index1 = k + p;
+                            final int index2 = k + (nbBytePerPixel - p - 1);
+                            temp = datas[index1];
+                            datas[index1] = datas[index2];
+                            datas[index2] = temp;
+                        }
+                    }
+                }
+                band.setDatas(datas);
+            }
+
+            bands[i] = band;
+        }
+
+        //we expect all bands to have the same type
+        final int dataBufferType = bands[0].getDataBufferType();
+
+        //rebuild raster
+        final WritableRaster raster;
+        double min = Double.MAX_VALUE;
+        double max = Double.MIN_VALUE;
+
+        if (dataBufferType == DataBuffer.TYPE_BYTE) {
+            //more efficient but only works for byte type bands
+            //check all band have the same sample model and rebuild data buffer
+            Integer dataType = null;
+            final byte[][] dataArray = new byte[nbBand][0];
+            final int[] bankIndices = new int[nbBand];
+            final int[] bankOffsets = new int[nbBand];
+            for (int i = 0 ; i < bands.length; i++) {
+                final WKBRasterBand band = bands[i];
+                if (dataType == null) {
+                    dataType = band.getDataBufferType();
+                } else if (dataType != band.getDataBufferType()) {
+                    throw new IOException("Band type differ, can not be mapped 
to java image.");
+                }
+                dataArray[i] = band.getDatas();
+                bankIndices[i] = i;
+                bankOffsets[i] = 0;
+            }
+
+            min = -100.0;
+            max = 100.0;
+
+            //rebuild data buffer
+            final DataBuffer db = new DataBufferByte(dataArray, 
dataArray[0].length);
+            final int scanlineStride = width;
+            raster = RasterFactory.createRaster(
+                    db, width, height, 1, scanlineStride, bankIndices, 
bankOffsets, new Point(0,0));
+
+        } else {
+            raster = RasterFactory.createGrayScaleImage(dataBufferType, width, 
height, bands.length, 0, 0, 100).getRaster();
+            for (int i = 0;i < bands.length; i++) {
+                final byte[] datas = bands[i].getDatas();
+                final DataInputStream dds = new DataInputStream(new 
ByteArrayInputStream(datas));
+                for (int y = 0; y < height; y++) {
+                    for (int x = 0; x < width; x++) {
+                        switch (dataBufferType) {
+                            case DataBuffer.TYPE_SHORT:
+                                short d1 = dds.readShort();
+                                raster.setSample(x, y, i, d1);
+                                min = Math.min(min, (double) d1);
+                                max = Math.max(max, (double) d1);
+                                break;
+                            case DataBuffer.TYPE_USHORT:
+                                int d2 = dds.readUnsignedShort();
+                                raster.setSample(x, y, i, d2);
+                                min = Math.min(min, (double) d2);
+                                max = Math.max(max, (double) d2);
+                                break;
+                            case DataBuffer.TYPE_INT:
+                                int d3 = dds.readInt();
+                                raster.setSample(x, y, i, d3);
+                                min = Math.min(min, (double) d3);
+                                max = Math.max(max, (double) d3);
+                                break;
+                            case DataBuffer.TYPE_FLOAT:
+                                float d4 = dds.readFloat();
+                                raster.setSample(x, y, i, d4);
+                                min = Math.min(min, (double) d4);
+                                max = Math.max(max, (double) d4);
+                                break;
+                            case DataBuffer.TYPE_DOUBLE:
+                                double d5 = dds.readDouble();
+                                raster.setSample(x, y, i, d5);
+                                min = Math.min(min, d5);
+                                max = Math.max(max, d5);
+                                break;
+                            default:
+                                throw new IllegalArgumentException("unknowned 
data buffer type : " + dataBufferType);
+                        }
+                    }
+                }
+            }
+        }
+
+        //rebuild image
+        final SampleModel sm = raster.getSampleModel();
+        ColorModel cm = ColorModelFactory.createGrayScale(sm.getDataType(), 
raster.getNumBands(), 0, min, max);
+        return new BufferedImage(cm, raster, false, null);
+    }
+}
diff --git 
a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterWriter.java
 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterWriter.java
new file mode 100644
index 0000000..a27fcc2
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/postgis/WKBRasterWriter.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.awt.geom.AffineTransform;
+import java.awt.image.ComponentSampleModel;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferDouble;
+import java.awt.image.DataBufferFloat;
+import java.awt.image.DataBufferInt;
+import java.awt.image.DataBufferShort;
+import java.awt.image.DataBufferUShort;
+import java.awt.image.Raster;
+import java.awt.image.RenderedImage;
+import java.awt.image.SampleModel;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutput;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.sis.coverage.grid.GridCoverage;
+import org.apache.sis.coverage.grid.GridGeometry;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.opengis.referencing.crs.CoordinateReferenceSystem;
+import org.opengis.referencing.datum.PixelInCell;
+import org.opengis.referencing.operation.MathTransform;
+import org.opengis.util.FactoryException;
+
+/**
+ * WKB Raster Writer, used in postGIS 2 but can be used elsewhere.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+final class WKBRasterWriter {
+
+    public WKBRasterWriter() {
+    }
+
+    /**
+     * Reset values before new write call.
+     */
+    public void reset() {
+    }
+
+    /**
+     * Encode given coverage in Postgis WKB.
+     *
+     * @param coverage : grid coverage 2d , not null
+     * @return byte[] encoded image
+     * @throws IOException
+     */
+    public byte[] write(final GridCoverage coverage) throws IOException, 
FactoryException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        write(coverage, out);
+        return out.toByteArray();
+    }
+
+    /**
+     * Encode given coverage in Postgis WKB.
+     *
+     * @param coverage : grid coverage 2d , not null
+     * @param stream : output stream to write in
+     * @throws IOException
+     */
+    public void write(final GridCoverage coverage, final OutputStream stream) 
throws IOException, FactoryException {
+        write(coverage, stream, true);
+    }
+
+    /**
+     * Encode given coverage in Postgis WKB.
+     *
+     * @param coverage : grid coverage 2d , not null
+     * @param stream : output stream to write in
+     * @param littleEndian : wanted value encoding
+     * @throws IOException
+     */
+    public void write(final GridCoverage coverage, final OutputStream stream, 
final boolean littleEndian)
+            throws IOException, FactoryException {
+        final GridGeometry gridGeometry = coverage.getGridGeometry();
+        final CoordinateReferenceSystem crs = 
gridGeometry.getCoordinateReferenceSystem();
+        final Integer srid = IdentifiedObjects.lookupEPSG(crs);
+        if (srid == null) {
+            throw new IOException("CoordinateReferenceSystem does not have an 
EPSG code.");
+        }
+        final MathTransform gridToCRS = 
coverage.getGridGeometry().getGridToCRS(PixelInCell.CELL_CENTER);
+        if (!(gridToCRS instanceof AffineTransform)) {
+            throw new IOException("Coverage GridToCRS transform is not 
affine.");
+        }
+        final RenderedImage image = coverage.render(null);
+
+        write(image, (AffineTransform) gridToCRS, srid, stream);
+    }
+
+    /**
+     * Encode given image in Postgis WKB.
+     *
+     * @param image : image , not null
+     * @param gridToCRS : image grid to crs, can be null
+     * @param srid : image srid
+     * @return byte[] encoded image
+     * @throws IOException
+     */
+    public byte[] write(final RenderedImage image, final AffineTransform 
gridToCRS,
+            final int srid) throws IOException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        write(image, gridToCRS, srid, out);
+        return out.toByteArray();
+    }
+
+    /**
+     * Encode given image in Postgis WKB.
+     *
+     * @param image : image , not null
+     * @param gridToCRS : image grid to crs, can be null
+     * @param srid : image srid
+     * @return byte[] encoded image
+     * @throws IOException
+     */
+    public byte[] write(final Raster image, final AffineTransform gridToCRS,
+            final int srid) throws IOException {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        write(image, gridToCRS, srid, out);
+        return out.toByteArray();
+    }
+
+    /**
+     * Encode given image in Postgis WKB.
+     *
+     * @param image : image , not null
+     * @param gridToCRS : image grid to crs, can be null
+     * @param srid : image srid
+     * @param stream : output stream to write in
+     * @throws IOException
+     */
+    public void write(final RenderedImage image, AffineTransform gridToCRS,
+            final int srid, final OutputStream stream) throws IOException {
+        Raster raster;
+        if (image.getNumXTiles() == 1 && image.getNumYTiles() == 1) {
+            raster = image.getTile(image.getMinTileX(), image.getMinTileY());
+        } else {
+            raster = image.getData();
+        }
+        write(raster, gridToCRS, srid, stream, false);
+    }
+
+    /**
+     * Encode given image in Postgis WKB.
+     *
+     * @param image : image , not null
+     * @param gridToCRS : image grid to crs, can be null
+     * @param srid : image srid
+     * @param stream : output stream to write in
+     * @throws IOException
+     */
+    public void write(final Raster image, AffineTransform gridToCRS,
+            final int srid, final OutputStream stream) throws IOException {
+        write(image, gridToCRS, srid, stream, false);
+    }
+
+    /**
+     * Encode given image in Postgis WKB.
+     *
+     * @param image : image , not null
+     * @param gridToCRS : image grid to crs, can be null
+     * @param srid : image srid
+     * @param stream : output stream to write in
+     * @param littleEndian : wanted value encoding
+     * @throws IOException
+     */
+    public void write(final Raster image, AffineTransform gridToCRS,
+            final int srid, OutputStream stream, final boolean littleEndian) 
throws IOException {
+        if (gridToCRS == null) {
+            gridToCRS = new AffineTransform();
+        }
+
+        if (!(stream instanceof BufferedOutputStream)) {
+            stream = new BufferedOutputStream(stream);
+        }
+
+        final DataOutput ds;
+        if (littleEndian) {
+            ds = new LEDataOutputStream(stream);
+        } else {
+            ds = new DataOutputStream(stream);
+        }
+
+        final SampleModel sm = image.getSampleModel();
+        final Raster raster = image;
+        final int nbBand = sm.getNumBands();
+        final int width = image.getWidth();
+        final int height = image.getHeight();
+        int databufferType = sm.getDataType();
+        if (databufferType == DataBuffer.TYPE_INT) {
+            //special case, most image compression bands on single int when it 
would be byte
+            if (sm.getSampleSize()[0] <= 8) {
+                databufferType = DataBuffer.TYPE_BYTE;
+            }
+        }
+
+        final int pixelType = WKBRasterConstants.getPixelType(databufferType);
+        final int bytePerpixel = 
WKBRasterConstants.getNbBytePerPixel(pixelType);
+
+        //endianess
+        ds.write( littleEndian ? 1 : 0 );
+        //version, 0 for now
+        ds.writeShort(0);
+        //number of bands
+        ds.writeShort(nbBand);
+        //grid to crs
+        ds.writeDouble(gridToCRS.getScaleX());
+        ds.writeDouble(gridToCRS.getScaleY());
+        ds.writeDouble(gridToCRS.getTranslateX());
+        ds.writeDouble(gridToCRS.getTranslateY());
+        ds.writeDouble(gridToCRS.getShearX());
+        ds.writeDouble(gridToCRS.getShearY());
+        //write srid
+        ds.writeInt(srid);
+        //width and height
+        ds.writeShort(width);
+        ds.writeShort(height);
+
+
+        if (!littleEndian && nbBand == 1 && sm instanceof 
ComponentSampleModel) {
+            DataBuffer dataBuffer = raster.getDataBuffer();
+            final byte flags = (byte) pixelType;
+
+            if (dataBuffer instanceof DataBufferByte) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                byte[] pixelData = ((DataBufferByte) dataBuffer).getData();
+                ds.write(pixelData);
+                stream.flush();
+                return;
+
+            } else if (dataBuffer instanceof DataBufferUShort) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                short[] pixelData = ((DataBufferUShort) dataBuffer).getData();
+                for (short p : pixelData) ds.writeShort(p);
+                stream.flush();
+                return;
+
+            } else if (dataBuffer instanceof DataBufferShort) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                short[] pixelData = ((DataBufferShort) dataBuffer).getData();
+                for (short p : pixelData) ds.writeShort(p);
+                stream.flush();
+                return;
+
+            } else if (dataBuffer instanceof DataBufferInt) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                int[] pixelData = ((DataBufferInt) dataBuffer).getData();
+                for (int p : pixelData) ds.writeInt(p);
+                stream.flush();
+                return;
+
+            } else if (dataBuffer instanceof DataBufferFloat) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                float[] pixelData = ((DataBufferFloat) dataBuffer).getData();
+                for (float p : pixelData) ds.writeFloat(p);
+                stream.flush();
+                return;
+
+            } else if (dataBuffer instanceof DataBufferDouble) {
+                ds.write(flags);
+                ds.write(new byte[bytePerpixel]);
+                double[] pixelData = ((DataBufferDouble) dataBuffer).getData();
+                for (double p : pixelData) ds.writeDouble(p);
+                stream.flush();
+                return;
+            }
+            // fallback on pixel by pixel writing
+        }
+
+        //write each band
+        for (int b = 0; b < nbBand; b++) {
+
+            // band description
+            final byte flags = (byte) pixelType;
+            // OffDatabase = false
+            // TODO HasNodata : we don't have informations for no data
+            //      this would requiere a SampleDimension object
+            // IsNodata = false
+            // Reserved = false
+            ds.write(flags);
+
+            // TODO no data value
+            ds.write(new byte[bytePerpixel]);
+
+            //write values
+            for (int y = raster.getMinY(), maxy = raster.getMinY() + height; y 
< maxy; y++) {
+                for (int x = raster.getMinX(), maxx = raster.getMinX() + 
width; x < maxx; x++) {
+                    switch (databufferType) {
+                        case DataBuffer.TYPE_BYTE :     ds.writeByte( (byte) 
raster.getSample(x, y, b)); break;
+                        case DataBuffer.TYPE_SHORT :    ds.writeShort( (short) 
raster.getSample(x, y, b)); break;
+                        case DataBuffer.TYPE_USHORT :   ds.writeShort( (short) 
raster.getSample(x, y, b)); break;
+                        case DataBuffer.TYPE_INT :      ds.writeInt( 
raster.getSample(x, y, b)); break;
+                        case DataBuffer.TYPE_FLOAT :    ds.writeFloat( 
raster.getSampleFloat(x, y, b)); break;
+                        case DataBuffer.TYPE_DOUBLE :   ds.writeDouble( 
raster.getSampleDouble(x, y, b)); break;
+                    }
+                }
+            }
+
+        }
+        stream.flush();
+    }
+
+}
diff --git 
a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/LEStreamTest.java
 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/LEStreamTest.java
new file mode 100644
index 0000000..a6b1ac9
--- /dev/null
+++ 
b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/postgis/LEStreamTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.sql.postgis;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import org.apache.sis.test.TestCase;
+import static org.junit.Assert.*;
+import org.junit.Test;
+
+/**
+ * Little Endian data input and output stream tests.
+ *
+ * @author Johann Sorel (Geomatys)
+ * @version 2.0
+ * @since   2.0
+ * @module
+ */
+public class LEStreamTest extends TestCase {
+
+    private static final float DELTA = 0.0000001f;
+    private static final byte[] DATA = new byte[]{
+        (byte)0x00, //boolean false
+        (byte)0x01, //boolean true
+        (byte)0xF2, //ubyte 242
+        (byte)0xFA, //byte -6
+        (byte)0x01,(byte)0x02,(byte)0x03,(byte)0x04,(byte)0x05, //byte array 
1,2,3,4,5
+        (byte)0x70,(byte)0xE0, //ushort 57456
+        (byte)0x05,(byte)0xC1, //short -16123
+        (byte)0x79,(byte)0x00, //char 'y'
+        (byte)0xFB,(byte)0x24,(byte)0x0E,(byte)0x2F, //int 789456123
+        
(byte)0x15,(byte)0xCD,(byte)0x5B,(byte)0x07,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,
 //long 123456789
+        (byte)0xB6,(byte)0xD3,(byte)0xA0,(byte)0x43, //float 321.654
+        
(byte)0x21,(byte)0xB0,(byte)0x72,(byte)0x68,(byte)0x91,(byte)0xDA,(byte)0x8E,(byte)0x40
 //double 987.321
+    };
+
+    @Test
+    public void readTest() throws IOException{
+
+        final LEDataInputStream ds = new LEDataInputStream(new 
ByteArrayInputStream(DATA));
+        assertEquals(0, ds.getPosition());
+
+        //byte types
+        assertEquals(false, ds.readBoolean());
+        assertEquals(1,     ds.getPosition());
+        assertEquals(true,  ds.readBoolean());
+        assertEquals(2,     ds.getPosition());
+        assertEquals(242,   ds.readUnsignedByte());
+        assertEquals(3,     ds.getPosition());
+        assertEquals(-6,    ds.readByte());
+        assertEquals(4,     ds.getPosition());
+        final byte[] array = new byte[5];
+        ds.readFully(array);
+        assertArrayEquals(new byte[]{1,2,3,4,5}, array);
+        assertEquals(9, ds.getPosition());
+        //primitive types
+        assertEquals(57456, ds.readUnsignedShort());
+        assertEquals(11, ds.getPosition());
+        assertEquals(-16123, ds.readShort());
+        assertEquals(13, ds.getPosition());
+        assertEquals('y', ds.readChar());
+        assertEquals(15, ds.getPosition());
+        assertEquals(789456123, ds.readInt());
+        assertEquals(19, ds.getPosition());
+        assertEquals(123456789, ds.readLong());
+        assertEquals(27, ds.getPosition());
+        assertEquals(321.654f, ds.readFloat(),DELTA);
+        assertEquals(31, ds.getPosition());
+        assertEquals(987.321, ds.readDouble(), DELTA);
+        assertEquals(39, ds.getPosition());
+
+    }
+
+    @Test
+    public void writeTest() throws IOException{
+
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final LEDataOutputStream ds = new LEDataOutputStream(out);
+        assertEquals(0, ds.getPosition());
+
+        //byte types
+        ds.writeBoolean(false);
+        assertEquals(1,     ds.getPosition());
+        ds.writeBoolean(true);
+        assertEquals(2,     ds.getPosition());
+        ds.writeByte(242);
+        assertEquals(3,     ds.getPosition());
+        ds.writeByte(-6);
+        assertEquals(4,     ds.getPosition());
+        ds.write(new byte[]{1,2,3,4,5});
+        assertEquals(9, ds.getPosition());
+        //primitive types
+        ds.writeShort(57456);
+        assertEquals(11, ds.getPosition());
+        ds.writeShort(-16123);
+        assertEquals(13, ds.getPosition());
+        ds.writeChar('y');
+        assertEquals(15, ds.getPosition());
+        ds.writeInt(789456123);
+        assertEquals(19, ds.getPosition());
+        ds.writeLong(123456789);
+        assertEquals(27, ds.getPosition());
+        ds.writeFloat(321.654f);
+        assertEquals(31, ds.getPosition());
+        ds.writeDouble(987.321);
+        assertEquals(39, ds.getPosition());
+        ds.flush();
+
+        final byte[] res = out.toByteArray();
+        assertArrayEquals(DATA, res);
+
+    }
+
+
+}

Reply via email to