WKBReader.java
package org.cugos.wkg;
import org.cugos.wkg.WKB.Endian;
import org.cugos.wkg.WKB.GeometryType;
import org.cugos.wkg.WKB.GeometryTypeFlag;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
/**
* A WKB and EWKB Geometry Reader
* @author Jared Erickson
*/
public class WKBReader implements Reader<byte[]> {
/**
* Read a Geometry from an array of bytes.
* @param bytes The array of bytes
* @return A Geometry or null
*/
@Override
public Geometry read(byte[] bytes) {
ByteBuffer buffer = ByteBuffer.wrap(bytes);
return read(buffer);
}
@Override
public String getName() {
return "WKB";
}
/**
* Read a Geometry from a hex String
* @param hex The hex String
* @return A Geometry or null
*/
public Geometry read(String hex) {
return read(toBytes(hex));
}
/**
* Read a Geometry from a ByteBuffer
* @param buffer The ByteBuffer
* @return A Geometry or null
*/
protected Geometry read(ByteBuffer buffer) {
// Determine byte order
Endian endian = Endian.get(buffer.get());
if (endian == Endian.Big) {
buffer.order(ByteOrder.BIG_ENDIAN);
} else {
buffer.order(ByteOrder.LITTLE_ENDIAN);
}
// Determine geometry type
int geometryTypeInt = buffer.getInt();
int gt = geometryTypeInt;
// Determine dimension
boolean hasM = false;
boolean hasZ = false;
boolean hasSRID = false;
if ((geometryTypeInt & GeometryTypeFlag.M.getValue()) == GeometryTypeFlag.M.getValue()) {
hasM = true;
gt = gt - GeometryTypeFlag.M.getValue();
}
if ((geometryTypeInt & GeometryTypeFlag.Z.getValue()) == GeometryTypeFlag.Z.getValue()) {
hasZ = true;
gt = gt - GeometryTypeFlag.Z.getValue();
}
if ((geometryTypeInt & GeometryTypeFlag.SRID.getValue()) == GeometryTypeFlag.SRID.getValue()) {
hasSRID = true;
gt = gt - GeometryTypeFlag.SRID.getValue();
}
GeometryType geometryType = GeometryType.get(gt);
Dimension dimension;
if (hasZ && hasM) {
dimension = Dimension.ThreeMeasured;
} else if (hasZ) {
dimension = Dimension.Three;
} else if (hasM) {
dimension = Dimension.TwoMeasured;
} else {
dimension = Dimension.Two;
}
// Extract SRID if present
String srid = null;
if (hasSRID) {
srid = String.valueOf(buffer.getInt());
}
// Extract Geometry
if (geometryType == GeometryType.Point) {
Coordinate coordinate = getCoordinate(buffer, dimension);
return new Point(coordinate, dimension, srid);
} else if (geometryType == GeometryType.LineString) {
int numberOfCoordinates = buffer.getInt();
List<Coordinate> coordinates = new ArrayList<Coordinate>(numberOfCoordinates);
for(int i = 0; i<numberOfCoordinates; i++) {
coordinates.add(getCoordinate(buffer, dimension));
}
return new LineString(coordinates, dimension, srid);
} else if (geometryType == GeometryType.Polygon) {
int numberOfRings = buffer.getInt();
List<LinearRing> rings = new ArrayList<LinearRing>(numberOfRings);
for (int i = 0; i < numberOfRings; i++) {
int numberOfCoordinates = buffer.getInt();
List<Coordinate> coordinates = new ArrayList<Coordinate>(numberOfCoordinates);
for (int j = 0; j < numberOfCoordinates; j++) {
coordinates.add(getCoordinate(buffer, dimension));
}
rings.add(new LinearRing(coordinates, dimension, srid));
}
return new Polygon(rings.get(0), rings.subList(1, rings.size()), dimension, srid);
} else if (geometryType == GeometryType.MultiPoint) {
int numberOfPoints = buffer.getInt();
List<Point> points = new ArrayList<Point>(numberOfPoints);
for (int i = 0; i < numberOfPoints; i++) {
Point point = (Point) read(buffer);
points.add(point);
}
return new MultiPoint(points, dimension, srid);
} else if (geometryType == GeometryType.MultiLineString) {
int numberOfLines = buffer.getInt();
List<LineString> lines = new ArrayList<LineString>(numberOfLines);
for (int i = 0; i < numberOfLines; i++) {
LineString line = (LineString) read(buffer);
lines.add(line);
}
return new MultiLineString(lines, dimension, srid);
} else if (geometryType == GeometryType.MultiPolygon) {
int numberOfPolygons = buffer.getInt();
List<Polygon> polygons = new ArrayList<Polygon>(numberOfPolygons);
for (int i = 0; i < numberOfPolygons; i++) {
Polygon polygon = (Polygon) read(buffer);
polygons.add(polygon);
}
return new MultiPolygon(polygons, dimension, srid);
} else if (geometryType == GeometryType.GeometryCollection) {
int numberOfGeometries = buffer.getInt();
List<Geometry> geometries = new ArrayList<Geometry>(numberOfGeometries);
for (int i = 0; i < numberOfGeometries; i++) {
Geometry geometry = read(buffer);
geometries.add(geometry);
}
return new GeometryCollection(geometries, dimension, srid);
} else if (geometryType == GeometryType.CircularString) {
int numberOfCoordinates = buffer.getInt();
List<Coordinate> coordinates = new ArrayList<Coordinate>(numberOfCoordinates);
for(int i = 0; i<numberOfCoordinates; i++) {
coordinates.add(getCoordinate(buffer, dimension));
}
return new CircularString(coordinates, dimension, srid);
} else if (geometryType == GeometryType.Triangle) {
int numberOfRings = buffer.getInt();
List<LinearRing> rings = new ArrayList<LinearRing>(numberOfRings);
for (int i = 0; i < numberOfRings; i++) {
int numberOfCoordinates = buffer.getInt();
List<Coordinate> coordinates = new ArrayList<Coordinate>(numberOfCoordinates);
for (int j = 0; j < numberOfCoordinates; j++) {
coordinates.add(getCoordinate(buffer, dimension));
}
rings.add(new LinearRing(coordinates, dimension, srid));
}
return new Triangle(rings.get(0), rings.subList(1, rings.size()), dimension, srid);
} else if (geometryType == GeometryType.Tin) {
int numberOfTriangles = buffer.getInt();
List<Triangle> triangles = new ArrayList<Triangle>(numberOfTriangles);
for (int i = 0; i < numberOfTriangles; i++) {
Triangle triangle = (Triangle) read(buffer);
triangles.add(triangle);
}
return new Tin(triangles, dimension, srid);
} else if (geometryType == GeometryType.CompoundCurve) {
int numberOfCurves = buffer.getInt();
List<Curve> curves = new ArrayList<Curve>(numberOfCurves);
for (int i = 0; i < numberOfCurves; i++) {
Curve curve = (Curve) read(buffer);
curves.add(curve);
}
return new CompoundCurve(curves, dimension, srid);
} else if (geometryType == GeometryType.MultiCurve) {
int numberOfCurves = buffer.getInt();
List<Curve> curves = new ArrayList<Curve>(numberOfCurves);
for (int i = 0; i < numberOfCurves; i++) {
Curve curve = (Curve) read(buffer);
curves.add(curve);
}
return new MultiCurve(curves, dimension, srid);
} else if (geometryType == GeometryType.CurvePolygon) {
int numberOfCurves = buffer.getInt();
List<Curve> curves = new ArrayList<Curve>(numberOfCurves);
for (int i = 0; i < numberOfCurves; i++) {
Curve curve = (Curve) read(buffer);
curves.add(curve);
}
return new CurvePolygon(curves.get(0), curves.subList(1, curves.size()), dimension, srid);
} else if (geometryType == GeometryType.MultiSurface) {
int numberOfSurfaces = buffer.getInt();
List<Surface> surfaces = new ArrayList<Surface>(numberOfSurfaces);
for (int i = 0; i < numberOfSurfaces; i++) {
Surface surface = (Surface) read(buffer);
surfaces.add(surface);
}
return new MultiSurface(surfaces, dimension, srid);
} else if (geometryType == GeometryType.PolyHedralSurface) {
int numberOfPolygons = buffer.getInt();
List<Polygon> polygons = new ArrayList<Polygon>(numberOfPolygons);
for (int i = 0; i < numberOfPolygons; i++) {
Polygon polygon = (Polygon) read(buffer);
polygons.add(polygon);
}
return new PolyHedralSurface(polygons, dimension, srid);
}
return null;
}
/**
* Get Coordinate of the given Dimension from the ByteBuffer
* @param buffer The ByteBuffer
* @param dimension The Dimension
* @return A Coordinate
*/
private Coordinate getCoordinate(ByteBuffer buffer, Dimension dimension) {
double x = buffer.getDouble();
double y = buffer.getDouble();
double z = Double.NaN;
double m = Double.NaN;
if (dimension == Dimension.Three || dimension == Dimension.ThreeMeasured) {
z = buffer.getDouble();
}
if (dimension == Dimension.ThreeMeasured || dimension == Dimension.TwoMeasured) {
m = buffer.getDouble();
}
return new Coordinate(x,y,z,m);
}
/**
* Convert a hex String into a byte Array
* @param hexString The hex String
* @return An array of bytes
*/
private static byte[] toBytes(String hexString) {
int len = hexString.length();
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
+ Character.digit(hexString.charAt(i+1), 16));
}
return data;
}
}