/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.data.postgis.attributeio; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import org.geotools.data.DataSourceException; import org.geotools.data.jdbc.attributeio.AttributeIO; import org.geotools.factory.Hints; import org.geotools.geometry.jts.LiteCoordinateSequenceFactory; import com.vividsolutions.jts.geom.CoordinateSequenceFactory; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.PrecisionModel; import com.vividsolutions.jts.geom.impl.CoordinateArraySequenceFactory; import com.vividsolutions.jts.io.InStream; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; /** * An attribute IO implementation that can manage the WKB * * @author Andrea Aime * * * @source $URL$ */ public class PgWKBAttributeIO implements AttributeIO { private boolean useByteArray; // private GeometryFactory gf; WKBReader wkbr; ByteArrayInStream inStream = new ByteArrayInStream(); public PgWKBAttributeIO(boolean useByteArray) { this.useByteArray = useByteArray; wkbr = new WKBReader(); } public PgWKBAttributeIO(boolean useByteArray, Hints hints) { this.useByteArray = useByteArray; // setup the geometry factory according to the hints GeometryFactory gf = (GeometryFactory) hints.get(Hints.JTS_GEOMETRY_FACTORY); if(gf == null) { PrecisionModel pm = (PrecisionModel) hints.get(Hints.JTS_PRECISION_MODEL); if(pm == null) pm = new PrecisionModel(); Integer SRID = (Integer) hints.get(Hints.JTS_SRID); int srid = SRID == null ? 0 : SRID.intValue(); Integer dimension = (Integer) hints.get(Hints.COORDINATE_DIMENSION); CoordinateSequenceFactory csFactory = (CoordinateSequenceFactory) hints.get(Hints.JTS_COORDINATE_SEQUENCE_FACTORY); if(csFactory == null) { if(dimension == null || dimension <= 3) { csFactory = CoordinateArraySequenceFactory.instance(); } else { csFactory = new LiteCoordinateSequenceFactory(); } } gf = new GeometryFactory(pm, srid, csFactory); } wkbr = new WKBReader(gf); } /** * Turns a char that encodes four bits in hexadecimal notation into a byte * * @param c * */ public static byte getFromChar(char c) { if (c <= '9') { return (byte) (c - '0'); } else if (c <= 'F') { return (byte) (c - 'A' + 10); } else { return (byte) (c - 'a' + 10); } } /** * This method will convert a String of hex characters that represent the * hexadecimal form of a Geometry in Well Known Binary representation to a * JTS Geometry object. * * @param wkb a String of hex characters where each character represents a * hex value. In particular, each character is a value of 0-9, A, B * ,C, D, E, or F. * * @return a JTS Geometry object that is equivalent to the WTB * representation passed in by param wkb * * @throws IOException if more than one geometry object was found in the * WTB representation, or if the parser could not parse the WKB * representation. */ private Geometry WKB2Geometry(byte[] wkbBytes) throws IOException { // convert the byte[] to a JTS Geometry object if (wkbBytes == null) //DJB: null value from database --> null geometry (the same behavior as WKT). NOTE: sending back a GEOMETRYCOLLECTION(EMPTY) is also a possibility, but this is not the same as NULL return null; try { inStream.setBytes(wkbBytes); return wkbr.read(inStream); } catch (Exception e) { throw new DataSourceException("An exception occurred while parsing WKB data", e); } // JTSFactory factory = new JTSFactory(); // WKBParser parser = new WKBParser(factory); // try { // parser.parseData(wkbBytes, 42102); // } catch (Exception e) { // throw new DataSourceException("An exception occurred while parsing WKB data", e); // } // ArrayList geoms = factory.getGeometries(); // // if (geoms.size() > 0) { // return (Geometry) geoms.get(0); // } else if (geoms.size() > 1) { // throw new IOException( // "Found more than one Geometry in WKB representation "); // } else { // throw new IOException( // "Could not parse WKB representations - found no Geometries "); // } } private byte[] hexToBytes(String wkb) { // convert the String of hex values to a byte[] byte[] wkbBytes = new byte[wkb.length() / 2]; for (int i = 0; i < wkbBytes.length; i++) { byte b1 = getFromChar(wkb.charAt(i * 2)); byte b2 = getFromChar(wkb.charAt((i * 2) + 1)); wkbBytes[i] = (byte) ((b1 << 4) | b2); } return wkbBytes; } /* private byte[] byteaToBytes(byte[] bytes) { for(int i = 0; i < bytes.length; i++) { if(bytes[i] >= 'A') bytes[i] -= ('A' + 10); else bytes[i] -= '0'; } return bytes; } */ public void compare(byte[] b1, byte[] b2) { int len = b2.length; for (int t=0;t<len;t++) { if (b1[t] != b2[t]) System.out.println("differ at position "+t+ "("+b1[t]+" vs "+b2[t]+")"); } } /** * @see org.geotools.data.jdbc.attributeio.AttributeIO#read(java.sql.ResultSet, * int) */ public Object read(ResultSet rs, int position) throws IOException { try { if(useByteArray) { byte bytes[] = rs.getBytes(position); // byte b[] = Base64.decode(bytes); // byte b2[] = Base64.decode(bytes,0,bytes.length); // compare(b,b2); if (bytes == null) // ie. its a null column -> return a null geometry! return null; return WKB2Geometry(Base64.decode(bytes)); // return WKB2Geometry(bytes); } else { return WKB2Geometry(hexToBytes(rs.getString(position))); } } catch (SQLException e) { throw new DataSourceException("SQL exception occurred while reading the geometry.", e); } } /** * @param metaData * @throws SQLException * private void printMetadata(ResultSetMetaData md) throws SQLException { for (int i = 1; i <= md.getColumnCount(); i++) { System.out.println(i + " " + md.getColumnName(i) + " " + md.getColumnTypeName(i)); } }*/ /** * Unsupported, will throw an UnsupportedOperationException * @see org.geotools.data.jdbc.attributeio.AttributeIO#write(java.sql.ResultSet, * int, java.lang.Object) */ public void write(ResultSet rs, int position, Object value) throws IOException { try { if(value == null) { rs.updateNull(position); } else { //rs.updateString(position, WKBEncoder.encodeGeometryHex((Geometry) value)); rs.updateBytes( position, new WKBWriter().write( (Geometry)value )); } } catch (SQLException e) { throw new DataSourceException("SQL exception occurred while reading the geometry.", e); } } /** * @see org.geotools.data.jdbc.attributeio.AttributeIO#write(java.sql.PreparedStatement, int, java.lang.Object) */ public void write(PreparedStatement ps, int position, Object value) throws IOException { try { if(value == null) { ps.setNull(position, Types.OTHER); } else { //ps.setString(position, WKBEncoder.encodeGeometryHex((Geometry) value)); ps.setBytes( position, new WKBWriter().write( (Geometry)value )); } } catch (SQLException e) { throw new DataSourceException("SQL exception occurred while reading the geometry.", e); } } private static class ByteArrayInStream implements InStream { byte[] buffer; int position; public void setBytes(final byte[] buffer) { this.buffer = buffer; this.position = 0; } public void read(final byte[] buf) throws IOException { final int size = buf.length; System.arraycopy(buffer, position, buf, 0, size); position += size; } } }