/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
* (C) Copyright IBM Corporation, 2005-2007. All rights reserved.
*
* 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.db2;
/**
* This is a modified JTS WKBWriter suiting for DB2
*
* @author Christian Mueller
*
*/
import java.io.*;
import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.io.ByteOrderValues;
import com.vividsolutions.jts.io.OutStream;
import com.vividsolutions.jts.io.OutputStreamOutStream;
import com.vividsolutions.jts.io.WKBConstants;
import com.vividsolutions.jts.io.WKBWriter;
import com.vividsolutions.jts.util.Assert;
/**
*
* @author Christian Mueller
*
* Version of JTS WKB Writer adjusted for DB2
*
* @see WKBWriter
* for JTS Java Doc
*
*
* @source $URL$
*/
public class DB2WKBWriter {
/**
* returns the coordinate dimension for a geometry
* @param Geometry g
* @return if there is one z value != NaN, then 3 else 2
*/
public static final int guessCoorinateDims(Geometry g) {
int dims = 2;
final Coordinate[] cs = g.getCoordinates();
for (int t = cs.length - 1; t >= 0; t--) {
if (!(Double.isNaN(cs[t].z))) {
dims = 3;
break;
}
}
return dims;
}
public static String bytesToHex(byte[] bytes)
{
StringBuffer buf = new StringBuffer();
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];
buf.append(toHexDigit((b >> 4) & 0x0F));
buf.append(toHexDigit(b & 0x0F));
}
return buf.toString();
}
private static char toHexDigit(int n)
{
if (n < 0 || n > 15)
throw new IllegalArgumentException("Nibble value out of range: " + n);
if (n <= 9)
return (char) ('0' + n);
return (char) ('A' + (n - 10));
}
private int outputDimension = 2;
private int byteOrder;
private ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream();
private OutStream byteArrayOutStream = new OutputStreamOutStream(byteArrayOS);
// holds output data values
private byte[] buf = new byte[8];
private boolean hasOGCWkbZTyps;
/**
* Creates a writer that writes {@link Geometry}s with
* output dimension = 2 and BIG_ENDIAN byte order
*/
public DB2WKBWriter(boolean hasOGCWkbZTyps ) {
this(2, ByteOrderValues.BIG_ENDIAN, hasOGCWkbZTyps);
}
/**
* Creates a writer that writes {@link Geometry}s with
* the given dimension (2 or 3) for output coordinates
* and {@link BIG_ENDIAN} byte order.
* If the input geometry has a small coordinate dimension,
* coordinates will be padded with {@link NULL_ORDINATE}.
*
* @param outputDimension the coordinate dimension to output (2 or 3)
*/
public DB2WKBWriter(int outputDimension, boolean hasOGCWkbZTyps) {
this(outputDimension, ByteOrderValues.BIG_ENDIAN,hasOGCWkbZTyps);
}
/**
* Creates a writer that writes {@link Geometry}s with
* the given dimension (2 or 3) for output coordinates
* and byte order
* If the input geometry has a small coordinate dimension,
* coordinates will be padded with {@link NULL_ORDINATE}.
*
* @param outputDimension the coordinate dimension to output (2 or 3)
* @param byteOrder the byte ordering to use
*/
public DB2WKBWriter(int outputDimension, int byteOrder, boolean hasOGCWkbZTyps) {
this.outputDimension = outputDimension;
this.byteOrder = byteOrder;
this.hasOGCWkbZTyps=hasOGCWkbZTyps;
if (outputDimension < 2 || outputDimension > 3)
throw new IllegalArgumentException("Output dimension must be 2 or 3");
}
/**
* Writes a {@link Geometry} into a byte array.
*
* @param geom the geometry to write
* @return the byte array containing the WKB
*/
public byte[] write(Geometry geom)
{
try {
byteArrayOS.reset();
write(geom, byteArrayOutStream);
}
catch (IOException ex) {
throw new RuntimeException("Unexpected IO exception: " + ex.getMessage());
}
return byteArrayOS.toByteArray();
}
/**
* Writes a {@link Geometry} to an {@link OutStream}.
*
* @param geom the geometry to write
* @param os the out stream to write to
* @throws IOException if an I/O error occurs
*/
public void write(Geometry geom, OutStream os) throws IOException
{
if (geom instanceof Point)
writePoint((Point) geom, os);
// LinearRings will be written as LineStrings
else if (geom instanceof LineString)
writeLineString((LineString) geom, os);
else if (geom instanceof Polygon)
writePolygon((Polygon) geom, os);
else if (geom instanceof MultiPoint)
writeGeometryCollection(DB2WKBConstants.wkbMultiPoint2D, (MultiPoint) geom, os);
else if (geom instanceof MultiLineString)
writeGeometryCollection(DB2WKBConstants.wkbMultiLineString2D,
(MultiLineString) geom, os);
else if (geom instanceof MultiPolygon)
writeGeometryCollection(DB2WKBConstants.wkbMultiPolygon2D,
(MultiPolygon) geom, os);
else if (geom instanceof GeometryCollection)
writeGeometryCollection(DB2WKBConstants.wkbGeomCollection2D,
(GeometryCollection) geom, os);
else {
Assert.shouldNeverReachHere("Unknown Geometry type");
}
}
private void writePoint(Point pt, OutStream os) throws IOException
{
if (pt.getCoordinateSequence().size() == 0)
throw new IllegalArgumentException("Empty Points cannot be represented in WKB");
writeByteOrder(os);
writeGeometryType(DB2WKBConstants.wkbPoint2D, os);
writeCoordinateSequence(pt.getCoordinateSequence(), false, os);
}
private void writeLineString(LineString line, OutStream os)
throws IOException
{
writeByteOrder(os);
writeGeometryType(DB2WKBConstants.wkbLineString2D, os);
writeCoordinateSequence(line.getCoordinateSequence(), true, os);
}
private void writePolygon(Polygon poly, OutStream os) throws IOException
{
writeByteOrder(os);
writeGeometryType(DB2WKBConstants.wkbPolygon2D, os);
writeInt(poly.getNumInteriorRing() + 1, os);
writeCoordinateSequence(poly.getExteriorRing().getCoordinateSequence(), true, os);
for (int i = 0; i < poly.getNumInteriorRing(); i++) {
writeCoordinateSequence(poly.getInteriorRingN(i).getCoordinateSequence(), true,
os);
}
}
private void writeGeometryCollection(int geometryType, GeometryCollection gc,
OutStream os) throws IOException
{
writeByteOrder(os);
writeGeometryType(geometryType, os);
writeInt(gc.getNumGeometries(), os);
for (int i = 0; i < gc.getNumGeometries(); i++) {
write(gc.getGeometryN(i), os);
}
}
private void writeByteOrder(OutStream os) throws IOException
{
if (byteOrder == ByteOrderValues.LITTLE_ENDIAN)
buf[0] = WKBConstants.wkbNDR;
else
buf[0] = WKBConstants.wkbXDR;
os.write(buf, 1);
}
private void writeGeometryType(int geometryType, OutStream os)
throws IOException
{
int typeInt = geometryType;
if (outputDimension==3) { // DB2 specific for z support
if (hasOGCWkbZTyps) {
if (geometryType==DB2WKBConstants.wkbPoint2D) typeInt=DB2WKBConstants.wkbOGCPointZ;
if (geometryType==DB2WKBConstants.wkbLineString2D) typeInt=DB2WKBConstants.wkbOGCLineStringZ;
if (geometryType==DB2WKBConstants.wkbPolygon2D) typeInt=DB2WKBConstants.wkbOGCPolygonZ;
if (geometryType==DB2WKBConstants.wkbMultiPoint2D) typeInt=DB2WKBConstants.wkbOGCMultiPointZ;
if (geometryType==DB2WKBConstants.wkbMultiLineString2D) typeInt=DB2WKBConstants.wkbOGCMultiLineStringZ;
if (geometryType==DB2WKBConstants.wkbMultiPolygon2D) typeInt=DB2WKBConstants.wkbOGCMultiPolygonZ;
if (geometryType==DB2WKBConstants.wkbGeomCollection2D) typeInt=DB2WKBConstants.wkbOGCGeomCollectionZ;
}
else {
if (geometryType==DB2WKBConstants.wkbPoint2D) typeInt=DB2WKBConstants.wkbPointZ;
if (geometryType==DB2WKBConstants.wkbLineString2D) typeInt=DB2WKBConstants.wkbLineStringZ;
if (geometryType==DB2WKBConstants.wkbPolygon2D) typeInt=DB2WKBConstants.wkbPolygonZ;
if (geometryType==DB2WKBConstants.wkbMultiPoint2D) typeInt=DB2WKBConstants.wkbMultiPointZ;
if (geometryType==DB2WKBConstants.wkbMultiLineString2D) typeInt=DB2WKBConstants.wkbMultiLineStringZ;
if (geometryType==DB2WKBConstants.wkbMultiPolygon2D) typeInt=DB2WKBConstants.wkbMultiPolygonZ;
if (geometryType==DB2WKBConstants.wkbGeomCollection2D) typeInt=DB2WKBConstants.wkbGeomCollectionZ;
}
}
writeInt(typeInt, os);
}
private void writeInt(int intValue, OutStream os) throws IOException
{
ByteOrderValues.putInt(intValue, buf, byteOrder);
os.write(buf, 4);
}
private void writeCoordinateSequence(CoordinateSequence seq, boolean writeSize, OutStream os)
throws IOException
{
if (writeSize)
writeInt(seq.size(), os);
for (int i = 0; i < seq.size(); i++) {
writeCoordinate(seq, i, os);
}
}
private void writeCoordinate(CoordinateSequence seq, int index, OutStream os)
throws IOException
{
ByteOrderValues.putDouble(seq.getX(index), buf, byteOrder);
os.write(buf, 8);
ByteOrderValues.putDouble(seq.getY(index), buf, byteOrder);
os.write(buf, 8);
// only write 3rd dim if caller has requested it for this writer
if (outputDimension >= 3) {
// if 3rd dim is requested, only access and write it if the CS provides is
double ordVal = Coordinate.NULL_ORDINATE;
if (seq.getDimension() >= 3)
ordVal = seq.getOrdinate(index, 2);
ByteOrderValues.putDouble(ordVal, buf, byteOrder);
os.write(buf, 8);
}
}
}