Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* JtsBinaryWriter.java
*
* PostGIS extension for PostgreSQL JDBC driver - Binary Writer
*
* (C) 2005 Markus Schaber, [email protected]
*
* (C) 2015 Phillip Ross, [email protected]
*
* 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.postgis.jts;
import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryCollection;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.MultiLineString;
import org.locationtech.jts.geom.MultiPoint;
import org.locationtech.jts.geom.MultiPolygon;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.geom.Polygon;
import org.postgis.binary.ByteSetter;
import org.postgis.binary.ValueSetter;
/**
* Create binary representation of geometries. Currently, only text rep (hexed)
* implementation is tested. Supports only 2 dimensional geometries.
*
* It should be easy to add char[] and CharSequence ByteGetter instances,
* although the latter one is not compatible with older jdks.
*
* I did not implement real unsigned 32-bit integers or emulate them with long,
* as both java Arrays and Strings currently can have only 2^31-1 elements
* (bytes), so we cannot even get or build Geometries with more than approx.
* 2^28 coordinates (8 bytes each).
*
* @author [email protected]
*
*/
public class JtsBinaryWriter {
/**
* Get the appropriate ValueGetter for my endianness
*
* @param bytes The ByteSetter
* @param endian The endian to be used
* @return the appropriate ValueSetter for the specified endian
*/
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) {
return new ValueSetter.NDR(bytes);
} else {
throw new IllegalArgumentException("Unknown Endian type:" + endian);
}
}
/**
* Write a hex encoded geometry
*
* Currently, geometries with more than 2 dimensions and measures are not
* cleanly supported, but SRID is honored.
*
* @param geom The geometry to be written
* @param REP The endianness representation to use for writing
* @return String containing the hex-encoded geometry
*/
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) {
return writeHexed(geom, ValueSetter.NDR.NUMBER);
}
/**
* Write a binary encoded geometry.
*
* Currently, geometries with more than 2 dimensions and measures are not
* cleanly supported, but SRID is honored.
*
* @param geom The geometry to be written
* @param REP The endianness representation to use for writing
* @return byte array containing the encoded geometry
*/
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) {
return writeBinary(geom, ValueSetter.NDR.NUMBER);
}
/**
* Parse a geometry starting at offset.
* @param geom The Geometry to be written
* @param dest The ValueSettr to write to
*/
protected void writeGeometry(Geometry geom, ValueSetter dest) {
final int dimension;
if (geom == null) {
throw new NullPointerException();
} else if (geom.isEmpty()) {
// don't set any flag bits
dimension = 0;
} else {
dimension = getCoordDim(geom);
if (dimension < 2 || dimension > 4) {
throw new IllegalArgumentException("Unsupported geometry dimensionality: " + dimension);
}
}
// write endian flag
dest.setByte(dest.endian);
// write typeword
final int plaintype = getWKBType(geom);
int typeword = plaintype;
if (dimension == 3 || dimension == 4) {
typeword |= 0x80000000;
}
if (dimension == 4) {
typeword |= 0x40000000;
}
final boolean haveSrid = checkSrid(geom);
if (haveSrid) {
typeword |= 0x20000000;
}
dest.setInt(typeword);
if (haveSrid) {
dest.setInt(geom.getSRID());
}
switch (plaintype) {
case org.postgis.Geometry.POINT:
writePoint((Point) geom, dest);
break;
case org.postgis.Geometry.LINESTRING:
writeLineString((LineString) geom, dest);
break;
case org.postgis.Geometry.POLYGON:
writePolygon((Polygon) geom, dest);
break;
case org.postgis.Geometry.MULTIPOINT:
writeMultiPoint((MultiPoint) geom, dest);
break;
case org.postgis.Geometry.MULTILINESTRING:
writeMultiLineString((MultiLineString) geom, dest);
break;
case org.postgis.Geometry.MULTIPOLYGON:
writeMultiPolygon((MultiPolygon) geom, dest);
break;
case org.postgis.Geometry.GEOMETRYCOLLECTION:
writeCollection((GeometryCollection) geom, dest);
break;
default:
throw new IllegalArgumentException("Unknown Geometry Type: " + plaintype);
}
}
public static int getWKBType(Geometry geom) {
// We always write empty geometries as empty collections - for OpenGIS
// conformance
if (geom.isEmpty()) {
return org.postgis.Geometry.GEOMETRYCOLLECTION;
} else if (geom instanceof Point) {
return org.postgis.Geometry.POINT;
} else if (geom instanceof org.locationtech.jts.geom.LineString) {
return org.postgis.Geometry.LINESTRING;
} else if (geom instanceof org.locationtech.jts.geom.Polygon) {
return org.postgis.Geometry.POLYGON;
} else if (geom instanceof MultiPoint) {
return org.postgis.Geometry.MULTIPOINT;
} else if (geom instanceof MultiLineString) {
return org.postgis.Geometry.MULTILINESTRING;
} else if (geom instanceof org.locationtech.jts.geom.MultiPolygon) {
return org.postgis.Geometry.MULTIPOLYGON;
} if (geom instanceof org.locationtech.jts.geom.GeometryCollection) {
return org.postgis.Geometry.GEOMETRYCOLLECTION;
} 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) {
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++) {
dest.setDouble(seq.getOrdinate(i, d));
}
}
}
private void writeMultiPoint(MultiPoint geom, ValueSetter dest) {
dest.setInt(geom.getNumPoints());
for (int i = 0; i < geom.getNumPoints(); i++) {
writeGeometry(geom.getGeometryN(i), 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) {
dest.setInt(geom.getNumInteriorRing() + 1);
writeLineString(geom.getExteriorRing(), dest);
for (int i = 0; i < geom.getNumInteriorRing(); i++) {
writeLineString(geom.getInteriorRingN(i), dest);
}
}
private void writeMultiLineString(MultiLineString geom, ValueSetter dest) {
writeGeometryArray(geom, dest);
}
private void writeMultiPolygon(MultiPolygon geom, ValueSetter dest) {
writeGeometryArray(geom, dest);
}
private void writeCollection(GeometryCollection geom, ValueSetter dest) {
writeGeometryArray(geom, dest);
}
private void writeGeometryArray(Geometry geom, ValueSetter dest) {
dest.setInt(geom.getNumGeometries());
for (int i = 0; i < geom.getNumGeometries(); i++) {
writeGeometry(geom.getGeometryN(i), dest);
}
}
/**
* Estimate how much bytes a geometry will need in WKB.
* @param geom Geometry to estimate
* @return number of bytes needed
*/
protected int estimateBytes(Geometry geom) {
int result = 0;
// write endian flag
result += 1;
// write typeword
result += 4;
if (checkSrid(geom)) {
result += 4;
}
switch (getWKBType(geom)) {
case org.postgis.Geometry.POINT:
result += estimatePoint((Point) geom);
break;
case org.postgis.Geometry.LINESTRING:
result += estimateLineString((LineString) geom);
break;
case org.postgis.Geometry.POLYGON:
result += estimatePolygon((Polygon) geom);
break;
case org.postgis.Geometry.MULTIPOINT:
result += estimateMultiPoint((MultiPoint) geom);
break;
case org.postgis.Geometry.MULTILINESTRING:
result += estimateMultiLineString((MultiLineString) geom);
break;
case org.postgis.Geometry.MULTIPOLYGON:
result += estimateMultiPolygon((MultiPolygon) geom);
break;
case org.postgis.Geometry.GEOMETRYCOLLECTION:
result += estimateCollection((GeometryCollection) geom);
break;
default:
throw new IllegalArgumentException("Unknown Geometry Type: " + getWKBType(geom));
}
return result;
}
private boolean checkSrid(Geometry geom) {
final int srid = geom.getSRID();
return (srid > 0);
}
private int estimatePoint(Point geom) {
return 8 * getCoordDim(geom);
}
/** Write an Array of "full" Geometries */
private int estimateGeometryArray(Geometry container) {
int result = 0;
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) {
// int size
int result = 4;
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) {
return 0;
} else {
return 4 + 8 * getCoordSequenceDim(geom.getCoordinateSequence()) * geom.getCoordinateSequence().size();
}
}
private int estimatePolygon(Polygon geom) {
// int length
int result = 4;
result += estimateLineString(geom.getExteriorRing());
for (int i = 0; i < geom.getNumInteriorRing(); i++) {
result += estimateLineString(geom.getInteriorRingN(i));
}
return result;
}
private int estimateMultiLineString(MultiLineString geom) {
// 4-byte count + subgeometries
return 4 + estimateGeometryArray(geom);
}
private int estimateMultiPolygon(MultiPolygon geom) {
// 4-byte count + subgeometries
return 4 + estimateGeometryArray(geom);
}
private int estimateCollection(GeometryCollection geom) {
// 4-byte count + subgeometries
return 4 + estimateGeometryArray(geom);
}
public static final int getCoordDim(Geometry geom) {
if (geom.isEmpty()) {
return 0;
}
if (geom instanceof Point) {
return getCoordSequenceDim(((Point) geom).getCoordinateSequence());
} else if (geom instanceof LineString) {
return getCoordSequenceDim(((LineString) geom).getCoordinateSequence());
} else if (geom instanceof Polygon) {
return getCoordSequenceDim(((Polygon) geom).getExteriorRing().getCoordinateSequence());
} else {
return getCoordDim(geom.getGeometryN(0));
}
}
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) {
// 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 {
return dimensions;
}
}
}