| | |
| | | * |
| | | * @author markus.schaber@logi-track.com |
| | | */ |
| | | public class JtsBinaryWriter |
| | | { |
| | | public class JtsBinaryWriter { |
| | | |
| | | /** |
| | | * Get the appropriate ValueGetter for my endianness |
| | |
| | | * @param bytes The appropriate Byte Getter |
| | | * @return the ValueGetter |
| | | */ |
| | | public static ValueSetter valueSetterForEndian(ByteSetter bytes, byte endian) |
| | | { |
| | | if (endian == ValueSetter.XDR.NUMBER) |
| | | { // XDR |
| | | public static ValueSetter valueSetterForEndian(ByteSetter bytes, byte endian) { |
| | | if (endian == ValueSetter.XDR.NUMBER) { // XDR |
| | | return new ValueSetter.XDR(bytes); |
| | | } else if (endian == ValueSetter.NDR.NUMBER) |
| | | { |
| | | } else if (endian == ValueSetter.NDR.NUMBER) { |
| | | return new ValueSetter.NDR(bytes); |
| | | } else |
| | | { |
| | | } else { |
| | | throw new IllegalArgumentException("Unknown Endian type:" + endian); |
| | | } |
| | | } |
| | |
| | | * Currently, geometries with more than 2 dimensions and measures are not |
| | | * cleanly supported, but SRID is honored. |
| | | */ |
| | | public String writeHexed(Geometry geom, byte REP) |
| | | { |
| | | public String writeHexed(Geometry geom, byte REP) { |
| | | int length = estimateBytes(geom); |
| | | ByteSetter.StringByteSetter bytes = new ByteSetter.StringByteSetter(length); |
| | | writeGeometry(geom, valueSetterForEndian(bytes, REP)); |
| | | return bytes.result(); |
| | | } |
| | | |
| | | public String writeHexed(Geometry geom) |
| | | { |
| | | public String writeHexed(Geometry geom) { |
| | | return writeHexed(geom, ValueSetter.NDR.NUMBER); |
| | | } |
| | | |
| | |
| | | * Currently, geometries with more than 2 dimensions and measures are not |
| | | * cleanly supported, but SRID is honored. |
| | | */ |
| | | public byte[] writeBinary(Geometry geom, byte REP) |
| | | { |
| | | public byte[] writeBinary(Geometry geom, byte REP) { |
| | | int length = estimateBytes(geom); |
| | | ByteSetter.BinaryByteSetter bytes = new ByteSetter.BinaryByteSetter(length); |
| | | writeGeometry(geom, valueSetterForEndian(bytes, REP)); |
| | | return bytes.result(); |
| | | } |
| | | |
| | | public byte[] writeBinary(Geometry geom) |
| | | { |
| | | public byte[] writeBinary(Geometry geom) { |
| | | return writeBinary(geom, ValueSetter.NDR.NUMBER); |
| | | } |
| | | |
| | | /** |
| | | * Parse a geometry starting at offset. |
| | | */ |
| | | protected void writeGeometry(Geometry geom, ValueSetter dest) |
| | | { |
| | | protected void writeGeometry(Geometry geom, ValueSetter dest) { |
| | | final int dimension; |
| | | if (geom == null) |
| | | { |
| | | if (geom == null) { |
| | | throw new NullPointerException(); |
| | | } else if (geom.isEmpty()) |
| | | { |
| | | } else if (geom.isEmpty()) { |
| | | // don't set any flag bits |
| | | dimension = 0; |
| | | } else |
| | | { |
| | | } else { |
| | | dimension = getCoordDim(geom); |
| | | if (dimension < 2 || dimension > 4) |
| | | { |
| | | if (dimension < 2 || dimension > 4) { |
| | | throw new IllegalArgumentException("Unsupported geometry dimensionality: " + dimension); |
| | | } |
| | | } |
| | |
| | | // write typeword |
| | | final int plaintype = getWKBType(geom); |
| | | int typeword = plaintype; |
| | | if (dimension == 3 || dimension == 4) |
| | | { |
| | | if (dimension == 3 || dimension == 4) { |
| | | typeword |= 0x80000000; |
| | | } |
| | | if (dimension == 4) |
| | | { |
| | | if (dimension == 4) { |
| | | typeword |= 0x40000000; |
| | | } |
| | | |
| | | final boolean haveSrid = checkSrid(geom); |
| | | if (haveSrid) |
| | | { |
| | | if (haveSrid) { |
| | | typeword |= 0x20000000; |
| | | } |
| | | |
| | | dest.setInt(typeword); |
| | | |
| | | if (haveSrid) |
| | | { |
| | | if (haveSrid) { |
| | | dest.setInt(geom.getSRID()); |
| | | } |
| | | |
| | | switch (plaintype) |
| | | { |
| | | switch (plaintype) { |
| | | case org.postgis.Geometry.POINT: |
| | | writePoint((Point) geom, dest); |
| | | break; |
| | |
| | | } |
| | | } |
| | | |
| | | public static int getWKBType(Geometry geom) |
| | | { |
| | | public static int getWKBType(Geometry geom) { |
| | | // We always write emtpy geometries as emtpy collections - for OpenGIS |
| | | // conformance |
| | | if (geom.isEmpty()) |
| | | { |
| | | if (geom.isEmpty()) { |
| | | return org.postgis.Geometry.GEOMETRYCOLLECTION; |
| | | } else if (geom instanceof Point) |
| | | { |
| | | } else if (geom instanceof Point) { |
| | | return org.postgis.Geometry.POINT; |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.LineString) |
| | | { |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.LineString) { |
| | | return org.postgis.Geometry.LINESTRING; |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.Polygon) |
| | | { |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.Polygon) { |
| | | return org.postgis.Geometry.POLYGON; |
| | | } else if (geom instanceof MultiPoint) |
| | | { |
| | | } else if (geom instanceof MultiPoint) { |
| | | return org.postgis.Geometry.MULTIPOINT; |
| | | } else if (geom instanceof MultiLineString) |
| | | { |
| | | } else if (geom instanceof MultiLineString) { |
| | | return org.postgis.Geometry.MULTILINESTRING; |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.MultiPolygon) |
| | | { |
| | | } else if (geom instanceof com.vividsolutions.jts.geom.MultiPolygon) { |
| | | return org.postgis.Geometry.MULTIPOLYGON; |
| | | } |
| | | if (geom instanceof com.vividsolutions.jts.geom.GeometryCollection) |
| | | { |
| | | if (geom instanceof com.vividsolutions.jts.geom.GeometryCollection) { |
| | | return org.postgis.Geometry.GEOMETRYCOLLECTION; |
| | | } else |
| | | { |
| | | } else { |
| | | throw new IllegalArgumentException("Unknown Geometry Type: " + geom.getClass().getName()); |
| | | } |
| | | } |
| | |
| | | * Writes a "slim" Point (without endiannes, srid ant type, only the |
| | | * ordinates and measure. Used by writeGeometry. |
| | | */ |
| | | private void writePoint(Point geom, ValueSetter dest) |
| | | { |
| | | private void writePoint(Point geom, ValueSetter dest) { |
| | | writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest); |
| | | } |
| | | |
| | |
| | | * Write a Coordinatesequence, part of LinearRing and Linestring, but not |
| | | * MultiPoint! |
| | | */ |
| | | private void writeCoordinates(CoordinateSequence seq, int dims, ValueSetter dest) |
| | | { |
| | | for (int i = 0; i < seq.size(); i++) |
| | | { |
| | | for (int d = 0; d < dims; d++) |
| | | { |
| | | private void writeCoordinates(CoordinateSequence seq, int dims, ValueSetter dest) { |
| | | for (int i = 0; i < seq.size(); i++) { |
| | | for (int d = 0; d < dims; d++) { |
| | | dest.setDouble(seq.getOrdinate(i, d)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void writeMultiPoint(MultiPoint geom, ValueSetter dest) |
| | | { |
| | | private void writeMultiPoint(MultiPoint geom, ValueSetter dest) { |
| | | dest.setInt(geom.getNumPoints()); |
| | | for (int i = 0; i < geom.getNumPoints(); i++) |
| | | { |
| | | for (int i = 0; i < geom.getNumPoints(); i++) { |
| | | writeGeometry(geom.getGeometryN(i), dest); |
| | | } |
| | | } |
| | | |
| | | private void writeLineString(LineString geom, ValueSetter dest) |
| | | { |
| | | private void writeLineString(LineString geom, ValueSetter dest) { |
| | | dest.setInt(geom.getNumPoints()); |
| | | writeCoordinates(geom.getCoordinateSequence(), getCoordDim(geom), dest); |
| | | } |
| | | |
| | | private void writePolygon(Polygon geom, ValueSetter dest) |
| | | { |
| | | private void writePolygon(Polygon geom, ValueSetter dest) { |
| | | dest.setInt(geom.getNumInteriorRing() + 1); |
| | | writeLineString(geom.getExteriorRing(), dest); |
| | | for (int i = 0; i < geom.getNumInteriorRing(); i++) |
| | | { |
| | | for (int i = 0; i < geom.getNumInteriorRing(); i++) { |
| | | writeLineString(geom.getInteriorRingN(i), dest); |
| | | } |
| | | } |
| | | |
| | | private void writeMultiLineString(MultiLineString geom, ValueSetter dest) |
| | | { |
| | | private void writeMultiLineString(MultiLineString geom, ValueSetter dest) { |
| | | writeGeometryArray(geom, dest); |
| | | } |
| | | |
| | | private void writeMultiPolygon(MultiPolygon geom, ValueSetter dest) |
| | | { |
| | | private void writeMultiPolygon(MultiPolygon geom, ValueSetter dest) { |
| | | writeGeometryArray(geom, dest); |
| | | } |
| | | |
| | | private void writeCollection(GeometryCollection geom, ValueSetter dest) |
| | | { |
| | | private void writeCollection(GeometryCollection geom, ValueSetter dest) { |
| | | writeGeometryArray(geom, dest); |
| | | } |
| | | |
| | | private void writeGeometryArray(Geometry geom, ValueSetter dest) |
| | | { |
| | | private void writeGeometryArray(Geometry geom, ValueSetter dest) { |
| | | dest.setInt(geom.getNumGeometries()); |
| | | for (int i = 0; i < geom.getNumGeometries(); i++) |
| | | { |
| | | for (int i = 0; i < geom.getNumGeometries(); i++) { |
| | | writeGeometry(geom.getGeometryN(i), dest); |
| | | } |
| | | } |
| | |
| | | /** |
| | | * Estimate how much bytes a geometry will need in WKB. |
| | | */ |
| | | protected int estimateBytes(Geometry geom) |
| | | { |
| | | protected int estimateBytes(Geometry geom) { |
| | | int result = 0; |
| | | |
| | | // write endian flag |
| | |
| | | // write typeword |
| | | result += 4; |
| | | |
| | | if (checkSrid(geom)) |
| | | { |
| | | if (checkSrid(geom)) { |
| | | result += 4; |
| | | } |
| | | |
| | | switch (getWKBType(geom)) |
| | | { |
| | | switch (getWKBType(geom)) { |
| | | case org.postgis.Geometry.POINT: |
| | | result += estimatePoint((Point) geom); |
| | | break; |
| | |
| | | return result; |
| | | } |
| | | |
| | | private boolean checkSrid(Geometry geom) |
| | | { |
| | | private boolean checkSrid(Geometry geom) { |
| | | final int srid = geom.getSRID(); |
| | | // SRID is default 0 with jts geometries |
| | | return (srid != -1) && (srid != 0); |
| | | } |
| | | |
| | | private int estimatePoint(Point geom) |
| | | { |
| | | private int estimatePoint(Point geom) { |
| | | return 8 * getCoordDim(geom); |
| | | } |
| | | |
| | | /** |
| | | * Write an Array of "full" Geometries |
| | | */ |
| | | private int estimateGeometryArray(Geometry container) |
| | | { |
| | | private int estimateGeometryArray(Geometry container) { |
| | | int result = 0; |
| | | for (int i = 0; i < container.getNumGeometries(); i++) |
| | | { |
| | | for (int i = 0; i < container.getNumGeometries(); i++) { |
| | | result += estimateBytes(container.getGeometryN(i)); |
| | | } |
| | | return result; |
| | |
| | | /** |
| | | * Estimate an array of "fat" Points |
| | | */ |
| | | private int estimateMultiPoint(MultiPoint geom) |
| | | { |
| | | private int estimateMultiPoint(MultiPoint geom) { |
| | | // int size |
| | | int result = 4; |
| | | if (geom.getNumGeometries() > 0) |
| | | { |
| | | if (geom.getNumGeometries() > 0) { |
| | | // We can shortcut here, compared to estimateGeometryArray, as all |
| | | // subgeoms have the same fixed size |
| | | result += geom.getNumGeometries() * estimateBytes(geom.getGeometryN(0)); |
| | |
| | | return result; |
| | | } |
| | | |
| | | private int estimateLineString(LineString geom) |
| | | { |
| | | if (geom == null || geom.getNumGeometries() == 0) |
| | | { |
| | | private int estimateLineString(LineString geom) { |
| | | if (geom == null || geom.getNumGeometries() == 0) { |
| | | return 0; |
| | | } else |
| | | { |
| | | } else { |
| | | return 4 + 8 * getCoordSequenceDim(geom.getCoordinateSequence()) * geom.getCoordinateSequence().size(); |
| | | } |
| | | } |
| | | |
| | | private int estimatePolygon(Polygon geom) |
| | | { |
| | | private int estimatePolygon(Polygon geom) { |
| | | // int length |
| | | int result = 4; |
| | | result += estimateLineString(geom.getExteriorRing()); |
| | | for (int i = 0; i < geom.getNumInteriorRing(); i++) |
| | | { |
| | | for (int i = 0; i < geom.getNumInteriorRing(); i++) { |
| | | result += estimateLineString(geom.getInteriorRingN(i)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private int estimateMultiLineString(MultiLineString geom) |
| | | { |
| | | private int estimateMultiLineString(MultiLineString geom) { |
| | | // 4-byte count + subgeometries |
| | | return 4 + estimateGeometryArray(geom); |
| | | } |
| | | |
| | | private int estimateMultiPolygon(MultiPolygon geom) |
| | | { |
| | | private int estimateMultiPolygon(MultiPolygon geom) { |
| | | // 4-byte count + subgeometries |
| | | return 4 + estimateGeometryArray(geom); |
| | | } |
| | | |
| | | private int estimateCollection(GeometryCollection geom) |
| | | { |
| | | private int estimateCollection(GeometryCollection geom) { |
| | | // 4-byte count + subgeometries |
| | | return 4 + estimateGeometryArray(geom); |
| | | } |
| | | |
| | | public static final int getCoordDim(Geometry geom) |
| | | { |
| | | if (geom.isEmpty()) |
| | | { |
| | | public static final int getCoordDim(Geometry geom) { |
| | | if (geom.isEmpty()) { |
| | | return 0; |
| | | } |
| | | if (geom instanceof Point) |
| | | { |
| | | if (geom instanceof Point) { |
| | | return getCoordSequenceDim(((Point) geom).getCoordinateSequence()); |
| | | } else if (geom instanceof LineString) |
| | | { |
| | | } else if (geom instanceof LineString) { |
| | | return getCoordSequenceDim(((LineString) geom).getCoordinateSequence()); |
| | | } else if (geom instanceof Polygon) |
| | | { |
| | | } else if (geom instanceof Polygon) { |
| | | return getCoordSequenceDim(((Polygon) geom).getExteriorRing().getCoordinateSequence()); |
| | | } else |
| | | { |
| | | } else { |
| | | return getCoordDim(geom.getGeometryN(0)); |
| | | } |
| | | } |
| | | |
| | | public static final int getCoordSequenceDim(CoordinateSequence coords) |
| | | { |
| | | public static final int getCoordSequenceDim(CoordinateSequence coords) { |
| | | if (coords == null || coords.size() == 0) |
| | | return 0; |
| | | // JTS has a really strange way to handle dimensions! |
| | | // Just have a look at PackedCoordinateSequence and |
| | | // CoordinateArraySequence |
| | | int dimensions = coords.getDimension(); |
| | | if (dimensions == 3) |
| | | { |
| | | if (dimensions == 3) { |
| | | // CoordinateArraySequence will always return 3, so we have to |
| | | // check, if |
| | | // the third ordinate contains NaN, then the geom is actually |
| | | // 2-dimensional |
| | | return Double.isNaN(coords.getOrdinate(0, CoordinateSequence.Z)) ? 2 : 3; |
| | | } else |
| | | { |
| | | } else { |
| | | return dimensions; |
| | | } |
| | | } |