using System; using System.IO; using Core.Gis.GeoApi.Geometries; namespace Core.GIS.NetTopologySuite.IO { /// /// Writes a Well-Known Binary byte data representation of a Geometry. /// /// /// WKBWriter stores X,Y,Z values if is not , /// otherwise value is discarded and only X,Y are stored. /// // Thanks to Roberto Acioli for ICoordinate.Z patch [Serializable] public class WKBWriter { /// /// Standard byte size for each complex point. /// Each complex point (LineString, Polygon, ...) contains: /// 1 byte for ByteOrder and /// 4 bytes for WKBType. /// protected const int InitCount = 5; protected ByteOrder encodingType; /// /// Initializes writer with LittleIndian byte order. /// public WKBWriter() : this(ByteOrder.LittleEndian) {} /// /// Initializes writer with the specified byte order. /// /// Encoding type public WKBWriter(ByteOrder encodingType) { this.encodingType = encodingType; } /// /// Writes a WKB representation of a given point. /// /// /// public virtual byte[] Write(IGeometry geometry) { byte[] bytes = GetBytes(geometry); Write(geometry, new MemoryStream(bytes)); return bytes; } /// /// Writes a WKB representation of a given point. /// /// /// /// public virtual void Write(IGeometry geometry, Stream stream) { BinaryWriter writer = null; try { writer = encodingType == ByteOrder.LittleEndian ? new BinaryWriter(stream) : new BEBinaryWriter(stream); Write(geometry, writer); } finally { if (writer != null) { writer.Close(); } } } /// /// /// /// /// protected void Write(IGeometry geometry, BinaryWriter writer) { if (geometry is IPoint) { Write(geometry as IPoint, writer); } else if (geometry is ILineString) { Write(geometry as ILineString, writer); } else if (geometry is IPolygon) { Write(geometry as IPolygon, writer); } else if (geometry is IMultiPoint) { Write(geometry as IMultiPoint, writer); } else if (geometry is IMultiLineString) { Write(geometry as IMultiLineString, writer); } else if (geometry is IMultiPolygon) { Write(geometry as IMultiPolygon, writer); } else if (geometry is IGeometryCollection) { Write(geometry as IGeometryCollection, writer); } else { throw new ArgumentException("Geometry not recognized: " + geometry.ToString()); } } /// /// Writes LittleIndian ByteOrder. /// /// protected void WriteByteOrder(BinaryWriter writer) { writer.Write((byte) ByteOrder.LittleEndian); } /// /// /// /// /// protected void Write(ICoordinate coordinate, BinaryWriter writer) { writer.Write((double) coordinate.X); writer.Write((double) coordinate.Y); if (!Double.IsNaN(coordinate.Z)) { writer.Write((double) coordinate.Z); } } /// /// /// /// /// protected void Write(IPoint point, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(point) ? WKBGeometryTypes.WKBPointZ : WKBGeometryTypes.WKBPoint)); Write(point.Coordinate, writer); } /// /// /// /// /// protected void Write(ILineString lineString, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(lineString) ? WKBGeometryTypes.WKBLineStringZ : WKBGeometryTypes.WKBLineString)); writer.Write((int) lineString.NumPoints); for (int i = 0; i < lineString.Coordinates.Length; i++) { Write(lineString.Coordinates[i], writer); } } /// /// /// /// /// protected void Write(ILinearRing ring, BinaryWriter writer) { writer.Write((int) ring.NumPoints); for (int i = 0; i < ring.Coordinates.Length; i++) { Write(ring.Coordinates[i], writer); } } /// /// /// /// /// protected void Write(IPolygon polygon, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(polygon) ? WKBGeometryTypes.WKBPolygonZ : WKBGeometryTypes.WKBPolygon)); writer.Write((int) polygon.NumInteriorRings + 1); Write(polygon.ExteriorRing as ILinearRing, writer); for (int i = 0; i < polygon.NumInteriorRings; i++) { Write(polygon.InteriorRings[i] as ILinearRing, writer); } } /// /// /// /// /// protected void Write(IMultiPoint multiPoint, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(multiPoint) ? WKBGeometryTypes.WKBMultiPointZ : WKBGeometryTypes.WKBMultiPoint)); writer.Write((int) multiPoint.NumGeometries); for (int i = 0; i < multiPoint.NumGeometries; i++) { Write(multiPoint.Geometries[i] as IPoint, writer); } } /// /// /// /// /// protected void Write(IMultiLineString multiLineString, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(multiLineString) ? WKBGeometryTypes.WKBMultiLineStringZ : WKBGeometryTypes.WKBMultiLineString)); writer.Write((int) multiLineString.NumGeometries); for (int i = 0; i < multiLineString.NumGeometries; i++) { Write(multiLineString.Geometries[i] as ILineString, writer); } } /// /// /// /// /// protected void Write(IMultiPolygon multiPolygon, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(multiPolygon) ? WKBGeometryTypes.WKBMultiPolygonZ : WKBGeometryTypes.WKBMultiPolygon)); writer.Write((int) multiPolygon.NumGeometries); for (int i = 0; i < multiPolygon.NumGeometries; i++) { Write(multiPolygon.Geometries[i] as IPolygon, writer); } } /// /// /// /// /// protected void Write(IGeometryCollection geomCollection, BinaryWriter writer) { WriteByteOrder(writer); // LittleIndian writer.Write((int) (HasValidCoordinateZ(geomCollection) ? WKBGeometryTypes.WKBGeometryCollectionZ : WKBGeometryTypes.WKBGeometryCollection)); writer.Write((int) geomCollection.NumGeometries); for (int i = 0; i < geomCollection.NumGeometries; i++) { Write(geomCollection.Geometries[i], writer); } } /// /// Sets corrent length for Byte Stream. /// /// /// protected byte[] GetBytes(IGeometry geometry) { if (geometry is IPoint) { return new byte[SetByteStream(geometry as IPoint)]; } else if (geometry is ILineString) { return new byte[SetByteStream(geometry as ILineString)]; } else if (geometry is IPolygon) { return new byte[SetByteStream(geometry as IPolygon)]; } else if (geometry is IMultiPoint) { return new byte[SetByteStream(geometry as IMultiPoint)]; } else if (geometry is IMultiLineString) { return new byte[SetByteStream(geometry as IMultiLineString)]; } else if (geometry is IMultiPolygon) { return new byte[SetByteStream(geometry as IMultiPolygon)]; } else if (geometry is IGeometryCollection) { return new byte[SetByteStream(geometry as IGeometryCollection)]; } else { throw new ArgumentException("ShouldNeverReachHere"); } } /// /// Sets corrent length for Byte Stream. /// /// /// protected virtual int SetByteStream(IGeometry geometry) { if (geometry is IPoint) { return SetByteStream(geometry as IPoint); } else if (geometry is ILineString) { return SetByteStream(geometry as ILineString); } else if (geometry is IPolygon) { return SetByteStream(geometry as IPolygon); } else if (geometry is IMultiPoint) { return SetByteStream(geometry as IMultiPoint); } else if (geometry is IMultiLineString) { return SetByteStream(geometry as IMultiLineString); } else if (geometry is IMultiPolygon) { return SetByteStream(geometry as IMultiPolygon); } else if (geometry is IGeometryCollection) { return SetByteStream(geometry as IGeometryCollection); } else { throw new ArgumentException("ShouldNeverReachHere"); } } /// /// /// /// /// protected int SetByteStream(IGeometryCollection geometry) { int count = InitCount; count += 4; foreach (IGeometry geom in geometry.Geometries) { count += SetByteStream(geom); } return count; } /// /// /// /// /// protected int SetByteStream(IMultiPolygon geometry) { int count = InitCount; count += 4; foreach (IPolygon geom in geometry.Geometries) { count += SetByteStream(geom); } return count; } /// /// /// /// /// protected int SetByteStream(IMultiLineString geometry) { int count = InitCount; count += 4; foreach (ILineString geom in geometry.Geometries) { count += SetByteStream(geom); } return count; } /// /// /// /// /// protected int SetByteStream(IMultiPoint geometry) { int count = InitCount; count += 4; // NumPoints foreach (IPoint geom in geometry.Geometries) { count += SetByteStream(geom); } return count; } /// /// /// /// /// protected int SetByteStream(IPolygon geometry) { int pointSize = HasValidCoordinateZ(geometry) ? 24 : 16; int count = InitCount; count += 4 + 4; // NumRings + NumPoints count += 4*(geometry.NumInteriorRings + 1); // Index parts count += geometry.NumPoints*pointSize; // Points in exterior and interior rings return count; } /// /// /// /// /// protected int SetByteStream(ILineString geometry) { int pointSize = HasValidCoordinateZ(geometry) ? 24 : 16; int numPoints = geometry.NumPoints; int count = InitCount; count += 4; // NumPoints count += pointSize*numPoints; return count; } /// /// /// /// /// protected int SetByteStream(IPoint geometry) { return HasValidCoordinateZ(geometry) ? 29 : 21; } // HACK: detect if geometry has Z coordinate, should be done somewhere in Geometry (e.g. HasZ, HasM) private static bool HasValidCoordinateZ(IGeometry geometry) { return geometry.Coordinate != null && !Double.IsNaN(geometry.Coordinate.Z); } } }