All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.shaded.org.locationtech.jts.io.WKBWriter Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright (c) 2016 Vivid Solutions.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */
package com.hazelcast.shaded.org.locationtech.jts.io;

import java.io.ByteArrayOutputStream;
import java.io.IOException;

import com.hazelcast.shaded.org.locationtech.jts.geom.Coordinate;
import com.hazelcast.shaded.org.locationtech.jts.geom.CoordinateSequence;
import com.hazelcast.shaded.org.locationtech.jts.geom.Geometry;
import com.hazelcast.shaded.org.locationtech.jts.geom.GeometryCollection;
import com.hazelcast.shaded.org.locationtech.jts.geom.LineString;
import com.hazelcast.shaded.org.locationtech.jts.geom.LinearRing;
import com.hazelcast.shaded.org.locationtech.jts.geom.MultiLineString;
import com.hazelcast.shaded.org.locationtech.jts.geom.MultiPoint;
import com.hazelcast.shaded.org.locationtech.jts.geom.MultiPolygon;
import com.hazelcast.shaded.org.locationtech.jts.geom.Point;
import com.hazelcast.shaded.org.locationtech.jts.geom.Polygon;
import com.hazelcast.shaded.org.locationtech.jts.util.Assert;

/**
 * Writes a {@link Geometry} into Well-Known Binary format.
 * Supports use of an {@link OutStream}, which allows easy use
 * with arbitrary byte stream sinks.
 * 

* The WKB format is specified in the * OGC Simple Features for SQL * specification (section 3.3.2.6). *

* There are a few cases which are not specified in the standard. * The implementation uses a representation which is compatible with * other common spatial systems (notably, PostGIS). *

    *
  • {@link LinearRing}s are written as {@link LineString}s
  • *
  • Empty geometries are output as follows: *
      *
    • Point: a WKBPoint with NaN ordinate values
    • *
    • LineString: a WKBLineString with zero points
    • *
    • Polygon: a WKBPolygon with zero rings
    • *
    • Multigeometries: a WKBMulti geometry of appropriate type with zero elements
    • *
    • GeometryCollections: a WKBGeometryCollection with zero elements
    • *
  • *
*

* This implementation supports the Extended WKB standard. * Extended WKB allows writing 3-dimensional coordinates * and the geometry SRID value. * The presence of 3D coordinates is indicated * by setting the high bit of the wkbType word. * The presence of a SRID is indicated * by setting the third bit of the wkbType word. * EWKB format is upward-compatible with the original SFS WKB format. *

* SRID output is optimized, if specified. * Only the top-level geometry has the SRID included. * This assumes that all geometries in a collection have the same SRID as * the collection (which is the JTS convention). *

* This class supports reuse of a single instance to read multiple * geometries. This class is not thread-safe; each thread should create its own * instance. * *

Syntax

* The following syntax specification describes the version of Well-Known Binary * supported by JTS. *

* The specification uses a syntax language similar to that used in * the C language. Bitfields are specified from high-order to low-order bits. *

*

 * 
 * byte = 1 byte
 * uint32 = 32 bit unsigned integer (4 bytes)
 * double = double precision number (8 bytes)
 * 
 * abstract Point { }
 * 
 * Point2D extends Point {
 * 	double x;
 * 	double y;
 * }
 * 
 * Point3D extends Point {
 * 	double x;
 * 	double y;
 * 	double z;
 * }
 * 
 * LinearRing {
 * 	uint32 numPoints;
 * 	Point points[numPoints];
 * }
 * 
 * enum wkbGeometryType {
 * 	wkbPoint = 1,
 * 	wkbLineString = 2,
 * 	wkbPolygon = 3,
 * 	wkbMultiPoint = 4,
 * 	wkbMultiLineString = 5,
 * 	wkbMultiPolygon = 6,
 * 	wkbGeometryCollection = 7
 * }
 * 
 * enum byteOrder {
 * 	wkbXDR = 0,	// Big Endian
 * 	wkbNDR = 1 	// Little Endian
 * }
 * 
 * WKBType {
 * 	uint32 wkbGeometryType : 8; // values from enum wkbGeometryType
 * }
 * 
 * EWKBType {
 * 	uint32 is3D : 1; 	// 0 = 2D, 1 = 3D
 * 	uint32 noData1 : 1; 
 * 	uint32 hasSRID : 1;  	// 0, no, 1 = yes
 * 	uint32 noData2 : 21; 
 * 	uint32 wkbGeometryType : 8; // values from enum wkbGeometryType
 * }
 * 
 * abstract WKBGeometry {
 * 	byte byteOrder;		// values from enum byteOrder
 * 	EWKBType wkbType
 * 	[ uint32 srid; ] 	// only if hasSRID = yes
 * }
 * 
 * WKBPoint extends WKBGeometry {
 * 	Point point;
 * }
 * 
 * WKBLineString extends WKBGeometry {
 * 	uint32 numCoords;
 * 	Point points[numCoords];
 * }
 * 
 * WKBPolygon extends WKBGeometry {
 * 	uint32 numRings;
 * 	LinearRing rings[numRings];
 * }
 * 
 * WKBMultiPoint extends WKBGeometry {
 * 	uint32 numElems;
 * 	WKBPoint elems[numElems];
 * }
 * 
 * WKBMultiLineString extends WKBGeometry {
 * 	uint32 numElems;
 * 	WKBLineString elems[numElems];
 * }
 * 
 * wkbMultiPolygon extends WKBGeometry {
 * 	uint32 numElems;
 * 	WKBPolygon elems[numElems];
 * }
 * 
 * WKBGeometryCollection extends WKBGeometry {
 * 	uint32 numElems;
 * 	WKBGeometry elems[numElems];
 * }
 * 
 * 
* @see WKBReader */ public class WKBWriter { /** * Converts a byte array to a hexadecimal string. * * @param bytes * @return a string of hexadecimal digits * * @deprecated */ public static String bytesToHex(byte[] bytes) { return toHex(bytes); } /** * Converts a byte array to a hexadecimal string. * * @param bytes a byte array * @return a string of hexadecimal digits */ public static String toHex(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 boolean includeSRID = false; private ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); private OutStream byteArrayOutStream = new OutputStreamOutStream(byteArrayOS); // holds output data values private byte[] buf = new byte[8]; /** * Creates a writer that writes {@link Geometry}s with * output dimension = 2 and BIG_ENDIAN byte order */ public WKBWriter() { this(2, ByteOrderValues.BIG_ENDIAN); } /** * Creates a writer that writes {@link Geometry}s with * the given dimension (2 or 3) for output coordinates * and {@link ByteOrderValues#BIG_ENDIAN} byte order. * If the input geometry has a small coordinate dimension, * coordinates will be padded with {@link Coordinate#NULL_ORDINATE}. * * @param outputDimension the coordinate dimension to output (2 or 3) */ public WKBWriter(int outputDimension) { this(outputDimension, ByteOrderValues.BIG_ENDIAN); } /** * Creates a writer that writes {@link Geometry}s with * the given dimension (2 or 3) for output coordinates * and {@link ByteOrderValues#BIG_ENDIAN} byte order. This constructor also * takes a flag to control whether srid information will be * written. * If the input geometry has a smaller coordinate dimension, * coordinates will be padded with {@link Coordinate#NULL_ORDINATE}. * * @param outputDimension the coordinate dimension to output (2 or 3) * @param includeSRID indicates whether SRID should be written */ public WKBWriter(int outputDimension, boolean includeSRID) { this(outputDimension, ByteOrderValues.BIG_ENDIAN, includeSRID); } /** * 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 Coordinate#NULL_ORDINATE}. * * @param outputDimension the coordinate dimension to output (2 or 3) * @param byteOrder the byte ordering to use */ public WKBWriter(int outputDimension, int byteOrder) { this(outputDimension, byteOrder, false); } /** * Creates a writer that writes {@link Geometry}s with * the given dimension (2 or 3) for output coordinates * and byte order. This constructor also takes a flag to * control whether srid information will be written. * If the input geometry has a small coordinate dimension, * coordinates will be padded with {@link Coordinate#NULL_ORDINATE}. * * @param outputDimension the coordinate dimension to output (2 or 3) * @param byteOrder the byte ordering to use * @param includeSRID indicates whether SRID should be written */ public WKBWriter(int outputDimension, int byteOrder, boolean includeSRID) { this.outputDimension = outputDimension; this.byteOrder = byteOrder; this.includeSRID = includeSRID; 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(WKBConstants.wkbMultiPoint, (MultiPoint) geom, os); else if (geom instanceof MultiLineString) writeGeometryCollection(WKBConstants.wkbMultiLineString, (MultiLineString) geom, os); else if (geom instanceof MultiPolygon) writeGeometryCollection(WKBConstants.wkbMultiPolygon, (MultiPolygon) geom, os); else if (geom instanceof GeometryCollection) writeGeometryCollection(WKBConstants.wkbGeometryCollection, (GeometryCollection) geom, os); else { Assert.shouldNeverReachHere("Unknown Geometry type"); } } private void writePoint(Point pt, OutStream os) throws IOException { writeByteOrder(os); writeGeometryType(WKBConstants.wkbPoint, pt, os); if (pt.getCoordinateSequence().size() == 0) { // write empty point as NaNs (extension to OGC standard) writeNaNs(outputDimension, os); } else { writeCoordinateSequence(pt.getCoordinateSequence(), false, os); } } private void writeLineString(LineString line, OutStream os) throws IOException { writeByteOrder(os); writeGeometryType(WKBConstants.wkbLineString, line, os); writeCoordinateSequence(line.getCoordinateSequence(), true, os); } private void writePolygon(Polygon poly, OutStream os) throws IOException { writeByteOrder(os); writeGeometryType(WKBConstants.wkbPolygon, poly, os); //--- write empty polygons with no rings (OCG extension) if (poly.isEmpty()) { writeInt(0, os); return; } 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, gc, os); writeInt(gc.getNumGeometries(), os); boolean originalIncludeSRID = this.includeSRID; this.includeSRID = false; for (int i = 0; i < gc.getNumGeometries(); i++) { write(gc.getGeometryN(i), os); } this.includeSRID = originalIncludeSRID; } 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, Geometry g, OutStream os) throws IOException { int flag3D = (outputDimension == 3) ? 0x80000000 : 0; int typeInt = geometryType | flag3D; typeInt |= includeSRID ? 0x20000000 : 0; writeInt(typeInt, os); if (includeSRID) { writeInt(g.getSRID(), 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 write it if the CoordinateSequence provides it double ordVal = Coordinate.NULL_ORDINATE; if (seq.getDimension() >= 3) { ordVal = seq.getOrdinate(index, 2); } ByteOrderValues.putDouble(ordVal, buf, byteOrder); os.write(buf, 8); } } private void writeNaNs(int numNaNs, OutStream os) throws IOException { for (int i = 0; i < numNaNs; i++) { ByteOrderValues.putDouble(Double.NaN, buf, byteOrder); os.write(buf, 8); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy